Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions Sources/AppDelegate+FocusedWorkspaceCloseShortcut.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import AppKit

extension AppDelegate {
func mainWindowContextForFocusedWorkspaceCloseShortcut(event: NSEvent) -> MainWindowContext? {
if let keyWindow = NSApp.keyWindow,
let context = contextForMainTerminalWindow(keyWindow) {
return context
}

if let mainWindow = NSApp.mainWindow,
let context = contextForMainTerminalWindow(mainWindow) {
return context
}

if let context = mainWindowContext(forShortcutEvent: event, debugSource: "shortcut.closeWorkspace") {
return context
}

if shortcutEventHasAddressableWindow(event) {
if let eventWindow = resolvedShortcutEventWindow(event),
cmuxWindowShouldOwnCloseShortcut(eventWindow) {
return preferredMainWindowContextForShortcutRouting(event: event)
}
return nil
}

return nil
}

@discardableResult
func closeWorkspaceFromFocusedShortcut(event: NSEvent) -> Bool {
guard let context = mainWindowContextForFocusedWorkspaceCloseShortcut(event: event) else {
return false
}

if let window = context.window ?? windowForMainWindowId(context.windowId) {
setActiveMainWindow(window)
} else {
activateMainWindowContext(context)
}

context.tabManager.closeCurrentWorkspaceWithConfirmation()
return true
}
}
8 changes: 4 additions & 4 deletions Sources/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6294,7 +6294,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
return nil
}

private func resolvedShortcutEventWindow(_ event: NSEvent) -> NSWindow? {
func resolvedShortcutEventWindow(_ event: NSEvent) -> NSWindow? {
if let window = event.window {
return window
}
Expand Down Expand Up @@ -8079,7 +8079,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
return fallback
}

private func shortcutEventHasAddressableWindow(_ event: NSEvent?) -> Bool {
func shortcutEventHasAddressableWindow(_ event: NSEvent?) -> Bool {
guard let event else { return false }
// NSEvent.windowNumber can be 0 for responder-chain events that are not
// actually bound to an NSWindow (notably some WebKit key paths).
Expand Down Expand Up @@ -13383,7 +13383,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
}

if matchConfiguredShortcut(event: event, action: .closeWorkspace) {
tabManagerForFocusedCloseShortcut(event: event)?.closeCurrentWorkspaceWithConfirmation()
_ = closeWorkspaceFromFocusedShortcut(event: event)
return true
}

Expand Down Expand Up @@ -15747,7 +15747,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
return nil
}

private func activateMainWindowContext(_ context: MainWindowContext?) {
func activateMainWindowContext(_ context: MainWindowContext?) {
guard let context else {
tabManager = nil
sidebarState = nil
Expand Down
37 changes: 37 additions & 0 deletions Sources/TabManager+CloseConfirmationTelemetry.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import AppKit
import Foundation

extension TabManager {
func recordCloseConfirmationTarget(workspaceIds: [UUID]) {
#if DEBUG
let joinedWorkspaceIds = workspaceIds.map(\.uuidString).joined(separator: ",")
UITestRecorder.record([
"closeConfirmationTargetWindowId": AppDelegate.shared?.windowId(for: self)?.uuidString ?? "",
"closeConfirmationTargetWorkspaceId": workspaceIds.first?.uuidString ?? "",
"closeConfirmationTargetWorkspaceIds": joinedWorkspaceIds,
])
#endif
}

func recordCloseConfirmationSheetPresentation(hostWindow: NSWindow) {
#if DEBUG
var updates = [
"closeConfirmationPresentation": "sheet",
"closeConfirmationAttachedSheet": "1",
]
if let hostWindowId = AppDelegate.shared?.mainWindowId(from: hostWindow)?.uuidString {
updates["closeConfirmationHostWindowId"] = hostWindowId
}
UITestRecorder.record(updates)
#endif
}

func recordCloseConfirmationAppModalPresentation(hostWindowHadAttachedSheet: Bool) {
#if DEBUG
UITestRecorder.record([
"closeConfirmationPresentation": "appModal",
"closeConfirmationAttachedSheet": hostWindowHadAttachedSheet ? "1" : "0",
])
#endif
}
}
18 changes: 4 additions & 14 deletions Sources/TabManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2201,6 +2201,7 @@ class TabManager: ObservableObject {

@discardableResult
func closeWorkspaceWithConfirmation(_ workspace: Workspace) -> Bool {
recordCloseConfirmationTarget(workspaceIds: [workspace.id])
if workspace.isPinned {
guard confirmPinnedWorkspaceClose(source: .workspace) else { return false }
closeWorkspaceIfRunningProcess(workspace, requiresConfirmation: false)
Expand Down Expand Up @@ -2264,6 +2265,7 @@ class TabManager: ObservableObject {
closeWorkspaceFromCloseTabGesture(workspaces[0])
return
}
recordCloseConfirmationTarget(workspaceIds: workspaces.map(\.id))

let plan = closeWorkspacesPlan(for: workspaces)
if shouldConfirmClose(requiresConfirmation: true, source: .tabClose) {
Expand Down Expand Up @@ -2394,24 +2396,12 @@ class TabManager: ObservableObject {
alert,
presentingWindow: closeConfirmationPresentingWindow()
) { presentation in
#if DEBUG
switch presentation {
case .sheet(let hostWindow):
// The sheet attaches after this hook returns, so read the
// attachment on the next runloop turn (during the modal loop).
DispatchQueue.main.async {
UITestRecorder.record([
"closeConfirmationPresentation": "sheet",
"closeConfirmationAttachedSheet": hostWindow.attachedSheet == nil ? "0" : "1",
])
}
self.recordCloseConfirmationSheetPresentation(hostWindow: hostWindow)
case .appModal(let hostWindowHadAttachedSheet):
UITestRecorder.record([
"closeConfirmationPresentation": "appModal",
"closeConfirmationAttachedSheet": hostWindowHadAttachedSheet ? "1" : "0",
])
self.recordCloseConfirmationAppModalPresentation(hostWindowHadAttachedSheet: hostWindowHadAttachedSheet)
}
#endif
}
}

Expand Down
12 changes: 12 additions & 0 deletions cmux.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
C4160A030000000000000001 /* AppDelegate+ClosedItemHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4160A030000000000000002 /* AppDelegate+ClosedItemHistory.swift */; };
C3677004000000000000001 /* AppDelegate+CmuxSSHURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3677004000000000000002 /* AppDelegate+CmuxSSHURL.swift */; };
E3309A01 /* AppDelegate+EqualizeSplitsShortcut.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3309A02 /* AppDelegate+EqualizeSplitsShortcut.swift */; };
F8C0A0010000000000000001 /* AppDelegate+FocusedWorkspaceCloseShortcut.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8C0A0010000000000000002 /* AppDelegate+FocusedWorkspaceCloseShortcut.swift */; };
C4160A040000000000000001 /* AppDelegate+FocusHistoryContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4160A040000000000000002 /* AppDelegate+FocusHistoryContextMenu.swift */; };
3865A0063865A0063865A006 /* AppDelegate+GlobalSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3865B0063865B0063865B006 /* AppDelegate+GlobalSearch.swift */; };
D7AB00000000000000000001 /* AppDelegate+MoveTabToNewWorkspace.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7AB00000000000000000002 /* AppDelegate+MoveTabToNewWorkspace.swift */; };
Expand Down Expand Up @@ -174,6 +175,7 @@
B9000025A1B2C3D4E5F60719 /* CloseWindowConfirmDialogUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9000026A1B2C3D4E5F60719 /* CloseWindowConfirmDialogUITests.swift */; };
B9000023A1B2C3D4E5F60719 /* CloseWorkspaceCmdDUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9000022A1B2C3D4E5F60719 /* CloseWorkspaceCmdDUITests.swift */; };
B900001AA1B2C3D4E5F60719 /* CloseWorkspaceConfirmDialogUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9000019A1B2C3D4E5F60719 /* CloseWorkspaceConfirmDialogUITests.swift */; };
F8C0A0030000000000000001 /* CloseWorkspacesConfirmDialogUITests+FocusedWindowHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8C0A0030000000000000002 /* CloseWorkspacesConfirmDialogUITests+FocusedWindowHelpers.swift */; };
B900001BA1B2C3D4E5F60719 /* CloseWorkspacesConfirmDialogUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B900001CA1B2C3D4E5F60719 /* CloseWorkspacesConfirmDialogUITests.swift */; };
C10D00010000000000000001 /* CloudVMActionLauncher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C10D00020000000000000002 /* CloudVMActionLauncher.swift */; };
B900000BA1B2C3D4E5F60719 /* cmux in Copy CLI */ = {isa = PBXBuildFile; fileRef = B9000004A1B2C3D4E5F60719 /* cmux */; };
Expand Down Expand Up @@ -812,6 +814,7 @@
D7AB00000000000000B041 /* SupersededPhoneDismissBuffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7AB00000000000000B040 /* SupersededPhoneDismissBuffer.swift */; };
A5001303 /* SurfaceSearchOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001301 /* SurfaceSearchOverlay.swift */; };
C9A5720BC9A5720BC9A5720B /* TabItemView+WorkspaceGroups.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9A5720CC9A5720CC9A5720C /* TabItemView+WorkspaceGroups.swift */; };
F8C0A0020000000000000001 /* TabManager+CloseConfirmationTelemetry.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8C0A0020000000000000002 /* TabManager+CloseConfirmationTelemetry.swift */; };
D7AB00000000000000000013 /* TabManager+DetachedWorkspace.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7AB00000000000000000014 /* TabManager+DetachedWorkspace.swift */; };
E3309A03 /* TabManager+EqualizeSplits.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3309A04 /* TabManager+EqualizeSplits.swift */; };
E3B7A400000000000000000B /* TabManager+FocusHistoryHosting.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3B7A400000000000000000C /* TabManager+FocusHistoryHosting.swift */; };
Expand Down Expand Up @@ -1130,6 +1133,7 @@
C4160A030000000000000002 /* AppDelegate+ClosedItemHistory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+ClosedItemHistory.swift"; sourceTree = "<group>"; };
C3677004000000000000002 /* AppDelegate+CmuxSSHURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+CmuxSSHURL.swift"; sourceTree = "<group>"; };
E3309A02 /* AppDelegate+EqualizeSplitsShortcut.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+EqualizeSplitsShortcut.swift"; sourceTree = "<group>"; };
F8C0A0010000000000000002 /* AppDelegate+FocusedWorkspaceCloseShortcut.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+FocusedWorkspaceCloseShortcut.swift"; sourceTree = "<group>"; };
C4160A040000000000000002 /* AppDelegate+FocusHistoryContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+FocusHistoryContextMenu.swift"; sourceTree = "<group>"; };
3865B0063865B0063865B006 /* AppDelegate+GlobalSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Search/AppDelegate+GlobalSearch.swift"; sourceTree = "<group>"; };
D7AB00000000000000000002 /* AppDelegate+MoveTabToNewWorkspace.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+MoveTabToNewWorkspace.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1247,6 +1251,7 @@
B9000026A1B2C3D4E5F60719 /* CloseWindowConfirmDialogUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseWindowConfirmDialogUITests.swift; sourceTree = "<group>"; };
B9000022A1B2C3D4E5F60719 /* CloseWorkspaceCmdDUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseWorkspaceCmdDUITests.swift; sourceTree = "<group>"; };
B9000019A1B2C3D4E5F60719 /* CloseWorkspaceConfirmDialogUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseWorkspaceConfirmDialogUITests.swift; sourceTree = "<group>"; };
F8C0A0030000000000000002 /* CloseWorkspacesConfirmDialogUITests+FocusedWindowHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CloseWorkspacesConfirmDialogUITests+FocusedWindowHelpers.swift"; sourceTree = "<group>"; };
B900001CA1B2C3D4E5F60719 /* CloseWorkspacesConfirmDialogUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseWorkspacesConfirmDialogUITests.swift; sourceTree = "<group>"; };
C10D00020000000000000002 /* CloudVMActionLauncher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudVMActionLauncher.swift; sourceTree = "<group>"; };
B9000004A1B2C3D4E5F60719 /* cmux */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = cmux; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -1819,6 +1824,7 @@
D7AB00000000000000B040 /* SupersededPhoneDismissBuffer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupersededPhoneDismissBuffer.swift; sourceTree = "<group>"; };
A5001301 /* SurfaceSearchOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Find/SurfaceSearchOverlay.swift; sourceTree = "<group>"; };
C9A5720CC9A5720CC9A5720C /* TabItemView+WorkspaceGroups.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TabItemView+WorkspaceGroups.swift"; sourceTree = "<group>"; };
F8C0A0020000000000000002 /* TabManager+CloseConfirmationTelemetry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TabManager+CloseConfirmationTelemetry.swift"; sourceTree = "<group>"; };
D7AB00000000000000000014 /* TabManager+DetachedWorkspace.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TabManager+DetachedWorkspace.swift"; sourceTree = "<group>"; };
E3309A04 /* TabManager+EqualizeSplits.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TabManager+EqualizeSplits.swift"; sourceTree = "<group>"; };
E3B7A400000000000000000C /* TabManager+FocusHistoryHosting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TabManager+FocusHistoryHosting.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2160,6 +2166,7 @@
B9000013A1B2C3D4E5F60719 /* JumpToUnreadUITests.swift */,
B9000022A1B2C3D4E5F60719 /* CloseWorkspaceCmdDUITests.swift */,
B9000019A1B2C3D4E5F60719 /* CloseWorkspaceConfirmDialogUITests.swift */,
F8C0A0030000000000000002 /* CloseWorkspacesConfirmDialogUITests+FocusedWindowHelpers.swift */,
B900001CA1B2C3D4E5F60719 /* CloseWorkspacesConfirmDialogUITests.swift */,
B9000026A1B2C3D4E5F60719 /* CloseWindowConfirmDialogUITests.swift */,
B9000016A1B2C3D4E5F60719 /* MultiWindowNotificationsUITests.swift */,
Expand Down Expand Up @@ -2419,6 +2426,7 @@
C10D51700000000000000001 /* ClosedItemHistory.swift */,
D1FFC0DE000000000000D001 /* DiffCommentSubmissionPool.swift */,
D1FFC0DE000000000000A001 /* DiffCommentStore.swift */,
F8C0A0020000000000000002 /* TabManager+CloseConfirmationTelemetry.swift */,
E3309A04 /* TabManager+EqualizeSplits.swift */,
C5CC15CEAA80B779ADEE78AA /* WorkspaceActionDispatcher.swift */,
D7AB00000000000000000014 /* TabManager+DetachedWorkspace.swift */,
Expand Down Expand Up @@ -2561,6 +2569,7 @@
C3677002000000000000002 /* CmuxSSHURLRequest.swift */,
C3677004000000000000002 /* AppDelegate+CmuxSSHURL.swift */,
E3309A02 /* AppDelegate+EqualizeSplitsShortcut.swift */,
F8C0A0010000000000000002 /* AppDelegate+FocusedWorkspaceCloseShortcut.swift */,
D7AB00000000000000000002 /* AppDelegate+MoveTabToNewWorkspace.swift */,
2907A0022907A0022907A002 /* AppDelegate+RecoverableMainWindowRoutes.swift */,
4EDE10010000000000000002 /* AppDelegate+NotificationDeliverySeams.swift */,
Expand Down Expand Up @@ -3613,6 +3622,7 @@
C4160A030000000000000001 /* AppDelegate+ClosedItemHistory.swift in Sources */,
C3677004000000000000001 /* AppDelegate+CmuxSSHURL.swift in Sources */,
E3309A01 /* AppDelegate+EqualizeSplitsShortcut.swift in Sources */,
F8C0A0010000000000000001 /* AppDelegate+FocusedWorkspaceCloseShortcut.swift in Sources */,
C4160A040000000000000001 /* AppDelegate+FocusHistoryContextMenu.swift in Sources */,
3865A0063865A0063865A006 /* AppDelegate+GlobalSearch.swift in Sources */,
D7AB00000000000000000001 /* AppDelegate+MoveTabToNewWorkspace.swift in Sources */,
Expand Down Expand Up @@ -4009,6 +4019,7 @@
D7AB00000000000000B041 /* SupersededPhoneDismissBuffer.swift in Sources */,
A5001303 /* SurfaceSearchOverlay.swift in Sources */,
C9A5720BC9A5720BC9A5720B /* TabItemView+WorkspaceGroups.swift in Sources */,
F8C0A0020000000000000001 /* TabManager+CloseConfirmationTelemetry.swift in Sources */,
D7AB00000000000000000013 /* TabManager+DetachedWorkspace.swift in Sources */,
E3309A03 /* TabManager+EqualizeSplits.swift in Sources */,
E3B7A400000000000000000B /* TabManager+FocusHistoryHosting.swift in Sources */,
Expand Down Expand Up @@ -4225,6 +4236,7 @@
B9000025A1B2C3D4E5F60719 /* CloseWindowConfirmDialogUITests.swift in Sources */,
B9000023A1B2C3D4E5F60719 /* CloseWorkspaceCmdDUITests.swift in Sources */,
B900001AA1B2C3D4E5F60719 /* CloseWorkspaceConfirmDialogUITests.swift in Sources */,
F8C0A0030000000000000001 /* CloseWorkspacesConfirmDialogUITests+FocusedWindowHelpers.swift in Sources */,
B900001BA1B2C3D4E5F60719 /* CloseWorkspacesConfirmDialogUITests.swift in Sources */,
C0DE32470000000000000005 /* CommandPaletteIdentifierClipboardUITests.swift in Sources */,
D10000000000000000B00000 /* ControlSocketReadinessUITestSupport.swift in Sources */,
Expand Down
Loading
Loading