Checkpoint

Nathan Sobo created

Change summary

crates/gpui/playground/src/div.rs            | 105 ++++++++++++++-------
crates/gpui/playground/src/layout_context.rs |  11 -
crates/gpui/playground/src/paint_context.rs  |  22 ++++
crates/gpui/src/scene.rs                     |  21 ++++
4 files changed, 114 insertions(+), 45 deletions(-)

Detailed changes

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

@@ -1,3 +1,4 @@
+use std::cell::Cell;
 use std::{marker::PhantomData, rc::Rc};
 
 use crate::element::{AnyElement, PaintContext};
@@ -15,20 +16,22 @@ use util::ResultExt;
 type LayoutId = gpui::LayoutId;
 
 #[derive(Deref, DerefMut)]
-pub struct Layout<V, E: Element<V>> {
+pub struct Layout<V, D> {
     id: LayoutId,
     engine_layout: Option<EngineLayout>,
     #[deref]
     #[deref_mut]
-    element_data: E::Layout,
+    element_data: D,
+    view_type: PhantomData<V>,
 }
 
-impl<V: 'static, E: Element<V>> Layout<V, E> {
-    pub fn new(id: LayoutId, engine_layout: Option<EngineLayout>, element_data: E::Layout) -> Self {
+impl<V: 'static, D> Layout<V, D> {
+    pub fn new(id: LayoutId, engine_layout: Option<EngineLayout>, element_data: D) -> Self {
         Self {
             id,
             engine_layout,
             element_data,
+            view_type: PhantomData,
         }
     }
 
@@ -36,6 +39,10 @@ impl<V: 'static, E: Element<V>> Layout<V, E> {
         self.engine_layout(cx).bounds
     }
 
+    pub fn order(&mut self, cx: &mut PaintContext<V>) -> u32 {
+        self.engine_layout(cx).order
+    }
+
     fn engine_layout(&mut self, cx: &mut PaintContext<'_, '_, '_, '_, V>) -> &mut EngineLayout {
         self.engine_layout
             .get_or_insert_with(|| cx.computed_layout(self.id).log_err().unwrap_or_default())
@@ -45,12 +52,20 @@ impl<V: 'static, E: Element<V>> Layout<V, E> {
 pub trait Element<V> {
     type Layout;
 
-    fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<Layout<V, Self>>
+    fn layout(
+        &mut self,
+        view: &mut V,
+        cx: &mut LayoutContext<V>,
+    ) -> Result<Layout<V, Self::Layout>>
     where
         Self: Sized;
 
-    fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>)
-    where
+    fn paint(
+        &mut self,
+        view: &mut V,
+        layout: &mut Layout<V, Self::Layout>,
+        cx: &mut PaintContext<V>,
+    ) where
         Self: Sized;
 
     /// ## Helpers
@@ -106,7 +121,7 @@ pub fn div<V>() -> Div<V> {
 impl<V: 'static> Element<V> for Div<V> {
     type Layout = ();
 
-    fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<Layout<V, Self>>
+    fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<Layout<V, ()>>
     where
         Self: Sized,
     {
@@ -119,7 +134,7 @@ impl<V: 'static> Element<V> for Div<V> {
         cx.add_layout_node(self.style(), (), children)
     }
 
-    fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>)
+    fn paint(&mut self, view: &mut V, layout: &mut Layout<V, ()>, cx: &mut PaintContext<V>)
     where
         Self: Sized,
     {
@@ -128,35 +143,17 @@ impl<V: 'static> Element<V> for Div<V> {
 }
 
 pub struct Hoverable<V, E: Element<V> + Styleable> {
-    default_style: Style,
+    hovered: Cell<bool>,
+    child_style: StyleRefinement,
     hovered_style: StyleRefinement,
     child: E,
     view_type: PhantomData<V>,
 }
 
-pub trait Interactive<V> {
-    fn declared_interactions(&mut self) -> &mut Interactions<V>;
-
-    fn on_mouse_move<H>(mut self, handler: H) -> Self
-    where
-        H: 'static + Fn(&mut V, &MouseMovedEvent, &mut EventContext<V>),
-        Self: Sized,
-    {
-        self.declared_interactions().mouse_moved = Some(Rc::new(move |view, event, cx| {
-            handler(view, event, cx);
-            cx.bubble
-        }));
-        self
-    }
-}
-
-pub struct Interactions<V> {
-    mouse_moved: Option<Rc<dyn Fn(&mut V, &MouseMovedEvent, &mut EventContext<V>) -> bool>>,
-}
-
 pub fn hoverable<V, E: Element<V> + Styleable>(mut child: E) -> Hoverable<V, E> {
     Hoverable {
-        default_style: child.style(),
+        hovered: Cell::new(false),
+        child_style: child.declared_style().clone(),
         hovered_style: Default::default(),
         child,
         view_type: PhantomData,
@@ -171,24 +168,60 @@ impl<V, E: Element<V> + Styleable> Styleable for Hoverable<V, E> {
     }
 }
 
-impl<V, E: Element<V> + Styleable> Element<V> for Hoverable<V, E> {
+impl<V: 'static, E: Element<V> + Styleable> Element<V> for Hoverable<V, E> {
     type Layout = E::Layout;
 
-    fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<Layout<V, Self>>
+    fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<Layout<V, Self::Layout>>
     where
         Self: Sized,
     {
-        todo!()
+        if self.hovered.get() {
+            // If hovered, refine the child's style with this element's style.
+            self.child.declared_style().refine(&self.hovered_style);
+        } else {
+            // Otherwise, set the child's style back to its original style.
+            *self.child.declared_style() = self.child_style.clone();
+        }
+
+        self.child.layout(view, cx)
     }
 
-    fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>)
+    fn paint(
+        &mut self,
+        view: &mut V,
+        layout: &mut Layout<V, Self::Layout>,
+        cx: &mut PaintContext<V>,
+    ) where
+        Self: Sized,
+    {
+        let bounds = layout.bounds(cx);
+        let order = layout.order(cx);
+        self.hovered.set(bounds.contains_point(cx.mouse_position()));
+        let hovered = self.hovered.clone();
+        cx.on_event(order, move |view, event: &MouseMovedEvent, cx| {});
+    }
+}
+
+pub trait Interactive<V> {
+    fn declared_interactions(&mut self) -> &mut Interactions<V>;
+
+    fn on_mouse_move<H>(mut self, handler: H) -> Self
     where
+        H: 'static + Fn(&mut V, &MouseMovedEvent, &mut EventContext<V>),
         Self: Sized,
     {
-        todo!()
+        self.declared_interactions().mouse_moved = Some(Rc::new(move |view, event, cx| {
+            handler(view, event, cx);
+            cx.bubble
+        }));
+        self
     }
 }
 
+pub struct Interactions<V> {
+    mouse_moved: Option<Rc<dyn Fn(&mut V, &MouseMovedEvent, &mut EventContext<V>) -> bool>>,
+}
+
 #[test]
 fn test() {
     // let elt = div().w_auto();

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

@@ -4,10 +4,7 @@ pub use gpui::LayoutContext as LegacyLayoutContext;
 use gpui::{RenderContext, ViewContext};
 pub use taffy::tree::NodeId;
 
-use crate::{
-    div::{Element, Layout},
-    style::Style,
-};
+use crate::{div::Layout, style::Style};
 
 #[derive(Deref, DerefMut)]
 pub struct LayoutContext<'a, 'b, 'c, 'd, V> {
@@ -43,12 +40,12 @@ impl<'a, 'b, 'c, 'd, V: 'static> LayoutContext<'a, 'b, 'c, 'd, V> {
         Self { legacy_cx, scene }
     }
 
-    pub fn add_layout_node<E: Element<V>>(
+    pub fn add_layout_node<D>(
         &mut self,
         style: Style,
-        element_data: E::Layout,
+        element_data: D,
         children: impl IntoIterator<Item = NodeId>,
-    ) -> Result<Layout<V, E>> {
+    ) -> Result<Layout<V, D>> {
         let rem_size = self.rem_pixels();
         let id = self
             .legacy_cx

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

@@ -1,7 +1,8 @@
 use anyhow::{anyhow, Result};
 use derive_more::{Deref, DerefMut};
 use gpui::{
-    geometry::rect::RectF, EngineLayout, EventContext, LayoutId, RenderContext, ViewContext,
+    geometry::rect::RectF, scene::EventHandler, EngineLayout, EventContext, LayoutId,
+    RenderContext, ViewContext,
 };
 pub use gpui::{LayoutContext, PaintContext as LegacyPaintContext};
 use std::{any::TypeId, rc::Rc};
@@ -41,6 +42,25 @@ impl<'a, 'b, 'c, 'd, V: 'static> PaintContext<'a, 'b, 'c, 'd, V> {
         Self { legacy_cx, scene }
     }
 
+    pub fn on_event<E: 'static>(
+        &mut self,
+        order: u32,
+        handler: impl Fn(&mut V, &E, &mut ViewContext<V>) + 'static,
+    ) {
+        self.scene.event_handlers.push(EventHandler {
+            order,
+            handler: Rc::new(move |view, event, window_cx, view_id| {
+                let mut view_context = ViewContext::mutable(window_cx, view_id);
+                handler(
+                    view.downcast_mut().unwrap(),
+                    event.downcast_ref().unwrap(),
+                    &mut view_context,
+                );
+            }),
+            event_type: TypeId::of::<E>(),
+        })
+    }
+
     pub fn draw_interactive_region<E: 'static>(
         &mut self,
         order: u32,

crates/gpui/src/scene.rs 🔗

@@ -31,8 +31,10 @@ pub struct SceneBuilder {
     scale_factor: f32,
     stacking_contexts: Vec<StackingContext>,
     active_stacking_context_stack: Vec<usize>,
-    /// Used by the playground crate.
+    /// Used by the playground crate. I hope to replace it with event_handlers.
     pub interactive_regions: Vec<InteractiveRegion>,
+    /// Used by the playground crate.
+    pub event_handlers: Vec<EventHandler>,
     #[cfg(debug_assertions)]
     mouse_region_ids: HashSet<MouseRegionId>,
 }
@@ -41,6 +43,7 @@ pub struct Scene {
     scale_factor: f32,
     stacking_contexts: Vec<StackingContext>,
     interactive_regions: Vec<InteractiveRegion>,
+    event_handlers: Vec<EventHandler>,
 }
 
 struct StackingContext {
@@ -282,11 +285,17 @@ impl Scene {
             .collect()
     }
 
+    /// TODO: Hoping to replace this with take_event_handlers
     pub fn take_interactive_regions(&mut self) -> Vec<InteractiveRegion> {
         self.interactive_regions
             .sort_by(|a, b| a.order.cmp(&b.order));
         std::mem::take(&mut self.interactive_regions)
     }
+
+    pub fn take_event_handlers(&mut self) -> Vec<EventHandler> {
+        self.event_handlers.sort_by(|a, b| a.order.cmp(&b.order));
+        std::mem::take(&mut self.event_handlers)
+    }
 }
 
 impl SceneBuilder {
@@ -299,6 +308,7 @@ impl SceneBuilder {
             #[cfg(debug_assertions)]
             mouse_region_ids: Default::default(),
             interactive_regions: Vec::new(),
+            event_handlers: Vec::new(),
         }
     }
 
@@ -309,6 +319,7 @@ impl SceneBuilder {
             scale_factor: self.scale_factor,
             stacking_contexts: self.stacking_contexts,
             interactive_regions: self.interactive_regions,
+            event_handlers: self.event_handlers,
         }
     }
 
@@ -717,6 +728,14 @@ pub struct InteractiveRegion {
     pub view_id: usize,
 }
 
+pub struct EventHandler {
+    pub order: u32,
+    // First param is a dynamic view reference
+    // Second param is a dynamic event reference
+    pub handler: Rc<dyn Fn(&mut dyn Any, &dyn Any, &mut WindowContext, usize)>,
+    pub event_type: TypeId,
+}
+
 fn can_draw(bounds: RectF) -> bool {
     let size = bounds.size();
     size.x() > 0. && size.y() > 0.