Cargo.lock 🔗
@@ -7384,6 +7384,7 @@ dependencies = [
"serde",
"settings",
"simplelog",
+ "slotmap",
"theme",
"util",
]
Nathan Sobo created
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(-)
@@ -7384,6 +7384,7 @@ dependencies = [
"serde",
"settings",
"simplelog",
+ "slotmap",
"theme",
"util",
]
@@ -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
{
@@ -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" }
@@ -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,
+ }
+ }
+}
@@ -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,
+ }
+ }
+}
@@ -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())
+ }
+}
@@ -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));
+ }
+}
@@ -0,0 +1 @@
+pub struct Style;
@@ -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)
+ }
+}
@@ -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>;
+}
@@ -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));
- }
-}
@@ -12,7 +12,7 @@ use simplelog::SimpleLogger;
mod collab_panel;
mod components;
mod element_ext;
-mod sketch;
+mod gpui3;
mod theme;
mod workspace;