hoverable.rs

  1use crate::{
  2    group_bounds, AnyElement, Bounds, DispatchPhase, Element, ElementId, IdentifiedElement,
  3    IntoAnyElement, LayoutId, MouseMoveEvent, ParentElement, Pixels, SharedString, Style,
  4    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 new(mut child: E) -> Self {
 34        let cascade_slot = child.style_cascade().reserve();
 35        HoverableElement {
 36            hover_style: StyleRefinement::default(),
 37            group: None,
 38            cascade_slot,
 39            hovered: Arc::new(AtomicBool::new(false)),
 40            child,
 41        }
 42    }
 43
 44    pub fn replace_child<E2: Element<ViewState = E::ViewState>>(
 45        self,
 46        replace: impl FnOnce(E) -> E2,
 47    ) -> HoverableElement<E2> {
 48        HoverableElement {
 49            hover_style: self.hover_style,
 50            group: self.group,
 51            cascade_slot: self.cascade_slot,
 52            hovered: self.hovered,
 53            child: replace(self.child),
 54        }
 55    }
 56}
 57
 58impl<E> IntoAnyElement<E::ViewState> for HoverableElement<E>
 59where
 60    E: Styled + Element,
 61{
 62    fn into_any(self) -> AnyElement<E::ViewState> {
 63        AnyElement::new(self)
 64    }
 65}
 66
 67impl<E> Element for HoverableElement<E>
 68where
 69    E: Styled + Element,
 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    ) -> (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.hover_style.clone());
104        self.child.style_cascade().set(slot, style);
105        self.hovered.store(hovered, SeqCst);
106
107        let hovered = self.hovered.clone();
108        cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
109            if phase == DispatchPhase::Capture {
110                if target_bounds.contains_point(event.position) != hovered.load(SeqCst) {
111                    cx.notify();
112                }
113            }
114        });
115
116        self.child.paint(bounds, state, element_state, cx);
117    }
118}
119
120impl<E> ParentElement for HoverableElement<E>
121where
122    E: Styled + ParentElement,
123{
124    fn children_mut(&mut self) -> &mut smallvec::SmallVec<[AnyElement<E::ViewState>; 2]> {
125        self.child.children_mut()
126    }
127
128    fn group_mut(&mut self) -> &mut Option<SharedString> {
129        self.child.group_mut()
130    }
131}
132
133impl<E: Styled + Element> Hoverable for HoverableElement<E> {
134    fn hover_style(&mut self) -> &mut StyleRefinement {
135        &mut self.hover_style
136    }
137}
138
139impl<E: Styled + Element> Styled for HoverableElement<E> {
140    fn style_cascade(&mut self) -> &mut StyleCascade {
141        self.child.style_cascade()
142    }
143
144    fn computed_style(&mut self) -> &Style {
145        self.child.computed_style()
146    }
147}
148
149impl<E: Styled + IdentifiedElement> IdentifiedElement for HoverableElement<E> {}