From 33b0b39e4047cb49597660e0dafebabd6cd5ec6f Mon Sep 17 00:00:00 2001 From: Austin Cummings Date: Mon, 17 Aug 2026 22:28:04 +0000 Subject: [PATCH] workspace: Reset all configured dock panels (#62552) Closes #57388. When a dock is listed in `resize_all_panels_in_dock`, reset its compatible panels to the active panel's default size. This applies to the reset actions and resize-handle double-clicks. Docks not listed in the setting continue to reset only the active panel. Adds regression coverage for fixed panels with different defaults, flexible panels, and active-panel-only resets. Release Notes: - Fixed dock size reset commands only resetting the active panel when `resize_all_panels_in_dock` is enabled. --------- Co-authored-by: dino --- crates/workspace/src/dock.rs | 65 ++++++- crates/workspace/src/workspace.rs | 302 +++++++++++++++++++++++++----- 2 files changed, 314 insertions(+), 53 deletions(-) diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 649cb9ccb2..a07a9035a2 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -546,6 +546,11 @@ impl Dock { .and_then(|index| self.panel_entries.get(index)) } + fn active_panel_entry_mut(&mut self) -> Option<&mut PanelEntry> { + self.active_panel_index + .and_then(|index| self.panel_entries.get_mut(index)) + } + pub fn active_panel_index(&self) -> Option { self.active_panel_index } @@ -999,18 +1004,17 @@ impl Dock { cx.notify(); } - pub fn resize_active_panel( + fn resize_active_panel( &mut self, size: Option, flex: Option, window: &mut Window, cx: &mut Context, ) { - if let Some(index) = self.active_panel_index - && let Some(entry) = self.panel_entries.get_mut(index) - { + let position = self.position; + if let Some(entry) = self.active_panel_entry_mut() { let (panel_key, size_state) = - resize_panel_entry(self.position, entry, size, flex, window, cx); + resize_panel_entry(position, entry, size, flex, window, cx); let workspace = self.workspace.clone(); cx.defer(move |cx| { @@ -1024,7 +1028,54 @@ impl Dock { } } - pub fn resize_all_panels( + /// Resizes the active panel and, when this dock is included in + /// `resize_all_panels_in_dock`, all panels using the same sizing mode. + pub fn resize_panel_sizes( + &mut self, + size: Option, + flex: Option, + window: &mut Window, + cx: &mut Context, + ) { + if self.should_resize_all_panels(cx) { + self.resize_all_panels(size, flex, window, cx); + } else { + self.resize_active_panel(size, flex, window, cx); + } + } + + /// Resets the active panel and, when this dock is included in + /// `resize_all_panels_in_dock`, all panels using the same sizing mode. + pub fn reset_panel_sizes(&mut self, window: &mut Window, cx: &mut Context) { + if self.should_resize_all_panels(cx) { + self.reset_all_panel_sizes(window, cx); + } else { + self.reset_active_panel_size(window, cx); + } + } + + fn reset_active_panel_size(&mut self, window: &mut Window, cx: &mut Context) { + let state = PanelSizeState::default(); + self.resize_active_panel(state.size, state.flex, window, cx); + } + + fn reset_all_panel_sizes(&mut self, window: &mut Window, cx: &mut Context) { + let Some(active_entry) = self.active_panel_entry() else { + return; + }; + let size = + (!panel_uses_flexible_width(self.position, active_entry.panel.as_ref(), window, cx)) + .then(|| active_entry.panel.default_size(window, cx)); + self.resize_all_panels(size, None, window, cx); + } + + fn should_resize_all_panels(&self, cx: &App) -> bool { + WorkspaceSettings::get_global(cx) + .resize_all_panels_in_dock + .contains(&self.position) + } + + fn resize_all_panels( &mut self, size: Option, flex: Option, @@ -1151,7 +1202,7 @@ impl Render for Dock { MouseButton::Left, cx.listener(|dock, e: &MouseUpEvent, window, cx| { if e.click_count == 2 { - dock.resize_active_panel(None, None, window, cx); + dock.reset_panel_sizes(window, cx); dock.workspace .update(cx, |workspace, cx| { workspace.serialize_workspace(window, cx); diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index c970393f1c..0687246410 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -7716,34 +7716,19 @@ impl Workspace { )) .on_action(cx.listener( |workspace: &mut Workspace, _: &ResetActiveDockSize, window, cx| { - for dock in workspace.all_docks() { - if dock.focus_handle(cx).contains_focused(window, cx) { - let panel = dock.read(cx).active_panel().cloned(); - if let Some(panel) = panel { - dock.update(cx, |dock, cx| { - dock.set_panel_size_state( - panel.as_ref(), - dock::PanelSizeState::default(), - cx, - ); - }); - } - return; - } + if let Some(dock) = workspace.active_dock(window, cx).cloned() { + dock.update(cx, |dock, cx| { + dock.reset_panel_sizes(window, cx); + }); } }, )) .on_action(cx.listener( - |workspace: &mut Workspace, _: &ResetOpenDocksSize, _window, cx| { + |workspace: &mut Workspace, _: &ResetOpenDocksSize, window, cx| { for dock in workspace.all_docks() { - let panel = dock.read(cx).visible_panel().cloned(); - if let Some(panel) = panel { + if dock.read(cx).visible_panel().is_some() { dock.update(cx, |dock, cx| { - dock.set_panel_size_state( - panel.as_ref(), - dock::PanelSizeState::default(), - cx, - ); + dock.reset_panel_sizes(window, cx); }); } } @@ -8456,14 +8441,7 @@ impl Workspace { let flex_grow = self.dock_flex_for_size(DockPosition::Left, size, window, cx); self.left_dock.update(cx, |left_dock, cx| { - if WorkspaceSettings::get_global(cx) - .resize_all_panels_in_dock - .contains(&DockPosition::Left) - { - left_dock.resize_all_panels(Some(size), flex_grow, window, cx); - } else { - left_dock.resize_active_panel(Some(size), flex_grow, window, cx); - } + left_dock.resize_panel_sizes(Some(size), flex_grow, window, cx); }); } @@ -8480,28 +8458,14 @@ impl Workspace { }); let flex_grow = self.dock_flex_for_size(DockPosition::Right, size, window, cx); self.right_dock.update(cx, |right_dock, cx| { - if WorkspaceSettings::get_global(cx) - .resize_all_panels_in_dock - .contains(&DockPosition::Right) - { - right_dock.resize_all_panels(Some(size), flex_grow, window, cx); - } else { - right_dock.resize_active_panel(Some(size), flex_grow, window, cx); - } + right_dock.resize_panel_sizes(Some(size), flex_grow, window, cx); }); } fn resize_bottom_dock(&mut self, new_size: Pixels, window: &mut Window, cx: &mut App) { let size = new_size.min(self.bounds.bottom() - RESIZE_HANDLE_SIZE - self.bounds.top()); self.bottom_dock.update(cx, |bottom_dock, cx| { - if WorkspaceSettings::get_global(cx) - .resize_all_panels_in_dock - .contains(&DockPosition::Bottom) - { - bottom_dock.resize_all_panels(Some(size), None, window, cx); - } else { - bottom_dock.resize_active_panel(Some(size), None, window, cx); - } + bottom_dock.resize_panel_sizes(Some(size), None, window, cx); }); } @@ -14663,6 +14627,252 @@ mod tests { ); } + #[gpui::test] + async fn test_reset_all_panel_sizes_in_dock(cx: &mut gpui::TestAppContext) { + init_test(cx); + cx.update_global(|store: &mut SettingsStore, cx| { + store.update_user_settings(cx, |settings| { + settings.workspace.resize_all_panels_in_dock = + Some(vec![settings::DockPosition::Left]); + }); + }); + + let fs = FakeFs::new(cx.executor()); + let project = Project::test(fs, [], cx).await; + let (multi_workspace, cx) = + cx.add_window_view(|window, cx| MultiWorkspace::test_new(project, window, cx)); + let workspace = + multi_workspace.read_with(cx, |multi_workspace, _| multi_workspace.workspace().clone()); + + let (first_panel, second_panel) = workspace.update_in(cx, |workspace, window, cx| { + let first_panel = cx.new(|cx| { + let mut panel = TestPanel::new(DockPosition::Left, 100, cx); + panel.default_size = px(350.); + panel + }); + let second_panel = cx.new(|cx| { + let mut panel = TestPanel::new(DockPosition::Left, 200, cx); + panel.default_size = px(500.); + panel + }); + workspace.add_panel(first_panel.clone(), window, cx); + workspace.add_panel(second_panel.clone(), window, cx); + workspace.left_dock().update(cx, |dock, cx| { + dock.activate_panel(0, window, cx); + dock.set_open(true, window, cx); + }); + workspace.resize_left_dock(px(400.), window, cx); + (first_panel, second_panel) + }); + + cx.dispatch_action(ResetOpenDocksSize); + + workspace.update_in(cx, |workspace, window, cx| { + let dock = workspace.left_dock().read(cx); + let first_panel_size = dock.stored_panel_size(&first_panel, window, cx); + let second_panel_size = dock.stored_panel_size(&second_panel, window, cx); + + assert_eq!(first_panel_size, Some(px(350.))); + assert_eq!(second_panel_size, Some(px(350.))); + }); + + workspace.update_in(cx, |workspace, window, cx| { + workspace.left_dock().update(cx, |dock, cx| { + dock.activate_panel(1, window, cx); + }); + workspace.resize_left_dock(px(450.), window, cx); + }); + cx.dispatch_action(ResetOpenDocksSize); + + workspace.update_in(cx, |workspace, window, cx| { + let dock = workspace.left_dock().read(cx); + let first_panel_size = dock.stored_panel_size(&first_panel, window, cx); + let second_panel_size = dock.stored_panel_size(&second_panel, window, cx); + + assert_eq!(first_panel_size, Some(px(500.))); + assert_eq!(second_panel_size, Some(px(500.))); + }); + + first_panel.update(cx, |panel, _| panel.flexible = true); + second_panel.update(cx, |panel, _| panel.flexible = true); + workspace.update_in(cx, |workspace, window, cx| { + workspace.resize_left_dock(px(350.), window, cx); + workspace.left_dock().update(cx, |dock, cx| { + dock.reset_panel_sizes(window, cx); + }); + let dock = workspace.left_dock().read(cx); + let first_panel_flex = dock + .stored_panel_size_state(&first_panel) + .and_then(|state| state.flex); + let second_panel_flex = dock + .stored_panel_size_state(&second_panel) + .and_then(|state| state.flex); + + assert_eq!( + workspace.dock_size(&dock, window, cx), + Some(workspace.bounds.size.width / 2.) + ); + assert!(first_panel_flex.is_none()); + assert!(second_panel_flex.is_none()); + }); + + cx.update_global(|store: &mut SettingsStore, cx| { + store.update_user_settings(cx, |settings| { + settings.workspace.resize_all_panels_in_dock = Some(Vec::new()); + }); + }); + workspace.update_in(cx, |workspace, window, cx| { + first_panel.update(cx, |panel, _| panel.flexible = false); + second_panel.update(cx, |panel, _| panel.flexible = false); + workspace.left_dock().update(cx, |dock, cx| { + dock.activate_panel(0, window, cx); + }); + workspace.resize_left_dock(px(400.), window, cx); + workspace.left_dock().update(cx, |dock, cx| { + dock.reset_panel_sizes(window, cx); + }); + + let dock = workspace.left_dock().read(cx); + let first_panel_size = dock.stored_panel_size(&first_panel, window, cx); + let second_panel_size = dock.stored_panel_size(&second_panel, window, cx); + + assert_eq!(first_panel_size, Some(px(350.))); + assert_eq!(second_panel_size, Some(px(500.))); + }); + } + + #[gpui::test] + async fn test_reset_panel_mixed_modes(cx: &mut gpui::TestAppContext) { + init_test(cx); + cx.update_global(|store: &mut SettingsStore, cx| { + store.update_user_settings(cx, |settings| { + settings.workspace.resize_all_panels_in_dock = + Some(vec![settings::DockPosition::Left]); + }); + }); + + let fs = FakeFs::new(cx.executor()); + let project = Project::test(fs, [], cx).await; + let (multi_workspace, cx) = + cx.add_window_view(|window, cx| MultiWorkspace::test_new(project, window, cx)); + let workspace = + multi_workspace.read_with(cx, |multi_workspace, _| multi_workspace.workspace().clone()); + + // Create 4 panels, 2 with fixed width and another 2 with flexible width + // so that we can later confirm that updating a flexible width panel + // only affects other flexible panels, and the same with fixed width + // panels. + let (fixed_panel_a, fixed_panel_b, flexible_panel_a, flexible_panel_b) = workspace + .update_in(cx, |workspace, window, cx| { + let fixed_panel_a = cx.new(|cx| { + let mut panel = TestPanel::new(DockPosition::Left, 100, cx); + panel.default_size = px(350.); + panel + }); + + let fixed_panel_b = cx.new(|cx| { + let mut panel = TestPanel::new(DockPosition::Left, 200, cx); + panel.default_size = px(375.); + panel + }); + + let flexible_panel_a = + cx.new(|cx| TestPanel::new_flexible(DockPosition::Left, 300, cx)); + + let flexible_panel_b = + cx.new(|cx| TestPanel::new_flexible(DockPosition::Left, 400, cx)); + + workspace.add_panel(fixed_panel_a.clone(), window, cx); + workspace.add_panel(fixed_panel_b.clone(), window, cx); + workspace.add_panel(flexible_panel_a.clone(), window, cx); + workspace.add_panel(flexible_panel_b.clone(), window, cx); + + ( + fixed_panel_a, + fixed_panel_b, + flexible_panel_a, + flexible_panel_b, + ) + }); + + // We'll start by activating one of the flexible panels and ensuring + // that resizing the dock will sync the state between the flexible + // panels. + workspace.update_in(cx, |workspace, window, cx| { + workspace.left_dock().update(cx, |dock, cx| { + dock.activate_panel(2, window, cx); + dock.set_open(true, window, cx); + }); + + workspace.resize_left_dock(px(450.0), window, cx); + + let dock = workspace.left_dock().read(cx); + let fixed_panel_a_size = dock.stored_panel_size(&fixed_panel_a, window, cx); + let fixed_panel_b_size = dock.stored_panel_size(&fixed_panel_b, window, cx); + let flexible_panel_a_flex = dock + .stored_panel_size_state(&flexible_panel_a) + .and_then(|state| state.flex); + let flexible_panel_b_flex = dock + .stored_panel_size_state(&flexible_panel_b) + .and_then(|state| state.flex); + + assert_eq!(fixed_panel_a_size, Some(px(350.))); + assert_eq!(fixed_panel_b_size, Some(px(375.))); + assert!(flexible_panel_a_flex.is_some()); + assert_eq!(flexible_panel_a_flex, flexible_panel_b_flex); + }); + + // Resetting the active panel's size should continue syncing the state + // between flexible width panels but not affect fixed width panels. + cx.dispatch_action(ResetOpenDocksSize); + + workspace.update_in(cx, |workspace, window, cx| { + let dock = workspace.left_dock().read(cx); + let fixed_panel_a_size = dock.stored_panel_size(&fixed_panel_a, window, cx); + let fixed_panel_b_size = dock.stored_panel_size(&fixed_panel_b, window, cx); + let flexible_panel_a_flex = dock + .stored_panel_size_state(&flexible_panel_a) + .and_then(|state| state.flex); + let flexible_panel_b_flex = dock + .stored_panel_size_state(&flexible_panel_b) + .and_then(|state| state.flex); + + assert_eq!(fixed_panel_a_size, Some(px(350.))); + assert_eq!(fixed_panel_b_size, Some(px(375.))); + assert_eq!(flexible_panel_a_flex, None); + assert_eq!(flexible_panel_b_flex, None); + }); + + // Lastly, activate a fixed width panel and ensure that resetting its + // size will affect all other fixed width panels. + workspace.update_in(cx, |workspace, window, cx| { + workspace.left_dock().update(cx, |dock, cx| { + dock.activate_panel(0, window, cx); + }); + + workspace.resize_left_dock(px(450.), window, cx); + }); + + cx.dispatch_action(ResetOpenDocksSize); + + workspace.update_in(cx, |workspace, window, cx| { + let dock = workspace.left_dock().read(cx); + let fixed_panel_a_size = dock.stored_panel_size(&fixed_panel_a, window, cx); + let fixed_panel_b_size = dock.stored_panel_size(&fixed_panel_b, window, cx); + let flexible_panel_a_flex = dock + .stored_panel_size_state(&flexible_panel_a) + .and_then(|state| state.flex); + let flexible_panel_b_flex = dock + .stored_panel_size_state(&flexible_panel_b) + .and_then(|state| state.flex); + + assert_eq!(fixed_panel_a_size, Some(px(350.))); + assert_eq!(fixed_panel_b_size, Some(px(350.))); + assert_eq!(flexible_panel_a_flex, None); + assert_eq!(flexible_panel_b_flex, None); + }); + } + #[gpui::test] async fn test_flexible_panel_left_dock_sizing(cx: &mut gpui::TestAppContext) { init_test(cx);