Simplify `AnyView`

Antonio Scandurra and Nathan Sobo created

Co-Authored-By: Nathan Sobo <nathan@zed.dev>

Change summary

crates/gpui2/src/app.rs                 |   2 
crates/gpui2/src/interactive.rs         |   2 
crates/gpui2/src/view.rs                | 168 +++++++++++---------------
crates/gpui2/src/window.rs              |   2 
crates/storybook2/src/story_selector.rs |  76 ++++++------
5 files changed, 112 insertions(+), 138 deletions(-)

Detailed changes

crates/gpui2/src/app.rs 🔗

@@ -829,7 +829,7 @@ impl MainThread<AppContext> {
             let handle = WindowHandle::new(id);
             let mut window = Window::new(handle.into(), options, cx);
             let root_view = build_root_view(&mut WindowContext::mutable(cx, &mut window));
-            window.root_view.replace(root_view.into_any());
+            window.root_view.replace(root_view.into());
             cx.windows.get_mut(id).unwrap().replace(window);
             handle
         })

crates/gpui2/src/interactive.rs 🔗

@@ -328,7 +328,7 @@ pub trait StatefulInteractive<V: 'static>: StatelessInteractive<V> {
         );
         self.stateful_interaction().drag_listener =
             Some(Box::new(move |view_state, cursor_offset, cx| AnyDrag {
-                view: listener(view_state, cx).into_any(),
+                view: listener(view_state, cx).into(),
                 cursor_offset,
             }));
         self

crates/gpui2/src/view.rs 🔗

@@ -4,7 +4,7 @@ use crate::{
     ViewContext, VisualContext, WeakModel, WindowContext,
 };
 use anyhow::{Context, Result};
-use std::{any::TypeId, marker::PhantomData, sync::Arc};
+use std::{any::TypeId, marker::PhantomData};
 
 pub trait Render: 'static + Sized {
     type Element: Element<Self> + 'static + Send;
@@ -18,12 +18,6 @@ pub struct View<V> {
 
 impl<V> Sealed for View<V> {}
 
-impl<V: Render> View<V> {
-    pub fn into_any(self) -> AnyView {
-        AnyView(Arc::new(self))
-    }
-}
-
 impl<V: 'static> Entity<V> for View<V> {
     type Weak = WeakView<V>;
 
@@ -265,94 +259,110 @@ where
     }
 }
 
-#[derive(Clone)]
-pub struct AnyView(Arc<dyn ViewObject>);
+#[derive(Clone, Debug)]
+pub struct AnyView {
+    model: AnyModel,
+    initialize: fn(&Self, &mut WindowContext) -> AnyBox,
+    layout: fn(&Self, &mut AnyBox, &mut WindowContext) -> LayoutId,
+    paint: fn(&Self, &mut AnyBox, &mut WindowContext),
+}
 
 impl AnyView {
-    pub fn downcast<V: 'static + Send>(self) -> Result<View<V>, AnyView> {
-        self.0
-            .model()
-            .downcast()
-            .map(|model| View { model })
-            .map_err(|_| self)
+    pub fn downcast<T: 'static>(self) -> Result<View<T>, Self> {
+        match self.model.downcast() {
+            Ok(model) => Ok(View { model }),
+            Err(model) => Err(Self {
+                model,
+                initialize: self.initialize,
+                layout: self.layout,
+                paint: self.paint,
+            }),
+        }
     }
 
     pub(crate) fn entity_type(&self) -> TypeId {
-        self.0.entity_type()
+        self.model.entity_type
     }
 
     pub(crate) fn draw(&self, available_space: Size<AvailableSpace>, cx: &mut WindowContext) {
-        let mut rendered_element = self.0.initialize(cx);
-        let layout_id = self.0.layout(&mut rendered_element, cx);
+        let mut rendered_element = (self.initialize)(self, cx);
+        let layout_id = (self.layout)(self, &mut rendered_element, cx);
         cx.window
             .layout_engine
             .compute_layout(layout_id, available_space);
-        let bounds = cx.window.layout_engine.layout_bounds(layout_id);
-        self.0.paint(bounds, &mut rendered_element, cx);
+        (self.paint)(self, &mut rendered_element, cx);
     }
 }
 
-impl<ParentV: 'static> Component<ParentV> for AnyView {
-    fn render(self) -> AnyElement<ParentV> {
-        AnyElement::new(EraseAnyViewState {
-            view: self,
-            parent_view_state_type: PhantomData,
-        })
+impl<V: 'static> Component<V> for AnyView {
+    fn render(self) -> AnyElement<V> {
+        AnyElement::new(self)
+    }
+}
+
+impl<V: Render> From<View<V>> for AnyView {
+    fn from(value: View<V>) -> Self {
+        AnyView {
+            model: value.model.into_any(),
+            initialize: |view, cx| {
+                cx.with_element_id(view.model.entity_id, |_, cx| {
+                    let view = view.clone().downcast::<V>().unwrap();
+                    Box::new(AnyElement::new(
+                        view.update(cx, |view, cx| Render::render(view, cx)),
+                    ))
+                })
+            },
+            layout: |view, element, cx| {
+                cx.with_element_id(view.model.entity_id, |_, cx| {
+                    let view = view.clone().downcast::<V>().unwrap();
+                    let element = element.downcast_mut::<AnyElement<V>>().unwrap();
+                    view.update(cx, |view, cx| element.layout(view, cx))
+                })
+            },
+            paint: |view, element, cx| {
+                cx.with_element_id(view.model.entity_id, |_, cx| {
+                    let view = view.clone().downcast::<V>().unwrap();
+                    let element = element.downcast_mut::<AnyElement<V>>().unwrap();
+                    view.update(cx, |view, cx| element.paint(view, cx))
+                })
+            },
+        }
     }
 }
 
-impl Element<()> for AnyView {
+impl<ParentViewState: 'static> Element<ParentViewState> for AnyView {
     type ElementState = AnyBox;
 
-    fn id(&self) -> Option<crate::ElementId> {
-        Some(ElementId::View(self.0.entity_id()))
+    fn id(&self) -> Option<ElementId> {
+        Some(self.model.entity_id.into())
     }
 
     fn initialize(
         &mut self,
-        _: &mut (),
-        _: Option<Self::ElementState>,
-        cx: &mut ViewContext<()>,
+        _view_state: &mut ParentViewState,
+        _element_state: Option<Self::ElementState>,
+        cx: &mut ViewContext<ParentViewState>,
     ) -> Self::ElementState {
-        self.0.initialize(cx)
+        (self.initialize)(self, cx)
     }
 
     fn layout(
         &mut self,
-        _: &mut (),
-        element: &mut Self::ElementState,
-        cx: &mut ViewContext<()>,
+        _view_state: &mut ParentViewState,
+        rendered_element: &mut Self::ElementState,
+        cx: &mut ViewContext<ParentViewState>,
     ) -> LayoutId {
-        self.0.layout(element, cx)
+        (self.layout)(self, rendered_element, cx)
     }
 
     fn paint(
         &mut self,
-        bounds: Bounds<Pixels>,
-        _: &mut (),
-        element: &mut AnyBox,
-        cx: &mut ViewContext<()>,
+        _bounds: Bounds<Pixels>,
+        _view_state: &mut ParentViewState,
+        rendered_element: &mut Self::ElementState,
+        cx: &mut ViewContext<ParentViewState>,
     ) {
-        self.0.paint(bounds, element, cx)
-    }
-}
-
-impl std::fmt::Debug for AnyView {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        self.0.debug(f)
-    }
-}
-
-struct EraseAnyViewState<ParentViewState> {
-    view: AnyView,
-    parent_view_state_type: PhantomData<ParentViewState>,
-}
-
-unsafe impl<ParentV> Send for EraseAnyViewState<ParentV> {}
-
-impl<ParentV: 'static> Component<ParentV> for EraseAnyViewState<ParentV> {
-    fn render(self) -> AnyElement<ParentV> {
-        AnyElement::new(self)
+        (self.paint)(self, rendered_element, cx)
     }
 }
 
@@ -367,39 +377,3 @@ where
         (self)(cx)
     }
 }
-
-impl<ParentV: 'static> Element<ParentV> for EraseAnyViewState<ParentV> {
-    type ElementState = AnyBox;
-
-    fn id(&self) -> Option<crate::ElementId> {
-        Element::id(&self.view)
-    }
-
-    fn initialize(
-        &mut self,
-        _: &mut ParentV,
-        _: Option<Self::ElementState>,
-        cx: &mut ViewContext<ParentV>,
-    ) -> Self::ElementState {
-        self.view.0.initialize(cx)
-    }
-
-    fn layout(
-        &mut self,
-        _: &mut ParentV,
-        element: &mut Self::ElementState,
-        cx: &mut ViewContext<ParentV>,
-    ) -> LayoutId {
-        self.view.0.layout(element, cx)
-    }
-
-    fn paint(
-        &mut self,
-        bounds: Bounds<Pixels>,
-        _: &mut ParentV,
-        element: &mut Self::ElementState,
-        cx: &mut ViewContext<ParentV>,
-    ) {
-        self.view.0.paint(bounds, element, cx)
-    }
-}

crates/gpui2/src/window.rs 🔗

@@ -967,7 +967,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
                     self.window.mouse_position = position;
                     if self.active_drag.is_none() {
                         self.active_drag = Some(AnyDrag {
-                            view: self.build_view(|_| files).into_any(),
+                            view: self.build_view(|_| files).into(),
                             cursor_offset: position,
                         });
                     }

crates/storybook2/src/story_selector.rs 🔗

@@ -28,17 +28,17 @@ pub enum ElementStory {
 impl ElementStory {
     pub fn story(&self, cx: &mut WindowContext) -> AnyView {
         match self {
-            Self::Colors => cx.build_view(|_| ColorsStory).into_any(),
-            Self::Avatar => cx.build_view(|_| AvatarStory).into_any(),
-            Self::Button => cx.build_view(|_| ButtonStory).into_any(),
-            Self::Details => cx.build_view(|_| DetailsStory).into_any(),
-            Self::Focus => FocusStory::view(cx).into_any(),
-            Self::Icon => cx.build_view(|_| IconStory).into_any(),
-            Self::Input => cx.build_view(|_| InputStory).into_any(),
-            Self::Label => cx.build_view(|_| LabelStory).into_any(),
-            Self::Scroll => ScrollStory::view(cx).into_any(),
-            Self::Text => TextStory::view(cx).into_any(),
-            Self::ZIndex => cx.build_view(|_| ZIndexStory).into_any(),
+            Self::Colors => cx.build_view(|_| ColorsStory).into(),
+            Self::Avatar => cx.build_view(|_| AvatarStory).into(),
+            Self::Button => cx.build_view(|_| ButtonStory).into(),
+            Self::Details => cx.build_view(|_| DetailsStory).into(),
+            Self::Focus => FocusStory::view(cx).into(),
+            Self::Icon => cx.build_view(|_| IconStory).into(),
+            Self::Input => cx.build_view(|_| InputStory).into(),
+            Self::Label => cx.build_view(|_| LabelStory).into(),
+            Self::Scroll => ScrollStory::view(cx).into(),
+            Self::Text => TextStory::view(cx).into(),
+            Self::ZIndex => cx.build_view(|_| ZIndexStory).into(),
         }
     }
 }
@@ -77,32 +77,32 @@ pub enum ComponentStory {
 impl ComponentStory {
     pub fn story(&self, cx: &mut WindowContext) -> AnyView {
         match self {
-            Self::AssistantPanel => cx.build_view(|_| ui::AssistantPanelStory).into_any(),
-            Self::Buffer => cx.build_view(|_| ui::BufferStory).into_any(),
-            Self::Breadcrumb => cx.build_view(|_| ui::BreadcrumbStory).into_any(),
-            Self::ChatPanel => cx.build_view(|_| ui::ChatPanelStory).into_any(),
-            Self::CollabPanel => cx.build_view(|_| ui::CollabPanelStory).into_any(),
-            Self::CommandPalette => cx.build_view(|_| ui::CommandPaletteStory).into_any(),
-            Self::ContextMenu => cx.build_view(|_| ui::ContextMenuStory).into_any(),
-            Self::Facepile => cx.build_view(|_| ui::FacepileStory).into_any(),
-            Self::Keybinding => cx.build_view(|_| ui::KeybindingStory).into_any(),
-            Self::LanguageSelector => cx.build_view(|_| ui::LanguageSelectorStory).into_any(),
-            Self::MultiBuffer => cx.build_view(|_| ui::MultiBufferStory).into_any(),
-            Self::NotificationsPanel => cx.build_view(|cx| ui::NotificationsPanelStory).into_any(),
-            Self::Palette => cx.build_view(|cx| ui::PaletteStory).into_any(),
-            Self::Panel => cx.build_view(|cx| ui::PanelStory).into_any(),
-            Self::ProjectPanel => cx.build_view(|_| ui::ProjectPanelStory).into_any(),
-            Self::RecentProjects => cx.build_view(|_| ui::RecentProjectsStory).into_any(),
-            Self::Tab => cx.build_view(|_| ui::TabStory).into_any(),
-            Self::TabBar => cx.build_view(|_| ui::TabBarStory).into_any(),
-            Self::Terminal => cx.build_view(|_| ui::TerminalStory).into_any(),
-            Self::ThemeSelector => cx.build_view(|_| ui::ThemeSelectorStory).into_any(),
-            Self::Toast => cx.build_view(|_| ui::ToastStory).into_any(),
-            Self::Toolbar => cx.build_view(|_| ui::ToolbarStory).into_any(),
-            Self::TrafficLights => cx.build_view(|_| ui::TrafficLightsStory).into_any(),
-            Self::Copilot => cx.build_view(|_| ui::CopilotModalStory).into_any(),
-            Self::TitleBar => ui::TitleBarStory::view(cx).into_any(),
-            Self::Workspace => ui::WorkspaceStory::view(cx).into_any(),
+            Self::AssistantPanel => cx.build_view(|_| ui::AssistantPanelStory).into(),
+            Self::Buffer => cx.build_view(|_| ui::BufferStory).into(),
+            Self::Breadcrumb => cx.build_view(|_| ui::BreadcrumbStory).into(),
+            Self::ChatPanel => cx.build_view(|_| ui::ChatPanelStory).into(),
+            Self::CollabPanel => cx.build_view(|_| ui::CollabPanelStory).into(),
+            Self::CommandPalette => cx.build_view(|_| ui::CommandPaletteStory).into(),
+            Self::ContextMenu => cx.build_view(|_| ui::ContextMenuStory).into(),
+            Self::Facepile => cx.build_view(|_| ui::FacepileStory).into(),
+            Self::Keybinding => cx.build_view(|_| ui::KeybindingStory).into(),
+            Self::LanguageSelector => cx.build_view(|_| ui::LanguageSelectorStory).into(),
+            Self::MultiBuffer => cx.build_view(|_| ui::MultiBufferStory).into(),
+            Self::NotificationsPanel => cx.build_view(|cx| ui::NotificationsPanelStory).into(),
+            Self::Palette => cx.build_view(|cx| ui::PaletteStory).into(),
+            Self::Panel => cx.build_view(|cx| ui::PanelStory).into(),
+            Self::ProjectPanel => cx.build_view(|_| ui::ProjectPanelStory).into(),
+            Self::RecentProjects => cx.build_view(|_| ui::RecentProjectsStory).into(),
+            Self::Tab => cx.build_view(|_| ui::TabStory).into(),
+            Self::TabBar => cx.build_view(|_| ui::TabBarStory).into(),
+            Self::Terminal => cx.build_view(|_| ui::TerminalStory).into(),
+            Self::ThemeSelector => cx.build_view(|_| ui::ThemeSelectorStory).into(),
+            Self::Toast => cx.build_view(|_| ui::ToastStory).into(),
+            Self::Toolbar => cx.build_view(|_| ui::ToolbarStory).into(),
+            Self::TrafficLights => cx.build_view(|_| ui::TrafficLightsStory).into(),
+            Self::Copilot => cx.build_view(|_| ui::CopilotModalStory).into(),
+            Self::TitleBar => ui::TitleBarStory::view(cx).into(),
+            Self::Workspace => ui::WorkspaceStory::view(cx).into(),
         }
     }
 }
@@ -149,7 +149,7 @@ impl StorySelector {
         match self {
             Self::Element(element_story) => element_story.story(cx),
             Self::Component(component_story) => component_story.story(cx),
-            Self::KitchenSink => KitchenSinkStory::view(cx).into_any(),
+            Self::KitchenSink => KitchenSinkStory::view(cx).into(),
         }
     }
 }