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}