use crate::{ group_bounds, AnyElement, Bounds, DispatchPhase, Element, ElementId, IdentifiedElement, IntoAnyElement, LayoutId, MouseMoveEvent, ParentElement, Pixels, SharedString, Style, StyleCascade, StyleRefinement, Styled, ViewContext, }; use refineable::CascadeSlot; use std::sync::{ atomic::{AtomicBool, Ordering::SeqCst}, Arc, }; pub trait Hoverable { fn hover_style(&mut self) -> &mut StyleRefinement; fn hover(mut self, f: impl FnOnce(&mut StyleRefinement) -> &mut StyleRefinement) -> Self where Self: Sized, { f(self.hover_style()); self } } pub struct HoverableElement { hover_style: StyleRefinement, group: Option, cascade_slot: CascadeSlot, hovered: Arc, child: E, } impl HoverableElement { pub fn new(mut child: E) -> Self { let cascade_slot = child.style_cascade().reserve(); HoverableElement { hover_style: StyleRefinement::default(), group: None, cascade_slot, hovered: Arc::new(AtomicBool::new(false)), child, } } pub fn replace_child>( self, replace: impl FnOnce(E) -> E2, ) -> HoverableElement { HoverableElement { hover_style: self.hover_style, group: self.group, cascade_slot: self.cascade_slot, hovered: self.hovered, child: replace(self.child), } } } impl IntoAnyElement for HoverableElement where E: Styled + Element, { fn into_any(self) -> AnyElement { AnyElement::new(self) } } impl Element for HoverableElement where E: Styled + Element, { type ViewState = E::ViewState; type ElementState = E::ElementState; fn id(&self) -> Option { self.child.id() } fn layout( &mut self, state: &mut Self::ViewState, element_state: Option, cx: &mut ViewContext, ) -> (LayoutId, Self::ElementState) { self.child.layout(state, element_state, cx) } fn paint( &mut self, bounds: Bounds, state: &mut Self::ViewState, element_state: &mut Self::ElementState, cx: &mut ViewContext, ) { let target_bounds = self .group .as_ref() .and_then(|group| group_bounds(group, cx)) .unwrap_or(bounds); let hovered = target_bounds.contains_point(cx.mouse_position()); let slot = self.cascade_slot; let style = hovered.then_some(self.hover_style.clone()); self.child.style_cascade().set(slot, style); self.hovered.store(hovered, SeqCst); let hovered = self.hovered.clone(); cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| { if phase == DispatchPhase::Capture { if target_bounds.contains_point(event.position) != hovered.load(SeqCst) { cx.notify(); } } }); self.child.paint(bounds, state, element_state, cx); } } impl ParentElement for HoverableElement where E: Styled + ParentElement, { fn children_mut(&mut self) -> &mut smallvec::SmallVec<[AnyElement; 2]> { self.child.children_mut() } fn group_mut(&mut self) -> &mut Option { self.child.group_mut() } } impl Hoverable for HoverableElement { fn hover_style(&mut self) -> &mut StyleRefinement { &mut self.hover_style } } impl Styled for HoverableElement { fn style_cascade(&mut self) -> &mut StyleCascade { self.child.style_cascade() } fn computed_style(&mut self) -> &Style { self.child.computed_style() } } impl IdentifiedElement for HoverableElement {}