Skip to content

Commit

Permalink
feat: ui appears faster + is scrollable (closes #171, closes #128, cl…
Browse files Browse the repository at this point in the history
…oses #89)

* Replaced the NSCollectionView with a custom NSView. Benefit includes simpler code, and control over recycling logic.
* In cases where the user has many open windows, replaced the downscaling strategy with a scrollable view
* Added a preference: align thumbnails to the left or center
  • Loading branch information
lwouis committed Mar 24, 2020
1 parent 2008025 commit 9dca7b0
Show file tree
Hide file tree
Showing 19 changed files with 326 additions and 319 deletions.
38 changes: 16 additions & 22 deletions alt-tab-macos.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
D04BA004884A273D4D2D3EF1 /* HelperExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BAD91161791D42FEC4A60 /* HelperExtensions.swift */; };
D04BA084CD1236EC78D90A01 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D04BACCBE5F97BE9B6CA645B /* Localizable.strings */; };
D04BA100BD0F47828EB649FF /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D04BAAEC2847830A3991F8D1 /* InfoPlist.strings */; };
D04BA11E56383D082D7BE5A5 /* ThumbnailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BAA998119CAA8B70A2B67 /* ThumbnailsView.swift */; };
D04BA14D93726795A6937832 /* LabelAndControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BA2526DC6726E0F7ACF7C /* LabelAndControl.swift */; };
D04BA15A1B0C4871EA7CB899 /* GeneralTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BACE22DC907F03D193075 /* GeneralTab.swift */; };
D04BA1B133D53572D7B312C2 /* CollectionViewItemFontIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BA1DF8CAB2FAB7FE9244B /* CollectionViewItemFontIcon.swift */; };
D04BA1CEC6B9C8945FEC8740 /* CollectionViewItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BA258B56193958D60978A /* CollectionViewItemView.swift */; };
D04BA263682C4EA2F85B6CF0 /* QA.md in Sources */ = {isa = PBXBuildFile; fileRef = D04BACAE340252C1E910D3CE /* QA.md */; };
D04BA1B133D53572D7B312C2 /* ThumbnailFontIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BA1DF8CAB2FAB7FE9244B /* ThumbnailFontIconView.swift */; };
D04BA1CEC6B9C8945FEC8740 /* ThumbnailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BA258B56193958D60978A /* ThumbnailView.swift */; };
D04BA26A691D56031FCCF00C /* Sysctl.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BA8DB8AA7E5570DAC568A /* Sysctl.swift */; };
D04BA276B3241D440F65B149 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D04BA5C2BB394F1624DD5B45 /* InfoPlist.strings */; };
D04BA2A6FF9DDDC5A1A68E36 /* Applications.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BA282BB16C1554595A968 /* Applications.swift */; };
Expand All @@ -30,12 +30,10 @@
D04BA5F99B45DC13B9E9DD91 /* Keyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BA8276B3D3905E80B1739 /* Keyboard.swift */; };
D04BA6187A91A847844B6ABB /* Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BA015A45DE7AFDC9794FE /* Window.swift */; };
D04BA691CB6082A3C39CBC89 /* TabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BAE757BB2B605234FBF58 /* TabViewController.swift */; };
D04BA69D47B5E60A6AD9CBD9 /* CollectionViewItemTitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BAD1297730B191E96E7FE /* CollectionViewItemTitle.swift */; };
D04BA6C953494839648107D1 /* CollectionViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BA2A4A4140AF3E09DA94D /* CollectionViewItem.swift */; };
D04BA69D47B5E60A6AD9CBD9 /* ThumbnailTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BAD1297730B191E96E7FE /* ThumbnailTitleView.swift */; };
D04BA737008AA2CD4E230A21 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BA10777505D8A67ABD186 /* Application.swift */; };
D04BA73E90EFEF8247A5105D /* CGWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BAC34CFD42A7F6F1F01C0 /* CGWindow.swift */; };
D04BA76A74267B1346D23687 /* GridView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BA6D57A1456C07318B8EA /* GridView.swift */; };
D04BA76DDB00FC50D203D62C /* CollectionViewFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BAC2FEF7248B7BF9579E2 /* CollectionViewFlowLayout.swift */; };
D04BA775CF3F8D9394A1E256 /* Screen.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BA68C2561D9EE4FD851B8 /* Screen.swift */; };
D04BA7BE7F3DD24D58ACE942 /* AppearanceTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BA64F1F344007EA13BA05 /* AppearanceTab.swift */; };
D04BA7F86F1926FBE31F44BF /* BaseLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04BA53992F116E5E704CAB3 /* BaseLabel.swift */; };
Expand Down Expand Up @@ -85,15 +83,14 @@
D04BA123744B0C27E9F54B05 /* codesign_sparkle_embedded_apps.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = codesign_sparkle_embedded_apps.sh; sourceTree = "<group>"; };
D04BA1C3E42AC44CA2C5D3D8 /* app-icon.svg */ = {isa = PBXFileReference; lastKnownFileType = file.svg; path = "app-icon.svg"; sourceTree = "<group>"; };
D04BA1D80F4EEF2A91BAD29C /* release.config.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = release.config.js; sourceTree = "<group>"; };
D04BA1DF8CAB2FAB7FE9244B /* CollectionViewItemFontIcon.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewItemFontIcon.swift; sourceTree = "<group>"; };
D04BA1DF8CAB2FAB7FE9244B /* ThumbnailFontIconView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThumbnailFontIconView.swift; sourceTree = "<group>"; };
D04BA1FC9022590D7AA02486 /* 1 window - 1 line.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "1 window - 1 line.jpg"; sourceTree = "<group>"; };
D04BA2526DC6726E0F7ACF7C /* LabelAndControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LabelAndControl.swift; sourceTree = "<group>"; };
D04BA258B56193958D60978A /* CollectionViewItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewItemView.swift; sourceTree = "<group>"; };
D04BA258B56193958D60978A /* ThumbnailView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThumbnailView.swift; sourceTree = "<group>"; };
D04BA26154AB2A2897E08CAF /* windows-theme.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "windows-theme.jpg"; sourceTree = "<group>"; };
D04BA26C75F76C277653C932 /* FeedbackWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedbackWindow.swift; sourceTree = "<group>"; };
D04BA27C87B86C4484A5B15B /* TabViewItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabViewItem.swift; sourceTree = "<group>"; };
D04BA282BB16C1554595A968 /* Applications.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Applications.swift; sourceTree = "<group>"; };
D04BA2A4A4140AF3E09DA94D /* CollectionViewItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewItem.swift; sourceTree = "<group>"; };
D04BA2A4F257F4DCE1421758 /* Podfile.lock */ = {isa = PBXFileReference; lastKnownFileType = file.lock; path = Podfile.lock; sourceTree = "<group>"; };
D04BA2C7B51F68651B3C60E2 /* 6 windows - 1 line.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "6 windows - 1 line.jpg"; sourceTree = "<group>"; };
D04BA32F25860B686DFE818A /* 3 windows - 1 line.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "3 windows - 1 line.jpg"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -133,6 +130,7 @@
D04BA9B93823398A542FF7A0 /* Preferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
D04BA9EF65B2E7AF9E3ADCA3 /* 2 windows - 1 line.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "2 windows - 1 line.jpg"; sourceTree = "<group>"; };
D04BAA34E0CB00DED7C04B4F /* 2-rows.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "2-rows.jpg"; sourceTree = "<group>"; };
D04BAA998119CAA8B70A2B67 /* ThumbnailsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThumbnailsView.swift; sourceTree = "<group>"; };
D04BAA9E0539EE620D08F63F /* fr */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = fr; path = InfoPlist.strings; sourceTree = "<group>"; };
D04BAAB92261FC04854FDDE9 /* App.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
D04BAAF760E3A8A22BDA84D6 /* appcast.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = appcast.xml; sourceTree = "<group>"; };
Expand All @@ -142,7 +140,6 @@
D04BABFEC8F9DF41BB7A449E /* import_codesign_certificate_into_keychain.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = import_codesign_certificate_into_keychain.sh; sourceTree = "<group>"; };
D04BAC02D60EF22D9CC7D969 /* commitlint.config.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = commitlint.config.js; sourceTree = "<group>"; };
D04BAC159731F80FDAF4EA6C /* 1-row.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "1-row.jpg"; sourceTree = "<group>"; };
D04BAC2FEF7248B7BF9579E2 /* CollectionViewFlowLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewFlowLayout.swift; sourceTree = "<group>"; };
D04BAC2FF99F629CD4ED20FC /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = "<group>"; };
D04BAC34CFD42A7F6F1F01C0 /* CGWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGWindow.swift; sourceTree = "<group>"; };
D04BAC6AFC7F06D1A567F27A /* replace_environment_variables_in_app.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = replace_environment_variables_in_app.sh; sourceTree = "<group>"; };
Expand All @@ -152,7 +149,7 @@
D04BACD976030676FD0761D5 /* Windows.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Windows.swift; sourceTree = "<group>"; };
D04BACE22DC907F03D193075 /* GeneralTab.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneralTab.swift; sourceTree = "<group>"; };
D04BACEE8D430B8CAAD8C4CD /* BoldLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoldLabel.swift; sourceTree = "<group>"; };
D04BAD1297730B191E96E7FE /* CollectionViewItemTitle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewItemTitle.swift; sourceTree = "<group>"; };
D04BAD1297730B191E96E7FE /* ThumbnailTitleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThumbnailTitleView.swift; sourceTree = "<group>"; };
D04BAD241A6928F45355B315 /* es */ = {isa = PBXFileReference; fileEncoding = 2483028224; lastKnownFileType = text.plist.strings; name = es; path = Localizable.strings; sourceTree = "<group>"; };
D04BAD40CE2D3A8AAC3819D0 /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = file.gitignore; path = .gitignore; sourceTree = "<group>"; };
D04BAD60C97E609A759E721E /* UpdatesTab.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdatesTab.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -375,12 +372,11 @@
D04BA62E30174C336E4080FA /* main-window */ = {
isa = PBXGroup;
children = (
D04BAC2FEF7248B7BF9579E2 /* CollectionViewFlowLayout.swift */,
D04BA2A4A4140AF3E09DA94D /* CollectionViewItem.swift */,
D04BA653BD073CB58E2CFC93 /* ThumbnailsPanel.swift */,
D04BAD1297730B191E96E7FE /* CollectionViewItemTitle.swift */,
D04BA1DF8CAB2FAB7FE9244B /* CollectionViewItemFontIcon.swift */,
D04BA258B56193958D60978A /* CollectionViewItemView.swift */,
D04BAD1297730B191E96E7FE /* ThumbnailTitleView.swift */,
D04BA1DF8CAB2FAB7FE9244B /* ThumbnailFontIconView.swift */,
D04BA258B56193958D60978A /* ThumbnailView.swift */,
D04BAA998119CAA8B70A2B67 /* ThumbnailsView.swift */,
);
path = "main-window";
sourceTree = "<group>";
Expand Down Expand Up @@ -678,12 +674,10 @@
D04BA3CF766857381519B892 /* DispatchQueues.swift in Sources */,
D04BA48B00B4211A465C7337 /* DebugProfile.swift in Sources */,
D04BAB68B7B8D1B548BC3AD5 /* App.swift in Sources */,
D04BA76DDB00FC50D203D62C /* CollectionViewFlowLayout.swift in Sources */,
D04BA6C953494839648107D1 /* CollectionViewItem.swift in Sources */,
D04BAB048DE698E013577C51 /* ThumbnailsPanel.swift in Sources */,
D04BA69D47B5E60A6AD9CBD9 /* CollectionViewItemTitle.swift in Sources */,
D04BA1B133D53572D7B312C2 /* CollectionViewItemFontIcon.swift in Sources */,
D04BA1CEC6B9C8945FEC8740 /* CollectionViewItemView.swift in Sources */,
D04BA69D47B5E60A6AD9CBD9 /* ThumbnailTitleView.swift in Sources */,
D04BA1B133D53572D7B312C2 /* ThumbnailFontIconView.swift in Sources */,
D04BA1CEC6B9C8945FEC8740 /* ThumbnailView.swift in Sources */,
D04BA3BFB0CDF4ED343914B2 /* PreferencesWindow.swift in Sources */,
D04BA691CB6082A3C39CBC89 /* TabViewController.swift in Sources */,
D04BA14D93726795A6937832 /* LabelAndControl.swift in Sources */,
Expand All @@ -700,7 +694,7 @@
D04BAF25E67A5B31CF7676DB /* TextField.swift in Sources */,
D04BAD2A7F2E8BF64EE982E9 /* TextArea.swift in Sources */,
D04BA7F86F1926FBE31F44BF /* BaseLabel.swift in Sources */,
D04BA263682C4EA2F85B6CF0 /* QA.md in Sources */,
D04BA11E56383D082D7BE5A5 /* ThumbnailsView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
1 change: 0 additions & 1 deletion config/base.xcconfig
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ MACOSX_DEPLOYMENT_TARGET = 10.12
SWIFT_VERSION = 4.2
INFOPLIST_FILE = Info.plist
CODE_SIGN_ENTITLEMENTS = alt_tab_macos.entitlements
IDEDerivedDataPathOverride = DerivedData
FRAMEWORK_SEARCH_PATHS[config=*] = $(inherited) /System/Library/PrivateFrameworks // for SkyLight.framework
ENABLE_HARDENED_RUNTIME = YES // for notarization
OTHER_CODE_SIGN_FLAGS = --timestamp --deep --options runtime // for notarization
Expand Down
21 changes: 18 additions & 3 deletions src/api-wrappers/HelperExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,23 @@ extension NSObject {

extension Array where Element == Window {
func firstIndexThatMatches(_ element: AXUIElement) -> Self.Index? {
// `CFEqual` is safer than comparing `CGWindowID` because it will succeed even if the window is deallocated
// == is safer than comparing `CGWindowID` because it will succeed even if the window is deallocated
// by the OS, in which case the `CGWindowID` will be `-1`
return firstIndex(where: { CFEqual($0.axUiElement, element) })
return firstIndex(where: { $0.axUiElement == element })
}

func firstWindowThatMatches(_ element: AXUIElement) -> Window? {
func firstWindowThatMatches(_ element: AXUIElement) -> Element? {
guard let index = firstIndexThatMatches(element) else { return nil }
return self[index]
}

mutating func insertAndScaleRecycledPool(_ elements: [Element], at i: Int) {
insert(contentsOf: elements, at: i)
let neededRecycledViews = count - ThumbnailsView.recycledViews.count
if neededRecycledViews > 0 {
(1...neededRecycledViews).forEach { _ in ThumbnailsView.recycledViews.append(ThumbnailView()) }
}
}
}

extension NSView {
Expand Down Expand Up @@ -164,3 +172,10 @@ extension NSImage {
return img
}
}

// only assign if different; useful for performance
func assignIfDifferent<T: Equatable>(_ a: UnsafeMutablePointer<T>, _ b: T) {
if a.pointee != b {
a.pointee = b
}
}
21 changes: 12 additions & 9 deletions src/logic/Application.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ class Application: NSObject {
}

func observeNewWindows() {
let newWindows = axUiElement!.windows()?
.filter { $0.isActualWindow(runningApplication) && Windows.list.firstIndexThatMatches($0) == nil } ?? []
addWindows(newWindows)
if let windows = axUiElement!.windows(), windows.count > 0 {
addWindows(windows.filter { $0.isActualWindow(runningApplication) && Windows.list.firstIndexThatMatches($0) == nil })
}
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
Expand All @@ -59,8 +59,11 @@ class Application: NSObject {
addAndObserveWindows()
}

private func addWindows(_ windows: [AXUIElement]) {
Windows.list.insert(contentsOf: windows.map { Window($0, self) }, at: 0)
private func addWindows(_ axWindows: [AXUIElement]) {
let windows = axWindows.map { Window($0, self) }
Windows.list.insertAndScaleRecycledPool(windows, at: 0)
windows.forEach { _ in Windows.moveFocusedWindowIndexAfterWindowCreatedInBackground() }
(App.shared as! App).refreshOpenUi()
}

private func observeEvents() {
Expand Down Expand Up @@ -101,11 +104,12 @@ private func eventApplicationActivated(_ app: App, _ element: AXUIElement) {
let appFocusedWindow = element.focusedWindow(),
let existingIndex = Windows.list.firstIndexThatMatches(appFocusedWindow) else { return }
Windows.list.insert(Windows.list.remove(at: existingIndex), at: 0)
app.refreshOpenUi()
}

private func eventApplicationHiddenOrShown(_ app: App, _ element: AXUIElement, _ type: String) {
for window in Windows.list {
guard CFEqual(window.application.axUiElement!, element) else { continue }
guard window.application.axUiElement! == element else { continue }
window.isHidden = type == kAXApplicationHiddenNotification
}
app.refreshOpenUi()
Expand All @@ -116,15 +120,14 @@ private func eventWindowCreated(_ app: App, _ element: AXUIElement, _ applicatio
// a window being un-minimized can trigger kAXWindowCreatedNotification
guard Windows.list.firstIndexThatMatches(element) == nil else { return }
let window = Window(element, application)
Windows.list.insert(window, at: 0)
Windows.list.insertAndScaleRecycledPool([window], at: 0)
Windows.moveFocusedWindowIndexAfterWindowCreatedInBackground()
// TODO: find a better way to get thumbnail of the new window
window.refreshThumbnail()
app.refreshOpenUi()
}

private func eventFocusedWindowChanged(_ app: App, _ element: AXUIElement) {
guard !app.appIsBeingUsed,
let existingIndex = Windows.list.firstIndexThatMatches(element) else { return }
Windows.list.insert(Windows.list.remove(at: existingIndex), at: 0)
app.refreshOpenUi()
}
8 changes: 7 additions & 1 deletion src/logic/Applications.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,17 @@ class Applications {
static func removeRunningApplications(_ runningApps: [NSRunningApplication]) {
for runningApp in runningApps {
Applications.list.removeAll(where: { $0.runningApplication.isEqual(runningApp) })
var indexesToRemove = [Int]()
Windows.list.enumerated().forEach { (index, window) in
if window.application.runningApplication.isEqual(runningApp) {
indexesToRemove.append(index)
}
}
Windows.list.removeAll(where: { $0.application.runningApplication.isEqual(runningApp) })
}
guard Windows.list.count > 0 else { (App.shared as! App).hideUi(); return }
// TODO: implement of more sophisticated way to decide which thumbnail gets focused on app quit
Windows.focusedWindowIndex = 1
Windows.updateFocusedWindowIndex(1)
(App.shared as! App).refreshOpenUi()
}

Expand Down
Loading

0 comments on commit 9dca7b0

Please sign in to comment.