WIP

Nathan Sobo created

Change summary

crates/gpui/src/app.rs                             |  36 +++++
crates/gpui/src/app/window.rs                      |  56 ++++++--
crates/gpui/src/scene/mouse_region.rs              |   8 +
crates/workspace/src/dock.rs                       |  22 +-
crates/workspace/src/dock/toggle_dock_button.rs    |  16 +-
crates/workspace/src/item.rs                       |   8 
crates/workspace/src/notifications.rs              |  12 
crates/workspace/src/pane.rs                       |  78 ++++++-----
crates/workspace/src/pane/dragged_item_receiver.rs |  19 +-
crates/workspace/src/pane_group.rs                 |   4 
crates/workspace/src/shared_screen.rs              |  10 
crates/workspace/src/sidebar.rs                    |   8 
crates/workspace/src/toolbar.rs                    |   6 
crates/workspace/src/workspace.rs                  | 102 ++++++++-------
14 files changed, 235 insertions(+), 150 deletions(-)

Detailed changes

crates/gpui/src/app.rs 🔗

@@ -802,6 +802,14 @@ impl AppContext {
             .is_some()
     }
 
+    pub fn window_is_active(&self, window_id: usize) -> bool {
+        self.windows.get(&window_id).map_or(false, |w| w.is_active)
+    }
+
+    pub fn root_view(&self, window_id: usize) -> Option<&AnyViewHandle> {
+        self.windows.get(&window_id).map(|w| w.root_view())
+    }
+
     pub fn window_ids(&self) -> impl Iterator<Item = usize> + '_ {
         self.windows.keys().copied()
     }
@@ -1648,6 +1656,18 @@ impl AppContext {
         window
     }
 
+    pub fn replace_root_view<V, F>(
+        &mut self,
+        window_id: usize,
+        build_root_view: F,
+    ) -> Option<ViewHandle<V>>
+    where
+        V: View,
+        F: FnOnce(&mut ViewContext<V>) -> V,
+    {
+        self.update_window(window_id, |cx| cx.replace_root_view(build_root_view))
+    }
+
     pub fn add_view<S, F>(&mut self, parent: &AnyViewHandle, build_view: F) -> ViewHandle<S>
     where
         S: View,
@@ -3326,6 +3346,22 @@ impl<'a, 'b, 'c, V: View> ViewContext<'a, 'b, 'c, V> {
         self.window.focused_view_id == Some(self.view_id)
     }
 
+    pub fn is_parent_view_focused(&self) -> bool {
+        if let Some(parent_view_id) = self.ancestors(self.window_id, self.view_id).next().clone() {
+            self.focused_view_id() == Some(parent_view_id)
+        } else {
+            false
+        }
+    }
+
+    pub fn focus_parent_view(&mut self) {
+        let next = self.ancestors(self.window_id, self.view_id).next().clone();
+        if let Some(parent_view_id) = next {
+            let window_id = self.window_id;
+            self.window_context.focus(window_id, Some(parent_view_id));
+        }
+    }
+
     pub fn is_child(&self, view: impl Into<AnyViewHandle>) -> bool {
         let view = view.into();
         if self.window_id != view.window_id {

crates/gpui/src/app/window.rs 🔗

@@ -12,8 +12,9 @@ use crate::{
     },
     text_layout::TextLayoutCache,
     util::post_inc,
-    AnyView, AnyViewHandle, AppContext, Element, ElementBox, MouseRegion, MouseRegionId, ParentId,
-    RenderParams, SceneBuilder, View, ViewContext, ViewHandle, WindowInvalidation,
+    AnyView, AnyViewHandle, AppContext, Element, ElementBox, Entity, ModelContext, ModelHandle,
+    MouseRegion, MouseRegionId, ParentId, ReadView, RenderParams, SceneBuilder, UpdateModel, View,
+    ViewContext, ViewHandle, WindowInvalidation,
 };
 use anyhow::bail;
 use collections::{HashMap, HashSet};
@@ -119,6 +120,22 @@ impl DerefMut for WindowContext<'_, '_> {
     }
 }
 
+impl UpdateModel for WindowContext<'_, '_> {
+    fn update_model<M: Entity, R>(
+        &mut self,
+        handle: &ModelHandle<M>,
+        update: &mut dyn FnMut(&mut M, &mut ModelContext<M>) -> R,
+    ) -> R {
+        self.app_context.update_model(handle, update)
+    }
+}
+
+impl ReadView for WindowContext<'_, '_> {
+    fn read_view<W: View>(&self, handle: &crate::ViewHandle<W>) -> &W {
+        self.app_context.read_view(handle)
+    }
+}
+
 impl<'a: 'b, 'b> WindowContext<'a, 'b> {
     pub fn new(app_context: &'a mut AppContext, window: &'b mut Window, window_id: usize) -> Self {
         Self {
@@ -133,6 +150,14 @@ impl<'a: 'b, 'b> WindowContext<'a, 'b> {
         self.window_id
     }
 
+    pub fn app_context(&mut self) -> &mut AppContext {
+        self.app_context
+    }
+
+    pub fn root_view(&self) -> &AnyViewHandle {
+        self.window.root_view()
+    }
+
     pub fn window_size(&self) -> Vector2F {
         self.window.platform_window.content_size()
     }
@@ -701,23 +726,22 @@ impl<'a: 'b, 'b> WindowContext<'a, 'b> {
     }
 
     pub fn rect_for_text_range(&self, range_utf16: Range<usize>) -> Option<RectF> {
-        todo!()
+        let root_view_id = self.window.root_view().id();
+        self.window
+            .rendered_views
+            .get(&root_view_id)?
+            .rect_for_text_range(range_utf16, self, root_view_id)
     }
 
     pub fn debug_elements(&self) -> Option<json::Value> {
-        todo!()
-        // let view = self.root_view()?;
-        // Some(json!({
-        //     "root_view": view.debug_json(self),
-        //     "root_element": self.window.rendered_views.get(&view.id())
-        //         .map(|root_element| {
-        //             root_element.debug(&DebugContext {
-        //                 rendered_views: &self.window.rendered_views,
-        //                 font_cache: &self.window.font_cache,
-        //                 app: self,
-        //             })
-        //         })
-        // }))
+        let view = self.window.root_view();
+        Some(json!({
+            "root_view": view.debug_json(self),
+            "root_element": self.window.rendered_views.get(&view.id())
+                .map(|root_element| {
+                    root_element.debug(self, view.id())
+                })
+        }))
     }
 
     pub fn set_window_title(&mut self, title: &str) {

crates/gpui/src/scene/mouse_region.rs 🔗

@@ -11,7 +11,7 @@ use collections::HashMap;
 use pathfinder_geometry::rect::RectF;
 use smallvec::SmallVec;
 
-use crate::{platform::MouseButton, window::WindowContext, View, ViewContext};
+use crate::{platform::MouseButton, window::WindowContext, ReadView, View, ViewContext};
 
 use super::{
     mouse_event::{
@@ -234,6 +234,12 @@ impl<V: View> DerefMut for EventContext<'_, '_, '_, '_, V> {
     }
 }
 
+impl<V: View> ReadView for EventContext<'_, '_, '_, '_, V> {
+    fn read_view<W: View>(&self, handle: &crate::ViewHandle<W>) -> &W {
+        self.view_context.read_view(handle)
+    }
+}
+
 pub type HandlerCallback = Rc<dyn Fn(MouseEvent, &mut dyn Any, &mut WindowContext, usize) -> bool>;
 
 #[derive(Clone, PartialEq, Eq, Hash)]

crates/workspace/src/dock.rs 🔗

@@ -315,7 +315,7 @@ impl Dock {
         theme: &Theme,
         anchor: DockAnchor,
         cx: &mut ViewContext<Workspace>,
-    ) -> Option<ElementBox<Self>> {
+    ) -> Option<ElementBox<Workspace>> {
         let style = &theme.workspace.dock;
 
         self.position
@@ -350,7 +350,7 @@ impl Dock {
 
                     let resizable = Container::new(ChildView::new(&self.pane, cx).boxed())
                         .with_style(panel_style)
-                        .with_resize_handle::<DockResizeHandle, _>(
+                        .with_resize_handle::<DockResizeHandle>(
                             resize_side as usize,
                             resize_side,
                             4.,
@@ -362,8 +362,8 @@ impl Dock {
                         );
 
                     let size = resizable.current_size();
-                    let workspace = cx.handle();
-                    cx.defer(move |cx| {
+                    let workspace = cx.handle().downgrade();
+                    cx.defer(move |_, cx| {
                         if let Some(workspace) = workspace.upgrade(cx) {
                             workspace.update(cx, |workspace, _| {
                                 workspace.dock.panel_sizes.insert(anchor, size);
@@ -374,20 +374,20 @@ impl Dock {
                     if anchor == DockAnchor::Right {
                         resizable
                             .constrained()
-                            .dynamically(|constraint, cx| {
+                            .dynamically(|constraint, _, cx| {
                                 SizeConstraint::new(
                                     Vector2F::new(20., constraint.min.y()),
-                                    Vector2F::new(cx.window_size.x() * 0.8, constraint.max.y()),
+                                    Vector2F::new(cx.window_size().x() * 0.8, constraint.max.y()),
                                 )
                             })
                             .boxed()
                     } else {
                         resizable
                             .constrained()
-                            .dynamically(|constraint, cx| {
+                            .dynamically(|constraint, _, cx| {
                                 SizeConstraint::new(
                                     Vector2F::new(constraint.min.x(), 50.),
-                                    Vector2F::new(constraint.max.x(), cx.window_size.y() * 0.8),
+                                    Vector2F::new(constraint.max.x(), cx.window_size().y() * 0.8),
                                 )
                             })
                             .boxed()
@@ -399,21 +399,21 @@ impl Dock {
                     Stack::new()
                         .with_child(
                             // Render wash under the dock which when clicked hides it
-                            MouseEventHandler::<ExpandedDockWash>::new(0, cx, |_, _| {
+                            MouseEventHandler::<ExpandedDockWash, _>::new(0, cx, |_, _| {
                                 Empty::new()
                                     .contained()
                                     .with_background_color(style.wash_color)
                                     .boxed()
                             })
                             .capture_all()
-                            .on_down(MouseButton::Left, |_, cx| {
+                            .on_down(MouseButton::Left, |_, _, cx| {
                                 cx.dispatch_action(HideDock);
                             })
                             .with_cursor_style(CursorStyle::Arrow)
                             .boxed(),
                         )
                         .with_child(
-                            MouseEventHandler::<ExpandedDockPane>::new(0, cx, |_state, cx| {
+                            MouseEventHandler::<ExpandedDockPane, _>::new(0, cx, |_state, cx| {
                                 ChildView::new(&self.pane, cx).boxed()
                             })
                             // Make sure all events directly under the dock pane

crates/workspace/src/dock/toggle_dock_button.rs 🔗

@@ -43,11 +43,11 @@ impl View for ToggleDockButton {
 
         let workspace = workspace.unwrap();
         let dock_position = workspace.read(cx).dock.position;
-        let dock_pane = workspace.read(cx.app).dock_pane().clone();
+        let dock_pane = workspace.read(cx).dock_pane().clone();
 
         let theme = cx.global::<Settings>().theme.clone();
 
-        let button = MouseEventHandler::<Self>::new(0, cx, {
+        let button = MouseEventHandler::<Self, _>::new(0, cx, {
             let theme = theme.clone();
             move |state, _| {
                 let style = theme
@@ -68,17 +68,17 @@ impl View for ToggleDockButton {
             }
         })
         .with_cursor_style(CursorStyle::PointingHand)
-        .on_up(MouseButton::Left, move |event, cx| {
-            let drop_index = dock_pane.read(cx.app).items_len() + 1;
+        .on_up(MouseButton::Left, move |event, _, cx| {
+            let drop_index = dock_pane.read(cx).items_len() + 1;
             handle_dropped_item(event, &dock_pane.downgrade(), drop_index, false, None, cx);
         });
 
         if dock_position.is_visible() {
             button
-                .on_click(MouseButton::Left, |_, cx| {
+                .on_click(MouseButton::Left, |_, _, cx| {
                     cx.dispatch_action(HideDock);
                 })
-                .with_tooltip::<Self, _>(
+                .with_tooltip::<Self>(
                     0,
                     "Hide Dock".into(),
                     Some(Box::new(HideDock)),
@@ -87,10 +87,10 @@ impl View for ToggleDockButton {
                 )
         } else {
             button
-                .on_click(MouseButton::Left, |_, cx| {
+                .on_click(MouseButton::Left, |_, _, cx| {
                     cx.dispatch_action(FocusDock);
                 })
-                .with_tooltip::<Self, _>(
+                .with_tooltip::<Self>(
                     0,
                     "Focus Dock".into(),
                     Some(Box::new(FocusDock)),

crates/workspace/src/item.rs 🔗

@@ -52,7 +52,7 @@ pub trait Item: View {
         detail: Option<usize>,
         style: &theme::Tab,
         cx: &AppContext,
-    ) -> ElementBox<Self>;
+    ) -> ElementBox<Pane>;
     fn for_each_project_item(&self, _: &AppContext, _: &mut dyn FnMut(usize, &dyn project::Item)) {}
     fn is_singleton(&self, _cx: &AppContext) -> bool {
         false
@@ -134,7 +134,7 @@ pub trait Item: View {
         ToolbarItemLocation::Hidden
     }
 
-    fn breadcrumbs(&self, _theme: &Theme, _cx: &AppContext) -> Option<Vec<ElementBox<Self>>> {
+    fn breadcrumbs(&self, _theme: &Theme, _cx: &AppContext) -> Option<Vec<ElementBox<Pane>>> {
         None
     }
 
@@ -591,7 +591,7 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
         self.read(cx).breadcrumb_location()
     }
 
-    fn breadcrumbs(&self, theme: &Theme, cx: &AppContext) -> Option<Vec<ElementBox<Self>>> {
+    fn breadcrumbs(&self, theme: &Theme, cx: &AppContext) -> Option<Vec<ElementBox<Pane>>> {
         self.read(cx).breadcrumbs(theme, cx)
     }
 
@@ -925,7 +925,7 @@ pub(crate) mod test {
             detail: Option<usize>,
             _: &theme::Tab,
             _: &AppContext,
-        ) -> ElementBox<Self> {
+        ) -> ElementBox<Pane> {
             self.tab_detail.set(detail);
             Empty::new().boxed()
         }

crates/workspace/src/notifications.rs 🔗

@@ -244,7 +244,7 @@ pub mod simple_message_notification {
 
             let has_click_action = click_action.is_some();
 
-            MouseEventHandler::<MessageNotificationTag>::new(0, cx, |state, cx| {
+            MouseEventHandler::<MessageNotificationTag, _>::new(0, cx, |state, cx| {
                 Flex::column()
                     .with_child(
                         Flex::row()
@@ -259,7 +259,7 @@ pub mod simple_message_notification {
                                     .boxed(),
                             )
                             .with_child(
-                                MouseEventHandler::<Cancel>::new(0, cx, |state, _| {
+                                MouseEventHandler::<Cancel, _>::new(0, cx, |state, _| {
                                     let style = theme.dismiss_button.style_for(state, false);
                                     Svg::new("icons/x_mark_8.svg")
                                         .with_color(style.color)
@@ -274,7 +274,7 @@ pub mod simple_message_notification {
                                         .boxed()
                                 })
                                 .with_padding(Padding::uniform(5.))
-                                .on_click(MouseButton::Left, move |_, cx| {
+                                .on_click(MouseButton::Left, move |_, _, cx| {
                                     cx.dispatch_action(CancelMessageNotification)
                                 })
                                 .with_cursor_style(CursorStyle::PointingHand)
@@ -312,9 +312,9 @@ pub mod simple_message_notification {
                     .boxed()
             })
             // Since we're not using a proper overlay, we have to capture these extra events
-            .on_down(MouseButton::Left, |_, _| {})
-            .on_up(MouseButton::Left, |_, _| {})
-            .on_click(MouseButton::Left, move |_, cx| {
+            .on_down(MouseButton::Left, |_, _, _| {})
+            .on_up(MouseButton::Left, |_, _, _| {})
+            .on_click(MouseButton::Left, move |_, _, cx| {
                 if let Some(click_action) = click_action.as_ref() {
                     cx.dispatch_any_action(click_action.boxed_clone());
                     cx.dispatch_action(CancelMessageNotification)

crates/workspace/src/pane.rs 🔗

@@ -1220,10 +1220,10 @@ impl Pane {
         });
     }
 
-    fn render_tabs(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render_tabs(&mut self, cx: &mut ViewContext<Self>) -> impl Element<Self> {
         let theme = cx.global::<Settings>().theme.clone();
 
-        let pane = cx.handle();
+        let pane = cx.handle().downgrade();
         let autoscroll = if mem::take(&mut self.autoscroll) {
             Some(self.active_item_index)
         } else {
@@ -1233,7 +1233,7 @@ impl Pane {
         let pane_active = self.is_active;
 
         enum Tabs {}
-        let mut row = Flex::row().scrollable::<Tabs, _>(1, autoscroll, cx);
+        let mut row = Flex::row().scrollable::<Tabs>(1, autoscroll, cx);
         for (ix, (item, detail)) in self
             .items
             .iter()
@@ -1260,8 +1260,8 @@ impl Pane {
                             let hovered = mouse_state.hovered();
 
                             enum Tab {}
-                            MouseEventHandler::<Tab>::new(ix, cx, |_, cx| {
-                                Self::render_tab(
+                            MouseEventHandler::<Tab, Pane>::new(ix, cx, |_, cx| {
+                                Self::render_tab::<Pane>(
                                     &item,
                                     pane.clone(),
                                     ix == 0,
@@ -1271,12 +1271,12 @@ impl Pane {
                                     cx,
                                 )
                             })
-                            .on_down(MouseButton::Left, move |_, cx| {
+                            .on_down(MouseButton::Left, move |_, _, cx| {
                                 cx.dispatch_action(ActivateItem(ix));
                             })
                             .on_click(MouseButton::Middle, {
                                 let item = item.clone();
-                                move |_, cx: &mut EventContext| {
+                                move |_, _, cx: &mut EventContext<Self>| {
                                     cx.dispatch_action(CloseItem {
                                         item_id: item.id(),
                                         pane: pane.clone(),
@@ -1301,9 +1301,9 @@ impl Pane {
                             let theme = cx.global::<Settings>().theme.clone();
 
                             let detail = detail.clone();
-                            move |dragged_item, cx: &mut ViewContext<Workspace>| {
+                            move |dragged_item: &DraggedItem, cx: &mut ViewContext<Pane>| {
                                 let tab_style = &theme.workspace.tab_bar.dragged_tab;
-                                Self::render_tab(
+                                Self::render_tab::<Pane>(
                                     &dragged_item.item,
                                     dragged_item.pane.clone(),
                                     false,
@@ -1404,7 +1404,7 @@ impl Pane {
                     };
 
                     ConstrainedBox::new(
-                        Canvas::new(move |scene, bounds, _, cx| {
+                        Canvas::new(move |scene, bounds, _, _, cx| {
                             if let Some(color) = icon_color {
                                 let square = RectF::new(bounds.origin(), vec2f(diameter, diameter));
                                 scene.push_quad(Quad {
@@ -1441,18 +1441,22 @@ impl Pane {
                         let item_id = item.id();
                         enum TabCloseButton {}
                         let icon = Svg::new("icons/x_mark_8.svg");
-                        MouseEventHandler::<TabCloseButton>::new(item_id, cx, |mouse_state, _| {
-                            if mouse_state.hovered() {
-                                icon.with_color(tab_style.icon_close_active).boxed()
-                            } else {
-                                icon.with_color(tab_style.icon_close).boxed()
-                            }
-                        })
+                        MouseEventHandler::<TabCloseButton, _>::new(
+                            item_id,
+                            cx,
+                            |mouse_state, _| {
+                                if mouse_state.hovered() {
+                                    icon.with_color(tab_style.icon_close_active).boxed()
+                                } else {
+                                    icon.with_color(tab_style.icon_close).boxed()
+                                }
+                            },
+                        )
                         .with_padding(Padding::uniform(4.))
                         .with_cursor_style(CursorStyle::PointingHand)
                         .on_click(MouseButton::Left, {
                             let pane = pane.clone();
-                            move |_, cx| {
+                            move |_, _, cx| {
                                 cx.dispatch_action(CloseItem {
                                     item_id,
                                     pane: pane.clone(),
@@ -1551,13 +1555,13 @@ impl View for Pane {
     }
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> ElementBox<Self> {
-        let this = cx.handle();
+        let this = cx.handle().downgrade();
 
         enum MouseNavigationHandler {}
 
         Stack::new()
             .with_child(
-                MouseEventHandler::<MouseNavigationHandler>::new(0, cx, |_, cx| {
+                MouseEventHandler::<MouseNavigationHandler, _>::new(0, cx, |_, cx| {
                     let active_item_index = self.active_item_index;
 
                     if let Some(active_item) = self.active_item() {
@@ -1569,13 +1573,17 @@ impl View for Pane {
 
                                 enum TabBarEventHandler {}
                                 stack.add_child(
-                                    MouseEventHandler::<TabBarEventHandler>::new(0, cx, |_, _| {
-                                        Empty::new()
-                                            .contained()
-                                            .with_style(theme.workspace.tab_bar.container)
-                                            .boxed()
-                                    })
-                                    .on_down(MouseButton::Left, move |_, cx| {
+                                    MouseEventHandler::<TabBarEventHandler, _>::new(
+                                        0,
+                                        cx,
+                                        |_, _| {
+                                            Empty::new()
+                                                .contained()
+                                                .with_style(theme.workspace.tab_bar.container)
+                                                .boxed()
+                                        },
+                                    )
+                                    .on_down(MouseButton::Left, move |_, _, cx| {
                                         cx.dispatch_action(ActivateItem(active_item_index));
                                     })
                                     .boxed(),
@@ -1635,7 +1643,7 @@ impl View for Pane {
                         dragged_item_receiver::<EmptyPane, _>(0, 0, false, None, cx, |_, cx| {
                             self.render_blank_pane(&theme, cx)
                         })
-                        .on_down(MouseButton::Left, |_, cx| {
+                        .on_down(MouseButton::Left, |_, _, cx| {
                             cx.focus_parent_view();
                         })
                         .boxed()
@@ -1643,7 +1651,7 @@ impl View for Pane {
                 })
                 .on_down(MouseButton::Navigate(NavigationDirection::Back), {
                     let this = this.clone();
-                    move |_, cx| {
+                    move |_, _, cx| {
                         cx.dispatch_action(GoBack {
                             pane: Some(this.clone()),
                         });
@@ -1651,7 +1659,7 @@ impl View for Pane {
                 })
                 .on_down(MouseButton::Navigate(NavigationDirection::Forward), {
                     let this = this.clone();
-                    move |_, cx| {
+                    move |_, _, cx| {
                         cx.dispatch_action(GoForward {
                             pane: Some(this.clone()),
                         })
@@ -1716,7 +1724,7 @@ fn render_tab_bar_button<A: Action + Clone>(
 
     Stack::new()
         .with_child(
-            MouseEventHandler::<TabBarButton>::new(index, cx, |mouse_state, cx| {
+            MouseEventHandler::<TabBarButton, _>::new(index, cx, |mouse_state, cx| {
                 let theme = &cx.global::<Settings>().theme.workspace.tab_bar;
                 let style = theme.pane_button.style_for(mouse_state, false);
                 Svg::new(icon)
@@ -1730,7 +1738,7 @@ fn render_tab_bar_button<A: Action + Clone>(
                     .boxed()
             })
             .with_cursor_style(CursorStyle::PointingHand)
-            .on_click(MouseButton::Left, move |_, cx| {
+            .on_click(MouseButton::Left, move |_, _, cx| {
                 cx.dispatch_action(action.clone());
             })
             .boxed(),
@@ -1895,9 +1903,9 @@ impl Element<Pane> for PaneBackdrop {
         scene.push_mouse_region(
             MouseRegion::new::<Self>(child_view_id, 0, visible_bounds).on_down(
                 gpui::platform::MouseButton::Left,
-                move |_, cx| {
-                    let window_id = cx.window_id;
-                    cx.focus(window_id, Some(child_view_id))
+                move |_, _: &mut Pane, cx| {
+                    let window_id = cx.window_id();
+                    cx.app_context().focus(window_id, Some(child_view_id))
                 },
             ),
         );

crates/workspace/src/pane/dragged_item_receiver.rs 🔗

@@ -5,7 +5,8 @@ use gpui::{
     geometry::{rect::RectF, vector::Vector2F},
     platform::MouseButton,
     scene::MouseUp,
-    AppContext, Element, ElementBox, MouseState, Quad, ViewContext, WeakViewHandle,
+    AppContext, Element, ElementBox, EventContext, MouseState, Quad, View, ViewContext,
+    WeakViewHandle,
 };
 use project::ProjectEntryId;
 use settings::Settings;
@@ -29,7 +30,7 @@ where
     Tag: 'static,
     F: FnOnce(&mut MouseState, &mut ViewContext<Pane>) -> ElementBox<Pane>,
 {
-    MouseEventHandler::<Tag>::above(region_id, cx, |state, _, cx| {
+    MouseEventHandler::<Tag, _>::above(region_id, cx, |state, cx| {
         // Observing hovered will cause a render when the mouse enters regardless
         // of if mouse position was accessed before
         let drag_position = if state.hovered() {
@@ -48,7 +49,7 @@ where
         Stack::new()
             .with_child(render_child(state, cx))
             .with_children(drag_position.map(|drag_position| {
-                Canvas::new(move |scene, bounds, _, cx| {
+                Canvas::new(move |scene, bounds, _, _, cx| {
                     if bounds.contains_point(drag_position) {
                         let overlay_region = split_margin
                             .and_then(|split_margin| {
@@ -58,7 +59,7 @@ where
                             .map(|(dir, margin)| dir.along_edge(bounds, margin))
                             .unwrap_or(bounds);
 
-                        scene.paint_stacking_context(None, None, |cx| {
+                        scene.paint_stacking_context(None, None, |scene| {
                             scene.push_quad(Quad {
                                 bounds: overlay_region,
                                 background: Some(overlay_color(cx)),
@@ -73,13 +74,13 @@ where
             .boxed()
     })
     .on_up(MouseButton::Left, {
-        let pane = cx.handle();
-        move |event, cx| {
+        let pane = cx.handle().downgrade();
+        move |event, _, cx| {
             handle_dropped_item(event, &pane, drop_index, allow_same_pane, split_margin, cx);
             cx.notify();
         }
     })
-    .on_move(|_, cx| {
+    .on_move(|_, _, cx| {
         let drag_and_drop = cx.global::<DragAndDrop<Workspace>>();
 
         if drag_and_drop
@@ -96,13 +97,13 @@ where
     })
 }
 
-pub fn handle_dropped_item(
+pub fn handle_dropped_item<V: View>(
     event: MouseUp,
     pane: &WeakViewHandle<Pane>,
     index: usize,
     allow_same_pane: bool,
     split_margin: Option<f32>,
-    cx: &mut ViewContext<Pane>,
+    cx: &mut EventContext<V>,
 ) {
     enum Action {
         Move(WeakViewHandle<Pane>, usize),

crates/workspace/src/pane_group.rs 🔗

@@ -176,7 +176,7 @@ impl Member {
                                 let leader_user = leader.user.clone();
                                 let leader_user_id = leader.user.id;
                                 Some(
-                                    MouseEventHandler::<FollowIntoExternalProject>::new(
+                                    MouseEventHandler::<FollowIntoExternalProject, _>::new(
                                         pane.id(),
                                         cx,
                                         |_, _| {
@@ -199,7 +199,7 @@ impl Member {
                                         },
                                     )
                                     .with_cursor_style(CursorStyle::PointingHand)
-                                    .on_click(MouseButton::Left, move |_, cx| {
+                                    .on_click(MouseButton::Left, move |_, _, cx| {
                                         cx.dispatch_action(JoinProject {
                                             project_id: leader_project_id,
                                             follow_user_id: leader_user_id,

crates/workspace/src/shared_screen.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{
     item::{Item, ItemEvent},
-    ItemNavHistory, WorkspaceId,
+    ItemNavHistory, Pane, WorkspaceId,
 };
 use call::participant::{Frame, RemoteVideoTrack};
 use client::{proto::PeerId, User};
@@ -68,8 +68,8 @@ impl View for SharedScreen {
         enum Focus {}
 
         let frame = self.frame.clone();
-        MouseEventHandler::<Focus>::new(0, cx, |_, cx| {
-            Canvas::new(move |scene, bounds, _, cx| {
+        MouseEventHandler::<Focus, _>::new(0, cx, |_, cx| {
+            Canvas::new(move |scene, bounds, _, _, cx| {
                 if let Some(frame) = frame.clone() {
                     let size = constrain_size_preserving_aspect_ratio(
                         bounds.size(),
@@ -86,7 +86,7 @@ impl View for SharedScreen {
             .with_style(cx.global::<Settings>().theme.shared_screen)
             .boxed()
         })
-        .on_down(MouseButton::Left, |_, cx| cx.focus_parent_view())
+        .on_down(MouseButton::Left, |_, _, cx| cx.focus_parent_view())
         .boxed()
     }
 }
@@ -103,7 +103,7 @@ impl Item for SharedScreen {
         _: Option<usize>,
         style: &theme::Tab,
         _: &AppContext,
-    ) -> gpui::ElementBox<Self> {
+    ) -> gpui::ElementBox<Pane> {
         Flex::row()
             .with_child(
                 Svg::new("icons/disable_screen_sharing_12.svg")

crates/workspace/src/sidebar.rs 🔗

@@ -195,7 +195,7 @@ impl View for Sidebar {
             ChildView::new(active_item.as_any(), cx)
                 .contained()
                 .with_style(style.container)
-                .with_resize_handle::<ResizeHandleTag, _>(
+                .with_resize_handle::<ResizeHandleTag>(
                     self.sidebar_side as usize,
                     self.sidebar_side.to_resizable_side(),
                     4.,
@@ -254,7 +254,7 @@ impl View for SidebarButtons {
                         sidebar_side,
                         item_index: ix,
                     };
-                    MouseEventHandler::<Self>::new(ix, cx, |state, cx| {
+                    MouseEventHandler::<Self, _>::new(ix, cx, |state, cx| {
                         let is_active = is_open && ix == active_ix;
                         let style = item_style.style_for(state, is_active);
                         Stack::new()
@@ -283,9 +283,9 @@ impl View for SidebarButtons {
                     .with_cursor_style(CursorStyle::PointingHand)
                     .on_click(MouseButton::Left, {
                         let action = action.clone();
-                        move |_, cx| cx.dispatch_action(action.clone())
+                        move |_, _, cx| cx.dispatch_action(action.clone())
                     })
-                    .with_tooltip::<Self, _>(
+                    .with_tooltip::<Self>(
                         ix,
                         tooltip,
                         Some(Box::new(action)),

crates/workspace/src/toolbar.rs 🔗

@@ -170,7 +170,7 @@ fn nav_button<A: Action + Clone>(
     action_name: &str,
     cx: &mut ViewContext<Toolbar>,
 ) -> ElementBox<Toolbar> {
-    MouseEventHandler::<A>::new(0, cx, |state, _| {
+    MouseEventHandler::<A, _>::new(0, cx, |state, _| {
         let style = if enabled {
             style.style_for(state, false)
         } else {
@@ -194,10 +194,10 @@ fn nav_button<A: Action + Clone>(
     } else {
         CursorStyle::default()
     })
-    .on_click(MouseButton::Left, move |_, cx| {
+    .on_click(MouseButton::Left, move |_, _, cx| {
         cx.dispatch_action(action.clone())
     })
-    .with_tooltip::<A, _>(
+    .with_tooltip::<A>(
         0,
         action_name.to_string(),
         Some(Box::new(tooltip_action)),

crates/workspace/src/workspace.rs 🔗

@@ -928,11 +928,7 @@ impl Workspace {
                     workspace
                 };
 
-            let workspace = if let Some(window_id) = requesting_window_id {
-                cx.update(|cx| {
-                    cx.replace_root_view(window_id, |cx| build_workspace(cx, serialized_workspace))
-                })
-            } else {
+            let workspace = {
                 let (bounds, display) = if let Some(bounds) = window_bounds_override {
                     (Some(bounds), None)
                 } else {
@@ -1133,7 +1129,14 @@ impl Workspace {
         let window_id = cx.window_id();
         let workspace_count = cx
             .window_ids()
-            .filter_map(|window_id| cx.root_view(window_id)?.clone().downcast::<Workspace>())
+            .collect::<Vec<_>>()
+            .into_iter()
+            .filter_map(|window_id| {
+                cx.app_context()
+                    .root_view(window_id)?
+                    .clone()
+                    .downcast::<Workspace>()
+            })
             .count();
 
         cx.spawn(|this, mut cx| async move {
@@ -1142,23 +1145,22 @@ impl Workspace {
                     && workspace_count == 1
                     && active_call.read_with(&cx, |call, _| call.room().is_some())
                 {
-                    let answer = cx
-                        .prompt(
-                            window_id,
-                            PromptLevel::Warning,
-                            "Do you want to leave the current call?",
-                            &["Close window and hang up", "Cancel"],
-                        )
-                        .next()
-                        .await;
+                    let answer = cx.prompt(
+                        window_id,
+                        PromptLevel::Warning,
+                        "Do you want to leave the current call?",
+                        &["Close window and hang up", "Cancel"],
+                    );
 
-                    if answer == Some(1) {
-                        return anyhow::Ok(false);
-                    } else {
-                        active_call
-                            .update(&mut cx, |call, cx| call.hang_up(cx))
-                            .await
-                            .log_err();
+                    if let Some(mut answer) = answer {
+                        if answer.next().await == Some(1) {
+                            return anyhow::Ok(false);
+                        } else {
+                            active_call
+                                .update(&mut cx, |call, cx| call.hang_up(cx))
+                                .await
+                                .log_err();
+                        }
                     }
                 }
             }
@@ -1621,7 +1623,7 @@ impl Workspace {
     > {
         let project = self.project().clone();
         let project_item = project.update(cx, |project, cx| project.open_path(path, cx));
-        cx.as_mut().spawn(|mut cx| async move {
+        cx.spawn(|_, mut cx| async move {
             let (project_entry_id, project_item) = project_item.await?;
             let build_item = cx.update(|cx| {
                 cx.default_global::<ProjectItemBuilders>()
@@ -1802,15 +1804,14 @@ impl Workspace {
         }
 
         let item = pane.read(cx).active_item()?;
-        let maybe_pane_handle =
-            if let Some(clone) = item.clone_on_split(self.database_id(), cx.as_mut()) {
-                let new_pane = self.add_pane(cx);
-                Pane::add_item(self, &new_pane, clone, true, true, None, cx);
-                self.center.split(&pane, &new_pane, direction).unwrap();
-                Some(new_pane)
-            } else {
-                None
-            };
+        let maybe_pane_handle = if let Some(clone) = item.clone_on_split(self.database_id(), cx) {
+            let new_pane = self.add_pane(cx);
+            Pane::add_item(self, &new_pane, clone, true, true, None, cx);
+            self.center.split(&pane, &new_pane, direction).unwrap();
+            Some(new_pane)
+        } else {
+            None
+        };
         cx.notify();
         maybe_pane_handle
     }
@@ -2067,7 +2068,7 @@ impl Workspace {
 
         enum TitleBar {}
         ConstrainedBox::new(
-            MouseEventHandler::<TitleBar>::new(0, cx, |_, cx| {
+            MouseEventHandler::<TitleBar, _>::new(0, cx, |_, cx| {
                 Container::new(
                     Stack::new()
                         .with_children(
@@ -2080,9 +2081,9 @@ impl Workspace {
                 .with_style(container_theme)
                 .boxed()
             })
-            .on_click(MouseButton::Left, |event, cx| {
+            .on_click(MouseButton::Left, |event, _, cx| {
                 if event.click_count == 2 {
-                    cx.zoom_window(cx.window_id());
+                    cx.zoom_window();
                 }
             })
             .boxed(),
@@ -2160,7 +2161,7 @@ impl Workspace {
         if self.project.read(cx).is_read_only() {
             enum DisconnectedOverlay {}
             Some(
-                MouseEventHandler::<DisconnectedOverlay>::new(0, cx, |_, cx| {
+                MouseEventHandler::<DisconnectedOverlay, _>::new(0, cx, |_, cx| {
                     let theme = &cx.global::<Settings>().theme;
                     Label::new(
                         "Your connection to the remote project has been lost.",
@@ -2828,11 +2829,11 @@ impl View for Workspace {
                                             Some(
                                                 ChildView::new(&self.left_sidebar, cx)
                                                     .constrained()
-                                                    .dynamically(|constraint, cx| {
+                                                    .dynamically(|constraint, _, cx| {
                                                         SizeConstraint::new(
                                                             Vector2F::new(20., constraint.min.y()),
                                                             Vector2F::new(
-                                                                cx.window_size.x() * 0.8,
+                                                                cx.window_size().x() * 0.8,
                                                                 constraint.max.y(),
                                                             ),
                                                         )
@@ -2874,11 +2875,11 @@ impl View for Workspace {
                                             Some(
                                                 ChildView::new(&self.right_sidebar, cx)
                                                     .constrained()
-                                                    .dynamically(|constraint, cx| {
+                                                    .dynamically(|constraint, _, cx| {
                                                         SizeConstraint::new(
                                                             Vector2F::new(20., constraint.min.y()),
                                                             Vector2F::new(
-                                                                cx.window_size.x() * 0.8,
+                                                                cx.window_size().x() * 0.8,
                                                                 constraint.max.y(),
                                                             ),
                                                         )
@@ -2998,12 +2999,21 @@ pub fn activate_workspace_for_project(
     predicate: impl Fn(&mut Project, &mut ModelContext<Project>) -> bool,
 ) -> Option<ViewHandle<Workspace>> {
     for window_id in cx.window_ids().collect::<Vec<_>>() {
-        if let Some(workspace_handle) = cx.root_view(window_id)?.downcast_ref::<Workspace>() {
-            let project = workspace_handle.read(cx).project.clone();
-            if project.update(cx, &predicate) {
-                cx.activate_window(window_id);
-                return Some(workspace_handle.clone());
-            }
+        let handle = cx
+            .update_window(window_id, |cx| {
+                if let Some(workspace_handle) = cx.root_view().clone().downcast::<Workspace>() {
+                    let project = workspace_handle.read(cx).project.clone();
+                    if project.update(cx, &predicate) {
+                        cx.activate_window();
+                        return Some(workspace_handle.clone());
+                    }
+                }
+                None
+            })
+            .flatten();
+
+        if handle.is_some() {
+            return handle;
         }
     }
     None