hoverable.rs

  1use crate::{
  2    group_bounds, AnyElement, Bounds, DispatchPhase, Element, ElementId, ElementKind,
  3    IdentifiedElement, IntoAnyElement, LayoutId, LayoutNode, MouseMoveEvent, Pixels, SharedString,
  4    Style, StyleCascade, StyleRefinement, Styled, ViewContext,
  5};
  6use refineable::CascadeSlot;
  7use std::sync::{
  8    atomic::{AtomicBool, Ordering::SeqCst},
  9    Arc,
 10};
 11
 12pub trait Hoverable {
 13    fn hover_style(&mut self) -> &mut StyleRefinement;
 14
 15    fn hover(mut self, f: impl FnOnce(&mut StyleRefinement) -> &mut StyleRefinement) -> Self
 16    where
 17        Self: Sized,
 18    {
 19        f(self.hover_style());
 20        self
 21    }
 22}
 23
 24pub struct HoverableElement<E> {
 25    hover_style: StyleRefinement,
 26    group: Option<SharedString>,
 27    cascade_slot: CascadeSlot,
 28    hovered: Arc<AtomicBool>,
 29    child: E,
 30}
 31
 32impl<E: Styled + Element> HoverableElement<E> {
 33    pub fn replace_child<E2: Element<ViewState = E::ViewState>>(
 34        self,
 35        replace: impl FnOnce(E) -> E2,
 36    ) -> HoverableElement<E2> {
 37        HoverableElement {
 38            hover_style: self.hover_style,
 39            group: self.group,
 40            cascade_slot: self.cascade_slot,
 41            hovered: self.hovered,
 42            child: replace(self.child),
 43        }
 44    }
 45}
 46
 47impl<E> IntoAnyElement<E::ViewState> for HoverableElement<E>
 48where
 49    E: Styled + Element,
 50{
 51    fn into_any(self) -> AnyElement<E::ViewState> {
 52        AnyElement::new(self)
 53    }
 54}
 55
 56impl<E> Element for HoverableElement<E>
 57where
 58    E: Styled + Element,
 59{
 60    type ViewState = E::ViewState;
 61    type ElementState = E::ElementState;
 62
 63    fn id(&self) -> Option<ElementId> {
 64        self.child.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    ) -> (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.hover_style.clone());
 93        self.child.style_cascade().set(slot, style);
 94        self.hovered.store(hovered, SeqCst);
 95
 96        let hovered = self.hovered.clone();
 97        cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
 98            if phase == DispatchPhase::Capture {
 99                if target_bounds.contains_point(event.position) != hovered.load(SeqCst) {
100                    cx.notify();
101                }
102            }
103        });
104
105        self.child.paint(bounds, state, element_state, cx);
106    }
107}
108
109impl<E, K, V> LayoutNode<V, K> for HoverableElement<E>
110where
111    E: LayoutNode<V, K>,
112    K: ElementKind,
113    V: 'static + Send + Sync,
114{
115    fn children_mut(&mut self) -> &mut smallvec::SmallVec<[AnyElement<V>; 2]> {
116        self.child.children_mut()
117    }
118
119    fn group_mut(&mut self) -> &mut Option<SharedString> {
120        self.child.group_mut()
121    }
122}
123
124impl<E: Styled + Element> Hoverable for HoverableElement<E> {
125    fn hover_style(&mut self) -> &mut StyleRefinement {
126        &mut self.hover_style
127    }
128}
129
130impl<E: Styled + Element> Styled for HoverableElement<E> {
131    fn style_cascade(&mut self) -> &mut StyleCascade {
132        self.child.style_cascade()
133    }
134
135    fn computed_style(&mut self) -> &Style {
136        self.child.computed_style()
137    }
138}
139
140impl<E: Styled + IdentifiedElement> IdentifiedElement for HoverableElement<E> {}