macos: quick terminal restores focus to previous application

This commit is contained in:
Mitchell Hashimoto
2024-09-29 15:32:53 -07:00
parent c70e0b2634
commit 19012cb6f5

View File

@ -13,6 +13,11 @@ class QuickTerminalController: BaseTerminalController {
/// The current state of the quick terminal /// The current state of the quick terminal
private(set) var visible: Bool = false private(set) var visible: Bool = false
/// The previously running application when the terminal is shown. This is NEVER Ghostty.
/// If this is set then when the quick terminal is animated out then we will restore this
/// application to the front.
private var previousApp: NSRunningApplication? = nil
init(_ ghostty: Ghostty.App, init(_ ghostty: Ghostty.App,
position: QuickTerminalPosition = .top, position: QuickTerminalPosition = .top,
baseConfig base: Ghostty.SurfaceConfiguration? = nil, baseConfig base: Ghostty.SurfaceConfiguration? = nil,
@ -58,10 +63,23 @@ class QuickTerminalController: BaseTerminalController {
override func windowDidResignKey(_ notification: Notification) { override func windowDidResignKey(_ notification: Notification) {
super.windowDidResignKey(notification) super.windowDidResignKey(notification)
// If we're not visible then we don't want to run any of the logic below
// because things like resetting our previous app assume we're visible.
// windowDidResignKey will also get called after animateOut so this
// ensures we don't run logic twice.
guard visible else { return }
// We don't animate out if there is a modal sheet being shown currently. // We don't animate out if there is a modal sheet being shown currently.
// This lets us show alerts without causing the window to disappear. // This lets us show alerts without causing the window to disappear.
guard window?.attachedSheet == nil else { return } guard window?.attachedSheet == nil else { return }
// If our app is still active, then it means that we're switching
// to another window within our app, so we remove the previous app
// so we don't restore it.
if NSApp.isActive {
self.previousApp = nil
}
animateOut() animateOut()
} }
@ -103,6 +121,14 @@ class QuickTerminalController: BaseTerminalController {
// If our application is not active, then we grab focus. The quick terminal // If our application is not active, then we grab focus. The quick terminal
// always grabs focus on animation in. // always grabs focus on animation in.
if !NSApp.isActive { if !NSApp.isActive {
// If we have a previously focused application and it isn't us, then
// we want to store it so we can restore state later.
if let previousApp = NSWorkspace.shared.frontmostApplication,
previousApp.bundleIdentifier != Bundle.main.bundleIdentifier
{
self.previousApp = previousApp
}
NSApp.activate(ignoringOtherApps: true) NSApp.activate(ignoringOtherApps: true)
} }
@ -178,6 +204,18 @@ class QuickTerminalController: BaseTerminalController {
// This causes the window to be removed from the screen list and macOS // This causes the window to be removed from the screen list and macOS
// handles what should be focused next. // handles what should be focused next.
window.orderOut(self) window.orderOut(self)
// If we have a previously active application, restore focus to it.
if let previousApp = self.previousApp {
// Make sure we unset the state no matter what
self.previousApp = nil
// If the app is terminated to nothing
guard !previousApp.isTerminated else { return }
// Ignore the result, it doesn't change our behavior.
_ = previousApp.activate(options: [])
}
}) })
} }