diff --git a/Cargo.lock b/Cargo.lock index 1e4193a4cb..16ff87f3da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7384,6 +7384,7 @@ dependencies = [ "serde", "settings", "simplelog", + "slotmap", "theme", "util", ] diff --git a/crates/refineable/derive_refineable/src/derive_refineable.rs b/crates/refineable/derive_refineable/src/derive_refineable.rs index 547af0e757..7fd79141a8 100644 --- a/crates/refineable/derive_refineable/src/derive_refineable.rs +++ b/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 { diff --git a/crates/storybook/Cargo.toml b/crates/storybook/Cargo.toml index a14482038c..32b0b989e3 100644 --- a/crates/storybook/Cargo.toml +++ b/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" } diff --git a/crates/storybook/src/gpui3/app.rs b/crates/storybook/src/gpui3/app.rs new file mode 100644 index 0000000000..13bd484127 --- /dev/null +++ b/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>, + pub(crate) window_count: usize, + pub(crate) windows: HashMap, + // We recycle this memory across layout requests. + pub(crate) child_layout_buffer: Vec, +} + +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( + &mut self, + build_root_view: impl FnOnce(&mut WindowContext) -> View, + ) -> WindowHandle { + let window = Window::new(&mut self.window_count); + + unimplemented!() + } + + pub(crate) fn update_window( + &mut self, + window_id: WindowId, + update: impl FnOnce(&mut WindowContext) -> R, + ) -> Result { + 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( + &mut self, + build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T, + ) -> Handle { + 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( + &mut self, + handle: &Handle, + update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R, + ) -> R { + let mut entity = self + .entities + .remove(&handle.id) + .unwrap() + .downcast::() + .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, + 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(&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::().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( + &mut self, + build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, U>) -> U, + ) -> Handle { + self.app.entity(build_entity) + } + + fn update_entity( + &mut self, + handle: &Handle, + update: impl FnOnce(&mut U, &mut Self::EntityContext<'_, '_, U>) -> R, + ) -> R { + self.app.update_entity(handle, update) + } +} + +pub struct Handle { + pub(crate) id: EntityId, + pub(crate) entity_type: PhantomData, +} + +impl Handle { + 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( + &self, + cx: &mut C, + update: impl FnOnce(&mut T, &mut C::EntityContext<'_, '_, T>) -> R, + ) -> R { + cx.update_entity(self, update) + } +} + +impl Clone for Handle { + fn clone(&self) -> Self { + Self { + id: self.id, + entity_type: PhantomData, + } + } +} diff --git a/crates/storybook/src/gpui3/element.rs b/crates/storybook/src/gpui3/element.rs new file mode 100644 index 0000000000..ea54e1a9c3 --- /dev/null +++ b/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, + ) -> Result<(LayoutId, Self::FrameState)>; + + fn paint( + &mut self, + layout: Layout, + state: &mut Self::State, + frame_state: &mut Self::FrameState, + cx: &mut ViewContext, + ) -> Result<()>; +} + +pub trait ParentElement { + fn child(self, child: impl IntoAnyElement) -> Self; +} + +trait ElementObject { + fn layout(&mut self, state: &mut S, cx: &mut ViewContext) -> Result; + fn paint( + &mut self, + parent_origin: super::Point, + state: &mut S, + cx: &mut ViewContext, + ) -> Result<()>; +} + +struct RenderedElement { + element: E, + phase: ElementRenderPhase, +} + +#[derive(Default)] +enum ElementRenderPhase { + #[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 for +/// improved usability. +impl RenderedElement { + fn new(element: E) -> Self { + RenderedElement { + element, + phase: ElementRenderPhase::Rendered, + } + } +} + +impl ElementObject for RenderedElement { + fn layout(&mut self, state: &mut E::State, cx: &mut ViewContext) -> Result { + 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, + state: &mut E::State, + cx: &mut ViewContext, + ) -> 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(Box>); + +impl AnyElement { + pub fn layout(&mut self, state: &mut S, cx: &mut ViewContext) -> Result { + self.0.layout(state, cx) + } + + pub fn paint( + &mut self, + parent_origin: Point, + state: &mut S, + cx: &mut ViewContext, + ) -> Result<()> { + self.0.paint(parent_origin, state, cx) + } +} + +pub trait IntoAnyElement { + fn into_any(self) -> AnyElement; +} + +impl IntoAnyElement for E { + fn into_any(self) -> AnyElement { + AnyElement(Box::new(RenderedElement::new(self))) + } +} + +impl IntoAnyElement for AnyElement { + fn into_any(self) -> AnyElement { + self + } +} + +#[derive(Clone)] +pub struct View { + state: Handle, + render: Rc) -> AnyElement>, +} + +pub fn view>( + state: Handle, + render: impl 'static + Fn(&mut S, &mut ViewContext) -> E, +) -> View { + View { + state, + render: Rc::new(move |state, cx| render(state, cx).into_any()), + } +} + +impl View { + pub fn into_any(self) -> AnyView { + AnyView { + view: Rc::new(RefCell::new(self)), + parent_state_type: PhantomData, + } + } +} + +impl Element for View { + type State = (); + type FrameState = AnyElement; + + fn layout( + &mut self, + _: &mut Self::State, + cx: &mut ViewContext, + ) -> 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, + ) -> 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)>; + fn paint( + &mut self, + layout: Layout, + element: &mut dyn Any, + cx: &mut WindowContext, + ) -> Result<()>; +} + +impl ViewObject for View { + fn layout(&mut self, cx: &mut WindowContext) -> Result<(LayoutId, Box)> { + 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; + 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::>() + .unwrap() + .paint(layout.bounds.origin, state, cx) + }) + } +} + +pub struct AnyView { + view: Rc>, + parent_state_type: PhantomData, +} + +impl Element for AnyView { + type State = S; + type FrameState = Box; + + fn layout( + &mut self, + _: &mut Self::State, + cx: &mut ViewContext, + ) -> 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, + ) -> Result<()> { + self.view.borrow_mut().paint(layout, element, cx) + } +} + +impl Clone for AnyView { + fn clone(&self) -> Self { + Self { + view: self.view.clone(), + parent_state_type: PhantomData, + } + } +} diff --git a/crates/storybook/src/gpui3/geometry.rs b/crates/storybook/src/gpui3/geometry.rs new file mode 100644 index 0000000000..2a969d8b71 --- /dev/null +++ b/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 { + pub x: T, + pub y: T, +} + +impl Clone for Point { + fn clone(&self) -> Self { + Self { + x: self.x.clone(), + y: self.y.clone(), + } + } +} + +#[derive(Default, Clone, Refineable, Debug)] +pub struct Size { + pub width: T, + pub height: T, +} + +impl Size { + pub fn full() -> Self { + Self { + width: relative(1.), + height: relative(1.), + } + } +} + +impl Size { + pub fn zero() -> Self { + Self { + width: px(0.), + height: px(0.), + } + } +} + +impl Size { + pub fn auto() -> Self { + Self { + width: Length::Auto, + height: Length::Auto, + } + } +} + +#[derive(Clone, Default, Debug)] +pub struct Bounds { + pub origin: Point, + pub size: Size, +} + +#[derive(Clone, Default, Refineable, Debug)] +pub struct Edges { + pub top: T, + pub right: T, + pub bottom: T, + pub left: T, +} + +impl Edges { + 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 { + pub fn zero() -> Self { + Self { + top: px(0.), + right: px(0.), + bottom: px(0.), + left: px(0.), + } + } +} + +impl Edges { + 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 { + 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 { + 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 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 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 for AbsoluteLength { + fn from(pixels: Pixels) -> Self { + AbsoluteLength::Pixels(pixels) + } +} + +impl From 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 for DefiniteLength { + fn from(pixels: Pixels) -> Self { + Self::Absolute(pixels.into()) + } +} + +impl From for DefiniteLength { + fn from(rems: Rems) -> Self { + Self::Absolute(rems.into()) + } +} + +impl From 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>(fraction: f32) -> T { + DefiniteLength::Fraction(fraction).into() +} + +pub fn rems>(rems: f32) -> T { + Rems(rems).into() +} + +pub fn px>(pixels: f32) -> T { + Pixels(pixels).into() +} + +pub fn auto() -> Length { + Length::Auto +} + +impl From for Length { + fn from(pixels: Pixels) -> Self { + Self::Definite(pixels.into()) + } +} + +impl From for Length { + fn from(rems: Rems) -> Self { + Self::Definite(rems.into()) + } +} + +impl From for Length { + fn from(length: DefiniteLength) -> Self { + Self::Definite(length) + } +} + +impl From 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()) + } +} diff --git a/crates/storybook/src/gpui3/mod.rs b/crates/storybook/src/gpui3/mod.rs new file mode 100644 index 0000000000..3690126532 --- /dev/null +++ b/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( + &mut self, + build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T, + ) -> Handle; + + fn update_entity( + &mut self, + handle: &Handle, + update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R, + ) -> R; +} + +pub struct Div(PhantomData); + +impl Element for Div { + type State = S; + type FrameState = (); + + fn layout( + &mut self, + state: &mut Self::State, + cx: &mut ViewContext, + ) -> Result<(LayoutId, Self::FrameState)> { + todo!() + } + + fn paint( + &mut self, + layout: Layout, + state: &mut Self::State, + frame_state: &mut Self::FrameState, + cx: &mut ViewContext, + ) -> Result<()> { + todo!() + } +} + +impl ParentElement for Div { + fn child(self, child: impl IntoAnyElement) -> Self { + todo!() + } +} + +pub fn div() -> Div { + todo!() +} + +pub struct SharedString(ArcCow<'static, str>); + +impl>> From for SharedString { + fn from(value: T) -> Self { + Self(value.into()) + } +} + +struct Workspace { + left_panel: AnyView, +} + +fn workspace(cx: &mut WindowContext) -> View { + 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, +} + +fn collab_panel(cx: &mut WindowContext) -> View { + 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 { + filter_editor: cx.entity(|cx| Editor::new(cx)), + } + } +} + +fn field(editor: Handle) -> EditorElement { + EditorElement { + editor, + field: true, + placeholder_text: None, + parent_state: PhantomData, + } +} + +struct EditorElement { + editor: Handle, + field: bool, + placeholder_text: Option, + parent_state: PhantomData, +} + +impl EditorElement { + pub fn field(mut self) -> Self { + self.field = true; + self + } + + pub fn placeholder_text(mut self, text: impl Into) -> Self { + self.placeholder_text = Some(text.into()); + self + } +} + +impl Element for EditorElement { + type State = S; + type FrameState = (); + + fn layout( + &mut self, + _: &mut Self::State, + cx: &mut ViewContext, + ) -> 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, + ) -> Result<()> { + self.editor.update(cx, |editor, cx| todo!()) + } +} + +struct Editor {} + +impl Editor { + pub fn new(_: &mut ViewContext) -> Self { + Editor {} + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test() { + let mut cx = AppContext::new(); + cx.open_window(|cx| workspace(cx)); + } +} diff --git a/crates/storybook/src/gpui3/style.rs b/crates/storybook/src/gpui3/style.rs new file mode 100644 index 0000000000..b819b33e99 --- /dev/null +++ b/crates/storybook/src/gpui3/style.rs @@ -0,0 +1 @@ +pub struct Style; diff --git a/crates/storybook/src/gpui3/taffy.rs b/crates/storybook/src/gpui3/taffy.rs new file mode 100644 index 0000000000..7405b0161a --- /dev/null +++ b/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 { + todo!() + } + + fn layout(&mut self, id: LayoutId) -> Result { + 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 { +// type Output = taffy::prelude::Bounds; + +// fn to_taffy( +// &self, +// rem_size: Pixels, +// ) -> taffy::prelude::Bounds { +// 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 From> for Point +where + T: Into, +{ + fn from(point: taffy::geometry::Point) -> Point { + Point { + x: point.x.into(), + y: point.y.into(), + } + } +} + +impl Into> for Point +where + T: Into, +{ + fn into(self) -> taffy::geometry::Point { + taffy::geometry::Point { + x: self.x.into(), + y: self.y.into(), + } + } +} + +impl ToTaffy for Size { + type Output = taffy::geometry::Size; + + 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 ToTaffy for Edges { + type Output = taffy::geometry::Rect; + + 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 From> for Size +where + S: Into, +{ + fn from(value: taffy::geometry::Size) -> 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 for Pixels { + fn from(pixels: f32) -> Self { + Self(pixels) + } +} diff --git a/crates/storybook/src/gpui3/window.rs b/crates/storybook/src/gpui3/window.rs new file mode 100644 index 0000000000..20d7aa66ef --- /dev/null +++ b/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, +} + +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, + ) -> Result { + 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 { + Ok(self + .window + .layout_engine + .layout(layout_id) + .map(Into::into)?) + } + + pub fn rem_size(&self) -> Pixels { + self.window.rem_size + } + + fn update_window( + &mut self, + window_id: WindowId, + update: impl FnOnce(&mut WindowContext) -> R, + ) -> Result { + 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( + &mut self, + build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T, + ) -> Handle { + { + 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( + &mut self, + handle: &Handle, + update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R, + ) -> R { + let mut entity = self + .app + .entities + .remove(&handle.id) + .unwrap() + .downcast::() + .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, + entity_id: EntityId, +} + +impl<'a, 'w, T: 'static> ViewContext<'a, 'w, T> { + fn update(&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::().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( + &mut self, + build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T2>) -> T2, + ) -> Handle { + self.window_cx.entity(build_entity) + } + + fn update_entity( + &mut self, + handle: &Handle, + 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 { + id: WindowId, + state_type: PhantomData, +} + +#[derive(Clone)] +pub struct Layout { + pub order: u32, + pub bounds: Bounds, +} + +#[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; + + /// Get the layout for the given id. + fn layout(&mut self, id: LayoutId) -> Result; +} diff --git a/crates/storybook/src/sketch.rs b/crates/storybook/src/sketch.rs deleted file mode 100644 index 28b5a9b6bb..0000000000 --- a/crates/storybook/src/sketch.rs +++ /dev/null @@ -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>, - window_count: usize, - windows: HashMap, -} - -impl AppContext { - pub fn new() -> Self { - AppContext { - entity_count: 0, - entities: HashMap::new(), - window_count: 0, - windows: HashMap::new(), - } - } - - pub fn open_window( - &mut self, - build_root_view: impl FnOnce(&mut WindowContext) -> View, - ) -> WindowHandle { - let window = Window::new(&mut self.window_count); - - unimplemented!() - } - - fn add_entity( - &mut self, - build_entity: impl FnOnce(&mut ModelContext) -> T, - ) -> Handle { - 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( - &mut self, - window_id: WindowId, - update: impl FnOnce(&mut WindowContext) -> R, - ) -> Result { - 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, - 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(&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::().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( - &mut self, - build_entity: impl FnOnce(&mut ViewContext<'_, '_, T>) -> T, - ) -> Handle { - 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( - &mut self, - handle: &Handle, - update: impl FnOnce(&mut T, &mut ViewContext) -> 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( - &mut self, - window_id: WindowId, - update: impl FnOnce(&mut WindowContext) -> R, - ) -> Result { - 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, - entity_id: EntityId, -} - -impl<'a, 'w, T: 'static> ViewContext<'a, 'w, T> { - fn update(&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::().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( - &mut self, - handle: &Handle, - 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 { - id: WindowId, - state_type: PhantomData, -} - -#[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( - &mut self, - handle: &Handle, - 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( - &mut self, - handle: &Handle, - update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R, - ) -> R { - let mut entity = self - .entities - .remove(&handle.id) - .unwrap() - .downcast::() - .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( - &mut self, - handle: &Handle, - update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R, - ) -> R { - let mut entity = self - .app - .entities - .remove(&handle.id) - .unwrap() - .downcast::() - .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 { - id: EntityId, - entity_type: PhantomData, -} - -impl Handle { - fn update( - &self, - cx: &mut C, - update: impl FnOnce(&mut T, &mut C::EntityContext<'_, '_, T>) -> R, - ) -> R { - cx.update_entity(self, update) - } -} - -impl Clone for Handle { - 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, - ) -> Result<(LayoutId, Self::FrameState)>; - - fn paint( - &mut self, - layout: Layout, - state: &mut Self::State, - frame_state: &mut Self::FrameState, - cx: &mut ViewContext, - ) -> Result<()>; -} - -pub trait ParentElement { - fn child(self, child: impl IntoAnyElement) -> Self; -} - -trait ElementObject { - fn layout(&mut self, state: &mut S, cx: &mut ViewContext) -> Result; - fn paint( - &mut self, - parent_origin: Vector2F, - state: &mut S, - cx: &mut ViewContext, - ) -> Result<()>; -} - -struct RenderedElement { - element: E, - phase: ElementRenderPhase, -} - -#[derive(Default)] -enum ElementRenderPhase { - #[default] - Rendered, - LayoutRequested { - layout_id: LayoutId, - frame_state: S, - }, - Painted { - layout: Layout, - frame_state: S, - }, -} - -impl RenderedElement { - fn new(element: E) -> Self { - RenderedElement { - element, - phase: ElementRenderPhase::Rendered, - } - } -} - -impl ElementObject for RenderedElement { - fn layout(&mut self, state: &mut E::State, cx: &mut ViewContext) -> Result { - 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, - ) -> 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(Box>); - -impl AnyElement { - pub fn layout(&mut self, state: &mut S, cx: &mut ViewContext) -> Result { - self.0.layout(state, cx) - } - - pub fn paint( - &mut self, - parent_origin: Vector2F, - state: &mut S, - cx: &mut ViewContext, - ) -> Result<()> { - self.0.paint(parent_origin, state, cx) - } -} - -pub trait IntoAnyElement { - fn into_any(self) -> AnyElement; -} - -impl IntoAnyElement for E { - fn into_any(self) -> AnyElement { - AnyElement(Box::new(RenderedElement::new(self))) - } -} - -impl IntoAnyElement for AnyElement { - fn into_any(self) -> AnyElement { - self - } -} - -#[derive(Clone)] -pub struct View { - state: Handle, - render: Rc) -> AnyElement>, -} - -pub fn view>( - state: Handle, - render: impl 'static + Fn(&mut S, &mut ViewContext) -> E, -) -> View { - View { - state, - render: Rc::new(move |state, cx| render(state, cx).into_any()), - } -} - -impl View { - pub fn into_any(self) -> AnyView { - AnyView { - view: Rc::new(RefCell::new(self)), - parent_state_type: PhantomData, - } - } -} - -impl Element for View { - type State = (); - type FrameState = AnyElement; - - fn layout( - &mut self, - _: &mut Self::State, - cx: &mut ViewContext, - ) -> 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, - ) -> 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)>; - fn paint( - &mut self, - layout: Layout, - element: &mut dyn Any, - cx: &mut WindowContext, - ) -> Result<()>; -} - -impl ViewObject for View { - fn layout(&mut self, cx: &mut WindowContext) -> Result<(LayoutId, Box)> { - 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; - 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::>().unwrap().paint( - layout.bounds.origin(), - state, - cx, - ) - }) - } -} - -pub struct AnyView { - view: Rc>, - parent_state_type: PhantomData, -} - -impl Element for AnyView { - type State = S; - type FrameState = Box; - - fn layout( - &mut self, - _: &mut Self::State, - cx: &mut ViewContext, - ) -> 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, - ) -> Result<()> { - self.view.borrow_mut().paint(layout, element, cx) - } -} - -impl Clone for AnyView { - fn clone(&self) -> Self { - Self { - view: self.view.clone(), - parent_state_type: PhantomData, - } - } -} - -pub struct Div(PhantomData); - -impl Element for Div { - type State = S; - type FrameState = (); - - fn layout( - &mut self, - state: &mut Self::State, - cx: &mut ViewContext, - ) -> Result<(LayoutId, Self::FrameState)> { - todo!() - } - - fn paint( - &mut self, - layout: Layout, - state: &mut Self::State, - frame_state: &mut Self::FrameState, - cx: &mut ViewContext, - ) -> Result<()> { - todo!() - } -} - -impl ParentElement for Div { - fn child(self, child: impl IntoAnyElement) -> Self { - todo!() - } -} - -pub fn div() -> Div { - todo!() -} - -pub struct SharedString(ArcCow<'static, str>); - -impl>> From for SharedString { - fn from(value: T) -> Self { - Self(value.into()) - } -} - -struct Workspace { - left_panel: AnyView, -} - -fn workspace(cx: &mut WindowContext) -> View { - 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, -} - -fn collab_panel(cx: &mut WindowContext) -> View { - 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 { - filter_editor: cx.entity(|cx| Editor::new(cx)), - } - } -} - -fn field(editor: Handle) -> EditorElement { - EditorElement { - editor, - field: true, - placeholder_text: None, - parent_state: PhantomData, - } -} - -struct EditorElement { - editor: Handle, - field: bool, - placeholder_text: Option, - parent_state: PhantomData, -} - -impl EditorElement { - pub fn field(mut self) -> Self { - self.field = true; - self - } - - pub fn placeholder_text(mut self, text: impl Into) -> Self { - self.placeholder_text = Some(text.into()); - self - } -} - -impl Element for EditorElement { - type State = S; - type FrameState = (); - - fn layout( - &mut self, - _: &mut Self::State, - cx: &mut ViewContext, - ) -> 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, - ) -> Result<()> { - self.editor.update(cx, |editor, cx| todo!()) - } -} - -struct Editor {} - -impl Editor { - pub fn new(_: &mut ViewContext) -> Self { - Editor {} - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test() { - let mut cx = AppContext::new(); - cx.open_window(|cx| workspace(cx)); - } -} diff --git a/crates/storybook/src/storybook.rs b/crates/storybook/src/storybook.rs index ac84f934fa..1e96a6ec73 100644 --- a/crates/storybook/src/storybook.rs +++ b/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;