1use crate::{
2 group_bounds, AnyElement, Bounds, DispatchPhase, Element, ElementId, ElementKind,
3 IdentifiedElement, IntoAnyElement, LayoutId, LayoutNode, MouseMoveEvent, Pixels, SharedString,
4 Style, StyleCascade, StyleRefinement, Styled, ViewContext,
5};
6use refineable::CascadeSlot;
7use std::sync::{
8 atomic::{AtomicBool, Ordering::SeqCst},
9 Arc,
10};
11
12pub trait Hoverable {
13 fn hover_style(&mut self) -> &mut StyleRefinement;
14
15 fn hover(mut self, f: impl FnOnce(&mut StyleRefinement) -> &mut StyleRefinement) -> Self
16 where
17 Self: Sized,
18 {
19 f(self.hover_style());
20 self
21 }
22}
23
24pub struct HoverableElement<E> {
25 hover_style: StyleRefinement,
26 group: Option<SharedString>,
27 cascade_slot: CascadeSlot,
28 hovered: Arc<AtomicBool>,
29 child: E,
30}
31
32impl<E: Styled + Element> HoverableElement<E> {
33 pub fn replace_child<E2: Element<ViewState = E::ViewState>>(
34 self,
35 replace: impl FnOnce(E) -> E2,
36 ) -> HoverableElement<E2> {
37 HoverableElement {
38 hover_style: self.hover_style,
39 group: self.group,
40 cascade_slot: self.cascade_slot,
41 hovered: self.hovered,
42 child: replace(self.child),
43 }
44 }
45}
46
47impl<E> IntoAnyElement<E::ViewState> for HoverableElement<E>
48where
49 E: Styled + Element,
50{
51 fn into_any(self) -> AnyElement<E::ViewState> {
52 AnyElement::new(self)
53 }
54}
55
56impl<E> Element for HoverableElement<E>
57where
58 E: Styled + Element,
59{
60 type ViewState = E::ViewState;
61 type ElementState = E::ElementState;
62
63 fn id(&self) -> Option<ElementId> {
64 self.child.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 ) -> (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.hover_style.clone());
93 self.child.style_cascade().set(slot, style);
94 self.hovered.store(hovered, SeqCst);
95
96 let hovered = self.hovered.clone();
97 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
98 if phase == DispatchPhase::Capture {
99 if target_bounds.contains_point(event.position) != hovered.load(SeqCst) {
100 cx.notify();
101 }
102 }
103 });
104
105 self.child.paint(bounds, state, element_state, cx);
106 }
107}
108
109impl<E, K, V> LayoutNode<V, K> for HoverableElement<E>
110where
111 E: LayoutNode<V, K>,
112 K: ElementKind,
113 V: 'static + Send + Sync,
114{
115 fn children_mut(&mut self) -> &mut smallvec::SmallVec<[AnyElement<V>; 2]> {
116 self.child.children_mut()
117 }
118
119 fn group_mut(&mut self) -> &mut Option<SharedString> {
120 self.child.group_mut()
121 }
122}
123
124impl<E: Styled + Element> Hoverable for HoverableElement<E> {
125 fn hover_style(&mut self) -> &mut StyleRefinement {
126 &mut self.hover_style
127 }
128}
129
130impl<E: Styled + Element> Styled for HoverableElement<E> {
131 fn style_cascade(&mut self) -> &mut StyleCascade {
132 self.child.style_cascade()
133 }
134
135 fn computed_style(&mut self) -> &Style {
136 self.child.computed_style()
137 }
138}
139
140impl<E: Styled + IdentifiedElement> IdentifiedElement for HoverableElement<E> {}