Checkpoint

Nathan Sobo created

Change summary

Cargo.lock                                                   |   1 
crates/refineable/derive_refineable/src/derive_refineable.rs |  29 
crates/storybook/Cargo.toml                                  |   1 
crates/storybook/src/gpui3/app.rs                            | 167 +
crates/storybook/src/gpui3/element.rs                        | 276 ++
crates/storybook/src/gpui3/geometry.rs                       | 303 ++
crates/storybook/src/gpui3/mod.rs                            | 191 +
crates/storybook/src/gpui3/style.rs                          |   1 
crates/storybook/src/gpui3/taffy.rs                          | 177 +
crates/storybook/src/gpui3/window.rs                         | 215 +
crates/storybook/src/sketch.rs                               | 737 ------
crates/storybook/src/storybook.rs                            |   2 
12 files changed, 1,353 insertions(+), 747 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -7384,6 +7384,7 @@ dependencies = [
  "serde",
  "settings",
  "simplelog",
+ "slotmap",
  "theme",
  "util",
 ]

crates/refineable/derive_refineable/src/derive_refineable.rs 🔗

@@ -35,7 +35,7 @@ pub fn derive_refineable(input: TokenStream) -> TokenStream {
     let field_visibilities: Vec<_> = fields.iter().map(|f| &f.vis).collect();
     let wrapped_types: Vec<_> = fields.iter().map(|f| get_wrapper_type(f, &f.ty)).collect();
 
-    // Create trait bound that each wrapped type must implement Clone & Default
+    // Create trait bound that each wrapped type must implement Clone // & Default
     let type_param_bounds: Vec<_> = wrapped_types
         .iter()
         .map(|ty| {
@@ -51,13 +51,14 @@ pub fn derive_refineable(input: TokenStream) -> TokenStream {
                         lifetimes: None,
                         path: parse_quote!(Clone),
                     }));
-                    punctuated.push_punct(syn::token::Add::default());
-                    punctuated.push_value(TypeParamBound::Trait(TraitBound {
-                        paren_token: None,
-                        modifier: syn::TraitBoundModifier::None,
-                        lifetimes: None,
-                        path: parse_quote!(Default),
-                    }));
+
+                    // punctuated.push_punct(syn::token::Add::default());
+                    // punctuated.push_value(TypeParamBound::Trait(TraitBound {
+                    //     paren_token: None,
+                    //     modifier: syn::TraitBoundModifier::None,
+                    //     lifetimes: None,
+                    //     path: parse_quote!(Default),
+                    // }));
                     punctuated
                 },
             })
@@ -161,7 +162,7 @@ pub fn derive_refineable(input: TokenStream) -> TokenStream {
     };
 
     let gen = quote! {
-        #[derive(Default, Clone)]
+        #[derive(Clone)]
         pub struct #refinement_ident #impl_generics {
             #( #field_visibilities #field_names: #wrapped_types ),*
         }
@@ -186,6 +187,16 @@ pub fn derive_refineable(input: TokenStream) -> TokenStream {
             }
         }
 
+        impl #impl_generics ::core::default::Default for #refinement_ident #ty_generics
+            #where_clause
+        {
+            fn default() -> Self {
+                #refinement_ident {
+                    #( #field_names: Default::default() ),*
+                }
+            }
+        }
+
         impl #impl_generics #refinement_ident #ty_generics
             #where_clause
         {

crates/storybook/Cargo.toml 🔗

@@ -18,6 +18,7 @@ rust-embed.workspace = true
 serde.workspace = true
 settings = { path = "../settings" }
 simplelog = "0.9"
+slotmap = "1.0.6"
 theme = { path = "../theme" }
 util = { path = "../util" }
 

crates/storybook/src/gpui3/app.rs 🔗

@@ -0,0 +1,167 @@
+use anyhow::{anyhow, Result};
+use std::{any::Any, collections::HashMap, marker::PhantomData};
+
+use super::{
+    window::{Window, WindowHandle, WindowId},
+    Context, EntityId, LayoutId, Reference, View, WindowContext,
+};
+
+pub struct AppContext {
+    pub(crate) entity_count: usize,
+    pub(crate) entities: HashMap<EntityId, Box<dyn Any>>,
+    pub(crate) window_count: usize,
+    pub(crate) windows: HashMap<WindowId, Window>,
+    // We recycle this memory across layout requests.
+    pub(crate) child_layout_buffer: Vec<LayoutId>,
+}
+
+impl AppContext {
+    pub fn new() -> Self {
+        AppContext {
+            entity_count: 0,
+            entities: HashMap::new(),
+            window_count: 0,
+            windows: HashMap::new(),
+            child_layout_buffer: Default::default(),
+        }
+    }
+
+    pub fn open_window<S>(
+        &mut self,
+        build_root_view: impl FnOnce(&mut WindowContext) -> View<S>,
+    ) -> WindowHandle<S> {
+        let window = Window::new(&mut self.window_count);
+
+        unimplemented!()
+    }
+
+    pub(crate) fn update_window<R>(
+        &mut self,
+        window_id: WindowId,
+        update: impl FnOnce(&mut WindowContext) -> R,
+    ) -> Result<R> {
+        let mut window = self
+            .windows
+            .remove(&window_id)
+            .ok_or_else(|| anyhow!("window not found"))?;
+        let result = update(&mut WindowContext::mutable(self, &mut window));
+        self.windows.insert(window_id, window);
+        Ok(result)
+    }
+}
+
+impl Context for AppContext {
+    type EntityContext<'a, 'w, T: 'static> = ModelContext<'a, T>;
+
+    fn entity<T: 'static>(
+        &mut self,
+        build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
+    ) -> Handle<T> {
+        let entity_id = EntityId::new(&mut self.entity_count);
+        let entity = build_entity(&mut ModelContext::mutable(self, entity_id));
+        self.entities.insert(entity_id, Box::new(entity));
+        Handle::new(entity_id)
+    }
+
+    fn update_entity<T: 'static, R>(
+        &mut self,
+        handle: &Handle<T>,
+        update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
+    ) -> R {
+        let mut entity = self
+            .entities
+            .remove(&handle.id)
+            .unwrap()
+            .downcast::<T>()
+            .unwrap();
+        let result = update(&mut *entity, &mut ModelContext::mutable(self, handle.id));
+        self.entities.insert(handle.id, Box::new(entity));
+        result
+    }
+}
+
+pub struct ModelContext<'a, T> {
+    app: Reference<'a, AppContext>,
+    entity_type: PhantomData<T>,
+    entity_id: EntityId,
+}
+
+impl<'a, T: 'static> ModelContext<'a, T> {
+    pub(crate) fn mutable(app: &'a mut AppContext, entity_id: EntityId) -> Self {
+        Self {
+            app: Reference::Mutable(app),
+            entity_type: PhantomData,
+            entity_id,
+        }
+    }
+
+    fn immutable(app: &'a AppContext, entity_id: EntityId) -> Self {
+        Self {
+            app: Reference::Immutable(app),
+            entity_type: PhantomData,
+            entity_id,
+        }
+    }
+
+    fn update<R>(&mut self, update: impl FnOnce(&mut T, &mut Self) -> R) -> R {
+        let mut entity = self.app.entities.remove(&self.entity_id).unwrap();
+        let result = update(entity.downcast_mut::<T>().unwrap(), self);
+        self.app.entities.insert(self.entity_id, Box::new(entity));
+        result
+    }
+}
+
+impl<'a, T: 'static> Context for ModelContext<'a, T> {
+    type EntityContext<'b, 'c, U: 'static> = ModelContext<'b, U>;
+
+    fn entity<U: 'static>(
+        &mut self,
+        build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, U>) -> U,
+    ) -> Handle<U> {
+        self.app.entity(build_entity)
+    }
+
+    fn update_entity<U: 'static, R>(
+        &mut self,
+        handle: &Handle<U>,
+        update: impl FnOnce(&mut U, &mut Self::EntityContext<'_, '_, U>) -> R,
+    ) -> R {
+        self.app.update_entity(handle, update)
+    }
+}
+
+pub struct Handle<T> {
+    pub(crate) id: EntityId,
+    pub(crate) entity_type: PhantomData<T>,
+}
+
+impl<T: 'static> Handle<T> {
+    fn new(id: EntityId) -> Self {
+        Self {
+            id,
+            entity_type: PhantomData,
+        }
+    }
+
+    /// Update the entity referenced by this handle with the given function.
+    ///
+    /// The update function receives a context appropriate for its environment.
+    /// When updating in an `AppContext`, it receives a `ModelContext`.
+    /// When updating an a `WindowContext`, it receives a `ViewContext`.
+    pub fn update<C: Context, R>(
+        &self,
+        cx: &mut C,
+        update: impl FnOnce(&mut T, &mut C::EntityContext<'_, '_, T>) -> R,
+    ) -> R {
+        cx.update_entity(self, update)
+    }
+}
+
+impl<T> Clone for Handle<T> {
+    fn clone(&self) -> Self {
+        Self {
+            id: self.id,
+            entity_type: PhantomData,
+        }
+    }
+}

crates/storybook/src/gpui3/element.rs 🔗

@@ -0,0 +1,276 @@
+use super::{Handle, Layout, LayoutId, Pixels, Point, ViewContext, WindowContext};
+use anyhow::Result;
+use std::{any::Any, cell::RefCell, marker::PhantomData, rc::Rc};
+
+pub trait Element: 'static {
+    type State;
+    type FrameState;
+
+    fn layout(
+        &mut self,
+        state: &mut Self::State,
+        cx: &mut ViewContext<Self::State>,
+    ) -> Result<(LayoutId, Self::FrameState)>;
+
+    fn paint(
+        &mut self,
+        layout: Layout,
+        state: &mut Self::State,
+        frame_state: &mut Self::FrameState,
+        cx: &mut ViewContext<Self::State>,
+    ) -> Result<()>;
+}
+
+pub trait ParentElement<S> {
+    fn child(self, child: impl IntoAnyElement<S>) -> Self;
+}
+
+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,
+        cx: &mut ViewContext<S>,
+    ) -> Result<()>;
+}
+
+struct RenderedElement<E: Element> {
+    element: E,
+    phase: ElementRenderPhase<E::FrameState>,
+}
+
+#[derive(Default)]
+enum ElementRenderPhase<S> {
+    #[default]
+    Rendered,
+    LayoutRequested {
+        layout_id: LayoutId,
+        frame_state: S,
+    },
+    Painted {
+        layout: Layout,
+        frame_state: S,
+    },
+}
+
+/// Internal struct that wraps an element to store Layout and FrameState after the element is rendered.
+/// It's allocated as a trait object to erase the element type and wrapped in AnyElement<E::State> for
+/// improved usability.
+impl<E: Element> RenderedElement<E> {
+    fn new(element: E) -> Self {
+        RenderedElement {
+            element,
+            phase: ElementRenderPhase::Rendered,
+        }
+    }
+}
+
+impl<E: Element> ElementObject<E::State> for RenderedElement<E> {
+    fn layout(&mut self, state: &mut E::State, cx: &mut ViewContext<E::State>) -> Result<LayoutId> {
+        let (layout_id, frame_state) = self.element.layout(state, cx)?;
+        self.phase = ElementRenderPhase::LayoutRequested {
+            layout_id,
+            frame_state,
+        };
+        Ok(layout_id)
+    }
+
+    fn paint(
+        &mut self,
+        parent_origin: Point<Pixels>,
+        state: &mut E::State,
+        cx: &mut ViewContext<E::State>,
+    ) -> Result<()> {
+        self.phase = match std::mem::take(&mut self.phase) {
+            ElementRenderPhase::Rendered => panic!("must call layout before paint"),
+
+            ElementRenderPhase::LayoutRequested {
+                layout_id,
+                mut frame_state,
+            } => {
+                let mut layout = cx.layout(layout_id)?;
+                layout.bounds.origin += parent_origin;
+                self.element
+                    .paint(layout.clone(), state, &mut frame_state, cx)?;
+                ElementRenderPhase::Painted {
+                    layout,
+                    frame_state,
+                }
+            }
+
+            ElementRenderPhase::Painted {
+                layout,
+                mut frame_state,
+            } => {
+                self.element
+                    .paint(layout.clone(), state, &mut frame_state, cx)?;
+                ElementRenderPhase::Painted {
+                    layout,
+                    frame_state,
+                }
+            }
+        };
+
+        Ok(())
+    }
+}
+
+pub struct AnyElement<S>(Box<dyn ElementObject<S>>);
+
+impl<S> AnyElement<S> {
+    pub fn layout(&mut self, state: &mut S, cx: &mut ViewContext<S>) -> Result<LayoutId> {
+        self.0.layout(state, cx)
+    }
+
+    pub fn paint(
+        &mut self,
+        parent_origin: Point<Pixels>,
+        state: &mut S,
+        cx: &mut ViewContext<S>,
+    ) -> Result<()> {
+        self.0.paint(parent_origin, state, cx)
+    }
+}
+
+pub trait IntoAnyElement<S> {
+    fn into_any(self) -> AnyElement<S>;
+}
+
+impl<E: Element> IntoAnyElement<E::State> for E {
+    fn into_any(self) -> AnyElement<E::State> {
+        AnyElement(Box::new(RenderedElement::new(self)))
+    }
+}
+
+impl<S> IntoAnyElement<S> for AnyElement<S> {
+    fn into_any(self) -> AnyElement<S> {
+        self
+    }
+}
+
+#[derive(Clone)]
+pub struct View<S> {
+    state: Handle<S>,
+    render: Rc<dyn Fn(&mut S, &mut ViewContext<S>) -> AnyElement<S>>,
+}
+
+pub fn view<S: 'static, E: Element<State = S>>(
+    state: Handle<S>,
+    render: impl 'static + Fn(&mut S, &mut ViewContext<S>) -> E,
+) -> View<S> {
+    View {
+        state,
+        render: Rc::new(move |state, cx| render(state, cx).into_any()),
+    }
+}
+
+impl<S: 'static> View<S> {
+    pub fn into_any<ParentState>(self) -> AnyView<ParentState> {
+        AnyView {
+            view: Rc::new(RefCell::new(self)),
+            parent_state_type: PhantomData,
+        }
+    }
+}
+
+impl<S: 'static> Element for View<S> {
+    type State = ();
+    type FrameState = AnyElement<S>;
+
+    fn layout(
+        &mut self,
+        _: &mut Self::State,
+        cx: &mut ViewContext<Self::State>,
+    ) -> Result<(LayoutId, Self::FrameState)> {
+        self.state.update(cx, |state, cx| {
+            let mut element = (self.render)(state, cx);
+            let layout_id = element.layout(state, cx)?;
+            Ok((layout_id, element))
+        })
+    }
+
+    fn paint(
+        &mut self,
+        layout: Layout,
+        _: &mut Self::State,
+        element: &mut Self::FrameState,
+        cx: &mut ViewContext<Self::State>,
+    ) -> Result<()> {
+        self.state.update(cx, |state, cx| {
+            element.paint(layout.bounds.origin, state, cx)
+        })
+    }
+}
+
+trait ViewObject {
+    fn layout(&mut self, cx: &mut WindowContext) -> Result<(LayoutId, Box<dyn Any>)>;
+    fn paint(
+        &mut self,
+        layout: Layout,
+        element: &mut dyn Any,
+        cx: &mut WindowContext,
+    ) -> Result<()>;
+}
+
+impl<S: 'static> ViewObject for View<S> {
+    fn layout(&mut self, cx: &mut WindowContext) -> Result<(LayoutId, Box<dyn Any>)> {
+        self.state.update(cx, |state, cx| {
+            let mut element = (self.render)(state, cx);
+            let layout_id = element.layout(state, cx)?;
+            let element = Box::new(element) as Box<dyn Any>;
+            Ok((layout_id, element))
+        })
+    }
+
+    fn paint(
+        &mut self,
+        layout: Layout,
+        element: &mut dyn Any,
+        cx: &mut WindowContext,
+    ) -> Result<()> {
+        self.state.update(cx, |state, cx| {
+            element
+                .downcast_mut::<AnyElement<S>>()
+                .unwrap()
+                .paint(layout.bounds.origin, state, cx)
+        })
+    }
+}
+
+pub struct AnyView<S> {
+    view: Rc<RefCell<dyn ViewObject>>,
+    parent_state_type: PhantomData<S>,
+}
+
+impl<S: 'static> Element for AnyView<S> {
+    type State = S;
+    type FrameState = Box<dyn Any>;
+
+    fn layout(
+        &mut self,
+        _: &mut Self::State,
+        cx: &mut ViewContext<Self::State>,
+    ) -> Result<(LayoutId, Self::FrameState)> {
+        self.view.borrow_mut().layout(cx)
+    }
+
+    fn paint(
+        &mut self,
+        layout: Layout,
+        _: &mut Self::State,
+        element: &mut Self::FrameState,
+        cx: &mut ViewContext<Self::State>,
+    ) -> Result<()> {
+        self.view.borrow_mut().paint(layout, element, cx)
+    }
+}
+
+impl<S> Clone for AnyView<S> {
+    fn clone(&self) -> Self {
+        Self {
+            view: self.view.clone(),
+            parent_state_type: PhantomData,
+        }
+    }
+}

crates/storybook/src/gpui3/geometry.rs 🔗

@@ -0,0 +1,303 @@
+use core::fmt::Debug;
+use derive_more::{Add, AddAssign, Div, Mul, Sub};
+use refineable::Refineable;
+use std::ops::Mul;
+
+#[derive(Default, Add, AddAssign, Sub, Mul, Div, Copy, Debug, PartialEq, Eq, Hash)]
+pub struct Point<T> {
+    pub x: T,
+    pub y: T,
+}
+
+impl<T: Clone> Clone for Point<T> {
+    fn clone(&self) -> Self {
+        Self {
+            x: self.x.clone(),
+            y: self.y.clone(),
+        }
+    }
+}
+
+#[derive(Default, Clone, Refineable, Debug)]
+pub struct Size<T: Clone> {
+    pub width: T,
+    pub height: T,
+}
+
+impl Size<Length> {
+    pub fn full() -> Self {
+        Self {
+            width: relative(1.),
+            height: relative(1.),
+        }
+    }
+}
+
+impl Size<DefiniteLength> {
+    pub fn zero() -> Self {
+        Self {
+            width: px(0.),
+            height: px(0.),
+        }
+    }
+}
+
+impl Size<Length> {
+    pub fn auto() -> Self {
+        Self {
+            width: Length::Auto,
+            height: Length::Auto,
+        }
+    }
+}
+
+#[derive(Clone, Default, Debug)]
+pub struct Bounds<F: Clone> {
+    pub origin: Point<F>,
+    pub size: Size<F>,
+}
+
+#[derive(Clone, Default, Refineable, Debug)]
+pub struct Edges<T: Clone> {
+    pub top: T,
+    pub right: T,
+    pub bottom: T,
+    pub left: T,
+}
+
+impl Edges<Length> {
+    pub fn auto() -> Self {
+        Self {
+            top: Length::Auto,
+            right: Length::Auto,
+            bottom: Length::Auto,
+            left: Length::Auto,
+        }
+    }
+
+    pub fn zero() -> Self {
+        Self {
+            top: px(0.),
+            right: px(0.),
+            bottom: px(0.),
+            left: px(0.),
+        }
+    }
+}
+
+impl Edges<DefiniteLength> {
+    pub fn zero() -> Self {
+        Self {
+            top: px(0.),
+            right: px(0.),
+            bottom: px(0.),
+            left: px(0.),
+        }
+    }
+}
+
+impl Edges<AbsoluteLength> {
+    pub fn zero() -> Self {
+        Self {
+            top: px(0.),
+            right: px(0.),
+            bottom: px(0.),
+            left: px(0.),
+        }
+    }
+
+    pub fn to_pixels(&self, rem_size: Pixels) -> Edges<Pixels> {
+        Edges {
+            top: self.top.to_pixels(rem_size),
+            right: self.right.to_pixels(rem_size),
+            bottom: self.bottom.to_pixels(rem_size),
+            left: self.left.to_pixels(rem_size),
+        }
+    }
+}
+
+impl Edges<Pixels> {
+    pub fn is_empty(&self) -> bool {
+        self.top == px(0.) && self.right == px(0.) && self.bottom == px(0.) && self.left == px(0.)
+    }
+}
+
+#[derive(Clone, Copy, Default, Add, AddAssign, Sub, Mul, Div, PartialEq)]
+pub struct Pixels(pub(crate) f32);
+
+impl Debug for Pixels {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{} px", self.0)
+    }
+}
+
+impl From<Pixels> for f32 {
+    fn from(pixels: Pixels) -> Self {
+        pixels.0
+    }
+}
+
+impl From<&Pixels> for f32 {
+    fn from(pixels: &Pixels) -> Self {
+        pixels.0
+    }
+}
+
+#[derive(Clone, Copy, Default, Add, Sub, Mul, Div)]
+pub struct Rems(f32);
+
+impl Mul<Pixels> for Rems {
+    type Output = Pixels;
+
+    fn mul(self, other: Pixels) -> Pixels {
+        Pixels(self.0 * other.0)
+    }
+}
+
+impl Debug for Rems {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{} rem", self.0)
+    }
+}
+
+#[derive(Clone, Copy, Debug)]
+pub enum AbsoluteLength {
+    Pixels(Pixels),
+    Rems(Rems),
+}
+
+impl From<Pixels> for AbsoluteLength {
+    fn from(pixels: Pixels) -> Self {
+        AbsoluteLength::Pixels(pixels)
+    }
+}
+
+impl From<Rems> for AbsoluteLength {
+    fn from(rems: Rems) -> Self {
+        AbsoluteLength::Rems(rems)
+    }
+}
+
+impl AbsoluteLength {
+    pub fn to_pixels(&self, rem_size: Pixels) -> Pixels {
+        match self {
+            AbsoluteLength::Pixels(pixels) => *pixels,
+            AbsoluteLength::Rems(rems) => *rems * rem_size,
+        }
+    }
+}
+
+impl Default for AbsoluteLength {
+    fn default() -> Self {
+        px(0.)
+    }
+}
+
+/// A non-auto length that can be defined in pixels, rems, or percent of parent.
+#[derive(Clone, Copy)]
+pub enum DefiniteLength {
+    Absolute(AbsoluteLength),
+    /// A fraction of the parent's size between 0 and 1.
+    Fraction(f32),
+}
+
+impl Debug for DefiniteLength {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            DefiniteLength::Absolute(length) => Debug::fmt(length, f),
+            DefiniteLength::Fraction(fract) => write!(f, "{}%", (fract * 100.0) as i32),
+        }
+    }
+}
+
+impl From<Pixels> for DefiniteLength {
+    fn from(pixels: Pixels) -> Self {
+        Self::Absolute(pixels.into())
+    }
+}
+
+impl From<Rems> for DefiniteLength {
+    fn from(rems: Rems) -> Self {
+        Self::Absolute(rems.into())
+    }
+}
+
+impl From<AbsoluteLength> for DefiniteLength {
+    fn from(length: AbsoluteLength) -> Self {
+        Self::Absolute(length)
+    }
+}
+
+impl Default for DefiniteLength {
+    fn default() -> Self {
+        Self::Absolute(AbsoluteLength::default())
+    }
+}
+
+/// A length that can be defined in pixels, rems, percent of parent, or auto.
+#[derive(Clone, Copy)]
+pub enum Length {
+    Definite(DefiniteLength),
+    Auto,
+}
+
+impl Debug for Length {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Length::Definite(definite_length) => write!(f, "{:?}", definite_length),
+            Length::Auto => write!(f, "auto"),
+        }
+    }
+}
+
+pub fn relative<T: From<DefiniteLength>>(fraction: f32) -> T {
+    DefiniteLength::Fraction(fraction).into()
+}
+
+pub fn rems<T: From<Rems>>(rems: f32) -> T {
+    Rems(rems).into()
+}
+
+pub fn px<T: From<Pixels>>(pixels: f32) -> T {
+    Pixels(pixels).into()
+}
+
+pub fn auto() -> Length {
+    Length::Auto
+}
+
+impl From<Pixels> for Length {
+    fn from(pixels: Pixels) -> Self {
+        Self::Definite(pixels.into())
+    }
+}
+
+impl From<Rems> for Length {
+    fn from(rems: Rems) -> Self {
+        Self::Definite(rems.into())
+    }
+}
+
+impl From<DefiniteLength> for Length {
+    fn from(length: DefiniteLength) -> Self {
+        Self::Definite(length)
+    }
+}
+
+impl From<AbsoluteLength> for Length {
+    fn from(length: AbsoluteLength) -> Self {
+        Self::Definite(length.into())
+    }
+}
+
+impl Default for Length {
+    fn default() -> Self {
+        Self::Definite(DefiniteLength::default())
+    }
+}
+
+impl From<()> for Length {
+    fn from(_: ()) -> Self {
+        Self::Definite(DefiniteLength::default())
+    }
+}

crates/storybook/src/gpui3/mod.rs 🔗

@@ -0,0 +1,191 @@
+mod app;
+mod element;
+mod geometry;
+mod style;
+mod taffy;
+mod window;
+
+use anyhow::Result;
+pub use gpui2::ArcCow;
+use gpui2::Reference;
+use std::marker::PhantomData;
+
+pub use app::*;
+pub use element::*;
+pub use geometry::*;
+pub use style::*;
+use taffy::TaffyLayoutEngine;
+pub use window::*;
+
+#[derive(Clone, Copy, Eq, PartialEq, Hash)]
+pub struct EntityId(usize);
+
+impl EntityId {
+    fn new(entity_count: &mut usize) -> EntityId {
+        let id = *entity_count;
+        *entity_count += 1;
+        Self(id)
+    }
+}
+
+pub trait Context {
+    type EntityContext<'a, 'w, T: 'static>;
+
+    fn entity<T: 'static>(
+        &mut self,
+        build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
+    ) -> Handle<T>;
+
+    fn update_entity<T: 'static, R>(
+        &mut self,
+        handle: &Handle<T>,
+        update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
+    ) -> R;
+}
+
+pub struct Div<S>(PhantomData<S>);
+
+impl<S: 'static> Element for Div<S> {
+    type State = S;
+    type FrameState = ();
+
+    fn layout(
+        &mut self,
+        state: &mut Self::State,
+        cx: &mut ViewContext<Self::State>,
+    ) -> Result<(LayoutId, Self::FrameState)> {
+        todo!()
+    }
+
+    fn paint(
+        &mut self,
+        layout: Layout,
+        state: &mut Self::State,
+        frame_state: &mut Self::FrameState,
+        cx: &mut ViewContext<Self::State>,
+    ) -> Result<()> {
+        todo!()
+    }
+}
+
+impl<S> ParentElement<S> for Div<S> {
+    fn child(self, child: impl IntoAnyElement<S>) -> Self {
+        todo!()
+    }
+}
+
+pub fn div<S>() -> Div<S> {
+    todo!()
+}
+
+pub struct SharedString(ArcCow<'static, str>);
+
+impl<T: Into<ArcCow<'static, str>>> From<T> for SharedString {
+    fn from(value: T) -> Self {
+        Self(value.into())
+    }
+}
+
+struct Workspace {
+    left_panel: AnyView<Self>,
+}
+
+fn workspace(cx: &mut WindowContext) -> View<Workspace> {
+    let workspace = cx.entity(|cx| Workspace {
+        left_panel: collab_panel(cx).into_any(),
+    });
+    view(workspace, |workspace, cx| {
+        div().child(workspace.left_panel.clone())
+    })
+}
+
+struct CollabPanel {
+    filter_editor: Handle<Editor>,
+}
+
+fn collab_panel(cx: &mut WindowContext) -> View<CollabPanel> {
+    let panel = cx.entity(|cx| CollabPanel::new(cx));
+    view(panel, |panel, cx| {
+        div()
+            .child(div())
+            .child(field(panel.filter_editor.clone()).placeholder_text("Search channels, contacts"))
+    })
+}
+
+impl CollabPanel {
+    fn new(cx: &mut ViewContext<Self>) -> Self {
+        Self {
+            filter_editor: cx.entity(|cx| Editor::new(cx)),
+        }
+    }
+}
+
+fn field<S>(editor: Handle<Editor>) -> EditorElement<S> {
+    EditorElement {
+        editor,
+        field: true,
+        placeholder_text: None,
+        parent_state: PhantomData,
+    }
+}
+
+struct EditorElement<S> {
+    editor: Handle<Editor>,
+    field: bool,
+    placeholder_text: Option<SharedString>,
+    parent_state: PhantomData<S>,
+}
+
+impl<S> EditorElement<S> {
+    pub fn field(mut self) -> Self {
+        self.field = true;
+        self
+    }
+
+    pub fn placeholder_text(mut self, text: impl Into<SharedString>) -> Self {
+        self.placeholder_text = Some(text.into());
+        self
+    }
+}
+
+impl<S: 'static> Element for EditorElement<S> {
+    type State = S;
+    type FrameState = ();
+
+    fn layout(
+        &mut self,
+        _: &mut Self::State,
+        cx: &mut ViewContext<Self::State>,
+    ) -> Result<(LayoutId, Self::FrameState)> {
+        self.editor.update(cx, |editor, cx| todo!())
+    }
+
+    fn paint(
+        &mut self,
+        layout: Layout,
+        state: &mut Self::State,
+        frame_state: &mut Self::FrameState,
+        cx: &mut ViewContext<Self::State>,
+    ) -> Result<()> {
+        self.editor.update(cx, |editor, cx| todo!())
+    }
+}
+
+struct Editor {}
+
+impl Editor {
+    pub fn new(_: &mut ViewContext<Self>) -> Self {
+        Editor {}
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test() {
+        let mut cx = AppContext::new();
+        cx.open_window(|cx| workspace(cx));
+    }
+}

crates/storybook/src/gpui3/taffy.rs 🔗

@@ -0,0 +1,177 @@
+use super::{
+    AbsoluteLength, Bounds, DefiniteLength, Edges, Layout, LayoutEngine, LayoutId, Length, Pixels,
+    Point, Size, Style,
+};
+use anyhow::Result;
+use gpui2::taffy::{self, Taffy};
+use std::fmt::Debug;
+
+pub use gpui2::taffy::tree::NodeId;
+
+pub struct TaffyLayoutEngine(Taffy);
+
+impl TaffyLayoutEngine {
+    pub fn new() -> Self {
+        TaffyLayoutEngine(Taffy::new())
+    }
+}
+
+impl LayoutEngine for TaffyLayoutEngine {
+    fn request_layout(&mut self, style: Style, children: &[LayoutId]) -> Result<LayoutId> {
+        todo!()
+    }
+
+    fn layout(&mut self, id: LayoutId) -> Result<Layout> {
+        todo!()
+    }
+}
+
+trait ToTaffy {
+    type Output;
+
+    fn to_taffy(&self, rem_size: Pixels) -> Self::Output;
+}
+
+impl ToTaffy for Style {
+    type Output = taffy::style::Style;
+
+    fn to_taffy(&self, rem_size: Pixels) -> Self::Output {
+        todo!()
+    }
+}
+
+// impl ToTaffy for Bounds<Length> {
+//     type Output = taffy::prelude::Bounds<taffy::prelude::LengthPercentageAuto>;
+
+//     fn to_taffy(
+//         &self,
+//         rem_size: Pixels,
+//     ) -> taffy::prelude::Bounds<taffy::prelude::LengthPercentageAuto> {
+//         taffy::prelude::Bounds {
+//             origin: self.origin.to_taffy(rem_size),
+//             size: self.size.to_taffy(rem_size),
+//         }
+//     }
+// }
+
+impl ToTaffy for Length {
+    type Output = taffy::style::LengthPercentageAuto;
+
+    fn to_taffy(&self, rem_size: Pixels) -> taffy::prelude::LengthPercentageAuto {
+        match self {
+            Length::Definite(length) => length.to_taffy(rem_size).into(),
+            Length::Auto => taffy::prelude::LengthPercentageAuto::Auto,
+        }
+    }
+}
+
+impl ToTaffy for DefiniteLength {
+    type Output = taffy::style::LengthPercentage;
+
+    fn to_taffy(&self, rem_size: Pixels) -> taffy::style::LengthPercentage {
+        match self {
+            DefiniteLength::Absolute(length) => match length {
+                AbsoluteLength::Pixels(pixels) => {
+                    taffy::style::LengthPercentage::Length(pixels.into())
+                }
+                AbsoluteLength::Rems(rems) => {
+                    taffy::style::LengthPercentage::Length((*rems * rem_size).into())
+                }
+            },
+            DefiniteLength::Fraction(fraction) => {
+                taffy::style::LengthPercentage::Percent(*fraction)
+            }
+        }
+    }
+}
+
+impl ToTaffy for AbsoluteLength {
+    type Output = taffy::style::LengthPercentage;
+
+    fn to_taffy(&self, rem_size: Pixels) -> Self::Output {
+        match self {
+            AbsoluteLength::Pixels(pixels) => taffy::style::LengthPercentage::Length(pixels.into()),
+            AbsoluteLength::Rems(rems) => {
+                taffy::style::LengthPercentage::Length((*rems * rem_size).into())
+            }
+        }
+    }
+}
+
+impl<T, T2> From<taffy::geometry::Point<T>> for Point<T2>
+where
+    T: Into<T2>,
+{
+    fn from(point: taffy::geometry::Point<T>) -> Point<T2> {
+        Point {
+            x: point.x.into(),
+            y: point.y.into(),
+        }
+    }
+}
+
+impl<T, T2> Into<taffy::geometry::Point<T2>> for Point<T>
+where
+    T: Into<T2>,
+{
+    fn into(self) -> taffy::geometry::Point<T2> {
+        taffy::geometry::Point {
+            x: self.x.into(),
+            y: self.y.into(),
+        }
+    }
+}
+
+impl<T: ToTaffy + Clone> ToTaffy for Size<T> {
+    type Output = taffy::geometry::Size<T::Output>;
+
+    fn to_taffy(&self, rem_size: Pixels) -> Self::Output {
+        taffy::geometry::Size {
+            width: self.width.to_taffy(rem_size).into(),
+            height: self.height.to_taffy(rem_size).into(),
+        }
+    }
+}
+
+impl<T: ToTaffy + Clone> ToTaffy for Edges<T> {
+    type Output = taffy::geometry::Rect<T::Output>;
+
+    fn to_taffy(&self, rem_size: Pixels) -> Self::Output {
+        taffy::geometry::Rect {
+            top: self.top.to_taffy(rem_size),
+            right: self.right.to_taffy(rem_size),
+            bottom: self.bottom.to_taffy(rem_size),
+            left: self.left.to_taffy(rem_size),
+        }
+    }
+}
+
+impl<S, T: Clone + Default + Debug> From<taffy::geometry::Size<S>> for Size<T>
+where
+    S: Into<T>,
+{
+    fn from(value: taffy::geometry::Size<S>) -> Self {
+        Self {
+            width: value.width.into(),
+            height: value.height.into(),
+        }
+    }
+}
+
+impl From<&taffy::tree::Layout> for Layout {
+    fn from(layout: &taffy::tree::Layout) -> Self {
+        Layout {
+            order: layout.order,
+            bounds: Bounds {
+                origin: layout.location.into(),
+                size: layout.size.into(),
+            },
+        }
+    }
+}
+
+impl From<f32> for Pixels {
+    fn from(pixels: f32) -> Self {
+        Self(pixels)
+    }
+}

crates/storybook/src/gpui3/window.rs 🔗

@@ -0,0 +1,215 @@
+use super::{px, AppContext, Bounds, Context, EntityId, Handle, Pixels, Style, TaffyLayoutEngine};
+use anyhow::Result;
+use derive_more::{Deref, DerefMut};
+use gpui2::Reference;
+use std::marker::PhantomData;
+
+pub struct Window {
+    id: WindowId,
+    rem_size: Pixels,
+    layout_engine: Box<dyn LayoutEngine>,
+}
+
+impl Window {
+    pub fn new(window_count: &mut usize) -> Window {
+        let id = WindowId::new(window_count);
+        Window {
+            id,
+            layout_engine: Box::new(TaffyLayoutEngine::new()),
+            rem_size: px(16.),
+        }
+    }
+}
+
+#[derive(Deref, DerefMut)]
+pub struct WindowContext<'a, 'b> {
+    #[deref]
+    #[deref_mut]
+    app: Reference<'a, AppContext>,
+    window: Reference<'b, Window>,
+}
+
+impl<'a, 'w> WindowContext<'a, 'w> {
+    pub(crate) fn mutable(app: &'a mut AppContext, window: &'w mut Window) -> Self {
+        Self {
+            app: Reference::Mutable(app),
+            window: Reference::Mutable(window),
+        }
+    }
+
+    pub(crate) fn immutable(app: &'a AppContext, window: &'w Window) -> Self {
+        Self {
+            app: Reference::Immutable(app),
+            window: Reference::Immutable(window),
+        }
+    }
+
+    pub fn request_layout(
+        &mut self,
+        style: Style,
+        children: impl IntoIterator<Item = LayoutId>,
+    ) -> Result<LayoutId> {
+        self.app.child_layout_buffer.clear();
+        self.app.child_layout_buffer.extend(children.into_iter());
+        self.window
+            .layout_engine
+            .request_layout(style, &self.app.child_layout_buffer)
+    }
+
+    pub fn layout(&mut self, layout_id: LayoutId) -> Result<Layout> {
+        Ok(self
+            .window
+            .layout_engine
+            .layout(layout_id)
+            .map(Into::into)?)
+    }
+
+    pub fn rem_size(&self) -> Pixels {
+        self.window.rem_size
+    }
+
+    fn update_window<R>(
+        &mut self,
+        window_id: WindowId,
+        update: impl FnOnce(&mut WindowContext) -> R,
+    ) -> Result<R> {
+        if window_id == self.window.id {
+            Ok(update(self))
+        } else {
+            self.app.update_window(window_id, update)
+        }
+    }
+}
+
+impl Context for WindowContext<'_, '_> {
+    type EntityContext<'a, 'w, T: 'static> = ViewContext<'a, 'w, T>;
+
+    fn entity<T: 'static>(
+        &mut self,
+        build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
+    ) -> Handle<T> {
+        {
+            let id = EntityId::new(&mut self.app.entity_count);
+            let entity = build_entity(&mut ViewContext::mutable(
+                &mut *self.app,
+                &mut self.window,
+                id,
+            ));
+            let handle = Handle {
+                id,
+                entity_type: PhantomData,
+            };
+            self.app.entities.insert(handle.id, Box::new(entity));
+            handle
+        }
+    }
+
+    fn update_entity<T: 'static, R>(
+        &mut self,
+        handle: &Handle<T>,
+        update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
+    ) -> R {
+        let mut entity = self
+            .app
+            .entities
+            .remove(&handle.id)
+            .unwrap()
+            .downcast::<T>()
+            .unwrap();
+        let result = update(
+            &mut *entity,
+            &mut ViewContext::mutable(&mut *self.app, &mut *self.window, handle.id),
+        );
+        self.entities.insert(handle.id, Box::new(entity));
+        result
+    }
+}
+
+#[derive(Deref, DerefMut)]
+pub struct ViewContext<'a, 'w, T> {
+    #[deref]
+    #[deref_mut]
+    window_cx: WindowContext<'a, 'w>,
+    entity_type: PhantomData<T>,
+    entity_id: EntityId,
+}
+
+impl<'a, 'w, T: 'static> ViewContext<'a, 'w, T> {
+    fn update<R>(&mut self, update: impl FnOnce(&mut T, &mut Self) -> R) -> R {
+        let mut entity = self.window_cx.app.entities.remove(&self.entity_id).unwrap();
+        let result = update(entity.downcast_mut::<T>().unwrap(), self);
+        self.window_cx
+            .app
+            .entities
+            .insert(self.entity_id, Box::new(entity));
+        result
+    }
+
+    fn mutable(app: &'a mut AppContext, window: &'w mut Window, entity_id: EntityId) -> Self {
+        Self {
+            window_cx: WindowContext::mutable(app, window),
+            entity_id,
+            entity_type: PhantomData,
+        }
+    }
+
+    fn immutable(app: &'a AppContext, window: &'w Window, entity_id: EntityId) -> Self {
+        Self {
+            window_cx: WindowContext::immutable(app, window),
+            entity_id,
+            entity_type: PhantomData,
+        }
+    }
+}
+
+impl<'a, 'w, T: 'static> Context for ViewContext<'a, 'w, T> {
+    type EntityContext<'b, 'c, U: 'static> = ViewContext<'b, 'c, U>;
+
+    fn entity<T2: 'static>(
+        &mut self,
+        build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T2>) -> T2,
+    ) -> Handle<T2> {
+        self.window_cx.entity(build_entity)
+    }
+
+    fn update_entity<U: 'static, R>(
+        &mut self,
+        handle: &Handle<U>,
+        update: impl FnOnce(&mut U, &mut Self::EntityContext<'_, '_, U>) -> R,
+    ) -> R {
+        self.window_cx.update_entity(handle, update)
+    }
+}
+
+#[derive(Clone, Copy, Eq, PartialEq, Hash)]
+pub struct WindowId(usize);
+
+impl WindowId {
+    fn new(window_count: &mut usize) -> Self {
+        let id = *window_count;
+        *window_count += 1;
+        Self(id)
+    }
+}
+
+pub struct WindowHandle<S> {
+    id: WindowId,
+    state_type: PhantomData<S>,
+}
+
+#[derive(Clone)]
+pub struct Layout {
+    pub order: u32,
+    pub bounds: Bounds<Pixels>,
+}
+
+#[derive(Copy, Clone)]
+pub struct LayoutId(slotmap::DefaultKey);
+
+pub trait LayoutEngine {
+    /// Register a new node on which to perform layout.
+    fn request_layout(&mut self, style: Style, children: &[LayoutId]) -> Result<LayoutId>;
+
+    /// Get the layout for the given id.
+    fn layout(&mut self, id: LayoutId) -> Result<Layout>;
+}

crates/storybook/src/sketch.rs 🔗

@@ -1,737 +0,0 @@
-use anyhow::{anyhow, Result};
-use derive_more::{Deref, DerefMut};
-use gpui2::{taffy::Taffy, ArcCow, Layout, LayoutId, Reference, Vector2F};
-use std::{any::Any, cell::RefCell, collections::HashMap, marker::PhantomData, rc::Rc};
-
-pub struct AppContext {
-    entity_count: usize,
-    entities: HashMap<EntityId, Box<dyn Any>>,
-    window_count: usize,
-    windows: HashMap<WindowId, Window>,
-}
-
-impl AppContext {
-    pub fn new() -> Self {
-        AppContext {
-            entity_count: 0,
-            entities: HashMap::new(),
-            window_count: 0,
-            windows: HashMap::new(),
-        }
-    }
-
-    pub fn open_window<S>(
-        &mut self,
-        build_root_view: impl FnOnce(&mut WindowContext) -> View<S>,
-    ) -> WindowHandle<S> {
-        let window = Window::new(&mut self.window_count);
-
-        unimplemented!()
-    }
-
-    fn add_entity<T: 'static>(
-        &mut self,
-        build_entity: impl FnOnce(&mut ModelContext<T>) -> T,
-    ) -> Handle<T> {
-        let id = EntityId::new(&mut self.entity_count);
-        let entity = build_entity(&mut ModelContext::mutable(self, id));
-        self.entities.insert(id, Box::new(entity));
-        Handle {
-            id,
-            entity_type: PhantomData,
-        }
-    }
-
-    fn update_window<R>(
-        &mut self,
-        window_id: WindowId,
-        update: impl FnOnce(&mut WindowContext) -> R,
-    ) -> Result<R> {
-        let mut window = self
-            .windows
-            .remove(&window_id)
-            .ok_or_else(|| anyhow!("window not found"))?;
-        let result = update(&mut WindowContext::mutable(self, &mut window));
-        self.windows.insert(window_id, window);
-        Ok(result)
-    }
-}
-
-pub struct ModelContext<'a, T> {
-    app: Reference<'a, AppContext>,
-    entity_type: PhantomData<T>,
-    entity_id: EntityId,
-}
-
-impl<'a, T: 'static> ModelContext<'a, T> {
-    fn mutable(app: &'a mut AppContext, entity_id: EntityId) -> Self {
-        Self {
-            app: Reference::Mutable(app),
-            entity_type: PhantomData,
-            entity_id,
-        }
-    }
-
-    fn immutable(app: &'a AppContext, entity_id: EntityId) -> Self {
-        Self {
-            app: Reference::Immutable(app),
-            entity_type: PhantomData,
-            entity_id,
-        }
-    }
-
-    fn update<R>(&mut self, update: impl FnOnce(&mut T, &mut Self) -> R) -> R {
-        let mut entity = self.app.entities.remove(&self.entity_id).unwrap();
-        let result = update(entity.downcast_mut::<T>().unwrap(), self);
-        self.app.entities.insert(self.entity_id, Box::new(entity));
-        result
-    }
-}
-
-pub struct Window {
-    id: WindowId,
-    layout_engine: Taffy,
-}
-
-impl Window {
-    pub fn new(window_count: &mut usize) -> Window {
-        let id = WindowId::new(window_count);
-        Window {
-            id,
-            layout_engine: Taffy::new(),
-        }
-    }
-}
-
-#[derive(Deref, DerefMut)]
-pub struct WindowContext<'a, 'b> {
-    #[deref]
-    #[deref_mut]
-    app: Reference<'a, AppContext>,
-    window: Reference<'b, Window>,
-}
-
-impl<'a, 'w> WindowContext<'a, 'w> {
-    fn mutable(app: &'a mut AppContext, window: &'w mut Window) -> Self {
-        Self {
-            app: Reference::Mutable(app),
-            window: Reference::Mutable(window),
-        }
-    }
-
-    fn immutable(app: &'a AppContext, window: &'w Window) -> Self {
-        Self {
-            app: Reference::Immutable(app),
-            window: Reference::Immutable(window),
-        }
-    }
-
-    fn app_context(&mut self) -> &mut AppContext {
-        &mut *self.app
-    }
-}
-
-impl<'a, 'w> WindowContext<'a, 'w> {
-    fn entity<T: 'static>(
-        &mut self,
-        build_entity: impl FnOnce(&mut ViewContext<'_, '_, T>) -> T,
-    ) -> Handle<T> {
-        let id = EntityId::new(&mut self.app_context().entity_count);
-        let entity = build_entity(&mut ViewContext::mutable(
-            &mut *self.app,
-            &mut *self.window,
-            id,
-        ));
-        self.app.entities.insert(id, Box::new(entity));
-        Handle {
-            id,
-            entity_type: PhantomData,
-        }
-    }
-
-    fn update_entity<T: 'static, R>(
-        &mut self,
-        handle: &Handle<T>,
-        update: impl FnOnce(&mut T, &mut ViewContext<T>) -> R,
-    ) -> R {
-        let mut entity = self.app.entities.remove(&handle.id).unwrap();
-        let result = update(
-            entity.downcast_mut().unwrap(),
-            &mut ViewContext::mutable(&mut *self.app, &mut *self.window, handle.id),
-        );
-        self.app.entities.insert(handle.id, entity);
-        result
-    }
-
-    fn update_window<R>(
-        &mut self,
-        window_id: WindowId,
-        update: impl FnOnce(&mut WindowContext) -> R,
-    ) -> Result<R> {
-        if window_id == self.window.id {
-            Ok(update(self))
-        } else {
-            self.app.update_window(window_id, update)
-        }
-    }
-}
-
-#[derive(Deref, DerefMut)]
-pub struct ViewContext<'a, 'w, T> {
-    #[deref]
-    #[deref_mut]
-    window_cx: WindowContext<'a, 'w>,
-    entity_type: PhantomData<T>,
-    entity_id: EntityId,
-}
-
-impl<'a, 'w, T: 'static> ViewContext<'a, 'w, T> {
-    fn update<R>(&mut self, update: impl FnOnce(&mut T, &mut Self) -> R) -> R {
-        let mut entity = self.window_cx.app.entities.remove(&self.entity_id).unwrap();
-        let result = update(entity.downcast_mut::<T>().unwrap(), self);
-        self.window_cx
-            .app
-            .entities
-            .insert(self.entity_id, Box::new(entity));
-        result
-    }
-
-    fn mutable(app: &'a mut AppContext, window: &'w mut Window, entity_id: EntityId) -> Self {
-        Self {
-            window_cx: WindowContext::mutable(app, window),
-            entity_id,
-            entity_type: PhantomData,
-        }
-    }
-
-    fn immutable(app: &'a AppContext, window: &'w Window, entity_id: EntityId) -> Self {
-        Self {
-            window_cx: WindowContext::immutable(app, window),
-            entity_id,
-            entity_type: PhantomData,
-        }
-    }
-}
-
-impl<'a, 'w, T: 'static> Context for ViewContext<'a, 'w, T> {
-    type EntityContext<'b, 'c, U: 'static> = ViewContext<'b, 'c, U>;
-
-    fn update_entity<U: 'static, R>(
-        &mut self,
-        handle: &Handle<U>,
-        update: impl FnOnce(&mut U, &mut Self::EntityContext<'_, '_, U>) -> R,
-    ) -> R {
-        ViewContext::mutable(
-            &mut *self.window_cx.app,
-            &mut *self.window_cx.window,
-            handle.id,
-        )
-        .update(update)
-    }
-}
-
-#[derive(Clone, Copy, Eq, PartialEq, Hash)]
-pub struct WindowId(usize);
-
-impl WindowId {
-    fn new(window_count: &mut usize) -> Self {
-        let id = *window_count;
-        *window_count += 1;
-        Self(id)
-    }
-}
-
-pub struct WindowHandle<S> {
-    id: WindowId,
-    state_type: PhantomData<S>,
-}
-
-#[derive(Clone, Copy, Eq, PartialEq, Hash)]
-pub struct EntityId(usize);
-
-impl EntityId {
-    fn new(entity_count: &mut usize) -> EntityId {
-        let id = *entity_count;
-        *entity_count += 1;
-        Self(id)
-    }
-}
-
-trait Context {
-    type EntityContext<'a, 'w, T: 'static>;
-
-    fn update_entity<T: 'static, R>(
-        &mut self,
-        handle: &Handle<T>,
-        update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
-    ) -> R;
-}
-
-impl Context for AppContext {
-    type EntityContext<'a, 'w, T: 'static> = ModelContext<'a, T>;
-
-    fn update_entity<T: 'static, R>(
-        &mut self,
-        handle: &Handle<T>,
-        update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
-    ) -> R {
-        let mut entity = self
-            .entities
-            .remove(&handle.id)
-            .unwrap()
-            .downcast::<T>()
-            .unwrap();
-        let result = update(&mut *entity, &mut ModelContext::mutable(self, handle.id));
-        self.entities.insert(handle.id, Box::new(entity));
-        result
-    }
-}
-
-impl Context for WindowContext<'_, '_> {
-    type EntityContext<'a, 'w, T: 'static> = ViewContext<'a, 'w, T>;
-
-    fn update_entity<T: 'static, R>(
-        &mut self,
-        handle: &Handle<T>,
-        update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
-    ) -> R {
-        let mut entity = self
-            .app
-            .entities
-            .remove(&handle.id)
-            .unwrap()
-            .downcast::<T>()
-            .unwrap();
-        let result = update(
-            &mut *entity,
-            &mut ViewContext::mutable(&mut *self.app, &mut *self.window, handle.id),
-        );
-        self.entities.insert(handle.id, Box::new(entity));
-        result
-    }
-}
-
-pub struct Handle<T> {
-    id: EntityId,
-    entity_type: PhantomData<T>,
-}
-
-impl<T: 'static> Handle<T> {
-    fn update<C: Context, R>(
-        &self,
-        cx: &mut C,
-        update: impl FnOnce(&mut T, &mut C::EntityContext<'_, '_, T>) -> R,
-    ) -> R {
-        cx.update_entity(self, update)
-    }
-}
-
-impl<T> Clone for Handle<T> {
-    fn clone(&self) -> Self {
-        Self {
-            id: self.id,
-            entity_type: PhantomData,
-        }
-    }
-}
-
-pub trait Element: 'static {
-    type State;
-    type FrameState;
-
-    fn layout(
-        &mut self,
-        state: &mut Self::State,
-        cx: &mut ViewContext<Self::State>,
-    ) -> Result<(LayoutId, Self::FrameState)>;
-
-    fn paint(
-        &mut self,
-        layout: Layout,
-        state: &mut Self::State,
-        frame_state: &mut Self::FrameState,
-        cx: &mut ViewContext<Self::State>,
-    ) -> Result<()>;
-}
-
-pub trait ParentElement<S> {
-    fn child(self, child: impl IntoAnyElement<S>) -> Self;
-}
-
-trait ElementObject<S> {
-    fn layout(&mut self, state: &mut S, cx: &mut ViewContext<S>) -> Result<LayoutId>;
-    fn paint(
-        &mut self,
-        parent_origin: Vector2F,
-        state: &mut S,
-        cx: &mut ViewContext<S>,
-    ) -> Result<()>;
-}
-
-struct RenderedElement<E: Element> {
-    element: E,
-    phase: ElementRenderPhase<E::FrameState>,
-}
-
-#[derive(Default)]
-enum ElementRenderPhase<S> {
-    #[default]
-    Rendered,
-    LayoutRequested {
-        layout_id: LayoutId,
-        frame_state: S,
-    },
-    Painted {
-        layout: Layout,
-        frame_state: S,
-    },
-}
-
-impl<E: Element> RenderedElement<E> {
-    fn new(element: E) -> Self {
-        RenderedElement {
-            element,
-            phase: ElementRenderPhase::Rendered,
-        }
-    }
-}
-
-impl<E: Element> ElementObject<E::State> for RenderedElement<E> {
-    fn layout(&mut self, state: &mut E::State, cx: &mut ViewContext<E::State>) -> Result<LayoutId> {
-        let (layout_id, frame_state) = self.element.layout(state, cx)?;
-        self.phase = ElementRenderPhase::LayoutRequested {
-            layout_id,
-            frame_state,
-        };
-        Ok(layout_id)
-    }
-
-    fn paint(
-        &mut self,
-        parent_origin: Vector2F,
-        state: &mut E::State,
-        cx: &mut ViewContext<E::State>,
-    ) -> Result<()> {
-        self.phase = match std::mem::take(&mut self.phase) {
-            ElementRenderPhase::Rendered => panic!("must call layout before paint"),
-            ElementRenderPhase::LayoutRequested {
-                layout_id,
-                frame_state,
-            } => {
-                todo!()
-            }
-            ElementRenderPhase::Painted {
-                layout,
-                frame_state,
-            } => todo!(),
-        };
-        Ok(())
-    }
-}
-
-pub struct AnyElement<S>(Box<dyn ElementObject<S>>);
-
-impl<S> AnyElement<S> {
-    pub fn layout(&mut self, state: &mut S, cx: &mut ViewContext<S>) -> Result<LayoutId> {
-        self.0.layout(state, cx)
-    }
-
-    pub fn paint(
-        &mut self,
-        parent_origin: Vector2F,
-        state: &mut S,
-        cx: &mut ViewContext<S>,
-    ) -> Result<()> {
-        self.0.paint(parent_origin, state, cx)
-    }
-}
-
-pub trait IntoAnyElement<S> {
-    fn into_any(self) -> AnyElement<S>;
-}
-
-impl<E: Element> IntoAnyElement<E::State> for E {
-    fn into_any(self) -> AnyElement<E::State> {
-        AnyElement(Box::new(RenderedElement::new(self)))
-    }
-}
-
-impl<S> IntoAnyElement<S> for AnyElement<S> {
-    fn into_any(self) -> AnyElement<S> {
-        self
-    }
-}
-
-#[derive(Clone)]
-pub struct View<S> {
-    state: Handle<S>,
-    render: Rc<dyn Fn(&mut S, &mut ViewContext<S>) -> AnyElement<S>>,
-}
-
-pub fn view<S: 'static, E: Element<State = S>>(
-    state: Handle<S>,
-    render: impl 'static + Fn(&mut S, &mut ViewContext<S>) -> E,
-) -> View<S> {
-    View {
-        state,
-        render: Rc::new(move |state, cx| render(state, cx).into_any()),
-    }
-}
-
-impl<S: 'static> View<S> {
-    pub fn into_any<ParentState>(self) -> AnyView<ParentState> {
-        AnyView {
-            view: Rc::new(RefCell::new(self)),
-            parent_state_type: PhantomData,
-        }
-    }
-}
-
-impl<S: 'static> Element for View<S> {
-    type State = ();
-    type FrameState = AnyElement<S>;
-
-    fn layout(
-        &mut self,
-        _: &mut Self::State,
-        cx: &mut ViewContext<Self::State>,
-    ) -> Result<(LayoutId, Self::FrameState)> {
-        self.state.update(cx, |state, cx| {
-            let mut element = (self.render)(state, cx);
-            let layout_id = element.layout(state, cx)?;
-            Ok((layout_id, element))
-        })
-    }
-
-    fn paint(
-        &mut self,
-        layout: Layout,
-        _: &mut Self::State,
-        element: &mut Self::FrameState,
-        cx: &mut ViewContext<Self::State>,
-    ) -> Result<()> {
-        self.state.update(cx, |state, cx| {
-            element.paint(layout.bounds.origin(), state, cx)
-        })
-    }
-}
-
-trait ViewObject {
-    fn layout(&mut self, cx: &mut WindowContext) -> Result<(LayoutId, Box<dyn Any>)>;
-    fn paint(
-        &mut self,
-        layout: Layout,
-        element: &mut dyn Any,
-        cx: &mut WindowContext,
-    ) -> Result<()>;
-}
-
-impl<S: 'static> ViewObject for View<S> {
-    fn layout(&mut self, cx: &mut WindowContext) -> Result<(LayoutId, Box<dyn Any>)> {
-        self.state.update(cx, |state, cx| {
-            let mut element = (self.render)(state, cx);
-            let layout_id = element.layout(state, cx)?;
-            let element = Box::new(element) as Box<dyn Any>;
-            Ok((layout_id, element))
-        })
-    }
-
-    fn paint(
-        &mut self,
-        layout: Layout,
-        element: &mut dyn Any,
-        cx: &mut WindowContext,
-    ) -> Result<()> {
-        self.state.update(cx, |state, cx| {
-            element.downcast_mut::<AnyElement<S>>().unwrap().paint(
-                layout.bounds.origin(),
-                state,
-                cx,
-            )
-        })
-    }
-}
-
-pub struct AnyView<S> {
-    view: Rc<RefCell<dyn ViewObject>>,
-    parent_state_type: PhantomData<S>,
-}
-
-impl<S: 'static> Element for AnyView<S> {
-    type State = S;
-    type FrameState = Box<dyn Any>;
-
-    fn layout(
-        &mut self,
-        _: &mut Self::State,
-        cx: &mut ViewContext<Self::State>,
-    ) -> Result<(LayoutId, Self::FrameState)> {
-        self.view.borrow_mut().layout(cx)
-    }
-
-    fn paint(
-        &mut self,
-        layout: Layout,
-        _: &mut Self::State,
-        element: &mut Self::FrameState,
-        cx: &mut ViewContext<Self::State>,
-    ) -> Result<()> {
-        self.view.borrow_mut().paint(layout, element, cx)
-    }
-}
-
-impl<S> Clone for AnyView<S> {
-    fn clone(&self) -> Self {
-        Self {
-            view: self.view.clone(),
-            parent_state_type: PhantomData,
-        }
-    }
-}
-
-pub struct Div<S>(PhantomData<S>);
-
-impl<S: 'static> Element for Div<S> {
-    type State = S;
-    type FrameState = ();
-
-    fn layout(
-        &mut self,
-        state: &mut Self::State,
-        cx: &mut ViewContext<Self::State>,
-    ) -> Result<(LayoutId, Self::FrameState)> {
-        todo!()
-    }
-
-    fn paint(
-        &mut self,
-        layout: Layout,
-        state: &mut Self::State,
-        frame_state: &mut Self::FrameState,
-        cx: &mut ViewContext<Self::State>,
-    ) -> Result<()> {
-        todo!()
-    }
-}
-
-impl<S> ParentElement<S> for Div<S> {
-    fn child(self, child: impl IntoAnyElement<S>) -> Self {
-        todo!()
-    }
-}
-
-pub fn div<S>() -> Div<S> {
-    todo!()
-}
-
-pub struct SharedString(ArcCow<'static, str>);
-
-impl<T: Into<ArcCow<'static, str>>> From<T> for SharedString {
-    fn from(value: T) -> Self {
-        Self(value.into())
-    }
-}
-
-struct Workspace {
-    left_panel: AnyView<Self>,
-}
-
-fn workspace(cx: &mut WindowContext) -> View<Workspace> {
-    let workspace = cx.entity(|cx| Workspace {
-        left_panel: collab_panel(cx).into_any(),
-    });
-    view(workspace, |workspace, cx| {
-        div().child(workspace.left_panel.clone())
-    })
-}
-
-struct CollabPanel {
-    filter_editor: Handle<Editor>,
-}
-
-fn collab_panel(cx: &mut WindowContext) -> View<CollabPanel> {
-    let panel = cx.entity(|cx| CollabPanel::new(cx));
-    view(panel, |panel, cx| {
-        div()
-            .child(div())
-            .child(field(panel.filter_editor.clone()).placeholder_text("Search channels, contacts"))
-    })
-}
-
-impl CollabPanel {
-    fn new(cx: &mut ViewContext<Self>) -> Self {
-        Self {
-            filter_editor: cx.entity(|cx| Editor::new(cx)),
-        }
-    }
-}
-
-fn field<S>(editor: Handle<Editor>) -> EditorElement<S> {
-    EditorElement {
-        editor,
-        field: true,
-        placeholder_text: None,
-        parent_state: PhantomData,
-    }
-}
-
-struct EditorElement<S> {
-    editor: Handle<Editor>,
-    field: bool,
-    placeholder_text: Option<SharedString>,
-    parent_state: PhantomData<S>,
-}
-
-impl<S> EditorElement<S> {
-    pub fn field(mut self) -> Self {
-        self.field = true;
-        self
-    }
-
-    pub fn placeholder_text(mut self, text: impl Into<SharedString>) -> Self {
-        self.placeholder_text = Some(text.into());
-        self
-    }
-}
-
-impl<S: 'static> Element for EditorElement<S> {
-    type State = S;
-    type FrameState = ();
-
-    fn layout(
-        &mut self,
-        _: &mut Self::State,
-        cx: &mut ViewContext<Self::State>,
-    ) -> Result<(LayoutId, Self::FrameState)> {
-        self.editor.update(cx, |editor, cx| todo!())
-    }
-
-    fn paint(
-        &mut self,
-        layout: Layout,
-        state: &mut Self::State,
-        frame_state: &mut Self::FrameState,
-        cx: &mut ViewContext<Self::State>,
-    ) -> Result<()> {
-        self.editor.update(cx, |editor, cx| todo!())
-    }
-}
-
-struct Editor {}
-
-impl Editor {
-    pub fn new(_: &mut ViewContext<Self>) -> Self {
-        Editor {}
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn test() {
-        let mut cx = AppContext::new();
-        cx.open_window(|cx| workspace(cx));
-    }
-}

crates/storybook/src/storybook.rs 🔗

@@ -12,7 +12,7 @@ use simplelog::SimpleLogger;
 mod collab_panel;
 mod components;
 mod element_ext;
-mod sketch;
+mod gpui3;
 mod theme;
 mod workspace;