WIP

Nathan Sobo created

Change summary

Cargo.lock                                          |   3 
crates/editor/src/editor.rs                         |   1 
crates/gpui/Cargo.toml                              |   2 
crates/gpui/playground/Cargo.toml                   |   1 
crates/gpui/playground/src/div.rs                   |  29 +++
crates/gpui/playground/src/element.rs               |  22 +-
crates/gpui/playground/src/interactive.rs           |   1 
crates/gpui/playground/src/layout_context.rs        |  23 ++
crates/gpui/playground/src/paint_context.rs         |   8 
crates/gpui/playground/src/playground.rs            |  67 +--------
crates/gpui/playground/src/style.rs                 |  30 +++
crates/gpui/playground/src/text.rs                  | 103 ++++++--------
crates/gpui/playground_macros/src/derive_element.rs |   2 
crates/gpui/src/app.rs                              |   1 
crates/gpui/src/app/window.rs                       |   2 
crates/gpui/src/geometry.rs                         |   2 
crates/gpui/src/gpui.rs                             |   1 
17 files changed, 155 insertions(+), 143 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -5119,7 +5119,6 @@ dependencies = [
  "serde",
  "simplelog",
  "smallvec",
- "taffy",
  "util",
 ]
 
@@ -7435,7 +7434,7 @@ dependencies = [
 [[package]]
 name = "taffy"
 version = "0.3.11"
-source = "git+https://github.com/DioxusLabs/taffy?rev=dab541d6104d58e2e10ce90c4a1dad0b703160cd#dab541d6104d58e2e10ce90c4a1dad0b703160cd"
+source = "git+https://github.com/DioxusLabs/taffy?rev=4fb530bdd71609bb1d3f76c6a8bde1ba82805d5e#4fb530bdd71609bb1d3f76c6a8bde1ba82805d5e"
 dependencies = [
  "arrayvec 0.7.4",
  "grid",

crates/editor/src/editor.rs 🔗

@@ -4866,7 +4866,6 @@ impl Editor {
                             if let Some(clipboard_selection) = clipboard_selections.get(ix) {
                                 let end_offset = start_offset + clipboard_selection.len;
                                 to_insert = &clipboard_text[start_offset..end_offset];
-                                dbg!(start_offset, end_offset, &clipboard_text, &to_insert);
                                 entire_line = clipboard_selection.is_entire_line;
                                 start_offset = end_offset + 1;
                                 original_indent_column =

crates/gpui/Cargo.toml 🔗

@@ -48,7 +48,7 @@ serde_derive.workspace = true
 serde_json.workspace = true
 smallvec.workspace = true
 smol.workspace = true
-taffy = { git = "https://github.com/DioxusLabs/taffy", rev = "dab541d6104d58e2e10ce90c4a1dad0b703160cd", features = ["flexbox"] }
+taffy = { git = "https://github.com/DioxusLabs/taffy", rev = "4fb530bdd71609bb1d3f76c6a8bde1ba82805d5e" }
 time.workspace = true
 tiny-skia = "0.5"
 usvg = { version = "0.14", features = [] }

crates/gpui/playground/Cargo.toml 🔗

@@ -19,7 +19,6 @@ refineable.workspace = true
 serde.workspace = true
 simplelog = "0.9"
 smallvec.workspace = true
-taffy = { git = "https://github.com/DioxusLabs/taffy", rev = "dab541d6104d58e2e10ce90c4a1dad0b703160cd", features = ["flexbox"] }
 util = { path = "../../util" }
 
 [dev-dependencies]

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

@@ -6,7 +6,7 @@ use crate::{
     style::{Style, StyleHelpers, Styleable},
 };
 use anyhow::Result;
-use gpui::LayoutId;
+use gpui::{LayoutId, RenderContext};
 use refineable::{Refineable, RefinementCascade};
 use smallvec::SmallVec;
 
@@ -31,27 +31,46 @@ impl<V: 'static> Element<V> for Div<V> {
     where
         Self: Sized,
     {
+        let style = self.computed_style();
+        let pop_text_style = style.text_style().map_or(false, |style| {
+            cx.push_text_style(cx.text_style().clone().refined(&style));
+            true
+        });
+
         let children = self
             .children
             .iter_mut()
             .map(|child| child.layout(view, cx))
             .collect::<Result<Vec<LayoutId>>>()?;
 
-        let style = Style::from_refinement(&self.style_cascade().merged());
-        cx.add_layout_node(style.clone(), (), children)
+        if pop_text_style {
+            cx.pop_text_style();
+        }
+
+        let layout = cx.add_layout_node(style, (), children.clone())?;
+
+        dbg!(layout.id(), children);
+        Ok(layout)
     }
 
     fn paint(&mut self, view: &mut V, layout: &mut Layout<V, ()>, cx: &mut PaintContext<V>)
     where
         Self: Sized,
     {
-        self.computed_style()
-            .paint_background(layout.bounds(cx), cx);
+        let style = &self.computed_style();
+        let pop_text_style = style.text_style().map_or(false, |style| {
+            cx.push_text_style(cx.text_style().clone().refined(&style));
+            true
+        });
+        style.paint_background(layout.bounds(cx), cx);
         self.interaction_handlers()
             .paint(layout.order(cx), layout.bounds(cx), cx);
         for child in &mut self.children {
             child.paint(view, cx);
         }
+        if pop_text_style {
+            cx.pop_text_style();
+        }
     }
 }
 

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

@@ -1,4 +1,4 @@
-use anyhow::{anyhow, Result};
+use anyhow::Result;
 use gpui::{geometry::rect::RectF, EngineLayout};
 use smallvec::SmallVec;
 use std::marker::PhantomData;
@@ -32,7 +32,7 @@ pub trait Element<V: 'static>: 'static {
     where
         Self: 'static + Sized,
     {
-        AnyElement(Box::new(ElementState {
+        AnyElement(Box::new(StatefulElement {
             element: self,
             layout: None,
         }))
@@ -40,19 +40,19 @@ pub trait Element<V: 'static>: 'static {
 }
 
 /// Used to make ElementState<V, E> into a trait object, so we can wrap it in AnyElement<V>.
-trait ElementStateObject<V> {
+trait AnyStatefulElement<V> {
     fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId>;
     fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>);
 }
 
 /// A wrapper around an element that stores its layout state.
-struct ElementState<V: 'static, E: Element<V>> {
+struct StatefulElement<V: 'static, E: Element<V>> {
     element: E,
     layout: Option<Layout<V, E::Layout>>,
 }
 
 /// We blanket-implement the object-safe ElementStateObject interface to make ElementStates into trait objects
-impl<V, E: Element<V>> ElementStateObject<V> for ElementState<V, E> {
+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;
@@ -63,14 +63,14 @@ impl<V, E: Element<V>> ElementStateObject<V> for ElementState<V, E> {
     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 = cx.computed_layout(layout.id).log_err()
+            layout.engine_layout = dbg!(cx.computed_layout(dbg!(layout.id)).log_err())
         }
         self.element.paint(view, layout, cx)
     }
 }
 
 /// A dynamic element.
-pub struct AnyElement<V>(Box<dyn ElementStateObject<V>>);
+pub struct AnyElement<V>(Box<dyn AnyStatefulElement<V>>);
 
 impl<V> AnyElement<V> {
     pub fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId> {
@@ -99,6 +99,10 @@ impl<V: 'static, D> Layout<V, D> {
         }
     }
 
+    pub fn id(&self) -> LayoutId {
+        self.id
+    }
+
     pub fn bounds(&mut self, cx: &mut PaintContext<V>) -> RectF {
         self.engine_layout(cx).bounds
     }
@@ -107,7 +111,7 @@ impl<V: 'static, D> Layout<V, D> {
         self.engine_layout(cx).order
     }
 
-    pub fn update<F, T>(&mut self, update: F) -> Result<T>
+    pub fn update<F, T>(&mut self, update: F) -> T
     where
         F: FnOnce(&mut Self, &mut D) -> T,
     {
@@ -118,7 +122,7 @@ impl<V: 'static, D> Layout<V, D> {
                 self.element_data = Some(element_data);
                 result
             })
-            .ok_or_else(|| anyhow!("reentrant calls to Layout::update are not allowed"))
+            .expect("reentrant calls to Layout::update are not supported")
     }
 
     fn engine_layout(&mut self, cx: &mut PaintContext<'_, '_, '_, '_, V>) -> &mut EngineLayout {

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

@@ -11,6 +11,7 @@ use crate::element::PaintContext;
 pub trait Interactive<V: 'static> {
     fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V>;
 
+    // One line change.
     fn on_mouse_down(
         mut self,
         button: MouseButton,

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

@@ -1,8 +1,7 @@
 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 gpui::{geometry::Size, MeasureParams, RenderContext, ViewContext};
+pub use gpui::{taffy::tree::NodeId, LayoutContext as LegacyLayoutContext};
 
 use crate::{element::Layout, style::Style};
 
@@ -51,4 +50,22 @@ impl<'a, 'b, 'c, 'd, V: 'static> LayoutContext<'a, 'b, 'c, 'd, V> {
 
         Ok(Layout::new(id, element_data))
     }
+
+    pub fn add_measured_layout_node<D, F>(
+        &mut self,
+        style: Style,
+        element_data: D,
+        measure: F,
+    ) -> Result<Layout<V, D>>
+    where
+        F: Fn(MeasureParams) -> Size<f32> + Sync + Send + 'static,
+    {
+        let rem_size = self.rem_pixels();
+        let layout_id = self
+            .layout_engine()
+            .ok_or_else(|| anyhow!("no layout engine"))?
+            .add_measured_node(style.to_taffy(rem_size), measure)?;
+
+        Ok(Layout::new(layout_id, element_data))
+    }
 }

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

@@ -1,9 +1,11 @@
 use anyhow::{anyhow, Result};
 use derive_more::{Deref, DerefMut};
-use gpui::{scene::EventHandler, EngineLayout, EventContext, LayoutId, RenderContext, ViewContext};
-pub use gpui::{LayoutContext, PaintContext as LegacyPaintContext};
+pub use gpui::taffy::tree::NodeId;
+use gpui::{
+    scene::EventHandler, EngineLayout, EventContext, LayoutId, PaintContext as LegacyPaintContext,
+    RenderContext, ViewContext,
+};
 use std::{any::TypeId, rc::Rc};
-pub use taffy::tree::NodeId;
 
 #[derive(Deref, DerefMut)]
 pub struct PaintContext<'a, 'b, 'c, 'd, V> {

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

@@ -1,9 +1,6 @@
 #![allow(dead_code, unused_variables)]
 use crate::{
-    color::black,
-    components::button,
-    element::ParentElement,
-    style::{StyleHelpers, Styleable},
+    color::black, element::ParentElement, style::StyleHelpers, themes::rose_pine::RosePinePalette,
 };
 use element::Element;
 use gpui::{
@@ -51,64 +48,24 @@ fn main() {
 
 fn playground<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
     use div::div;
+    let p = RosePinePalette::dawn();
 
     div()
         .text_color(black())
         .h_full()
-        .w_1_2()
-        .fill(theme.success(0.5))
-        .hovered()
-        .fill(theme.error(0.5))
-        .pressed()
-        .fill(theme.warning(0.5))
+        .w_full()
+        .fill(p.rose)
+        .block()
         .child(
             div()
-                .h_6()
-                .w_6()
-                .absolute()
-                .bottom_0()
-                .fill(theme.success(0.)),
+                .block()
+                .fill(p.pine)
+                .child(div().block().fill(p.love).w_6().h_3()),
         )
         .child(
-            button()
-                .label("Click me")
-                .data(1_usize)
-                .on_click(|_, data, _| {
-                    dbg!(*data);
-                }),
-        )
-        .child(
-            button()
-                .label("And me")
-                .data(2_usize)
-                .on_click(|_, data, _| {
-                    dbg!(*data);
-                }),
+            div()
+                .block()
+                .fill(p.gold)
+                .child(div().block().fill(p.iris).w_3().h_3()),
         )
 }
-
-//     todo!()
-//     // column()
-//     // .size(auto())
-//     // .fill(theme.base(0.5))
-//     // .text_color(theme.text(0.5))
-//     // .child(title_bar(theme))
-//     // .child(stage(theme))
-//     // .child(status_bar(theme))
-// }
-
-// fn title_bar<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
-//     row()
-//         .fill(theme.base(0.2))
-//         .justify(0.)
-//         .width(auto())
-//         .child(text("Zed Playground"))
-// }
-
-// fn stage<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
-//     row().fill(theme.surface(0.9))
-// }
-
-// fn status_bar<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
-//     row().fill(theme.surface(0.1))
-// }

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

@@ -4,19 +4,20 @@ use crate::{
     paint_context::PaintContext,
     pressable::{pressable, Pressable},
 };
+pub use gpui::taffy::style::{
+    AlignContent, AlignItems, AlignSelf, Display, FlexDirection, FlexWrap, JustifyContent,
+    Overflow, Position,
+};
 use gpui::{
     fonts::TextStyleRefinement,
     geometry::{
         rect::RectF, AbsoluteLength, DefiniteLength, Edges, EdgesRefinement, Length, Point,
         PointRefinement, Size, SizeRefinement,
     },
+    taffy,
 };
 use playground_macros::styleable_helpers;
 use refineable::{Refineable, RefinementCascade};
-pub use taffy::style::{
-    AlignContent, AlignItems, AlignSelf, Display, FlexDirection, FlexWrap, JustifyContent,
-    Overflow, Position,
-};
 
 #[derive(Clone, Refineable)]
 pub struct Style {
@@ -137,12 +138,23 @@ impl Style {
             });
         }
     }
+
+    pub fn text_style(&self) -> Option<TextStyleRefinement> {
+        if let Some(color) = self.text_color {
+            Some(TextStyleRefinement {
+                color: Some(color.into()),
+                ..Default::default()
+            })
+        } else {
+            None
+        }
+    }
 }
 
 impl Default for Style {
     fn default() -> Self {
         Style {
-            display: Display::DEFAULT,
+            display: Display::Block,
             overflow: Point {
                 x: Overflow::Visible,
                 y: Overflow::Visible,
@@ -290,6 +302,14 @@ pub trait StyleHelpers: Styleable<Style = Style> {
         self
     }
 
+    fn block(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().display = Some(Display::Block);
+        self
+    }
+
     fn fill<F>(mut self, fill: F) -> Self
     where
         F: Into<Fill>,

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

@@ -4,7 +4,7 @@ use crate::{
     paint_context::PaintContext,
 };
 use anyhow::Result;
-use gpui::text_layout::LineLayout;
+use gpui::{geometry::Size, text_layout::LineLayout, RenderContext};
 use parking_lot::Mutex;
 use std::sync::Arc;
 
@@ -28,40 +28,32 @@ impl<V: 'static> Element<V> for Text {
         view: &mut V,
         cx: &mut LayoutContext<V>,
     ) -> Result<Layout<V, Self::Layout>> {
-        // 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 layout_engine = cx.layout_engine().expect("no layout engine present");
-        // let text = self.text.clone();
-        // let layout = Arc::new(Mutex::new(None));
-
-        // let style: Style = Style::default().refined(&self.metadata.style);
-        // let node_id = layout_engine.add_measured_node(style.to_taffy(rem_size), {
-        //     let layout = 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
-        //     }
-        // })?;
-
-        // Ok((node_id, layout))
-        todo!()
+        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
+        })
     }
 
     fn paint<'a>(
@@ -70,26 +62,27 @@ impl<V: 'static> Element<V> for Text {
         layout: &mut Layout<V, Self::Layout>,
         cx: &mut PaintContext<V>,
     ) {
-        // ) {
-        //     let element_layout_lock = layout.from_element.lock();
-        //     let element_layout = element_layout_lock
-        //         .as_ref()
-        //         .expect("layout has not been performed");
-        //     let line_layout = element_layout.line_layout.clone();
-        //     let line_height = element_layout.line_height;
-        //     drop(element_layout_lock);
-
-        //     let text_style = cx.text_style();
-        //     let line =
-        //         gpui::text_layout::Line::new(line_layout, &[(self.text.len(), text_style.to_run())]);
-        //     line.paint(
-        //         cx.scene,
-        //         layout.from_engine.bounds.origin(),
-        //         layout.from_engine.bounds,
-        //         line_height,
-        //         cx.legacy_cx,
-        //     );
-        todo!()
+        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
+                .as_ref()
+                .expect("measurement has not been performed");
+            line_layout = element_layout.line_layout.clone();
+            line_height = element_layout.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();
+        // TODO: We haven't added visible bounds to the new element system yet, so this is a placeholder.
+        let visible_bounds = layout.bounds(cx);
+        line.paint(cx.scene, origin, visible_bounds, line_height, cx.legacy_cx);
     }
 }
 

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

@@ -80,7 +80,7 @@ pub fn derive_element(input: TokenStream) -> TokenStream {
                 layout: &mut playground::element::Layout<V, Self::Layout>,
                 cx: &mut playground::element::PaintContext<V>,
             ) {
-                layout.update(|_, rendered_element| rendered_element.paint(view, cx)).ok();
+                layout.update(|_, rendered_element| rendered_element.paint(view, cx));
             }
         }
 

crates/gpui/src/app.rs 🔗

@@ -53,6 +53,7 @@ use std::{
 pub use test_app_context::{ContextHandle, TestAppContext};
 use util::ResultExt;
 use uuid::Uuid;
+pub use window::MeasureParams;
 use window_input_handler::WindowInputHandler;
 
 pub trait Entity: 'static {

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

@@ -1302,7 +1302,7 @@ impl LayoutEngine {
     }
 
     pub fn computed_layout(&mut self, node: LayoutId) -> Result<EngineLayout> {
-        Ok(self.0.layout(node)?.into())
+        dbg!(Ok(self.0.layout(node)?.into()))
     }
 }
 

crates/gpui/src/geometry.rs 🔗

@@ -157,7 +157,7 @@ impl<T: Clone + Default> Into<taffy::geometry::Point<T>> for Point<T> {
     }
 }
 
-#[derive(Clone, Refineable)]
+#[derive(Clone, Refineable, Debug)]
 pub struct Size<T: Clone + Default> {
     pub width: T,
     pub height: T,

crates/gpui/src/gpui.rs 🔗

@@ -7,6 +7,7 @@ pub use assets::*;
 pub mod elements;
 pub mod font_cache;
 mod image_data;
+pub use taffy;
 pub use crate::image_data::ImageData;
 pub mod views;
 pub use font_cache::FontCache;