1use crate::{
2 group_bounds, AnyElement, Bounds, DispatchPhase, Element, ElementId, IdentifiedElement,
3 Interactive, MouseEventListeners, MouseMoveEvent, ParentElement, Pixels, SharedString, Styled,
4 ViewContext,
5};
6use refineable::{Cascade, CascadeSlot, Refineable};
7use smallvec::SmallVec;
8use std::sync::{
9 atomic::{AtomicBool, Ordering::SeqCst},
10 Arc,
11};
12
13pub struct Hoverable<E: Styled> {
14 group: Option<SharedString>,
15 hovered: Arc<AtomicBool>,
16 cascade_slot: CascadeSlot,
17 hovered_style: <E::Style as Refineable>::Refinement,
18 child: E,
19}
20
21impl<E: Styled> Hoverable<E> {
22 pub fn new(mut child: E, hover_group: Option<SharedString>) -> Self {
23 Self {
24 group: hover_group,
25 hovered: Arc::new(AtomicBool::new(false)),
26 cascade_slot: child.style_cascade().reserve(),
27 hovered_style: Default::default(),
28 child,
29 }
30 }
31}
32
33impl<E> Styled for Hoverable<E>
34where
35 E: Styled,
36{
37 type Style = E::Style;
38
39 fn style_cascade(&mut self) -> &mut Cascade<E::Style> {
40 self.child.style_cascade()
41 }
42
43 fn declared_style(&mut self) -> &mut <Self::Style as refineable::Refineable>::Refinement {
44 &mut self.hovered_style
45 }
46}
47
48impl<S: 'static + Send + Sync, E: Interactive<S> + Styled> Interactive<S> for Hoverable<E> {
49 fn listeners(&mut self) -> &mut MouseEventListeners<S> {
50 self.child.listeners()
51 }
52}
53
54impl<E> Element for Hoverable<E>
55where
56 E: Element + Styled,
57 <E as Styled>::Style: 'static + Refineable + Send + Sync + Default,
58 <<E as Styled>::Style as Refineable>::Refinement: 'static + Refineable + Send + Sync + Default,
59{
60 type ViewState = E::ViewState;
61 type ElementState = E::ElementState;
62
63 fn element_id(&self) -> Option<ElementId> {
64 self.child.element_id()
65 }
66
67 fn layout(
68 &mut self,
69 state: &mut Self::ViewState,
70 element_state: Option<Self::ElementState>,
71 cx: &mut ViewContext<Self::ViewState>,
72 ) -> (crate::LayoutId, Self::ElementState) {
73 self.child.layout(state, element_state, cx)
74 }
75
76 fn paint(
77 &mut self,
78 bounds: Bounds<Pixels>,
79 state: &mut Self::ViewState,
80 element_state: &mut Self::ElementState,
81 cx: &mut ViewContext<Self::ViewState>,
82 ) {
83 let target_bounds = self
84 .group
85 .as_ref()
86 .and_then(|group| group_bounds(group, cx))
87 .unwrap_or(bounds);
88
89 let hovered = target_bounds.contains_point(cx.mouse_position());
90
91 let slot = self.cascade_slot;
92 let style = hovered.then_some(self.hovered_style.clone());
93 self.style_cascade().set(slot, style);
94 self.hovered.store(hovered, SeqCst);
95
96 cx.on_mouse_event({
97 let hovered = self.hovered.clone();
98
99 move |_, event: &MouseMoveEvent, phase, cx| {
100 if phase == DispatchPhase::Capture {
101 if target_bounds.contains_point(event.position) != hovered.load(SeqCst) {
102 cx.notify();
103 }
104 }
105 }
106 });
107
108 self.child.paint(bounds, state, element_state, cx);
109 }
110}
111
112impl<E: ParentElement + Styled> ParentElement for Hoverable<E> {
113 type State = E::State;
114
115 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::State>; 2]> {
116 self.child.children_mut()
117 }
118}
119
120impl<E> IdentifiedElement for Hoverable<E>
121where
122 E: IdentifiedElement + Styled,
123 <E as Styled>::Style: 'static + Refineable + Send + Sync + Default,
124 <<E as Styled>::Style as Refineable>::Refinement: 'static + Refineable + Send + Sync + Default,
125{
126}