feat: display quickly even with many open windows (closes #171)

BREAKING CHANGE: the window thumbnails are now updated *after* the UI is shown. AltTab will first display its window, with the first 3 thumbnails up-to-date, then asynchronously update the rest of the thumbnails one-by-one. This improves the experience of users with lots of windows open.
This commit is contained in:
Louis Pontoise authored and lwouis committed 2020-08-27 22:44:40 +09:00
1 parent 2bc09e64b2
commit da16a0b8f9
4 files changed
+34 -5

No files matched your search

+1 -1
View File
@@ -41,7 +41,7 @@ extension NSView {
}
}
extension Array {
extension Collection {
// forEach with each iteration run concurrently on the global queue
func forEachAsync(fn: @escaping (Element) -> Void) {
let group = DispatchGroup()
+1
View File
@@ -42,6 +42,7 @@ class Window {
self.position = position
self.title = bestEffortTitle(axTitle)
self.isTabbed = false
refreshThumbnail()
debugPrint("Adding window", cgWindowId, title ?? "nil", application.runningApplication.bundleIdentifier ?? "nil")
observeEvents()
}
+27 -1
View File
@@ -5,6 +5,8 @@ class Windows {
static var list = [Window]()
static var previousFocusedWindowIndex = Int(0)
static var focusedWindowIndex = Int(0)
// the first few thumbnails are the most commonly looked at; we pay special attention to them
static let criticalFirstThumbnails = 3
static func updateFocusedWindowIndex(_ newIndex: Int) {
previousFocusedWindowIndex = focusedWindowIndex
@@ -84,11 +86,35 @@ class Windows {
Windows.list = sortedTuples.map { $0.1 }
}
static func refreshAllThumbnails() {
static func refreshFirstFewThumbnailsSync() {
list.filter { $0.shouldShowTheUser }
.prefix(criticalFirstThumbnails)
.forEachAsync { window in window.refreshThumbnail() }
}
static func refreshThumbnailsAsync(_ screen: NSScreen, _ currentIndex: Int = criticalFirstThumbnails) {
guard App.app.appIsBeingUsed else { return }
BackgroundWork.mainQueueConcurrentWorkQueue.async {
if currentIndex < list.count {
let window = list[currentIndex]
if window.shouldShowTheUser {
window.refreshThumbnail()
DispatchQueue.main.async {
let view = ThumbnailsView.recycledViews[currentIndex]
if view.thumbnail.image != window.thumbnail {
view.thumbnail.image = window.thumbnail
let (thumbnailWidth, thumbnailHeight) = ThumbnailView.thumbnailSize(window.thumbnail, screen)
let thumbnailSize = NSSize(width: thumbnailWidth.rounded(), height: thumbnailHeight.rounded())
view.thumbnail.image?.size = thumbnailSize
view.thumbnail.frame.size = thumbnailSize
}
}
}
refreshThumbnailsAsync(screen, currentIndex + 1)
}
}
}
static func refreshWhichWindowsToShowTheUser(_ screen: NSScreen) {
Windows.list.forEach { (window: Window) in
refreshIfWindowShouldBeShownToTheUser(window, screen)
+5 -3
View File
@@ -228,21 +228,23 @@ class App: AppCenterApplication, NSApplicationDelegate {
Windows.updateFocusedWindowIndex(0)
Windows.cycleFocusedWindowIndex(1)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Preferences.windowDisplayDelay) { () -> () in
self.rebuildUi()
self.rebuildUi(screen)
}
} else {
cycleSelection(.leading)
}
}
func rebuildUi() {
func rebuildUi(_ screen: NSScreen = Screen.preferred()) {
guard appIsBeingUsed else { return }
Windows.refreshAllThumbnails()
Windows.refreshFirstFewThumbnailsSync()
guard appIsBeingUsed else { return }
Applications.refreshBadges()
guard appIsBeingUsed else { return }
refreshOpenUi()
guard appIsBeingUsed else { return }
thumbnailsPanel.show()
guard appIsBeingUsed else { return }
Windows.refreshThumbnailsAsync(screen)
}
}