mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
macos: close all windows
Fixes #1052 This implements a `close_all_windows` binding in the core and implements it for macOS specifically. This will ask for close confirmation if any surface in any of the windows requires confirmation. This is bound by default to option+shift+command+w to match Safari. The binding is generall option+command+w but users may expect this to also mean "Close All Other Tabs" which is the changed behavior if any tabs are present in a standard macOS application. So I chose to follow Safari instead. This doesn't implement this feature for GTK, that's left as an exercise for a contributor.
This commit is contained in:
@ -21,6 +21,7 @@ class AppDelegate: NSObject, ObservableObject, NSApplicationDelegate, UNUserNoti
|
||||
@IBOutlet private var menuSplitVertical: NSMenuItem?
|
||||
@IBOutlet private var menuClose: NSMenuItem?
|
||||
@IBOutlet private var menuCloseWindow: NSMenuItem?
|
||||
@IBOutlet private var menuCloseAllWindows: NSMenuItem?
|
||||
|
||||
@IBOutlet private var menuCopy: NSMenuItem?
|
||||
@IBOutlet private var menuPaste: NSMenuItem?
|
||||
@ -218,6 +219,7 @@ class AppDelegate: NSObject, ObservableObject, NSApplicationDelegate, UNUserNoti
|
||||
syncMenuShortcut(action: "new_tab", menuItem: self.menuNewTab)
|
||||
syncMenuShortcut(action: "close_surface", menuItem: self.menuClose)
|
||||
syncMenuShortcut(action: "close_window", menuItem: self.menuCloseWindow)
|
||||
syncMenuShortcut(action: "close_all_windows", menuItem: self.menuCloseAllWindows)
|
||||
syncMenuShortcut(action: "new_split:right", menuItem: self.menuSplitHorizontal)
|
||||
syncMenuShortcut(action: "new_split:down", menuItem: self.menuSplitVertical)
|
||||
|
||||
@ -360,6 +362,11 @@ class AppDelegate: NSObject, ObservableObject, NSApplicationDelegate, UNUserNoti
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
}
|
||||
|
||||
@IBAction func closeAllWindows(_ sender: Any?) {
|
||||
terminalManager.closeAllWindows()
|
||||
AboutController.shared.hide()
|
||||
}
|
||||
|
||||
@IBAction func showAbout(_ sender: Any?) {
|
||||
AboutController.shared.show()
|
||||
}
|
||||
|
@ -16,8 +16,11 @@ class AboutController: NSWindowController, NSWindowDelegate {
|
||||
// MARK: - Functions
|
||||
|
||||
func show() {
|
||||
guard let window = window else { return }
|
||||
window.makeKeyAndOrderFront(nil)
|
||||
window?.makeKeyAndOrderFront(nil)
|
||||
}
|
||||
|
||||
func hide() {
|
||||
window?.close()
|
||||
}
|
||||
|
||||
//MARK: - First Responder
|
||||
|
@ -189,6 +189,52 @@ class TerminalManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Close all windows, asking for confirmation if necessary.
|
||||
func closeAllWindows() {
|
||||
var needsConfirm: Bool = false
|
||||
for w in self.windows {
|
||||
if (w.controller.surfaceTree?.needsConfirmQuit() ?? false) {
|
||||
needsConfirm = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!needsConfirm) {
|
||||
for w in self.windows {
|
||||
w.controller.close()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// If we don't have a main window, we just close all windows because
|
||||
// we have no window to show the modal on top of. I'm sure there's a way
|
||||
// to do an app-level alert but I don't know how and this case should never
|
||||
// really happen.
|
||||
guard let alertWindow = mainWindow?.controller.window else {
|
||||
for w in self.windows {
|
||||
w.controller.close()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// If we need confirmation by any, show one confirmation for all windows
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "Close All Windows?"
|
||||
alert.informativeText = "All terminal sessions will be terminated."
|
||||
alert.addButton(withTitle: "Close All Windows")
|
||||
alert.addButton(withTitle: "Cancel")
|
||||
alert.alertStyle = .warning
|
||||
alert.beginSheetModal(for: alertWindow, completionHandler: { response in
|
||||
if (response == .alertFirstButtonReturn) {
|
||||
for w in self.windows {
|
||||
w.controller.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Relabels all the tabs with the proper keyboard shortcut.
|
||||
func relabelAllTabs() {
|
||||
for w in windows {
|
||||
|
@ -15,6 +15,7 @@
|
||||
<customObject id="bbz-4X-AYv" userLabel="AppDelegate" customClass="AppDelegate" customModule="Ghostty" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="menuClose" destination="DVo-aG-piG" id="R3t-0C-aSU"/>
|
||||
<outlet property="menuCloseAllWindows" destination="yKr-Vi-Yqw" id="Zet-Ir-zbm"/>
|
||||
<outlet property="menuCloseWindow" destination="W5w-UZ-crk" id="6ff-BT-ENV"/>
|
||||
<outlet property="menuCopy" destination="Jqf-pv-Zcu" id="bKd-1C-oy9"/>
|
||||
<outlet property="menuDecreaseFontSize" destination="kzb-SZ-dOA" id="Y1B-Vh-6Z2"/>
|
||||
@ -134,6 +135,12 @@
|
||||
<action selector="closeWindow:" target="-1" id="ovs-xn-3ju"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Close All Windows" id="yKr-Vi-Yqw">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="closeAllWindows:" target="-1" id="hrz-eb-l5t"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
|
@ -2885,6 +2885,12 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
||||
|
||||
.close_window => try self.app.closeSurface(self),
|
||||
|
||||
.close_all_windows => {
|
||||
if (@hasDecl(apprt.Surface, "closeAllWindows")) {
|
||||
self.rt_surface.closeAllWindows();
|
||||
} else log.warn("runtime doesn't implement closeAllWindows", .{});
|
||||
},
|
||||
|
||||
.quit => try self.app.setQuit(),
|
||||
}
|
||||
|
||||
|
@ -1183,6 +1183,11 @@ pub fn default(alloc_gpa: Allocator) Allocator.Error!Config {
|
||||
.{ .key = .w, .mods = .{ .super = true, .shift = true } },
|
||||
.{ .close_window = {} },
|
||||
);
|
||||
try result.keybind.set.put(
|
||||
alloc,
|
||||
.{ .key = .w, .mods = .{ .super = true, .shift = true, .alt = true } },
|
||||
.{ .close_all_windows = {} },
|
||||
);
|
||||
try result.keybind.set.put(
|
||||
alloc,
|
||||
.{ .key = .t, .mods = .{ .super = true } },
|
||||
|
@ -221,12 +221,18 @@ pub const Action = union(enum) {
|
||||
reload_config: void,
|
||||
|
||||
/// Close the current "surface", whether that is a window, tab, split,
|
||||
/// etc. This only closes ONE surface.
|
||||
/// etc. This only closes ONE surface. This will trigger close confirmation
|
||||
/// as configured.
|
||||
close_surface: void,
|
||||
|
||||
/// Close the window, regardless of how many tabs or splits there may be.
|
||||
/// This will trigger close confirmation as configured.
|
||||
close_window: void,
|
||||
|
||||
/// Close all windows. This will trigger close confirmation as configured.
|
||||
/// This only works for macOS currently.
|
||||
close_all_windows: void,
|
||||
|
||||
/// Toggle fullscreen mode of window.
|
||||
toggle_fullscreen: void,
|
||||
|
||||
|
Reference in New Issue
Block a user