feat: add new setting for auto size (closes #3669)

This commit is contained in:
lwouis committed 2026-02-19 23:20:13 +01:00
1 parent b9e0344da4
commit c51376fd26
8 files changed
+88 -21

No files matched your search

+3
View File
@@ -121,6 +121,9 @@
/* No comment provided by engineer. */
"Ask whether to send crash reports" = "Ask whether to send crash reports";
/* No comment provided by engineer. */
"Auto" = "Auto";
/* No comment provided by engineer. */
"Auto-install updates periodically" = "Auto-install updates periodically";
@@ -26,6 +26,7 @@
"AltTab will show if the window is currently minimized or fullscreen with a status icon." = "AltTab will show if the window is currently minimized or fullscreen with a status icon.";
"Always" = "Always";
"Always send crash reports" = "Always send crash reports";
"Auto" = "Auto";
"and press" = "and press";
"and press:" = "and press:";
"Animations" = "Animations";
+30 -16
View File
@@ -2,6 +2,7 @@ import Cocoa
class Appearance {
// size
static var resolvedSize = AppearanceSizePreference.medium
static var hideThumbnails = Bool(false)
static var windowPadding = CGFloat(1000)
static var windowCornerRadius = CGFloat(1000)
@@ -54,16 +55,29 @@ class Appearance {
private static func updateSize() {
let isHorizontalScreen = NSScreen.preferred.isHorizontal()
maxWidthOnScreen = AppearanceTestable.comfortableWidth(NSScreen.preferred.physicalSize().map { $0.width })
if currentStyle == .appIcons {
appIconsSize()
} else if currentStyle == .titles {
titlesSize(isHorizontalScreen)
} else {
thumbnailsSize(isHorizontalScreen)
}
let sizeToApply: AppearanceSizePreference = currentSize == .auto ? .large : currentSize
resolvedSize = sizeToApply
applyConcreteSize(sizeToApply, isHorizontalScreen)
updateFont()
}
static func applySize(_ size: AppearanceSizePreference) {
let isHorizontalScreen = NSScreen.preferred.isHorizontal()
resolvedSize = size
applyConcreteSize(size, isHorizontalScreen)
updateFont()
}
private static func applyConcreteSize(_ size: AppearanceSizePreference, _ isHorizontalScreen: Bool) {
if currentStyle == .appIcons {
appIconsSize(size)
} else if currentStyle == .titles {
titlesSize(isHorizontalScreen, size)
} else {
thumbnailsSize(isHorizontalScreen, size)
}
}
private static func updateTheme() {
highlightBorderWidth = currentStyle == .titles ? 2 : 3
if currentTheme == .dark {
@@ -79,7 +93,7 @@ class Appearance {
}
}
private static func thumbnailsSize(_ isHorizontalScreen: Bool) {
private static func thumbnailsSize(_ isHorizontalScreen: Bool, _ size: AppearanceSizePreference) {
hideThumbnails = false
windowPadding = 18
windowCornerRadius = 23
@@ -90,7 +104,7 @@ class Appearance {
windowCornerRadius = 43
cellCornerRadius = 18
}
switch currentSize {
switch size {
case .small:
rowsCount = isHorizontalScreen ? 5 : 8
iconSize = 16
@@ -99,7 +113,7 @@ class Appearance {
rowsCount = isHorizontalScreen ? 4 : 7
iconSize = 26
fontHeight = 14
case .large:
case .large, .auto:
rowsCount = isHorizontalScreen ? 3 : 6
iconSize = 28
fontHeight = 16
@@ -108,7 +122,7 @@ class Appearance {
(windowMinWidthInRow, windowMaxWidthInRow) = AppearanceTestable.goodValuesForThumbnailsWidthMinMax(tilesPanelRatio, rowsCount)
}
private static func appIconsSize() {
private static func appIconsSize(_ size: AppearanceSizePreference) {
hideThumbnails = true
windowPadding = 25
windowCornerRadius = 23
@@ -120,7 +134,7 @@ class Appearance {
windowMinWidthInRow = 0.04
windowMaxWidthInRow = 0.3
rowsCount = 1
switch currentSize {
switch size {
case .small:
iconSize = 70
fontHeight = 13
@@ -135,7 +149,7 @@ class Appearance {
windowCornerRadius = 55
cellCornerRadius = 35
}
case .large:
case .large, .auto:
windowPadding = 28
iconSize = 150
fontHeight = 16
@@ -146,7 +160,7 @@ class Appearance {
}
}
private static func titlesSize(_ isHorizontalScreen: Bool) {
private static func titlesSize(_ isHorizontalScreen: Bool, _ size: AppearanceSizePreference) {
hideThumbnails = true
windowPadding = 18
windowCornerRadius = 23
@@ -155,14 +169,14 @@ class Appearance {
windowMinWidthInRow = 0.6
windowMaxWidthInRow = 0.9
rowsCount = 1
switch currentSize {
switch size {
case .small:
iconSize = 18
fontHeight = 13
case .medium:
iconSize = 24
fontHeight = 14
case .large:
case .large, .auto:
iconSize = 30
fontHeight = 16
}
+3
View File
@@ -424,12 +424,14 @@ enum AppearanceSizePreference: CaseIterable, SfSymbolMacroPreference {
case small
case medium
case large
case auto
var localizedString: LocalizedString {
switch self {
case .small: return NSLocalizedString("Small", comment: "")
case .medium: return NSLocalizedString("Medium", comment: "")
case .large: return NSLocalizedString("Large", comment: "")
case .auto: return NSLocalizedString("Auto", comment: "")
}
}
@@ -438,6 +440,7 @@ enum AppearanceSizePreference: CaseIterable, SfSymbolMacroPreference {
case .small: return "moonphase.waning.gibbous.inverse"
case .medium: return "moonphase.last.quarter.inverse"
case .large: return "moonphase.waning.crescent.inverse"
case .auto: return "sparkles"
}
}
}
+1 -1
View File
@@ -40,7 +40,7 @@ class Preferences {
"hideColoredCircles": "false",
"windowDisplayDelay": "100",
"appearanceStyle": AppearanceStylePreference.thumbnails.indexAsString,
"appearanceSize": AppearanceSizePreference.medium.indexAsString,
"appearanceSize": AppearanceSizePreference.auto.indexAsString,
"appearanceTheme": AppearanceThemePreference.system.indexAsString,
"theme": ThemePreference.macOs.indexAsString,
"showOnScreen": ShowOnScreenPreference.active.indexAsString,
@@ -43,12 +43,12 @@ class WindowlessAppIndicator: NSView {
static func getAppearanceParameter() -> AppearanceParameter {
if Preferences.appearanceStyle == .thumbnails || Preferences.appearanceStyle == .appIcons {
if Preferences.appearanceSize == .large {
if Appearance.resolvedSize == .large {
return AppearanceParameter(width: 12, height: 5, cornerRadius: 2)
}
return AppearanceParameter(width: 10, height: 5, cornerRadius: 2)
}
if Preferences.appearanceSize == .large {
if Appearance.resolvedSize == .large {
return AppearanceParameter(width: 8, height: 3, cornerRadius: 1)
}
return AppearanceParameter(width: 6, height: 3, cornerRadius: 1)
+2 -2
View File
@@ -204,7 +204,7 @@ class TileView: FlippedView {
private func updateAppIconsLabelFrame() {
let viewWidth = frame.width
let labelWidth = fullTitleWidth
let padding = (Preferences.appearanceSize == .small ? 0 : ( Preferences.appearanceSize == .medium ? 1 : 2)) * Appearance.intraCellPadding
let padding = (Appearance.resolvedSize == .small ? 0 : (Appearance.resolvedSize == .medium ? 1 : 2)) * Appearance.intraCellPadding
let maxAllowedLabelWidth = getMaxAllowedLabelWidth()
let sidesToOffset: CGFloat = (isFirstInRow ? 1 : 0) + (isLastInRow ? 1 : 0)
let paddingForOffset = sidesToOffset * padding
@@ -500,7 +500,7 @@ class TileView: FlippedView {
private func updateDockLabelIconPosition() {
let iconSize = max(appIcon.frame.width, appIcon.frame.height)
let offset = (iconSize * (Preferences.appearanceStyle == .appIcons && Preferences.appearanceSize == .large ? 0.03 : 0.05)).rounded()
let offset = (iconSize * (Preferences.appearanceStyle == .appIcons && Appearance.resolvedSize == .large ? 0.03 : 0.05)).rounded()
let badgeTopRightX = appIcon.frame.maxX + offset
let badgeTopRightY = appIcon.frame.minY - offset
assignIfDifferent(&dockLabelIcon.frame.origin.x, badgeTopRightX - dockLabelIcon.frame.width)
+46
View File
@@ -363,6 +363,10 @@ class TilesView: NSObject {
func updateItemsAndLayout(_ preservedScrollOrigin: CGPoint?) {
let widthMax = TilesPanel.maxThumbnailsWidth().rounded()
if Preferences.appearanceSize == .auto {
resolveAutoSize(widthMax)
Self.updateCachedSizes()
}
if let (maxX, maxY, labelHeight, rowSignature) = layoutTileViews(widthMax) {
layoutParentViews(maxX, widthMax, maxY, labelHeight)
if Preferences.alignThumbnails == .center {
@@ -401,6 +405,48 @@ class TilesView: NSObject {
scrollView.reflectScrolledClipView(scrollView.contentView)
}
private func resolveAutoSize(_ widthMax: CGFloat) {
let searchReservedHeight: CGFloat = searchMode == .off ? 0 : searchBarHeight() + 10
let heightMax = max(0, TilesPanel.maxThumbnailsHeight() - searchReservedHeight)
for size in [AppearanceSizePreference.large, .medium, .small] {
Appearance.applySize(size)
Self.updateCachedSizes()
let maxY = dryRunLayoutTileViews(widthMax)
if size == .small || maxY <= heightMax { return }
}
}
private func dryRunLayoutTileViews(_ widthMax: CGFloat) -> CGFloat {
let labelHeight = Self.layoutCache.labelHeight
let height = TileView.height(labelHeight)
let isLeftToRight = App.shared.userInterfaceLayoutDirection == .leftToRight
let startingX = isLeftToRight ? Appearance.interCellPadding : widthMax - Appearance.interCellPadding
var currentX = startingX
var currentY = Appearance.interCellPadding
var maxY = currentY + height + Appearance.interCellPadding
var index = 0
while index < TilesView.recycledViews.count {
guard App.app.appIsBeingUsed else { return maxY }
defer { index += 1 }
let view = TilesView.recycledViews[index]
guard index < Windows.list.count else { break }
let window = Windows.list[index]
guard Windows.shouldDisplay(window) else { view.frame = .zero; continue }
view.updateRecycledCellWithNewContent(window, index, height)
let width = view.frame.size.width
let projectedX = projectedWidth(currentX, width).rounded(.down)
if needNewLine(projectedX, widthMax) {
currentX = startingX
currentY = (currentY + height + Appearance.interCellPadding).rounded(.down)
currentX = projectedWidth(currentX, width).rounded(.down)
maxY = max(currentY + height + Appearance.interCellPadding, maxY)
} else {
currentX = projectedX
}
}
return maxY
}
private func layoutTileViews(_ widthMax: CGFloat) -> (CGFloat, CGFloat, CGFloat, [Int])? {
let labelHeight = Self.layoutCache.labelHeight
let height = TileView.height(labelHeight)