hoverable.rs

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