Add action to move active item into the dock

Julia and Kay Simmons created

Co-Authored-By: Kay Simmons <kay@zed.dev>

Change summary

assets/keymaps/default.json       |  6 +++++
crates/workspace/src/dock.rs      | 38 +++++++++++++++++++++++++++++---
crates/workspace/src/pane.rs      | 12 ++++++---
crates/workspace/src/workspace.rs | 10 ++++----
crates/zed/src/zed.rs             |  6 ++--
5 files changed, 56 insertions(+), 16 deletions(-)

Detailed changes

assets/keymaps/default.json 🔗

@@ -431,6 +431,12 @@
             "shift-escape": "dock::HideDock"
         }
     },
+    {
+        "context": "Pane",
+        "bindings": {
+            "cmd-escape": "dock::MoveActiveItemToDock"
+        }
+    },
     {
         "context": "ProjectPanel",
         "bindings": {

crates/workspace/src/dock.rs 🔗

@@ -24,7 +24,8 @@ actions!(
         HideDock,
         AnchorDockRight,
         AnchorDockBottom,
-        ExpandDock
+        ExpandDock,
+        MoveActiveItemToDock,
     ]
 );
 impl_internal_actions!(dock, [MoveDock, AddDefaultItemToDock]);
@@ -48,6 +49,30 @@ pub fn init(cx: &mut MutableAppContext) {
             Dock::move_dock(workspace, &MoveDock(DockAnchor::Expanded), cx)
         },
     );
+    cx.add_action(
+        |workspace: &mut Workspace, _: &MoveActiveItemToDock, cx: &mut ViewContext<Workspace>| {
+            if let Some(active_item) = workspace.active_item(cx) {
+                let item_id = active_item.id();
+
+                let from = workspace.active_pane();
+                let to = workspace.dock_pane();
+                if from.id() == to.id() {
+                    return;
+                }
+
+                let destination_index = to.read(cx).items_len() + 1;
+
+                Pane::move_item(
+                    workspace,
+                    from.clone(),
+                    to.clone(),
+                    item_id,
+                    destination_index,
+                    cx,
+                );
+            }
+        },
+    );
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
@@ -338,7 +363,8 @@ impl View for ToggleDockButton {
             return Empty::new().boxed();
         }
 
-        let dock_position = workspace.unwrap().read(cx).dock.position;
+        let workspace = workspace.unwrap();
+        let dock_position = workspace.read(cx).dock.position;
 
         let theme = cx.global::<Settings>().theme.clone();
         let button = MouseEventHandler::<Self>::new(0, cx, {
@@ -361,8 +387,12 @@ impl View for ToggleDockButton {
                     .boxed()
             }
         })
-        .with_cursor_style(CursorStyle::PointingHand);
-        .on_
+        .with_cursor_style(CursorStyle::PointingHand)
+        .on_up(MouseButton::Left, move |_, cx| {
+            let dock_pane = workspace.read(cx.app).dock_pane();
+            let drop_index = dock_pane.read(cx.app).items_len() + 1;
+            Pane::handle_dropped_item(&dock_pane.downgrade(), drop_index, cx);
+        });
 
         if dock_position.is_visible() {
             button

crates/workspace/src/pane.rs 🔗

@@ -575,6 +575,10 @@ impl Pane {
         }
     }
 
+    pub fn items_len(&self) -> usize {
+        self.items.len()
+    }
+
     pub fn items(&self) -> impl Iterator<Item = &Box<dyn ItemHandle>> {
         self.items.iter()
     }
@@ -943,11 +947,11 @@ impl Pane {
         }
     }
 
-    fn move_item(
+    pub fn move_item(
         workspace: &mut Workspace,
         from: ViewHandle<Pane>,
         to: ViewHandle<Pane>,
-        item_to_move: usize,
+        item_id_to_move: usize,
         destination_index: usize,
         cx: &mut ViewContext<Workspace>,
     ) {
@@ -955,7 +959,7 @@ impl Pane {
             .read(cx)
             .items()
             .enumerate()
-            .find(|(_, item_handle)| item_handle.id() == item_to_move);
+            .find(|(_, item_handle)| item_handle.id() == item_id_to_move);
 
         if item_to_move.is_none() {
             log::warn!("Tried to move item handle which was not in `from` pane. Maybe tab was closed during drop");
@@ -1321,7 +1325,7 @@ impl Pane {
         tab.constrained().with_height(tab_style.height).boxed()
     }
 
-    fn handle_dropped_item(pane: &WeakViewHandle<Pane>, index: usize, cx: &mut EventContext) {
+    pub fn handle_dropped_item(pane: &WeakViewHandle<Pane>, index: usize, cx: &mut EventContext) {
         if let Some((_, dragged_item)) = cx
             .global::<DragAndDrop<Workspace>>()
             .currently_dragged::<DraggedItem>(cx.window_id)

crates/workspace/src/workspace.rs 🔗

@@ -3175,7 +3175,7 @@ mod tests {
 
         cx.foreground().run_until_parked();
         pane.read_with(cx, |pane, _| {
-            assert_eq!(pane.items().count(), 4);
+            assert_eq!(pane.items_len(), 4);
             assert_eq!(pane.active_item().unwrap().id(), item1.id());
         });
 
@@ -3185,7 +3185,7 @@ mod tests {
             assert_eq!(item1.read(cx).save_count, 1);
             assert_eq!(item1.read(cx).save_as_count, 0);
             assert_eq!(item1.read(cx).reload_count, 0);
-            assert_eq!(pane.items().count(), 3);
+            assert_eq!(pane.items_len(), 3);
             assert_eq!(pane.active_item().unwrap().id(), item3.id());
         });
 
@@ -3195,7 +3195,7 @@ mod tests {
             assert_eq!(item3.read(cx).save_count, 0);
             assert_eq!(item3.read(cx).save_as_count, 0);
             assert_eq!(item3.read(cx).reload_count, 1);
-            assert_eq!(pane.items().count(), 2);
+            assert_eq!(pane.items_len(), 2);
             assert_eq!(pane.active_item().unwrap().id(), item4.id());
         });
 
@@ -3207,7 +3207,7 @@ mod tests {
             assert_eq!(item4.read(cx).save_count, 0);
             assert_eq!(item4.read(cx).save_as_count, 1);
             assert_eq!(item4.read(cx).reload_count, 0);
-            assert_eq!(pane.items().count(), 1);
+            assert_eq!(pane.items_len(), 1);
             assert_eq!(pane.active_item().unwrap().id(), item2.id());
         });
     }
@@ -3309,7 +3309,7 @@ mod tests {
         cx.foreground().run_until_parked();
         close.await.unwrap();
         left_pane.read_with(cx, |pane, _| {
-            assert_eq!(pane.items().count(), 0);
+            assert_eq!(pane.items_len(), 0);
         });
     }
 

crates/zed/src/zed.rs 🔗

@@ -811,7 +811,7 @@ mod tests {
                 pane.active_item().unwrap().project_path(cx),
                 Some(file1.clone())
             );
-            assert_eq!(pane.items().count(), 1);
+            assert_eq!(pane.items_len(), 1);
         });
 
         // Open the second entry
@@ -825,7 +825,7 @@ mod tests {
                 pane.active_item().unwrap().project_path(cx),
                 Some(file2.clone())
             );
-            assert_eq!(pane.items().count(), 2);
+            assert_eq!(pane.items_len(), 2);
         });
 
         // Open the first entry again. The existing pane item is activated.
@@ -841,7 +841,7 @@ mod tests {
                 pane.active_item().unwrap().project_path(cx),
                 Some(file1.clone())
             );
-            assert_eq!(pane.items().count(), 2);
+            assert_eq!(pane.items_len(), 2);
         });
 
         // Split the pane with the first entry, then open the second entry again.