Checkpoint

Nathan Sobo created

Change summary

crates/gpui/playground/src/div.rs                   |  26 +-
crates/gpui/playground/src/element.rs               | 136 +++++++-------
crates/gpui/playground/src/hoverable.rs             |  26 +-
crates/gpui/playground/src/layout_context.rs        |  19 -
crates/gpui/playground/src/paint_context.rs         |   4 
crates/gpui/playground/src/pressable.rs             |  20 +
crates/gpui/playground/src/text.rs                  |  70 ++++---
crates/gpui/playground_macros/src/derive_element.rs |  11 
crates/gpui/src/app/window.rs                       |   8 
crates/gpui/src/gpui.rs                             |   5 
10 files changed, 165 insertions(+), 160 deletions(-)

Detailed changes

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

@@ -25,9 +25,13 @@ pub fn div<V>() -> Div<V> {
 }
 
 impl<V: 'static> Element<V> for Div<V> {
-    type Layout = ();
+    type PaintState = ();
 
-    fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<Layout<V, ()>>
+    fn layout(
+        &mut self,
+        view: &mut V,
+        cx: &mut LayoutContext<V>,
+    ) -> Result<(LayoutId, Self::PaintState)>
     where
         Self: Sized,
     {
@@ -47,14 +51,16 @@ impl<V: 'static> Element<V> for Div<V> {
             cx.pop_text_style();
         }
 
-        let layout = cx.add_layout_node(style, (), children.clone())?;
-
-        dbg!(layout.id(), children);
-        Ok(layout)
+        Ok((cx.add_layout_node(style, children)?, ()))
     }
 
-    fn paint(&mut self, view: &mut V, layout: &mut Layout<V, ()>, cx: &mut PaintContext<V>)
-    where
+    fn paint(
+        &mut self,
+        view: &mut V,
+        layout: &Layout,
+        paint_state: &mut Self::PaintState,
+        cx: &mut PaintContext<V>,
+    ) where
         Self: Sized,
     {
         let style = &self.computed_style();
@@ -62,9 +68,9 @@ impl<V: 'static> Element<V> for Div<V> {
             cx.push_text_style(cx.text_style().clone().refined(&style));
             true
         });
-        style.paint_background(layout.bounds(cx), cx);
+        style.paint_background(layout.bounds, cx);
         self.interaction_handlers()
-            .paint(layout.order(cx), layout.bounds(cx), cx);
+            .paint(layout.order, layout.bounds, cx);
         for child in &mut self.children {
             child.paint(view, cx);
         }

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

@@ -1,29 +1,25 @@
-use anyhow::Result;
-use gpui::{geometry::rect::RectF, EngineLayout};
-use smallvec::SmallVec;
-use std::marker::PhantomData;
-use util::ResultExt;
-
 pub use crate::layout_context::LayoutContext;
 pub use crate::paint_context::PaintContext;
-
-type LayoutId = gpui::LayoutId;
+use anyhow::Result;
+pub use gpui::{Layout, LayoutId};
+use smallvec::SmallVec;
 
 pub trait Element<V: 'static>: 'static {
-    type Layout;
+    type PaintState;
 
     fn layout(
         &mut self,
         view: &mut V,
         cx: &mut LayoutContext<V>,
-    ) -> Result<Layout<V, Self::Layout>>
+    ) -> Result<(LayoutId, Self::PaintState)>
     where
         Self: Sized;
 
     fn paint(
         &mut self,
         view: &mut V,
-        layout: &mut Layout<V, Self::Layout>,
+        layout: &Layout,
+        state: &mut Self::PaintState,
         cx: &mut PaintContext<V>,
     ) where
         Self: Sized;
@@ -34,7 +30,7 @@ pub trait Element<V: 'static>: 'static {
     {
         AnyElement(Box::new(StatefulElement {
             element: self,
-            layout: None,
+            phase: ElementPhase::Init,
         }))
     }
 }
@@ -48,24 +44,71 @@ trait AnyStatefulElement<V> {
 /// A wrapper around an element that stores its layout state.
 struct StatefulElement<V: 'static, E: Element<V>> {
     element: E,
-    layout: Option<Layout<V, E::Layout>>,
+    phase: ElementPhase<V, E>,
+}
+
+enum ElementPhase<V: 'static, E: Element<V>> {
+    Init,
+    PostLayout {
+        layout_id: LayoutId,
+        paint_state: E::PaintState,
+    },
+    PostPaint {
+        layout: Layout,
+        paint_state: E::PaintState,
+    },
+    Error(String),
+}
+
+impl<V: 'static, E: Element<V>> Default for ElementPhase<V, E> {
+    fn default() -> Self {
+        Self::Init
+    }
 }
 
 /// We blanket-implement the object-safe ElementStateObject interface to make ElementStates into trait objects
 impl<V, E: Element<V>> AnyStatefulElement<V> for StatefulElement<V, E> {
     fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId> {
-        let layout = self.element.layout(view, cx)?;
-        let layout_id = layout.id;
-        self.layout = Some(layout);
-        Ok(layout_id)
+        let result;
+        self.phase = match std::mem::take(&mut self.phase) {
+            ElementPhase::Init => match self.element.layout(view, cx) {
+                Ok((layout_id, paint_state)) => {
+                    result = Ok(layout_id);
+                    ElementPhase::PostLayout {
+                        layout_id,
+                        paint_state,
+                    }
+                }
+                Err(error) => {
+                    let message = error.to_string();
+                    result = Err(error);
+                    ElementPhase::Error(message)
+                }
+            },
+            _ => panic!("invalid element phase to call layout"),
+        };
+
+        result
     }
 
     fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) {
-        let layout = self.layout.as_mut().expect("paint called before layout");
-        if layout.engine_layout.is_none() {
-            layout.engine_layout = dbg!(cx.computed_layout(dbg!(layout.id)).log_err())
-        }
-        self.element.paint(view, layout, cx)
+        self.phase = match std::mem::take(&mut self.phase) {
+            ElementPhase::PostLayout {
+                layout_id,
+                mut paint_state,
+            } => match cx.computed_layout(layout_id) {
+                Ok(layout) => {
+                    self.element.paint(view, &layout, &mut paint_state, cx);
+                    ElementPhase::PostPaint {
+                        layout,
+                        paint_state,
+                    }
+                }
+                Err(error) => ElementPhase::Error(error.to_string()),
+            },
+            phase @ ElementPhase::Error(_) => phase,
+            _ => panic!("invalid element phase to call paint"),
+        };
     }
 }
 
@@ -82,55 +125,6 @@ impl<V> AnyElement<V> {
     }
 }
 
-pub struct Layout<V, D> {
-    id: LayoutId,
-    engine_layout: Option<EngineLayout>,
-    element_data: Option<D>,
-    view_type: PhantomData<V>,
-}
-
-impl<V: 'static, D> Layout<V, D> {
-    pub fn new(id: LayoutId, element_data: D) -> Self {
-        Self {
-            id,
-            engine_layout: None,
-            element_data: Some(element_data),
-            view_type: PhantomData,
-        }
-    }
-
-    pub fn id(&self) -> LayoutId {
-        self.id
-    }
-
-    pub fn bounds(&mut self, cx: &mut PaintContext<V>) -> RectF {
-        self.engine_layout(cx).bounds
-    }
-
-    pub fn order(&mut self, cx: &mut PaintContext<V>) -> u32 {
-        self.engine_layout(cx).order
-    }
-
-    pub fn update<F, T>(&mut self, update: F) -> T
-    where
-        F: FnOnce(&mut Self, &mut D) -> T,
-    {
-        self.element_data
-            .take()
-            .map(|mut element_data| {
-                let result = update(self, &mut element_data);
-                self.element_data = Some(element_data);
-                result
-            })
-            .expect("reentrant calls to Layout::update are not supported")
-    }
-
-    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())
-    }
-}
-
 pub trait ParentElement<V: 'static> {
     fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]>;
 

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

@@ -6,7 +6,7 @@ use crate::{
     style::{Style, StyleHelpers, Styleable},
 };
 use anyhow::Result;
-use gpui::platform::MouseMovedEvent;
+use gpui::{platform::MouseMovedEvent, LayoutId};
 use refineable::{CascadeSlot, Refineable, RefinementCascade};
 use smallvec::SmallVec;
 use std::{cell::Cell, rc::Rc};
@@ -40,40 +40,44 @@ impl<E: Styleable> Styleable for Hoverable<E> {
 }
 
 impl<V: 'static, E: Element<V> + Styleable> Element<V> for Hoverable<E> {
-    type Layout = E::Layout;
+    type PaintState = E::PaintState;
 
-    fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<Layout<V, Self::Layout>>
+    fn layout(
+        &mut self,
+        view: &mut V,
+        cx: &mut LayoutContext<V>,
+    ) -> Result<(LayoutId, Self::PaintState)>
     where
         Self: Sized,
     {
-        self.child.layout(view, cx)
+        Ok(self.child.layout(view, cx)?)
     }
 
     fn paint(
         &mut self,
         view: &mut V,
-        layout: &mut Layout<V, Self::Layout>,
+        layout: &Layout,
+        paint_state: &mut Self::PaintState,
         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()));
+        self.hovered
+            .set(layout.bounds.contains_point(cx.mouse_position()));
 
         let slot = self.cascade_slot;
         let style = self.hovered.get().then_some(self.hovered_style.clone());
         self.style_cascade().set(slot, style);
 
         let hovered = self.hovered.clone();
-        cx.on_event(order, move |view, event: &MouseMovedEvent, cx| {
+        let bounds = layout.bounds;
+        cx.on_event(layout.order, move |view, event: &MouseMovedEvent, cx| {
             if bounds.contains_point(cx.mouse_position()) != hovered.get() {
                 cx.repaint();
             }
         });
 
-        self.child.paint(view, layout, cx);
+        self.child.paint(view, layout, paint_state, cx);
     }
 }
 

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

@@ -1,10 +1,9 @@
+use crate::{element::LayoutId, style::Style};
 use anyhow::{anyhow, Result};
 use derive_more::{Deref, DerefMut};
 use gpui::{geometry::Size, MeasureParams, RenderContext, ViewContext};
 pub use gpui::{taffy::tree::NodeId, LayoutContext as LegacyLayoutContext};
 
-use crate::{element::Layout, style::Style};
-
 #[derive(Deref, DerefMut)]
 pub struct LayoutContext<'a, 'b, 'c, 'd, V> {
     #[deref]
@@ -35,12 +34,11 @@ impl<'a, 'b, 'c, 'd, V: 'static> LayoutContext<'a, 'b, 'c, 'd, V> {
         Self { legacy_cx }
     }
 
-    pub fn add_layout_node<D>(
+    pub fn add_layout_node(
         &mut self,
         style: Style,
-        element_data: D,
         children: impl IntoIterator<Item = NodeId>,
-    ) -> Result<Layout<V, D>> {
+    ) -> Result<LayoutId> {
         let rem_size = self.rem_pixels();
         let id = self
             .legacy_cx
@@ -48,15 +46,10 @@ impl<'a, 'b, 'c, 'd, V: 'static> LayoutContext<'a, 'b, 'c, 'd, V> {
             .ok_or_else(|| anyhow!("no layout engine"))?
             .add_node(style.to_taffy(rem_size), children)?;
 
-        Ok(Layout::new(id, element_data))
+        Ok(id)
     }
 
-    pub fn add_measured_layout_node<D, F>(
-        &mut self,
-        style: Style,
-        element_data: D,
-        measure: F,
-    ) -> Result<Layout<V, D>>
+    pub fn add_measured_layout_node<F>(&mut self, style: Style, measure: F) -> Result<LayoutId>
     where
         F: Fn(MeasureParams) -> Size<f32> + Sync + Send + 'static,
     {
@@ -66,6 +59,6 @@ impl<'a, 'b, 'c, 'd, V: 'static> LayoutContext<'a, 'b, 'c, 'd, V> {
             .ok_or_else(|| anyhow!("no layout engine"))?
             .add_measured_node(style.to_taffy(rem_size), measure)?;
 
-        Ok(Layout::new(layout_id, element_data))
+        Ok(layout_id)
     }
 }

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

@@ -2,7 +2,7 @@ use anyhow::{anyhow, Result};
 use derive_more::{Deref, DerefMut};
 pub use gpui::taffy::tree::NodeId;
 use gpui::{
-    scene::EventHandler, EngineLayout, EventContext, LayoutId, PaintContext as LegacyPaintContext,
+    scene::EventHandler, EventContext, Layout, LayoutId, PaintContext as LegacyPaintContext,
     RenderContext, ViewContext,
 };
 use std::{any::TypeId, rc::Rc};
@@ -65,7 +65,7 @@ impl<'a, 'b, 'c, 'd, V: 'static> PaintContext<'a, 'b, 'c, 'd, V> {
         })
     }
 
-    pub(crate) fn computed_layout(&mut self, layout_id: LayoutId) -> Result<EngineLayout> {
+    pub(crate) fn computed_layout(&mut self, layout_id: LayoutId) -> Result<Layout> {
         self.layout_engine()
             .ok_or_else(|| anyhow!("no layout engine present"))?
             .computed_layout(layout_id)

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

@@ -6,7 +6,7 @@ use crate::{
     style::{Style, StyleHelpers, Styleable},
 };
 use anyhow::Result;
-use gpui::platform::MouseButtonEvent;
+use gpui::{platform::MouseButtonEvent, LayoutId};
 use refineable::{CascadeSlot, Refineable, RefinementCascade};
 use smallvec::SmallVec;
 use std::{cell::Cell, rc::Rc};
@@ -40,9 +40,13 @@ impl<E: Styleable> Styleable for Pressable<E> {
 }
 
 impl<V: 'static, E: Element<V> + Styleable> Element<V> for Pressable<E> {
-    type Layout = E::Layout;
+    type PaintState = E::PaintState;
 
-    fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<Layout<V, Self::Layout>>
+    fn layout(
+        &mut self,
+        view: &mut V,
+        cx: &mut LayoutContext<V>,
+    ) -> Result<(LayoutId, Self::PaintState)>
     where
         Self: Sized,
     {
@@ -52,7 +56,8 @@ impl<V: 'static, E: Element<V> + Styleable> Element<V> for Pressable<E> {
     fn paint(
         &mut self,
         view: &mut V,
-        layout: &mut Layout<V, Self::Layout>,
+        layout: &Layout,
+        paint_state: &mut Self::PaintState,
         cx: &mut PaintContext<V>,
     ) where
         Self: Sized,
@@ -61,10 +66,9 @@ impl<V: 'static, E: Element<V> + Styleable> Element<V> for Pressable<E> {
         let style = self.pressed.get().then_some(self.pressed_style.clone());
         self.style_cascade().set(slot, style);
 
-        let bounds = layout.bounds(cx);
-        let order = layout.order(cx);
         let pressed = self.pressed.clone();
-        cx.on_event(order, move |view, event: &MouseButtonEvent, cx| {
+        let bounds = layout.bounds;
+        cx.on_event(layout.order, move |view, event: &MouseButtonEvent, cx| {
             if event.is_down {
                 if bounds.contains_point(event.position) {
                     pressed.set(true);
@@ -76,7 +80,7 @@ impl<V: 'static, E: Element<V> + Styleable> Element<V> for Pressable<E> {
             }
         });
 
-        self.child.paint(view, layout, cx);
+        self.child.paint(view, layout, paint_state, cx);
     }
 }
 

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

@@ -4,7 +4,7 @@ use crate::{
     paint_context::PaintContext,
 };
 use anyhow::Result;
-use gpui::{geometry::Size, text_layout::LineLayout, RenderContext};
+use gpui::{geometry::Size, text_layout::LineLayout, LayoutId, RenderContext};
 use parking_lot::Mutex;
 use std::sync::Arc;
 
@@ -21,67 +21,71 @@ pub struct Text {
 }
 
 impl<V: 'static> Element<V> for Text {
-    type Layout = Arc<Mutex<Option<TextLayout>>>;
+    type PaintState = Arc<Mutex<Option<TextLayout>>>;
 
     fn layout(
         &mut self,
         view: &mut V,
         cx: &mut LayoutContext<V>,
-    ) -> Result<Layout<V, Self::Layout>> {
+    ) -> Result<(LayoutId, Self::PaintState)> {
         let rem_size = cx.rem_pixels();
         let fonts = cx.platform().fonts();
         let text_style = cx.text_style();
         let line_height = cx.font_cache().line_height(text_style.font_size);
         let text = self.text.clone();
-        let layout = Arc::new(Mutex::new(None));
-
-        cx.add_measured_layout_node(Default::default(), layout.clone(), move |params| {
-            let line_layout = fonts.layout_line(
-                text.as_ref(),
-                text_style.font_size,
-                &[(text.len(), text_style.to_run())],
-            );
-
-            let size = Size {
-                width: line_layout.width,
-                height: line_height,
-            };
-
-            layout.lock().replace(TextLayout {
-                line_layout: Arc::new(line_layout),
-                line_height,
-            });
-
-            size
-        })
+        let paint_state = Arc::new(Mutex::new(None));
+
+        let layout_id = cx.add_measured_layout_node(Default::default(), {
+            let paint_state = paint_state.clone();
+            move |params| {
+                let line_layout = fonts.layout_line(
+                    text.as_ref(),
+                    text_style.font_size,
+                    &[(text.len(), text_style.to_run())],
+                );
+
+                let size = Size {
+                    width: line_layout.width,
+                    height: line_height,
+                };
+
+                paint_state.lock().replace(TextLayout {
+                    line_layout: Arc::new(line_layout),
+                    line_height,
+                });
+
+                size
+            }
+        });
+
+        Ok((layout_id?, paint_state))
     }
 
     fn paint<'a>(
         &mut self,
         view: &mut V,
-        layout: &mut Layout<V, Self::Layout>,
+        layout: &Layout,
+        paint_state: &mut Self::PaintState,
         cx: &mut PaintContext<V>,
     ) {
-        let element_layout = layout.update(|layout, element_data| element_data.clone());
-
         let line_layout;
         let line_height;
         {
-            let element_layout = element_layout.lock();
-            let element_layout = element_layout
+            let paint_state = paint_state.lock();
+            let paint_state = paint_state
                 .as_ref()
                 .expect("measurement has not been performed");
-            line_layout = element_layout.line_layout.clone();
-            line_height = element_layout.line_height;
+            line_layout = paint_state.line_layout.clone();
+            line_height = paint_state.line_height;
         }
 
         let text_style = cx.text_style();
         let line =
             gpui::text_layout::Line::new(line_layout, &[(self.text.len(), text_style.to_run())]);
 
-        let origin = layout.bounds(cx).origin();
+        let origin = layout.bounds.origin();
         // TODO: We haven't added visible bounds to the new element system yet, so this is a placeholder.
-        let visible_bounds = layout.bounds(cx);
+        let visible_bounds = layout.bounds;
         line.paint(cx.scene, origin, visible_bounds, line_height, cx.legacy_cx);
     }
 }

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

@@ -62,25 +62,26 @@ pub fn derive_element(input: TokenStream) -> TokenStream {
         impl #impl_generics playground::element::Element<#view_type_name> for #type_name #type_generics
         #where_clause
         {
-            type Layout = playground::element::AnyElement<#view_type_name #lifetimes>;
+            type PaintState = playground::element::AnyElement<#view_type_name #lifetimes>;
 
             fn layout(
                 &mut self,
                 view: &mut V,
                 cx: &mut playground::element::LayoutContext<V>,
-            ) -> anyhow::Result<playground::element::Layout<V, Self::Layout>> {
+            ) -> anyhow::Result<(playground::element::LayoutId, Self::PaintState)> {
                 let mut rendered_element = self.render(view, cx).into_any();
                 let layout_id = rendered_element.layout(view, cx)?;
-                Ok(playground::element::Layout::new(layout_id, rendered_element))
+                Ok((layout_id, rendered_element))
             }
 
             fn paint(
                 &mut self,
                 view: &mut V,
-                layout: &mut playground::element::Layout<V, Self::Layout>,
+                layout: &playground::element::Layout,
+                rendered_element: &mut Self::PaintState,
                 cx: &mut playground::element::PaintContext<V>,
             ) {
-                layout.update(|_, rendered_element| rendered_element.paint(view, cx));
+                rendered_element.paint(view, cx);
             }
         }
 

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

@@ -1304,8 +1304,8 @@ impl LayoutEngine {
         Ok(())
     }
 
-    pub fn computed_layout(&mut self, node: LayoutId) -> Result<EngineLayout> {
-        Ok(EngineLayout::from(self.0.layout(node)?))
+    pub fn computed_layout(&mut self, node: LayoutId) -> Result<Layout> {
+        Ok(Layout::from(self.0.layout(node)?))
     }
 }
 
@@ -1329,7 +1329,7 @@ where
 }
 
 #[derive(Debug, Clone, Default)]
-pub struct EngineLayout {
+pub struct Layout {
     pub bounds: RectF,
     pub order: u32,
 }
@@ -1365,7 +1365,7 @@ impl From<taffy::prelude::AvailableSpace> for AvailableSpace {
     }
 }
 
-impl From<&taffy::tree::Layout> for EngineLayout {
+impl From<&taffy::tree::Layout> for Layout {
     fn from(value: &taffy::tree::Layout) -> Self {
         Self {
             bounds: RectF::new(

crates/gpui/src/gpui.rs 🔗

@@ -7,8 +7,8 @@ pub use assets::*;
 pub mod elements;
 pub mod font_cache;
 mod image_data;
-pub use taffy;
 pub use crate::image_data::ImageData;
+pub use taffy;
 pub mod views;
 pub use font_cache::FontCache;
 mod clipboard;
@@ -29,8 +29,7 @@ pub mod keymap_matcher;
 pub mod platform;
 pub use gpui_macros::{test, Element};
 pub use window::{
-    Axis, EngineLayout, LayoutEngine, LayoutId, RectFExt, SizeConstraint, Vector2FExt,
-    WindowContext,
+    Axis, Layout, LayoutEngine, LayoutId, RectFExt, SizeConstraint, Vector2FExt, WindowContext,
 };
 
 pub use anyhow;