Enable zoom (#3668)

Max Brunsfeld created

* [x] zoom
* [x] pane tests

Change summary

crates/workspace2/src/dock.rs       |    2 
crates/workspace2/src/pane.rs       | 1187 +++++++++++++-----------------
crates/workspace2/src/workspace2.rs |  200 +---
3 files changed, 585 insertions(+), 804 deletions(-)

Detailed changes

crates/workspace2/src/dock.rs 🔗

@@ -317,7 +317,7 @@ impl Dock {
                         new_dock.add_panel(panel.clone(), workspace.clone(), cx);
                         if was_visible {
                             new_dock.set_open(true, cx);
-                            new_dock.activate_panel(this.panels_len() - 1, cx);
+                            new_dock.activate_panel(new_dock.panels_len() - 1, cx);
                         }
                     });
                 }

crates/workspace2/src/pane.rs 🔗

@@ -177,7 +177,7 @@ pub struct Pane {
     toolbar: View<Toolbar>,
     new_item_menu: Option<View<ContextMenu>>,
     split_item_menu: Option<View<ContextMenu>>,
-    //     tab_context_menu: ViewHandle<ContextMenu>,
+    //     tab_context_menu: View<ContextMenu>,
     workspace: WeakView<Workspace>,
     project: Model<Project>,
     drag_split_direction: Option<SplitDirection>,
@@ -256,11 +256,11 @@ struct DraggedTab {
 
 // struct TabBarContextMenu {
 //     kind: TabBarContextMenuKind,
-//     handle: ViewHandle<ContextMenu>,
+//     handle: View<ContextMenu>,
 // }
 
 // impl TabBarContextMenu {
-//     fn handle_if_kind(&self, kind: TabBarContextMenuKind) -> Option<ViewHandle<ContextMenu>> {
+//     fn handle_if_kind(&self, kind: TabBarContextMenuKind) -> Option<View<ContextMenu>> {
 //         if self.kind == kind {
 //             return Some(self.handle.clone());
 //         }
@@ -329,7 +329,7 @@ impl Pane {
     ) -> Self {
         // todo!("context menu")
         // let pane_view_id = cx.view_id();
-        // let context_menu = cx.add_view(|cx| ContextMenu::new(pane_view_id, cx));
+        // let context_menu = cx.build_view(|cx| ContextMenu::new(pane_view_id, cx));
         // context_menu.update(cx, |menu, _| {
         //     menu.set_position_mode(OverlayPositionMode::Local)
         // });
@@ -368,7 +368,7 @@ impl Pane {
             //     kind: TabBarContextMenuKind::New,
             //     handle: context_menu,
             // },
-            // tab_context_menu: cx.add_view(|cx| ContextMenu::new(pane_view_id, cx)),
+            // tab_context_menu: cx.build_view(|_| ContextMenu::new(pane_view_id, cx)),
             workspace,
             project,
             // can_drop: Rc::new(|_, _| true),
@@ -1963,52 +1963,6 @@ impl Render for Pane {
                 }),
             )
     }
-
-    // fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {
-    //     if !self.has_focus {
-    //         self.has_focus = true;
-    //         cx.emit(Event::Focus);
-    //         cx.notify();
-    //     }
-
-    //     self.toolbar.update(cx, |toolbar, cx| {
-    //         toolbar.focus_changed(true, cx);
-    //     });
-
-    //     if let Some(active_item) = self.active_item() {
-    //         if cx.is_self_focused() {
-    //             // Pane was focused directly. We need to either focus a view inside the active item,
-    //             // or focus the active item itself
-    //             if let Some(weak_last_focused_view) =
-    //                 self.last_focused_view_by_item.get(&active_item.id())
-    //             {
-    //                 if let Some(last_focused_view) = weak_last_focused_view.upgrade(cx) {
-    //                     cx.focus(&last_focused_view);
-    //                     return;
-    //                 } else {
-    //                     self.last_focused_view_by_item.remove(&active_item.id());
-    //                 }
-    //             }
-
-    //             cx.focus(active_item.as_any());
-    //         } else if focused != self.tab_bar_context_menu.handle {
-    //             self.last_focused_view_by_item
-    //                 .insert(active_item.id(), focused.downgrade());
-    //         }
-    //     }
-    // }
-
-    // fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
-    //     self.has_focus = false;
-    //     self.toolbar.update(cx, |toolbar, cx| {
-    //         toolbar.focus_changed(false, cx);
-    //     });
-    //     cx.notify();
-    // }
-
-    // fn update_keymap_context(&self, keymap: &mut KeymapContext, _: &AppContext) {
-    //     Self::reset_to_default_keymap_context(keymap);
-    // }
 }
 
 impl ItemNavHistory {
@@ -2166,98 +2120,6 @@ impl NavHistoryState {
     }
 }
 
-// pub struct PaneBackdrop<V> {
-//     child_view: usize,
-//     child: AnyElement<V>,
-// }
-
-// impl<V> PaneBackdrop<V> {
-//     pub fn new(pane_item_view: usize, child: AnyElement<V>) -> Self {
-//         PaneBackdrop {
-//             child,
-//             child_view: pane_item_view,
-//         }
-//     }
-// }
-
-// impl<V: 'static> Element<V> for PaneBackdrop<V> {
-//     type LayoutState = ();
-
-//     type PaintState = ();
-
-//     fn layout(
-//         &mut self,
-//         constraint: gpui::SizeConstraint,
-//         view: &mut V,
-//         cx: &mut ViewContext<V>,
-//     ) -> (Vector2F, Self::LayoutState) {
-//         let size = self.child.layout(constraint, view, cx);
-//         (size, ())
-//     }
-
-//     fn paint(
-//         &mut self,
-//         bounds: RectF,
-//         visible_bounds: RectF,
-//         _: &mut Self::LayoutState,
-//         view: &mut V,
-//         cx: &mut ViewContext<V>,
-//     ) -> Self::PaintState {
-//         let background = theme::current(cx).editor.background;
-
-//         let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
-
-//         cx.scene().push_quad(gpui::Quad {
-//             bounds: RectF::new(bounds.origin(), bounds.size()),
-//             background: Some(background),
-//             ..Default::default()
-//         });
-
-//         let child_view_id = self.child_view;
-//         cx.scene().push_mouse_region(
-//             MouseRegion::new::<Self>(child_view_id, 0, visible_bounds).on_down(
-//                 gpui::platform::MouseButton::Left,
-//                 move |_, _: &mut V, cx| {
-//                     let window = cx.window();
-//                     cx.app_context().focus(window, Some(child_view_id))
-//                 },
-//             ),
-//         );
-
-//         cx.scene().push_layer(Some(bounds));
-//         self.child.paint(bounds.origin(), visible_bounds, view, cx);
-//         cx.scene().pop_layer();
-//     }
-
-//     fn rect_for_text_range(
-//         &self,
-//         range_utf16: std::ops::Range<usize>,
-//         _bounds: RectF,
-//         _visible_bounds: RectF,
-//         _layout: &Self::LayoutState,
-//         _paint: &Self::PaintState,
-//         view: &V,
-//         cx: &gpui::ViewContext<V>,
-//     ) -> Option<RectF> {
-//         self.child.rect_for_text_range(range_utf16, view, cx)
-//     }
-
-//     fn debug(
-//         &self,
-//         _bounds: RectF,
-//         _layout: &Self::LayoutState,
-//         _paint: &Self::PaintState,
-//         view: &V,
-//         cx: &gpui::ViewContext<V>,
-//     ) -> serde_json::Value {
-//         gpui::json::json!({
-//             "type": "Pane Back Drop",
-//             "view": self.child_view,
-//             "child": self.child.debug(view, cx),
-//         })
-//     }
-// }
-
 fn dirty_message_for(buffer_path: Option<ProjectPath>) -> String {
     let path = buffer_path
         .as_ref()
@@ -2267,528 +2129,549 @@ fn dirty_message_for(buffer_path: Option<ProjectPath>) -> String {
     format!("{path} contains unsaved edits. Do you want to save it?")
 }
 
-// todo!("uncomment tests")
-// #[cfg(test)]
-// mod tests {
-//     use super::*;
-//     use crate::item::test::{TestItem, TestProjectItem};
-//     use gpui::TestAppContext;
-//     use project::FakeFs;
-//     use settings::SettingsStore;
-
-//     #[gpui::test]
-//     async fn test_remove_active_empty(cx: &mut TestAppContext) {
-//         init_test(cx);
-//         let fs = FakeFs::new(cx.background());
-
-//         let project = Project::test(fs, None, cx).await;
-//         let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
-//         let workspace = window.root(cx);
-//         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
-
-//         pane.update(cx, |pane, cx| {
-//             assert!(pane
-//                 .close_active_item(&CloseActiveItem { save_intent: None }, cx)
-//                 .is_none())
-//         });
-//     }
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::item::test::{TestItem, TestProjectItem};
+    use gpui::{TestAppContext, VisualTestContext};
+    use project::FakeFs;
+    use settings::SettingsStore;
+    use theme::LoadThemes;
+
+    #[gpui::test]
+    async fn test_remove_active_empty(cx: &mut TestAppContext) {
+        init_test(cx);
+        let fs = FakeFs::new(cx.executor());
+
+        let project = Project::test(fs, None, cx).await;
+        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+        let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
+
+        pane.update(cx, |pane, cx| {
+            assert!(pane
+                .close_active_item(&CloseActiveItem { save_intent: None }, cx)
+                .is_none())
+        });
+    }
 
-//     #[gpui::test]
-//     async fn test_add_item_with_new_item(cx: &mut TestAppContext) {
-//         cx.foreground().forbid_parking();
-//         init_test(cx);
-//         let fs = FakeFs::new(cx.background());
-
-//         let project = Project::test(fs, None, cx).await;
-//         let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
-//         let workspace = window.root(cx);
-//         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
-
-//         // 1. Add with a destination index
-//         //   a. Add before the active item
-//         set_labeled_items(&pane, ["A", "B*", "C"], cx);
-//         pane.update(cx, |pane, cx| {
-//             pane.add_item(
-//                 Box::new(cx.add_view(|_| TestItem::new().with_label("D"))),
-//                 false,
-//                 false,
-//                 Some(0),
-//                 cx,
-//             );
-//         });
-//         assert_item_labels(&pane, ["D*", "A", "B", "C"], cx);
-
-//         //   b. Add after the active item
-//         set_labeled_items(&pane, ["A", "B*", "C"], cx);
-//         pane.update(cx, |pane, cx| {
-//             pane.add_item(
-//                 Box::new(cx.add_view(|_| TestItem::new().with_label("D"))),
-//                 false,
-//                 false,
-//                 Some(2),
-//                 cx,
-//             );
-//         });
-//         assert_item_labels(&pane, ["A", "B", "D*", "C"], cx);
-
-//         //   c. Add at the end of the item list (including off the length)
-//         set_labeled_items(&pane, ["A", "B*", "C"], cx);
-//         pane.update(cx, |pane, cx| {
-//             pane.add_item(
-//                 Box::new(cx.add_view(|_| TestItem::new().with_label("D"))),
-//                 false,
-//                 false,
-//                 Some(5),
-//                 cx,
-//             );
-//         });
-//         assert_item_labels(&pane, ["A", "B", "C", "D*"], cx);
-
-//         // 2. Add without a destination index
-//         //   a. Add with active item at the start of the item list
-//         set_labeled_items(&pane, ["A*", "B", "C"], cx);
-//         pane.update(cx, |pane, cx| {
-//             pane.add_item(
-//                 Box::new(cx.add_view(|_| TestItem::new().with_label("D"))),
-//                 false,
-//                 false,
-//                 None,
-//                 cx,
-//             );
-//         });
-//         set_labeled_items(&pane, ["A", "D*", "B", "C"], cx);
-
-//         //   b. Add with active item at the end of the item list
-//         set_labeled_items(&pane, ["A", "B", "C*"], cx);
-//         pane.update(cx, |pane, cx| {
-//             pane.add_item(
-//                 Box::new(cx.add_view(|_| TestItem::new().with_label("D"))),
-//                 false,
-//                 false,
-//                 None,
-//                 cx,
-//             );
-//         });
-//         assert_item_labels(&pane, ["A", "B", "C", "D*"], cx);
-//     }
+    #[gpui::test]
+    async fn test_add_item_with_new_item(cx: &mut TestAppContext) {
+        init_test(cx);
+        let fs = FakeFs::new(cx.executor());
+
+        let project = Project::test(fs, None, cx).await;
+        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+        let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
+
+        // 1. Add with a destination index
+        //   a. Add before the active item
+        set_labeled_items(&pane, ["A", "B*", "C"], cx);
+        pane.update(cx, |pane, cx| {
+            pane.add_item(
+                Box::new(cx.build_view(|cx| TestItem::new(cx).with_label("D"))),
+                false,
+                false,
+                Some(0),
+                cx,
+            );
+        });
+        assert_item_labels(&pane, ["D*", "A", "B", "C"], cx);
+
+        //   b. Add after the active item
+        set_labeled_items(&pane, ["A", "B*", "C"], cx);
+        pane.update(cx, |pane, cx| {
+            pane.add_item(
+                Box::new(cx.build_view(|cx| TestItem::new(cx).with_label("D"))),
+                false,
+                false,
+                Some(2),
+                cx,
+            );
+        });
+        assert_item_labels(&pane, ["A", "B", "D*", "C"], cx);
+
+        //   c. Add at the end of the item list (including off the length)
+        set_labeled_items(&pane, ["A", "B*", "C"], cx);
+        pane.update(cx, |pane, cx| {
+            pane.add_item(
+                Box::new(cx.build_view(|cx| TestItem::new(cx).with_label("D"))),
+                false,
+                false,
+                Some(5),
+                cx,
+            );
+        });
+        assert_item_labels(&pane, ["A", "B", "C", "D*"], cx);
+
+        // 2. Add without a destination index
+        //   a. Add with active item at the start of the item list
+        set_labeled_items(&pane, ["A*", "B", "C"], cx);
+        pane.update(cx, |pane, cx| {
+            pane.add_item(
+                Box::new(cx.build_view(|cx| TestItem::new(cx).with_label("D"))),
+                false,
+                false,
+                None,
+                cx,
+            );
+        });
+        set_labeled_items(&pane, ["A", "D*", "B", "C"], cx);
+
+        //   b. Add with active item at the end of the item list
+        set_labeled_items(&pane, ["A", "B", "C*"], cx);
+        pane.update(cx, |pane, cx| {
+            pane.add_item(
+                Box::new(cx.build_view(|cx| TestItem::new(cx).with_label("D"))),
+                false,
+                false,
+                None,
+                cx,
+            );
+        });
+        assert_item_labels(&pane, ["A", "B", "C", "D*"], cx);
+    }
 
-//     #[gpui::test]
-//     async fn test_add_item_with_existing_item(cx: &mut TestAppContext) {
-//         cx.foreground().forbid_parking();
-//         init_test(cx);
-//         let fs = FakeFs::new(cx.background());
-
-//         let project = Project::test(fs, None, cx).await;
-//         let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
-//         let workspace = window.root(cx);
-//         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
-
-//         // 1. Add with a destination index
-//         //   1a. Add before the active item
-//         let [_, _, _, d] = set_labeled_items(&pane, ["A", "B*", "C", "D"], cx);
-//         pane.update(cx, |pane, cx| {
-//             pane.add_item(d, false, false, Some(0), cx);
-//         });
-//         assert_item_labels(&pane, ["D*", "A", "B", "C"], cx);
-
-//         //   1b. Add after the active item
-//         let [_, _, _, d] = set_labeled_items(&pane, ["A", "B*", "C", "D"], cx);
-//         pane.update(cx, |pane, cx| {
-//             pane.add_item(d, false, false, Some(2), cx);
-//         });
-//         assert_item_labels(&pane, ["A", "B", "D*", "C"], cx);
-
-//         //   1c. Add at the end of the item list (including off the length)
-//         let [a, _, _, _] = set_labeled_items(&pane, ["A", "B*", "C", "D"], cx);
-//         pane.update(cx, |pane, cx| {
-//             pane.add_item(a, false, false, Some(5), cx);
-//         });
-//         assert_item_labels(&pane, ["B", "C", "D", "A*"], cx);
-
-//         //   1d. Add same item to active index
-//         let [_, b, _] = set_labeled_items(&pane, ["A", "B*", "C"], cx);
-//         pane.update(cx, |pane, cx| {
-//             pane.add_item(b, false, false, Some(1), cx);
-//         });
-//         assert_item_labels(&pane, ["A", "B*", "C"], cx);
-
-//         //   1e. Add item to index after same item in last position
-//         let [_, _, c] = set_labeled_items(&pane, ["A", "B*", "C"], cx);
-//         pane.update(cx, |pane, cx| {
-//             pane.add_item(c, false, false, Some(2), cx);
-//         });
-//         assert_item_labels(&pane, ["A", "B", "C*"], cx);
-
-//         // 2. Add without a destination index
-//         //   2a. Add with active item at the start of the item list
-//         let [_, _, _, d] = set_labeled_items(&pane, ["A*", "B", "C", "D"], cx);
-//         pane.update(cx, |pane, cx| {
-//             pane.add_item(d, false, false, None, cx);
-//         });
-//         assert_item_labels(&pane, ["A", "D*", "B", "C"], cx);
-
-//         //   2b. Add with active item at the end of the item list
-//         let [a, _, _, _] = set_labeled_items(&pane, ["A", "B", "C", "D*"], cx);
-//         pane.update(cx, |pane, cx| {
-//             pane.add_item(a, false, false, None, cx);
-//         });
-//         assert_item_labels(&pane, ["B", "C", "D", "A*"], cx);
-
-//         //   2c. Add active item to active item at end of list
-//         let [_, _, c] = set_labeled_items(&pane, ["A", "B", "C*"], cx);
-//         pane.update(cx, |pane, cx| {
-//             pane.add_item(c, false, false, None, cx);
-//         });
-//         assert_item_labels(&pane, ["A", "B", "C*"], cx);
-
-//         //   2d. Add active item to active item at start of list
-//         let [a, _, _] = set_labeled_items(&pane, ["A*", "B", "C"], cx);
-//         pane.update(cx, |pane, cx| {
-//             pane.add_item(a, false, false, None, cx);
-//         });
-//         assert_item_labels(&pane, ["A*", "B", "C"], cx);
-//     }
+    #[gpui::test]
+    async fn test_add_item_with_existing_item(cx: &mut TestAppContext) {
+        init_test(cx);
+        let fs = FakeFs::new(cx.executor());
 
-//     #[gpui::test]
-//     async fn test_add_item_with_same_project_entries(cx: &mut TestAppContext) {
-//         cx.foreground().forbid_parking();
-//         init_test(cx);
-//         let fs = FakeFs::new(cx.background());
-
-//         let project = Project::test(fs, None, cx).await;
-//         let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
-//         let workspace = window.root(cx);
-//         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
-
-//         // singleton view
-//         pane.update(cx, |pane, cx| {
-//             let item = TestItem::new()
-//                 .with_singleton(true)
-//                 .with_label("buffer 1")
-//                 .with_project_items(&[TestProjectItem::new(1, "one.txt", cx)]);
-
-//             pane.add_item(Box::new(cx.add_view(|_| item)), false, false, None, cx);
-//         });
-//         assert_item_labels(&pane, ["buffer 1*"], cx);
-
-//         // new singleton view with the same project entry
-//         pane.update(cx, |pane, cx| {
-//             let item = TestItem::new()
-//                 .with_singleton(true)
-//                 .with_label("buffer 1")
-//                 .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]);
-
-//             pane.add_item(Box::new(cx.add_view(|_| item)), false, false, None, cx);
-//         });
-//         assert_item_labels(&pane, ["buffer 1*"], cx);
-
-//         // new singleton view with different project entry
-//         pane.update(cx, |pane, cx| {
-//             let item = TestItem::new()
-//                 .with_singleton(true)
-//                 .with_label("buffer 2")
-//                 .with_project_items(&[TestProjectItem::new(2, "2.txt", cx)]);
-//             pane.add_item(Box::new(cx.add_view(|_| item)), false, false, None, cx);
-//         });
-//         assert_item_labels(&pane, ["buffer 1", "buffer 2*"], cx);
-
-//         // new multibuffer view with the same project entry
-//         pane.update(cx, |pane, cx| {
-//             let item = TestItem::new()
-//                 .with_singleton(false)
-//                 .with_label("multibuffer 1")
-//                 .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]);
-
-//             pane.add_item(Box::new(cx.add_view(|_| item)), false, false, None, cx);
-//         });
-//         assert_item_labels(&pane, ["buffer 1", "buffer 2", "multibuffer 1*"], cx);
-
-//         // another multibuffer view with the same project entry
-//         pane.update(cx, |pane, cx| {
-//             let item = TestItem::new()
-//                 .with_singleton(false)
-//                 .with_label("multibuffer 1b")
-//                 .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]);
-
-//             pane.add_item(Box::new(cx.add_view(|_| item)), false, false, None, cx);
-//         });
-//         assert_item_labels(
-//             &pane,
-//             ["buffer 1", "buffer 2", "multibuffer 1", "multibuffer 1b*"],
-//             cx,
-//         );
-//     }
+        let project = Project::test(fs, None, cx).await;
+        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+        let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
 
-//     #[gpui::test]
-//     async fn test_remove_item_ordering(cx: &mut TestAppContext) {
-//         init_test(cx);
-//         let fs = FakeFs::new(cx.background());
-
-//         let project = Project::test(fs, None, cx).await;
-//         let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
-//         let workspace = window.root(cx);
-//         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
-
-//         add_labeled_item(&pane, "A", false, cx);
-//         add_labeled_item(&pane, "B", false, cx);
-//         add_labeled_item(&pane, "C", false, cx);
-//         add_labeled_item(&pane, "D", false, cx);
-//         assert_item_labels(&pane, ["A", "B", "C", "D*"], cx);
-
-//         pane.update(cx, |pane, cx| pane.activate_item(1, false, false, cx));
-//         add_labeled_item(&pane, "1", false, cx);
-//         assert_item_labels(&pane, ["A", "B", "1*", "C", "D"], cx);
-
-//         pane.update(cx, |pane, cx| {
-//             pane.close_active_item(&CloseActiveItem { save_intent: None }, cx)
-//         })
-//         .unwrap()
-//         .await
-//         .unwrap();
-//         assert_item_labels(&pane, ["A", "B*", "C", "D"], cx);
-
-//         pane.update(cx, |pane, cx| pane.activate_item(3, false, false, cx));
-//         assert_item_labels(&pane, ["A", "B", "C", "D*"], cx);
-
-//         pane.update(cx, |pane, cx| {
-//             pane.close_active_item(&CloseActiveItem { save_intent: None }, cx)
-//         })
-//         .unwrap()
-//         .await
-//         .unwrap();
-//         assert_item_labels(&pane, ["A", "B*", "C"], cx);
-
-//         pane.update(cx, |pane, cx| {
-//             pane.close_active_item(&CloseActiveItem { save_intent: None }, cx)
-//         })
-//         .unwrap()
-//         .await
-//         .unwrap();
-//         assert_item_labels(&pane, ["A", "C*"], cx);
-
-//         pane.update(cx, |pane, cx| {
-//             pane.close_active_item(&CloseActiveItem { save_intent: None }, cx)
-//         })
-//         .unwrap()
-//         .await
-//         .unwrap();
-//         assert_item_labels(&pane, ["A*"], cx);
-//     }
+        // 1. Add with a destination index
+        //   1a. Add before the active item
+        let [_, _, _, d] = set_labeled_items(&pane, ["A", "B*", "C", "D"], cx);
+        pane.update(cx, |pane, cx| {
+            pane.add_item(d, false, false, Some(0), cx);
+        });
+        assert_item_labels(&pane, ["D*", "A", "B", "C"], cx);
 
-//     #[gpui::test]
-//     async fn test_close_inactive_items(cx: &mut TestAppContext) {
-//         init_test(cx);
-//         let fs = FakeFs::new(cx.background());
-
-//         let project = Project::test(fs, None, cx).await;
-//         let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
-//         let workspace = window.root(cx);
-//         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
-
-//         set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx);
-
-//         pane.update(cx, |pane, cx| {
-//             pane.close_inactive_items(&CloseInactiveItems, cx)
-//         })
-//         .unwrap()
-//         .await
-//         .unwrap();
-//         assert_item_labels(&pane, ["C*"], cx);
-//     }
+        //   1b. Add after the active item
+        let [_, _, _, d] = set_labeled_items(&pane, ["A", "B*", "C", "D"], cx);
+        pane.update(cx, |pane, cx| {
+            pane.add_item(d, false, false, Some(2), cx);
+        });
+        assert_item_labels(&pane, ["A", "B", "D*", "C"], cx);
 
-//     #[gpui::test]
-//     async fn test_close_clean_items(cx: &mut TestAppContext) {
-//         init_test(cx);
-//         let fs = FakeFs::new(cx.background());
-
-//         let project = Project::test(fs, None, cx).await;
-//         let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
-//         let workspace = window.root(cx);
-//         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
-
-//         add_labeled_item(&pane, "A", true, cx);
-//         add_labeled_item(&pane, "B", false, cx);
-//         add_labeled_item(&pane, "C", true, cx);
-//         add_labeled_item(&pane, "D", false, cx);
-//         add_labeled_item(&pane, "E", false, cx);
-//         assert_item_labels(&pane, ["A^", "B", "C^", "D", "E*"], cx);
-
-//         pane.update(cx, |pane, cx| pane.close_clean_items(&CloseCleanItems, cx))
-//             .unwrap()
-//             .await
-//             .unwrap();
-//         assert_item_labels(&pane, ["A^", "C*^"], cx);
-//     }
+        //   1c. Add at the end of the item list (including off the length)
+        let [a, _, _, _] = set_labeled_items(&pane, ["A", "B*", "C", "D"], cx);
+        pane.update(cx, |pane, cx| {
+            pane.add_item(a, false, false, Some(5), cx);
+        });
+        assert_item_labels(&pane, ["B", "C", "D", "A*"], cx);
 
-//     #[gpui::test]
-//     async fn test_close_items_to_the_left(cx: &mut TestAppContext) {
-//         init_test(cx);
-//         let fs = FakeFs::new(cx.background());
-
-//         let project = Project::test(fs, None, cx).await;
-//         let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
-//         let workspace = window.root(cx);
-//         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
-
-//         set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx);
-
-//         pane.update(cx, |pane, cx| {
-//             pane.close_items_to_the_left(&CloseItemsToTheLeft, cx)
-//         })
-//         .unwrap()
-//         .await
-//         .unwrap();
-//         assert_item_labels(&pane, ["C*", "D", "E"], cx);
-//     }
+        //   1d. Add same item to active index
+        let [_, b, _] = set_labeled_items(&pane, ["A", "B*", "C"], cx);
+        pane.update(cx, |pane, cx| {
+            pane.add_item(b, false, false, Some(1), cx);
+        });
+        assert_item_labels(&pane, ["A", "B*", "C"], cx);
 
-//     #[gpui::test]
-//     async fn test_close_items_to_the_right(cx: &mut TestAppContext) {
-//         init_test(cx);
-//         let fs = FakeFs::new(cx.background());
-
-//         let project = Project::test(fs, None, cx).await;
-//         let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
-//         let workspace = window.root(cx);
-//         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
-
-//         set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx);
-
-//         pane.update(cx, |pane, cx| {
-//             pane.close_items_to_the_right(&CloseItemsToTheRight, cx)
-//         })
-//         .unwrap()
-//         .await
-//         .unwrap();
-//         assert_item_labels(&pane, ["A", "B", "C*"], cx);
-//     }
+        //   1e. Add item to index after same item in last position
+        let [_, _, c] = set_labeled_items(&pane, ["A", "B*", "C"], cx);
+        pane.update(cx, |pane, cx| {
+            pane.add_item(c, false, false, Some(2), cx);
+        });
+        assert_item_labels(&pane, ["A", "B", "C*"], cx);
 
-//     #[gpui::test]
-//     async fn test_close_all_items(cx: &mut TestAppContext) {
-//         init_test(cx);
-//         let fs = FakeFs::new(cx.background());
-
-//         let project = Project::test(fs, None, cx).await;
-//         let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
-//         let workspace = window.root(cx);
-//         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
-
-//         add_labeled_item(&pane, "A", false, cx);
-//         add_labeled_item(&pane, "B", false, cx);
-//         add_labeled_item(&pane, "C", false, cx);
-//         assert_item_labels(&pane, ["A", "B", "C*"], cx);
-
-//         pane.update(cx, |pane, cx| {
-//             pane.close_all_items(&CloseAllItems { save_intent: None }, cx)
-//         })
-//         .unwrap()
-//         .await
-//         .unwrap();
-//         assert_item_labels(&pane, [], cx);
-
-//         add_labeled_item(&pane, "A", true, cx);
-//         add_labeled_item(&pane, "B", true, cx);
-//         add_labeled_item(&pane, "C", true, cx);
-//         assert_item_labels(&pane, ["A^", "B^", "C*^"], cx);
-
-//         let save = pane
-//             .update(cx, |pane, cx| {
-//                 pane.close_all_items(&CloseAllItems { save_intent: None }, cx)
-//             })
-//             .unwrap();
-
-//         cx.foreground().run_until_parked();
-//         window.simulate_prompt_answer(2, cx);
-//         save.await.unwrap();
-//         assert_item_labels(&pane, [], cx);
-//     }
+        // 2. Add without a destination index
+        //   2a. Add with active item at the start of the item list
+        let [_, _, _, d] = set_labeled_items(&pane, ["A*", "B", "C", "D"], cx);
+        pane.update(cx, |pane, cx| {
+            pane.add_item(d, false, false, None, cx);
+        });
+        assert_item_labels(&pane, ["A", "D*", "B", "C"], cx);
 
-//     fn init_test(cx: &mut TestAppContext) {
-//         cx.update(|cx| {
-//             cx.set_global(SettingsStore::test(cx));
-//             theme::init((), cx);
-//             crate::init_settings(cx);
-//             Project::init_settings(cx);
-//         });
-//     }
+        //   2b. Add with active item at the end of the item list
+        let [a, _, _, _] = set_labeled_items(&pane, ["A", "B", "C", "D*"], cx);
+        pane.update(cx, |pane, cx| {
+            pane.add_item(a, false, false, None, cx);
+        });
+        assert_item_labels(&pane, ["B", "C", "D", "A*"], cx);
 
-//     fn add_labeled_item(
-//         pane: &ViewHandle<Pane>,
-//         label: &str,
-//         is_dirty: bool,
-//         cx: &mut TestAppContext,
-//     ) -> Box<ViewHandle<TestItem>> {
-//         pane.update(cx, |pane, cx| {
-//             let labeled_item =
-//                 Box::new(cx.add_view(|_| TestItem::new().with_label(label).with_dirty(is_dirty)));
-//             pane.add_item(labeled_item.clone(), false, false, None, cx);
-//             labeled_item
-//         })
-//     }
+        //   2c. Add active item to active item at end of list
+        let [_, _, c] = set_labeled_items(&pane, ["A", "B", "C*"], cx);
+        pane.update(cx, |pane, cx| {
+            pane.add_item(c, false, false, None, cx);
+        });
+        assert_item_labels(&pane, ["A", "B", "C*"], cx);
 
-//     fn set_labeled_items<const COUNT: usize>(
-//         pane: &ViewHandle<Pane>,
-//         labels: [&str; COUNT],
-//         cx: &mut TestAppContext,
-//     ) -> [Box<ViewHandle<TestItem>>; COUNT] {
-//         pane.update(cx, |pane, cx| {
-//             pane.items.clear();
-//             let mut active_item_index = 0;
-
-//             let mut index = 0;
-//             let items = labels.map(|mut label| {
-//                 if label.ends_with("*") {
-//                     label = label.trim_end_matches("*");
-//                     active_item_index = index;
-//                 }
-
-//                 let labeled_item = Box::new(cx.add_view(|_| TestItem::new().with_label(label)));
-//                 pane.add_item(labeled_item.clone(), false, false, None, cx);
-//                 index += 1;
-//                 labeled_item
-//             });
-
-//             pane.activate_item(active_item_index, false, false, cx);
-
-//             items
-//         })
-//     }
+        //   2d. Add active item to active item at start of list
+        let [a, _, _] = set_labeled_items(&pane, ["A*", "B", "C"], cx);
+        pane.update(cx, |pane, cx| {
+            pane.add_item(a, false, false, None, cx);
+        });
+        assert_item_labels(&pane, ["A*", "B", "C"], cx);
+    }
+
+    #[gpui::test]
+    async fn test_add_item_with_same_project_entries(cx: &mut TestAppContext) {
+        init_test(cx);
+        let fs = FakeFs::new(cx.executor());
+
+        let project = Project::test(fs, None, cx).await;
+        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+        let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
+
+        // singleton view
+        pane.update(cx, |pane, cx| {
+            pane.add_item(
+                Box::new(cx.build_view(|cx| {
+                    TestItem::new(cx)
+                        .with_singleton(true)
+                        .with_label("buffer 1")
+                        .with_project_items(&[TestProjectItem::new(1, "one.txt", cx)])
+                })),
+                false,
+                false,
+                None,
+                cx,
+            );
+        });
+        assert_item_labels(&pane, ["buffer 1*"], cx);
+
+        // new singleton view with the same project entry
+        pane.update(cx, |pane, cx| {
+            pane.add_item(
+                Box::new(cx.build_view(|cx| {
+                    TestItem::new(cx)
+                        .with_singleton(true)
+                        .with_label("buffer 1")
+                        .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
+                })),
+                false,
+                false,
+                None,
+                cx,
+            );
+        });
+        assert_item_labels(&pane, ["buffer 1*"], cx);
+
+        // new singleton view with different project entry
+        pane.update(cx, |pane, cx| {
+            pane.add_item(
+                Box::new(cx.build_view(|cx| {
+                    TestItem::new(cx)
+                        .with_singleton(true)
+                        .with_label("buffer 2")
+                        .with_project_items(&[TestProjectItem::new(2, "2.txt", cx)])
+                })),
+                false,
+                false,
+                None,
+                cx,
+            );
+        });
+        assert_item_labels(&pane, ["buffer 1", "buffer 2*"], cx);
+
+        // new multibuffer view with the same project entry
+        pane.update(cx, |pane, cx| {
+            pane.add_item(
+                Box::new(cx.build_view(|cx| {
+                    TestItem::new(cx)
+                        .with_singleton(false)
+                        .with_label("multibuffer 1")
+                        .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
+                })),
+                false,
+                false,
+                None,
+                cx,
+            );
+        });
+        assert_item_labels(&pane, ["buffer 1", "buffer 2", "multibuffer 1*"], cx);
+
+        // another multibuffer view with the same project entry
+        pane.update(cx, |pane, cx| {
+            pane.add_item(
+                Box::new(cx.build_view(|cx| {
+                    TestItem::new(cx)
+                        .with_singleton(false)
+                        .with_label("multibuffer 1b")
+                        .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
+                })),
+                false,
+                false,
+                None,
+                cx,
+            );
+        });
+        assert_item_labels(
+            &pane,
+            ["buffer 1", "buffer 2", "multibuffer 1", "multibuffer 1b*"],
+            cx,
+        );
+    }
 
-//     // Assert the item label, with the active item label suffixed with a '*'
-//     fn assert_item_labels<const COUNT: usize>(
-//         pane: &ViewHandle<Pane>,
-//         expected_states: [&str; COUNT],
-//         cx: &mut TestAppContext,
-//     ) {
-//         pane.read_with(cx, |pane, cx| {
-//             let actual_states = pane
-//                 .items
-//                 .iter()
-//                 .enumerate()
-//                 .map(|(ix, item)| {
-//                     let mut state = item
-//                         .as_any()
-//                         .downcast_ref::<TestItem>()
-//                         .unwrap()
-//                         .read(cx)
-//                         .label
-//                         .clone();
-//                     if ix == pane.active_item_index {
-//                         state.push('*');
-//                     }
-//                     if item.is_dirty(cx) {
-//                         state.push('^');
-//                     }
-//                     state
-//                 })
-//                 .collect::<Vec<_>>();
-
-//             assert_eq!(
-//                 actual_states, expected_states,
-//                 "pane items do not match expectation"
-//             );
-//         })
-//     }
-// }
+    #[gpui::test]
+    async fn test_remove_item_ordering(cx: &mut TestAppContext) {
+        init_test(cx);
+        let fs = FakeFs::new(cx.executor());
+
+        let project = Project::test(fs, None, cx).await;
+        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+        let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
+
+        add_labeled_item(&pane, "A", false, cx);
+        add_labeled_item(&pane, "B", false, cx);
+        add_labeled_item(&pane, "C", false, cx);
+        add_labeled_item(&pane, "D", false, cx);
+        assert_item_labels(&pane, ["A", "B", "C", "D*"], cx);
+
+        pane.update(cx, |pane, cx| pane.activate_item(1, false, false, cx));
+        add_labeled_item(&pane, "1", false, cx);
+        assert_item_labels(&pane, ["A", "B", "1*", "C", "D"], cx);
+
+        pane.update(cx, |pane, cx| {
+            pane.close_active_item(&CloseActiveItem { save_intent: None }, cx)
+        })
+        .unwrap()
+        .await
+        .unwrap();
+        assert_item_labels(&pane, ["A", "B*", "C", "D"], cx);
+
+        pane.update(cx, |pane, cx| pane.activate_item(3, false, false, cx));
+        assert_item_labels(&pane, ["A", "B", "C", "D*"], cx);
+
+        pane.update(cx, |pane, cx| {
+            pane.close_active_item(&CloseActiveItem { save_intent: None }, cx)
+        })
+        .unwrap()
+        .await
+        .unwrap();
+        assert_item_labels(&pane, ["A", "B*", "C"], cx);
+
+        pane.update(cx, |pane, cx| {
+            pane.close_active_item(&CloseActiveItem { save_intent: None }, cx)
+        })
+        .unwrap()
+        .await
+        .unwrap();
+        assert_item_labels(&pane, ["A", "C*"], cx);
+
+        pane.update(cx, |pane, cx| {
+            pane.close_active_item(&CloseActiveItem { save_intent: None }, cx)
+        })
+        .unwrap()
+        .await
+        .unwrap();
+        assert_item_labels(&pane, ["A*"], cx);
+    }
+
+    #[gpui::test]
+    async fn test_close_inactive_items(cx: &mut TestAppContext) {
+        init_test(cx);
+        let fs = FakeFs::new(cx.executor());
+
+        let project = Project::test(fs, None, cx).await;
+        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+        let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
+
+        set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx);
+
+        pane.update(cx, |pane, cx| {
+            pane.close_inactive_items(&CloseInactiveItems, cx)
+        })
+        .unwrap()
+        .await
+        .unwrap();
+        assert_item_labels(&pane, ["C*"], cx);
+    }
+
+    #[gpui::test]
+    async fn test_close_clean_items(cx: &mut TestAppContext) {
+        init_test(cx);
+        let fs = FakeFs::new(cx.executor());
+
+        let project = Project::test(fs, None, cx).await;
+        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+        let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
+
+        add_labeled_item(&pane, "A", true, cx);
+        add_labeled_item(&pane, "B", false, cx);
+        add_labeled_item(&pane, "C", true, cx);
+        add_labeled_item(&pane, "D", false, cx);
+        add_labeled_item(&pane, "E", false, cx);
+        assert_item_labels(&pane, ["A^", "B", "C^", "D", "E*"], cx);
+
+        pane.update(cx, |pane, cx| pane.close_clean_items(&CloseCleanItems, cx))
+            .unwrap()
+            .await
+            .unwrap();
+        assert_item_labels(&pane, ["A^", "C*^"], cx);
+    }
+
+    #[gpui::test]
+    async fn test_close_items_to_the_left(cx: &mut TestAppContext) {
+        init_test(cx);
+        let fs = FakeFs::new(cx.executor());
+
+        let project = Project::test(fs, None, cx).await;
+        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+        let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
+
+        set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx);
+
+        pane.update(cx, |pane, cx| {
+            pane.close_items_to_the_left(&CloseItemsToTheLeft, cx)
+        })
+        .unwrap()
+        .await
+        .unwrap();
+        assert_item_labels(&pane, ["C*", "D", "E"], cx);
+    }
+
+    #[gpui::test]
+    async fn test_close_items_to_the_right(cx: &mut TestAppContext) {
+        init_test(cx);
+        let fs = FakeFs::new(cx.executor());
+
+        let project = Project::test(fs, None, cx).await;
+        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+        let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
+
+        set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx);
+
+        pane.update(cx, |pane, cx| {
+            pane.close_items_to_the_right(&CloseItemsToTheRight, cx)
+        })
+        .unwrap()
+        .await
+        .unwrap();
+        assert_item_labels(&pane, ["A", "B", "C*"], cx);
+    }
+
+    #[gpui::test]
+    async fn test_close_all_items(cx: &mut TestAppContext) {
+        init_test(cx);
+        let fs = FakeFs::new(cx.executor());
+
+        let project = Project::test(fs, None, cx).await;
+        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+        let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
+
+        add_labeled_item(&pane, "A", false, cx);
+        add_labeled_item(&pane, "B", false, cx);
+        add_labeled_item(&pane, "C", false, cx);
+        assert_item_labels(&pane, ["A", "B", "C*"], cx);
+
+        pane.update(cx, |pane, cx| {
+            pane.close_all_items(&CloseAllItems { save_intent: None }, cx)
+        })
+        .unwrap()
+        .await
+        .unwrap();
+        assert_item_labels(&pane, [], cx);
+
+        add_labeled_item(&pane, "A", true, cx);
+        add_labeled_item(&pane, "B", true, cx);
+        add_labeled_item(&pane, "C", true, cx);
+        assert_item_labels(&pane, ["A^", "B^", "C*^"], cx);
+
+        let save = pane
+            .update(cx, |pane, cx| {
+                pane.close_all_items(&CloseAllItems { save_intent: None }, cx)
+            })
+            .unwrap();
+
+        cx.executor().run_until_parked();
+        cx.simulate_prompt_answer(2);
+        save.await.unwrap();
+        assert_item_labels(&pane, [], cx);
+    }
+
+    fn init_test(cx: &mut TestAppContext) {
+        cx.update(|cx| {
+            let settings_store = SettingsStore::test(cx);
+            cx.set_global(settings_store);
+            theme::init(LoadThemes::JustBase, cx);
+            crate::init_settings(cx);
+            Project::init_settings(cx);
+        });
+    }
+
+    fn add_labeled_item(
+        pane: &View<Pane>,
+        label: &str,
+        is_dirty: bool,
+        cx: &mut VisualTestContext,
+    ) -> Box<View<TestItem>> {
+        pane.update(cx, |pane, cx| {
+            let labeled_item = Box::new(
+                cx.build_view(|cx| TestItem::new(cx).with_label(label).with_dirty(is_dirty)),
+            );
+            pane.add_item(labeled_item.clone(), false, false, None, cx);
+            labeled_item
+        })
+    }
+
+    fn set_labeled_items<const COUNT: usize>(
+        pane: &View<Pane>,
+        labels: [&str; COUNT],
+        cx: &mut VisualTestContext,
+    ) -> [Box<View<TestItem>>; COUNT] {
+        pane.update(cx, |pane, cx| {
+            pane.items.clear();
+            let mut active_item_index = 0;
+
+            let mut index = 0;
+            let items = labels.map(|mut label| {
+                if label.ends_with("*") {
+                    label = label.trim_end_matches("*");
+                    active_item_index = index;
+                }
+
+                let labeled_item =
+                    Box::new(cx.build_view(|cx| TestItem::new(cx).with_label(label)));
+                pane.add_item(labeled_item.clone(), false, false, None, cx);
+                index += 1;
+                labeled_item
+            });
+
+            pane.activate_item(active_item_index, false, false, cx);
+
+            items
+        })
+    }
+
+    // Assert the item label, with the active item label suffixed with a '*'
+    fn assert_item_labels<const COUNT: usize>(
+        pane: &View<Pane>,
+        expected_states: [&str; COUNT],
+        cx: &mut VisualTestContext,
+    ) {
+        pane.update(cx, |pane, cx| {
+            let actual_states = pane
+                .items
+                .iter()
+                .enumerate()
+                .map(|(ix, item)| {
+                    let mut state = item
+                        .to_any()
+                        .downcast::<TestItem>()
+                        .unwrap()
+                        .read(cx)
+                        .label
+                        .clone();
+                    if ix == pane.active_item_index {
+                        state.push('*');
+                    }
+                    if item.is_dirty(cx) {
+                        state.push('^');
+                    }
+                    state
+                })
+                .collect::<Vec<_>>();
+
+            assert_eq!(
+                actual_states, expected_states,
+                "pane items do not match expectation"
+            );
+        })
+    }
+}
 
 impl Render for DraggedTab {
     type Element = <Tab as RenderOnce>::Rendered;

crates/workspace2/src/workspace2.rs 🔗

@@ -3556,6 +3556,8 @@ impl Render for Workspace {
             )
         };
 
+        let theme = cx.theme().clone();
+        let colors = theme.colors();
         cx.set_rem_size(ui_font_size);
 
         self.actions(div(), cx)
@@ -3568,10 +3570,10 @@ impl Render for Workspace {
             .gap_0()
             .justify_start()
             .items_start()
-            .text_color(cx.theme().colors().text)
-            .bg(cx.theme().colors().background)
+            .text_color(colors.text)
+            .bg(colors.background)
             .border()
-            .border_color(cx.theme().colors().border)
+            .border_color(colors.border)
             .children(self.titlebar_item.clone())
             .child(
                 div()
@@ -3584,7 +3586,7 @@ impl Render for Workspace {
                     .overflow_hidden()
                     .border_t()
                     .border_b()
-                    .border_color(cx.theme().colors().border)
+                    .border_color(colors.border)
                     .child(
                         canvas(cx.listener(|workspace, bounds, _| {
                             workspace.bounds = *bounds;
@@ -3623,13 +3625,15 @@ impl Render for Workspace {
                             .flex_row()
                             .h_full()
                             // Left Dock
-                            .child(
-                                div()
-                                    .flex()
-                                    .flex_none()
-                                    .overflow_hidden()
-                                    .child(self.left_dock.clone()),
-                            )
+                            .children(self.zoomed_position.ne(&Some(DockPosition::Left)).then(
+                                || {
+                                    div()
+                                        .flex()
+                                        .flex_none()
+                                        .overflow_hidden()
+                                        .child(self.left_dock.clone())
+                                },
+                            ))
                             // Panes
                             .child(
                                 div()
@@ -3646,154 +3650,48 @@ impl Render for Workspace {
                                         &self.app_state,
                                         cx,
                                     ))
-                                    .child(self.bottom_dock.clone()),
+                                    .children(
+                                        self.zoomed_position
+                                            .ne(&Some(DockPosition::Bottom))
+                                            .then(|| self.bottom_dock.clone()),
+                                    ),
                             )
                             // Right Dock
-                            .child(
-                                div()
-                                    .flex()
-                                    .flex_none()
-                                    .overflow_hidden()
-                                    .child(self.right_dock.clone()),
-                            ),
+                            .children(self.zoomed_position.ne(&Some(DockPosition::Right)).then(
+                                || {
+                                    div()
+                                        .flex()
+                                        .flex_none()
+                                        .overflow_hidden()
+                                        .child(self.right_dock.clone())
+                                },
+                            )),
                     )
-                    .children(self.render_notifications(cx)),
+                    .children(self.render_notifications(cx))
+                    .children(self.zoomed.as_ref().and_then(|view| {
+                        let zoomed_view = view.upgrade()?;
+                        let div = div()
+                            .z_index(1)
+                            .absolute()
+                            .overflow_hidden()
+                            .border_color(colors.border)
+                            .bg(colors.background)
+                            .child(zoomed_view)
+                            .inset_0()
+                            .shadow_lg();
+
+                        Some(match self.zoomed_position {
+                            Some(DockPosition::Left) => div.right_2().border_r(),
+                            Some(DockPosition::Right) => div.left_2().border_l(),
+                            Some(DockPosition::Bottom) => div.top_2().border_t(),
+                            None => div.top_2().bottom_2().left_2().right_2().border(),
+                        })
+                    })),
             )
             .child(self.status_bar.clone())
     }
 }
 
-// impl View for Workspace {
-
-//     fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
-//         let theme = theme::current(cx).clone();
-//         Stack::new()
-//             .with_child(
-//                 Flex::column()
-//                     .with_child(self.render_titlebar(&theme, cx))
-//                     .with_child(
-//                         Stack::new()
-//                             .with_child({
-//                                 let project = self.project.clone();
-//                                 Flex::row()
-//                                     .with_children(self.render_dock(DockPosition::Left, cx))
-//                                     .with_child(
-//                                         Flex::column()
-//                                             .with_child(
-//                                                 FlexItem::new(
-//                                                     self.center.render(
-//                                                         &project,
-//                                                         &theme,
-//                                                         &self.follower_states,
-//                                                         self.active_call(),
-//                                                         self.active_pane(),
-//                                                         self.zoomed
-//                                                             .as_ref()
-//                                                             .and_then(|zoomed| zoomed.upgrade(cx))
-//                                                             .as_ref(),
-//                                                         &self.app_state,
-//                                                         cx,
-//                                                     ),
-//                                                 )
-//                                                 .flex(1., true),
-//                                             )
-//                                             .with_children(
-//                                                 self.render_dock(DockPosition::Bottom, cx),
-//                                             )
-//                                             .flex(1., true),
-//                                     )
-//                                     .with_children(self.render_dock(DockPosition::Right, cx))
-//                             })
-//                             .with_child(Overlay::new(
-//                                 Stack::new()
-//                                     .with_children(self.zoomed.as_ref().and_then(|zoomed| {
-//                                         enum ZoomBackground {}
-//                                         let zoomed = zoomed.upgrade(cx)?;
-
-//                                         let mut foreground_style =
-//                                             theme.workspace.zoomed_pane_foreground;
-//                                         if let Some(zoomed_dock_position) = self.zoomed_position {
-//                                             foreground_style =
-//                                                 theme.workspace.zoomed_panel_foreground;
-//                                             let margin = foreground_style.margin.top;
-//                                             let border = foreground_style.border.top;
-
-//                                             // Only include a margin and border on the opposite side.
-//                                             foreground_style.margin.top = 0.;
-//                                             foreground_style.margin.left = 0.;
-//                                             foreground_style.margin.bottom = 0.;
-//                                             foreground_style.margin.right = 0.;
-//                                             foreground_style.border.top = false;
-//                                             foreground_style.border.left = false;
-//                                             foreground_style.border.bottom = false;
-//                                             foreground_style.border.right = false;
-//                                             match zoomed_dock_position {
-//                                                 DockPosition::Left => {
-//                                                     foreground_style.margin.right = margin;
-//                                                     foreground_style.border.right = border;
-//                                                 }
-//                                                 DockPosition::Right => {
-//                                                     foreground_style.margin.left = margin;
-//                                                     foreground_style.border.left = border;
-//                                                 }
-//                                                 DockPosition::Bottom => {
-//                                                     foreground_style.margin.top = margin;
-//                                                     foreground_style.border.top = border;
-//                                                 }
-//                                             }
-//                                         }
-
-//                                         Some(
-//                                             ChildView::new(&zoomed, cx)
-//                                                 .contained()
-//                                                 .with_style(foreground_style)
-//                                                 .aligned()
-//                                                 .contained()
-//                                                 .with_style(theme.workspace.zoomed_background)
-//                                                 .mouse::<ZoomBackground>(0)
-//                                                 .capture_all()
-//                                                 .on_down(
-//                                                     MouseButton::Left,
-//                                                     |_, this: &mut Self, cx| {
-//                                                         this.zoom_out(cx);
-//                                                     },
-//                                                 ),
-//                                         )
-//                                     }))
-//                                     .with_children(self.modal.as_ref().map(|modal| {
-//                                         // Prevent clicks within the modal from falling
-//                                         // through to the rest of the workspace.
-//                                         enum ModalBackground {}
-//                                         MouseEventHandler::new::<ModalBackground, _>(
-//                                             0,
-//                                             cx,
-//                                             |_, cx| ChildView::new(modal.view.as_any(), cx),
-//                                         )
-//                                         .on_click(MouseButton::Left, |_, _, _| {})
-//                                         .contained()
-//                                         .with_style(theme.workspace.modal)
-//                                         .aligned()
-//                                         .top()
-//                                     }))
-//                                     .with_children(self.render_notifications(&theme.workspace, cx)),
-//                             ))
-//                             .provide_resize_bounds::<WorkspaceBounds>()
-//                             .flex(1.0, true),
-//                     )
-//                     .with_child(ChildView::new(&self.status_bar, cx))
-//                     .contained()
-//                     .with_background_color(theme.workspace.background),
-//             )
-//             .with_children(DragAndDrop::render(cx))
-//             .with_children(self.render_disconnected_overlay(cx))
-//             .into_any_named("workspace")
-//     }
-
-//     fn modifiers_changed(&mut self, e: &ModifiersChangedEvent, cx: &mut ViewContext<Self>) -> bool {
-//         DragAndDrop::<Workspace>::update_modifiers(e.modifiers, cx)
-//     }
-// }
-
 impl WorkspaceStore {
     pub fn new(client: Arc<Client>, cx: &mut ModelContext<Self>) -> Self {
         Self {