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",
|
||||
"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" }
|
||||
|
||||
|
|
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 components;
|
||||
mod element_ext;
|
||||
mod sketch;
|
||||
mod gpui3;
|
||||
mod theme;
|
||||
mod workspace;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue