hoverable.rs

  1use crate::{
  2    group_bounds, AnyElement, Bounds, DispatchPhase, Element, ElementId, IdentifiedElement,
  3    Interactive, MouseEventListeners, MouseMoveEvent, ParentElement, Pixels, SharedString, Styled,
  4    ViewContext,
  5};
  6use refineable::{Cascade, CascadeSlot, Refineable};
  7use smallvec::SmallVec;
  8use std::sync::{
  9    atomic::{AtomicBool, Ordering::SeqCst},
 10    Arc,
 11};
 12
 13pub struct Hoverable<E: Styled> {
 14    group: Option<SharedString>,
 15    hovered: Arc<AtomicBool>,
 16    cascade_slot: CascadeSlot,
 17    hovered_style: <E::Style as Refineable>::Refinement,
 18    child: E,
 19}
 20
 21impl<E: Styled> Hoverable<E> {
 22    pub fn new(mut child: E, hover_group: Option<SharedString>) -> Self {
 23        Self {
 24            group: hover_group,
 25            hovered: Arc::new(AtomicBool::new(false)),
 26            cascade_slot: child.style_cascade().reserve(),
 27            hovered_style: Default::default(),
 28            child,
 29        }
 30    }
 31}
 32
 33impl<E> Styled for Hoverable<E>
 34where
 35    E: Styled,
 36{
 37    type Style = E::Style;
 38
 39    fn style_cascade(&mut self) -> &mut Cascade<E::Style> {
 40        self.child.style_cascade()
 41    }
 42
 43    fn declared_style(&mut self) -> &mut <Self::Style as refineable::Refineable>::Refinement {
 44        &mut self.hovered_style
 45    }
 46}
 47
 48impl<S: 'static + Send + Sync, E: Interactive<S> + Styled> Interactive<S> for Hoverable<E> {
 49    fn listeners(&mut self) -> &mut MouseEventListeners<S> {
 50        self.child.listeners()
 51    }
 52}
 53
 54impl<E> Element for Hoverable<E>
 55where
 56    E: Element + Styled,
 57    <E as Styled>::Style: 'static + Refineable + Send + Sync + Default,
 58    <<E as Styled>::Style as Refineable>::Refinement: 'static + Refineable + Send + Sync + Default,
 59{
 60    type ViewState = E::ViewState;
 61    type ElementState = E::ElementState;
 62
 63    fn element_id(&self) -> Option<ElementId> {
 64        self.child.element_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    ) -> (crate::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.hovered_style.clone());
 93        self.style_cascade().set(slot, style);
 94        self.hovered.store(hovered, SeqCst);
 95
 96        cx.on_mouse_event({
 97            let hovered = self.hovered.clone();
 98
 99            move |_, event: &MouseMoveEvent, phase, cx| {
100                if phase == DispatchPhase::Capture {
101                    if target_bounds.contains_point(event.position) != hovered.load(SeqCst) {
102                        cx.notify();
103                    }
104                }
105            }
106        });
107
108        self.child.paint(bounds, state, element_state, cx);
109    }
110}
111
112impl<E: ParentElement + Styled> ParentElement for Hoverable<E> {
113    type State = E::State;
114
115    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::State>; 2]> {
116        self.child.children_mut()
117    }
118}
119
120impl<E> IdentifiedElement for Hoverable<E>
121where
122    E: IdentifiedElement + Styled,
123    <E as Styled>::Style: 'static + Refineable + Send + Sync + Default,
124    <<E as Styled>::Style as Refineable>::Refinement: 'static + Refineable + Send + Sync + Default,
125{
126}