feat: better switcher max-width at various monitor sizes
This commit is contained in:
1 parent
353a7c9c2b
commit
49178b50b0
2 files changed
+105
-35
No files matched your search
+86
-20
@@ -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
@@ -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
|
||||
Reference in new issue
Block a user