Get a 50% colored box rendering

Nathan Sobo and Mikayla Maki created

Co-Authored-By: Mikayla Maki <mikayla@zed.dev>

Change summary

crates/gpui/playground/src/adapter.rs                  | 17 ++-
crates/gpui/playground/src/element.rs                  | 46 +++++++++-
crates/gpui/playground/src/frame.rs                    |  7 +
crates/gpui/playground/src/playground.rs               | 42 ++++++----
crates/gpui/playground/src/style.rs                    | 48 ++++++++++--
crates/gpui/playground_macros/src/playground_macros.rs |  2 
crates/gpui/src/app/window.rs                          | 13 +-
crates/gpui/src/gpui.rs                                |  2 
8 files changed, 129 insertions(+), 48 deletions(-)

Detailed changes

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

@@ -1,5 +1,5 @@
 use crate::element::{LayoutContext, PaintContext};
-use gpui::geometry::rect::RectF;
+use gpui::{geometry::rect::RectF, LayoutEngine};
 use util::ResultExt;
 
 use crate::element::AnyElement;
@@ -8,7 +8,7 @@ use crate::element::AnyElement;
 pub struct Adapter<V>(pub(crate) AnyElement<V>);
 
 impl<V: 'static> gpui::Element<V> for Adapter<V> {
-    type LayoutState = ();
+    type LayoutState = Option<LayoutEngine>;
     type PaintState = ();
 
     fn layout(
@@ -17,7 +17,7 @@ impl<V: 'static> gpui::Element<V> for Adapter<V> {
         view: &mut V,
         legacy_cx: &mut gpui::LayoutContext<V>,
     ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
-        legacy_cx.push_layout_engine();
+        legacy_cx.push_layout_engine(LayoutEngine::new());
         let node = self
             .0
             .layout(view, &mut LayoutContext { legacy_cx })
@@ -27,9 +27,9 @@ impl<V: 'static> gpui::Element<V> for Adapter<V> {
             let layout_engine = legacy_cx.layout_engine().unwrap();
             layout_engine.compute_layout(node, constraint.max).log_err();
         }
-        legacy_cx.pop_layout_engine();
-
-        (constraint.max, ())
+        let layout_engine = legacy_cx.pop_layout_engine();
+        debug_assert!(layout_engine.is_some());
+        (constraint.max, layout_engine)
     }
 
     fn paint(
@@ -37,12 +37,15 @@ impl<V: 'static> gpui::Element<V> for Adapter<V> {
         scene: &mut gpui::SceneBuilder,
         bounds: RectF,
         visible_bounds: RectF,
-        layout: &mut (),
+        layout_engine: &mut Option<LayoutEngine>,
         view: &mut V,
         legacy_cx: &mut gpui::PaintContext<V>,
     ) -> Self::PaintState {
+        legacy_cx.push_layout_engine(layout_engine.take().unwrap());
         let mut cx = PaintContext { legacy_cx, scene };
         self.0.paint(view, &mut cx).log_err();
+        *layout_engine = legacy_cx.pop_layout_engine();
+        debug_assert!(layout_engine.is_some());
     }
 
     fn rect_for_text_range(

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

@@ -1,6 +1,6 @@
 use crate::{
     adapter::Adapter,
-    style::{DefinedLength, Display, Fill, Overflow, Position, Style},
+    style::{DefinedLength, Display, Fill, Length, Overflow, Position, Style},
 };
 use anyhow::Result;
 use derive_more::{Deref, DerefMut};
@@ -168,7 +168,7 @@ pub trait Element<V: 'static>: 'static + Clone {
     }
 
     #[tailwind_lengths]
-    fn inset(mut self, length: DefinedLength) -> Self
+    fn inset_(mut self, length: DefinedLength) -> Self
     where
         Self: Sized,
     {
@@ -179,8 +179,24 @@ pub trait Element<V: 'static>: 'static + Clone {
         self
     }
 
+    fn w(mut self, width: impl Into<Length>) -> Self
+    where
+        Self: Sized,
+    {
+        self.style_mut().size.width = width.into();
+        self
+    }
+
+    fn w_auto(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.style_mut().size.width = Length::Auto;
+        self
+    }
+
     #[tailwind_lengths]
-    fn w(mut self, length: DefinedLength) -> Self
+    fn w_(mut self, length: DefinedLength) -> Self
     where
         Self: Sized,
     {
@@ -189,7 +205,7 @@ pub trait Element<V: 'static>: 'static + Clone {
     }
 
     #[tailwind_lengths]
-    fn min_w(mut self, length: DefinedLength) -> Self
+    fn min_w_(mut self, length: DefinedLength) -> Self
     where
         Self: Sized,
     {
@@ -197,17 +213,33 @@ pub trait Element<V: 'static>: 'static + Clone {
         self
     }
 
+    fn h(mut self, height: impl Into<Length>) -> Self
+    where
+        Self: Sized,
+    {
+        self.style_mut().size.height = height.into();
+        self
+    }
+
+    fn h_auto(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.style_mut().size.height = Length::Auto;
+        self
+    }
+
     #[tailwind_lengths]
-    fn h(mut self, length: DefinedLength) -> Self
+    fn h_(mut self, height: DefinedLength) -> Self
     where
         Self: Sized,
     {
-        self.style_mut().size.height = length;
+        self.style_mut().size.height = height;
         self
     }
 
     #[tailwind_lengths]
-    fn min_h(mut self, length: DefinedLength) -> Self
+    fn min_h_(mut self, length: DefinedLength) -> Self
     where
         Self: Sized,
     {

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

@@ -37,6 +37,13 @@ impl<V: 'static> Element<V> for Frame<V> {
     }
 
     fn paint(&mut self, layout: Layout, view: &mut V, cx: &mut PaintContext<V>) -> Result<()> {
+        cx.scene.push_quad(gpui::scene::Quad {
+            bounds: layout.bounds,
+            background: self.style.fill.color().map(Into::into),
+            border: Default::default(),
+            corner_radii: Default::default(),
+        });
+
         for child in &mut self.children {
             child.paint(view, cx)?;
         }

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

@@ -1,20 +1,14 @@
 #![allow(dead_code, unused_variables)]
-use element::{AnyElement, Element};
+use element::Element;
 use frame::frame;
+use gpui::{
+    geometry::{rect::RectF, vector::vec2f},
+    platform::WindowOptions,
+};
 use log::LevelFilter;
 use simplelog::SimpleLogger;
 
-fn main() {
-    SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
-
-    gpui::App::new(()).unwrap().run(|cx| {
-        cx.add_window(Default::default(), |_| {
-            view(|_| workspace(&rose_pine::moon()))
-        });
-        cx.platform().activate(true);
-    });
-}
-
+use style::percent;
 use themes::{rose_pine, ThemeColors};
 use view::view;
 
@@ -26,16 +20,28 @@ mod style;
 mod themes;
 mod view;
 
-pub struct Playground<V: 'static>(AnyElement<V>);
+fn main() {
+    SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
 
-impl<V> Playground<V> {
-    pub fn new() -> Self {
-        Self(workspace(&rose_pine::moon()).into_any())
-    }
+    gpui::App::new(()).unwrap().run(|cx| {
+        cx.add_window(
+            WindowOptions {
+                bounds: gpui::platform::WindowBounds::Fixed(RectF::new(
+                    vec2f(0., 0.),
+                    vec2f(400., 300.),
+                )),
+                center: true,
+                ..Default::default()
+            },
+            |_| view(|_| workspace(&rose_pine::moon())),
+        );
+        cx.platform().activate(true);
+    });
 }
 
 fn workspace<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
-    frame()
+    // frame().w_full().h_half().fill(theme.success(0.5))
+    frame().h_full().w(percent(50.)).fill(theme.success(0.5))
 }
 //     todo!()
 //     // column()

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

@@ -239,10 +239,10 @@ impl Edges<Length> {
 
     pub const fn zero() -> Self {
         Self {
-            top: Length::Length(DefinedLength::Pixels(0.0)),
-            right: Length::Length(DefinedLength::Pixels(0.0)),
-            bottom: Length::Length(DefinedLength::Pixels(0.0)),
-            left: Length::Length(DefinedLength::Pixels(0.0)),
+            top: Length::Defined(DefinedLength::Pixels(0.0)),
+            right: Length::Defined(DefinedLength::Pixels(0.0)),
+            bottom: Length::Defined(DefinedLength::Pixels(0.0)),
+            left: Length::Defined(DefinedLength::Pixels(0.0)),
         }
     }
 
@@ -272,7 +272,9 @@ impl DefinedLength {
         match self {
             DefinedLength::Pixels(pixels) => taffy::style::LengthPercentage::Length(*pixels),
             DefinedLength::Rems(rems) => taffy::style::LengthPercentage::Length(rems * rem_size),
-            DefinedLength::Percent(percent) => taffy::style::LengthPercentage::Percent(*percent),
+            DefinedLength::Percent(percent) => {
+                taffy::style::LengthPercentage::Percent(*percent / 100.)
+            }
         }
     }
 }
@@ -280,14 +282,30 @@ impl DefinedLength {
 /// A length that can be defined in pixels, rems, percent of parent, or auto.
 #[derive(Clone, Copy)]
 pub enum Length {
-    Length(DefinedLength),
+    Defined(DefinedLength),
     Auto,
 }
 
+pub fn auto() -> Length {
+    Length::Auto
+}
+
+pub fn percent(percent: f32) -> DefinedLength {
+    DefinedLength::Percent(percent)
+}
+
+pub fn rems(rems: f32) -> DefinedLength {
+    DefinedLength::Rems(rems)
+}
+
+pub fn pixels(pixels: f32) -> DefinedLength {
+    DefinedLength::Pixels(pixels)
+}
+
 impl Length {
     fn to_taffy(&self, rem_size: f32) -> taffy::prelude::LengthPercentageAuto {
         match self {
-            Length::Length(length) => length.to_taffy(rem_size).into(),
+            Length::Defined(length) => length.to_taffy(rem_size).into(),
             Length::Auto => taffy::prelude::LengthPercentageAuto::Auto,
         }
     }
@@ -295,7 +313,7 @@ impl Length {
 
 impl From<DefinedLength> for Length {
     fn from(value: DefinedLength) -> Self {
-        Length::Length(value)
+        Length::Defined(value)
     }
 }
 
@@ -304,8 +322,22 @@ pub enum Fill {
     Color(Hsla),
 }
 
+impl Fill {
+    pub fn color(&self) -> Option<Hsla> {
+        match self {
+            Fill::Color(color) => Some(*color),
+        }
+    }
+}
+
 impl Default for Fill {
     fn default() -> Self {
         Self::Color(Hsla::default())
     }
 }
+
+impl From<Hsla> for Fill {
+    fn from(color: Hsla) -> Self {
+        Self::Color(color)
+    }
+}

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

@@ -20,7 +20,7 @@ pub fn tailwind_lengths(_attr: TokenStream, item: TokenStream) -> TokenStream {
     let mut output_functions = TokenStream2::new();
 
     for (length, value) in fixed_lengths() {
-        let function_name = format_ident!("{}_{}", function_signature.ident, length);
+        let function_name = format_ident!("{}{}", function_signature.ident, length);
         output_functions.extend(quote! {
             #visibility fn #function_name(mut self) -> Self #where_clause {
                 let #argument_name = #value.into();

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

@@ -214,12 +214,12 @@ impl<'a> WindowContext<'a> {
         self.window.layout_engines.last_mut()
     }
 
-    pub fn push_layout_engine(&mut self) {
-        self.window.layout_engines.push(LayoutEngine::new());
+    pub fn push_layout_engine(&mut self, engine: LayoutEngine) {
+        self.window.layout_engines.push(engine);
     }
 
-    pub fn pop_layout_engine(&mut self) {
-        self.window.layout_engines.pop();
+    pub fn pop_layout_engine(&mut self) -> Option<LayoutEngine> {
+        self.window.layout_engines.pop()
     }
 
     pub fn remove_window(&mut self) {
@@ -1235,12 +1235,13 @@ impl<'a> WindowContext<'a> {
     }
 }
 
+#[derive(Default)]
 pub struct LayoutEngine(Taffy);
 pub use taffy::style::Style as LayoutStyle;
 
 impl LayoutEngine {
-    fn new() -> Self {
-        Self(Taffy::new())
+    pub fn new() -> Self {
+        Default::default()
     }
 
     pub fn add_node<C>(&mut self, style: LayoutStyle, children: C) -> Result<LayoutNodeId>

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, Layout, LayoutNodeId, RectFExt, SizeConstraint, Vector2FExt, WindowContext,
+    Axis, Layout, LayoutEngine, LayoutNodeId, RectFExt, SizeConstraint, Vector2FExt, WindowContext,
 };
 
 pub use anyhow;