1use crate::{
2 group_bounds, AnyElement, Bounds, DispatchPhase, Element, ElementId, IdentifiedElement,
3 Interactive, IntoAnyElement, MouseEventListeners, MouseMoveEvent, ParentElement, Pixels,
4 SharedString, Styled, 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> IntoAnyElement<E::ViewState> 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 fn into_any(self) -> AnyElement<E::ViewState> {
61 AnyElement::new(self)
62 }
63}
64
65impl<E> Element for Hoverable<E>
66where
67 E: Element + Styled,
68 <E as Styled>::Style: 'static + Refineable + Send + Sync + Default,
69 <<E as Styled>::Style as Refineable>::Refinement: 'static + Refineable + Send + Sync + Default,
70{
71 type ViewState = E::ViewState;
72 type ElementState = E::ElementState;
73
74 fn id(&self) -> Option<ElementId> {
75 self.child.id()
76 }
77
78 fn layout(
79 &mut self,
80 state: &mut Self::ViewState,
81 element_state: Option<Self::ElementState>,
82 cx: &mut ViewContext<Self::ViewState>,
83 ) -> (crate::LayoutId, Self::ElementState) {
84 self.child.layout(state, element_state, cx)
85 }
86
87 fn paint(
88 &mut self,
89 bounds: Bounds<Pixels>,
90 state: &mut Self::ViewState,
91 element_state: &mut Self::ElementState,
92 cx: &mut ViewContext<Self::ViewState>,
93 ) {
94 let target_bounds = self
95 .group
96 .as_ref()
97 .and_then(|group| group_bounds(group, cx))
98 .unwrap_or(bounds);
99
100 let hovered = target_bounds.contains_point(cx.mouse_position());
101
102 let slot = self.cascade_slot;
103 let style = hovered.then_some(self.hovered_style.clone());
104 self.style_cascade().set(slot, style);
105 self.hovered.store(hovered, SeqCst);
106
107 cx.on_mouse_event({
108 let hovered = self.hovered.clone();
109
110 move |_, event: &MouseMoveEvent, phase, cx| {
111 if phase == DispatchPhase::Bubble {
112 if target_bounds.contains_point(event.position) != hovered.load(SeqCst) {
113 cx.notify();
114 cx.stop_propagation();
115 }
116 }
117 }
118 });
119
120 self.child.paint(bounds, state, element_state, cx);
121 }
122}
123
124impl<E: ParentElement + Styled> ParentElement for Hoverable<E> {
125 type State = E::State;
126
127 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::State>; 2]> {
128 self.child.children_mut()
129 }
130}
131
132impl<E> IdentifiedElement for Hoverable<E>
133where
134 E: IdentifiedElement + Styled,
135 <E as Styled>::Style: 'static + Refineable + Send + Sync + Default,
136 <<E as Styled>::Style as Refineable>::Refinement: 'static + Refineable + Send + Sync + Default,
137{
138}