macOS: add review windows when quitting

Inspired by Terminal.app
This commit is contained in:
Lukas
2026-05-19 20:01:32 +02:00
parent 8f9b86afa8
commit 00a989774e

View File

@@ -404,20 +404,7 @@ class AppDelegate: NSObject,
// If our app says we don't need to confirm, we can exit now.
if !ghostty.needsConfirmQuit { return .terminateNow }
// We have some visible window. Show an app-wide modal to confirm quitting.
let alert = NSAlert()
alert.messageText = "Quit Ghostty?"
alert.informativeText = "All terminal sessions will be terminated."
alert.addButton(withTitle: "Close Ghostty")
alert.addButton(withTitle: "Cancel")
alert.alertStyle = .warning
switch alert.runModal() {
case .alertFirstButtonReturn:
return .terminateNow
default:
return .terminateCancel
}
return terminate()
}
func applicationWillTerminate(_ notification: Notification) {
@@ -1305,6 +1292,79 @@ extension AppDelegate: NSMenuItemValidation {
}
}
// MARK: - Termination Flow
extension AppDelegate {
func terminate() -> NSApplication.TerminateReply {
let controllersNeedConfirmation = NSApplication.shared.windows
.compactMap { $0.windowController as? BaseTerminalController }
.filter { !$0.windowCanBeClosedWithoutConfirmation() }
guard !controllersNeedConfirmation.isEmpty else {
return .terminateNow
}
if controllersNeedConfirmation.count == 1 {
Task {
let response = await controllersNeedConfirmation[0].confirmCloseAsync(
messageText: "Quit Ghostty?",
informativeText: "The terminal still has a running process. If you quit, the process will be killed.",
confirmButtonTitle: "Terminate",
)
if [.OK, .alertFirstButtonReturn].contains(response) {
await NSApp.reply(toApplicationShouldTerminate: true)
} else {
await NSApp.reply(toApplicationShouldTerminate: false)
}
}
return .terminateLater
} else {
let alert = NSAlert()
alert.messageText = "You have \(controllersNeedConfirmation.count) windows with running processes. Do you want to review these windows before quitting?"
alert.informativeText = "If you don't review your windows, any running processes will be terminated"
alert.addButton(withTitle: "Review Windows...")
alert.addButton(withTitle: "Terminate Processes")
alert.addButton(withTitle: "Cancel")
alert.alertStyle = .warning
switch alert.runModal() {
case .alertFirstButtonReturn:
reviewWindows(controllersNeedConfirmation)
return .terminateLater
case .alertSecondButtonReturn:
return .terminateNow
default:
return .terminateCancel
}
}
}
private func reviewWindows(_ controllers: [BaseTerminalController]) {
Task {
for controller in controllers {
let response = await controller.confirmCloseAsync(
messageText: "Quit Ghostty?",
informativeText: "The terminal still has a running process. If you quit, the process will be killed.",
confirmButtonTitle: "Terminate",
)
if [.OK, .alertFirstButtonReturn].contains(response) {
// Close this window and until next review is cancelled
await controller.window?.close()
continue
} else {
await NSApp.reply(toApplicationShouldTerminate: false)
// Cancel the review
return
}
}
await NSApp.reply(toApplicationShouldTerminate: true)
}
}
}
/// Represents the state of the quick terminal controller.
private enum QuickTerminalState {
/// Controller has not been initialized and has no pending restoration state.