Get taffy style conversion working

Nathan Sobo created

Change summary

crates/gpui/Cargo.toml                                 |   2 
crates/gpui/playground/src/element.rs                  | 190 ++++++++++
crates/gpui/playground/src/frame.rs                    |  33 +
crates/gpui/playground/src/playground.rs               |   1 
crates/gpui/playground/src/style.rs                    | 221 ++++++-----
crates/gpui/playground_macros/src/playground_macros.rs |   5 
6 files changed, 343 insertions(+), 109 deletions(-)

Detailed changes

crates/gpui/Cargo.toml 🔗

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

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

@@ -0,0 +1,190 @@
+use crate::style::{Display, Length, Overflow, Position, Style};
+use gpui::{LayoutContext, PaintContext};
+use playground_macros::tailwind_lengths;
+pub use taffy::tree::{Layout, NodeId};
+
+pub trait Element<V> {
+    fn style_mut(&mut self) -> &mut Style;
+    fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> NodeId;
+    fn paint(&mut self, layout: &Layout, view: &mut V, cx: &mut gpui::PaintContext<V>);
+
+    // Display ////////////////////
+
+    fn block(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.style_mut().display = Display::Block;
+        self
+    }
+
+    fn flex(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.style_mut().display = Display::Flex;
+        self
+    }
+
+    fn grid(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.style_mut().display = Display::Grid;
+        self
+    }
+
+    // style::Overflow ///////////////////
+
+    fn overflow_visible(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.style_mut().overflow.x = Overflow::Visible;
+        self.style_mut().overflow.y = Overflow::Visible;
+        self
+    }
+
+    fn overflow_hidden(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.style_mut().overflow.x = Overflow::Hidden;
+        self.style_mut().overflow.y = Overflow::Hidden;
+        self
+    }
+
+    fn overflow_scroll(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.style_mut().overflow.x = Overflow::Scroll;
+        self.style_mut().overflow.y = Overflow::Scroll;
+        self
+    }
+
+    fn overflow_x_visible(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.style_mut().overflow.x = Overflow::Visible;
+        self
+    }
+
+    fn overflow_x_hidden(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.style_mut().overflow.x = Overflow::Hidden;
+        self
+    }
+
+    fn overflow_x_scroll(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.style_mut().overflow.x = Overflow::Scroll;
+        self
+    }
+
+    fn overflow_y_visible(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.style_mut().overflow.y = Overflow::Visible;
+        self
+    }
+
+    fn overflow_y_hidden(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.style_mut().overflow.y = Overflow::Hidden;
+        self
+    }
+
+    fn overflow_y_scroll(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.style_mut().overflow.y = Overflow::Scroll;
+        self
+    }
+
+    // Position ///////////////////
+
+    fn relative(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.style_mut().position = Position::Relative;
+        self
+    }
+
+    fn absolute(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.style_mut().position = Position::Absolute;
+
+        self
+    }
+
+    #[tailwind_lengths]
+    fn inset(mut self, length: Length) -> Self
+    where
+        Self: Sized,
+    {
+        self.style_mut().inset.top = length;
+        self.style_mut().inset.right = length;
+        self.style_mut().inset.bottom = length;
+        self.style_mut().inset.left = length;
+        self
+    }
+
+    #[tailwind_lengths]
+    fn w(mut self, length: Length) -> Self
+    where
+        Self: Sized,
+    {
+        self.style_mut().size.width = length;
+        self
+    }
+
+    #[tailwind_lengths]
+    fn min_w(mut self, length: Length) -> Self
+    where
+        Self: Sized,
+    {
+        self.style_mut().size.width = length;
+        self
+    }
+
+    #[tailwind_lengths]
+    fn h(mut self, length: Length) -> Self
+    where
+        Self: Sized,
+    {
+        self.style_mut().size.height = length;
+        self
+    }
+}
+
+pub struct AnyElement<V> {
+    element: Box<dyn Element<V>>,
+    layout_node_id: Option<NodeId>,
+}
+
+impl<V> AnyElement<V> {
+    fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> NodeId {
+        let layout_node_id = self.element.layout(view, cx);
+        self.layout_node_id = Some(layout_node_id);
+        layout_node_id
+    }
+
+    fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) {
+        let layout_node_id = self.layout_node_id.expect("paint called before layout");
+        let layout = cx.layout_engine().layout(layout_node_id).unwrap().clone();
+        self.element.paint(&layout, view, cx);
+    }
+}

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

@@ -1 +1,34 @@
+use crate::{element::Element, style::Style};
 
+pub struct Frame {
+    style: Style,
+    children: Vec<Frame>,
+}
+
+impl<V: 'static> Element<V> for Frame {
+    fn style_mut(&mut self) -> &mut Style {
+        &mut self.style
+    }
+
+    fn layout(&mut self, view: &mut V, cx: &mut gpui::LayoutContext<V>) -> taffy::tree::NodeId {
+        let child_layout_node_ids = self
+            .children
+            .iter_mut()
+            .map(|child| child.layout(view, cx))
+            .collect::<Vec<_>>();
+
+        let rem_size = cx.rem_pixels();
+        cx.layout_engine()
+            .new_with_children(self.style.to_taffy(rem_size), &child_layout_node_ids)
+            .unwrap()
+    }
+
+    fn paint(
+        &mut self,
+        layout: &taffy::tree::Layout,
+        view: &mut V,
+        cx: &mut gpui::PaintContext<V>,
+    ) {
+        todo!()
+    }
+}

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

@@ -1,5 +1,5 @@
-use playground_macros::tailwind_lengths;
-use taffy::style::{
+use crate::color::Hsla;
+pub use taffy::style::{
     AlignContent, AlignItems, AlignSelf, Display, FlexDirection, FlexWrap, JustifyContent,
     Overflow, Position,
 };
@@ -62,6 +62,9 @@ pub struct Style {
     pub flex_grow: f32,
     /// The relative rate at which this item shrinks when it is contracting to fit into space, 1.0 is the default value, and this value must be positive.
     pub flex_shrink: f32,
+
+    /// The fill color of this element
+    pub fill: Fill,
 }
 
 impl Style {
@@ -93,117 +96,44 @@ impl Style {
         flex_grow: 0.0,
         flex_shrink: 1.0,
         flex_basis: LengthOrAuto::Auto,
+        fill: Fill::Color(Hsla {
+            h: 0.,
+            s: 0.,
+            l: 0.,
+            a: 0.,
+        }),
     };
 
     pub fn new() -> Self {
         Self::DEFAULT.clone()
     }
 
-    // Display ////////////////////
-
-    fn block(mut self) -> Self {
-        self.display = Display::Block;
-        self
-    }
-
-    fn flex(mut self) -> Self {
-        self.display = Display::Flex;
-        self
-    }
-
-    fn grid(mut self) -> Self {
-        self.display = Display::Grid;
-        self
-    }
-
-    // Overflow ///////////////////
-
-    pub fn overflow_visible(mut self) -> Self {
-        self.overflow.x = Overflow::Visible;
-        self.overflow.y = Overflow::Visible;
-        self
-    }
-
-    pub fn overflow_hidden(mut self) -> Self {
-        self.overflow.x = Overflow::Hidden;
-        self.overflow.y = Overflow::Hidden;
-        self
-    }
-
-    pub fn overflow_scroll(mut self) -> Self {
-        self.overflow.x = Overflow::Scroll;
-        self.overflow.y = Overflow::Scroll;
-        self
-    }
-
-    pub fn overflow_x_visible(mut self) -> Self {
-        self.overflow.x = Overflow::Visible;
-        self
-    }
-
-    pub fn overflow_x_hidden(mut self) -> Self {
-        self.overflow.x = Overflow::Hidden;
-        self
-    }
-
-    pub fn overflow_x_scroll(mut self) -> Self {
-        self.overflow.x = Overflow::Scroll;
-        self
-    }
-
-    pub fn overflow_y_visible(mut self) -> Self {
-        self.overflow.y = Overflow::Visible;
-        self
-    }
-
-    pub fn overflow_y_hidden(mut self) -> Self {
-        self.overflow.y = Overflow::Hidden;
-        self
-    }
-
-    pub fn overflow_y_scroll(mut self) -> Self {
-        self.overflow.y = Overflow::Scroll;
-        self
-    }
-
-    // Position ///////////////////
-
-    pub fn relative(mut self) -> Self {
-        self.position = Position::Relative;
-        self
-    }
-
-    pub fn absolute(mut self) -> Self {
-        self.position = Position::Absolute;
-
-        self
-    }
-
-    #[tailwind_lengths]
-    pub fn inset(mut self, length: Length) -> Self {
-        self.inset.top = length;
-        self.inset.right = length;
-        self.inset.bottom = length;
-        self.inset.left = length;
-        self
-    }
-
-    #[tailwind_lengths]
-    pub fn w(mut self, length: Length) -> Self {
-        self.size.width = length;
-        self
-    }
-
-    #[tailwind_lengths]
-    pub fn min_w(mut self, length: Length) -> Self {
-        self.size.width = length;
-        self
-    }
-
-    #[tailwind_lengths]
-    pub fn h(mut self, length: Length) -> Self {
-        self.size.height = length;
-        self
+    pub fn to_taffy(&self, rem_size: f32) -> taffy::style::Style {
+        taffy::style::Style {
+            display: self.display,
+            overflow: self.overflow.clone().into(),
+            scrollbar_width: self.scrollbar_width,
+            position: self.position,
+            inset: self.inset.to_taffy(rem_size),
+            size: self.size.to_taffy(rem_size),
+            min_size: self.min_size.to_taffy(rem_size),
+            max_size: self.max_size.to_taffy(rem_size),
+            aspect_ratio: self.aspect_ratio,
+            margin: self.margin.to_taffy(rem_size),
+            padding: self.padding.to_taffy(rem_size),
+            border: self.border.to_taffy(rem_size),
+            align_items: self.align_items,
+            align_self: self.align_self,
+            align_content: self.align_content,
+            justify_content: self.justify_content,
+            gap: self.gap.to_taffy(rem_size),
+            flex_direction: self.flex_direction,
+            flex_wrap: self.flex_wrap,
+            flex_basis: self.flex_basis.to_taffy(rem_size).into(),
+            flex_grow: self.flex_grow,
+            flex_shrink: self.flex_shrink,
+            ..Default::default() // Ignore grid properties for now
+        }
     }
 }
 
@@ -213,6 +143,15 @@ pub struct Point<T> {
     pub y: T,
 }
 
+impl<T> Into<taffy::geometry::Point<T>> for Point<T> {
+    fn into(self) -> taffy::geometry::Point<T> {
+        taffy::geometry::Point {
+            x: self.x,
+            y: self.y,
+        }
+    }
+}
+
 #[derive(Clone)]
 pub struct Size<T> {
     pub width: T,
@@ -226,6 +165,13 @@ impl Size<Length> {
             height: Length::Pixels(0.),
         }
     }
+
+    pub fn to_taffy(&self, rem_size: f32) -> taffy::geometry::Size<taffy::style::LengthPercentage> {
+        taffy::geometry::Size {
+            width: self.width.to_taffy(rem_size),
+            height: self.height.to_taffy(rem_size),
+        }
+    }
 }
 
 impl Size<LengthOrAuto> {
@@ -235,6 +181,16 @@ impl Size<LengthOrAuto> {
             height: LengthOrAuto::Auto,
         }
     }
+
+    pub fn to_taffy<T: From<taffy::prelude::LengthPercentageAuto>>(
+        &self,
+        rem_size: f32,
+    ) -> taffy::geometry::Size<T> {
+        taffy::geometry::Size {
+            width: self.width.to_taffy(rem_size).into(),
+            height: self.height.to_taffy(rem_size).into(),
+        }
+    }
 }
 
 #[derive(Clone)]
@@ -254,6 +210,15 @@ impl Edges<Length> {
             left: Length::Pixels(0.0),
         }
     }
+
+    pub fn to_taffy(&self, rem_size: f32) -> taffy::geometry::Rect<taffy::style::LengthPercentage> {
+        taffy::geometry::Rect {
+            top: self.top.to_taffy(rem_size),
+            right: self.right.to_taffy(rem_size),
+            bottom: self.bottom.to_taffy(rem_size),
+            left: self.left.to_taffy(rem_size),
+        }
+    }
 }
 
 impl Edges<LengthOrAuto> {
@@ -274,6 +239,18 @@ impl Edges<LengthOrAuto> {
             left: LengthOrAuto::Length(Length::Pixels(0.0)),
         }
     }
+
+    pub fn to_taffy(
+        &self,
+        rem_size: f32,
+    ) -> taffy::geometry::Rect<taffy::style::LengthPercentageAuto> {
+        taffy::geometry::Rect {
+            top: self.top.to_taffy(rem_size),
+            right: self.right.to_taffy(rem_size),
+            bottom: self.bottom.to_taffy(rem_size),
+            left: self.left.to_taffy(rem_size),
+        }
+    }
 }
 
 #[derive(Clone, Copy)]
@@ -283,14 +260,44 @@ pub enum Length {
     Percent(f32), // 0. - 100.
 }
 
+impl Length {
+    fn to_taffy(&self, rem_size: f32) -> taffy::style::LengthPercentage {
+        match self {
+            Length::Pixels(pixels) => taffy::style::LengthPercentage::Length(*pixels),
+            Length::Rems(rems) => taffy::style::LengthPercentage::Length(rems * rem_size),
+            Length::Percent(percent) => taffy::style::LengthPercentage::Percent(*percent),
+        }
+    }
+}
+
 #[derive(Clone, Copy)]
 pub enum LengthOrAuto {
     Length(Length),
     Auto,
 }
 
+impl LengthOrAuto {
+    fn to_taffy(&self, rem_size: f32) -> taffy::prelude::LengthPercentageAuto {
+        match self {
+            LengthOrAuto::Length(length) => length.to_taffy(rem_size).into(),
+            LengthOrAuto::Auto => taffy::prelude::LengthPercentageAuto::Auto,
+        }
+    }
+}
+
 impl From<Length> for LengthOrAuto {
     fn from(value: Length) -> Self {
         LengthOrAuto::Length(value)
     }
 }
+
+#[derive(Clone)]
+pub enum Fill {
+    Color(Hsla),
+}
+
+impl Default for Fill {
+    fn default() -> Self {
+        Self::Color(Hsla::default())
+    }
+}

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

@@ -6,8 +6,11 @@ use syn::{parse_macro_input, FnArg, ItemFn, PatType};
 #[proc_macro_attribute]
 pub fn tailwind_lengths(_attr: TokenStream, item: TokenStream) -> TokenStream {
     let input_function = parse_macro_input!(item as ItemFn);
+
+    let visibility = &input_function.vis;
     let function_signature = input_function.sig.clone();
     let function_body = input_function.block;
+    let where_clause = &function_signature.generics.where_clause;
 
     let argument_name = match function_signature.inputs.iter().nth(1) {
         Some(FnArg::Typed(PatType { pat, .. })) => pat,
@@ -19,7 +22,7 @@ pub fn tailwind_lengths(_attr: TokenStream, item: TokenStream) -> TokenStream {
     for (length, value) in fixed_lengths() {
         let function_name = format_ident!("{}_{}", function_signature.ident, length);
         output_functions.extend(quote! {
-            pub fn #function_name(mut self) -> Self {
+            #visibility fn #function_name(mut self) -> Self #where_clause {
                 let #argument_name = #value.into();
                 #function_body
             }