Checkpoint
This commit is contained in:
parent
1c20a8cd31
commit
b9e1ca1385
12 changed files with 1353 additions and 747 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -7384,6 +7384,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"settings",
|
"settings",
|
||||||
"simplelog",
|
"simplelog",
|
||||||
|
"slotmap",
|
||||||
"theme",
|
"theme",
|
||||||
"util",
|
"util",
|
||||||
]
|
]
|
||||||
|
|
|
@ -35,7 +35,7 @@ pub fn derive_refineable(input: TokenStream) -> TokenStream {
|
||||||
let field_visibilities: Vec<_> = fields.iter().map(|f| &f.vis).collect();
|
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();
|
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
|
let type_param_bounds: Vec<_> = wrapped_types
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ty| {
|
.map(|ty| {
|
||||||
|
@ -51,13 +51,14 @@ pub fn derive_refineable(input: TokenStream) -> TokenStream {
|
||||||
lifetimes: None,
|
lifetimes: None,
|
||||||
path: parse_quote!(Clone),
|
path: parse_quote!(Clone),
|
||||||
}));
|
}));
|
||||||
punctuated.push_punct(syn::token::Add::default());
|
|
||||||
punctuated.push_value(TypeParamBound::Trait(TraitBound {
|
// punctuated.push_punct(syn::token::Add::default());
|
||||||
paren_token: None,
|
// punctuated.push_value(TypeParamBound::Trait(TraitBound {
|
||||||
modifier: syn::TraitBoundModifier::None,
|
// paren_token: None,
|
||||||
lifetimes: None,
|
// modifier: syn::TraitBoundModifier::None,
|
||||||
path: parse_quote!(Default),
|
// lifetimes: None,
|
||||||
}));
|
// path: parse_quote!(Default),
|
||||||
|
// }));
|
||||||
punctuated
|
punctuated
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -161,7 +162,7 @@ pub fn derive_refineable(input: TokenStream) -> TokenStream {
|
||||||
};
|
};
|
||||||
|
|
||||||
let gen = quote! {
|
let gen = quote! {
|
||||||
#[derive(Default, Clone)]
|
#[derive(Clone)]
|
||||||
pub struct #refinement_ident #impl_generics {
|
pub struct #refinement_ident #impl_generics {
|
||||||
#( #field_visibilities #field_names: #wrapped_types ),*
|
#( #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
|
impl #impl_generics #refinement_ident #ty_generics
|
||||||
#where_clause
|
#where_clause
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,6 +18,7 @@ rust-embed.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
simplelog = "0.9"
|
simplelog = "0.9"
|
||||||
|
slotmap = "1.0.6"
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
|
|
||||||
|
|
167
crates/storybook/src/gpui3/app.rs
Normal file
167
crates/storybook/src/gpui3/app.rs
Normal file
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
276
crates/storybook/src/gpui3/element.rs
Normal file
276
crates/storybook/src/gpui3/element.rs
Normal file
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
303
crates/storybook/src/gpui3/geometry.rs
Normal file
303
crates/storybook/src/gpui3/geometry.rs
Normal file
|
@ -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())
|
||||||
|
}
|
||||||
|
}
|
191
crates/storybook/src/gpui3/mod.rs
Normal file
191
crates/storybook/src/gpui3/mod.rs
Normal file
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
1
crates/storybook/src/gpui3/style.rs
Normal file
1
crates/storybook/src/gpui3/style.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub struct Style;
|
177
crates/storybook/src/gpui3/taffy.rs
Normal file
177
crates/storybook/src/gpui3/taffy.rs
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
215
crates/storybook/src/gpui3/window.rs
Normal file
215
crates/storybook/src/gpui3/window.rs
Normal file
|
@ -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 collab_panel;
|
||||||
mod components;
|
mod components;
|
||||||
mod element_ext;
|
mod element_ext;
|
||||||
mod sketch;
|
mod gpui3;
|
||||||
mod theme;
|
mod theme;
|
||||||
mod workspace;
|
mod workspace;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue