Checkpoint

Nathan Sobo created

Change summary

crates/gpui/playground/src/components.rs                 | 232 ++++-----
crates/gpui/playground/src/div.rs                        |  28 
crates/gpui/playground/src/element.rs                    |  27 
crates/gpui/playground/src/layout_context.rs             |   2 
crates/gpui/playground/src/playground.rs                 |  55 +-
crates/gpui/playground/src/style.rs                      |  20 
crates/gpui/playground_macros/src/derive_element.rs      | 122 ++---
crates/gpui/playground_macros/src/derive_into_element.rs |  31 -
8 files changed, 241 insertions(+), 276 deletions(-)

Detailed changes

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

@@ -1,131 +1,101 @@
-// use crate::{
-//     element::{Element, ElementMetadata, ParentElement},
-//     frame,
-//     text::ArcCow,
-//     themes::rose_pine,
-// };
-// use gpui::{platform::MouseButton, ViewContext};
-// use playground_macros::Element;
-// use std::{marker::PhantomData, rc::Rc};
-
-// struct ButtonHandlers<V, D> {
-//     click: Option<Rc<dyn Fn(&mut V, &D, &mut ViewContext<V>)>>,
-// }
-
-// impl<V, D> Default for ButtonHandlers<V, D> {
-//     fn default() -> Self {
-//         Self { click: None }
-//     }
-// }
-
-// #[derive(Element)]
-// #[element_crate = "crate"]
-// pub struct Button<V: 'static, D: 'static> {
-//     metadata: ElementMetadata<V>,
-//     handlers: ButtonHandlers<V, D>,
-//     label: Option<ArcCow<'static, str>>,
-//     icon: Option<ArcCow<'static, str>>,
-//     data: Rc<D>,
-//     view_type: PhantomData<V>,
-// }
-
-// // Impl block for buttons without data.
-// // See below for an impl block for any button.
-// impl<V: 'static> Button<V, ()> {
-//     fn new() -> Self {
-//         Self {
-//             metadata: Default::default(),
-//             handlers: ButtonHandlers::default(),
-//             label: None,
-//             icon: None,
-//             data: Rc::new(()),
-//             view_type: PhantomData,
-//         }
-//     }
-
-//     pub fn data<D: 'static>(self, data: D) -> Button<V, D> {
-//         Button {
-//             metadata: Default::default(),
-//             handlers: ButtonHandlers::default(),
-//             label: self.label,
-//             icon: self.icon,
-//             data: Rc::new(data),
-//             view_type: PhantomData,
-//         }
-//     }
-// }
-
-// // Impl block for *any* button.
-// impl<V: 'static, D: 'static> Button<V, D> {
-//     pub fn label(mut self, label: impl Into<ArcCow<'static, str>>) -> Self {
-//         self.label = Some(label.into());
-//         self
-//     }
-
-//     pub fn icon(mut self, icon: impl Into<ArcCow<'static, str>>) -> Self {
-//         self.icon = Some(icon.into());
-//         self
-//     }
-
-//     pub fn click(self, handler: impl Fn(&mut V, &D, &mut ViewContext<V>) + 'static) -> Self {
-//         let data = self.data.clone();
-//         Element::click(self, MouseButton::Left, move |view, _, cx| {
-//             handler(view, data.as_ref(), cx);
-//         })
-//     }
-// }
-
-// pub fn button<V>() -> Button<V, ()> {
-//     Button::new()
-// }
-
-// impl<V: 'static, D: 'static> Button<V, D> {
-//     fn render(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
-//         // TODO: Drive theme from the context
-//         let button = frame()
-//             .fill(rose_pine::dawn().error(0.5))
-//             .h_4()
-//             .children(self.label.clone());
-
-//         if let Some(handler) = self.handlers.click.clone() {
-//             let data = self.data.clone();
-//             button.mouse_down(MouseButton::Left, move |view, event, cx| {
-//                 handler(view, data.as_ref(), cx)
-//             })
-//         } else {
-//             button
-//         }
-//     }
-// }
-
-// // impl<V: 'static, D> Element<V> for Button<V, D> {
-// //     type Layout = AnyElement<V>;
-
-// //     fn style_mut(&mut self) -> &mut crate::style::ElementStyle {
-// //         &mut self.metadata.style
-// //     }
-
-// //     fn handlers_mut(&mut self) -> &mut crate::element::ElementHandlers<V> {
-// //         &mut self.metadata.handlers
-// //     }
-
-// //     fn layout(
-// //         &mut self,
-// //         view: &mut V,
-// //         cx: &mut crate::element::LayoutContext<V>,
-// //     ) -> anyhow::Result<(taffy::tree::NodeId, Self::Layout)> {
-// //         let mut element = self.render(view, cx).into_any();
-// //         let node_id = element.layout(view, cx)?;
-// //         Ok((node_id, element))
-// //     }
-
-// //     fn paint<'a>(
-// //         &mut self,
-// //         layout: crate::element::Layout<'a, Self::Layout>,
-// //         view: &mut V,
-// //         cx: &mut crate::element::PaintContext<V>,
-// //     ) -> anyhow::Result<()> {
-// //         layout.from_element.paint(view, cx)?;
-// //         Ok(())
-// //     }
-// // }
+use crate::{
+    div::div,
+    element::{Element, ParentElement},
+    style::StyleHelpers,
+    text::ArcCow,
+    themes::rose_pine,
+};
+use gpui::ViewContext;
+use playground_macros::Element;
+
+use std::{marker::PhantomData, rc::Rc};
+
+struct ButtonHandlers<V, D> {
+    click: Option<Rc<dyn Fn(&mut V, &D, &mut ViewContext<V>)>>,
+}
+
+impl<V, D> Default for ButtonHandlers<V, D> {
+    fn default() -> Self {
+        Self { click: None }
+    }
+}
+
+use crate as playground;
+#[derive(Element)]
+pub struct Button<V: 'static, D: 'static> {
+    handlers: ButtonHandlers<V, D>,
+    label: Option<ArcCow<'static, str>>,
+    icon: Option<ArcCow<'static, str>>,
+    data: Rc<D>,
+    view_type: PhantomData<V>,
+}
+
+// Impl block for buttons without data.
+// See below for an impl block for any button.
+impl<V: 'static> Button<V, ()> {
+    fn new() -> Self {
+        Self {
+            handlers: ButtonHandlers::default(),
+            label: None,
+            icon: None,
+            data: Rc::new(()),
+            view_type: PhantomData,
+        }
+    }
+
+    pub fn data<D: 'static>(self, data: D) -> Button<V, D> {
+        Button {
+            handlers: ButtonHandlers::default(),
+            label: self.label,
+            icon: self.icon,
+            data: Rc::new(data),
+            view_type: PhantomData,
+        }
+    }
+}
+
+// Impl block for *any* button.
+impl<V: 'static, D: 'static> Button<V, D> {
+    pub fn label(mut self, label: impl Into<ArcCow<'static, str>>) -> Self {
+        self.label = Some(label.into());
+        self
+    }
+
+    pub fn icon(mut self, icon: impl Into<ArcCow<'static, str>>) -> Self {
+        self.icon = Some(icon.into());
+        self
+    }
+
+    // pub fn click(self, handler: impl Fn(&mut V, &D, &mut ViewContext<V>) + 'static) -> Self {
+    //     let data = self.data.clone();
+    //     Self::click(self, MouseButton::Left, move |view, _, cx| {
+    //         handler(view, data.as_ref(), cx);
+    //     })
+    // }
+}
+
+pub fn button<V>() -> Button<V, ()> {
+    Button::new()
+}
+
+impl<V: 'static, D: 'static> Button<V, D> {
+    fn render(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+        // TODO: Drive theme from the context
+        let button = div()
+            .fill(rose_pine::dawn().error(0.5))
+            .h_4()
+            .children(self.label.clone());
+
+        button
+
+        // TODO: Event handling
+        // if let Some(handler) = self.handlers.click.clone() {
+        //     let data = self.data.clone();
+        //     // button.mouse_down(MouseButton::Left, move |view, event, cx| {
+        //     //     handler(view, data.as_ref(), cx)
+        //     // })
+        // } else {
+        //     button
+        // }
+    }
+}

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

@@ -1,8 +1,8 @@
 use crate::{
-    element::{AnyElement, Element, Layout},
+    element::{AnyElement, Element, Layout, ParentElement},
     layout_context::LayoutContext,
     paint_context::PaintContext,
-    style::{Style, StyleRefinement, Styleable},
+    style::{Style, StyleHelpers, StyleRefinement, Styleable},
 };
 use anyhow::Result;
 use gpui::{platform::MouseMovedEvent, EventContext, LayoutId};
@@ -14,14 +14,6 @@ pub struct Div<V> {
     children: SmallVec<[AnyElement<V>; 2]>,
 }
 
-impl<V> Styleable for Div<V> {
-    type Style = Style;
-
-    fn declared_style(&mut self) -> &mut StyleRefinement {
-        &mut self.style
-    }
-}
-
 pub fn div<V>() -> Div<V> {
     Div {
         style: Default::default(),
@@ -55,6 +47,22 @@ impl<V: 'static> Element<V> for Div<V> {
     }
 }
 
+impl<V> Styleable for Div<V> {
+    type Style = Style;
+
+    fn declared_style(&mut self) -> &mut crate::style::StyleRefinement {
+        &mut self.style
+    }
+}
+
+impl<V> StyleHelpers for Div<V> {}
+
+impl<V: 'static> ParentElement<V> for Div<V> {
+    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
+        &mut self.children
+    }
+}
+
 pub trait Interactive<V> {
     fn declared_interactions(&mut self) -> &mut Interactions<V>;
 

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

@@ -1,12 +1,12 @@
 use anyhow::Result;
 use derive_more::{Deref, DerefMut};
-use gpui::geometry::rect::RectF;
-use gpui::EngineLayout;
+use gpui::{geometry::rect::RectF, EngineLayout};
+use smallvec::SmallVec;
 use std::marker::PhantomData;
 use util::ResultExt;
 
-use crate::layout_context::LayoutContext;
-use crate::paint_context::PaintContext;
+pub use crate::layout_context::LayoutContext;
+pub use crate::paint_context::PaintContext;
 
 type LayoutId = gpui::LayoutId;
 
@@ -21,11 +21,11 @@ pub struct Layout<V, D> {
 }
 
 impl<V: 'static, D> Layout<V, D> {
-    pub fn new(id: LayoutId, engine_layout: Option<EngineLayout>, element_data: D) -> Self {
+    pub fn new(id: LayoutId, element_data: D) -> Self {
         Self {
             id,
-            engine_layout,
-            element_data,
+            engine_layout: None,
+            element_data: element_data,
             view_type: PhantomData,
         }
     }
@@ -44,6 +44,14 @@ impl<V: 'static, D> Layout<V, D> {
     }
 }
 
+impl<V: 'static> Layout<V, Option<AnyElement<V>>> {
+    pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) {
+        let mut element = self.element_data.take().unwrap();
+        element.paint(view, self.id, cx);
+        self.element_data = Some(element);
+    }
+}
+
 pub trait Element<V: 'static>: 'static {
     type Layout;
 
@@ -97,6 +105,9 @@ impl<V, E: Element<V>> ElementStateObject<V> for ElementState<V, E> {
 
     fn paint(&mut self, view: &mut V, layout_id: LayoutId, cx: &mut PaintContext<V>) {
         let layout = self.layout.as_mut().expect("paint called before layout");
+        if layout.engine_layout.is_none() {
+            layout.engine_layout = cx.computed_layout(layout_id).log_err()
+        }
         self.element.paint(view, layout, cx)
     }
 }
@@ -115,7 +126,7 @@ impl<V> AnyElement<V> {
 }
 
 pub trait ParentElement<V: 'static> {
-    fn children_mut(&mut self) -> &mut Vec<AnyElement<V>>;
+    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]>;
 
     fn child(mut self, child: impl IntoElement<V>) -> Self
     where

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

@@ -49,6 +49,6 @@ impl<'a, 'b, 'c, 'd, V: 'static> LayoutContext<'a, 'b, 'c, 'd, V> {
             .ok_or_else(|| anyhow!("no layout engine"))?
             .add_node(style.to_taffy(rem_size), children)?;
 
-        Ok(Layout::new(id, None, element_data))
+        Ok(Layout::new(id, element_data))
     }
 }

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

@@ -1,8 +1,14 @@
 #![allow(dead_code, unused_variables)]
+use crate::{color::black, style::StyleHelpers};
+use element::Element;
+use gpui::{
+    geometry::{rect::RectF, vector::vec2f},
+    platform::WindowOptions,
+};
 use log::LevelFilter;
 use simplelog::SimpleLogger;
-
-use themes::ThemeColors;
+use themes::{rose_pine, ThemeColors};
+use view::view;
 
 mod adapter;
 mod color;
@@ -21,32 +27,33 @@ fn main() {
     SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
 
     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(|_| playground(&rose_pine::moon())),
-        // );
+        cx.add_window(
+            WindowOptions {
+                bounds: gpui::platform::WindowBounds::Fixed(RectF::new(
+                    vec2f(0., 0.),
+                    vec2f(400., 300.),
+                )),
+                center: true,
+                ..Default::default()
+            },
+            |_| view(|_| playground(&rose_pine::moon())),
+        );
         cx.platform().activate(true);
     });
 }
 
-// fn playground<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
-//     todo!()
-//     // frame()
-//     //     .text_color(black())
-//     //     .h_full()
-//     //     .w_half()
-//     //     .fill(theme.success(0.5))
-//     //     .hover()
-//     //     .fill(theme.error(0.5))
-//     //     .child(button().label("Hello").click(|_, _, _| println!("click!")))
-// }
+fn playground<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
+    use div::div;
+
+    div()
+        .text_color(black())
+        .h_full()
+        .w_24()
+        .fill(theme.success(0.5))
+    // .hover()
+    // .fill(theme.error(0.5))
+    // .child(button().label("Hello").click(|_, _, _| println!("click!")))
+}
 
 //     todo!()
 //     // column()

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

@@ -257,7 +257,7 @@ pub trait Styleable {
     }
 }
 
-// Tailwind-style helpers methods that take and return mut self
+// Helpers methods that take and return mut self. This includes tailwind style methods for standard sizes etc.
 //
 // Example:
 // // Sets the padding to 0.5rem, just like class="p-2" in Tailwind.
@@ -265,4 +265,22 @@ pub trait Styleable {
 use crate as playground; // Macro invocation references this crate as playground.
 pub trait StyleHelpers: Styleable<Style = Style> {
     styleable_helpers!();
+
+    fn fill<F>(mut self, fill: F) -> Self
+    where
+        F: Into<Fill>,
+        Self: Sized,
+    {
+        self.declared_style().fill = Some(fill.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
+    }
 }

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

@@ -1,72 +1,57 @@
 use proc_macro::TokenStream;
-use quote::{format_ident, quote};
-use syn::{
-    parse_macro_input, parse_quote, DeriveInput, GenericParam, Generics, Ident, Lit, Meta,
-    WhereClause,
-};
+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 crate_name: String = ast
-        .attrs
-        .iter()
-        .find_map(|attr| {
-            if attr.path.is_ident("element_crate") {
-                match attr.parse_meta() {
-                    Ok(Meta::NameValue(nv)) => {
-                        if let Lit::Str(s) = nv.lit {
-                            Some(s.value())
-                        } else {
-                            None
-                        }
-                    }
-                    _ => None,
-                }
+    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
             }
-        })
-        .unwrap_or_else(|| String::from("playground"));
-
-    let crate_name = format_ident!("{}", crate_name);
-
-    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 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();
-            impl_generics = generics.0;
-            type_generics = Some(generics.1);
-            where_clause = generics.2;
-        }
-        _ => {
-            view_type_name = placeholder_view_type_name;
+            (
+                generics.0,
+                Some(generics.1),
+                generics.2,
+                first_type_param,
+                lifetimes,
+            )
+        } else {
             let generics = placeholder_view_generics.split_for_impl();
-            impl_generics = generics.0;
-            type_generics = None;
-            where_clause = generics.2;
-        }
-    }
+            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,
-        &crate_name,
         &view_type_name,
         &type_name,
         &type_generics,
@@ -74,37 +59,28 @@ pub fn derive_element(input: TokenStream) -> TokenStream {
     );
 
     let gen = quote! {
-        impl #impl_generics #crate_name::element::Element<#view_type_name> for #type_name #type_generics
+        impl #impl_generics playground::element::Element<#view_type_name> for #type_name #type_generics
         #where_clause
         {
-            type Layout = #crate_name::element::AnyElement<V>;
-
-            fn declared_style(&mut self) -> &mut #crate_name::style::StyleRefinement {
-                &mut self.metadata.style
-            }
-
-            fn handlers_mut(&mut self) -> &mut Vec<#crate_name::element::EventHandler<V>> {
-                &mut self.metadata.handlers
-            }
+            type Layout = Option<playground::element::AnyElement<#view_type_name #lifetimes>>;
 
             fn layout(
                 &mut self,
                 view: &mut V,
-                cx: &mut #crate_name::element::LayoutContext<V>,
-            ) -> anyhow::Result<(taffy::tree::NodeId, Self::Layout)> {
+                cx: &mut playground::element::LayoutContext<V>,
+            ) -> anyhow::Result<playground::element::Layout<V, Self::Layout>> {
                 let mut element = self.render(view, cx).into_any();
-                let node_id = element.layout(view, cx)?;
-                Ok((node_id, element))
+                let layout_id = element.layout(view, cx)?;
+                Ok(playground::element::Layout::new(layout_id, Some(element)))
             }
 
-            fn paint<'a>(
+            fn paint(
                 &mut self,
-                layout: #crate_name::element::Layout<'a, Self::Layout>,
                 view: &mut V,
-                cx: &mut #crate_name::element::PaintContext<V>,
-            ) -> anyhow::Result<()> {
-                layout.from_element.paint(view, cx)?;
-                Ok(())
+                layout: &mut playground::element::Layout<V, Self::Layout>,
+                cx: &mut playground::element::PaintContext<V>,
+            ) {
+                layout.paint(view, cx);
             }
         }
 

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

@@ -1,36 +1,13 @@
 use proc_macro::TokenStream;
-use quote::{format_ident, quote};
+use quote::quote;
 use syn::{
-    parse_macro_input, parse_quote, DeriveInput, GenericParam, Generics, Ident, Lit, Meta,
-    WhereClause,
+    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 crate_name: String = ast
-        .attrs
-        .iter()
-        .find_map(|attr| {
-            if attr.path.is_ident("element_crate") {
-                match attr.parse_meta() {
-                    Ok(Meta::NameValue(nv)) => {
-                        if let Lit::Str(s) = nv.lit {
-                            Some(s.value())
-                        } else {
-                            None
-                        }
-                    }
-                    _ => None,
-                }
-            } else {
-                None
-            }
-        })
-        .unwrap_or_else(|| String::from("playground"));
-    let crate_name = format_ident!("{}", crate_name);
-
     let placeholder_view_generics: Generics = parse_quote! { <V: 'static> };
     let placeholder_view_type_name: Ident = parse_quote! { V };
     let view_type_name: Ident;
@@ -63,7 +40,6 @@ pub fn derive_into_element(input: TokenStream) -> TokenStream {
 
     impl_into_element(
         &impl_generics,
-        &crate_name,
         &view_type_name,
         &type_name,
         &type_generics,
@@ -74,14 +50,13 @@ pub fn derive_into_element(input: TokenStream) -> TokenStream {
 
 pub fn impl_into_element(
     impl_generics: &syn::ImplGenerics<'_>,
-    crate_name: &Ident,
     view_type_name: &Ident,
     type_name: &Ident,
     type_generics: &Option<syn::TypeGenerics<'_>>,
     where_clause: &Option<&WhereClause>,
 ) -> proc_macro2::TokenStream {
     quote! {
-        impl #impl_generics #crate_name::element::IntoElement<#view_type_name> for #type_name #type_generics
+        impl #impl_generics playground::element::IntoElement<#view_type_name> for #type_name #type_generics
         #where_clause
         {
             type Element = Self;