WIP

Antonio Scandurra created

Change summary

crates/gpui3/src/app.rs                 |   6 
crates/gpui3/src/element.rs             |  14 +-
crates/gpui3/src/elements/clickable.rs  |  10 +
crates/gpui3/src/elements/div.rs        |  20 ++
crates/gpui3/src/elements/group.rs      |   8 +
crates/gpui3/src/elements/hoverable.rs  |  15 ++
crates/gpui3/src/elements/identified.rs |  10 +
crates/gpui3/src/elements/img.rs        |  12 +
crates/gpui3/src/elements/pressable.rs  |  15 ++
crates/gpui3/src/elements/svg.rs        |  13 +
crates/gpui3/src/elements/text.rs       |   6 
crates/gpui3/src/view.rs                | 164 ++++++++++++++++++++++----
crates/gpui3/src/window.rs              |   4 
crates/storybook2/src/collab_panel.rs   |   2 
crates/storybook2/src/workspace.rs      |  10 
crates/ui2/src/components/workspace.rs  |   8 
16 files changed, 255 insertions(+), 62 deletions(-)

Detailed changes

crates/gpui3/src/app.rs 🔗

@@ -9,8 +9,8 @@ use refineable::Refineable;
 
 use crate::{
     current_platform, image_cache::ImageCache, AssetSource, Context, DisplayId, Executor, LayoutId,
-    MainThread, MainThreadOnly, Platform, RootView, SubscriberSet, SvgRenderer, Task, TextStyle,
-    TextStyleRefinement, TextSystem, Window, WindowContext, WindowHandle, WindowId,
+    MainThread, MainThreadOnly, Platform, SubscriberSet, SvgRenderer, Task, TextStyle,
+    TextStyleRefinement, TextSystem, View, Window, WindowContext, WindowHandle, WindowId,
 };
 use anyhow::{anyhow, Result};
 use collections::{HashMap, HashSet, VecDeque};
@@ -404,7 +404,7 @@ impl MainThread<AppContext> {
     pub fn open_window<S: 'static + Send + Sync>(
         &mut self,
         options: crate::WindowOptions,
-        build_root_view: impl FnOnce(&mut WindowContext) -> RootView<S> + Send + 'static,
+        build_root_view: impl FnOnce(&mut WindowContext) -> View<S> + Send + 'static,
     ) -> WindowHandle<S> {
         self.update(|cx| {
             let id = cx.windows.insert(None);

crates/gpui3/src/element.rs 🔗

@@ -7,7 +7,7 @@ use crate::{
 use derive_more::{Deref, DerefMut};
 pub(crate) use smallvec::SmallVec;
 
-pub trait Element: 'static + Send + Sync {
+pub trait Element: 'static + Send + Sync + IntoAnyElement<Self::ViewState> {
     type ViewState: 'static + Send + Sync;
     type ElementState: 'static + Send + Sync;
 
@@ -227,6 +227,12 @@ where
 
 pub struct AnyElement<S>(Box<dyn ElementObject<S>>);
 
+impl<S: 'static + Send + Sync> AnyElement<S> {
+    pub fn new<E: Element<ViewState = S>>(element: E) -> Self {
+        AnyElement(Box::new(RenderedElement::new(element)))
+    }
+}
+
 impl<S: 'static + Send + Sync> AnyElement<S> {
     pub fn layout(&mut self, state: &mut S, cx: &mut ViewContext<S>) -> LayoutId {
         self.0.layout(state, cx)
@@ -241,12 +247,6 @@ pub trait IntoAnyElement<S> {
     fn into_any(self) -> AnyElement<S>;
 }
 
-impl<E: Element> IntoAnyElement<E::ViewState> for E {
-    fn into_any(self) -> AnyElement<E::ViewState> {
-        AnyElement(Box::new(RenderedElement::new(self)))
-    }
-}
-
 impl<S> IntoAnyElement<S> for AnyElement<S> {
     fn into_any(self) -> AnyElement<S> {
         self

crates/gpui3/src/elements/clickable.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{
-    AnyElement, Bounds, DispatchPhase, Element, IdentifiedElement, Interactive, MouseDownEvent,
-    MouseEventListeners, MouseUpEvent, ParentElement, Pixels, Styled, ViewContext,
+    AnyElement, Bounds, DispatchPhase, Element, IdentifiedElement, Interactive, IntoAnyElement,
+    MouseDownEvent, MouseEventListeners, MouseUpEvent, ParentElement, Pixels, Styled, ViewContext,
 };
 use parking_lot::Mutex;
 use refineable::Cascade;
@@ -51,6 +51,12 @@ where
     }
 }
 
+impl<E: IdentifiedElement> IntoAnyElement<E::ViewState> for Clickable<E> {
+    fn into_any(self) -> AnyElement<E::ViewState> {
+        AnyElement::new(self)
+    }
+}
+
 impl<E> Element for Clickable<E>
 where
     E: IdentifiedElement,

crates/gpui3/src/elements/div.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{
     AnyElement, BorrowWindow, Bounds, Cascade, Element, ElementId, IdentifiedElement, Interactive,
-    LayoutId, MouseEventListeners, Overflow, ParentElement, Pixels, Point, Refineable, Style,
-    Styled, ViewContext,
+    IntoAnyElement, LayoutId, MouseEventListeners, Overflow, ParentElement, Pixels, Point,
+    Refineable, Style, Styled, ViewContext,
 };
 use parking_lot::Mutex;
 use smallvec::SmallVec;
@@ -29,7 +29,21 @@ pub fn div<S>() -> Div<S> {
     }
 }
 
-impl<S: 'static + Send + Sync, Marker: 'static + Send + Sync> Element for Div<S, Marker> {
+impl<S, Marker> IntoAnyElement<S> for Div<S, Marker>
+where
+    S: 'static + Send + Sync,
+    Marker: 'static + Send + Sync,
+{
+    fn into_any(self) -> AnyElement<S> {
+        AnyElement::new(self)
+    }
+}
+
+impl<S, Marker> Element for Div<S, Marker>
+where
+    S: 'static + Send + Sync,
+    Marker: 'static + Send + Sync,
+{
     type ViewState = S;
     type ElementState = ();
 

crates/gpui3/src/elements/group.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{
     AnyElement, AppContext, Bounds, Element, ElementId, IdentifiedElement, Interactive,
-    MouseEventListeners, ParentElement, Pixels, SharedString, Styled, ViewContext,
+    IntoAnyElement, MouseEventListeners, ParentElement, Pixels, SharedString, Styled, ViewContext,
 };
 use collections::HashMap;
 use refineable::Cascade;
@@ -27,6 +27,12 @@ impl<E> Group<E> {
     }
 }
 
+impl<E: Element> IntoAnyElement<E::ViewState> for Group<E> {
+    fn into_any(self) -> AnyElement<E::ViewState> {
+        AnyElement::new(self)
+    }
+}
+
 impl<E: Element> Element for Group<E> {
     type ViewState = E::ViewState;
     type ElementState = E::ElementState;

crates/gpui3/src/elements/hoverable.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{
     group_bounds, AnyElement, Bounds, DispatchPhase, Element, ElementId, IdentifiedElement,
-    Interactive, MouseEventListeners, MouseMoveEvent, ParentElement, Pixels, SharedString, Styled,
-    ViewContext,
+    Interactive, IntoAnyElement, MouseEventListeners, MouseMoveEvent, ParentElement, Pixels,
+    SharedString, Styled, ViewContext,
 };
 use refineable::{Cascade, CascadeSlot, Refineable};
 use smallvec::SmallVec;
@@ -51,6 +51,17 @@ impl<S: 'static + Send + Sync, E: Interactive<S> + Styled> Interactive<S> for Ho
     }
 }
 
+impl<E> IntoAnyElement<E::ViewState> for Hoverable<E>
+where
+    E: Element + Styled,
+    <E as Styled>::Style: 'static + Refineable + Send + Sync + Default,
+    <<E as Styled>::Style as Refineable>::Refinement: 'static + Refineable + Send + Sync + Default,
+{
+    fn into_any(self) -> AnyElement<E::ViewState> {
+        AnyElement::new(self)
+    }
+}
+
 impl<E> Element for Hoverable<E>
 where
     E: Element + Styled,

crates/gpui3/src/elements/identified.rs 🔗

@@ -2,8 +2,8 @@ use refineable::{Cascade, Refineable};
 use smallvec::SmallVec;
 
 use crate::{
-    AnyElement, BorrowWindow, Bounds, Element, ElementId, IdentifiedElement, LayoutId,
-    ParentElement, Styled, ViewContext,
+    AnyElement, BorrowWindow, Bounds, Element, ElementId, IdentifiedElement, IntoAnyElement,
+    LayoutId, ParentElement, Styled, ViewContext,
 };
 
 pub struct Identified<E> {
@@ -11,6 +11,12 @@ pub struct Identified<E> {
     pub(crate) id: ElementId,
 }
 
+impl<E: Element> IntoAnyElement<E::ViewState> for Identified<E> {
+    fn into_any(self) -> AnyElement<E::ViewState> {
+        AnyElement::new(self)
+    }
+}
+
 impl<E: Element> Element for Identified<E> {
     type ViewState = E::ViewState;
     type ElementState = E::ElementState;

crates/gpui3/src/elements/img.rs 🔗

@@ -1,5 +1,6 @@
 use crate::{
-    BorrowWindow, Bounds, Element, LayoutId, Pixels, SharedString, Style, Styled, ViewContext,
+    AnyElement, BorrowWindow, Bounds, Element, IntoAnyElement, LayoutId, Pixels, SharedString,
+    Style, Styled, ViewContext,
 };
 use futures::FutureExt;
 use refineable::Cascade;
@@ -34,6 +35,15 @@ impl<S> Img<S> {
     }
 }
 
+impl<S> IntoAnyElement<S> for Img<S>
+where
+    S: 'static + Send + Sync,
+{
+    fn into_any(self) -> AnyElement<S> {
+        AnyElement::new(self)
+    }
+}
+
 impl<S: Send + Sync + 'static> Element for Img<S> {
     type ViewState = S;
     type ElementState = ();

crates/gpui3/src/elements/pressable.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{
     group_bounds, AnyElement, Bounds, DispatchPhase, Element, IdentifiedElement, Interactive,
-    MouseDownEvent, MouseEventListeners, MouseUpEvent, ParentElement, Pixels, SharedString, Styled,
-    ViewContext,
+    IntoAnyElement, MouseDownEvent, MouseEventListeners, MouseUpEvent, ParentElement, Pixels,
+    SharedString, Styled, ViewContext,
 };
 use refineable::{Cascade, CascadeSlot, Refineable};
 use smallvec::SmallVec;
@@ -54,6 +54,17 @@ impl<S: 'static + Send + Sync, E: Interactive<S> + Styled> Interactive<S> for Pr
     }
 }
 
+impl<E> IntoAnyElement<E::ViewState> for Pressable<E>
+where
+    E: Styled + IdentifiedElement,
+    <E as Styled>::Style: 'static + Refineable + Send + Sync + Default,
+    <<E as Styled>::Style as Refineable>::Refinement: 'static + Refineable + Send + Sync + Default,
+{
+    fn into_any(self) -> AnyElement<E::ViewState> {
+        AnyElement::new(self)
+    }
+}
+
 impl<E> Element for Pressable<E>
 where
     E: Styled + IdentifiedElement,

crates/gpui3/src/elements/svg.rs 🔗

@@ -1,4 +1,6 @@
-use crate::{Bounds, Element, LayoutId, Pixels, SharedString, Style, Styled};
+use crate::{
+    AnyElement, Bounds, Element, IntoAnyElement, LayoutId, Pixels, SharedString, Style, Styled,
+};
 use refineable::Cascade;
 use std::marker::PhantomData;
 use util::ResultExt;
@@ -24,6 +26,15 @@ impl<S> Svg<S> {
     }
 }
 
+impl<S> IntoAnyElement<S> for Svg<S>
+where
+    S: 'static + Send + Sync,
+{
+    fn into_any(self) -> AnyElement<S> {
+        AnyElement::new(self)
+    }
+}
+
 impl<S: 'static + Send + Sync> Element for Svg<S> {
     type ViewState = S;
     type ElementState = ();

crates/gpui3/src/elements/text.rs 🔗

@@ -42,6 +42,12 @@ pub struct Text<S> {
     state_type: PhantomData<S>,
 }
 
+impl<S: 'static + Send + Sync> IntoAnyElement<S> for Text<S> {
+    fn into_any(self) -> AnyElement<S> {
+        AnyElement::new(self)
+    }
+}
+
 impl<S: 'static + Send + Sync> Element for Text<S> {
     type ViewState = S;
     type ElementState = Arc<Mutex<Option<TextElementState>>>;

crates/gpui3/src/view.rs 🔗

@@ -1,56 +1,60 @@
 use parking_lot::Mutex;
 
 use crate::{
-    AnyBox, AnyElement, BorrowWindow, Bounds, Element, ElementId, Handle, IdentifiedElement,
-    IntoAnyElement, LayoutId, Pixels, ViewContext, WindowContext,
+    AnyBox, AnyElement, BorrowWindow, Bounds, Element, ElementId, EntityId, Handle,
+    IdentifiedElement, IntoAnyElement, LayoutId, Pixels, ViewContext, WindowContext,
 };
 use std::{any::Any, marker::PhantomData, sync::Arc};
 
-pub struct View<S: Send + Sync, P> {
+pub struct View<S: Send + Sync> {
     state: Handle<S>,
     render: Arc<dyn Fn(&mut S, &mut ViewContext<S>) -> AnyElement<S> + Send + Sync + 'static>,
-    parent_state_type: PhantomData<P>,
 }
 
-impl<S: 'static + Send + Sync, P: 'static + Send + Sync> View<S, P> {
-    pub fn into_any(self) -> AnyView<P> {
+impl<S: 'static + Send + Sync> View<S> {
+    pub fn into_any(self) -> AnyView {
         AnyView {
             view: Arc::new(Mutex::new(self)),
-            parent_state_type: PhantomData,
         }
     }
 }
 
-impl<S: Send + Sync, P> Clone for View<S, P> {
+impl<S: Send + Sync> Clone for View<S> {
     fn clone(&self) -> Self {
         Self {
             state: self.state.clone(),
             render: self.render.clone(),
-            parent_state_type: PhantomData,
         }
     }
 }
 
-pub type RootView<S> = View<S, ()>;
-
-pub fn view<S, P, E>(
+pub fn view<S, E>(
     state: Handle<S>,
     render: impl Fn(&mut S, &mut ViewContext<S>) -> E + Send + Sync + 'static,
-) -> View<S, P>
+) -> View<S>
 where
     S: 'static + Send + Sync,
-    P: 'static,
     E: Element<ViewState = S>,
 {
     View {
         state,
         render: Arc::new(move |state, cx| render(state, cx).into_any()),
-        parent_state_type: PhantomData,
     }
 }
 
-impl<S: 'static + Send + Sync, P: 'static + Send + Sync> Element for View<S, P> {
-    type ViewState = P;
+impl<S: 'static + Send + Sync, ParentViewState: 'static + Send + Sync>
+    IntoAnyElement<ParentViewState> for View<S>
+{
+    fn into_any(self) -> AnyElement<ParentViewState> {
+        AnyElement::new(EraseViewState {
+            view: self,
+            parent_view_state_type: PhantomData,
+        })
+    }
+}
+
+impl<S: 'static + Send + Sync> Element for View<S> {
+    type ViewState = ();
     type ElementState = AnyElement<S>;
 
     fn element_id(&self) -> Option<crate::ElementId> {
@@ -82,14 +86,67 @@ impl<S: 'static + Send + Sync, P: 'static + Send + Sync> Element for View<S, P>
     }
 }
 
-trait ViewObject: Send + 'static {
+struct EraseViewState<ViewState: 'static + Send + Sync, ParentViewState> {
+    view: View<ViewState>,
+    parent_view_state_type: PhantomData<ParentViewState>,
+}
+
+impl<ViewState, ParentViewState> IntoAnyElement<ParentViewState>
+    for EraseViewState<ViewState, ParentViewState>
+where
+    ViewState: 'static + Send + Sync,
+    ParentViewState: 'static + Send + Sync,
+{
+    fn into_any(self) -> AnyElement<ParentViewState> {
+        AnyElement::new(self)
+    }
+}
+
+impl<ViewState, ParentViewState> Element for EraseViewState<ViewState, ParentViewState>
+where
+    ViewState: 'static + Send + Sync,
+    ParentViewState: 'static + Send + Sync,
+{
+    type ViewState = ParentViewState;
+    type ElementState = AnyBox;
+
+    fn element_id(&self) -> Option<crate::ElementId> {
+        Element::element_id(&self.view)
+    }
+
+    fn layout(
+        &mut self,
+        _: &mut Self::ViewState,
+        _: Option<Self::ElementState>,
+        cx: &mut ViewContext<Self::ViewState>,
+    ) -> (LayoutId, Self::ElementState) {
+        ViewObject::layout(&mut self.view, cx)
+    }
+
+    fn paint(
+        &mut self,
+        bounds: Bounds<Pixels>,
+        _: &mut Self::ViewState,
+        element: &mut Self::ElementState,
+        cx: &mut ViewContext<Self::ViewState>,
+    ) {
+        ViewObject::paint(&mut self.view, bounds, element, cx)
+    }
+}
+
+trait ViewObject: 'static + Send + Sync {
+    fn entity_id(&self) -> EntityId;
     fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, AnyBox);
     fn paint(&mut self, bounds: Bounds<Pixels>, element: &mut dyn Any, cx: &mut WindowContext);
 }
 
-impl<S: Send + Sync + 'static, P: Send + Sync + 'static> IdentifiedElement for View<S, P> {}
+impl<S: Send + Sync + 'static> IdentifiedElement for View<S> {}
+
+impl<S: Send + Sync + 'static> ViewObject for View<S> {
+    fn entity_id(&self) -> EntityId {
+        self.state.id
+    }
 
-impl<S: Send + Sync + 'static, P: Send + Sync + 'static> ViewObject for View<S, P> {
     fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, AnyBox) {
         cx.with_element_id(IdentifiedElement::element_id(self), |cx| {
             self.state.update(cx, |state, cx| {
@@ -111,17 +168,28 @@ impl<S: Send + Sync + 'static, P: Send + Sync + 'static> ViewObject for View<S,
     }
 }
 
-pub struct AnyView<S> {
+pub struct AnyView {
     view: Arc<Mutex<dyn ViewObject>>,
-    parent_state_type: PhantomData<S>,
 }
 
-impl<S: 'static + Send + Sync> Element for AnyView<S> {
+impl<ParentViewState> IntoAnyElement<ParentViewState> for AnyView
+where
+    ParentViewState: 'static + Send + Sync,
+{
+    fn into_any(self) -> AnyElement<ParentViewState> {
+        AnyElement::new(EraseAnyViewState {
+            view: self,
+            parent_view_state_type: PhantomData,
+        })
+    }
+}
+
+impl Element for AnyView {
     type ViewState = ();
     type ElementState = AnyBox;
 
     fn element_id(&self) -> Option<crate::ElementId> {
-        None
+        Some(ElementId::View(self.view.lock().entity_id()))
     }
 
     fn layout(
@@ -144,11 +212,55 @@ impl<S: 'static + Send + Sync> Element for AnyView<S> {
     }
 }
 
-impl<S> Clone for AnyView<S> {
+struct EraseAnyViewState<ParentViewState> {
+    view: AnyView,
+    parent_view_state_type: PhantomData<ParentViewState>,
+}
+
+impl<ParentViewState> IntoAnyElement<ParentViewState> for EraseAnyViewState<ParentViewState>
+where
+    ParentViewState: 'static + Send + Sync,
+{
+    fn into_any(self) -> AnyElement<ParentViewState> {
+        AnyElement::new(self)
+    }
+}
+
+impl<ParentViewState> Element for EraseAnyViewState<ParentViewState>
+where
+    ParentViewState: 'static + Send + Sync,
+{
+    type ViewState = ParentViewState;
+    type ElementState = AnyBox;
+
+    fn element_id(&self) -> Option<crate::ElementId> {
+        Element::element_id(&self.view)
+    }
+
+    fn layout(
+        &mut self,
+        _: &mut Self::ViewState,
+        _: Option<Self::ElementState>,
+        cx: &mut ViewContext<Self::ViewState>,
+    ) -> (LayoutId, Self::ElementState) {
+        self.view.view.lock().layout(cx)
+    }
+
+    fn paint(
+        &mut self,
+        bounds: Bounds<Pixels>,
+        _: &mut Self::ViewState,
+        element: &mut Self::ElementState,
+        cx: &mut ViewContext<Self::ViewState>,
+    ) {
+        self.view.view.lock().paint(bounds, element, cx)
+    }
+}
+
+impl Clone for AnyView {
     fn clone(&self) -> Self {
         Self {
             view: self.view.clone(),
-            parent_state_type: PhantomData,
         }
     }
 }

crates/gpui3/src/window.rs 🔗

@@ -50,7 +50,7 @@ pub struct Window {
     rem_size: Pixels,
     content_size: Size<Pixels>,
     layout_engine: TaffyLayoutEngine,
-    pub(crate) root_view: Option<AnyView<()>>,
+    pub(crate) root_view: Option<AnyView>,
     pub(crate) element_id_stack: GlobalElementId,
     prev_element_states: HashMap<GlobalElementId, AnyBox>,
     element_states: HashMap<GlobalElementId, AnyBox>,
@@ -624,7 +624,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         });
 
         fn draw_with_element_state(
-            root_view: &mut AnyView<()>,
+            root_view: &mut AnyView,
             element_state: Option<AnyBox>,
             cx: &mut ViewContext<()>,
         ) -> AnyBox {

crates/storybook2/src/collab_panel.rs 🔗

@@ -8,7 +8,7 @@ pub struct CollabPanel {
     scroll_state: ScrollState,
 }
 
-pub fn collab_panel<S: 'static>(cx: &mut WindowContext) -> View<CollabPanel, S> {
+pub fn collab_panel(cx: &mut WindowContext) -> View<CollabPanel> {
     view(cx.entity(|cx| CollabPanel::new(cx)), CollabPanel::render)
 }
 

crates/storybook2/src/workspace.rs 🔗

@@ -3,17 +3,17 @@ use crate::{
     themes::rose_pine,
 };
 use gpui3::{
-    div, img, svg, view, Context, Element, ParentElement, RootView, StyleHelpers, Styled, View,
-    ViewContext, WindowContext,
+    div, img, svg, view, Context, Element, ParentElement, StyleHelpers, Styled, View, ViewContext,
+    WindowContext,
 };
 use ui::{theme, themed};
 
 pub struct Workspace {
-    left_panel: View<CollabPanel, Self>,
-    right_panel: View<CollabPanel, Self>,
+    left_panel: View<CollabPanel>,
+    right_panel: View<CollabPanel>,
 }
 
-pub fn workspace(cx: &mut WindowContext) -> RootView<Workspace> {
+pub fn workspace(cx: &mut WindowContext) -> View<Workspace> {
     view(cx.entity(|cx| Workspace::new(cx)), Workspace::render)
 }
 

crates/ui2/src/components/workspace.rs 🔗

@@ -116,7 +116,7 @@ pub struct Workspace {
     bottom_panel_scroll_state: ScrollState,
 }
 
-fn workspace<P: 'static>(cx: &mut WindowContext) -> View<Workspace, P> {
+fn workspace<P: 'static>(cx: &mut WindowContext) -> View<Workspace> {
     view(cx.entity(|cx| Workspace::new()), Workspace::render)
 }
 
@@ -377,11 +377,11 @@ mod stories {
     //     state_type: PhantomData<S>,
     // }
 
-    pub struct WorkspaceStory<P> {
-        workspace: View<Workspace, P>,
+    pub struct WorkspaceStory {
+        workspace: View<Workspace>,
     }
 
-    pub fn workspace_story<P: 'static + Send + Sync>(cx: &mut WindowContext) -> View<WorkspaceStory<P>, P> {
+    pub fn workspace_story(cx: &mut WindowContext) -> View<WorkspaceStory> {
         todo!()
         // let workspace = workspace::<P>(cx);
         // view(