Detailed changes
@@ -145,6 +145,10 @@
"restore_on_file_reopen": true,
// Whether to automatically close files that have been deleted on disk.
"close_on_file_delete": false,
+ // Whether toggling a panel (e.g. with its keyboard shortcut) also closes
+ // the panel when it is already focused, instead of just moving focus back
+ // to the editor.
+ "close_panel_on_toggle": false,
// Relative size of the drop target in the editor that will open dropped file as a split pane (0-0.5)
// E.g. 0.25 == If you drop onto the top/bottom quarter of the pane a new vertical split will be used
// If you drop onto the left/right quarter of the pane a new horizontal split will be used
@@ -75,7 +75,7 @@ use zed_actions::{
agent::{
OpenAcpOnboardingModal, OpenOnboardingModal, OpenSettings, ResetAgentZoom, ResetOnboarding,
},
- assistant::{OpenRulesLibrary, ToggleFocus},
+ assistant::{OpenRulesLibrary, Toggle, ToggleFocus},
};
const AGENT_PANEL_KEY: &str = "agent_panel";
@@ -789,6 +789,22 @@ impl AgentPanel {
}
}
+ pub fn toggle(
+ workspace: &mut Workspace,
+ _: &Toggle,
+ window: &mut Window,
+ cx: &mut Context<Workspace>,
+ ) {
+ if workspace
+ .panel::<Self>(cx)
+ .is_some_and(|panel| panel.read(cx).enabled(cx))
+ {
+ if !workspace.toggle_panel_focus::<Self>(window, cx) {
+ workspace.close_panel::<Self>(window, cx);
+ }
+ }
+ }
+
pub(crate) fn prompt_store(&self) -> &Option<Entity<PromptStore>> {
&self.prompt_store
}
@@ -45,6 +45,8 @@ use workspace::{
actions!(
collab_panel,
[
+ /// Toggles the collab panel.
+ Toggle,
/// Toggles focus on the collaboration panel.
ToggleFocus,
/// Removes the selected channel or contact.
@@ -93,6 +95,11 @@ pub fn init(cx: &mut App) {
})
}
});
+ workspace.register_action(|workspace, _: &Toggle, window, cx| {
+ if !workspace.toggle_panel_focus::<CollabPanel>(window, cx) {
+ workspace.close_panel::<CollabPanel>(window, cx);
+ }
+ });
workspace.register_action(|_, _: &OpenChannelNotes, window, cx| {
let channel_id = ActiveCall::global(cx)
.read(cx)
@@ -76,6 +76,8 @@ pub struct NotificationPresenter {
actions!(
notification_panel,
[
+ /// Toggles the notification panel.
+ Toggle,
/// Toggles focus on the notification panel.
ToggleFocus
]
@@ -86,6 +88,11 @@ pub fn init(cx: &mut App) {
workspace.register_action(|workspace, _: &ToggleFocus, window, cx| {
workspace.toggle_panel_focus::<NotificationPanel>(window, cx);
});
+ workspace.register_action(|workspace, _: &Toggle, window, cx| {
+ if !workspace.toggle_panel_focus::<NotificationPanel>(window, cx) {
+ workspace.close_panel::<NotificationPanel>(window, cx);
+ }
+ });
})
.detach();
}
@@ -14,8 +14,8 @@ use tasks_ui::{Spawn, TaskOverrides};
use ui::{FluentBuilder, InteractiveElement};
use util::maybe;
use workspace::{ItemHandle, ShutdownDebugAdapters, Workspace};
-use zed_actions::ToggleFocus;
use zed_actions::debugger::OpenOnboardingModal;
+use zed_actions::{Toggle, ToggleFocus};
pub mod attach_modal;
pub mod debugger_panel;
@@ -121,6 +121,11 @@ pub fn init(cx: &mut App) {
.register_action(|workspace, _: &ToggleFocus, window, cx| {
workspace.toggle_panel_focus::<DebugPanel>(window, cx);
})
+ .register_action(|workspace, _: &Toggle, window, cx| {
+ if !workspace.toggle_panel_focus::<DebugPanel>(window, cx) {
+ workspace.close_panel::<DebugPanel>(window, cx);
+ }
+ })
.register_action(|workspace: &mut Workspace, _: &Start, window, cx| {
NewProcessModal::show(workspace, window, NewProcessMode::Debug, None, cx);
})
@@ -84,6 +84,8 @@ actions!(
[
/// Closes the git panel.
Close,
+ /// Toggles the git panel.
+ Toggle,
/// Toggles focus on the git panel.
ToggleFocus,
/// Opens the git panel menu.
@@ -225,6 +227,11 @@ pub fn register(workspace: &mut Workspace) {
workspace.register_action(|workspace, _: &ToggleFocus, window, cx| {
workspace.toggle_panel_focus::<GitPanel>(window, cx);
});
+ workspace.register_action(|workspace, _: &Toggle, window, cx| {
+ if !workspace.toggle_panel_focus::<GitPanel>(window, cx) {
+ workspace.close_panel::<GitPanel>(window, cx);
+ }
+ });
workspace.register_action(|workspace, _: &ExpandCommitEditor, window, cx| {
CommitModal::toggle(workspace, None, window, cx)
});
@@ -93,6 +93,8 @@ actions!(
ToggleActiveEditorPin,
/// Unfolds the selected directory.
UnfoldDirectory,
+ /// Toggles the outline panel.
+ Toggle,
/// Toggles focus on the outline panel.
ToggleFocus,
]
@@ -670,6 +672,11 @@ pub fn init(cx: &mut App) {
workspace.register_action(|workspace, _: &ToggleFocus, window, cx| {
workspace.toggle_panel_focus::<OutlinePanel>(window, cx);
});
+ workspace.register_action(|workspace, _: &Toggle, window, cx| {
+ if !workspace.toggle_panel_focus::<OutlinePanel>(window, cx) {
+ workspace.close_panel::<OutlinePanel>(window, cx);
+ }
+ });
})
.detach();
}
@@ -75,7 +75,10 @@ use workspace::{
notifications::{DetachAndPromptErr, NotifyResultExt, NotifyTaskExt},
};
use worktree::CreatedEntry;
-use zed_actions::{project_panel::ToggleFocus, workspace::OpenWithSystem};
+use zed_actions::{
+ project_panel::{Toggle, ToggleFocus},
+ workspace::OpenWithSystem,
+};
const PROJECT_PANEL_KEY: &str = "ProjectPanel";
const NEW_ENTRY_ID: ProjectEntryId = ProjectEntryId::MAX;
@@ -418,6 +421,11 @@ pub fn init(cx: &mut App) {
workspace.register_action(|workspace, _: &ToggleFocus, window, cx| {
workspace.toggle_panel_focus::<ProjectPanel>(window, cx);
});
+ workspace.register_action(|workspace, _: &Toggle, window, cx| {
+ if !workspace.toggle_panel_focus::<ProjectPanel>(window, cx) {
+ workspace.close_panel::<ProjectPanel>(window, cx);
+ }
+ });
workspace.register_action(|workspace, _: &ToggleHideGitIgnore, _, cx| {
let fs = workspace.app_state().fs.clone();
@@ -952,6 +952,7 @@ impl VsCodeSettings {
bottom_dock_layout: None,
centered_layout: None,
close_on_file_delete: None,
+ close_panel_on_toggle: None,
command_aliases: Default::default(),
confirm_quit: self.read_enum("window.confirmBeforeClose", |s| match s {
"always" | "keyboardOnly" => Some(true),
@@ -113,6 +113,12 @@ pub struct WorkspaceSettingsContent {
///
/// Default: true
pub zoomed_padding: Option<bool>,
+ /// Whether toggling a panel (e.g. with its keyboard shortcut) also closes
+ /// the panel when it is already focused, instead of just moving focus back
+ /// to the editor.
+ ///
+ /// Default: false
+ pub close_panel_on_toggle: Option<bool>,
/// What draws window decorations/titlebar, the client application (Zed) or display server
/// Default: client
pub window_decorations: Option<WindowDecorations>,
@@ -3630,6 +3630,8 @@ impl Workspace {
/// Focus the panel of the given type if it isn't already focused. If it is
/// already focused, then transfer focus back to the workspace center.
+ /// When the `close_panel_on_toggle` setting is enabled, also closes the
+ /// panel when transferring focus back to the center.
pub fn toggle_panel_focus<T: Panel>(
&mut self,
window: &mut Window,
@@ -3641,6 +3643,10 @@ impl Workspace {
did_focus_panel
});
+ if !did_focus_panel && WorkspaceSettings::get_global(cx).close_panel_on_toggle {
+ self.close_panel::<T>(window, cx);
+ }
+
telemetry::event!(
"Panel Button Clicked",
name = T::persistent_name(),
@@ -10546,6 +10552,118 @@ mod tests {
});
}
+ #[gpui::test]
+ async fn test_close_panel_on_toggle(cx: &mut gpui::TestAppContext) {
+ init_test(cx);
+ let fs = FakeFs::new(cx.executor());
+
+ let project = Project::test(fs, [], cx).await;
+ let (workspace, cx) =
+ cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
+
+ let panel = workspace.update_in(cx, |workspace, window, cx| {
+ let panel = cx.new(|cx| TestPanel::new(DockPosition::Right, 100, cx));
+ workspace.add_panel(panel.clone(), window, cx);
+ panel
+ });
+
+ let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
+ pane.update_in(cx, |pane, window, cx| {
+ let item = cx.new(TestItem::new);
+ pane.add_item(Box::new(item), true, true, None, window, cx);
+ });
+
+ // Enable close_panel_on_toggle
+ cx.update_global(|store: &mut SettingsStore, cx| {
+ store.update_user_settings(cx, |settings| {
+ settings.workspace.close_panel_on_toggle = Some(true);
+ });
+ });
+
+ // Panel starts closed. Toggling should open and focus it.
+ workspace.update_in(cx, |workspace, window, cx| {
+ assert!(!workspace.right_dock().read(cx).is_open());
+ workspace.toggle_panel_focus::<TestPanel>(window, cx);
+ });
+
+ workspace.update_in(cx, |workspace, window, cx| {
+ assert!(
+ workspace.right_dock().read(cx).is_open(),
+ "Dock should be open after toggling from center"
+ );
+ assert!(
+ panel.read(cx).focus_handle(cx).contains_focused(window, cx),
+ "Panel should be focused after toggling from center"
+ );
+ });
+
+ // Panel is open and focused. Toggling should close the panel and
+ // return focus to the center.
+ workspace.update_in(cx, |workspace, window, cx| {
+ workspace.toggle_panel_focus::<TestPanel>(window, cx);
+ });
+
+ workspace.update_in(cx, |workspace, window, cx| {
+ assert!(
+ !workspace.right_dock().read(cx).is_open(),
+ "Dock should be closed after toggling from focused panel"
+ );
+ assert!(
+ !panel.read(cx).focus_handle(cx).contains_focused(window, cx),
+ "Panel should not be focused after toggling from focused panel"
+ );
+ });
+
+ // Open the dock and focus something else so the panel is open but not
+ // focused. Toggling should focus the panel (not close it).
+ workspace.update_in(cx, |workspace, window, cx| {
+ workspace
+ .right_dock()
+ .update(cx, |dock, cx| dock.set_open(true, window, cx));
+ window.focus(&pane.read(cx).focus_handle(cx), cx);
+ });
+
+ workspace.update_in(cx, |workspace, window, cx| {
+ assert!(workspace.right_dock().read(cx).is_open());
+ assert!(!panel.read(cx).focus_handle(cx).contains_focused(window, cx));
+ workspace.toggle_panel_focus::<TestPanel>(window, cx);
+ });
+
+ workspace.update_in(cx, |workspace, window, cx| {
+ assert!(
+ workspace.right_dock().read(cx).is_open(),
+ "Dock should remain open when toggling focuses an open-but-unfocused panel"
+ );
+ assert!(
+ panel.read(cx).focus_handle(cx).contains_focused(window, cx),
+ "Panel should be focused after toggling an open-but-unfocused panel"
+ );
+ });
+
+ // Now disable the setting and verify the original behavior: toggling
+ // from a focused panel moves focus to center but leaves the dock open.
+ cx.update_global(|store: &mut SettingsStore, cx| {
+ store.update_user_settings(cx, |settings| {
+ settings.workspace.close_panel_on_toggle = Some(false);
+ });
+ });
+
+ workspace.update_in(cx, |workspace, window, cx| {
+ workspace.toggle_panel_focus::<TestPanel>(window, cx);
+ });
+
+ workspace.update_in(cx, |workspace, window, cx| {
+ assert!(
+ workspace.right_dock().read(cx).is_open(),
+ "Dock should remain open when setting is disabled"
+ );
+ assert!(
+ !panel.read(cx).focus_handle(cx).contains_focused(window, cx),
+ "Panel should not be focused after toggling with setting disabled"
+ );
+ });
+ }
+
#[gpui::test]
async fn test_pane_zoom_in_out(cx: &mut TestAppContext) {
init_test(cx);
@@ -31,6 +31,7 @@ pub struct WorkspaceSettings {
pub text_rendering_mode: settings::TextRenderingMode,
pub resize_all_panels_in_dock: Vec<DockPosition>,
pub close_on_file_delete: bool,
+ pub close_panel_on_toggle: bool,
pub use_system_window_tabs: bool,
pub zoomed_padding: bool,
pub window_decorations: settings::WindowDecorations,
@@ -108,6 +109,7 @@ impl Settings for WorkspaceSettings {
.map(Into::into)
.collect(),
close_on_file_delete: workspace.close_on_file_delete.unwrap(),
+ close_panel_on_toggle: workspace.close_panel_on_toggle.unwrap(),
use_system_window_tabs: workspace.use_system_window_tabs.unwrap(),
zoomed_padding: workspace.zoomed_padding.unwrap(),
window_decorations: workspace.window_decorations.unwrap(),
@@ -740,6 +740,7 @@ async fn initialize_agent_panel(
workspace
.register_action(agent_ui::AgentPanel::toggle_focus)
+ .register_action(agent_ui::AgentPanel::toggle)
.register_action(agent_ui::InlineAssistant::inline_assist);
}
})?;
@@ -302,6 +302,8 @@ pub mod project_panel {
actions!(
project_panel,
[
+ /// Toggles the project panel.
+ Toggle,
/// Toggles focus on the project panel.
ToggleFocus
]
@@ -465,6 +467,8 @@ pub mod assistant {
actions!(
agent,
[
+ /// Toggles the agent panel.
+ Toggle,
#[action(deprecated_aliases = ["assistant::ToggleFocus"])]
ToggleFocus
]
@@ -639,6 +643,8 @@ actions!(
actions!(
debug_panel,
[
+ /// Toggles the debug panel.
+ Toggle,
/// Toggles focus on the debug panel.
ToggleFocus
]