Still need to wire up MouseMove with the new regions

Nathan Sobo created

Change summary

crates/gpui/playground/src/components.rs |  2 
crates/gpui/playground/src/element.rs    | 40 ++++++++++++++-------
crates/gpui/playground/src/frame.rs      |  9 ++--
crates/gpui/playground/src/hoverable.rs  | 47 ++++++++++++++++++++++---
crates/gpui/playground/src/playground.rs |  4 +
crates/gpui/src/app.rs                   |  7 ---
crates/gpui/src/app/window.rs            |  6 +++
7 files changed, 82 insertions(+), 33 deletions(-)

Detailed changes

crates/gpui/playground/src/element.rs 🔗

@@ -419,7 +419,7 @@ pub trait Element<V: 'static>: 'static {
         self
     }
 
-    fn hoverable(self) -> Hoverable<V, Self>
+    fn hover(self) -> Hoverable<V, Self>
     where
         Self: Sized,
     {
@@ -443,6 +443,18 @@ pub trait Element<V: 'static>: 'static {
     }
 }
 
+pub trait ParentElement<V: 'static>: Element<V> {
+    fn child(self, child: impl IntoElement<V>) -> Self
+    where
+        Self: Sized;
+
+    fn children<I, E>(self, children: I) -> Self
+    where
+        Self: Sized,
+        I: IntoIterator<Item = E>,
+        E: IntoElement<V>;
+}
+
 // Object-safe counterpart of Element used by AnyElement to store elements as trait objects.
 trait ElementObject<V> {
     fn declared_style(&mut self) -> &mut StyleRefinement;
@@ -516,19 +528,6 @@ impl<V: 'static> AnyElement<V> {
         Ok(node_id)
     }
 
-    pub fn push_text_style<'a: 'b, 'b>(&mut self, cx: &mut impl RenderContext<'a, 'b, V>) -> bool {
-        let text_style = self
-            .element
-            .computed_style(cx.as_view_context())
-            .text_style();
-        if let Some(text_style) = text_style {
-            cx.push_text_style(cx.text_style().refined(&text_style));
-            true
-        } else {
-            false
-        }
-    }
-
     pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) -> Result<()> {
         let pushed_text_style = self.push_text_style(cx);
 
@@ -586,6 +585,19 @@ impl<V: 'static> AnyElement<V> {
 
         Ok(())
     }
+
+    fn push_text_style<'a: 'b, 'b>(&mut self, cx: &mut impl RenderContext<'a, 'b, V>) -> bool {
+        let text_style = self
+            .element
+            .computed_style(cx.as_view_context())
+            .text_style();
+        if let Some(text_style) = text_style {
+            cx.push_text_style(cx.text_style().refined(&text_style));
+            true
+        } else {
+            false
+        }
+    }
 }
 
 impl<V: 'static> Element<V> for AnyElement<V> {

crates/gpui/playground/src/frame.rs 🔗

@@ -1,6 +1,7 @@
 use crate::{
     element::{
-        AnyElement, Element, EventHandler, IntoElement, Layout, LayoutContext, NodeId, PaintContext,
+        AnyElement, Element, EventHandler, IntoElement, Layout, LayoutContext, NodeId,
+        PaintContext, ParentElement,
     },
     style::{Style, StyleRefinement},
 };
@@ -65,13 +66,13 @@ impl<V: 'static> Element<V> for Frame<V> {
     }
 }
 
-impl<V: 'static> Frame<V> {
-    pub fn child(mut self, child: impl IntoElement<V>) -> Self {
+impl<V: 'static> ParentElement<V> for Frame<V> {
+    fn child(mut self, child: impl IntoElement<V>) -> Self {
         self.children.push(child.into_any_element());
         self
     }
 
-    pub fn children<I, E>(mut self, children: I) -> Self
+    fn children<I, E>(mut self, children: I) -> Self
     where
         I: IntoIterator<Item = E>,
         E: IntoElement<V>,

crates/gpui/playground/src/hoverable.rs 🔗

@@ -7,11 +7,15 @@ use gpui::{
 };
 use refineable::Refineable;
 
-use crate::{element::Element, style::StyleRefinement};
+use crate::{
+    element::{Element, ParentElement},
+    style::StyleRefinement,
+};
 
 pub struct Hoverable<V, E> {
     hover_style: StyleRefinement,
     computed_style: Option<StyleRefinement>,
+    hovered: Rc<Cell<bool>>,
     view_type: PhantomData<V>,
     child: E,
 }
@@ -21,6 +25,7 @@ impl<V, E> Hoverable<V, E> {
         Self {
             hover_style: StyleRefinement::default(),
             computed_style: None,
+            hovered: Default::default(),
             view_type: PhantomData,
             child,
         }
@@ -37,8 +42,9 @@ impl<V: 'static, E: Element<V>> Element<V> for Hoverable<V, E> {
     fn computed_style(&mut self, cx: &mut ViewContext<V>) -> &StyleRefinement {
         self.computed_style.get_or_insert_with(|| {
             let mut style = self.child.computed_style(cx).clone();
-            // if hovered
-            style.refine(&self.hover_style);
+            if self.hovered.get() {
+                style.refine(&self.hover_style);
+            }
             style
         })
     }
@@ -63,17 +69,24 @@ impl<V: 'static, E: Element<V>> Element<V> for Hoverable<V, E> {
     ) -> anyhow::Result<()> {
         let EngineLayout { bounds, order } = layout.from_engine;
         let window_bounds = RectF::new(Vector2F::zero(), cx.window_size());
-        let was_hovered = Rc::new(Cell::new(false));
+        let hovered = self.hovered.clone();
 
         self.child.paint(layout, view, cx)?;
+
+        let mouse_within_bounds = bounds.contains_point(cx.mouse_position());
+        if mouse_within_bounds != hovered.get() {
+            hovered.set(mouse_within_bounds);
+            cx.repaint();
+        }
+
         cx.draw_interactive_region(
             order,
             window_bounds,
             false,
             move |view, event: &MouseMove, cx| {
-                let is_hovered = bounds.contains_point(cx.mouse_position());
-                if is_hovered != was_hovered.get() {
-                    was_hovered.set(is_hovered);
+                let mouse_within_bounds = bounds.contains_point(cx.mouse_position());
+                if mouse_within_bounds != hovered.get() {
+                    hovered.set(mouse_within_bounds);
                     cx.repaint();
                 }
             },
@@ -81,3 +94,23 @@ impl<V: 'static, E: Element<V>> Element<V> for Hoverable<V, E> {
         Ok(())
     }
 }
+
+impl<V: 'static, P: ParentElement<V>> ParentElement<V> for Hoverable<V, P> {
+    fn child(mut self, child: impl crate::element::IntoElement<V>) -> Self
+    where
+        Self: Sized,
+    {
+        self.child = self.child.child(child);
+        self
+    }
+
+    fn children<I, E>(mut self, children: I) -> Self
+    where
+        Self: Sized,
+        I: IntoIterator<Item = E>,
+        E: crate::element::IntoElement<V>,
+    {
+        self.child = self.child.children(children);
+        self
+    }
+}

crates/gpui/playground/src/playground.rs 🔗

@@ -1,7 +1,7 @@
 #![allow(dead_code, unused_variables)]
 use color::black;
 use components::button;
-use element::Element;
+use element::{Element, ParentElement};
 use frame::frame;
 use gpui::{
     geometry::{rect::RectF, vector::vec2f},
@@ -50,6 +50,8 @@ fn playground<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
         .h_full()
         .w_half()
         .fill(theme.success(0.5))
+        .hover()
+        .fill(theme.error(0.5))
         .child(button().label("Hello").click(|_, _, _| println!("click!")))
 }
 

crates/gpui/src/app.rs 🔗

@@ -1890,6 +1890,7 @@ impl AppContext {
 
     fn handle_repaint_window_effect(&mut self, window: AnyWindowHandle) {
         self.update_window(window, |cx| {
+            cx.layout(false).log_err();
             if let Some(scene) = cx.paint().log_err() {
                 cx.window.platform_window.present_scene(scene);
             }
@@ -3655,12 +3656,6 @@ impl<'a, 'b, 'c, V: 'static> EventContext<'a, 'b, 'c, V> {
     pub fn propagate_event(&mut self) {
         self.handled = false;
     }
-
-    pub fn repaint(&mut self) {
-        let window = self.window();
-        self.pending_effects
-            .push_back(Effect::RepaintWindow { window });
-    }
 }
 
 impl<'a, 'b, 'c, V> Deref for EventContext<'a, 'b, 'c, V> {

crates/gpui/src/app/window.rs 🔗

@@ -220,6 +220,12 @@ impl<'a> WindowContext<'a> {
         }
     }
 
+    pub fn repaint(&mut self) {
+        let window = self.window();
+        self.pending_effects
+            .push_back(Effect::RepaintWindow { window });
+    }
+
     pub fn layout_engine(&mut self) -> Option<&mut LayoutEngine> {
         self.window.layout_engines.last_mut()
     }