pane: Fix rough edges around pinning of dropped project entries (#20722)

Piotr Osiewicz created

Closes #20485

Release Notes:

- Fixed quirks around dropping project entries into tab bar which
might've led to tabs being pinned sometimes.

Change summary

crates/workspace/src/pane.rs      | 25 ++++++++++++++++++-------
crates/workspace/src/workspace.rs | 19 +++++++++++++++++--
2 files changed, 35 insertions(+), 9 deletions(-)

Detailed changes

crates/workspace/src/pane.rs 🔗

@@ -771,6 +771,7 @@ impl Pane {
         project_entry_id: Option<ProjectEntryId>,
         focus_item: bool,
         allow_preview: bool,
+        suggested_position: Option<usize>,
         cx: &mut ViewContext<Self>,
         build_item: impl FnOnce(&mut ViewContext<Pane>) -> Box<dyn ItemHandle>,
     ) -> Box<dyn ItemHandle> {
@@ -806,7 +807,7 @@ impl Pane {
             let destination_index = if allow_preview {
                 self.close_current_preview_item(cx)
             } else {
-                None
+                suggested_position
             };
 
             let new_item = build_item(cx);
@@ -1822,7 +1823,7 @@ impl Pane {
     fn pin_tab_at(&mut self, ix: usize, cx: &mut ViewContext<'_, Self>) {
         maybe!({
             let pane = cx.view().clone();
-            let destination_index = self.pinned_tab_count;
+            let destination_index = self.pinned_tab_count.min(ix);
             self.pinned_tab_count += 1;
             let id = self.item_for_index(ix)?.item_id();
 
@@ -1967,7 +1968,7 @@ impl Pane {
             }))
             .on_drop(cx.listener(move |this, selection: &DraggedSelection, cx| {
                 this.drag_split_direction = None;
-                this.handle_dragged_selection_drop(selection, cx)
+                this.handle_dragged_selection_drop(selection, Some(ix), cx)
             }))
             .on_drop(cx.listener(move |this, paths, cx| {
                 this.drag_split_direction = None;
@@ -2260,7 +2261,7 @@ impl Pane {
             .zip(tab_details(&self.items, cx))
             .map(|((ix, item), detail)| self.render_tab(ix, &**item, detail, &focus_handle, cx))
             .collect::<Vec<_>>();
-
+        let tab_count = tab_items.len();
         let unpinned_tabs = tab_items.split_off(self.pinned_tab_count);
         let pinned_tabs = tab_items;
         TabBar::new("tab_bar")
@@ -2316,6 +2317,7 @@ impl Pane {
                                 this.drag_split_direction = None;
                                 this.handle_project_entry_drop(
                                     &selection.active_selection.entry_id,
+                                    Some(tab_count),
                                     cx,
                                 )
                             }))
@@ -2463,6 +2465,7 @@ impl Pane {
     fn handle_dragged_selection_drop(
         &mut self,
         dragged_selection: &DraggedSelection,
+        dragged_onto: Option<usize>,
         cx: &mut ViewContext<'_, Self>,
     ) {
         if let Some(custom_drop_handle) = self.custom_drop_handle.clone() {
@@ -2470,12 +2473,17 @@ impl Pane {
                 return;
             }
         }
-        self.handle_project_entry_drop(&dragged_selection.active_selection.entry_id, cx);
+        self.handle_project_entry_drop(
+            &dragged_selection.active_selection.entry_id,
+            dragged_onto,
+            cx,
+        );
     }
 
     fn handle_project_entry_drop(
         &mut self,
         project_entry_id: &ProjectEntryId,
+        target: Option<usize>,
         cx: &mut ViewContext<'_, Self>,
     ) {
         if let Some(custom_drop_handle) = self.custom_drop_handle.clone() {
@@ -2510,6 +2518,7 @@ impl Pane {
                                                 project_entry_id,
                                                 true,
                                                 false,
+                                                target,
                                                 cx,
                                                 build_item,
                                             )
@@ -2523,7 +2532,9 @@ impl Pane {
                                         else {
                                             return;
                                         };
-                                        if !this.is_tab_pinned(index) {
+
+                                        if target.map_or(false, |target| this.is_tab_pinned(target))
+                                        {
                                             this.pin_tab_at(index, cx);
                                         }
                                     })
@@ -2823,7 +2834,7 @@ impl Render for Pane {
                                 this.handle_tab_drop(dragged_tab, this.active_item_index(), cx)
                             }))
                             .on_drop(cx.listener(move |this, selection: &DraggedSelection, cx| {
-                                this.handle_dragged_selection_drop(selection, cx)
+                                this.handle_dragged_selection_drop(selection, None, cx)
                             }))
                             .on_drop(cx.listener(move |this, paths, cx| {
                                 this.handle_external_paths_drop(paths, cx)

crates/workspace/src/workspace.rs 🔗

@@ -1406,6 +1406,7 @@ impl Workspace {
                                 project_entry_id,
                                 true,
                                 entry.is_preview,
+                                None,
                                 cx,
                                 build_item,
                             );
@@ -2621,7 +2622,14 @@ impl Workspace {
         cx.spawn(move |mut cx| async move {
             let (project_entry_id, build_item) = task.await?;
             pane.update(&mut cx, |pane, cx| {
-                pane.open_item(project_entry_id, focus_item, allow_preview, cx, build_item)
+                pane.open_item(
+                    project_entry_id,
+                    focus_item,
+                    allow_preview,
+                    None,
+                    cx,
+                    build_item,
+                )
             })
         })
     }
@@ -2662,7 +2670,14 @@ impl Workspace {
                 let new_pane =
                     this.split_pane(pane, split_direction.unwrap_or(SplitDirection::Right), cx);
                 new_pane.update(cx, |new_pane, cx| {
-                    Some(new_pane.open_item(project_entry_id, true, allow_preview, cx, build_item))
+                    Some(new_pane.open_item(
+                        project_entry_id,
+                        true,
+                        allow_preview,
+                        None,
+                        cx,
+                        build_item,
+                    ))
                 })
             })
             .map(|option| option.ok_or_else(|| anyhow!("pane was dropped")))?