macos: unify confirm close in our terminal controllers

pull/7523/head
Mitchell Hashimoto 2025-06-03 16:43:20 -07:00
parent d1dce1e372
commit b84b715ddb
No known key found for this signature in database
GPG Key ID: 523D5DC389D273BC
2 changed files with 47 additions and 68 deletions

View File

@ -194,6 +194,40 @@ class BaseTerminalController: NSWindowController,
savedFrame = .init(window: window.frame, screen: screen.visibleFrame)
}
func confirmClose(
messageText: String,
informativeText: String,
completion: @escaping () -> Void
) {
// If we already have an alert, we need to wait for that one.
guard alert == nil else { return }
// If there is no window to attach the modal then we assume success
// since we'll never be able to show the modal.
guard let window else {
completion()
return
}
// If we need confirmation by any, show one confirmation for all windows
// in the tab group.
let alert = NSAlert()
alert.messageText = messageText
alert.informativeText = informativeText
alert.addButton(withTitle: "Close")
alert.addButton(withTitle: "Cancel")
alert.alertStyle = .warning
alert.beginSheetModal(for: window) { response in
self.alert = nil
if response == .alertFirstButtonReturn {
completion()
}
}
// Store our alert so we only ever show one.
self.alert = alert
}
// MARK: Notifications
@objc private func didChangeScreenParametersNotification(_ notification: Notification) {
@ -287,37 +321,19 @@ class BaseTerminalController: NSWindowController,
return
}
// If we don't have a window to attach our modal to, we also exit immediately.
// This should NOT happen.
guard let window = target.window else {
surfaceTree2 = surfaceTree2.remove(node)
return
}
// Confirm close. We use an NSAlert instead of a SwiftUI confirmationDialog
// due to SwiftUI bugs (see Ghostty #560). To repeat from #560, the bug is that
// confirmationDialog allows the user to Cmd-W close the alert, but when doing
// so SwiftUI does not update any of the bindings to note that window is no longer
// being shown, and provides no callback to detect this.
let alert = NSAlert()
alert.messageText = "Close Terminal?"
alert.informativeText = "The terminal still has a running process. If you close the " +
"terminal the process will be killed."
alert.addButton(withTitle: "Close the Terminal")
alert.addButton(withTitle: "Cancel")
alert.alertStyle = .warning
alert.beginSheetModal(for: window, completionHandler: { [weak self] response in
switch (response) {
case .alertFirstButtonReturn:
alert.window.orderOut(nil)
if let self {
self.surfaceTree2 = self.surfaceTree2.remove(node)
}
default:
break
confirmClose(
messageText: "Close Terminal?",
informativeText: "The terminal still has a running process. If you close the terminal the process will be killed."
) { [weak self] in
if let self {
self.surfaceTree2 = self.surfaceTree2.remove(node)
}
})
}
}
@objc private func ghosttyDidNewSplit(_ notification: Notification) {
@ -624,26 +640,12 @@ class BaseTerminalController: NSWindowController,
if (!node.needsConfirmQuit()) { return true }
// We require confirmation, so show an alert as long as we aren't already.
let alert = NSAlert()
alert.messageText = "Close Terminal?"
alert.informativeText = "The terminal still has a running process. If you close the " +
"terminal the process will be killed."
alert.addButton(withTitle: "Close the Terminal")
alert.addButton(withTitle: "Cancel")
alert.alertStyle = .warning
alert.beginSheetModal(for: window, completionHandler: { response in
self.alert = nil
switch (response) {
case .alertFirstButtonReturn:
alert.window.orderOut(nil)
window.close()
default:
break
}
})
self.alert = alert
confirmClose(
messageText: "Close Terminal?",
informativeText: "The terminal still has a running process. If you close the terminal the process will be killed."
) {
window.close()
}
return false
}

View File

@ -587,27 +587,6 @@ class TerminalController: BaseTerminalController {
ghostty.newTab(surface: surface)
}
private func confirmClose(
window: NSWindow,
messageText: String,
informativeText: String,
completion: @escaping () -> Void
) {
// If we need confirmation by any, show one confirmation for all windows
// in the tab group.
let alert = NSAlert()
alert.messageText = messageText
alert.informativeText = informativeText
alert.addButton(withTitle: "Close")
alert.addButton(withTitle: "Cancel")
alert.alertStyle = .warning
alert.beginSheetModal(for: window) { response in
if response == .alertFirstButtonReturn {
completion()
}
}
}
@IBAction func closeTab(_ sender: Any?) {
guard let window = window else { return }
guard window.tabGroup != nil else {
@ -618,7 +597,6 @@ class TerminalController: BaseTerminalController {
if surfaceTree?.needsConfirmQuit() ?? false {
confirmClose(
window: window,
messageText: "Close Tab?",
informativeText: "The terminal still has a running process. If you close the tab the process will be killed."
) {
@ -664,7 +642,6 @@ class TerminalController: BaseTerminalController {
}
confirmClose(
window: window,
messageText: "Close Window?",
informativeText: "All terminal sessions in this window will be terminated."
) {