From 9d6208b2091061e88105a74d2b2993223381a7c8 Mon Sep 17 00:00:00 2001 From: Austin Cummings Date: Fri, 27 Mar 2026 11:52:11 -0700 Subject: [PATCH] Add check to prevent closing pinned items (#50333) Pinned items should not be closed when the close action is triggered. Closes #50309 Before you mark this PR as ready for review, make sure that you have: - [x] Added a solid test coverage and/or screenshots from doing manual testing - [x] Done a self-review taking into account security and performance aspects - [x] Aligned any UI changes with the [UI checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) Release Notes: - Fixed a bug where middle-clicking a pinned tab would close it. --- crates/workspace/src/pane.rs | 75 +++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 113e5af6424904655d21d83591c8e9a361df3b50..ca8e0dce44dee6f7da3c0e3f20083645974da4b0 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -2849,7 +2849,7 @@ impl Pane { })) .on_aux_click( cx.listener(move |pane: &mut Self, event: &ClickEvent, window, cx| { - if !event.is_middle_click() { + if !event.is_middle_click() || is_pinned { return; } @@ -6858,6 +6858,79 @@ mod tests { assert_item_labels(&pane, ["A!", "B!", "D", "E", "C*", "F"], cx); } + #[gpui::test] + async fn test_middle_click_pinned_tab_does_not_close(cx: &mut TestAppContext) { + use gpui::{Modifiers, MouseButton, MouseDownEvent, 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()); + + let item_a = add_labeled_item(&pane, "A", false, cx); + add_labeled_item(&pane, "B", false, cx); + + pane.update_in(cx, |pane, window, cx| { + pane.pin_tab_at( + pane.index_for_item_id(item_a.item_id()).unwrap(), + window, + cx, + ); + }); + assert_item_labels(&pane, ["A!", "B*"], cx); + cx.run_until_parked(); + + let tab_a_bounds = cx + .debug_bounds("TAB-0") + .expect("Tab A (index 1) should have debug bounds"); + let tab_b_bounds = cx + .debug_bounds("TAB-1") + .expect("Tab B (index 2) should have debug bounds"); + + cx.simulate_event(MouseDownEvent { + position: tab_a_bounds.center(), + button: MouseButton::Middle, + modifiers: Modifiers::default(), + click_count: 1, + first_mouse: false, + }); + + cx.run_until_parked(); + + cx.simulate_event(MouseUpEvent { + position: tab_a_bounds.center(), + button: MouseButton::Middle, + modifiers: Modifiers::default(), + click_count: 1, + }); + + cx.run_until_parked(); + + cx.simulate_event(MouseDownEvent { + position: tab_b_bounds.center(), + button: MouseButton::Middle, + modifiers: Modifiers::default(), + click_count: 1, + first_mouse: false, + }); + + cx.run_until_parked(); + + cx.simulate_event(MouseUpEvent { + position: tab_b_bounds.center(), + button: MouseButton::Middle, + modifiers: Modifiers::default(), + click_count: 1, + }); + + cx.run_until_parked(); + + assert_item_labels(&pane, ["A*!"], cx); + } + #[gpui::test] async fn test_add_item_with_new_item(cx: &mut TestAppContext) { init_test(cx);