From 24ba1a6100fb13a07fd847416b0dbb20aabbf4b0 Mon Sep 17 00:00:00 2001 From: Roland Peelen Date: Mon, 30 Sep 2024 19:53:18 +0200 Subject: [PATCH 1/6] Add action on Zig side --- include/ghostty.h | 1 + src/App.zig | 1 + src/apprt/action.zig | 4 ++++ src/apprt/glfw.zig | 1 + src/apprt/gtk/App.zig | 1 + src/input/Binding.zig | 4 ++++ 6 files changed, 12 insertions(+) diff --git a/include/ghostty.h b/include/ghostty.h index e66ce08ea..c64ce0160 100644 --- a/include/ghostty.h +++ b/include/ghostty.h @@ -508,6 +508,7 @@ typedef enum { GHOSTTY_ACTION_TOGGLE_TAB_OVERVIEW, GHOSTTY_ACTION_TOGGLE_WINDOW_DECORATIONS, GHOSTTY_ACTION_TOGGLE_QUICK_TERMINAL, + GHOSTTY_ACTION_TOGGLE_VISIBILITY, GHOSTTY_ACTION_GOTO_TAB, GHOSTTY_ACTION_GOTO_SPLIT, GHOSTTY_ACTION_RESIZE_SPLIT, diff --git a/src/App.zig b/src/App.zig index 5922528ab..7e82bf007 100644 --- a/src/App.zig +++ b/src/App.zig @@ -325,6 +325,7 @@ pub fn performAction( .reload_config => try self.reloadConfig(rt_app), .close_all_windows => try rt_app.performAction(.app, .close_all_windows, {}), .toggle_quick_terminal => try rt_app.performAction(.app, .toggle_quick_terminal, {}), + .toggle_visibility => try rt_app.performAction(.app, .toggle_visibility, {}), } } diff --git a/src/apprt/action.zig b/src/apprt/action.zig index 2f7616bc4..6543d7630 100644 --- a/src/apprt/action.zig +++ b/src/apprt/action.zig @@ -96,6 +96,9 @@ pub const Action = union(Key) { /// Toggle the quick terminal in or out. toggle_quick_terminal, + /// Toggle the quick terminal in or out. + toggle_visibility, + /// Jump to a specific tab. Must handle the scenario that the tab /// value is invalid. goto_tab: GotoTab, @@ -180,6 +183,7 @@ pub const Action = union(Key) { toggle_tab_overview, toggle_window_decorations, toggle_quick_terminal, + toggle_visibility, goto_tab, goto_split, resize_split, diff --git a/src/apprt/glfw.zig b/src/apprt/glfw.zig index 87314c0e1..948b38a29 100644 --- a/src/apprt/glfw.zig +++ b/src/apprt/glfw.zig @@ -197,6 +197,7 @@ pub const App = struct { .toggle_tab_overview, .toggle_window_decorations, .toggle_quick_terminal, + .toggle_visibility, .goto_tab, .inspector, .render_inspector, diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index 8e683829c..2e0bbad84 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -389,6 +389,7 @@ pub fn performAction( .close_all_windows, .toggle_split_zoom, .toggle_quick_terminal, + .toggle_visibility, .size_limit, .cell_size, .secure_input, diff --git a/src/input/Binding.zig b/src/input/Binding.zig index da87ac230..e10ef3b1f 100644 --- a/src/input/Binding.zig +++ b/src/input/Binding.zig @@ -387,6 +387,9 @@ pub const Action = union(enum) { /// configuration file to customize its behavior. toggle_quick_terminal: void, + /// Toggle visibility of all windows + toggle_visibility: void, + /// Quit ghostty. quit: void, @@ -588,6 +591,7 @@ pub const Action = union(enum) { .close_all_windows, .quit, .toggle_quick_terminal, + .toggle_visibility, => .app, // These are app but can be special-cased in a surface context. From 4aac4ecd98c1085e94bcefdaad3b1f42ecdf2602 Mon Sep 17 00:00:00 2001 From: Roland Peelen Date: Mon, 30 Sep 2024 19:54:41 +0200 Subject: [PATCH 2/6] Add hiding toggle, hook up to menu / shortcut --- macos/Sources/App/macOS/AppDelegate.swift | 28 +++++++++++++++++++++++ macos/Sources/App/macOS/MainMenu.xib | 10 ++++++-- macos/Sources/Ghostty/Ghostty.App.swift | 10 ++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/macos/Sources/App/macOS/AppDelegate.swift b/macos/Sources/App/macOS/AppDelegate.swift index 5980b8d66..658f4ca83 100644 --- a/macos/Sources/App/macOS/AppDelegate.swift +++ b/macos/Sources/App/macOS/AppDelegate.swift @@ -86,6 +86,9 @@ class AppDelegate: NSObject, return ProcessInfo.processInfo.systemUptime - applicationLaunchTime } + /// Tracks whether the application is currently visible + private var isVisible: Bool = true + override init() { terminalManager = TerminalManager(ghostty) updaterController = SPUStandardUpdaterController( @@ -251,6 +254,7 @@ class AppDelegate: NSObject, // Ghostty will validate as well but we can avoid creating an entirely new // surface by doing our own validation here. We can also show a useful error // this way. + var isDirectory = ObjCBool(true) guard FileManager.default.fileExists(atPath: filename, isDirectory: &isDirectory) else { return false } @@ -315,6 +319,7 @@ class AppDelegate: NSObject, syncMenuShortcut(action: "decrease_font_size:1", menuItem: self.menuDecreaseFontSize) syncMenuShortcut(action: "reset_font_size", menuItem: self.menuResetFontSize) syncMenuShortcut(action: "toggle_quick_terminal", menuItem: self.menuQuickTerminal) + syncMenuShortcut(action: "toggle_visibility", menuItem: self.menuQuickTerminal) syncMenuShortcut(action: "inspector:toggle", menuItem: self.menuTerminalInspector) syncMenuShortcut(action: "toggle_secure_input", menuItem: self.menuSecureInput) @@ -564,4 +569,27 @@ class AppDelegate: NSObject, self.menuQuickTerminal?.state = if (quickController.visible) { .on } else { .off } } + + /// Toggles the visibility of all Ghostty windows + @IBAction func toggleVisibility(_ sender: Any) { + let configurationErrorsWindow = ConfigurationErrorsController.sharedInstance.window + + if isVisible { + // Hide all windows + for window in NSApp.windows { + if window !== configurationErrorsWindow { + window.orderOut(nil) + } + } + } else { + // Show all windows + for window in NSApp.windows { + if window !== configurationErrorsWindow { + window.makeKeyAndOrderFront(nil) + } + } + NSApp.activate(ignoringOtherApps: true) + } + isVisible.toggle() + } } diff --git a/macos/Sources/App/macOS/MainMenu.xib b/macos/Sources/App/macOS/MainMenu.xib index 63aae4c60..2982d6cfc 100644 --- a/macos/Sources/App/macOS/MainMenu.xib +++ b/macos/Sources/App/macOS/MainMenu.xib @@ -217,10 +217,16 @@ - + - + + + + + + + diff --git a/macos/Sources/Ghostty/Ghostty.App.swift b/macos/Sources/Ghostty/Ghostty.App.swift index 352fb4107..e46d6fbdf 100644 --- a/macos/Sources/Ghostty/Ghostty.App.swift +++ b/macos/Sources/Ghostty/Ghostty.App.swift @@ -490,6 +490,9 @@ extension Ghostty { case GHOSTTY_ACTION_TOGGLE_QUICK_TERMINAL: toggleQuickTerminal(app, target: target) + case GHOSTTY_ACTION_TOGGLE_VISIBILITY: + toggleVisibility(app, target: target) + case GHOSTTY_ACTION_CLOSE_ALL_WINDOWS: fallthrough case GHOSTTY_ACTION_TOGGLE_TAB_OVERVIEW: @@ -630,6 +633,13 @@ extension Ghostty { } } + private static func toggleVisibility( + _ app: ghostty_app_t, + target: ghostty_target_s) { + guard let appDelegate = NSApplication.shared.delegate as? AppDelegate else { return } + appDelegate.toggleVisibility(self) + } + private static func gotoTab( _ app: ghostty_app_t, target: ghostty_target_s, From 2dbd46096f785a069207ff03ea9bd8c9db6c68b5 Mon Sep 17 00:00:00 2001 From: Roland Peelen Date: Mon, 30 Sep 2024 19:59:30 +0200 Subject: [PATCH 3/6] Fix typo --- src/apprt/action.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apprt/action.zig b/src/apprt/action.zig index 6543d7630..224ed5e5c 100644 --- a/src/apprt/action.zig +++ b/src/apprt/action.zig @@ -96,7 +96,7 @@ pub const Action = union(Key) { /// Toggle the quick terminal in or out. toggle_quick_terminal, - /// Toggle the quick terminal in or out. + /// Toggle the visiblity of all windows toggle_visibility, /// Jump to a specific tab. Must handle the scenario that the tab From af48c1af0c6555964b07de5e33445ff194a34549 Mon Sep 17 00:00:00 2001 From: Roland Peelen Date: Tue, 1 Oct 2024 09:52:14 +0200 Subject: [PATCH 4/6] Refactor to hide only BaseTerminalController windows This also slightly changes the code, as the duplication of the for loop was making it harder to read now. I think technically slightly less efficient, but this is hardly a hot code path, so should be fine imo. --- macos/Sources/App/macOS/AppDelegate.swift | 26 +++++++++-------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/macos/Sources/App/macOS/AppDelegate.swift b/macos/Sources/App/macOS/AppDelegate.swift index 658f4ca83..c1f2615bf 100644 --- a/macos/Sources/App/macOS/AppDelegate.swift +++ b/macos/Sources/App/macOS/AppDelegate.swift @@ -570,26 +570,20 @@ class AppDelegate: NSObject, self.menuQuickTerminal?.state = if (quickController.visible) { .on } else { .off } } - /// Toggles the visibility of all Ghostty windows + /// Toggles visibility of all Ghosty Terminal windows. When hidden, activates Ghostty as the frontmost application @IBAction func toggleVisibility(_ sender: Any) { - let configurationErrorsWindow = ConfigurationErrorsController.sharedInstance.window - - if isVisible { - // Hide all windows - for window in NSApp.windows { - if window !== configurationErrorsWindow { - window.orderOut(nil) - } - } - } else { - // Show all windows - for window in NSApp.windows { - if window !== configurationErrorsWindow { - window.makeKeyAndOrderFront(nil) - } + for controller in NSApp.windows.compactMap({ $0.windowController as? BaseTerminalController }) { + if isVisible { + controller.window?.orderOut(nil) + } else { + controller.window?.makeKeyAndOrderFront(nil) } + } + + if !isVisible { NSApp.activate(ignoringOtherApps: true) } + isVisible.toggle() } } From 9e00eeff863d2d9163bcdcf43cb9f451c6e58d9e Mon Sep 17 00:00:00 2001 From: Roland Peelen Date: Tue, 1 Oct 2024 09:53:12 +0200 Subject: [PATCH 5/6] Review Feedback - formatting / docs --- macos/Sources/Ghostty/Ghostty.App.swift | 9 +++++---- src/apprt/action.zig | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/macos/Sources/Ghostty/Ghostty.App.swift b/macos/Sources/Ghostty/Ghostty.App.swift index e46d6fbdf..9198e48b6 100644 --- a/macos/Sources/Ghostty/Ghostty.App.swift +++ b/macos/Sources/Ghostty/Ghostty.App.swift @@ -635,10 +635,11 @@ extension Ghostty { private static func toggleVisibility( _ app: ghostty_app_t, - target: ghostty_target_s) { - guard let appDelegate = NSApplication.shared.delegate as? AppDelegate else { return } - appDelegate.toggleVisibility(self) - } + target: ghostty_target_s + ) { + guard let appDelegate = NSApplication.shared.delegate as? AppDelegate else { return } + appDelegate.toggleVisibility(self) + } private static func gotoTab( _ app: ghostty_app_t, diff --git a/src/apprt/action.zig b/src/apprt/action.zig index 224ed5e5c..07985fbd0 100644 --- a/src/apprt/action.zig +++ b/src/apprt/action.zig @@ -96,7 +96,7 @@ pub const Action = union(Key) { /// Toggle the quick terminal in or out. toggle_quick_terminal, - /// Toggle the visiblity of all windows + /// Toggle the visiblity of all Ghostty terminal windows toggle_visibility, /// Jump to a specific tab. Must handle the scenario that the tab From 28ec11e52b53a2ac02c3bcd44b00d655c84838ec Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 2 Oct 2024 10:51:57 -0700 Subject: [PATCH 6/6] docs updates --- macos/Sources/App/macOS/AppDelegate.swift | 14 +++++++++----- src/apprt/action.zig | 2 +- src/input/Binding.zig | 6 +++++- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/macos/Sources/App/macOS/AppDelegate.swift b/macos/Sources/App/macOS/AppDelegate.swift index c1f2615bf..e926e380b 100644 --- a/macos/Sources/App/macOS/AppDelegate.swift +++ b/macos/Sources/App/macOS/AppDelegate.swift @@ -86,7 +86,9 @@ class AppDelegate: NSObject, return ProcessInfo.processInfo.systemUptime - applicationLaunchTime } - /// Tracks whether the application is currently visible + /// Tracks whether the application is currently visible. This can be gamed, i.e. if a user manually + /// brings each window one by one to the front. But at worst its off by one set of toggles and this + /// makes our logic very easy. private var isVisible: Bool = true override init() { @@ -572,14 +574,16 @@ class AppDelegate: NSObject, /// Toggles visibility of all Ghosty Terminal windows. When hidden, activates Ghostty as the frontmost application @IBAction func toggleVisibility(_ sender: Any) { - for controller in NSApp.windows.compactMap({ $0.windowController as? BaseTerminalController }) { + // We only care about terminal windows. + for window in NSApp.windows.filter({ $0.windowController is BaseTerminalController }) { if isVisible { - controller.window?.orderOut(nil) + window.orderOut(nil) } else { - controller.window?.makeKeyAndOrderFront(nil) + window.makeKeyAndOrderFront(nil) } } - + + // After bringing them all to front we make sure our app is active too. if !isVisible { NSApp.activate(ignoringOtherApps: true) } diff --git a/src/apprt/action.zig b/src/apprt/action.zig index 07985fbd0..5c8ba6a01 100644 --- a/src/apprt/action.zig +++ b/src/apprt/action.zig @@ -96,7 +96,7 @@ pub const Action = union(Key) { /// Toggle the quick terminal in or out. toggle_quick_terminal, - /// Toggle the visiblity of all Ghostty terminal windows + /// Toggle the visibility of all Ghostty terminal windows. toggle_visibility, /// Jump to a specific tab. Must handle the scenario that the tab diff --git a/src/input/Binding.zig b/src/input/Binding.zig index e10ef3b1f..9f4ebd626 100644 --- a/src/input/Binding.zig +++ b/src/input/Binding.zig @@ -387,7 +387,11 @@ pub const Action = union(enum) { /// configuration file to customize its behavior. toggle_quick_terminal: void, - /// Toggle visibility of all windows + /// Show/hide all windows. If all windows become shown, we also ensure + /// Ghostty is focused. + /// + /// This currently only works on macOS. When hiding all windows, we do + /// not yield focus to the previous application. toggle_visibility: void, /// Quit ghostty.