1use crate::{
2 group_bounds, AnyElement, Bounds, DispatchPhase, Element, ElementId, IdentifiedElement,
3 IntoAnyElement, LayoutId, MouseMoveEvent, ParentElement, Pixels, SharedString, Style,
4 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 new(mut child: E) -> Self {
34 let cascade_slot = child.style_cascade().reserve();
35 HoverableElement {
36 hover_style: StyleRefinement::default(),
37 group: None,
38 cascade_slot,
39 hovered: Arc::new(AtomicBool::new(false)),
40 child,
41 }
42 }
43
44 pub fn replace_child<E2: Element<ViewState = E::ViewState>>(
45 self,
46 replace: impl FnOnce(E) -> E2,
47 ) -> HoverableElement<E2> {
48 HoverableElement {
49 hover_style: self.hover_style,
50 group: self.group,
51 cascade_slot: self.cascade_slot,
52 hovered: self.hovered,
53 child: replace(self.child),
54 }
55 }
56}
57
58impl<E> IntoAnyElement<E::ViewState> for HoverableElement<E>
59where
60 E: Styled + Element,
61{
62 fn into_any(self) -> AnyElement<E::ViewState> {
63 AnyElement::new(self)
64 }
65}
66
67impl<E> Element for HoverableElement<E>
68where
69 E: Styled + Element,
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 ) -> (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.hover_style.clone());
104 self.child.style_cascade().set(slot, style);
105 self.hovered.store(hovered, SeqCst);
106
107 let hovered = self.hovered.clone();
108 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
109 if phase == DispatchPhase::Capture {
110 if target_bounds.contains_point(event.position) != hovered.load(SeqCst) {
111 cx.notify();
112 }
113 }
114 });
115
116 self.child.paint(bounds, state, element_state, cx);
117 }
118}
119
120impl<E> ParentElement for HoverableElement<E>
121where
122 E: Styled + ParentElement,
123{
124 fn children_mut(&mut self) -> &mut smallvec::SmallVec<[AnyElement<E::ViewState>; 2]> {
125 self.child.children_mut()
126 }
127
128 fn group_mut(&mut self) -> &mut Option<SharedString> {
129 self.child.group_mut()
130 }
131}
132
133impl<E: Styled + Element> Hoverable for HoverableElement<E> {
134 fn hover_style(&mut self) -> &mut StyleRefinement {
135 &mut self.hover_style
136 }
137}
138
139impl<E: Styled + Element> Styled for HoverableElement<E> {
140 fn style_cascade(&mut self) -> &mut StyleCascade {
141 self.child.style_cascade()
142 }
143
144 fn computed_style(&mut self) -> &Style {
145 self.child.computed_style()
146 }
147}
148
149impl<E: Styled + IdentifiedElement> IdentifiedElement for HoverableElement<E> {}