Compiling checkpoint

Nathan Sobo created

Change summary

crates/gpui/playground/src/div.rs                    | 173 +++++++++++--
crates/gpui/playground/src/frame.rs                  |   4 
crates/gpui/playground/src/layout_context.rs         |  61 ++++
crates/gpui/playground/src/paint_context.rs          |  14 
crates/gpui/playground/src/playground.rs             |   1 
crates/gpui/playground_macros/src/styleable_trait.rs |   2 
crates/gpui/src/app.rs                               |  15 +
crates/gpui/src/app/window.rs                        |  20 +
crates/gpui/src/geometry.rs                          |  14 
crates/gpui/src/gpui.rs                              |   2 
crates/refineable/src/refineable.rs                  |   2 
11 files changed, 261 insertions(+), 47 deletions(-)

Detailed changes

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

@@ -1,57 +1,180 @@
-use crate::style::StyleRefinement;
+use std::{marker::PhantomData, rc::Rc};
+
+use crate::element::{AnyElement, PaintContext};
+use crate::layout_context::LayoutContext;
+use crate::style::{Style, StyleRefinement};
+use anyhow::Result;
+use derive_more::{Deref, DerefMut};
+use gpui::EngineLayout;
+use gpui::{geometry::rect::RectF, platform::MouseMovedEvent, EventContext};
 use playground_macros::styleable_trait;
 use refineable::Refineable;
+use smallvec::SmallVec;
+use util::ResultExt;
+
+type LayoutId = gpui::LayoutId;
+
+#[derive(Deref, DerefMut)]
+pub struct Layout<V, E: Element<V>> {
+    id: LayoutId,
+    engine_layout: Option<EngineLayout>,
+    #[deref]
+    #[deref_mut]
+    element_data: E::Layout,
+}
+
+impl<V: 'static, E: Element<V>> Layout<V, E> {
+    pub fn new(id: LayoutId, engine_layout: Option<EngineLayout>, element_data: E::Layout) -> Self {
+        Self {
+            id,
+            engine_layout,
+            element_data,
+        }
+    }
+
+    pub fn bounds(&mut self, cx: &mut PaintContext<V>) -> RectF {
+        self.engine_layout(cx).bounds
+    }
+
+    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())
+    }
+}
 
-trait Element<V> {
-    type Style;
+pub trait Element<V> {
+    type Layout;
 
-    fn hover(self) -> Hover<V, Self>
+    fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<Layout<V, Self>>
     where
-        Self: Sized,
-        Self::Style: Refineable,
-        <Self::Style as Refineable>::Refinement: Default,
+        Self: Sized;
+
+    fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>)
+    where
+        Self: Sized;
+
+    /// ## Helpers
+
+    fn hoverable(self) -> Hoverable<V, Self>
+    where
+        Self: Styleable + Sized,
     {
-        Hover {
-            child: self,
-            style: <<Self as Element<V>>::Style as Refineable>::Refinement::default(),
-        }
+        hoverable(self)
     }
 }
 
 use crate as playground;
 styleable_trait!();
 
-struct Hover<V, E: Element<V>>
-where
-    E::Style: Refineable,
-{
-    child: E,
-    style: <E::Style as Refineable>::Refinement,
-}
-
-struct Div {
+pub struct Div<V> {
     style: StyleRefinement,
+    children: SmallVec<[AnyElement<V>; 2]>,
 }
 
-impl Styleable for Div {
+impl<V> Styleable for Div<V> {
+    type Style = Style;
+
     fn declared_style(&mut self) -> &mut StyleRefinement {
         &mut self.style
     }
 }
 
-fn div() -> Div {
+pub fn div<V>() -> Div<V> {
     Div {
         style: Default::default(),
+        children: Default::default(),
+    }
+}
+
+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>>
+    where
+        Self: Sized,
+    {
+        let children = self
+            .children
+            .iter_mut()
+            .map(|child| child.layout(view, cx))
+            .collect::<Result<Vec<LayoutId>>>()?;
+
+        cx.add_layout_node(self.style(), (), children)
+    }
+
+    fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>)
+    where
+        Self: Sized,
+    {
+        todo!()
+    }
+}
+
+pub struct Hoverable<V, E: Element<V> + Styleable> {
+    default_style: Style,
+    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
     }
 }
 
-impl<V> Element<V> for Div {
-    type Style = StyleRefinement;
+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_style: Default::default(),
+        child,
+        view_type: PhantomData,
+    }
+}
+
+impl<V, E: Element<V> + Styleable> Styleable for Hoverable<V, E> {
+    type Style = E::Style;
+
+    fn declared_style(&mut self) -> &mut playground::style::StyleRefinement {
+        self.child.declared_style()
+    }
+}
+
+impl<V, 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>>
+    where
+        Self: Sized,
+    {
+        todo!()
+    }
+
+    fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>)
+    where
+        Self: Sized,
+    {
+        todo!()
+    }
 }
 
 #[test]
 fn test() {
-    let elt = div().w_auto();
+    // let elt = div().w_auto();
 }
 
 // trait Element<V: 'static> {

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

@@ -6,7 +6,7 @@ use crate::{
     style::{Style, StyleRefinement},
 };
 use anyhow::{anyhow, Result};
-use gpui::LayoutNodeId;
+use gpui::LayoutId;
 use playground_macros::IntoElement;
 use refineable::Refineable;
 
@@ -46,7 +46,7 @@ impl<V: 'static> Element<V> for Frame<V> {
             .children
             .iter_mut()
             .map(|child| child.layout(view, cx))
-            .collect::<Result<Vec<LayoutNodeId>>>()?;
+            .collect::<Result<Vec<LayoutId>>>()?;
 
         let rem_size = cx.rem_pixels();
         let style = Style::default().refined(&self.style);

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

@@ -0,0 +1,61 @@
+use anyhow::{anyhow, Result};
+use derive_more::{Deref, DerefMut};
+pub use gpui::LayoutContext as LegacyLayoutContext;
+use gpui::{RenderContext, ViewContext};
+pub use taffy::tree::NodeId;
+
+use crate::{
+    div::{Element, Layout},
+    style::Style,
+};
+
+#[derive(Deref, DerefMut)]
+pub struct LayoutContext<'a, 'b, 'c, 'd, V> {
+    #[deref]
+    #[deref_mut]
+    pub(crate) legacy_cx: &'d mut LegacyLayoutContext<'a, 'b, 'c, V>,
+    pub(crate) scene: &'d mut gpui::SceneBuilder,
+}
+
+impl<'a, 'b, V> RenderContext<'a, 'b, V> for LayoutContext<'a, 'b, '_, '_, V> {
+    fn text_style(&self) -> gpui::fonts::TextStyle {
+        self.legacy_cx.text_style()
+    }
+
+    fn push_text_style(&mut self, style: gpui::fonts::TextStyle) {
+        self.legacy_cx.push_text_style(style)
+    }
+
+    fn pop_text_style(&mut self) {
+        self.legacy_cx.pop_text_style()
+    }
+
+    fn as_view_context(&mut self) -> &mut ViewContext<'a, 'b, V> {
+        &mut self.view_context
+    }
+}
+
+impl<'a, 'b, 'c, 'd, V: 'static> LayoutContext<'a, 'b, 'c, 'd, V> {
+    pub fn new(
+        legacy_cx: &'d mut LegacyLayoutContext<'a, 'b, 'c, V>,
+        scene: &'d mut gpui::SceneBuilder,
+    ) -> Self {
+        Self { legacy_cx, scene }
+    }
+
+    pub fn add_layout_node<E: Element<V>>(
+        &mut self,
+        style: Style,
+        element_data: E::Layout,
+        children: impl IntoIterator<Item = NodeId>,
+    ) -> Result<Layout<V, E>> {
+        let rem_size = self.rem_pixels();
+        let id = self
+            .legacy_cx
+            .layout_engine()
+            .ok_or_else(|| anyhow!("no layout engine"))?
+            .add_node(style.to_taffy(rem_size), children)?;
+
+        Ok(Layout::new(id, None, element_data))
+    }
+}

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

@@ -1,8 +1,10 @@
-use std::{any::TypeId, rc::Rc};
-
+use anyhow::{anyhow, Result};
 use derive_more::{Deref, DerefMut};
-use gpui::{geometry::rect::RectF, EventContext, RenderContext, ViewContext};
+use gpui::{
+    geometry::rect::RectF, EngineLayout, EventContext, LayoutId, RenderContext, ViewContext,
+};
 pub use gpui::{LayoutContext, PaintContext as LegacyPaintContext};
+use std::{any::TypeId, rc::Rc};
 pub use taffy::tree::NodeId;
 
 #[derive(Deref, DerefMut)]
@@ -66,4 +68,10 @@ impl<'a, 'b, 'c, 'd, V: 'static> PaintContext<'a, 'b, 'c, 'd, V> {
                 view_id: self.view_id(),
             });
     }
+
+    pub(crate) fn computed_layout(&mut self, layout_id: LayoutId) -> Result<EngineLayout> {
+        self.layout_engine()
+            .ok_or_else(|| anyhow!("no layout engine present"))?
+            .computed_layout(layout_id)
+    }
 }

crates/gpui/playground_macros/src/styleable_trait.rs 🔗

@@ -109,6 +109,8 @@ pub fn styleable_trait(_item: TokenStream) -> TokenStream {
 
     let output = quote! {
         pub trait Styleable {
+            type Style: refineable::Refineable;
+
             fn declared_style(&mut self) -> &mut playground::style::StyleRefinement;
 
             fn style(&mut self) -> playground::style::Style {

crates/gpui/src/app.rs 🔗

@@ -3392,7 +3392,8 @@ pub trait RenderContext<'a, 'b, V> {
 }
 
 pub struct LayoutContext<'a, 'b, 'c, V> {
-    view_context: &'c mut ViewContext<'a, 'b, V>,
+    // Nathan: Making this is public while I work on playground.
+    pub view_context: &'c mut ViewContext<'a, 'b, V>,
     new_parents: &'c mut HashMap<usize, usize>,
     views_to_notify_if_ancestors_change: &'c mut HashMap<usize, SmallVec<[usize; 2]>>,
     text_style_stack: Vec<TextStyle>,
@@ -3643,6 +3644,9 @@ impl<V> BorrowWindowContext for PaintContext<'_, '_, '_, V> {
 pub struct EventContext<'a, 'b, 'c, V> {
     view_context: &'c mut ViewContext<'a, 'b, V>,
     pub(crate) handled: bool,
+    // I would like to replace handled with this.
+    // Being additive for now.
+    pub bubble: bool,
 }
 
 impl<'a, 'b, 'c, V: 'static> EventContext<'a, 'b, 'c, V> {
@@ -3650,12 +3654,21 @@ impl<'a, 'b, 'c, V: 'static> EventContext<'a, 'b, 'c, V> {
         EventContext {
             view_context,
             handled: true,
+            bubble: false,
         }
     }
 
     pub fn propagate_event(&mut self) {
         self.handled = false;
     }
+
+    pub fn bubble_event(&mut self) {
+        self.bubble = true;
+    }
+
+    pub fn event_bubbled(&self) -> bool {
+        self.bubble
+    }
 }
 
 impl<'a, 'b, 'c, V> Deref for EventContext<'a, 'b, 'c, V> {

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

@@ -1293,16 +1293,16 @@ impl LayoutEngine {
         Default::default()
     }
 
-    pub fn add_node<C>(&mut self, style: LayoutStyle, children: C) -> Result<LayoutNodeId>
+    pub fn add_node<C>(&mut self, style: LayoutStyle, children: C) -> Result<LayoutId>
     where
-        C: IntoIterator<Item = LayoutNodeId>,
+        C: IntoIterator<Item = LayoutId>,
     {
         Ok(self
             .0
             .new_with_children(style, &children.into_iter().collect::<Vec<_>>())?)
     }
 
-    pub fn add_measured_node<F>(&mut self, style: LayoutStyle, measure: F) -> Result<LayoutNodeId>
+    pub fn add_measured_node<F>(&mut self, style: LayoutStyle, measure: F) -> Result<LayoutId>
     where
         F: Fn(MeasureParams) -> Size<f32> + Sync + Send + 'static,
     {
@@ -1311,7 +1311,7 @@ impl LayoutEngine {
             .new_leaf_with_measure(style, MeasureFunc::Boxed(Box::new(MeasureFn(measure))))?)
     }
 
-    pub fn compute_layout(&mut self, root: LayoutNodeId, available_space: Vector2F) -> Result<()> {
+    pub fn compute_layout(&mut self, root: LayoutId, available_space: Vector2F) -> Result<()> {
         self.0.compute_layout(
             root,
             taffy::geometry::Size {
@@ -1322,7 +1322,7 @@ impl LayoutEngine {
         Ok(())
     }
 
-    pub fn computed_layout(&mut self, node: LayoutNodeId) -> Result<EngineLayout> {
+    pub fn computed_layout(&mut self, node: LayoutId) -> Result<EngineLayout> {
         Ok(self.0.layout(node)?.into())
     }
 }
@@ -1346,7 +1346,7 @@ where
     }
 }
 
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Default)]
 pub struct EngineLayout {
     pub bounds: RectF,
     pub order: u32,
@@ -1367,6 +1367,12 @@ pub enum AvailableSpace {
     MaxContent,
 }
 
+impl Default for AvailableSpace {
+    fn default() -> Self {
+        Self::Pixels(0.)
+    }
+}
+
 impl From<taffy::prelude::AvailableSpace> for AvailableSpace {
     fn from(value: taffy::prelude::AvailableSpace) -> Self {
         match value {
@@ -1389,7 +1395,7 @@ impl From<&taffy::tree::Layout> for EngineLayout {
     }
 }
 
-pub type LayoutNodeId = taffy::prelude::NodeId;
+pub type LayoutId = taffy::prelude::NodeId;
 
 pub struct RenderParams {
     pub view_id: usize,

crates/gpui/src/geometry.rs 🔗

@@ -134,12 +134,12 @@ impl ToJson for RectF {
 }
 
 #[derive(Refineable)]
-pub struct Point<T: Clone> {
+pub struct Point<T: Clone + Default> {
     pub x: T,
     pub y: T,
 }
 
-impl<T: Clone> Clone for Point<T> {
+impl<T: Clone + Default> Clone for Point<T> {
     fn clone(&self) -> Self {
         Self {
             x: self.x.clone(),
@@ -148,7 +148,7 @@ impl<T: Clone> Clone for Point<T> {
     }
 }
 
-impl<T: Clone> Into<taffy::geometry::Point<T>> for Point<T> {
+impl<T: Clone + Default> Into<taffy::geometry::Point<T>> for Point<T> {
     fn into(self) -> taffy::geometry::Point<T> {
         taffy::geometry::Point {
             x: self.x,
@@ -158,12 +158,12 @@ impl<T: Clone> Into<taffy::geometry::Point<T>> for Point<T> {
 }
 
 #[derive(Clone, Refineable)]
-pub struct Size<T: Clone> {
+pub struct Size<T: Clone + Default> {
     pub width: T,
     pub height: T,
 }
 
-impl<S, T: Clone> From<taffy::geometry::Size<S>> for Size<T>
+impl<S, T: Clone + Default> From<taffy::geometry::Size<S>> for Size<T>
 where
     S: Into<T>,
 {
@@ -175,7 +175,7 @@ where
     }
 }
 
-impl<S, T: Clone> Into<taffy::geometry::Size<S>> for Size<T>
+impl<S, T: Clone + Default> Into<taffy::geometry::Size<S>> for Size<T>
 where
     T: Into<S>,
 {
@@ -223,7 +223,7 @@ impl Size<Length> {
 }
 
 #[derive(Clone, Default, Refineable)]
-pub struct Edges<T: Clone> {
+pub struct Edges<T: Clone + Default> {
     pub top: T,
     pub right: T,
     pub bottom: T,

crates/gpui/src/gpui.rs 🔗

@@ -28,7 +28,7 @@ pub mod keymap_matcher;
 pub mod platform;
 pub use gpui_macros::{test, Element};
 pub use window::{
-    Axis, EngineLayout, LayoutEngine, LayoutNodeId, RectFExt, SizeConstraint, Vector2FExt,
+    Axis, EngineLayout, LayoutEngine, LayoutId, RectFExt, SizeConstraint, Vector2FExt,
     WindowContext,
 };
 

crates/refineable/src/refineable.rs 🔗

@@ -1,7 +1,7 @@
 pub use derive_refineable::Refineable;
 
 pub trait Refineable {
-    type Refinement;
+    type Refinement: Default;
 
     fn refine(&mut self, refinement: &Self::Refinement);
     fn refined(mut self, refinement: &Self::Refinement) -> Self