diff --git a/src/ui/App.swift b/src/ui/App.swift index f801de04..c41bf01c 100644 --- a/src/ui/App.swift +++ b/src/ui/App.swift @@ -205,15 +205,16 @@ class App: AppCenterApplication { refreshOpenUiWithThrottling { guard self.appIsBeingUsed else { return } if !Windows.updatesBeforeShowing() { self.hideUi(); return } - self.refreshUi() + self.refreshUi(true) } } - - func refreshUi() { + + func refreshUi(_ preserveScrollPosition: Bool = false) { guard self.appIsBeingUsed else { return } + let preservedScrollOrigin = preserveScrollPosition ? thumbnailsPanel.tilesView.currentScrollOrigin() : nil Windows.updateSelectedWindow() guard self.appIsBeingUsed else { return } - self.thumbnailsPanel.updateContents() + self.thumbnailsPanel.updateContents(preservedScrollOrigin) guard self.appIsBeingUsed else { return } Windows.voiceOverWindow() // at this point TileViews are assigned to the window, and ready guard self.appIsBeingUsed else { return } diff --git a/src/ui/main-window/TilesPanel.swift b/src/ui/main-window/TilesPanel.swift index b3cdedd2..3569fc52 100644 --- a/src/ui/main-window/TilesPanel.swift +++ b/src/ui/main-window/TilesPanel.swift @@ -32,9 +32,9 @@ class TilesPanel: NSPanel { appearance = NSAppearance(named: Appearance.currentTheme == .dark ? .vibrantDark : .vibrantLight) } - func updateContents() { + func updateContents(_ preservedScrollOrigin: CGPoint?) { caTransaction { - tilesView.updateItemsAndLayout() + tilesView.updateItemsAndLayout(preservedScrollOrigin) guard App.app.appIsBeingUsed else { return } setContentSize(tilesView.contentView.frame.size) guard App.app.appIsBeingUsed else { return } @@ -130,4 +130,4 @@ extension TilesPanel: NSWindowDelegate { Applications.manuallyRefreshAllWindows() } } -} \ No newline at end of file +} diff --git a/src/ui/main-window/TilesView.swift b/src/ui/main-window/TilesView.swift index 0a292185..6737bb96 100644 --- a/src/ui/main-window/TilesView.swift +++ b/src/ui/main-window/TilesView.swift @@ -118,7 +118,7 @@ class TilesView { Windows.updateSelectedAndHoveredWindowIndex(targetIndex) } - func updateItemsAndLayout() { + func updateItemsAndLayout(_ preservedScrollOrigin: CGPoint?) { let widthMax = TilesPanel.maxThumbnailsWidth().rounded() if let (maxX, maxY, labelHeight, rowSignature) = layoutTileViews(widthMax) { layoutParentViews(maxX, widthMax, maxY, labelHeight) @@ -137,9 +137,27 @@ class TilesView { lastRowSignature = rowSignature } highlightStartView() + if let preservedScrollOrigin { + restoreScrollOrigin(preservedScrollOrigin) + } } } + func currentScrollOrigin() -> CGPoint { + scrollView.contentView.bounds.origin + } + + private func restoreScrollOrigin(_ scrollOrigin: CGPoint) { + guard let documentView = scrollView.documentView else { return } + let visibleSize = scrollView.contentView.bounds.size + let documentSize = documentView.frame.size + let maxX = max(0, documentSize.width - visibleSize.width) + let maxY = max(0, documentSize.height - visibleSize.height) + let clampedOrigin = CGPoint(x: min(max(0, scrollOrigin.x), maxX), y: min(max(0, scrollOrigin.y), maxY)) + scrollView.contentView.scroll(to: clampedOrigin) + scrollView.reflectScrolledClipView(scrollView.contentView) + } + private func layoutTileViews(_ widthMax: CGFloat) -> (CGFloat, CGFloat, CGFloat, [Int])? { let labelHeight = Self.layoutCache.labelHeight let height = TileView.height(labelHeight)