WIP

Nathan Sobo created

Change summary

crates/gpui3/src/element.rs                    | 19 ++--
crates/gpui3/src/elements/div.rs               |  5 
crates/gpui3/src/geometry.rs                   |  9 +
crates/gpui3/src/gpui3.rs                      |  3 
crates/gpui3/src/platform/mac/platform.rs      |  4 
crates/gpui3/src/platform/mac/window.rs        |  2 
crates/gpui3/src/platform/test.rs              | 47 +++++----
crates/gpui3/src/style_helpers.rs              |  2 
crates/gpui3/src/taffy.rs                      | 85 +++++++++++++++---
crates/gpui3_macros/src/derive_element.rs      | 87 +++++--------------
crates/gpui3_macros/src/derive_into_element.rs | 69 ---------------
crates/gpui3_macros/src/gpui3_macros.rs        |  6 -
crates/gpui3_macros/src/style_helpers.rs       |  8 -
crates/storybook2/src/theme.rs                 |  6 
crates/storybook2/src/workspace.rs             |  2 
15 files changed, 147 insertions(+), 207 deletions(-)

Detailed changes

crates/gpui3/src/element.rs 🔗

@@ -47,8 +47,8 @@ trait ElementObject<S> {
     fn layout(&mut self, state: &mut S, cx: &mut ViewContext<S>) -> Result<LayoutId>;
     fn paint(
         &mut self,
-        parent_origin: super::Point<Pixels>,
         state: &mut S,
+        offset: Option<Point<Pixels>>,
         cx: &mut ViewContext<S>,
     ) -> Result<()>;
 }
@@ -96,8 +96,8 @@ impl<E: Element> ElementObject<E::State> for RenderedElement<E> {
 
     fn paint(
         &mut self,
-        parent_origin: Point<Pixels>,
         state: &mut E::State,
+        offset: Option<Point<Pixels>>,
         cx: &mut ViewContext<E::State>,
     ) -> Result<()> {
         self.phase = match std::mem::take(&mut self.phase) {
@@ -107,8 +107,8 @@ impl<E: Element> ElementObject<E::State> for RenderedElement<E> {
                 layout_id,
                 mut frame_state,
             } => {
-                let mut layout = cx.layout(layout_id)?;
-                layout.bounds.origin += parent_origin;
+                let mut layout = cx.layout(layout_id)?.clone();
+                offset.map(|offset| layout.bounds.origin += offset);
                 self.element
                     .paint(layout.clone(), state, &mut frame_state, cx)?;
                 ElementRenderPhase::Painted {
@@ -143,11 +143,11 @@ impl<S> AnyElement<S> {
 
     pub fn paint(
         &mut self,
-        parent_origin: Point<Pixels>,
         state: &mut S,
+        offset: Option<Point<Pixels>>,
         cx: &mut ViewContext<S>,
     ) -> Result<()> {
-        self.0.paint(parent_origin, state, cx)
+        self.0.paint(state, offset, cx)
     }
 }
 
@@ -215,9 +215,8 @@ impl<S: 'static> Element for View<S> {
         element: &mut Self::FrameState,
         cx: &mut ViewContext<Self::State>,
     ) -> Result<()> {
-        self.state.update(cx, |state, cx| {
-            element.paint(layout.bounds.origin, state, cx)
-        })
+        self.state
+            .update(cx, |state, cx| element.paint(state, None, cx))
     }
 }
 
@@ -251,7 +250,7 @@ impl<S: 'static> ViewObject for View<S> {
             element
                 .downcast_mut::<AnyElement<S>>()
                 .unwrap()
-                .paint(layout.bounds.origin, state, cx)
+                .paint(state, None, cx)
         })
     }
 }

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

@@ -67,8 +67,6 @@ impl<S: 'static> Element for Div<S> {
         style.paint_background(bounds, cx);
         // self.interaction_handlers().paint(order, bounds, cx);
 
-        let scrolled_origin = bounds.origin - self.scroll_offset(&style.overflow);
-
         // // TODO: Support only one dimension being hidden
         // let mut pop_layer = false;
         // if style.overflow.y != Overflow::Visible || style.overflow.x != Overflow::Visible {
@@ -76,8 +74,9 @@ impl<S: 'static> Element for Div<S> {
         //     pop_layer = true;
         // }
 
+        let scroll_offset = self.scroll_offset(&style.overflow);
         for child in &mut self.children {
-            child.paint(scrolled_origin, state, cx)?;
+            child.paint(state, Some(scroll_offset), cx)?;
         }
 
         // if pop_layer {

crates/gpui3/src/geometry.rs 🔗

@@ -2,7 +2,7 @@ use bytemuck::{Pod, Zeroable};
 use core::fmt::Debug;
 use derive_more::{Add, AddAssign, Div, Mul, Sub, SubAssign};
 use refineable::Refineable;
-use std::ops::{Add, Mul, Sub, SubAssign};
+use std::ops::{Add, AddAssign, Mul, Sub, SubAssign};
 
 #[derive(
     Refineable, Default, Add, AddAssign, Sub, SubAssign, Mul, Div, Copy, Debug, PartialEq, Eq, Hash,
@@ -38,6 +38,13 @@ impl<T: Clone + Debug + Sub<Output = T>> SubAssign<Size<T>> for Point<T> {
     }
 }
 
+impl<T: Clone + Debug + Add<Output = T> + Copy> AddAssign<T> for Point<T> {
+    fn add_assign(&mut self, rhs: T) {
+        self.x = self.x.clone() + rhs;
+        self.y = self.y.clone() + rhs;
+    }
+}
+
 impl<T: Clone + Debug + std::cmp::PartialOrd> Point<T> {
     pub fn max(&self, other: &Self) -> Self {
         Point {

crates/gpui3/src/gpui3.rs 🔗

@@ -15,13 +15,14 @@ mod text_system;
 mod util;
 mod window;
 
-use anyhow::Result;
+pub use anyhow::Result;
 pub use app::*;
 pub use color::*;
 pub use element::*;
 pub use elements::*;
 pub use executor::*;
 pub use geometry::*;
+pub use gpui3_macros::*;
 pub use platform::*;
 pub use refineable::*;
 pub use scene::*;

crates/gpui3/src/platform/mac/platform.rs 🔗

@@ -996,7 +996,7 @@ extern "C" fn open_urls(this: &mut Object, _: Sel, _: id, urls: id) {
     }
 }
 
-extern "C" fn handle_menu_item(this: &mut Object, _: Sel, item: id) {
+extern "C" fn handle_menu_item(__this: &mut Object, _: Sel, __item: id) {
     todo!()
     // unsafe {
     //     let platform = get_foreground_platform(this);
@@ -1012,7 +1012,7 @@ extern "C" fn handle_menu_item(this: &mut Object, _: Sel, item: id) {
     // }
 }
 
-extern "C" fn validate_menu_item(this: &mut Object, _: Sel, item: id) -> bool {
+extern "C" fn validate_menu_item(__this: &mut Object, _: Sel, __item: id) -> bool {
     todo!()
     // unsafe {
     //     let mut result = false;

crates/gpui3/src/platform/mac/window.rs 🔗

@@ -1378,7 +1378,7 @@ extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
     };
 }
 
-extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
+extern "C" fn display_layer(_this: &Object, _: Sel, _: id) {
     // unsafe {
     // let window_state = get_window_state(this);
     // let mut window_state = window_state.as_ref().borrow_mut();

crates/gpui3/src/platform/test.rs 🔗

@@ -17,7 +17,7 @@ impl Platform for TestPlatform {
         todo!()
     }
 
-    fn run(&self, on_finish_launching: Box<dyn FnOnce()>) {
+    fn run(&self, _on_finish_launching: Box<dyn FnOnce()>) {
         todo!()
     }
 
@@ -29,7 +29,7 @@ impl Platform for TestPlatform {
         todo!()
     }
 
-    fn activate(&self, ignoring_other_apps: bool) {
+    fn activate(&self, _ignoring_other_apps: bool) {
         todo!()
     }
 
@@ -49,7 +49,7 @@ impl Platform for TestPlatform {
         todo!()
     }
 
-    fn screen_by_id(&self, id: uuid::Uuid) -> Option<std::rc::Rc<dyn crate::PlatformScreen>> {
+    fn screen_by_id(&self, _id: uuid::Uuid) -> Option<std::rc::Rc<dyn crate::PlatformScreen>> {
         todo!()
     }
 
@@ -59,55 +59,55 @@ impl Platform for TestPlatform {
 
     fn open_window(
         &self,
-        handle: crate::AnyWindowHandle,
-        options: crate::WindowOptions,
+        _handle: crate::AnyWindowHandle,
+        _options: crate::WindowOptions,
     ) -> Box<dyn crate::PlatformWindow> {
         todo!()
     }
 
-    fn open_url(&self, url: &str) {
+    fn open_url(&self, _url: &str) {
         todo!()
     }
 
-    fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>) {
+    fn on_open_urls(&self, _callback: Box<dyn FnMut(Vec<String>)>) {
         todo!()
     }
 
     fn prompt_for_paths(
         &self,
-        options: crate::PathPromptOptions,
+        _options: crate::PathPromptOptions,
     ) -> futures::channel::oneshot::Receiver<Option<Vec<std::path::PathBuf>>> {
         todo!()
     }
 
     fn prompt_for_new_path(
         &self,
-        directory: &std::path::Path,
+        _directory: &std::path::Path,
     ) -> futures::channel::oneshot::Receiver<Option<std::path::PathBuf>> {
         todo!()
     }
 
-    fn reveal_path(&self, path: &std::path::Path) {
+    fn reveal_path(&self, _path: &std::path::Path) {
         todo!()
     }
 
-    fn on_become_active(&self, callback: Box<dyn FnMut()>) {
+    fn on_become_active(&self, _callback: Box<dyn FnMut()>) {
         todo!()
     }
 
-    fn on_resign_active(&self, callback: Box<dyn FnMut()>) {
+    fn on_resign_active(&self, _callback: Box<dyn FnMut()>) {
         todo!()
     }
 
-    fn on_quit(&self, callback: Box<dyn FnMut()>) {
+    fn on_quit(&self, _callback: Box<dyn FnMut()>) {
         todo!()
     }
 
-    fn on_reopen(&self, callback: Box<dyn FnMut()>) {
+    fn on_reopen(&self, _callback: Box<dyn FnMut()>) {
         todo!()
     }
 
-    fn on_event(&self, callback: Box<dyn FnMut(crate::Event) -> bool>) {
+    fn on_event(&self, _callback: Box<dyn FnMut(crate::Event) -> bool>) {
         todo!()
     }
 
@@ -131,11 +131,11 @@ impl Platform for TestPlatform {
         todo!()
     }
 
-    fn path_for_auxiliary_executable(&self, name: &str) -> anyhow::Result<std::path::PathBuf> {
+    fn path_for_auxiliary_executable(&self, _name: &str) -> anyhow::Result<std::path::PathBuf> {
         todo!()
     }
 
-    fn set_cursor_style(&self, style: crate::CursorStyle) {
+    fn set_cursor_style(&self, _style: crate::CursorStyle) {
         todo!()
     }
 
@@ -143,7 +143,7 @@ impl Platform for TestPlatform {
         todo!()
     }
 
-    fn write_to_clipboard(&self, item: crate::ClipboardItem) {
+    fn write_to_clipboard(&self, _item: crate::ClipboardItem) {
         todo!()
     }
 
@@ -151,15 +151,20 @@ impl Platform for TestPlatform {
         todo!()
     }
 
-    fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> anyhow::Result<()> {
+    fn write_credentials(
+        &self,
+        _url: &str,
+        _username: &str,
+        _password: &[u8],
+    ) -> anyhow::Result<()> {
         todo!()
     }
 
-    fn read_credentials(&self, url: &str) -> anyhow::Result<Option<(String, Vec<u8>)>> {
+    fn read_credentials(&self, _url: &str) -> anyhow::Result<Option<(String, Vec<u8>)>> {
         todo!()
     }
 
-    fn delete_credentials(&self, url: &str) -> anyhow::Result<()> {
+    fn delete_credentials(&self, _url: &str) -> anyhow::Result<()> {
         todo!()
     }
 }

crates/gpui3/src/style_helpers.rs 🔗

@@ -1,5 +1,5 @@
 use crate::{
-    self as gpui2, relative, rems, AlignItems, Display, Fill, FlexDirection, Hsla, JustifyContent,
+    self as gpui3, relative, rems, AlignItems, Display, Fill, FlexDirection, Hsla, JustifyContent,
     Length, Position, SharedString, Style, Styled,
 };
 

crates/gpui3/src/taffy.rs 🔗

@@ -2,29 +2,28 @@ use super::{
     AbsoluteLength, Bounds, DefiniteLength, Edges, Layout, Length, Pixels, Point, Result, Size,
     Style,
 };
+use collections::HashMap;
 use std::fmt::Debug;
-pub use taffy::tree::NodeId as LayoutId;
 use taffy::{
     geometry::Size as TaffySize,
     style::AvailableSpace as TaffyAvailableSpace,
-    tree::{Measurable, MeasureFunc},
+    tree::{Measurable, MeasureFunc, NodeId},
     Taffy,
 };
-pub struct TaffyLayoutEngine(Taffy);
 
-#[derive(Copy, Clone, Debug)]
-pub enum AvailableSpace {
-    /// The amount of space available is the specified number of pixels
-    Definite(Pixels),
-    /// The amount of space available is indefinite and the node should be laid out under a min-content constraint
-    MinContent,
-    /// The amount of space available is indefinite and the node should be laid out under a max-content constraint
-    MaxContent,
+pub struct TaffyLayoutEngine {
+    taffy: Taffy,
+    children_to_parents: HashMap<LayoutId, LayoutId>,
+    absolute_layouts: HashMap<LayoutId, Layout>,
 }
 
 impl TaffyLayoutEngine {
     pub fn new() -> Self {
-        TaffyLayoutEngine(Taffy::new())
+        TaffyLayoutEngine {
+            taffy: Taffy::new(),
+            children_to_parents: HashMap::default(),
+            absolute_layouts: HashMap::default(),
+        }
     }
 
     pub fn request_layout(
@@ -35,9 +34,17 @@ impl TaffyLayoutEngine {
     ) -> Result<LayoutId> {
         let style = style.to_taffy(rem_size);
         if children.is_empty() {
-            Ok(self.0.new_leaf(style)?)
+            Ok(self.taffy.new_leaf(style)?.into())
         } else {
-            Ok(self.0.new_with_children(style, children)?)
+            let parent_id = self
+                .taffy
+                // This is safe because LayoutId is repr(transparent) to taffy::tree::NodeId.
+                .new_with_children(style, unsafe { std::mem::transmute(children) })?
+                .into();
+            for child_id in children {
+                self.children_to_parents.insert(*child_id, parent_id);
+            }
+            Ok(parent_id)
         }
     }
 
@@ -54,15 +61,59 @@ impl TaffyLayoutEngine {
 
         let measurable = Box::new(Measureable(measure)) as Box<dyn Measurable>;
         Ok(self
-            .0
-            .new_leaf_with_measure(style, MeasureFunc::Boxed(measurable))?)
+            .taffy
+            .new_leaf_with_measure(style, MeasureFunc::Boxed(measurable))?
+            .into())
     }
 
     pub fn layout(&mut self, id: LayoutId) -> Result<Layout> {
-        Ok(self.0.layout(id).map(Into::into)?)
+        if let Some(layout) = self.absolute_layouts.get(&id).cloned() {
+            return Ok(layout);
+        }
+
+        let mut relative_layout: Layout = self.taffy.layout(id.into()).map(Into::into)?;
+        if let Some(parent_id) = self.children_to_parents.get(&id).copied() {
+            let parent_layout = self.layout(parent_id)?;
+            relative_layout.bounds.origin += parent_layout.bounds.origin;
+        }
+        self.absolute_layouts.insert(id, relative_layout.clone());
+
+        Ok(relative_layout)
+    }
+}
+
+#[derive(Copy, Clone, Eq, PartialEq, Debug)]
+#[repr(transparent)]
+pub struct LayoutId(NodeId);
+
+impl std::hash::Hash for LayoutId {
+    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+        u64::from(self.0).hash(state);
     }
 }
 
+impl From<NodeId> for LayoutId {
+    fn from(node_id: NodeId) -> Self {
+        Self(node_id)
+    }
+}
+
+impl From<LayoutId> for NodeId {
+    fn from(layout_id: LayoutId) -> NodeId {
+        layout_id.0
+    }
+}
+
+#[derive(Copy, Clone, Debug)]
+pub enum AvailableSpace {
+    /// The amount of space available is the specified number of pixels
+    Definite(Pixels),
+    /// The amount of space available is indefinite and the node should be laid out under a min-content constraint
+    MinContent,
+    /// The amount of space available is indefinite and the node should be laid out under a max-content constraint
+    MaxContent,
+}
+
 struct Measureable<F>(F);
 
 impl<F> taffy::tree::Measurable for Measureable<F>

crates/gpui3_macros/src/derive_element.rs 🔗

@@ -1,92 +1,49 @@
 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;
+use syn::{parse_macro_input, parse_quote, DeriveInput, GenericParam};
 
 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 mut state_type: Option<Ident> = None;
 
-    let lifetimes = if !lifetimes.is_empty() {
-        quote! { <#(#lifetimes),*> }
-    } else {
-        quote! {}
-    };
+    for param in ast.generics.params.iter() {
+        if let GenericParam::Type(type_param) = param {
+            state_type = Some(type_param.ident.clone())
+        }
+    }
 
-    let impl_into_element = impl_into_element(
-        &impl_generics,
-        &view_type_name,
-        &type_name,
-        &type_generics,
-        &where_clause,
-    );
+    let state_type_name = state_type.unwrap_or_else(|| parse_quote! { () });
+    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
 
     let gen = quote! {
-        impl #impl_generics gpui2::element::Element<#view_type_name> for #type_name #type_generics
+        impl #impl_generics gpui3::Element for #type_name #ty_generics
         #where_clause
         {
-            type PaintState = gpui2::element::AnyElement<#view_type_name #lifetimes>;
+            type State = #state_type_name;
+            type FrameState = gpui3::AnyElement<#state_type_name>;
 
             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)?;
+                state: &mut #state_type_name,
+                cx: &mut gpui3::ViewContext<V>,
+            ) -> anyhow::Result<(gpui3::LayoutId, Self::FrameState)> {
+                let mut rendered_element = self.render(state, cx).into_element().into_any();
+                let layout_id = rendered_element.layout(state, 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>,
+                layout: &gpui3::Layout,
+                state: &mut #state_type_name,
+                rendered_element: &mut Self::FrameState,
+                cx: &mut gpui3::ViewContext<V>,
             ) {
-                rendered_element.paint(view, parent_origin, cx);
+                rendered_element.paint(layout.origin, state, cx);
             }
         }
-
-        #impl_into_element
     };
 
     gen.into()

crates/gpui3_macros/src/derive_into_element.rs 🔗

@@ -1,69 +0,0 @@
-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 🔗

@@ -1,7 +1,6 @@
 use proc_macro::TokenStream;
 
 mod derive_element;
-mod derive_into_element;
 mod style_helpers;
 
 #[proc_macro]
@@ -13,8 +12,3 @@ pub fn style_helpers(args: TokenStream) -> TokenStream {
 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 🔗

@@ -17,15 +17,9 @@ impl Parse for 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()
 }
 
@@ -74,7 +68,7 @@ fn generate_method(
         .iter()
         .map(|field_tokens| {
             quote! {
-                style.#field_tokens = Some(gpui2::#length_tokens.into());
+                style.#field_tokens = Some(gpui3::#length_tokens.into());
             }
         })
         .collect::<Vec<_>>();

crates/storybook2/src/theme.rs 🔗

@@ -1,4 +1,4 @@
-use gpui3::{Element, Hsla, Layout, LayoutId, ViewContext, WindowContext};
+use gpui3::{Element, Hsla, Layout, LayoutId, Result, ViewContext, WindowContext};
 use serde::{de::Visitor, Deserialize, Deserializer};
 use std::{collections::HashMap, fmt};
 
@@ -156,13 +156,15 @@ impl<E: Element> Element for Themed<E> {
         state: &mut Self::State,
         frame_state: &mut Self::FrameState,
         cx: &mut ViewContext<Self::State>,
-    ) where
+    ) -> Result<()>
+    where
         Self: Sized,
     {
         // todo!
         // cx.push_theme(self.theme.clone());
         self.child.paint(layout, state, frame_state, cx);
         // cx.pop_theme();
+        Ok(())
     }
 }
 

crates/storybook2/src/workspace.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{collab_panel::collab_panel, theme::theme};
 use gpui3::{div, img, svg, Element, ParentElement, ScrollState, StyleHelpers, ViewContext};
 
-#[derive(Default)]
+#[derive(Element, Default)]
 struct WorkspaceElement {
     left_scroll_state: ScrollState,
     right_scroll_state: ScrollState,