hoverable.rs

  1use std::{cell::Cell, marker::PhantomData, rc::Rc};
  2
  3use gpui::{
  4    geometry::{rect::RectF, vector::Vector2F},
  5    scene::MouseMove,
  6    EngineLayout, ViewContext,
  7};
  8use refineable::Refineable;
  9
 10use crate::{
 11    element::{Element, ParentElement},
 12    style::StyleRefinement,
 13};
 14
 15pub struct Hoverable<V, E> {
 16    hover_style: StyleRefinement,
 17    computed_style: Option<StyleRefinement>,
 18    hovered: Rc<Cell<bool>>,
 19    view_type: PhantomData<V>,
 20    child: E,
 21}
 22
 23impl<V, E> Hoverable<V, E> {
 24    pub fn new(child: E) -> Self {
 25        Self {
 26            hover_style: StyleRefinement::default(),
 27            computed_style: None,
 28            hovered: Default::default(),
 29            view_type: PhantomData,
 30            child,
 31        }
 32    }
 33}
 34
 35impl<V: 'static, E: Element<V>> Element<V> for Hoverable<V, E> {
 36    type Layout = E::Layout;
 37
 38    fn declared_style(&mut self) -> &mut StyleRefinement {
 39        &mut self.hover_style
 40    }
 41
 42    fn computed_style(&mut self, cx: &mut ViewContext<V>) -> &StyleRefinement {
 43        self.computed_style.get_or_insert_with(|| {
 44            let mut style = self.child.computed_style(cx).clone();
 45            if self.hovered.get() {
 46                style.refine(&self.hover_style);
 47            }
 48            style
 49        })
 50    }
 51
 52    fn handlers_mut(&mut self) -> &mut Vec<crate::element::EventHandler<V>> {
 53        self.child.handlers_mut()
 54    }
 55
 56    fn layout(
 57        &mut self,
 58        view: &mut V,
 59        cx: &mut gpui::LayoutContext<V>,
 60    ) -> anyhow::Result<(taffy::tree::NodeId, Self::Layout)> {
 61        self.child.layout(view, cx)
 62    }
 63
 64    fn paint<'a>(
 65        &mut self,
 66        layout: crate::element::Layout<Self::Layout>,
 67        view: &mut V,
 68        cx: &mut crate::element::PaintContext<V>,
 69    ) -> anyhow::Result<()> {
 70        let EngineLayout { bounds, order } = layout.from_engine;
 71        let window_bounds = RectF::new(Vector2F::zero(), cx.window_size());
 72        let hovered = self.hovered.clone();
 73
 74        self.child.paint(layout, view, cx)?;
 75
 76        let mouse_within_bounds = bounds.contains_point(cx.mouse_position());
 77        if mouse_within_bounds != hovered.get() {
 78            hovered.set(mouse_within_bounds);
 79            cx.repaint();
 80        }
 81
 82        cx.draw_interactive_region(
 83            order,
 84            window_bounds,
 85            false,
 86            move |view, event: &MouseMove, cx| {
 87                let mouse_within_bounds = bounds.contains_point(cx.mouse_position());
 88                if mouse_within_bounds != hovered.get() {
 89                    hovered.set(mouse_within_bounds);
 90                    cx.repaint();
 91                }
 92            },
 93        );
 94        Ok(())
 95    }
 96}
 97
 98impl<V: 'static, P: ParentElement<V>> ParentElement<V> for Hoverable<V, P> {
 99    fn child(mut self, child: impl crate::element::IntoElement<V>) -> Self
100    where
101        Self: Sized,
102    {
103        self.child = self.child.child(child);
104        self
105    }
106
107    fn children<I, E>(mut self, children: I) -> Self
108    where
109        Self: Sized,
110        I: IntoIterator<Item = E>,
111        E: crate::element::IntoElement<V>,
112    {
113        self.child = self.child.children(children);
114        self
115    }
116}