Merge pull request #1749 from zed-industries/child-view-panic

Antonio Scandurra created

Prevent `ChildView` from retaining an otherwise dropped view

Change summary

crates/chat_panel/src/chat_panel.rs            |  4 
crates/collab/src/integration_tests.rs         |  2 
crates/collab_ui/src/collab_titlebar_item.rs   |  2 
crates/collab_ui/src/contact_finder.rs         |  4 
crates/collab_ui/src/contact_list.rs           |  2 
crates/collab_ui/src/contacts_popover.rs       |  4 
crates/command_palette/src/command_palette.rs  |  8 
crates/diagnostics/src/diagnostics.rs          |  2 
crates/editor/src/editor.rs                    |  4 
crates/file_finder/src/file_finder.rs          |  4 
crates/go_to_line/src/go_to_line.rs            |  2 
crates/gpui/src/app.rs                         | 81 ++++++++++++++++++++
crates/gpui/src/presenter.rs                   | 81 +++++++++++++++----
crates/outline/src/outline.rs                  |  4 
crates/picker/src/picker.rs                    |  2 
crates/project_panel/src/project_panel.rs      |  6 
crates/project_symbols/src/project_symbols.rs  |  4 
crates/search/src/buffer_search.rs             |  2 
crates/search/src/project_search.rs            |  6 
crates/terminal/src/terminal_container_view.rs |  4 
crates/terminal/src/terminal_view.rs           |  2 
crates/theme_selector/src/theme_selector.rs    |  4 
crates/workspace/src/dock.rs                   |  6 
crates/workspace/src/pane.rs                   |  6 
crates/workspace/src/pane_group.rs             |  7 +
crates/workspace/src/sidebar.rs                |  2 
crates/workspace/src/status_bar.rs             |  4 
crates/workspace/src/toolbar.rs                |  6 
crates/workspace/src/workspace.rs              | 26 +++--
29 files changed, 215 insertions(+), 76 deletions(-)

Detailed changes

crates/chat_panel/src/chat_panel.rs 🔗

@@ -200,7 +200,7 @@ impl ChatPanel {
         let theme = &cx.global::<Settings>().theme;
         Flex::column()
             .with_child(
-                Container::new(ChildView::new(&self.channel_select).boxed())
+                Container::new(ChildView::new(&self.channel_select, cx).boxed())
                     .with_style(theme.chat_panel.channel_select.container)
                     .boxed(),
             )
@@ -265,7 +265,7 @@ impl ChatPanel {
 
     fn render_input_box(&self, cx: &AppContext) -> ElementBox {
         let theme = &cx.global::<Settings>().theme;
-        Container::new(ChildView::new(&self.input_editor).boxed())
+        Container::new(ChildView::new(&self.input_editor, cx).boxed())
             .with_style(theme.chat_panel.input_editor.container)
             .boxed()
     }

crates/collab/src/integration_tests.rs 🔗

@@ -25,7 +25,7 @@ use gpui::{
 };
 use language::{
     range_to_lsp, tree_sitter_rust, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language,
-    LanguageConfig, LanguageRegistry, OffsetRangeExt, Rope, Point,
+    LanguageConfig, LanguageRegistry, OffsetRangeExt, Point, Rope,
 };
 use lsp::{self, FakeLanguageServer};
 use parking_lot::Mutex;

crates/collab_ui/src/collab_titlebar_item.rs 🔗

@@ -223,7 +223,7 @@ impl CollabTitlebarItem {
             .with_children(badge)
             .with_children(self.contacts_popover.as_ref().map(|popover| {
                 Overlay::new(
-                    ChildView::new(popover)
+                    ChildView::new(popover, cx)
                         .contained()
                         .with_margin_top(titlebar.height)
                         .with_margin_left(titlebar.toggle_contacts_button.default.button_width)

crates/collab_ui/src/contact_finder.rs 🔗

@@ -32,8 +32,8 @@ impl View for ContactFinder {
         "ContactFinder"
     }
 
-    fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox {
-        ChildView::new(self.picker.clone()).boxed()
+    fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
+        ChildView::new(self.picker.clone(), cx).boxed()
     }
 
     fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {

crates/collab_ui/src/contact_list.rs 🔗

@@ -1072,7 +1072,7 @@ impl View for ContactList {
             .with_child(
                 Flex::row()
                     .with_child(
-                        ChildView::new(self.filter_editor.clone())
+                        ChildView::new(self.filter_editor.clone(), cx)
                             .contained()
                             .with_style(theme.contact_list.user_query_editor.container)
                             .flex(1., true)

crates/collab_ui/src/contacts_popover.rs 🔗

@@ -88,8 +88,8 @@ impl View for ContactsPopover {
     fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
         let theme = cx.global::<Settings>().theme.clone();
         let child = match &self.child {
-            Child::ContactList(child) => ChildView::new(child),
-            Child::ContactFinder(child) => ChildView::new(child),
+            Child::ContactList(child) => ChildView::new(child, cx),
+            Child::ContactFinder(child) => ChildView::new(child, cx),
         };
 
         MouseEventHandler::<ContactsPopover>::new(0, cx, |_, cx| {

crates/command_palette/src/command_palette.rs 🔗

@@ -4,8 +4,8 @@ use gpui::{
     actions,
     elements::{ChildView, Flex, Label, ParentElement},
     keymap::Keystroke,
-    Action, AnyViewHandle, Element, Entity, MouseState, MutableAppContext, View, ViewContext,
-    ViewHandle,
+    Action, AnyViewHandle, Element, Entity, MouseState, MutableAppContext, RenderContext, View,
+    ViewContext, ViewHandle,
 };
 use picker::{Picker, PickerDelegate};
 use settings::Settings;
@@ -131,8 +131,8 @@ impl View for CommandPalette {
         "CommandPalette"
     }
 
-    fn render(&mut self, _: &mut gpui::RenderContext<'_, Self>) -> gpui::ElementBox {
-        ChildView::new(self.picker.clone()).boxed()
+    fn render(&mut self, cx: &mut RenderContext<Self>) -> gpui::ElementBox {
+        ChildView::new(self.picker.clone(), cx).boxed()
     }
 
     fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {

crates/diagnostics/src/diagnostics.rs 🔗

@@ -95,7 +95,7 @@ impl View for ProjectDiagnosticsEditor {
             .with_style(theme.container)
             .boxed()
         } else {
-            ChildView::new(&self.editor).boxed()
+            ChildView::new(&self.editor, cx).boxed()
         }
     }
 

crates/editor/src/editor.rs 🔗

@@ -5206,7 +5206,7 @@ impl Editor {
                             render: Arc::new({
                                 let editor = rename_editor.clone();
                                 move |cx: &mut BlockContext| {
-                                    ChildView::new(editor.clone())
+                                    ChildView::new(editor.clone(), cx)
                                         .contained()
                                         .with_padding_left(cx.anchor_x)
                                         .boxed()
@@ -6270,7 +6270,7 @@ impl View for Editor {
             .with_child(
                 EditorElement::new(self.handle.clone(), style.clone(), self.cursor_shape).boxed(),
             )
-            .with_child(ChildView::new(&self.mouse_context_menu).boxed())
+            .with_child(ChildView::new(&self.mouse_context_menu, cx).boxed())
             .boxed()
     }
 

crates/file_finder/src/file_finder.rs 🔗

@@ -49,8 +49,8 @@ impl View for FileFinder {
         "FileFinder"
     }
 
-    fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox {
-        ChildView::new(self.picker.clone()).boxed()
+    fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
+        ChildView::new(self.picker.clone(), cx).boxed()
     }
 
     fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {

crates/go_to_line/src/go_to_line.rs 🔗

@@ -165,7 +165,7 @@ impl View for GoToLine {
             Container::new(
                 Flex::new(Axis::Vertical)
                     .with_child(
-                        Container::new(ChildView::new(&self.line_editor).boxed())
+                        Container::new(ChildView::new(&self.line_editor, cx).boxed())
                             .with_style(theme.input_editor.container)
                             .boxed(),
                     )

crates/gpui/src/app.rs 🔗

@@ -2571,6 +2571,10 @@ impl AppContext {
             .and_then(|window| window.focused_view_id)
     }
 
+    pub fn view_ui_name(&self, window_id: usize, view_id: usize) -> Option<&'static str> {
+        Some(self.views.get(&(window_id, view_id))?.ui_name())
+    }
+
     pub fn background(&self) -> &Arc<executor::Background> {
         &self.background
     }
@@ -4416,6 +4420,10 @@ impl AnyViewHandle {
         }
     }
 
+    pub fn window_id(&self) -> usize {
+        self.window_id
+    }
+
     pub fn id(&self) -> usize {
         self.view_id
     }
@@ -4732,6 +4740,10 @@ pub struct AnyWeakViewHandle {
 }
 
 impl AnyWeakViewHandle {
+    pub fn id(&self) -> usize {
+        self.view_id
+    }
+
     pub fn upgrade(&self, cx: &impl UpgradeViewHandle) -> Option<AnyViewHandle> {
         cx.upgrade_any_view_handle(self)
     }
@@ -7032,4 +7044,73 @@ mod tests {
         cx.simulate_window_activation(Some(window_3));
         assert_eq!(mem::take(&mut *events.borrow_mut()), []);
     }
+
+    #[crate::test(self)]
+    fn test_child_view(cx: &mut MutableAppContext) {
+        struct Child {
+            rendered: Rc<Cell<bool>>,
+            dropped: Rc<Cell<bool>>,
+        }
+
+        impl super::Entity for Child {
+            type Event = ();
+        }
+
+        impl super::View for Child {
+            fn ui_name() -> &'static str {
+                "child view"
+            }
+
+            fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox {
+                self.rendered.set(true);
+                Empty::new().boxed()
+            }
+        }
+
+        impl Drop for Child {
+            fn drop(&mut self) {
+                self.dropped.set(true);
+            }
+        }
+
+        struct Parent {
+            child: Option<ViewHandle<Child>>,
+        }
+
+        impl super::Entity for Parent {
+            type Event = ();
+        }
+
+        impl super::View for Parent {
+            fn ui_name() -> &'static str {
+                "parent view"
+            }
+
+            fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
+                if let Some(child) = self.child.as_ref() {
+                    ChildView::new(child, cx).boxed()
+                } else {
+                    Empty::new().boxed()
+                }
+            }
+        }
+
+        let child_rendered = Rc::new(Cell::new(false));
+        let child_dropped = Rc::new(Cell::new(false));
+        let (_, root_view) = cx.add_window(Default::default(), |cx| Parent {
+            child: Some(cx.add_view(|_| Child {
+                rendered: child_rendered.clone(),
+                dropped: child_dropped.clone(),
+            })),
+        });
+        assert!(child_rendered.take());
+        assert!(!child_dropped.take());
+
+        root_view.update(cx, |view, cx| {
+            view.child.take();
+            cx.notify();
+        });
+        assert!(!child_rendered.take());
+        assert!(child_dropped.take());
+    }
 }

crates/gpui/src/presenter.rs 🔗

@@ -12,10 +12,10 @@ use crate::{
         UpOutRegionEvent, UpRegionEvent,
     },
     text_layout::TextLayoutCache,
-    Action, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, Appearance, AssetCache, ElementBox,
-    Entity, FontSystem, ModelHandle, MouseButton, MouseMovedEvent, MouseRegion, MouseRegionId,
-    ParentId, ReadModel, ReadView, RenderContext, RenderParams, Scene, UpgradeModelHandle,
-    UpgradeViewHandle, View, ViewHandle, WeakModelHandle, WeakViewHandle,
+    Action, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AnyWeakViewHandle, Appearance,
+    AssetCache, ElementBox, Entity, FontSystem, ModelHandle, MouseButton, MouseMovedEvent,
+    MouseRegion, MouseRegionId, ParentId, ReadModel, ReadView, RenderContext, RenderParams, Scene,
+    UpgradeModelHandle, UpgradeViewHandle, View, ViewHandle, WeakModelHandle, WeakViewHandle,
 };
 use collections::{HashMap, HashSet};
 use pathfinder_geometry::vector::{vec2f, Vector2F};
@@ -972,17 +972,23 @@ impl ToJson for SizeConstraint {
 }
 
 pub struct ChildView {
-    view: AnyViewHandle,
+    view: AnyWeakViewHandle,
+    view_name: &'static str,
 }
 
 impl ChildView {
-    pub fn new(view: impl Into<AnyViewHandle>) -> Self {
-        Self { view: view.into() }
+    pub fn new(view: impl Into<AnyViewHandle>, cx: &AppContext) -> Self {
+        let view = view.into();
+        let view_name = cx.view_ui_name(view.window_id(), view.id()).unwrap();
+        Self {
+            view: view.downgrade(),
+            view_name,
+        }
     }
 }
 
 impl Element for ChildView {
-    type LayoutState = ();
+    type LayoutState = bool;
     type PaintState = ();
 
     fn layout(
@@ -990,18 +996,35 @@ impl Element for ChildView {
         constraint: SizeConstraint,
         cx: &mut LayoutContext,
     ) -> (Vector2F, Self::LayoutState) {
-        let size = cx.layout(self.view.id(), constraint);
-        (size, ())
+        if cx.rendered_views.contains_key(&self.view.id()) {
+            let size = cx.layout(self.view.id(), constraint);
+            (size, true)
+        } else {
+            log::error!(
+                "layout called on a ChildView element whose underlying view was dropped (view_id: {}, name: {:?})",
+                self.view.id(),
+                self.view_name
+            );
+            (Vector2F::zero(), false)
+        }
     }
 
     fn paint(
         &mut self,
         bounds: RectF,
         visible_bounds: RectF,
-        _: &mut Self::LayoutState,
+        view_is_valid: &mut Self::LayoutState,
         cx: &mut PaintContext,
-    ) -> Self::PaintState {
-        cx.paint(self.view.id(), bounds.origin(), visible_bounds);
+    ) {
+        if *view_is_valid {
+            cx.paint(self.view.id(), bounds.origin(), visible_bounds);
+        } else {
+            log::error!(
+                "paint called on a ChildView element whose underlying view was dropped (view_id: {}, name: {:?})",
+                self.view.id(),
+                self.view_name
+            );
+        }
     }
 
     fn dispatch_event(
@@ -1009,11 +1032,20 @@ impl Element for ChildView {
         event: &Event,
         _: RectF,
         _: RectF,
-        _: &mut Self::LayoutState,
+        view_is_valid: &mut Self::LayoutState,
         _: &mut Self::PaintState,
         cx: &mut EventContext,
     ) -> bool {
-        cx.dispatch_event(self.view.id(), event)
+        if *view_is_valid {
+            cx.dispatch_event(self.view.id(), event)
+        } else {
+            log::error!(
+                "dispatch_event called on a ChildView element whose underlying view was dropped (view_id: {}, name: {:?})",
+                self.view.id(),
+                self.view_name
+            );
+            false
+        }
     }
 
     fn rect_for_text_range(
@@ -1021,11 +1053,20 @@ impl Element for ChildView {
         range_utf16: Range<usize>,
         _: RectF,
         _: RectF,
-        _: &Self::LayoutState,
+        view_is_valid: &Self::LayoutState,
         _: &Self::PaintState,
         cx: &MeasurementContext,
     ) -> Option<RectF> {
-        cx.rect_for_text_range(self.view.id(), range_utf16)
+        if *view_is_valid {
+            cx.rect_for_text_range(self.view.id(), range_utf16)
+        } else {
+            log::error!(
+                "rect_for_text_range called on a ChildView element whose underlying view was dropped (view_id: {}, name: {:?})",
+                self.view.id(),
+                self.view_name
+            );
+            None
+        }
     }
 
     fn debug(
@@ -1039,7 +1080,11 @@ impl Element for ChildView {
             "type": "ChildView",
             "view_id": self.view.id(),
             "bounds": bounds.to_json(),
-            "view": self.view.debug_json(cx.app),
+            "view": if let Some(view) = self.view.upgrade(cx.app) {
+                view.debug_json(cx.app)
+            } else {
+                json!(null)
+            },
             "child": if let Some(view) = cx.rendered_views.get(&self.view.id()) {
                 view.debug(cx)
             } else {

crates/outline/src/outline.rs 🔗

@@ -48,8 +48,8 @@ impl View for OutlineView {
         "OutlineView"
     }
 
-    fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox {
-        ChildView::new(self.picker.clone()).boxed()
+    fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
+        ChildView::new(self.picker.clone(), cx).boxed()
     }
 
     fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {

crates/picker/src/picker.rs 🔗

@@ -63,7 +63,7 @@ impl<D: PickerDelegate> View for Picker<D> {
 
         Flex::new(Axis::Vertical)
             .with_child(
-                ChildView::new(&self.query_editor)
+                ChildView::new(&self.query_editor, cx)
                     .contained()
                     .with_style(theme.input_editor.container)
                     .boxed(),

crates/project_panel/src/project_panel.rs 🔗

@@ -1012,7 +1012,7 @@ impl ProjectPanel {
     ) -> ElementBox {
         let kind = details.kind;
         let show_editor = details.is_editing && !details.is_processing;
-        MouseEventHandler::<Self>::new(entry_id.to_usize(), cx, |state, _| {
+        MouseEventHandler::<Self>::new(entry_id.to_usize(), cx, |state, cx| {
             let padding = theme.container.padding.left + details.depth as f32 * theme.indent_width;
             let mut style = theme.entry.style_for(state, details.is_selected).clone();
             if details.is_ignored {
@@ -1051,7 +1051,7 @@ impl ProjectPanel {
                     .boxed(),
                 )
                 .with_child(if show_editor {
-                    ChildView::new(editor.clone())
+                    ChildView::new(editor.clone(), cx)
                         .contained()
                         .with_margin_left(theme.entry.default.icon_spacing)
                         .aligned()
@@ -1147,7 +1147,7 @@ impl View for ProjectPanel {
                 })
                 .boxed(),
             )
-            .with_child(ChildView::new(&self.context_menu).boxed())
+            .with_child(ChildView::new(&self.context_menu, cx).boxed())
             .boxed()
     }
 

crates/project_symbols/src/project_symbols.rs 🔗

@@ -47,8 +47,8 @@ impl View for ProjectSymbolsView {
         "ProjectSymbolsView"
     }
 
-    fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox {
-        ChildView::new(self.picker.clone()).boxed()
+    fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
+        ChildView::new(self.picker.clone(), cx).boxed()
     }
 
     fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {

crates/search/src/buffer_search.rs 🔗

@@ -105,7 +105,7 @@ impl View for BufferSearchBar {
             .with_child(
                 Flex::row()
                     .with_child(
-                        ChildView::new(&self.query_editor)
+                        ChildView::new(&self.query_editor, cx)
                             .aligned()
                             .left()
                             .flex(1., true)

crates/search/src/project_search.rs 🔗

@@ -189,7 +189,9 @@ impl View for ProjectSearchView {
             })
             .boxed()
         } else {
-            ChildView::new(&self.results_editor).flex(1., true).boxed()
+            ChildView::new(&self.results_editor, cx)
+                .flex(1., true)
+                .boxed()
         }
     }
 
@@ -824,7 +826,7 @@ impl View for ProjectSearchBar {
                 .with_child(
                     Flex::row()
                         .with_child(
-                            ChildView::new(&search.query_editor)
+                            ChildView::new(&search.query_editor, cx)
                                 .aligned()
                                 .left()
                                 .flex(1., true)

crates/terminal/src/terminal_container_view.rs 🔗

@@ -162,8 +162,8 @@ impl View for TerminalContainer {
 
     fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> ElementBox {
         let child_view = match &self.content {
-            TerminalContainerContent::Connected(connected) => ChildView::new(connected),
-            TerminalContainerContent::Error(error) => ChildView::new(error),
+            TerminalContainerContent::Connected(connected) => ChildView::new(connected, cx),
+            TerminalContainerContent::Error(error) => ChildView::new(error, cx),
         };
         if self.modal {
             let settings = cx.global::<Settings>();

crates/terminal/src/terminal_view.rs 🔗

@@ -339,7 +339,7 @@ impl View for TerminalView {
                 .contained()
                 .boxed(),
             )
-            .with_child(ChildView::new(&self.context_menu).boxed())
+            .with_child(ChildView::new(&self.context_menu, cx).boxed())
             .boxed()
     }
 

crates/theme_selector/src/theme_selector.rs 🔗

@@ -262,8 +262,8 @@ impl View for ThemeSelector {
         "ThemeSelector"
     }
 
-    fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox {
-        ChildView::new(self.picker.clone()).boxed()
+    fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
+        ChildView::new(self.picker.clone(), cx).boxed()
     }
 
     fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {

crates/workspace/src/dock.rs 🔗

@@ -255,7 +255,7 @@ impl Dock {
 
                     enum DockResizeHandle {}
 
-                    let resizable = Container::new(ChildView::new(self.pane.clone()).boxed())
+                    let resizable = Container::new(ChildView::new(self.pane.clone(), cx).boxed())
                         .with_style(panel_style)
                         .with_resize_handle::<DockResizeHandle, _>(
                             resize_side as usize,
@@ -285,8 +285,8 @@ impl Dock {
                     enum ExpandedDockPane {}
                     Container::new(
                         MouseEventHandler::<ExpandedDockWash>::new(0, cx, |_state, cx| {
-                            MouseEventHandler::<ExpandedDockPane>::new(0, cx, |_state, _cx| {
-                                ChildView::new(self.pane.clone()).boxed()
+                            MouseEventHandler::<ExpandedDockPane>::new(0, cx, |_state, cx| {
+                                ChildView::new(&self.pane, cx).boxed()
                             })
                             .capture_all()
                             .contained()

crates/workspace/src/pane.rs 🔗

@@ -1439,8 +1439,8 @@ impl View for Pane {
                                     .flex(1., false)
                                     .named("tab bar")
                             })
-                            .with_child(ChildView::new(&self.toolbar).expanded().boxed())
-                            .with_child(ChildView::new(active_item).flex(1., true).boxed())
+                            .with_child(ChildView::new(&self.toolbar, cx).expanded().boxed())
+                            .with_child(ChildView::new(active_item, cx).flex(1., true).boxed())
                             .boxed()
                     } else {
                         enum EmptyPane {}
@@ -1480,7 +1480,7 @@ impl View for Pane {
                 })
                 .boxed(),
             )
-            .with_child(ChildView::new(&self.tab_bar_context_menu).boxed())
+            .with_child(ChildView::new(&self.tab_bar_context_menu, cx).boxed())
             .named("pane")
     }
 

crates/workspace/src/pane_group.rs 🔗

@@ -222,7 +222,12 @@ impl Member {
                 };
 
                 Stack::new()
-                    .with_child(ChildView::new(pane).contained().with_border(border).boxed())
+                    .with_child(
+                        ChildView::new(pane, cx)
+                            .contained()
+                            .with_border(border)
+                            .boxed(),
+                    )
                     .with_children(prompt)
                     .boxed()
             }

crates/workspace/src/sidebar.rs 🔗

@@ -192,7 +192,7 @@ impl View for Sidebar {
         if let Some(active_item) = self.active_item() {
             enum ResizeHandleTag {}
             let style = &cx.global::<Settings>().theme.workspace.sidebar;
-            ChildView::new(active_item.to_any())
+            ChildView::new(active_item.to_any(), cx)
                 .contained()
                 .with_style(style.container)
                 .with_resize_handle::<ResizeHandleTag, _>(

crates/workspace/src/status_bar.rs 🔗

@@ -42,14 +42,14 @@ impl View for StatusBar {
         let theme = &cx.global::<Settings>().theme.workspace.status_bar;
         Flex::row()
             .with_children(self.left_items.iter().map(|i| {
-                ChildView::new(i.as_ref())
+                ChildView::new(i.as_ref(), cx)
                     .aligned()
                     .contained()
                     .with_margin_right(theme.item_spacing)
                     .boxed()
             }))
             .with_children(self.right_items.iter().rev().map(|i| {
-                ChildView::new(i.as_ref())
+                ChildView::new(i.as_ref(), cx)
                     .aligned()
                     .contained()
                     .with_margin_left(theme.item_spacing)

crates/workspace/src/toolbar.rs 🔗

@@ -67,7 +67,7 @@ impl View for Toolbar {
             match *position {
                 ToolbarItemLocation::Hidden => {}
                 ToolbarItemLocation::PrimaryLeft { flex } => {
-                    let left_item = ChildView::new(item.as_ref())
+                    let left_item = ChildView::new(item.as_ref(), cx)
                         .aligned()
                         .contained()
                         .with_margin_right(spacing);
@@ -78,7 +78,7 @@ impl View for Toolbar {
                     }
                 }
                 ToolbarItemLocation::PrimaryRight { flex } => {
-                    let right_item = ChildView::new(item.as_ref())
+                    let right_item = ChildView::new(item.as_ref(), cx)
                         .aligned()
                         .contained()
                         .with_margin_left(spacing)
@@ -91,7 +91,7 @@ impl View for Toolbar {
                 }
                 ToolbarItemLocation::Secondary => {
                     secondary_item = Some(
-                        ChildView::new(item.as_ref())
+                        ChildView::new(item.as_ref(), cx)
                             .constrained()
                             .with_height(theme.height)
                             .boxed(),

crates/workspace/src/workspace.rs 🔗

@@ -2126,7 +2126,7 @@ impl Workspace {
 
         enum TitleBar {}
         ConstrainedBox::new(
-            MouseEventHandler::<TitleBar>::new(0, cx, |_, _| {
+            MouseEventHandler::<TitleBar>::new(0, cx, |_, cx| {
                 Container::new(
                     Stack::new()
                         .with_child(
@@ -2138,7 +2138,7 @@ impl Workspace {
                         .with_children(
                             self.titlebar_item
                                 .as_ref()
-                                .map(|item| ChildView::new(item).aligned().right().boxed()),
+                                .map(|item| ChildView::new(item, cx).aligned().right().boxed()),
                         )
                         .boxed(),
                 )
@@ -2231,14 +2231,18 @@ impl Workspace {
         }
     }
 
-    fn render_notifications(&self, theme: &theme::Workspace) -> Option<ElementBox> {
+    fn render_notifications(
+        &self,
+        theme: &theme::Workspace,
+        cx: &AppContext,
+    ) -> Option<ElementBox> {
         if self.notifications.is_empty() {
             None
         } else {
             Some(
                 Flex::column()
                     .with_children(self.notifications.iter().map(|(_, _, notification)| {
-                        ChildView::new(notification.as_ref())
+                        ChildView::new(notification.as_ref(), cx)
                             .contained()
                             .with_style(theme.notification)
                             .boxed()
@@ -2570,7 +2574,7 @@ impl View for Workspace {
                                     .with_children(
                                         if self.left_sidebar.read(cx).active_item().is_some() {
                                             Some(
-                                                ChildView::new(&self.left_sidebar)
+                                                ChildView::new(&self.left_sidebar, cx)
                                                     .flex(0.8, false)
                                                     .boxed(),
                                             )
@@ -2606,7 +2610,7 @@ impl View for Workspace {
                                     .with_children(
                                         if self.right_sidebar.read(cx).active_item().is_some() {
                                             Some(
-                                                ChildView::new(&self.right_sidebar)
+                                                ChildView::new(&self.right_sidebar, cx)
                                                     .flex(0.8, false)
                                                     .boxed(),
                                             )
@@ -2624,15 +2628,17 @@ impl View for Workspace {
                                             DockAnchor::Expanded,
                                             cx,
                                         ))
-                                        .with_children(self.modal.as_ref().map(|m| {
-                                            ChildView::new(m)
+                                        .with_children(self.modal.as_ref().map(|modal| {
+                                            ChildView::new(modal, cx)
                                                 .contained()
                                                 .with_style(theme.workspace.modal)
                                                 .aligned()
                                                 .top()
                                                 .boxed()
                                         }))
-                                        .with_children(self.render_notifications(&theme.workspace))
+                                        .with_children(
+                                            self.render_notifications(&theme.workspace, cx),
+                                        )
                                         .boxed(),
                                 )
                                 .boxed(),
@@ -2640,7 +2646,7 @@ impl View for Workspace {
                             .flex(1.0, true)
                             .boxed(),
                     )
-                    .with_child(ChildView::new(&self.status_bar).boxed())
+                    .with_child(ChildView::new(&self.status_bar, cx).boxed())
                     .contained()
                     .with_background_color(theme.workspace.background)
                     .boxed(),