From fd906f46fbd4d8ece92efb6981e38b7ec5930b90 Mon Sep 17 00:00:00 2001 From: "louis.pontoise" Date: Mon, 20 Jan 2020 10:04:12 +0900 Subject: [PATCH] fix: layout was incorrect resulting in thumbnails clipping --- alt-tab-macos/logic/Preferences.swift | 9 ++-- alt-tab-macos/ui/Cell.swift | 25 +++++------ .../ui/CollectionViewCenterFlowLayout.swift | 43 ++++++++++++------- alt-tab-macos/ui/ThumbnailsPanel.swift | 34 +++++++++------ 4 files changed, 66 insertions(+), 45 deletions(-) diff --git a/alt-tab-macos/logic/Preferences.swift b/alt-tab-macos/logic/Preferences.swift index f525a1c2..16e5f7aa 100644 --- a/alt-tab-macos/logic/Preferences.swift +++ b/alt-tab-macos/logic/Preferences.swift @@ -10,12 +10,13 @@ class Preferences { static let fontHeight = CGFloat(15) static let font = NSFont.systemFont(ofSize: fontHeight) static let windowPadding = CGFloat(23) - static let cellPadding = CGFloat(5) + static let interCellPadding = CGFloat(5) + static let intraCellPadding = CGFloat(5) static let fontIconSize = CGFloat(20) static let maxScreenUsage = CGFloat(0.8) - static let minCellsPerRow = CGFloat(4) - static let maxCellsPerRow = CGFloat(6) - static let nCellsRows = CGFloat(4) + static let minCellsPerRow = CGFloat(3) + static let maxCellsPerRow = CGFloat(4) + static let nCellsRows = CGFloat(3) static let themeMacro = MacroPreferenceHelper<(CGFloat, CGFloat, CGFloat, NSColor, NSColor)>([ MacroPreference(" macOS", (0, 5, 20, .clear, NSColor(red: 0, green: 0, blue: 0, alpha: 0.3))), diff --git a/alt-tab-macos/ui/Cell.swift b/alt-tab-macos/ui/Cell.swift index 93eb2cc0..bc17da2d 100644 --- a/alt-tab-macos/ui/Cell.swift +++ b/alt-tab-macos/ui/Cell.swift @@ -62,8 +62,9 @@ class Cell: NSCollectionViewItem { spaceIcon.setNumber(UInt32(element.spaceIndex!)) } } - let fontIconWidth = CGFloat([minimizedIcon, hiddenIcon, spaceIcon].filter { !$0.isHidden }.count) * (Preferences.fontIconSize + Preferences.cellPadding) - label.textContainer!.size.width = view.frame.width - Preferences.iconSize - Preferences.cellPadding * 3 - fontIconWidth + let fontIconWidth = CGFloat([minimizedIcon, hiddenIcon, spaceIcon].filter { !$0.isHidden }.count) * (Preferences.fontIconSize + Preferences.intraCellPadding) + label.textContainer!.size.width = view.frame.width - Preferences.iconSize - Preferences.intraCellPadding * 3 - fontIconWidth + view.subviews.first!.frame.size = view.frame.size self.mouseDownCallback = mouseDownCallback self.mouseMovedCallback = mouseMovedCallback if view.trackingAreas.count > 0 { @@ -82,7 +83,7 @@ class Cell: NSCollectionViewItem { private func makeHStackView() -> NSStackView { let hStackView = NSStackView() - hStackView.spacing = Preferences.cellPadding + hStackView.spacing = Preferences.intraCellPadding hStackView.setViews([appIcon, label, hiddenIcon, minimizedIcon, spaceIcon], in: .leading) return hStackView } @@ -94,9 +95,9 @@ class Cell: NSCollectionViewItem { vStackView.layer!.cornerRadius = Preferences.cellCornerRadius! vStackView.layer!.borderWidth = Preferences.cellBorderWidth! vStackView.layer!.borderColor = .clear - vStackView.edgeInsets = NSEdgeInsets(top: Preferences.cellPadding, left: Preferences.cellPadding, bottom: Preferences.cellPadding, right: Preferences.cellPadding) + vStackView.edgeInsets = NSEdgeInsets(top: Preferences.intraCellPadding, left: Preferences.intraCellPadding, bottom: Preferences.intraCellPadding, right: Preferences.intraCellPadding) vStackView.orientation = .vertical - vStackView.spacing = Preferences.cellPadding + vStackView.spacing = Preferences.intraCellPadding vStackView.setViews([hStackView, thumbnail], in: .leading) return vStackView } @@ -109,19 +110,19 @@ class Cell: NSCollectionViewItem { } static func widthMax(_ screen: NSScreen) -> CGFloat { - return floor((ThumbnailsPanel.widthMax(screen) / Preferences.minCellsPerRow - Preferences.cellPadding) * Cell.downscaleFactor()) + return floor((ThumbnailsPanel.widthMax(screen) / Preferences.minCellsPerRow - Preferences.interCellPadding) * Cell.downscaleFactor()) } static func widthMin(_ screen: NSScreen) -> CGFloat { - return floor((ThumbnailsPanel.widthMax(screen) / Preferences.maxCellsPerRow - Preferences.cellPadding) * Cell.downscaleFactor()) + return floor((ThumbnailsPanel.widthMax(screen) / Preferences.maxCellsPerRow - Preferences.interCellPadding) * Cell.downscaleFactor()) } static func height(_ screen: NSScreen) -> CGFloat { - return floor((ThumbnailsPanel.heightMax(screen) / Preferences.nCellsRows - Preferences.cellPadding) * Cell.downscaleFactor()) + return floor((ThumbnailsPanel.heightMax(screen) / Preferences.nCellsRows - Preferences.interCellPadding) * Cell.downscaleFactor()) } static func width(_ image: NSImage?, _ screen: NSScreen) -> CGFloat { - return floor(max(thumbnailSize(image, screen).width + Preferences.cellPadding * 2, ThumbnailsPanel.widthMin(screen))) + return floor(max(thumbnailSize(image, screen).width + Preferences.intraCellPadding * 2, Cell.widthMin(screen))) } static func thumbnailSize(_ image: NSImage?, _ screen: NSScreen) -> NSSize { @@ -130,9 +131,9 @@ class Cell: NSCollectionViewItem { } static func thumbnailSize_(_ image: NSImage?, _ screen: NSScreen) -> (CGFloat, CGFloat) { - let thumbnailWidthMin = Cell.widthMin(screen) - Preferences.cellPadding * 2 - let thumbnailHeightMax = Cell.height(screen) - Preferences.cellPadding * 3 - Preferences.iconSize - let thumbnailWidthMax = Cell.widthMax(screen) - Preferences.cellPadding * 2 + let thumbnailWidthMin = Cell.widthMin(screen) - Preferences.intraCellPadding * 2 + let thumbnailHeightMax = Cell.height(screen) - Preferences.intraCellPadding * 3 - Preferences.iconSize + let thumbnailWidthMax = Cell.widthMax(screen) - Preferences.intraCellPadding * 2 guard let image = image else { return (thumbnailWidthMin, thumbnailHeightMax) } let imageRatio = image.size.width / image.size.height let thumbnailRatio = thumbnailWidthMax / thumbnailHeightMax diff --git a/alt-tab-macos/ui/CollectionViewCenterFlowLayout.swift b/alt-tab-macos/ui/CollectionViewCenterFlowLayout.swift index 15992462..7adc6a32 100644 --- a/alt-tab-macos/ui/CollectionViewCenterFlowLayout.swift +++ b/alt-tab-macos/ui/CollectionViewCenterFlowLayout.swift @@ -2,6 +2,8 @@ import Cocoa class CollectionViewCenterFlowLayout: NSCollectionViewFlowLayout { var currentScreen: NSScreen? + var widestRow: CGFloat? + var totalHeight: CGFloat? override func layoutAttributesForElements(in rect: CGRect) -> [NSCollectionViewLayoutAttributes] { let attributes_ = super.layoutAttributesForElements(in: rect) @@ -10,42 +12,51 @@ class CollectionViewCenterFlowLayout: NSCollectionViewFlowLayout { var currentRow: [NSCollectionViewLayoutAttributes] = [] var currentRowY = CGFloat(0) var currentRowWidth = CGFloat(0) - var previousRowMaxY = CGFloat(0) - var currentRowMaxY = CGFloat(0) + var previousRowMaxHeight = CGFloat(0) + var currentRowMaxHeight = CGFloat(0) var widestRow = CGFloat(0) var totalHeight = CGFloat(0) for (index, attribute) in attributes.enumerated() { let isNewRow = abs(attribute.frame.origin.y - currentRowY) > Cell.height(currentScreen!) if isNewRow { - computeOriginXForAllItems(currentRowWidth - Preferences.cellPadding, previousRowMaxY, currentRow) + currentRowWidth -= Preferences.interCellPadding + widestRow = max(widestRow, currentRowWidth) + setCenteredPositionForPreviousRowCells(currentRowWidth, previousRowMaxHeight, currentRow) currentRow.removeAll() currentRowY = attribute.frame.origin.y currentRowWidth = 0 - previousRowMaxY += currentRowMaxY + Preferences.cellPadding - currentRowMaxY = 0 + previousRowMaxHeight += currentRowMaxHeight + Preferences.interCellPadding + currentRowMaxHeight = 0 } currentRow.append(attribute) - currentRowWidth += attribute.frame.size.width + Preferences.cellPadding - widestRow = max(widestRow, currentRowWidth) - currentRowMaxY = max(currentRowMaxY, attribute.frame.size.height) + currentRowWidth += attribute.frame.size.width + Preferences.interCellPadding + currentRowMaxHeight = max(currentRowMaxHeight, attribute.frame.size.height) if index == attributes.count - 1 { - computeOriginXForAllItems(currentRowWidth - Preferences.cellPadding, previousRowMaxY, currentRow) - totalHeight = previousRowMaxY + currentRowMaxY + currentRowWidth -= Preferences.interCellPadding + widestRow = max(widestRow, currentRowWidth) + totalHeight = previousRowMaxHeight + currentRowMaxHeight + setCenteredPositionForPreviousRowCells(currentRowWidth, previousRowMaxHeight, currentRow) } } - let newWidth = widestRow - Preferences.cellPadding - collectionView!.bounds.origin.x = (collectionView!.frame.size.width - newWidth) / 2 - collectionView!.frame.size.width = newWidth - collectionView!.frame.size.height = totalHeight + shiftCenteredElementToTheLeft(attributes, widestRow, totalHeight) + self.widestRow = widestRow + self.totalHeight = totalHeight return attributes } - private func computeOriginXForAllItems(_ currentRowWidth: CGFloat, _ previousRowMaxHeight: CGFloat, _ currentRow: [NSCollectionViewLayoutAttributes]) { + private func shiftCenteredElementToTheLeft(_ attributes: [NSCollectionViewLayoutAttributes], _ widestRow: CGFloat, _ totalHeight: CGFloat) { + let horizontalMargin = floor((collectionView!.frame.size.width - widestRow) / 2) + for attribute in attributes { + attribute.frame.origin.x -= horizontalMargin + } + } + + private func setCenteredPositionForPreviousRowCells(_ currentRowWidth: CGFloat, _ previousRowMaxHeight: CGFloat, _ currentRow: [NSCollectionViewLayoutAttributes]) { var marginLeft = (collectionView!.frame.size.width - currentRowWidth) / 2 for attribute in currentRow { attribute.frame.origin.x = marginLeft attribute.frame.origin.y = previousRowMaxHeight - marginLeft += attribute.frame.size.width + Preferences.cellPadding + marginLeft += attribute.frame.size.width + Preferences.interCellPadding } } } diff --git a/alt-tab-macos/ui/ThumbnailsPanel.swift b/alt-tab-macos/ui/ThumbnailsPanel.swift index e351c766..47fa1dec 100644 --- a/alt-tab-macos/ui/ThumbnailsPanel.swift +++ b/alt-tab-macos/ui/ThumbnailsPanel.swift @@ -22,8 +22,9 @@ class ThumbnailsPanel: NSPanel, NSCollectionViewDataSource, NSCollectionViewDele styleMask.remove(.titled) backgroundColor = .clear makeCollectionView() - makeBackgroundView() - contentView!.addSubview(backgroundView!) + backgroundView = ThumbnailsPanel.makeBackgroundView() + backgroundView.addSubview(collectionView) + contentView!.addSubview(backgroundView) // highest level possible; this allows the app to go on top of context menus level = .screenSaver // helps filter out this window from the thumbnails @@ -34,27 +35,34 @@ class ThumbnailsPanel: NSPanel, NSCollectionViewDataSource, NSCollectionViewDele makeKeyAndOrderFront(nil) } - private func makeBackgroundView() { - backgroundView = NSVisualEffectView() + static func makeBackgroundView() -> NSVisualEffectView { + let backgroundView = NSVisualEffectView() backgroundView.translatesAutoresizingMaskIntoConstraints = false backgroundView.material = Preferences.windowMaterial backgroundView.state = .active backgroundView.wantsLayer = true backgroundView.layer!.cornerRadius = Preferences.windowCornerRadius! - backgroundView.addSubview(collectionView) + return backgroundView } func makeCollectionView() { collectionView = NSCollectionView() collectionView.dataSource = self collectionView.delegate = self - collectionView.collectionViewLayout = CollectionViewCenterFlowLayout() + collectionView.collectionViewLayout = makeLayout() collectionView.backgroundColors = [.clear] collectionView.isSelectable = true collectionView.allowsMultipleSelection = false collectionView.register(Cell.self, forItemWithIdentifier: cellId) } + private func makeLayout() -> CollectionViewCenterFlowLayout { + let layout = CollectionViewCenterFlowLayout() + layout.minimumInteritemSpacing = Preferences.interCellPadding + layout.minimumLineSpacing = Preferences.interCellPadding + return layout + } + func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int { return Windows.list.count } @@ -86,12 +94,16 @@ class ThumbnailsPanel: NSPanel, NSCollectionViewDataSource, NSCollectionViewDele func refreshCollectionView(_ screen: NSScreen, _ uiWorkShouldBeDone: Bool) { if uiWorkShouldBeDone { self.currentScreen = screen } - if uiWorkShouldBeDone { (collectionView.collectionViewLayout as! CollectionViewCenterFlowLayout).currentScreen = screen } + let layout = collectionView.collectionViewLayout as! CollectionViewCenterFlowLayout + if uiWorkShouldBeDone { layout.currentScreen = screen } + if uiWorkShouldBeDone { layout.invalidateLayout() } if uiWorkShouldBeDone { collectionView.setFrameSize(NSSize(width: ThumbnailsPanel.widthMax(screen), height: ThumbnailsPanel.heightMax(screen))) } if uiWorkShouldBeDone { collectionView.reloadData() } if uiWorkShouldBeDone { collectionView.layoutSubtreeIfNeeded() } - if uiWorkShouldBeDone { setContentSize(NSSize(width: collectionView.frame.size.width + Preferences.windowPadding * 2, height: collectionView.frame.size.height + Preferences.windowPadding * 2)) } - if uiWorkShouldBeDone { backgroundView!.setFrameSize(frame.size) } + if uiWorkShouldBeDone { collectionView.setFrameSize(NSSize(width: layout.widestRow!, height: layout.totalHeight!)) } + let windowSize = NSSize(width: layout.widestRow! + Preferences.windowPadding * 2, height: layout.totalHeight! + Preferences.windowPadding * 2) + if uiWorkShouldBeDone { setContentSize(windowSize) } + if uiWorkShouldBeDone { backgroundView!.setFrameSize(windowSize) } if uiWorkShouldBeDone { collectionView.setFrameOrigin(NSPoint(x: Preferences.windowPadding, y: Preferences.windowPadding)) } } @@ -103,10 +115,6 @@ class ThumbnailsPanel: NSPanel, NSCollectionViewDataSource, NSCollectionViewDele return floor(screen.frame.height * Preferences.maxScreenUsage - Preferences.windowPadding * 2) } - static func widthMin(_ screen: NSScreen) -> CGFloat { - return floor(Cell.widthMin(screen) - Preferences.windowPadding * 2) - } - static func heightMin(_ screen: NSScreen) -> CGFloat { return floor(Cell.height(screen) - Preferences.windowPadding * 2) }