macOS: attach close confirmation alert to the first window that actually needs it

pull/9509/head
Lars 2025-11-07 13:07:18 +01:00 committed by Mitchell Hashimoto
parent fbabafe8e3
commit f94cb01ec8
2 changed files with 32 additions and 19 deletions

View File

@ -287,6 +287,7 @@ class BaseTerminalController: NSWindowController,
func confirmClose( func confirmClose(
messageText: String, messageText: String,
informativeText: String, informativeText: String,
attachedWindow: NSWindow? = nil,
completion: @escaping () -> Void completion: @escaping () -> Void
) { ) {
// If we already have an alert, we need to wait for that one. // If we already have an alert, we need to wait for that one.
@ -294,7 +295,7 @@ class BaseTerminalController: NSWindowController,
// If there is no window to attach the modal then we assume success // If there is no window to attach the modal then we assume success
// since we'll never be able to show the modal. // since we'll never be able to show the modal.
guard let window else { guard let window = attachedWindow ?? self.window else {
completion() completion()
return return
} }

View File

@ -817,32 +817,37 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
/// Close all windows, asking for confirmation if necessary. /// Close all windows, asking for confirmation if necessary.
static func closeAllWindows() { static func closeAllWindows() {
let needsConfirm: Bool = all.contains { var confirmWindow: NSWindow?
$0.surfaceTree.contains { $0.needsConfirmQuit } var needsConfirm = false
for controller in all {
if let surfaceToConfirm = controller.surfaceTree.first(where: { $0.needsConfirmQuit }) {
needsConfirm = true
confirmWindow = surfaceToConfirm.window
break
}
} }
if (!needsConfirm) { guard needsConfirm else {
closeAllWindowsImmediately() closeAllWindowsImmediately()
return 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 = preferredParent?.window else {
closeAllWindowsImmediately()
return
}
// If we need confirmation by any, show one confirmation for all windows
let alert = NSAlert() let alert = NSAlert()
alert.messageText = "Close All Windows?" alert.messageText = "Close All Windows?"
alert.informativeText = "All terminal sessions will be terminated." alert.informativeText = "All terminal sessions will be terminated."
alert.addButton(withTitle: "Close All Windows") alert.addButton(withTitle: "Close All Windows")
alert.addButton(withTitle: "Cancel") alert.addButton(withTitle: "Cancel")
alert.alertStyle = .warning alert.alertStyle = .warning
alert.beginSheetModal(for: alertWindow, completionHandler: { response in guard let confirmWindow else {
if (alert.runModal() == .alertFirstButtonReturn) {
// This is important so that we avoid losing focus when Stage
// Manager is used (#8336)
alert.window.orderOut(nil)
closeAllWindowsImmediately()
}
return
}
alert.beginSheetModal(for: confirmWindow, completionHandler: { response in
if (response == .alertFirstButtonReturn) { if (response == .alertFirstButtonReturn) {
// This is important so that we avoid losing focus when Stage // This is important so that we avoid losing focus when Stage
// Manager is used (#8336) // Manager is used (#8336)
@ -1163,12 +1168,18 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
// reason we check ourselves. // reason we check ourselves.
let windows: [NSWindow] = window.tabGroup?.windows ?? [window] let windows: [NSWindow] = window.tabGroup?.windows ?? [window]
var confirmWindow: NSWindow?
var needsConfirm = false
// Check if any windows require close confirmation. // Check if any windows require close confirmation.
let needsConfirm = windows.contains { tabWindow in for tabWindow in windows {
guard let controller = tabWindow.windowController as? TerminalController else { guard let controller = tabWindow.windowController as? TerminalController else {
return false continue
}
if let surfaceToConfirm = controller.surfaceTree.first(where: { $0.needsConfirmQuit }) {
needsConfirm = true
confirmWindow = surfaceToConfirm.window
break
} }
return controller.surfaceTree.contains(where: { $0.needsConfirmQuit })
} }
// If none need confirmation then we can just close all the windows. // If none need confirmation then we can just close all the windows.
@ -1179,7 +1190,8 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
confirmClose( confirmClose(
messageText: "Close Window?", messageText: "Close Window?",
informativeText: "All terminal sessions in this window will be terminated." informativeText: "All terminal sessions in this window will be terminated.",
attachedWindow: confirmWindow,
) { ) {
self.closeWindowImmediately() self.closeWindowImmediately()
} }