feat: better display on some external monitors (closes #3866)

This commit is contained in:
lwouis committed 2024-12-01 22:14:58 +01:00
1 parent b816745830
commit 165a6695b9
7 files changed
+128 -77

No files matched your search

+22 -10
View File
@@ -70,7 +70,7 @@
5F21C9E42C6E94700091F72F /* CustomizeStyleSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F21C9E32C6E94700091F72F /* CustomizeStyleSheet.swift */; };
5F21C9E62C6E94920091F72F /* AnimationsSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F21C9E52C6E94920091F72F /* AnimationsSheet.swift */; };
5F21C9E82C6E96A00091F72F /* SheetWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F21C9E72C6E96A00091F72F /* SheetWindow.swift */; };
5F4753072CEE5A2E002C6C5E /* AppearanceUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0C8D57C86AF492D43A576D /* AppearanceUtils.swift */; };
5F4753072CEE5A2E002C6C5E /* AppearanceTestable.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0C8D57C86AF492D43A576D /* AppearanceTestable.swift */; };
5F706EC02BA448B1004D542D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5F706EBE2BA448B1004D542D /* Localizable.strings */; };
5F706EC32BA448D8004D542D /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5F706EC12BA448D8004D542D /* InfoPlist.strings */; };
76D02BB22BFE7C9E0056008D /* Pods_alt_tab_macos.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C0712B3BEA2B3780398C0999 /* Pods_alt_tab_macos.framework */; };
@@ -86,7 +86,7 @@
BF0C81A79128CF877FE895CC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = BF0C8A48BBB3BF649864DB08 /* InfoPlist.strings */; };
BF0C81B64A1205F066AC796A /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = BF0C80404D3455D496C02F29 /* Localizable.strings */; };
BF0C81D836A8A4CA393B135F /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = BF0C82E55127D38ABF969CA0 /* InfoPlist.strings */; };
BF0C81FA62D810342F59291A /* AppearanceUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0C84933936C6FE676CD33C /* AppearanceUtilsTests.swift */; };
BF0C81FA62D810342F59291A /* AppearanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0C84933936C6FE676CD33C /* AppearanceTests.swift */; };
BF0C8203D0EFEDEED0F908E8 /* ScreensEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0C822D3BEDAB1F1C9F775E /* ScreensEvents.swift */; };
BF0C820C6E89C2D90FA30A4B /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = BF0C8F67ABD271F26FA0EDEF /* Localizable.strings */; };
BF0C8224CFD1ED511F3F0063 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = BF0C8D94228D90FE99D4A3C8 /* Localizable.strings */; };
@@ -116,7 +116,7 @@
BF0C865E3C3F3B353A7A7CBC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = BF0C86FFF1C801761B7CE363 /* Localizable.strings */; };
BF0C86BB79E417163E813179 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = BF0C80851C26EDB3DB98556C /* InfoPlist.strings */; };
BF0C87732A6E660899D13BFD /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = BF0C886B1D6E707E9E6BE46C /* InfoPlist.strings */; };
BF0C8846AFEE2FFF93FAB36F /* AppearanceUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0C8D57C86AF492D43A576D /* AppearanceUtils.swift */; };
BF0C8846AFEE2FFF93FAB36F /* AppearanceTestable.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0C8D57C86AF492D43A576D /* AppearanceTestable.swift */; };
BF0C884D88B74F05B8BBF956 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = BF0C88A25E1ED37E5F80AC91 /* Localizable.strings */; };
BF0C8879F979414A87AB2958 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = BF0C89348E07FC05870FEE62 /* InfoPlist.strings */; };
BF0C88C371B5550E7D23E4F1 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = BF0C8BF5835F97923BF3E8DA /* InfoPlist.strings */; };
@@ -331,6 +331,7 @@
5F706EC22BA448D8004D542D /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = InfoPlist.strings; sourceTree = "<group>"; };
672F94799CC7C90282AEB3EA /* Pods-alt-tab-macos.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-alt-tab-macos.debug.xcconfig"; path = "Target Support Files/Pods-alt-tab-macos/Pods-alt-tab-macos.debug.xcconfig"; sourceTree = "<group>"; };
AA974BA829B7D84C0099A29E /* PreviewPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewPanel.swift; sourceTree = "<group>"; };
BF0C8070A1BBA8B425C8EF34 /* IOKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IOKit.swift; sourceTree = "<group>"; };
BF0C809C9DE36EA1317816FB /* nn */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = nn; path = Localizable.strings; sourceTree = "<group>"; };
BF0C80A05C11C8ADC366EC4A /* lb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = lb; path = InfoPlist.strings; sourceTree = "<group>"; };
BF0C8113492C0FFCB4454BC9 /* sl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = sl; path = Localizable.strings; sourceTree = "<group>"; };
@@ -362,7 +363,7 @@
BF0C84674BAE2B4D7871B86C /* et */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = et; path = Localizable.strings; sourceTree = "<group>"; };
BF0C847A108ABC72723ABBA2 /* hi */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = hi; path = InfoPlist.strings; sourceTree = "<group>"; };
BF0C848A4DDFF5D118CA9CAF /* ca */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ca; path = Localizable.strings; sourceTree = "<group>"; };
BF0C84933936C6FE676CD33C /* AppearanceUtilsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceUtilsTests.swift; sourceTree = "<group>"; };
BF0C84933936C6FE676CD33C /* AppearanceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceTests.swift; sourceTree = "<group>"; };
BF0C84B9B7A41E4A4534E519 /* app-icons-style-full.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "app-icons-style-full.jpg"; sourceTree = "<group>"; };
BF0C84E11784B3B0900D0963 /* app.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = app.icns; sourceTree = "<group>"; };
BF0C850C46CCD8B978168CCD /* sv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = sv; path = Localizable.strings; sourceTree = "<group>"; };
@@ -423,7 +424,7 @@
BF0C8CE7BBC3B9B0CEC008FF /* vi */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = vi; path = Localizable.strings; sourceTree = "<group>"; };
BF0C8D3333653ACAEF730DD8 /* menubar-2@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "menubar-2@2x.png"; sourceTree = "<group>"; };
BF0C8D52C3D33B0669ECF996 /* ro */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ro; path = InfoPlist.strings; sourceTree = "<group>"; };
BF0C8D57C86AF492D43A576D /* AppearanceUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppearanceUtils.swift; sourceTree = "<group>"; };
BF0C8D57C86AF492D43A576D /* AppearanceTestable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppearanceTestable.swift; sourceTree = "<group>"; };
BF0C8D606B53D315F2D9927B /* KeyRepeatTimer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyRepeatTimer.swift; sourceTree = "<group>"; };
BF0C8D7DE89E594FEE6DA692 /* ta */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ta; path = Localizable.strings; sourceTree = "<group>"; };
BF0C8D87C20B7308B16498B6 /* et */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = et; path = InfoPlist.strings; sourceTree = "<group>"; };
@@ -436,6 +437,7 @@
BF0C8E54658B4DAEA0528200 /* menubar-3@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "menubar-3@2x.png"; sourceTree = "<group>"; };
BF0C8E5D2BD5F7F93DF77DF5 /* sk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = sk; path = InfoPlist.strings; sourceTree = "<group>"; };
BF0C8E7B087DCC97A454C0AE /* uk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = uk; path = InfoPlist.strings; sourceTree = "<group>"; };
BF0C8EAF02309B9C489E65F4 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
BF0C8EC400A560FDF99F8C35 /* el */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = el; path = Localizable.strings; sourceTree = "<group>"; };
BF0C8EE17F3ABD7A6D44BFFE /* pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = pl; path = Localizable.strings; sourceTree = "<group>"; };
BF0C8FC083A2443F3EAF5DB7 /* preferences-blacklist.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "preferences-blacklist.jpg"; sourceTree = "<group>"; };
@@ -854,6 +856,15 @@
path = sl.lproj;
sourceTree = "<group>";
};
BF0C827B752F58F79EDB6B6C /* experimentations */ = {
isa = PBXGroup;
children = (
BF0C8070A1BBA8B425C8EF34 /* IOKit.swift */,
BF0C8EAF02309B9C489E65F4 /* README.md */,
);
path = experimentations;
sourceTree = "<group>";
};
BF0C82D4A643500E81DB2965 /* ga.lproj */ = {
isa = PBXGroup;
children = (
@@ -902,7 +913,7 @@
BF0C8719351B41D268CE8B70 /* unit-tests */ = {
isa = PBXGroup;
children = (
BF0C84933936C6FE676CD33C /* AppearanceUtilsTests.swift */,
BF0C84933936C6FE676CD33C /* AppearanceTests.swift */,
);
path = "unit-tests";
sourceTree = "<group>";
@@ -1112,7 +1123,7 @@
BF0C8BA452332236D972C60E /* ATShortcut.swift */,
1C961AC3633CD0C62CCE050E /* Appearance.swift */,
BF0C8D606B53D315F2D9927B /* KeyRepeatTimer.swift */,
BF0C8D57C86AF492D43A576D /* AppearanceUtils.swift */,
BF0C8D57C86AF492D43A576D /* AppearanceTestable.swift */,
);
path = logic;
sourceTree = "<group>";
@@ -1376,6 +1387,7 @@
D04BA70746DEEC3D30B43F81 /* main.swift */,
D04BA0D80B24E72B9B981A1D /* logic */,
D04BA229883E5D540D00232D /* ui */,
BF0C827B752F58F79EDB6B6C /* experimentations */,
);
path = src;
sourceTree = "<group>";
@@ -2124,8 +2136,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
BF0C81FA62D810342F59291A /* AppearanceUtilsTests.swift in Sources */,
5F4753072CEE5A2E002C6C5E /* AppearanceUtils.swift in Sources */,
BF0C81FA62D810342F59291A /* AppearanceTests.swift in Sources */,
5F4753072CEE5A2E002C6C5E /* AppearanceTestable.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2210,7 +2222,7 @@
BF0C896B5D08B7DA8F2024AC /* KeyRepeatTimer.swift in Sources */,
BF0C8249228867A3188E13F3 /* SystemAppearanceEvents.swift in Sources */,
BF0C827A169F0DE86C8F12AD /* SystemScrollerStyleEvents.swift in Sources */,
BF0C8846AFEE2FFF93FAB36F /* AppearanceUtils.swift in Sources */,
BF0C8846AFEE2FFF93FAB36F /* AppearanceTestable.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
+5 -22
View File
@@ -56,13 +56,13 @@ class Appearance {
private static func updateSize() {
let screen = NSScreen.preferred()
let isHorizontalScreen = screen.isHorizontal()
maxWidthOnScreen = AppearanceUtils.comfortableWidth(screen.physicalSize()!.width)
maxWidthOnScreen = AppearanceTestable.comfortableWidth(screen.physicalSize().map { $0.width })
if currentStyle == .appIcons {
appIconsSize()
} else if currentStyle == .titles {
titlesSize(isHorizontalScreen)
} else {
thumbnailsSize(isHorizontalScreen)
thumbnailsSize(isHorizontalScreen, screen)
}
}
@@ -74,7 +74,7 @@ class Appearance {
}
}
private static func thumbnailsSize(_ isHorizontalScreen: Bool) {
private static func thumbnailsSize(_ isHorizontalScreen: Bool, _ screen: NSScreen) {
hideThumbnails = false
windowPadding = 18
cellCornerRadius = 10
@@ -96,7 +96,8 @@ class Appearance {
iconSize = 32
fontHeight = 16
}
(windowMinWidthInRow, windowMaxWidthInRow) = goodValuesForThumbnailsWidthMinMax()
let thumbnailsPanelRatio = (screen.frame.width * maxWidthOnScreen) / (screen.frame.height * maxHeightOnScreen)
(windowMinWidthInRow, windowMaxWidthInRow) = AppearanceTestable.goodValuesForThumbnailsWidthMinMax(thumbnailsPanelRatio, rowsCount)
if currentVisibility == .highest {
edgeInsetsSize = 10
cellCornerRadius = 12
@@ -241,22 +242,4 @@ 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))
}
}
+35
View File
@@ -0,0 +1,35 @@
import Foundation
class AppearanceTestable {
/// 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 90% like Windows 11
/// Let's clamp at 45% (value for the biggest, 60" screens)
static func comfortableWidth(_ physicalWidth: Double?) -> Double {
if let physicalWidth = physicalWidth {
return min(0.9, max(0.45, 600.0 / physicalWidth))
}
return 0.9
}
// 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
static func goodValuesForThumbnailsWidthMinMax(_ aspectRatio: CGFloat, _ rowsCount: CGFloat) -> (CGFloat, CGFloat) {
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.30, maxRatio))
}
}
-12
View File
@@ -1,12 +0,0 @@
class AppearanceUtils {
/// 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 90% like Windows 11
static func comfortableWidth(_ physicalWidth: Double) -> Double {
return min(0.9, 600.0 / physicalWidth)
}
}
+12 -1
View File
@@ -29,7 +29,16 @@ extension NSScreen {
}
func physicalSize() -> CGSize? {
return number().map { CGDisplayScreenSize($0) }
if let number = number() {
let size = CGDisplayScreenSize(number)
// CGDisplayScreenSize docs says it can return "zero"
if size.width > 0 && size.height > 0 &&
// CGDisplayScreenSize may return wrong values; we compare physical and logical ratios to reject
abs(ratio() - (size.width / size.height)) < 0.2 {
return size
}
}
return nil
}
static func preferred() -> NSScreen {
@@ -44,6 +53,8 @@ extension NSScreen {
}
}
// NSScreen.main docs are incorrect. It stopped returning the screen with the key window in macOS 10.9
// see https://stackoverflow.com/a/56268826/2249756
+54
View File
@@ -0,0 +1,54 @@
import XCTest
final class AppearanceTests: XCTestCase {
let screens: [(String, (CGFloat, CGFloat), (CGFloat, CGFloat), (CGFloat, CGFloat), [(Int, CGFloat, CGFloat)])] = [
// screen model, (widthInPixels, heightInPixels), (physicalWidthInMM, physicalHeightInMM), (expectedWidthForHorizontal, expectedWidthForVertical), (rowCount, expectedMinWidth, expectedMaxWidth)
("11\" Laptop: MacBook Air 11\": HD", (1366, 768), (255.7, 178.6), (0.90, 0.90), [(3, 0.12, 0.25), (4, 0.09, 0.19), (5, 0.09, 0.15)]),
("13\" Laptop: MacBook Air 13\": WXGA+", (1440, 900), (304.1, 197.8), (0.90, 0.90), [(3, 0.13, 0.28), (4, 0.10, 0.21), (5, 0.09, 0.17)]),
("14\" Laptop: MacBook Pro 14\": 3K", (3024, 1964), (311.0, 221.1), (0.90, 0.90), [(3, 0.13, 0.29), (4, 0.10, 0.22), (5, 0.09, 0.17)]),
("15\" Laptop: MacBook Pro 15\": QXGA", (2880, 1800), (344.4, 233.0), (0.90, 0.90), [(3, 0.13, 0.28), (4, 0.10, 0.21), (5, 0.09, 0.17)]),
("16\" Laptop: MacBook Pro 16\": 3.5K", (3456, 2234), (358.4, 245.9), (0.90, 0.90), [(3, 0.13, 0.29), (4, 0.10, 0.22), (5, 0.09, 0.17)]),
("19\" Monitor: Apple Studio Display 19\": HD", (1440, 900), (403.0, 236.0), (0.90, 0.90), [(3, 0.13, 0.28), (4, 0.10, 0.21), (5, 0.09, 0.17)]),
("20\" Monitor: Apple Cinema Display 20\": WSXGA+", (1680, 1050), (440.0, 268.0), (0.90, 0.90), [(3, 0.13, 0.28), (4, 0.10, 0.21), (5, 0.09, 0.17)]),
("21\" Monitor: LG 21:9 UltraWide: UWHD", (2560, 1080), (470.0, 290.0), (0.90, 0.90), [(3, 0.09, 0.19), (4, 0.09, 0.14), (5, 0.09, 0.11)]),
("22\" Monitor: ASUS 22\" Full HD: Full HD", (1920, 1080), (485.0, 290.0), (0.90, 0.90), [(3, 0.12, 0.25), (4, 0.09, 0.19), (5, 0.09, 0.15)]),
("24\" Monitor: Dell P2419H: Full HD", (1920, 1080), (531.3, 298.6), (0.90, 0.90), [(3, 0.12, 0.25), (4, 0.09, 0.19), (5, 0.09, 0.15)]),
("27\" Monitor: LG 27UK850-W: 4K", (3840, 2160), (596.8, 336.4), (0.90, 0.90), [(3, 0.12, 0.25), (4, 0.09, 0.19), (5, 0.09, 0.15)]),
("30\" Monitor: BenQ PD3200U: 4K", (3840, 2160), (657.5, 376.3), (0.90, 0.90), [(3, 0.12, 0.25), (4, 0.09, 0.19), (5, 0.09, 0.15)]),
("32\" Monitor: BenQ EW3270U: 4K", (3840, 2160), (711.5, 398.9), (0.84, 0.90), [(3, 0.12, 0.27), (4, 0.09, 0.20), (5, 0.09, 0.16)]),
("34\" UltraWide Monitor: LG 34UC79G-B: UWHD", (2560, 1080), (798.5, 336.5), (0.75, 0.90), [(3, 0.10, 0.22), (4, 0.09, 0.17), (5, 0.09, 0.14)]),
("34\" UltraWide Monitor: LG 34WN80C-B: UWQHD", (3440, 1440), (799.8, 334.8), (0.75, 0.90), [(3, 0.10, 0.22), (4, 0.09, 0.17), (5, 0.09, 0.13)]),
("32\" TV: Samsung UE32T5300: Full HD", (1920, 1080), (715.0, 406.0), (0.83, 0.90), [(3, 0.13, 0.27), (4, 0.09, 0.20), (5, 0.09, 0.16)]),
("40\" TV: Samsung Q60B: 4K", (3840, 2160), (889.0, 510.0), (0.67, 0.90), [(3, 0.16, 0.30), (4, 0.12, 0.25), (5, 0.09, 0.20)]),
("43\" TV: LG 43UN7300: 4K", (3840, 2160), (956.0, 551.0), (0.62, 0.90), [(3, 0.17, 0.30), (4, 0.13, 0.27), (5, 0.10, 0.22)]),
("50\" TV: Samsung TU8000: 4K", (3840, 2160), (1110.0, 630.0), (0.54, 0.90), [(3, 0.19, 0.30), (4, 0.15, 0.30), (5, 0.12, 0.25)]),
("55\" TV: LG OLED55CXPUA: 4K", (3840, 2160), (1210.0, 715.0), (0.49, 0.83), [(3, 0.21, 0.30), (4, 0.16, 0.30), (5, 0.13, 0.28)]),
("60\" TV: Vizio 60-inch 4K: 4K", (3840, 2160), (1320.0, 750.0), (0.45, 0.80), [(3, 0.23, 0.30), (4, 0.17, 0.30), (5, 0.14, 0.30)]),
]
// TODO add 6, 7, 8 rowsCount and reuse vertical screens data from bellow
func testGoodValuesForThumbnailsWidthMinMax() throws {
var actual: (CGFloat, CGFloat)
for (model, (pixelWidth, pixelHeight), _, (expectedHorizontal, _), expectedArray) in screens {
for (rowCount, expectedMin, expectedMax) in expectedArray {
actual = AppearanceTestable.goodValuesForThumbnailsWidthMinMax((pixelWidth * expectedHorizontal) / (pixelHeight * 0.8), CGFloat(rowCount))
XCTAssertEqual(actual.0, expectedMin, accuracy: 0.01, model)
XCTAssertEqual(actual.1, expectedMax, accuracy: 0.01, model)
}
}
}
func testComfortableWidth() throws {
var actual: Double
for (model, _, (physicalWidth, physicalHeight), (expectedHorizontal, expectedVertical), _) in screens {
// screen used horizontally
actual = AppearanceTestable.comfortableWidth(physicalWidth)
XCTAssertEqual(actual, expectedHorizontal, accuracy: 0.01, model)
// screen used vertically
actual = AppearanceTestable.comfortableWidth(physicalHeight)
XCTAssertEqual(actual, expectedVertical, accuracy: 0.01, model)
}
}
}
-32
View File
@@ -1,32 +0,0 @@
import XCTest
final class AppearanceUtilsTests: XCTestCase {
func testComfortableWidth() throws {
[
// screen model, width, height, expected width %
("11\" Laptop: MacBook Air 11\"", 255.7, 178.6, 0.9),
("13\" Laptop: MacBook Air 13\"", 304.1, 197.8, 0.9),
("14\" Laptop: MacBook Pro 14\"", 311.0, 221.1, 0.9),
("15\" Laptop: MacBook Pro 15\"", 344.4, 233.0, 0.9),
("16\" Laptop: MacBook Pro 16\"", 358.4, 245.9, 0.9),
("19\" Monitor: Apple Studio Display 19\"", 403, 236, 0.9),
("20\" Monitor: Apple Cinema Display 20\"", 440, 268, 0.9),
("21\" Monitor: LG 21:9 UltraWide", 470, 290, 0.9),
("22\" Monitor: ASUS 22\" Full HD", 485, 290, 0.9),
("24\" Monitor: Dell P2419H", 531.3, 298.6, 0.9),
("27\" Monitor: LG 27UK850-W", 596.8, 336.4, 0.9),
("30\" Monitor: BenQ PD3200U", 657.5, 376.3, 0.9),
("32\" Monitor: BenQ EW3270U", 711.5, 398.9, 0.8432888264230499),
("34\" UltraWide Monitor: LG 34UC79G-B", 798.5, 336.5, 0.7514088916718847),
("32\" TV: Samsung UE32T5300", 715, 406, 0.8391608391608392),
("40\" TV: Samsung Q60B", 889, 510, 0.6749156355455568),
("43\" TV: LG 43UN7300", 956, 551, 0.6276150627615062),
("50\" TV: Samsung TU8000", 1110, 630, 0.5405405405405406),
("55\" TV: LG OLED55CXPUA", 1210, 715, 0.49586776859504134),
("60\" TV: Vizio 60-inch 4K", 1320, 750, 0.45454545454545453),
].forEach { screen in
let percent = AppearanceUtils.comfortableWidth(screen.1)
XCTAssertEqual(percent, screen.3)
}
}
}