hoverable.rs

  1use crate::{
  2    group_bounds, AnyElement, Bounds, DispatchPhase, Element, ElementId, IdentifiedElement,
  3    Interactive, IntoAnyElement, MouseEventListeners, MouseMoveEvent, ParentElement, Pixels,
  4    SharedString, Styled, 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> IntoAnyElement<E::ViewState> 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    fn into_any(self) -> AnyElement<E::ViewState> {
 61        AnyElement::new(self)
 62    }
 63}
 64
 65impl<E> Element for Hoverable<E>
 66where
 67    E: Element + Styled,
 68    <E as Styled>::Style: 'static + Refineable + Send + Sync + Default,
 69    <<E as Styled>::Style as Refineable>::Refinement: 'static + Refineable + Send + Sync + Default,
 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    ) -> (crate::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.hovered_style.clone());
104        self.style_cascade().set(slot, style);
105        self.hovered.store(hovered, SeqCst);
106
107        cx.on_mouse_event({
108            let hovered = self.hovered.clone();
109
110            move |_, event: &MouseMoveEvent, phase, cx| {
111                if phase == DispatchPhase::Bubble {
112                    if target_bounds.contains_point(event.position) != hovered.load(SeqCst) {
113                        cx.notify();
114                        cx.stop_propagation();
115                    }
116                }
117            }
118        });
119
120        self.child.paint(bounds, state, element_state, cx);
121    }
122}
123
124impl<E: ParentElement + Styled> ParentElement for Hoverable<E> {
125    type State = E::State;
126
127    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::State>; 2]> {
128        self.child.children_mut()
129    }
130}
131
132impl<E> IdentifiedElement for Hoverable<E>
133where
134    E: IdentifiedElement + Styled,
135    <E as Styled>::Style: 'static + Refineable + Send + Sync + Default,
136    <<E as Styled>::Style as Refineable>::Refinement: 'static + Refineable + Send + Sync + Default,
137{
138}