feat: better switcher max-width at various monitor sizes

This commit is contained in:
lwouis committed 2024-11-11 15:51:22 +01:00
1 parent 353a7c9c2b
commit 49178b50b0
2 files changed
+105 -35

No files matched your search

+86 -20
View File
@@ -54,7 +54,9 @@ class Appearance {
}
private static func updateSize() {
let isHorizontalScreen = NSScreen.preferred().isHorizontal()
let screen = NSScreen.preferred()
let isHorizontalScreen = screen.isHorizontal()
maxWidthOnScreen = comfortableWidth(screen.physicalSize()!.width)
if currentStyle == .appIcons {
appIconsSize()
} else if currentStyle == .titles {
@@ -72,24 +74,6 @@ class Appearance {
}
}
// calculate windowMinWidthInRow and windowMaxWidthInRow such that:
// * fullscreen windows fill their tile vertically
// * narrow windows have enough width that a few words can be read from their title
private static func calculateGoodValuesForThumbnailsWidthMinMax() -> (CGFloat, CGFloat) {
let aspectRatio = NSScreen.preferred().ratio()
let minRatio: CGFloat
let maxRatio: CGFloat
if aspectRatio >= 1 {
minRatio = 0.7 / (aspectRatio * rowsCount)
maxRatio = 1.5 / (aspectRatio * rowsCount)
} else {
minRatio = 1.3 / rowsCount
maxRatio = 2.1 / rowsCount
}
// Make sure the values are clamped between some reasonable bounds
return (max(0.09, minRatio), min(0.3, maxRatio))
}
private static func thumbnailsSize(_ isHorizontalScreen: Bool) {
hideThumbnails = false
windowPadding = 18
@@ -112,7 +96,7 @@ class Appearance {
iconSize = 32
fontHeight = 16
}
(windowMinWidthInRow, windowMaxWidthInRow) = calculateGoodValuesForThumbnailsWidthMinMax()
(windowMinWidthInRow, windowMaxWidthInRow) = goodValuesForThumbnailsWidthMinMax()
if currentVisibility == .highest {
edgeInsetsSize = 10
cellCornerRadius = 12
@@ -257,4 +241,86 @@ class Appearance {
highlightBorderWidth = currentStyle == .titles ? 2 : 4
}
}
// calculate windowMinWidthInRow and windowMaxWidthInRow such that:
// * fullscreen windows fill their tile vertically
// * narrow windows have enough width that a few words can be read from their title
private static func goodValuesForThumbnailsWidthMinMax() -> (CGFloat, CGFloat) {
let aspectRatio = NSScreen.preferred().ratio()
let minRatio: CGFloat
let maxRatio: CGFloat
if aspectRatio >= 1 {
minRatio = 0.7 / (aspectRatio * rowsCount)
maxRatio = 1.5 / (aspectRatio * rowsCount)
} else {
minRatio = 1.3 / rowsCount
maxRatio = 2.1 / rowsCount
}
// Make sure the values are clamped between some reasonable bounds
return (max(0.09, minRatio), min(0.3, maxRatio))
}
// How wide should the ThumbnailsPanel be, for comfortable viewing?
// * a comfortable field-of-view is 50-60 degrees
// * people sit at various distances from the screen. We can't know how far they sit
// * most people will seat far enough so that they can view the whole width of the screen
// * some people use wide-screen or TV monitors. Those people tend to be too close to the screen, since they need to use keyboard and mouse on their desk
// Let's use this heuristic: let's assume that people can view 60cm comfortably. Bigger screens can only show parts of AltTab
// Let's clamp at 96% so there are 2% margins
private static func comfortableWidth(_ physicalWidth: Double) -> Double {
return min(0.96, 600.0 / physicalWidth)
}
/// Used for tuning. This is the latest output:
/// Width | Screen model
/// 96% | 11" Laptop: MacBook Air 11"
/// 96% | 13" Laptop: MacBook Air 13"
/// 96% | 14" Laptop: MacBook Pro 14"
/// 96% | 15" Laptop: MacBook Pro 15"
/// 96% | 16" Laptop: MacBook Pro 16"
/// 96% | 19" Monitor: Apple Studio Display 19"
/// 96% | 20" Monitor: Apple Cinema Display 20"
/// 96% | 21" Monitor: LG 21:9 UltraWide
/// 96% | 22" Monitor: ASUS 22" Full HD
/// 96% | 24" Monitor: Dell P2419H
/// 96% | 27" Monitor: LG 27UK850-W
/// 91% | 30" Monitor: BenQ PD3200U
/// 84% | 32" Monitor: BenQ EW3270U
/// 75% | 34" UltraWide Monitor: LG 34UC79G-B
/// 84% | 32" TV: Samsung UE32T5300
/// 67% | 40" TV: Samsung Q60B
/// 63% | 43" TV: LG 43UN7300
/// 54% | 50" TV: Samsung TU8000
/// 50% | 55" TV: LG OLED55CXPUA
/// 45% | 60" TV: Vizio 60-inch 4K
private static func printCoverageTable() {
let screens = [
// screen model, width, height
("11\" Laptop: MacBook Air 11\"", 255.7, 178.6),
("13\" Laptop: MacBook Air 13\"", 304.1, 197.8),
("14\" Laptop: MacBook Pro 14\"", 311.0, 221.1),
("15\" Laptop: MacBook Pro 15\"", 344.4, 233.0),
("16\" Laptop: MacBook Pro 16\"", 358.4, 245.9),
("19\" Monitor: Apple Studio Display 19\"", 403, 236),
("20\" Monitor: Apple Cinema Display 20\"", 440, 268),
("21\" Monitor: LG 21:9 UltraWide", 470, 290),
("22\" Monitor: ASUS 22\" Full HD", 485, 290),
("24\" Monitor: Dell P2419H", 531.3, 298.6),
("27\" Monitor: LG 27UK850-W", 596.8, 336.4),
("30\" Monitor: BenQ PD3200U", 657.5, 376.3),
("32\" Monitor: BenQ EW3270U", 711.5, 398.9),
("34\" UltraWide Monitor: LG 34UC79G-B", 798.5, 336.5),
("32\" TV: Samsung UE32T5300", 715, 406),
("40\" TV: Samsung Q60B", 889, 510),
("43\" TV: LG 43UN7300", 956, 551),
("50\" TV: Samsung TU8000", 1110, 630),
("55\" TV: LG OLED55CXPUA", 1210, 715),
("60\" TV: Vizio 60-inch 4K", 1320, 750),
]
print("Width | Screen model")
for screen in screens {
let percent = comfortableWidth(screen.1)
print("\(String(format: "%4.0f%", percent * 100))% | \(screen.0)")
}
}
}
+19 -15
View File
@@ -9,15 +9,29 @@ extension NSScreen {
return ratio() >= 1
}
// periphery:ignore
func refreshRate() -> Double? {
if let screenNumber = deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? CGDirectDisplayID,
let screenMode = CGDisplayCopyDisplayMode(screenNumber) {
return screenMode.refreshRate
func number() -> CGDirectDisplayID? {
return deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? CGDirectDisplayID
}
func uuid() -> ScreenUuid? {
if let screenNumber = number(),
// these APIs implicitly unwrap their return values, but it can actually be nil thus we check
let screenUuid = CGDisplayCreateUUIDFromDisplayID(screenNumber as! UInt32),
let uuid = CFUUIDCreateString(nil, screenUuid.takeRetainedValue()) {
return uuid
}
return nil
}
// periphery:ignore
func refreshRate() -> Double? {
return number().map { CGDisplayCopyDisplayMode($0)?.refreshRate }
}
func physicalSize() -> CGSize? {
return number().map { CGDisplayScreenSize($0) }
}
static func preferred() -> NSScreen {
preferred_() ?? NSScreen.screens.first!
}
@@ -63,16 +77,6 @@ extension NSScreen {
let y = screenFrame.minY + max(screenFrame.height - panelFrame.height, 0) * 0.5
window.setFrameOrigin(NSPoint(x: x, y: y))
}
func uuid() -> ScreenUuid? {
if let screenNumber = deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")],
// these APIs implicitly unwrap their return values, but it can actually be nil thus we check
let screenUuid = CGDisplayCreateUUIDFromDisplayID(screenNumber as! UInt32),
let uuid = CFUUIDCreateString(nil, screenUuid.takeRetainedValue()) {
return uuid
}
return nil
}
}
typealias ScreenUuid = CFString