hoverable.rs

 1use crate::{
 2    AnyElement, Bounds, DispatchPhase, Element, Interactive, MouseEventListeners, MouseMoveEvent,
 3    ParentElement, Pixels, Styled, ViewContext,
 4};
 5use anyhow::Result;
 6use refineable::{CascadeSlot, Refineable, RefinementCascade};
 7use smallvec::SmallVec;
 8use std::sync::{
 9    atomic::{AtomicBool, Ordering::SeqCst},
10    Arc,
11};
12
13pub struct Hoverable<E: Styled> {
14    hovered: Arc<AtomicBool>,
15    cascade_slot: CascadeSlot,
16    hovered_style: <E::Style as Refineable>::Refinement,
17    child: E,
18}
19
20impl<E: Styled> Hoverable<E> {
21    pub fn new(mut child: E) -> Self {
22        Self {
23            hovered: Arc::new(AtomicBool::new(false)),
24            cascade_slot: child.style_cascade().reserve(),
25            hovered_style: Default::default(),
26            child,
27        }
28    }
29}
30
31impl<E> Styled for Hoverable<E>
32where
33    E: Styled,
34{
35    type Style = E::Style;
36
37    fn style_cascade(&mut self) -> &mut RefinementCascade<E::Style> {
38        self.child.style_cascade()
39    }
40
41    fn declared_style(&mut self) -> &mut <Self::Style as refineable::Refineable>::Refinement {
42        &mut self.hovered_style
43    }
44}
45
46impl<S: 'static + Send + Sync, E: Interactive<S> + Styled> Interactive<S> for Hoverable<E> {
47    fn listeners(&mut self) -> &mut MouseEventListeners<S> {
48        self.child.listeners()
49    }
50}
51
52impl<E: Element + Styled> Element for Hoverable<E> {
53    type State = E::State;
54    type FrameState = E::FrameState;
55
56    fn layout(
57        &mut self,
58        state: &mut Self::State,
59        cx: &mut ViewContext<Self::State>,
60    ) -> Result<(crate::LayoutId, Self::FrameState)> {
61        Ok(self.child.layout(state, cx)?)
62    }
63
64    fn paint(
65        &mut self,
66        bounds: Bounds<Pixels>,
67        state: &mut Self::State,
68        frame_state: &mut Self::FrameState,
69        cx: &mut ViewContext<Self::State>,
70    ) -> Result<()> {
71        let hovered = bounds.contains_point(cx.mouse_position());
72        let slot = self.cascade_slot;
73        let style = hovered.then_some(self.hovered_style.clone());
74        self.style_cascade().set(slot, style);
75        self.hovered.store(hovered, SeqCst);
76
77        let hovered = self.hovered.clone();
78        cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
79            if phase == DispatchPhase::Capture {
80                if bounds.contains_point(event.position) != hovered.load(SeqCst) {
81                    cx.notify();
82                }
83            }
84        });
85
86        self.child.paint(bounds, state, frame_state, cx)?;
87        Ok(())
88    }
89}
90
91impl<E: ParentElement + Styled> ParentElement for Hoverable<E> {
92    type State = E::State;
93
94    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::State>; 2]> {
95        self.child.children_mut()
96    }
97}