From 8419ad9fcebf648549488a26a03d60196add8e44 Mon Sep 17 00:00:00 2001 From: Louis Pontoise Date: Wed, 6 May 2020 23:33:26 +0900 Subject: [PATCH] fix: don't display tabbed windows (closes #258) --- src/api-wrappers/AXUIElement.swift | 5 ++-- src/logic/Application.swift | 8 +++---- src/logic/Windows.swift | 9 +++++++- src/logic/events/AccessibilityEvents.swift | 27 +++++++++++++--------- src/ui/App.swift | 20 ++++++++-------- 5 files changed, 39 insertions(+), 30 deletions(-) diff --git a/src/api-wrappers/AXUIElement.swift b/src/api-wrappers/AXUIElement.swift index 2a6049e1b..265c9387d 100644 --- a/src/api-wrappers/AXUIElement.swift +++ b/src/api-wrappers/AXUIElement.swift @@ -43,8 +43,9 @@ extension AXUIElement { return attribute(kAXTitleAttribute, String.self) } - func windows() -> [AXUIElement]? { - return attribute(kAXWindowsAttribute, [AXUIElement].self) + func windows(_ bundleIdentifier: String?) -> [AXUIElement]? { + return attribute(kAXWindowsAttribute, [AXUIElement].self)? + .filter { $0.isActualWindow(bundleIdentifier) } } func isMinimized() -> Bool { diff --git a/src/logic/Application.swift b/src/logic/Application.swift index a201a43c6..f78a6fbe5 100644 --- a/src/logic/Application.swift +++ b/src/logic/Application.swift @@ -8,7 +8,7 @@ class Application: NSObject { static let notifications = [ kAXApplicationActivatedNotification, - kAXFocusedWindowChangedNotification, + kAXMainWindowChangedNotification, kAXWindowCreatedNotification, kAXApplicationHiddenNotification, kAXApplicationShownNotification, @@ -51,10 +51,8 @@ class Application: NSObject { } func observeNewWindows() { - if let windows = axUiElement!.windows() { - let actualWindows = windows.filter { - $0.isActualWindow(runningApplication.bundleIdentifier) && Windows.list.firstIndexThatMatches($0) == nil - } + if let windows = axUiElement!.windows(runningApplication.bundleIdentifier) { + let actualWindows = windows.filter { Windows.list.firstIndexThatMatches($0) == nil } if actualWindows.count > 0 { addWindows(actualWindows) } diff --git a/src/logic/Windows.swift b/src/logic/Windows.swift index 0b3cba8f5..bfbfb2315 100644 --- a/src/logic/Windows.swift +++ b/src/logic/Windows.swift @@ -99,7 +99,14 @@ class Windows { !(!Preferences.showHiddenWindows && window.isHidden) && !(Preferences.appsToShow == .active && window.application.runningApplication != NSWorkspace.shared.frontmostApplication) && !(Preferences.spacesToShow == .active && window.spaceId != Spaces.currentSpaceId) && - !(Preferences.screensToShow == .showingAltTab && !isOnScreen(window, screen)) + !(Preferences.screensToShow == .showingAltTab && !isOnScreen(window, screen)) && + !isTabbed(window) + } + + static func isTabbed(_ window: Window) -> Bool { + let app = window.application + let windows = app.axUiElement?.windows(app.runningApplication.bundleIdentifier) + return windows?.first(where: { $0 == window.axUiElement }) == nil } static func isOnScreen(_ window: Window, _ screen: NSScreen) -> Bool { diff --git a/src/logic/events/AccessibilityEvents.swift b/src/logic/events/AccessibilityEvents.swift index cdb80e834..28762e4db 100644 --- a/src/logic/events/AccessibilityEvents.swift +++ b/src/logic/events/AccessibilityEvents.swift @@ -8,7 +8,7 @@ func axObserverCallback(observer: AXObserver, element: AXUIElement, notification case kAXApplicationHiddenNotification, kAXApplicationShownNotification: applicationHiddenOrShown(element, type) case kAXWindowCreatedNotification: windowCreated(element, applicationPointer) - case kAXFocusedWindowChangedNotification: focusedWindowChanged(element) + case kAXMainWindowChangedNotification: focusedWindowChanged(element, applicationPointer) case kAXUIElementDestroyedNotification: windowDestroyed(element) case kAXWindowMiniaturizedNotification, kAXWindowDeminiaturizedNotification: windowMiniaturizedOrDeminiaturized(element, type) @@ -19,11 +19,10 @@ func axObserverCallback(observer: AXObserver, element: AXUIElement, notification } private func applicationActivated(_ element: AXUIElement) { - guard !App.app.appIsBeingUsed, - let appFocusedWindow = element.focusedWindow(), + guard let appFocusedWindow = element.focusedWindow(), let existingIndex = Windows.list.firstIndexThatMatches(appFocusedWindow) else { return } Windows.list.insert(Windows.list.remove(at: existingIndex), at: 0) - App.app.refreshOpenUi([Windows.list[0], Windows.list[existingIndex]], true) + App.app.refreshOpenUi([Windows.list[0], Windows.list[existingIndex]]) } private func applicationHiddenOrShown(_ element: AXUIElement, _ type: String) { @@ -32,7 +31,7 @@ private func applicationHiddenOrShown(_ element: AXUIElement, _ type: String) { $0.application.axUiElement!.pid() == element.pid() } windows.forEach { $0.isHidden = type == kAXApplicationHiddenNotification } - App.app.refreshOpenUi(windows, true) + App.app.refreshOpenUi(windows) } private func windowCreated(_ element: AXUIElement, _ applicationPointer: UnsafeMutableRawPointer?) { @@ -46,11 +45,17 @@ private func windowCreated(_ element: AXUIElement, _ applicationPointer: UnsafeM App.app.refreshOpenUi([window]) } -private func focusedWindowChanged(_ element: AXUIElement) { - guard !App.app.appIsBeingUsed, - let existingIndex = Windows.list.firstIndexThatMatches(element) else { return } - Windows.list.insert(Windows.list.remove(at: existingIndex), at: 0) - App.app.refreshOpenUi([Windows.list[0], Windows.list[existingIndex]]) +private func focusedWindowChanged(_ element: AXUIElement, _ applicationPointer: UnsafeMutableRawPointer?) { + if let existingIndex = Windows.list.firstIndexThatMatches(element) { + Windows.list.insert(Windows.list.remove(at: existingIndex), at: 0) + App.app.refreshOpenUi([Windows.list[0], Windows.list[existingIndex]]) + } else { + let application = Unmanaged.fromOpaque(applicationPointer!).takeUnretainedValue() + if element.isActualWindow(application.runningApplication.bundleIdentifier) { + Windows.list.insert(Window(element, application), at: 0) + App.app.refreshOpenUi([Windows.list[0]]) + } + } } private func windowDestroyed(_ element: AXUIElement) { @@ -65,7 +70,7 @@ private func windowMiniaturizedOrDeminiaturized(_ element: AXUIElement, _ type: guard let index = Windows.list.firstIndexThatMatches(element) else { return } let window = Windows.list[index] window.isMinimized = type == kAXWindowMiniaturizedNotification - App.app.refreshOpenUi([window], true) + App.app.refreshOpenUi([window]) } private func windowTitleChanged(_ element: AXUIElement) { diff --git a/src/ui/App.swift b/src/ui/App.swift index 1c95678d9..70e816842 100644 --- a/src/ui/App.swift +++ b/src/ui/App.swift @@ -154,13 +154,13 @@ class App: NSApplication, NSApplicationDelegate { rebuildUi() } - func refreshOpenUi(_ windowsToUpdate: [Window]? = nil, _ updateWindowsInfo: Bool = false) { + func refreshOpenUi(_ windowsToUpdate: [Window]? = nil) { guard appIsBeingUsed else { return } let currentScreen = Screen.preferred() // fix screen between steps since it could change (e.g. mouse moved to another screen) // workaround: when Preferences > Mission Control > "Displays have separate Spaces" is unchecked, // switching between displays doesn't trigger .activeSpaceDidChangeNotification; we get the latest manually Spaces.refreshCurrentSpaceId() - refreshSpecificWindows(windowsToUpdate, updateWindowsInfo, currentScreen) + refreshSpecificWindows(windowsToUpdate, currentScreen) guard appIsBeingUsed else { return } thumbnailsPanel.thumbnailsView.updateItems(currentScreen) guard appIsBeingUsed else { return } @@ -169,18 +169,16 @@ class App: NSApplication, NSApplicationDelegate { Screen.repositionPanel(thumbnailsPanel, currentScreen, .appleCentered) } - private func refreshSpecificWindows(_ windowsToUpdate: [Window]?, _ updateWindowsInfo: Bool, _ currentScreen: NSScreen) -> ()? { + private func refreshSpecificWindows(_ windowsToUpdate: [Window]?, _ currentScreen: NSScreen) -> ()? { windowsToUpdate?.forEach { (window: Window) in guard appIsBeingUsed else { return } window.refreshThumbnail() - if updateWindowsInfo { - Windows.refreshIfWindowShouldBeShownToTheUser(window, currentScreen) - if !window.shouldShowTheUser && window.cgWindowId == Windows.focusedWindow()!.cgWindowId { - let stepWithClosestWindow = Windows.windowIndexAfterCycling(-1) > Windows.focusedWindowIndex ? 1 : -1 - Windows.cycleFocusedWindowIndex(stepWithClosestWindow) - } else { - Windows.updatesWindowSpace(window) - } + Windows.refreshIfWindowShouldBeShownToTheUser(window, currentScreen) + if !window.shouldShowTheUser && window.cgWindowId == Windows.focusedWindow()!.cgWindowId { + let stepWithClosestWindow = Windows.windowIndexAfterCycling(-1) > Windows.focusedWindowIndex ? 1 : -1 + Windows.cycleFocusedWindowIndex(stepWithClosestWindow) + } else { + Windows.updatesWindowSpace(window) } } }