WIP - Next: implement Element derive macro

Nathan Sobo created

Change summary

Cargo.lock                                     |  11 
Cargo.toml                                     |   1 
crates/gpui/src/geometry.rs                    |  32 +
crates/gpui3/Cargo.toml                        |   2 
crates/gpui3/src/element.rs                    |   9 
crates/gpui3/src/elements/div.rs               |   4 
crates/gpui3/src/elements/img.rs               |   4 
crates/gpui3/src/elements/svg.rs               |   4 
crates/gpui3/src/geometry.rs                   |   6 
crates/gpui3/src/gpui3.rs                      |   2 
crates/gpui3/src/style.rs                      |   8 
crates/gpui3/src/style_helpers.rs              | 288 +++++++++++++++++
crates/gpui3_macros/Cargo.toml                 |  14 
crates/gpui3_macros/src/derive_element.rs      |  93 +++++
crates/gpui3_macros/src/derive_into_element.rs |  69 ++++
crates/gpui3_macros/src/gpui3_macros.rs        |  20 +
crates/gpui3_macros/src/style_helpers.rs       | 332 ++++++++++++++++++++
crates/storybook2/src/collab_panel.rs          |  15 
crates/storybook2/src/storybook2.rs            |  34 +
crates/storybook2/src/theme.rs                 |  61 +-
crates/storybook2/src/workspace.rs             |  90 +++--
21 files changed, 980 insertions(+), 119 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -3363,7 +3363,7 @@ dependencies = [
  "font-kit",
  "foreign-types 0.3.2",
  "futures 0.3.28",
- "gpui2_macros",
+ "gpui3_macros",
  "gpui_macros",
  "image",
  "itertools",
@@ -3405,6 +3405,15 @@ dependencies = [
  "wgpu",
 ]
 
+[[package]]
+name = "gpui3_macros"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
 [[package]]
 name = "gpui_macros"
 version = "0.1.0"

Cargo.toml 🔗

@@ -36,6 +36,7 @@ members = [
     "crates/gpui2",
     "crates/gpui2_macros",
     "crates/gpui3",
+    "crates/gpui3_macros",
     "crates/install_cli",
     "crates/journal",
     "crates/language",

crates/gpui/src/geometry.rs 🔗

@@ -203,8 +203,8 @@ where
 impl Size<DefiniteLength> {
     pub fn zero() -> Self {
         Self {
-            width: pixels(0.),
-            height: pixels(0.),
+            width: px(0.),
+            height: px(0.),
         }
     }
 
@@ -256,10 +256,10 @@ impl Edges<Length> {
 
     pub fn zero() -> Self {
         Self {
-            top: pixels(0.),
-            right: pixels(0.),
-            bottom: pixels(0.),
-            left: pixels(0.),
+            top: px(0.),
+            right: px(0.),
+            bottom: px(0.),
+            left: px(0.),
         }
     }
 
@@ -279,10 +279,10 @@ impl Edges<Length> {
 impl Edges<DefiniteLength> {
     pub fn zero() -> Self {
         Self {
-            top: pixels(0.),
-            right: pixels(0.),
-            bottom: pixels(0.),
-            left: pixels(0.),
+            top: px(0.),
+            right: px(0.),
+            bottom: px(0.),
+            left: px(0.),
         }
     }
 
@@ -299,10 +299,10 @@ impl Edges<DefiniteLength> {
 impl Edges<AbsoluteLength> {
     pub fn zero() -> Self {
         Self {
-            top: pixels(0.),
-            right: pixels(0.),
-            bottom: pixels(0.),
-            left: pixels(0.),
+            top: px(0.),
+            right: px(0.),
+            bottom: px(0.),
+            left: px(0.),
         }
     }
 
@@ -436,6 +436,10 @@ pub fn rems<T: From<AbsoluteLength>>(rems: f32) -> T {
     AbsoluteLength::Rems(rems).into()
 }
 
+pub fn px<T: From<AbsoluteLength>>(pixels: f32) -> T {
+    AbsoluteLength::Pixels(pixels).into()
+}
+
 pub fn pixels<T: From<AbsoluteLength>>(pixels: f32) -> T {
     AbsoluteLength::Pixels(pixels).into()
 }

crates/gpui3/Cargo.toml 🔗

@@ -16,7 +16,7 @@ doctest = false
 [dependencies]
 collections = { path = "../collections" }
 gpui_macros = { path = "../gpui_macros" }
-gpui2_macros = { path = "../gpui2_macros" }
+gpui3_macros = { path = "../gpui3_macros" }
 util = { path = "../util" }
 sum_tree = { path = "../sum_tree" }
 sqlez = { path = "../sqlez" }

crates/gpui3/src/element.rs 🔗

@@ -32,6 +32,15 @@ pub trait ParentElement<S> {
         self.children_mut().push(child.into_any());
         self
     }
+
+    fn children(mut self, iter: impl IntoIterator<Item = impl IntoAnyElement<S>>) -> Self
+    where
+        Self: Sized,
+    {
+        self.children_mut()
+            .extend(iter.into_iter().map(|item| item.into_any()));
+        self
+    }
 }
 
 trait ElementObject<S> {

crates/gpui3/src/elements/div.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{
     AnyElement, Bounds, Element, Layout, LayoutId, Overflow, ParentElement, Pixels, Point,
-    Refineable, RefinementCascade, Result, Style, Styled, ViewContext,
+    Refineable, RefinementCascade, Result, Style, StyleHelpers, Styled, ViewContext,
 };
 use smallvec::SmallVec;
 use std::{cell::Cell, rc::Rc};
@@ -257,6 +257,8 @@ impl<V> Styled for Div<V> {
     }
 }
 
+impl<V> StyleHelpers for Div<V> {}
+
 // impl<V> Interactive<V> for Div<V> {
 //     fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V> {
 //         &mut self.handlers

crates/gpui3/src/elements/img.rs 🔗

@@ -1,4 +1,4 @@
-use crate::{Element, Layout, LayoutId, Result, Style, Styled};
+use crate::{Element, Layout, LayoutId, Result, Style, StyleHelpers, Styled};
 use refineable::RefinementCascade;
 use std::marker::PhantomData;
 use util::arc_cow::ArcCow;
@@ -98,3 +98,5 @@ impl<S> Styled for Img<S> {
         self.style.base()
     }
 }
+
+impl<S> StyleHelpers for Img<S> {}

crates/gpui3/src/elements/svg.rs 🔗

@@ -1,4 +1,4 @@
-use crate::{Element, Layout, LayoutId, Result, Style, Styled};
+use crate::{Element, Layout, LayoutId, Result, Style, StyleHelpers, Styled};
 use refineable::RefinementCascade;
 use std::{borrow::Cow, marker::PhantomData};
 
@@ -77,3 +77,5 @@ impl<S> Styled for Svg<S> {
         self.style.base()
     }
 }
+
+impl<S> StyleHelpers for Svg<S> {}

crates/gpui3/src/geometry.rs 🔗

@@ -100,8 +100,8 @@ impl From<Size<Option<Pixels>>> for Size<Option<f32>> {
 impl Size<Length> {
     pub fn full() -> Self {
         Self {
-            width: relative(1.),
-            height: relative(1.),
+            width: relative(1.).into(),
+            height: relative(1.).into(),
         }
     }
 }
@@ -410,7 +410,7 @@ impl Debug for Length {
     }
 }
 
-pub fn relative<T: From<DefiniteLength>>(fraction: f32) -> T {
+pub fn relative(fraction: f32) -> DefiniteLength {
     DefiniteLength::Fraction(fraction).into()
 }
 

crates/gpui3/src/gpui3.rs 🔗

@@ -8,6 +8,7 @@ mod platform;
 mod renderer;
 mod scene;
 mod style;
+mod style_helpers;
 mod styled;
 mod taffy;
 mod text_system;
@@ -30,6 +31,7 @@ pub use smallvec;
 pub use smol::Timer;
 use std::ops::{Deref, DerefMut};
 pub use style::*;
+pub use style_helpers::*;
 pub use styled::*;
 use taffy::TaffyLayoutEngine;
 pub use taffy::{AvailableSpace, LayoutId};

crates/gpui3/src/style.rs 🔗

@@ -289,10 +289,10 @@ impl From<Hsla> for Fill {
 #[derive(Clone, Refineable, Default, Debug)]
 #[refineable(debug)]
 pub struct CornerRadii {
-    top_left: AbsoluteLength,
-    top_right: AbsoluteLength,
-    bottom_left: AbsoluteLength,
-    bottom_right: AbsoluteLength,
+    pub top_left: AbsoluteLength,
+    pub top_right: AbsoluteLength,
+    pub bottom_left: AbsoluteLength,
+    pub bottom_right: AbsoluteLength,
 }
 
 impl From<TextStyle> for HighlightStyle {

crates/gpui3/src/style_helpers.rs 🔗

@@ -0,0 +1,288 @@
+use crate::{
+    self as gpui2, relative, rems, AlignItems, Display, Fill, FlexDirection, Hsla, JustifyContent,
+    Length, Position, SharedString, Style, Styled,
+};
+
+pub trait StyleHelpers: Styled<Style = Style> {
+    gpui3_macros::style_helpers!();
+
+    fn h(mut self, height: Length) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().size.height = Some(height);
+        self
+    }
+
+    /// size_{n}: Sets width & height to {n}
+    ///
+    /// Example:
+    /// size_1: Sets width & height to 1
+    fn size(mut self, size: Length) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().size.height = Some(size);
+        self.declared_style().size.width = Some(size);
+        self
+    }
+
+    fn full(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().size.width = Some(relative(1.).into());
+        self.declared_style().size.height = Some(relative(1.).into());
+        self
+    }
+
+    fn relative(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().position = Some(Position::Relative);
+        self
+    }
+
+    fn absolute(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().position = Some(Position::Absolute);
+        self
+    }
+
+    fn block(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().display = Some(Display::Block);
+        self
+    }
+
+    fn flex(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().display = Some(Display::Flex);
+        self
+    }
+
+    fn flex_col(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().flex_direction = Some(FlexDirection::Column);
+        self
+    }
+
+    fn flex_row(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().flex_direction = Some(FlexDirection::Row);
+        self
+    }
+
+    fn flex_1(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().flex_grow = Some(1.);
+        self.declared_style().flex_shrink = Some(1.);
+        self.declared_style().flex_basis = Some(relative(0.).into());
+        self
+    }
+
+    fn flex_auto(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().flex_grow = Some(1.);
+        self.declared_style().flex_shrink = Some(1.);
+        self.declared_style().flex_basis = Some(Length::Auto);
+        self
+    }
+
+    fn flex_initial(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().flex_grow = Some(0.);
+        self.declared_style().flex_shrink = Some(1.);
+        self.declared_style().flex_basis = Some(Length::Auto);
+        self
+    }
+
+    fn flex_none(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().flex_grow = Some(0.);
+        self.declared_style().flex_shrink = Some(0.);
+        self
+    }
+
+    fn grow(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().flex_grow = Some(1.);
+        self
+    }
+
+    fn items_start(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().align_items = Some(AlignItems::FlexStart);
+        self
+    }
+
+    fn items_end(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().align_items = Some(AlignItems::FlexEnd);
+        self
+    }
+
+    fn items_center(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().align_items = Some(AlignItems::Center);
+        self
+    }
+
+    fn justify_between(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().justify_content = Some(JustifyContent::SpaceBetween);
+        self
+    }
+
+    fn justify_center(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().justify_content = Some(JustifyContent::Center);
+        self
+    }
+
+    fn justify_start(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().justify_content = Some(JustifyContent::Start);
+        self
+    }
+
+    fn justify_end(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().justify_content = Some(JustifyContent::End);
+        self
+    }
+
+    fn justify_around(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().justify_content = Some(JustifyContent::SpaceAround);
+        self
+    }
+
+    fn fill<F>(mut self, fill: F) -> Self
+    where
+        F: Into<Fill>,
+        Self: Sized,
+    {
+        self.declared_style().fill = Some(fill.into());
+        self
+    }
+
+    fn border_color<C>(mut self, border_color: C) -> Self
+    where
+        C: Into<Hsla>,
+        Self: Sized,
+    {
+        self.declared_style().border_color = Some(border_color.into());
+        self
+    }
+
+    fn text_color<C>(mut self, color: C) -> Self
+    where
+        C: Into<Hsla>,
+        Self: Sized,
+    {
+        self.declared_style().text_color = Some(color.into());
+        self
+    }
+
+    fn text_xs(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().font_size = Some(rems(0.75));
+        self
+    }
+
+    fn text_sm(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().font_size = Some(rems(0.875));
+        self
+    }
+
+    fn text_base(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().font_size = Some(rems(1.0));
+        self
+    }
+
+    fn text_lg(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().font_size = Some(rems(1.125));
+        self
+    }
+
+    fn text_xl(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().font_size = Some(rems(1.25));
+        self
+    }
+
+    fn text_2xl(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().font_size = Some(rems(1.5));
+        self
+    }
+
+    fn text_3xl(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().font_size = Some(rems(1.875));
+        self
+    }
+
+    fn font(mut self, family_name: impl Into<SharedString>) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().font_family = Some(family_name.into());
+        self
+    }
+}

crates/gpui3_macros/Cargo.toml 🔗

@@ -0,0 +1,14 @@
+[package]
+name = "gpui3_macros"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[lib]
+path = "src/gpui3_macros.rs"
+proc-macro = true
+
+[dependencies]
+syn = "1.0.72"
+quote = "1.0.9"
+proc-macro2 = "1.0.66"

crates/gpui3_macros/src/derive_element.rs 🔗

@@ -0,0 +1,93 @@
+use proc_macro::TokenStream;
+use proc_macro2::Ident;
+use quote::quote;
+use syn::{parse_macro_input, parse_quote, DeriveInput, GenericParam, Generics};
+
+use crate::derive_into_element::impl_into_element;
+
+pub fn derive_element(input: TokenStream) -> TokenStream {
+    let ast = parse_macro_input!(input as DeriveInput);
+    let type_name = ast.ident;
+    let placeholder_view_generics: Generics = parse_quote! { <V: 'static> };
+
+    let (impl_generics, type_generics, where_clause, view_type_name, lifetimes) =
+        if let Some(first_type_param) = ast.generics.params.iter().find_map(|param| {
+            if let GenericParam::Type(type_param) = param {
+                Some(type_param.ident.clone())
+            } else {
+                None
+            }
+        }) {
+            let mut lifetimes = vec![];
+            for param in ast.generics.params.iter() {
+                if let GenericParam::Lifetime(lifetime_def) = param {
+                    lifetimes.push(lifetime_def.lifetime.clone());
+                }
+            }
+            let generics = ast.generics.split_for_impl();
+            (
+                generics.0,
+                Some(generics.1),
+                generics.2,
+                first_type_param,
+                lifetimes,
+            )
+        } else {
+            let generics = placeholder_view_generics.split_for_impl();
+            let placeholder_view_type_name: Ident = parse_quote! { V };
+            (
+                generics.0,
+                None,
+                generics.2,
+                placeholder_view_type_name,
+                vec![],
+            )
+        };
+
+    let lifetimes = if !lifetimes.is_empty() {
+        quote! { <#(#lifetimes),*> }
+    } else {
+        quote! {}
+    };
+
+    let impl_into_element = impl_into_element(
+        &impl_generics,
+        &view_type_name,
+        &type_name,
+        &type_generics,
+        &where_clause,
+    );
+
+    let gen = quote! {
+        impl #impl_generics gpui2::element::Element<#view_type_name> for #type_name #type_generics
+        #where_clause
+        {
+            type PaintState = gpui2::element::AnyElement<#view_type_name #lifetimes>;
+
+            fn layout(
+                &mut self,
+                view: &mut V,
+                cx: &mut gpui2::ViewContext<V>,
+            ) -> anyhow::Result<(gpui2::element::LayoutId, Self::PaintState)> {
+                let mut rendered_element = self.render(view, cx).into_element().into_any();
+                let layout_id = rendered_element.layout(view, cx)?;
+                Ok((layout_id, rendered_element))
+            }
+
+            fn paint(
+                &mut self,
+                view: &mut V,
+                parent_origin: gpui2::Vector2F,
+                _: &gpui2::element::Layout,
+                rendered_element: &mut Self::PaintState,
+                cx: &mut gpui2::ViewContext<V>,
+            ) {
+                rendered_element.paint(view, parent_origin, cx);
+            }
+        }
+
+        #impl_into_element
+    };
+
+    gen.into()
+}

crates/gpui3_macros/src/derive_into_element.rs 🔗

@@ -0,0 +1,69 @@
+use proc_macro::TokenStream;
+use quote::quote;
+use syn::{
+    parse_macro_input, parse_quote, DeriveInput, GenericParam, Generics, Ident, WhereClause,
+};
+
+pub fn derive_into_element(input: TokenStream) -> TokenStream {
+    let ast = parse_macro_input!(input as DeriveInput);
+    let type_name = ast.ident;
+
+    let placeholder_view_generics: Generics = parse_quote! { <V: 'static> };
+    let placeholder_view_type_name: Ident = parse_quote! { V };
+    let view_type_name: Ident;
+    let impl_generics: syn::ImplGenerics<'_>;
+    let type_generics: Option<syn::TypeGenerics<'_>>;
+    let where_clause: Option<&'_ WhereClause>;
+
+    match ast.generics.params.iter().find_map(|param| {
+        if let GenericParam::Type(type_param) = param {
+            Some(type_param.ident.clone())
+        } else {
+            None
+        }
+    }) {
+        Some(type_name) => {
+            view_type_name = type_name;
+            let generics = ast.generics.split_for_impl();
+            impl_generics = generics.0;
+            type_generics = Some(generics.1);
+            where_clause = generics.2;
+        }
+        _ => {
+            view_type_name = placeholder_view_type_name;
+            let generics = placeholder_view_generics.split_for_impl();
+            impl_generics = generics.0;
+            type_generics = None;
+            where_clause = generics.2;
+        }
+    }
+
+    impl_into_element(
+        &impl_generics,
+        &view_type_name,
+        &type_name,
+        &type_generics,
+        &where_clause,
+    )
+    .into()
+}
+
+pub fn impl_into_element(
+    impl_generics: &syn::ImplGenerics<'_>,
+    view_type_name: &Ident,
+    type_name: &Ident,
+    type_generics: &Option<syn::TypeGenerics<'_>>,
+    where_clause: &Option<&WhereClause>,
+) -> proc_macro2::TokenStream {
+    quote! {
+        impl #impl_generics gpui2::element::IntoElement<#view_type_name> for #type_name #type_generics
+        #where_clause
+        {
+            type Element = Self;
+
+            fn into_element(self) -> Self {
+                self
+            }
+        }
+    }
+}

crates/gpui3_macros/src/gpui3_macros.rs 🔗

@@ -0,0 +1,20 @@
+use proc_macro::TokenStream;
+
+mod derive_element;
+mod derive_into_element;
+mod style_helpers;
+
+#[proc_macro]
+pub fn style_helpers(args: TokenStream) -> TokenStream {
+    style_helpers::style_helpers(args)
+}
+
+#[proc_macro_derive(Element, attributes(element_crate))]
+pub fn derive_element(input: TokenStream) -> TokenStream {
+    derive_element::derive_element(input)
+}
+
+#[proc_macro_derive(IntoElement, attributes(element_crate))]
+pub fn derive_into_element(input: TokenStream) -> TokenStream {
+    derive_into_element::derive_into_element(input)
+}

crates/gpui3_macros/src/style_helpers.rs 🔗

@@ -0,0 +1,332 @@
+use proc_macro::TokenStream;
+use proc_macro2::TokenStream as TokenStream2;
+use quote::{format_ident, quote};
+use syn::{
+    parse::{Parse, ParseStream, Result},
+    parse_macro_input,
+};
+
+struct StyleableMacroInput;
+
+impl Parse for StyleableMacroInput {
+    fn parse(_input: ParseStream) -> Result<Self> {
+        Ok(StyleableMacroInput)
+    }
+}
+
+pub fn style_helpers(input: TokenStream) -> TokenStream {
+    let _ = parse_macro_input!(input as StyleableMacroInput);
+    let methods = generate_methods();
+
+    for method in &methods {
+        println!("method: {}", method);
+    }
+
+    let output = quote! {
+        #(#methods)*
+    };
+
+    output.into()
+}
+
+fn generate_methods() -> Vec<TokenStream2> {
+    let mut methods = Vec::new();
+
+    for (prefix, auto_allowed, fields) in box_prefixes() {
+        for (suffix, length_tokens) in box_suffixes() {
+            if auto_allowed || suffix != "auto" {
+                let method = generate_method(prefix, suffix, &fields, length_tokens);
+                methods.push(method);
+            }
+        }
+    }
+
+    for (prefix, fields) in corner_prefixes() {
+        for (suffix, radius_tokens) in corner_suffixes() {
+            let method = generate_method(prefix, suffix, &fields, radius_tokens);
+            methods.push(method);
+        }
+    }
+
+    for (prefix, fields) in border_prefixes() {
+        for (suffix, width_tokens) in border_suffixes() {
+            let method = generate_method(prefix, suffix, &fields, width_tokens);
+            methods.push(method);
+        }
+    }
+
+    methods
+}
+
+fn generate_method(
+    prefix: &'static str,
+    suffix: &'static str,
+    fields: &Vec<TokenStream2>,
+    length_tokens: TokenStream2,
+) -> TokenStream2 {
+    let method_name = if suffix.is_empty() {
+        format_ident!("{}", prefix)
+    } else {
+        format_ident!("{}_{}", prefix, suffix)
+    };
+
+    let field_assignments = fields
+        .iter()
+        .map(|field_tokens| {
+            quote! {
+                style.#field_tokens = Some(gpui2::#length_tokens.into());
+            }
+        })
+        .collect::<Vec<_>>();
+
+    let method = quote! {
+        fn #method_name(mut self) -> Self where Self: std::marker::Sized {
+            let style = self.declared_style();
+            #(#field_assignments)*
+            self
+        }
+    };
+
+    method
+}
+
+fn box_prefixes() -> Vec<(&'static str, bool, Vec<TokenStream2>)> {
+    vec![
+        ("w", true, vec![quote! { size.width }]),
+        ("h", true, vec![quote! { size.height }]),
+        (
+            "size",
+            true,
+            vec![quote! {size.width}, quote! {size.height}],
+        ),
+        ("min_w", false, vec![quote! { min_size.width }]),
+        ("min_h", false, vec![quote! { min_size.height }]),
+        ("max_w", false, vec![quote! { max_size.width }]),
+        ("max_h", false, vec![quote! { max_size.height }]),
+        (
+            "m",
+            true,
+            vec![
+                quote! { margin.top },
+                quote! { margin.bottom },
+                quote! { margin.left },
+                quote! { margin.right },
+            ],
+        ),
+        ("mt", true, vec![quote! { margin.top }]),
+        ("mb", true, vec![quote! { margin.bottom }]),
+        (
+            "my",
+            true,
+            vec![quote! { margin.top }, quote! { margin.bottom }],
+        ),
+        (
+            "mx",
+            true,
+            vec![quote! { margin.left }, quote! { margin.right }],
+        ),
+        ("ml", true, vec![quote! { margin.left }]),
+        ("mr", true, vec![quote! { margin.right }]),
+        (
+            "p",
+            false,
+            vec![
+                quote! { padding.top },
+                quote! { padding.bottom },
+                quote! { padding.left },
+                quote! { padding.right },
+            ],
+        ),
+        ("pt", false, vec![quote! { padding.top }]),
+        ("pb", false, vec![quote! { padding.bottom }]),
+        (
+            "px",
+            false,
+            vec![quote! { padding.left }, quote! { padding.right }],
+        ),
+        (
+            "py",
+            false,
+            vec![quote! { padding.top }, quote! { padding.bottom }],
+        ),
+        ("pl", false, vec![quote! { padding.left }]),
+        ("pr", false, vec![quote! { padding.right }]),
+        ("top", true, vec![quote! { inset.top }]),
+        ("bottom", true, vec![quote! { inset.bottom }]),
+        ("left", true, vec![quote! { inset.left }]),
+        ("right", true, vec![quote! { inset.right }]),
+        (
+            "gap",
+            false,
+            vec![quote! { gap.width }, quote! { gap.height }],
+        ),
+        ("gap_x", false, vec![quote! { gap.width }]),
+        ("gap_y", false, vec![quote! { gap.height }]),
+    ]
+}
+
+fn box_suffixes() -> Vec<(&'static str, TokenStream2)> {
+    vec![
+        ("0", quote! { px(0.) }),
+        ("0p5", quote! { rems(0.125) }),
+        ("1", quote! { rems(0.25) }),
+        ("1p5", quote! { rems(0.375) }),
+        ("2", quote! { rems(0.5) }),
+        ("2p5", quote! { rems(0.625) }),
+        ("3", quote! { rems(0.75) }),
+        ("3p5", quote! { rems(0.875) }),
+        ("4", quote! { rems(1.) }),
+        ("5", quote! { rems(1.25) }),
+        ("6", quote! { rems(1.5) }),
+        ("7", quote! { rems(1.75) }),
+        ("8", quote! { rems(2.0) }),
+        ("9", quote! { rems(2.25) }),
+        ("10", quote! { rems(2.5) }),
+        ("11", quote! { rems(2.75) }),
+        ("12", quote! { rems(3.) }),
+        ("16", quote! { rems(4.) }),
+        ("20", quote! { rems(5.) }),
+        ("24", quote! { rems(6.) }),
+        ("32", quote! { rems(8.) }),
+        ("40", quote! { rems(10.) }),
+        ("48", quote! { rems(12.) }),
+        ("56", quote! { rems(14.) }),
+        ("64", quote! { rems(16.) }),
+        ("72", quote! { rems(18.) }),
+        ("80", quote! { rems(20.) }),
+        ("96", quote! { rems(24.) }),
+        ("auto", quote! { auto() }),
+        ("px", quote! { px(1.) }),
+        ("full", quote! { relative(1.) }),
+        ("1_2", quote! { relative(0.5) }),
+        ("1_3", quote! { relative(1./3.) }),
+        ("2_3", quote! { relative(2./3.) }),
+        ("1_4", quote! { relative(0.25) }),
+        ("2_4", quote! { relative(0.5) }),
+        ("3_4", quote! { relative(0.75) }),
+        ("1_5", quote! { relative(0.2) }),
+        ("2_5", quote! { relative(0.4) }),
+        ("3_5", quote! { relative(0.6) }),
+        ("4_5", quote! { relative(0.8) }),
+        ("1_6", quote! { relative(1./6.) }),
+        ("5_6", quote! { relative(5./6.) }),
+        ("1_12", quote! { relative(1./12.) }),
+        // ("screen_50", quote! { DefiniteLength::Vh(50.0) }),
+        // ("screen_75", quote! { DefiniteLength::Vh(75.0) }),
+        // ("screen", quote! { DefiniteLength::Vh(100.0) }),
+    ]
+}
+
+fn corner_prefixes() -> Vec<(&'static str, Vec<TokenStream2>)> {
+    vec![
+        (
+            "rounded",
+            vec![
+                quote! { corner_radii.top_left },
+                quote! { corner_radii.top_right },
+                quote! { corner_radii.bottom_right },
+                quote! { corner_radii.bottom_left },
+            ],
+        ),
+        (
+            "rounded_t",
+            vec![
+                quote! { corner_radii.top_left },
+                quote! { corner_radii.top_right },
+            ],
+        ),
+        (
+            "rounded_b",
+            vec![
+                quote! { corner_radii.bottom_left },
+                quote! { corner_radii.bottom_right },
+            ],
+        ),
+        (
+            "rounded_r",
+            vec![
+                quote! { corner_radii.top_right },
+                quote! { corner_radii.bottom_right },
+            ],
+        ),
+        (
+            "rounded_l",
+            vec![
+                quote! { corner_radii.top_left },
+                quote! { corner_radii.bottom_left },
+            ],
+        ),
+        ("rounded_tl", vec![quote! { corner_radii.top_left }]),
+        ("rounded_tr", vec![quote! { corner_radii.top_right }]),
+        ("rounded_bl", vec![quote! { corner_radii.bottom_left }]),
+        ("rounded_br", vec![quote! { corner_radii.bottom_right }]),
+    ]
+}
+
+fn corner_suffixes() -> Vec<(&'static str, TokenStream2)> {
+    vec![
+        ("none", quote! { px(0.) }),
+        ("sm", quote! { rems(0.125) }),
+        ("md", quote! { rems(0.25) }),
+        ("lg", quote! { rems(0.5) }),
+        ("xl", quote! { rems(0.75) }),
+        ("2xl", quote! { rems(1.) }),
+        ("3xl", quote! { rems(1.5) }),
+        ("full", quote! {  px(9999.) }),
+    ]
+}
+
+fn border_prefixes() -> Vec<(&'static str, Vec<TokenStream2>)> {
+    vec![
+        (
+            "border",
+            vec![
+                quote! { border_widths.top },
+                quote! { border_widths.right },
+                quote! { border_widths.bottom },
+                quote! { border_widths.left },
+            ],
+        ),
+        ("border_t", vec![quote! { border_widths.top }]),
+        ("border_b", vec![quote! { border_widths.bottom }]),
+        ("border_r", vec![quote! { border_widths.right }]),
+        ("border_l", vec![quote! { border_widths.left }]),
+        (
+            "border_x",
+            vec![
+                quote! { border_widths.left },
+                quote! { border_widths.right },
+            ],
+        ),
+        (
+            "border_y",
+            vec![
+                quote! { border_widths.top },
+                quote! { border_widths.bottom },
+            ],
+        ),
+    ]
+}
+
+fn border_suffixes() -> Vec<(&'static str, TokenStream2)> {
+    vec![
+        ("", quote! { px(1.) }),
+        ("0", quote! { px(0.) }),
+        ("1", quote! { px(1.) }),
+        ("2", quote! { px(2.) }),
+        ("3", quote! { px(3.) }),
+        ("4", quote! { px(4.) }),
+        ("5", quote! { px(5.) }),
+        ("6", quote! { px(6.) }),
+        ("7", quote! { px(7.) }),
+        ("8", quote! { px(8.) }),
+        ("9", quote! { px(9.) }),
+        ("10", quote! { px(10.) }),
+        ("11", quote! { px(11.) }),
+        ("12", quote! { px(12.) }),
+        ("16", quote! { px(16.) }),
+        ("20", quote! { px(20.) }),
+        ("24", quote! { px(24.) }),
+        ("32", quote! { px(32.) }),
+    ]
+}

crates/storybook2/src/collab_panel.rs 🔗

@@ -1,6 +1,7 @@
 use crate::theme::{theme, Theme};
 use gpui3::{
-    div, img, svg, ArcCow, Element, IntoAnyElement, ParentElement, ScrollState, Styled, ViewContext,
+    div, img, svg, ArcCow, Element, IntoAnyElement, ParentElement, ScrollState, StyleHelpers,
+    ViewContext,
 };
 use std::marker::PhantomData;
 
@@ -117,7 +118,7 @@ impl<V: 'static> CollabPanelElement<V> {
         label: impl IntoAnyElement<V>,
         expanded: bool,
         theme: &Theme,
-    ) -> impl Element {
+    ) -> impl Element<State = V> {
         div()
             .h_7()
             .px_2()
@@ -145,16 +146,16 @@ impl<V: 'static> CollabPanelElement<V> {
         avatar_uri: impl Into<ArcCow<'static, str>>,
         label: impl IntoAnyElement<V>,
         theme: &Theme,
-    ) -> impl Element {
+    ) -> impl Element<State = V> {
         div()
             .h_7()
             .px_2()
             .flex()
             .items_center()
-            .hover()
-            .fill(theme.lowest.variant.hovered.background)
-            .active()
-            .fill(theme.lowest.variant.pressed.background)
+            // .hover()
+            // .fill(theme.lowest.variant.hovered.background)
+            // .active()
+            // .fill(theme.lowest.variant.pressed.background)
             .child(
                 div()
                     .flex()

crates/storybook2/src/storybook2.rs 🔗

@@ -1,10 +1,9 @@
 #![allow(dead_code, unused_variables)]
 
 use crate::theme::Theme;
-use ::theme as legacy_theme;
 use element_ext::ElementExt;
 use gpui3::{Element, ViewContext};
-use legacy_theme::ThemeSettings;
+
 use log::LevelFilter;
 use simplelog::SimpleLogger;
 
@@ -22,7 +21,9 @@ mod workspace;
 fn main() {
     SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
 
-    gpui3::App::new().run(|cx| cx.open_window(Default::default(), |cx| todo!()));
+    gpui3::App::new().run(|cx| {
+        let window: gpui3::WindowHandle<()> = cx.open_window(Default::default(), |cx| todo!());
+    });
 
     // gpui3::App::new(Assets).unwrap().run(|cx| {
     //     let mut store = SettingsStore::default();
@@ -56,19 +57,20 @@ fn storybook<V: 'static>(cx: &mut ViewContext<V>) -> impl Element {
 
 // Nathan: During the transition to gpui2, we will include the base theme on the legacy Theme struct.
 fn current_theme<V: 'static>(cx: &mut ViewContext<V>) -> Theme {
-    settings::get::<ThemeSettings>(cx)
-        .theme
-        .deserialized_base_theme
-        .lock()
-        .get_or_insert_with(|| {
-            let theme: Theme =
-                serde_json::from_value(settings::get::<ThemeSettings>(cx).theme.base_theme.clone())
-                    .unwrap();
-            Box::new(theme)
-        })
-        .downcast_ref::<Theme>()
-        .unwrap()
-        .clone()
+    todo!()
+    // settings::get::<ThemeSettings>(cx)
+    //     .theme
+    //     .deserialized_base_theme
+    //     .lock()
+    //     .get_or_insert_with(|| {
+    //         let theme: Theme =
+    //             serde_json::from_value(settings::get::<ThemeSettings>(cx).theme.base_theme.clone())
+    //                 .unwrap();
+    //         Box::new(theme)
+    //     })
+    //     .downcast_ref::<Theme>()
+    //     .unwrap()
+    //     .clone()
 }
 
 use rust_embed::RustEmbed;

crates/storybook2/src/theme.rs 🔗

@@ -1,10 +1,6 @@
-use gpui3::{
-    serde_json, AppContext, Element, Hsla, IntoAnyElement, Layout, LayoutId, Vector2F, ViewContext,
-    WindowContext,
-};
+use gpui3::{Element, Hsla, Layout, LayoutId, ViewContext, WindowContext};
 use serde::{de::Visitor, Deserialize, Deserializer};
-use std::{collections::HashMap, fmt, marker::PhantomData};
-use theme::ThemeSettings;
+use std::{collections::HashMap, fmt};
 
 #[derive(Deserialize, Clone, Default, Debug)]
 pub struct Theme {
@@ -137,6 +133,7 @@ pub struct Themed<E> {
 }
 
 impl<E: Element> Element for Themed<E> {
+    type State = E::State;
     type FrameState = E::FrameState;
 
     fn layout(
@@ -147,43 +144,45 @@ impl<E: Element> Element for Themed<E> {
     where
         Self: Sized,
     {
-        cx.push_theme(self.theme.clone());
+        // cx.push_theme(self.theme.clone());
         let result = self.child.layout(state, cx);
-        cx.pop_theme();
+        // cx.pop_theme();
         result
     }
 
     fn paint(
         &mut self,
-        view: &mut V,
-        layout: &Layout,
-        state: &mut Self::FrameState,
-        cx: &mut ViewContext<V>,
+        layout: Layout,
+        state: &mut Self::State,
+        frame_state: &mut Self::FrameState,
+        cx: &mut ViewContext<Self::State>,
     ) where
         Self: Sized,
     {
-        cx.push_theme(self.theme.clone());
-        self.child.paint(view, layout, state, cx);
-        cx.pop_theme();
+        // todo!
+        // cx.push_theme(self.theme.clone());
+        self.child.paint(layout, state, frame_state, cx);
+        // cx.pop_theme();
     }
 }
 
-fn preferred_theme<V: 'static>(cx: &AppContext) -> Theme {
-    settings::get::<ThemeSettings>(cx)
-        .theme
-        .deserialized_base_theme
-        .lock()
-        .get_or_insert_with(|| {
-            let theme: Theme =
-                serde_json::from_value(settings::get::<ThemeSettings>(cx).theme.base_theme.clone())
-                    .unwrap();
-            Box::new(theme)
-        })
-        .downcast_ref::<Theme>()
-        .unwrap()
-        .clone()
-}
+// fn preferred_theme<V: 'static>(cx: &AppContext) -> Theme {
+//     settings::get::<ThemeSettings>(cx)
+//         .theme
+//         .deserialized_base_theme
+//         .lock()
+//         .get_or_insert_with(|| {
+//             let theme: Theme =
+//                 serde_json::from_value(settings::get::<ThemeSettings>(cx).theme.base_theme.clone())
+//                     .unwrap();
+//             Box::new(theme)
+//         })
+//         .downcast_ref::<Theme>()
+//         .unwrap()
+//         .clone()
+// }
 
 pub fn theme<'a>(cx: &'a WindowContext) -> &'a Theme {
-    cx.theme::<Theme>()
+    todo!()
+    // cx.theme::<Theme>()
 }

crates/storybook2/src/workspace.rs 🔗

@@ -1,5 +1,5 @@
 use crate::{collab_panel::collab_panel, theme::theme};
-use gpui3::{div, img, svg, Element, ParentElement, ScrollState, Styled, ViewContext};
+use gpui3::{div, img, svg, Element, ParentElement, ScrollState, StyleHelpers, ViewContext};
 
 #[derive(Default)]
 struct WorkspaceElement {
@@ -7,12 +7,16 @@ struct WorkspaceElement {
     right_scroll_state: ScrollState,
 }
 
-pub fn workspace<V: 'static>() -> impl Element {
+pub fn workspace() -> impl Element {
     WorkspaceElement::default()
 }
 
 impl WorkspaceElement {
-    fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl Element {
+    fn render<V: 'static>(
+        &mut self,
+        _: &mut V,
+        cx: &mut ViewContext<V>,
+    ) -> impl Element<State = V> {
         let theme = theme(cx);
 
         div()
@@ -43,12 +47,16 @@ impl WorkspaceElement {
 
 struct TitleBar;
 
-pub fn titlebar<V: 'static>() -> impl Element {
+pub fn titlebar<V: 'static>() -> impl Element<State = V> {
     TitleBar
 }
 
 impl TitleBar {
-    fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl Element {
+    fn render<V: 'static>(
+        &mut self,
+        _: &mut V,
+        cx: &mut ViewContext<V>,
+    ) -> impl Element<State = V> {
         let theme = theme(cx);
         div()
             .flex()
@@ -61,7 +69,7 @@ impl TitleBar {
             .child(self.right_group(cx))
     }
 
-    fn left_group<V: 'static>(&mut self, cx: &mut ViewContext<V>) -> impl Element {
+    fn left_group<V: 'static>(&mut self, cx: &mut ViewContext<V>) -> impl Element<State = V> {
         let theme = theme(cx);
         div()
             .flex()
@@ -111,10 +119,10 @@ impl TitleBar {
                             .justify_center()
                             .px_2()
                             .rounded_md()
-                            .hover()
-                            .fill(theme.lowest.base.hovered.background)
-                            .active()
-                            .fill(theme.lowest.base.pressed.background)
+                            // .hover()
+                            // .fill(theme.lowest.base.hovered.background)
+                            // .active()
+                            // .fill(theme.lowest.base.pressed.background)
                             .child(div().text_sm().child("project")),
                     )
                     .child(
@@ -126,16 +134,16 @@ impl TitleBar {
                             .px_2()
                             .rounded_md()
                             .text_color(theme.lowest.variant.default.foreground)
-                            .hover()
-                            .fill(theme.lowest.base.hovered.background)
-                            .active()
-                            .fill(theme.lowest.base.pressed.background)
+                            // .hover()
+                            // .fill(theme.lowest.base.hovered.background)
+                            // .active()
+                            // .fill(theme.lowest.base.pressed.background)
                             .child(div().text_sm().child("branch")),
                     ),
             )
     }
 
-    fn right_group<V: 'static>(&mut self, cx: &mut ViewContext<V>) -> impl Element {
+    fn right_group<V: 'static>(&mut self, cx: &mut ViewContext<V>) -> impl Element<State = V> {
         let theme = theme(cx);
         div()
             .flex()
@@ -173,10 +181,10 @@ impl TitleBar {
                                 .flex()
                                 .items_center()
                                 .justify_center()
-                                .hover()
-                                .fill(theme.lowest.base.hovered.background)
-                                .active()
-                                .fill(theme.lowest.base.pressed.background)
+                                // .hover()
+                                // .fill(theme.lowest.base.hovered.background)
+                                // .active()
+                                // .fill(theme.lowest.base.pressed.background)
                                 .child(
                                     svg()
                                         .path("icons/microphone.svg")
@@ -193,10 +201,10 @@ impl TitleBar {
                                 .flex()
                                 .items_center()
                                 .justify_center()
-                                .hover()
-                                .fill(theme.lowest.base.hovered.background)
-                                .active()
-                                .fill(theme.lowest.base.pressed.background)
+                                // .hover()
+                                // .fill(theme.lowest.base.hovered.background)
+                                // .active()
+                                // .fill(theme.lowest.base.pressed.background)
                                 .child(
                                     svg()
                                         .path("icons/radix/speaker-loud.svg")
@@ -213,10 +221,10 @@ impl TitleBar {
                                 .flex()
                                 .items_center()
                                 .justify_center()
-                                .hover()
-                                .fill(theme.lowest.base.hovered.background)
-                                .active()
-                                .fill(theme.lowest.base.pressed.background)
+                                // .hover()
+                                // .fill(theme.lowest.base.hovered.background)
+                                // .active()
+                                // .fill(theme.lowest.base.pressed.background)
                                 .child(
                                     svg()
                                         .path("icons/radix/desktop.svg")
@@ -238,10 +246,10 @@ impl TitleBar {
                         .justify_center()
                         .rounded_md()
                         .gap_0p5()
-                        .hover()
-                        .fill(theme.lowest.base.hovered.background)
-                        .active()
-                        .fill(theme.lowest.base.pressed.background)
+                        // .hover()
+                        // .fill(theme.lowest.base.hovered.background)
+                        // .active()
+                        // .fill(theme.lowest.base.pressed.background)
                         .child(
                             img()
                                 .uri("https://avatars.githubusercontent.com/u/1714999?v=4")
@@ -265,12 +273,16 @@ impl TitleBar {
 
 struct StatusBar;
 
-pub fn statusbar<V: 'static>() -> impl Element {
+pub fn statusbar<V: 'static>() -> impl Element<State = V> {
     StatusBar
 }
 
 impl StatusBar {
-    fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl Element {
+    fn render<V: 'static>(
+        &mut self,
+        _: &mut V,
+        cx: &mut ViewContext<V>,
+    ) -> impl Element<State = V> {
         let theme = theme(cx);
         div()
             .flex()
@@ -283,7 +295,7 @@ impl StatusBar {
             .child(self.right_group(cx))
     }
 
-    fn left_group<V: 'static>(&mut self, cx: &mut ViewContext<V>) -> impl Element {
+    fn left_group<V: 'static>(&mut self, cx: &mut ViewContext<V>) -> impl Element<State = V> {
         let theme = theme(cx);
         div()
             .flex()
@@ -358,10 +370,10 @@ impl StatusBar {
                             .gap_0p5()
                             .px_1()
                             .text_color(theme.lowest.variant.default.foreground)
-                            .hover()
-                            .fill(theme.lowest.base.hovered.background)
-                            .active()
-                            .fill(theme.lowest.base.pressed.background)
+                            // .hover()
+                            // .fill(theme.lowest.base.hovered.background)
+                            // .active()
+                            // .fill(theme.lowest.base.pressed.background)
                             .child(
                                 svg()
                                     .path("icons/error.svg")
@@ -380,7 +392,7 @@ impl StatusBar {
             )
     }
 
-    fn right_group<V: 'static>(&mut self, cx: &mut ViewContext<V>) -> impl Element {
+    fn right_group<V: 'static>(&mut self, cx: &mut ViewContext<V>) -> impl Element<State = V> {
         let theme = theme(cx);
         div()
             .flex()