hoverable.rs

  1use crate::{
  2    AnyElement, Bounds, DispatchPhase, Element, ElementId, Interactive, MouseEventListeners,
  3    MouseMoveEvent, ParentElement, Pixels, StatefulElement, 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 element_id(&self) -> Option<ElementId> {
 57        self.child.element_id()
 58    }
 59
 60    fn layout(
 61        &mut self,
 62        state: &mut Self::State,
 63        cx: &mut ViewContext<Self::State>,
 64    ) -> Result<(crate::LayoutId, Self::FrameState)> {
 65        Ok(self.child.layout(state, cx)?)
 66    }
 67
 68    fn paint(
 69        &mut self,
 70        bounds: Bounds<Pixels>,
 71        state: &mut Self::State,
 72        frame_state: &mut Self::FrameState,
 73        cx: &mut ViewContext<Self::State>,
 74    ) -> Result<()> {
 75        let hovered = bounds.contains_point(cx.mouse_position());
 76        let slot = self.cascade_slot;
 77        let style = hovered.then_some(self.hovered_style.clone());
 78        self.style_cascade().set(slot, style);
 79        self.hovered.store(hovered, SeqCst);
 80
 81        let hovered = self.hovered.clone();
 82        cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
 83            if phase == DispatchPhase::Capture {
 84                if bounds.contains_point(event.position) != hovered.load(SeqCst) {
 85                    cx.notify();
 86                }
 87            }
 88        });
 89
 90        self.child.paint(bounds, state, frame_state, cx)?;
 91        Ok(())
 92    }
 93}
 94
 95impl<E: ParentElement + Styled> ParentElement for Hoverable<E> {
 96    type State = E::State;
 97
 98    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::State>; 2]> {
 99        self.child.children_mut()
100    }
101}
102
103impl<E: StatefulElement + Styled> StatefulElement for Hoverable<E> {}