Skip to content

Commit c927920

Browse files
committed
fix: focusing alt-tab own windows with alt-tab had jank (closes #501)
1 parent 3062566 commit c927920

File tree

5 files changed

+40
-25
lines changed

5 files changed

+40
-25
lines changed

src/logic/Window.swift

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -136,18 +136,22 @@ class Window {
136136
}
137137

138138
func focus() {
139-
// macOS bug: when switching to a System Preferences window in another space, it switches to that space,
140-
// but quickly switches back to another window in that space
141-
// You can reproduce this buggy behaviour by clicking on the dock icon, proving it's an OS bug
142-
BackgroundWork.accessibilityCommandsQueue.asyncWithCap { [weak self] in
143-
guard let self = self else { return }
144-
var elementConnection = UInt32(0)
145-
CGSGetWindowOwner(cgsMainConnectionId, self.cgWindowId, &elementConnection)
146-
var psn = ProcessSerialNumber()
147-
CGSGetConnectionPSN(elementConnection, &psn)
148-
_SLPSSetFrontProcessWithOptions(&psn, self.cgWindowId, .userGenerated)
149-
self.makeKeyWindow(psn)
150-
self.axUiElement.focusWindow()
139+
if application.runningApplication.processIdentifier == ProcessInfo.processInfo.processIdentifier {
140+
App.app.showSecondaryWindow(App.app.window(withWindowNumber: Int(cgWindowId)))
141+
} else {
142+
// macOS bug: when switching to a System Preferences window in another space, it switches to that space,
143+
// but quickly switches back to another window in that space
144+
// You can reproduce this buggy behaviour by clicking on the dock icon, proving it's an OS bug
145+
BackgroundWork.accessibilityCommandsQueue.asyncWithCap { [weak self] in
146+
guard let self = self else { return }
147+
var elementConnection = UInt32(0)
148+
CGSGetWindowOwner(cgsMainConnectionId, self.cgWindowId, &elementConnection)
149+
var psn = ProcessSerialNumber()
150+
CGSGetConnectionPSN(elementConnection, &psn)
151+
_SLPSSetFrontProcessWithOptions(&psn, self.cgWindowId, .userGenerated)
152+
self.makeKeyWindow(psn)
153+
self.axUiElement.focusWindow()
154+
}
151155
}
152156
}
153157

src/logic/events/AccessibilityEvents.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,7 @@ private func windowCreated(_ element: AXUIElement, _ pid: pid_t) throws {
123123
}
124124

125125
private func focusedWindowChanged(_ element: AXUIElement, _ pid: pid_t) throws {
126-
if let wid = try element.cgWindowId(),
127-
pid != ProcessInfo.processInfo.processIdentifier ||
128-
(App.app.preferencesWindow.isKeyWindow || App.app.feedbackWindow?.isKeyWindow ?? false) {
126+
if let wid = try element.cgWindowId() {
129127
let axTitle = try element.title()
130128
let subrole = try element.subrole()
131129
let role = try element.role()

src/ui/App.swift

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class App: AppCenterApplication, NSApplicationDelegate {
1414
static var app: App!
1515
var thumbnailsPanel: ThumbnailsPanel!
1616
var preferencesWindow: PreferencesWindow!
17-
var feedbackWindow: FeedbackWindow?
17+
var feedbackWindow: FeedbackWindow!
1818
var isFirstSummon = true
1919
var appIsBeingUsed = false
2020
var shortcutsShouldBeDisabled = false
@@ -51,6 +51,7 @@ class App: AppCenterApplication, NSApplicationDelegate {
5151
Spaces.initialDiscovery()
5252
Applications.initialDiscovery()
5353
self.preferencesWindow = PreferencesWindow()
54+
self.feedbackWindow = FeedbackWindow()
5455
KeyboardEvents.observe()
5556
MouseEvents.observe()
5657
// TODO: undeterministic; events in the queue may still be processing; good enough for now
@@ -107,7 +108,16 @@ class App: AppCenterApplication, NSApplicationDelegate {
107108
appIsBeingUsed = false
108109
isFirstSummon = true
109110
MouseEvents.toggle(false)
111+
hideThumbnailPanelWithoutChangingKeyWindow()
112+
}
113+
114+
// we don't want another window to become key when the thumbnailPanel is hidden
115+
func hideThumbnailPanelWithoutChangingKeyWindow() {
116+
preferencesWindow.canBecomeKey_ = false
117+
feedbackWindow.canBecomeKey_ = false
110118
thumbnailsPanel.orderOut(nil)
119+
preferencesWindow.canBecomeKey_ = true
120+
feedbackWindow.canBecomeKey_ = true
111121
}
112122

113123
func closeSelectedWindow() {
@@ -136,19 +146,18 @@ class App: AppCenterApplication, NSApplicationDelegate {
136146
}
137147

138148
@objc func showFeedbackPanel() {
139-
if feedbackWindow == nil {
140-
feedbackWindow = FeedbackWindow()
141-
}
142-
Screen.repositionPanel(feedbackWindow!, Screen.preferred(), .appleCentered)
143-
App.shared.activate(ignoringOtherApps: true)
144-
feedbackWindow!.makeKeyAndOrderFront(nil)
149+
showSecondaryWindow(feedbackWindow)
145150
}
146151

147152
@objc func showPreferencesWindow() {
148-
if let preferencesWindow = preferencesWindow {
149-
Screen.repositionPanel(preferencesWindow, Screen.preferred(), .appleCentered)
153+
showSecondaryWindow(preferencesWindow)
154+
}
155+
156+
func showSecondaryWindow(_ window: NSWindow?) {
157+
if let window = window {
158+
Screen.repositionPanel(window, Screen.preferred(), .appleCentered)
150159
App.shared.activate(ignoringOtherApps: true)
151-
preferencesWindow.makeKeyAndOrderFront(nil)
160+
window.makeKeyAndOrderFront(nil)
152161
}
153162
}
154163

src/ui/FeedbackWindow.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ class FeedbackWindow: NSWindow {
66
var email: TextArea!
77
var sendButton: NSButton!
88
var debugProfile: NSButton!
9+
var canBecomeKey_ = true
10+
override var canBecomeKey: Bool { canBecomeKey_ }
911

1012
override init(contentRect: NSRect, styleMask style: StyleMask, backing backingStoreType: BackingStoreType, defer flag: Bool) {
1113
super.init(contentRect: .zero, styleMask: style, backing: backingStoreType, defer: flag)

src/ui/preferences-window/PreferencesWindow.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import Cocoa
22

33
class PreferencesWindow: NSWindow, NSToolbarDelegate {
44
var toolbarItems = [NSToolbarItem.Identifier: (Int, NSToolbarItem, NSView)]()
5+
var canBecomeKey_ = true
6+
override var canBecomeKey: Bool { canBecomeKey_ }
57

68
override init(contentRect: NSRect, styleMask style: StyleMask, backing backingStoreType: BackingStoreType, defer flag: Bool) {
79
super.init(contentRect: .zero, styleMask: style, backing: backingStoreType, defer: flag)

0 commit comments

Comments
 (0)