workspace: Fix tab reordering regression (#46872) (cherry-pick to preview) (#46878)

zed-zippy[bot] , Yaroslav Yenkala , and Smit Barmase created

Cherry-pick of #46872 to preview

----
Closes [#46864](https://github.com/zed-industries/zed/issues/46864)
Initial PR: https://github.com/zed-industries/zed/pull/46573

## Summary

Fix a regression where dragging a tab onto another tab would place it at
the end of the tab bar instead of at the target position.

The issue was in the `on_drop` handler: it used `this.items.len()`
(evaluated at drop time) instead of the captured `ix` index (the
position of the tab being dropped onto).

Release Notes:

- Fixed tab reordering not working correctly when dragging tabs

## Video after fix:

- when 'show_pinned_tabs_in_separate_row' is false:



https://github.com/user-attachments/assets/1ede0ce5-1161-4209-83f4-33a07572782a

- when 'show_pinned_tabs_in_separate_row' is true:



https://github.com/user-attachments/assets/d56c0e59-8372-41d4-973b-32a4895ca729

---------

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>

Co-authored-by: Yaroslav Yenkala <yaroslavrick@gmail.com>
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>

Change summary

crates/workspace/src/pane.rs | 53 +++++++++++++++++++++++++++++++++++++
1 file changed, 52 insertions(+), 1 deletion(-)

Detailed changes

crates/workspace/src/pane.rs 🔗

@@ -2765,7 +2765,7 @@ impl Pane {
             .on_drop(
                 cx.listener(move |this, dragged_tab: &DraggedTab, window, cx| {
                     this.drag_split_direction = None;
-                    this.handle_tab_drop(dragged_tab, this.items.len(), window, cx)
+                    this.handle_tab_drop(dragged_tab, ix, window, cx)
                 }),
             )
             .on_drop(
@@ -6225,6 +6225,57 @@ mod tests {
         assert_item_labels(&pane_a, ["C*", "A", "B"], cx);
     }
 
+    #[gpui::test]
+    async fn test_drag_tab_to_middle_tab_with_mouse_events(cx: &mut TestAppContext) {
+        use gpui::{Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent};
+
+        init_test(cx);
+        let fs = FakeFs::new(cx.executor());
+
+        let project = Project::test(fs, None, cx).await;
+        let (workspace, cx) =
+            cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, 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);
+        cx.run_until_parked();
+
+        let tab_a_bounds = cx
+            .debug_bounds("TAB-0")
+            .expect("Tab A (index 0) should have debug bounds");
+        let tab_c_bounds = cx
+            .debug_bounds("TAB-2")
+            .expect("Tab C (index 2) should have debug bounds");
+
+        cx.simulate_event(MouseDownEvent {
+            position: tab_a_bounds.center(),
+            button: MouseButton::Left,
+            modifiers: Modifiers::default(),
+            click_count: 1,
+            first_mouse: false,
+        });
+        cx.run_until_parked();
+        cx.simulate_event(MouseMoveEvent {
+            position: tab_c_bounds.center(),
+            pressed_button: Some(MouseButton::Left),
+            modifiers: Modifiers::default(),
+        });
+        cx.run_until_parked();
+        cx.simulate_event(MouseUpEvent {
+            position: tab_c_bounds.center(),
+            button: MouseButton::Left,
+            modifiers: Modifiers::default(),
+            click_count: 1,
+        });
+        cx.run_until_parked();
+
+        assert_item_labels(&pane, ["B", "C", "A*", "D"], cx);
+    }
+
     #[gpui::test]
     async fn test_add_item_with_new_item(cx: &mut TestAppContext) {
         init_test(cx);