hoverable.rs

  1use crate::{
  2    element::{AnyElement, Element, IntoElement, Layout, ParentElement},
  3    interactive::{InteractionHandlers, Interactive},
  4    layout_context::LayoutContext,
  5    paint_context::PaintContext,
  6    style::{Style, StyleHelpers, Styleable},
  7};
  8use anyhow::Result;
  9use gpui::platform::MouseMovedEvent;
 10use refineable::{CascadeSlot, Refineable, RefinementCascade};
 11use smallvec::SmallVec;
 12use std::{cell::Cell, rc::Rc};
 13
 14pub struct Hoverable<E: Styleable> {
 15    hovered: Rc<Cell<bool>>,
 16    cascade_slot: CascadeSlot,
 17    hovered_style: <E::Style as Refineable>::Refinement,
 18    child: E,
 19}
 20
 21pub fn hoverable<E: Styleable>(mut child: E) -> Hoverable<E> {
 22    Hoverable {
 23        hovered: Rc::new(Cell::new(false)),
 24        cascade_slot: child.style_cascade().reserve(),
 25        hovered_style: Default::default(),
 26        child,
 27    }
 28}
 29
 30impl<E: Styleable> Styleable for Hoverable<E> {
 31    type Style = E::Style;
 32
 33    fn style_cascade(&mut self) -> &mut RefinementCascade<Self::Style> {
 34        self.child.style_cascade()
 35    }
 36
 37    fn declared_style(&mut self) -> &mut <Self::Style as Refineable>::Refinement {
 38        &mut self.hovered_style
 39    }
 40}
 41
 42impl<V: 'static, E: Element<V> + Styleable> Element<V> for Hoverable<E> {
 43    type Layout = E::Layout;
 44
 45    fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<Layout<V, Self::Layout>>
 46    where
 47        Self: Sized,
 48    {
 49        self.child.layout(view, cx)
 50    }
 51
 52    fn paint(
 53        &mut self,
 54        view: &mut V,
 55        layout: &mut Layout<V, Self::Layout>,
 56        cx: &mut PaintContext<V>,
 57    ) where
 58        Self: Sized,
 59    {
 60        let bounds = layout.bounds(cx);
 61        let order = layout.order(cx);
 62
 63        self.hovered.set(bounds.contains_point(cx.mouse_position()));
 64
 65        let slot = self.cascade_slot;
 66        let style = self.hovered.get().then_some(self.hovered_style.clone());
 67        self.style_cascade().set(slot, style);
 68
 69        let hovered = self.hovered.clone();
 70        cx.on_event(order, move |view, event: &MouseMovedEvent, cx| {
 71            if bounds.contains_point(cx.mouse_position()) != hovered.get() {
 72                cx.repaint();
 73            }
 74        });
 75
 76        self.child.paint(view, layout, cx);
 77    }
 78}
 79
 80impl<E: Styleable<Style = Style>> StyleHelpers for Hoverable<E> {}
 81
 82impl<V: 'static, E: Interactive<V> + Styleable> Interactive<V> for Hoverable<E> {
 83    fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V> {
 84        self.child.interaction_handlers()
 85    }
 86}
 87
 88impl<V: 'static, E: ParentElement<V> + Styleable> ParentElement<V> for Hoverable<E> {
 89    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
 90        self.child.children_mut()
 91    }
 92}
 93
 94impl<V: 'static, E: Element<V> + Styleable> IntoElement<V> for Hoverable<E> {
 95    type Element = Self;
 96
 97    fn into_element(self) -> Self::Element {
 98        self
 99    }
100}