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 <dinojoaocosta@gmail.com>
This commit is contained in:
Austin Cummingsanddino authored and GitHub committed 2026-08-17 22:28:04 +00:00
1 parent 29202c1ab5
commit 33b0b39e40
2 files changed
+314 -53

No files matched your search

+58 -7
View File
@@ -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<usize> {
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<Pixels>,
flex: Option<f32>,
window: &mut Window,
cx: &mut Context<Self>,
) {
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<Pixels>,
flex: Option<f32>,
window: &mut Window,
cx: &mut Context<Self>,
) {
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<Self>) {
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<Self>) {
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<Self>) {
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<Pixels>,
flex: Option<f32>,
@@ -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);
+256 -46
View File
@@ -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);