Implement 'Cmd+W with no open tabs closes the window', with a setting (#11989)

Mikayla Maki created

Follow up to: https://github.com/zed-industries/zed/pull/10986

However, I have set this to have a default behavior of 'auto': matching
the current platform's conventions, rather than a default value of
'off'.

fixes https://github.com/zed-industries/zed/issues/5322.

Release Notes:

- Changed the behavior of `workspace::CloseActiveItem`: when you're
using macOS and there are no open tabs, it now closes the window
([#5322](https://github.com/zed-industries/zed/issues/5322)). This can
be controlled with a new setting, `when_closing_with_no_tabs`, to
disable it on macOS, or enable it on other platforms.

Change summary

assets/settings/default.json               |  9 ++++++++
crates/workspace/src/pane.rs               | 10 ++++++++
crates/workspace/src/workspace_settings.rs | 27 ++++++++++++++++++++++++
3 files changed, 45 insertions(+), 1 deletion(-)

Detailed changes

assets/settings/default.json 🔗

@@ -84,6 +84,15 @@
   "restore_on_startup": "last_workspace",
   // Size of the drop target in the editor.
   "drop_target_size": 0.2,
+  // Whether the window should be closed when using 'close active item' on a window with no tabs.
+  // May take 3 values:
+  //  1. Use the current platform's convention
+  //         "when_closing_with_no_tabs": "platform_default"
+  //  2. Always close the window:
+  //         "when_closing_with_no_tabs": "close_window",
+  //  3. Never close the window
+  //         "when_closing_with_no_tabs": "keep_window_open",
+  "when_closing_with_no_tabs": "platform_default",
   // Whether the cursor blinks in the editor.
   "cursor_blink": true,
   // How to highlight the current line in the editor.

crates/workspace/src/pane.rs 🔗

@@ -5,7 +5,7 @@ use crate::{
     },
     toolbar::Toolbar,
     workspace_settings::{AutosaveSetting, TabBarSettings, WorkspaceSettings},
-    NewCenterTerminal, NewFile, NewSearch, OpenInTerminal, OpenTerminal, OpenVisible,
+    CloseWindow, NewCenterTerminal, NewFile, NewSearch, OpenInTerminal, OpenTerminal, OpenVisible,
     SplitDirection, ToggleZoom, Workspace,
 };
 use anyhow::Result;
@@ -948,6 +948,14 @@ impl Pane {
         cx: &mut ViewContext<Self>,
     ) -> Option<Task<Result<()>>> {
         if self.items.is_empty() {
+            // Close the window when there's no active items to close, if configured
+            if WorkspaceSettings::get_global(cx)
+                .when_closing_with_no_tabs
+                .should_close()
+            {
+                cx.dispatch_action(Box::new(CloseWindow));
+            }
+
             return None;
         }
         let active_item_id = self.items[self.active_item_index].item_id();

crates/workspace/src/workspace_settings.rs 🔗

@@ -13,6 +13,29 @@ pub struct WorkspaceSettings {
     pub autosave: AutosaveSetting,
     pub restore_on_startup: RestoreOnStartupBehaviour,
     pub drop_target_size: f32,
+    pub when_closing_with_no_tabs: CloseWindowWhenNoItems,
+}
+
+#[derive(Copy, Clone, Default, Serialize, Deserialize, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum CloseWindowWhenNoItems {
+    /// Match platform conventions by default, so "on" on macOS and "off" everywhere else
+    #[default]
+    PlatformDefault,
+    /// Close the window when there are no tabs
+    CloseWindow,
+    /// Leave the window open when there are no tabs
+    KeepWindowOpen,
+}
+
+impl CloseWindowWhenNoItems {
+    pub fn should_close(&self) -> bool {
+        match self {
+            CloseWindowWhenNoItems::PlatformDefault => cfg!(target_os = "macos"),
+            CloseWindowWhenNoItems::CloseWindow => true,
+            CloseWindowWhenNoItems::KeepWindowOpen => false,
+        }
+    }
 }
 
 #[derive(Copy, Clone, Default, Serialize, Deserialize, JsonSchema)]
@@ -56,6 +79,10 @@ pub struct WorkspaceSettingsContent {
     ///
     /// Default: `0.2` (20% of the smaller dimension of the workspace)
     pub drop_target_size: Option<f32>,
+    /// Whether to close the window when using 'close active item' on a workspace with no tabs
+    ///
+    /// Default: auto ("on" on macOS, "off" otherwise)
+    pub when_closing_with_no_tabs: Option<CloseWindowWhenNoItems>,
 }
 
 #[derive(Deserialize)]