Skip to content

Commit 689adc0

Browse files
committed
feat: improve responsiveness of showing the switcher
1 parent fab9d7c commit 689adc0

File tree

4 files changed

+40
-17
lines changed

4 files changed

+40
-17
lines changed

src/logic/Windows.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -314,18 +314,18 @@ class Windows {
314314
}
315315

316316
// dispatch screenshot requests off the main-thread, then wait for completion
317-
static func refreshThumbnails(_ windows: [Window], _ source: RefreshCausedBy) {
317+
static func refreshThumbnailsAsync(_ windows: [Window], _ source: RefreshCausedBy) {
318318
var eligibleWindows = [Window]()
319319
for window in windows {
320320
if !window.isWindowlessApp, let cgWindowId = window.cgWindowId, cgWindowId != CGWindowID(bitPattern: -1) {
321321
eligibleWindows.append(window)
322322
}
323323
}
324324
if eligibleWindows.isEmpty { return }
325-
screenshotEligibleWindowsAndRefreshUi(eligibleWindows, source)
325+
screenshotEligibleWindowsAndUpdateUi(eligibleWindows, source)
326326
}
327327

328-
private static func screenshotEligibleWindowsAndRefreshUi(_ eligibleWindows: [Window], _ source: RefreshCausedBy) {
328+
private static func screenshotEligibleWindowsAndUpdateUi(_ eligibleWindows: [Window], _ source: RefreshCausedBy) {
329329
for window in eligibleWindows {
330330
BackgroundWork.screenshotsQueue.addOperation { [weak window] in
331331
if source == .refreshOnlyThumbnailsAfterShowUi && !App.app.appIsBeingUsed { return }

src/ui/App.swift

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ class App: AppCenterApplication {
206206
if !windowsToScreenshot.isEmpty && SystemPermissions.screenRecordingPermission == .granted
207207
&& !Preferences.onlyShowApplications()
208208
&& (!Appearance.hideThumbnails || Preferences.previewFocusedWindow) {
209-
Windows.refreshThumbnails(windowsToScreenshot, source)
209+
Windows.refreshThumbnailsAsync(windowsToScreenshot, source)
210210
if source == .refreshOnlyThumbnailsAfterShowUi { return }
211211
}
212212
guard appIsBeingUsed else { return }
@@ -216,12 +216,7 @@ class App: AppCenterApplication {
216216
guard appIsBeingUsed else { return }
217217
Windows.updateFocusedWindowIndex()
218218
guard appIsBeingUsed else { return }
219-
thumbnailsPanel.thumbnailsView.updateItemsAndLayout()
220-
guard appIsBeingUsed else { return }
221-
thumbnailsPanel.setContentSize(thumbnailsPanel.thumbnailsView.contentView.frame.size)
222-
thumbnailsPanel.display()
223-
guard appIsBeingUsed else { return }
224-
NSScreen.preferred.repositionPanel(thumbnailsPanel)
219+
thumbnailsPanel.updateContents()
225220
guard appIsBeingUsed else { return }
226221
Windows.voiceOverWindow() // at this point ThumbnailViews are assigned to the window, and ready
227222
guard appIsBeingUsed else { return }
@@ -267,12 +262,10 @@ class App: AppCenterApplication {
267262
guard appIsBeingUsed else { return }
268263
Appearance.update()
269264
guard appIsBeingUsed else { return }
270-
thumbnailsPanel.makeKeyAndOrderFront(nil) // workaround: without this, switching between 2 screens make thumbnailPanel invisible
271-
KeyRepeatTimer.toggleRepeatingKeyNextWindow()
272-
guard appIsBeingUsed else { return }
273265
refreshOpenUi([], .showUi)
274266
guard appIsBeingUsed else { return }
275267
thumbnailsPanel.show()
268+
KeyRepeatTimer.toggleRepeatingKeyNextWindow()
276269
refreshOpenUi(Windows.list, .refreshOnlyThumbnailsAfterShowUi)
277270
}
278271

src/ui/generic-components/LightImageView.swift

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,46 @@ class LightImageView: NSView {
1313
super.init(frame: frameRect)
1414
translatesAutoresizingMaskIntoConstraints = false
1515
wantsLayer = true
16+
// configure the layer for efficient GPU-scaling
1617
layer!.contentsGravity = .resize
1718
layer!.magnificationFilter = .trilinear
1819
layer!.minificationFilter = .trilinear
1920
layer!.minificationFilterBias = 0.0
2021
layer!.shouldRasterize = false
22+
// disable implicit animations
23+
layer!.actions = [
24+
"contents": NSNull(),
25+
"bounds": NSNull(),
26+
"position": NSNull(),
27+
"contentsScale": NSNull()
28+
]
2129
}
2230

2331
func updateWithResizedCopy(_ image: CGImage?, _ size: NSSize) {
2432
let scaleFactor = NSScreen.preferred.backingScaleFactor
2533
if let image {
26-
CATransaction.begin()
27-
// disable implicit fade-in animation from CALayer
28-
CATransaction.setDisableActions(true)
2934
// alternatively, we could set layer!.contentsGravity to .center, and use the lines bellow to resize ourselves
3035
// let scaledSize = NSSize(width: size.width * scaleFactor, height: size.height * scaleFactor)
3136
// layer!.contents = image.resizedCopyWithCoreGraphics(scaledSize, fixBitmapInfo)
3237
// it would produce subjectively better quality, but the resizing would be done on the CPU so poor performance
3338
layer!.contents = image
3439
layer!.contentsScale = scaleFactor
35-
CATransaction.commit()
3640
}
3741
frame.size = size
3842
}
43+
44+
/// this schedules the image update for the next cycle
45+
/// The Panel will show with previous pictures, then they each will get updated quickly. It's a trade-off of getting AltTab interactive faster at the price of wrong data being shown
46+
/// I feel the UX is better without it. I may reconsider
47+
func updateWithResizedCopyAsync(_ image: CGImage?, _ size: NSSize) {
48+
layer!.contentsScale = NSScreen.preferred.backingScaleFactor
49+
frame.size = size
50+
if let image {
51+
// set image on another cycle so it doesn't block initial rendering
52+
DispatchQueue.main.async { [weak self] in
53+
guard let self else { return }
54+
self.layer!.contents = image
55+
}
56+
}
57+
}
3958
}

src/ui/main-window/ThumbnailsPanel.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@ class ThumbnailsPanel: NSPanel {
3030
appearance = NSAppearance(named: Appearance.currentTheme == .dark ? .vibrantDark : .vibrantLight)
3131
}
3232

33+
func updateContents() {
34+
CATransaction.begin()
35+
defer { CATransaction.commit() }
36+
CATransaction.setDisableActions(true)
37+
thumbnailsView.updateItemsAndLayout()
38+
guard App.app.appIsBeingUsed else { return }
39+
setContentSize(thumbnailsView.contentView.frame.size)
40+
guard App.app.appIsBeingUsed else { return }
41+
NSScreen.preferred.repositionPanel(self)
42+
}
43+
3344
override func orderOut(_ sender: Any?) {
3445
if Preferences.fadeOutAnimation {
3546
NSAnimationContext.runAnimationGroup(

0 commit comments

Comments
 (0)