From 6e2e5b47fc850f907ba30f903ca67e5dd2d9fbcb Mon Sep 17 00:00:00 2001 From: Louis Pontoise Date: Mon, 7 Sep 2020 21:12:06 +0900 Subject: [PATCH] feat: split max screen size preference into width/height (closes #579) --- src/logic/Preferences.swift | 35 ++++++++++--------- src/ui/main-window/ThumbnailView.swift | 6 ++-- src/ui/main-window/ThumbnailsPanel.swift | 4 +-- .../preferences-window/LabelAndControl.swift | 4 +-- .../tabs/AppearanceTab.swift | 34 +++++++++++++++--- 5 files changed, 54 insertions(+), 29 deletions(-) diff --git a/src/logic/Preferences.swift b/src/logic/Preferences.swift index c352a3391..acf1e5846 100644 --- a/src/logic/Preferences.swift +++ b/src/logic/Preferences.swift @@ -5,11 +5,10 @@ import ShortcutRecorder let defaults = UserDefaults.standard class Preferences { - static var defaultsDependingOnScreenRatio_ = defaultsDependingOnScreenRatio() - // default values static var defaultValues: [String: String] = [ - "maxScreenUsage": "80", + "maxWidthOnScreen": "80", + "maxHeightOnScreen": "80", "iconSize": "32", "fontHeight": "15", "holdShortcut": "⌥", @@ -73,9 +72,10 @@ class Preferences { static var intraCellPadding: CGFloat { 5 } // persisted values - static var maxScreenUsage: CGFloat { defaults.cgfloat("maxScreenUsage") / CGFloat(100) } - static var minCellsPerRow: CGFloat { defaults.cgfloat("minCellsPerRow") } - static var maxCellsPerRow: CGFloat { defaults.cgfloat("maxCellsPerRow") } + static var maxWidthOnScreen: CGFloat { defaults.cgfloat("maxWidthOnScreen") / CGFloat(100) } + static var maxHeightOnScreen: CGFloat { defaults.cgfloat("maxHeightOnScreen") / CGFloat(100) } + static var windowMaxWidthInRow: CGFloat { defaults.cgfloat("windowMaxWidthInRow") / CGFloat(100) } + static var windowMinWidthInRow: CGFloat { defaults.cgfloat("windowMinWidthInRow") / CGFloat(100) } static var rowsCount: CGFloat { defaults.cgfloat("rowsCount") } static var iconSize: CGFloat { defaults.cgfloat("iconSize") } static var fontHeight: CGFloat { defaults.cgfloat("fontHeight") } @@ -188,6 +188,13 @@ class Preferences { defaults.set(App.version, forKey: preferencesVersion) } + private static func migrateMaxSizeOnScreenToWidthAndHeight() { + if let old = defaults.string(forKey: "maxScreenUsage") { + defaults.set(old, forKey: "maxWidthOnScreen") + defaults.set(old, forKey: "maxHeightOnScreen") + } + } + // dropdowns preferences used to store English text; now they store indexes static func migrateDropdownMenuPreference(_ preference: String, _ oldAndNew: [String: String]) { if let old = defaults.string(forKey: preference), @@ -206,19 +213,13 @@ class Preferences { } } - static func defaultsDependingOnScreenRatio() -> [String: String] { - let ratio = Screen.mainScreenRatio() - // landscape - if ratio > 1 { - // 15/10 and wider; tested with 16/10 and 16/9 - if ratio > (15 / 10) { - return ["rowsCount": "4", "minCellsPerRow": "4", "maxCellsPerRow": "7"] - } - // narrower than 15/10; tested with 4/3 - return ["rowsCount": "3", "minCellsPerRow": "4", "maxCellsPerRow": "7"] + static func rowCountDependingOnScreenRatio() -> String { + // landscape; tested with 4/3, 16/10, 16/9 + if Screen.mainScreenRatio() > 1 { + return "4" } // vertical; tested with 10/16 - return ["rowsCount": "6", "minCellsPerRow": "3", "maxCellsPerRow": "4"] + return "6" } static func keyAboveTabDependingOnInputSource() -> String { diff --git a/src/ui/main-window/ThumbnailView.swift b/src/ui/main-window/ThumbnailView.swift index 4d72e497b..906cf727b 100644 --- a/src/ui/main-window/ThumbnailView.swift +++ b/src/ui/main-window/ThumbnailView.swift @@ -145,7 +145,7 @@ class ThumbnailView: NSStackView { } dockLabelIcon.setFrameOrigin(NSPoint(x: appIcon.frame.maxX - dockLabelIcon.fittingSize.width - 1, y: appIcon.frame.maxY - dockLabelIcon.fittingSize.height + 4)) } - assignIfDifferent(&frame.size.width, max((Preferences.hideThumbnails ? hStackView.fittingSize.width : thumbnail.frame.size.width) + Preferences.intraCellPadding * 2, ThumbnailView.widthMin(screen))) + assignIfDifferent(&frame.size.width, max((Preferences.hideThumbnails ? hStackView.fittingSize.width : thumbnail.frame.size.width) + Preferences.intraCellPadding * 2, ThumbnailView.widthMin(screen)).rounded()) assignIfDifferent(&frame.size.height, newHeight) let fontIconWidth = CGFloat([fullscreenIcon, minimizedIcon, hiddenIcon, spaceIcon].filter { !$0.isHidden }.count) * (Preferences.fontHeight + Preferences.intraCellPadding) assignIfDifferent(&label.textContainer!.size.width, frame.width - Preferences.iconSize - Preferences.intraCellPadding * 3 - fontIconWidth) @@ -249,11 +249,11 @@ class ThumbnailView: NSStackView { } static func widthMax(_ screen: NSScreen) -> CGFloat { - return (ThumbnailsPanel.widthMax(screen) - Preferences.interCellPadding) / Preferences.minCellsPerRow - Preferences.interCellPadding + return ThumbnailsPanel.widthMax(screen) * Preferences.windowMaxWidthInRow - Preferences.interCellPadding * 2 } static func widthMin(_ screen: NSScreen) -> CGFloat { - return (ThumbnailsPanel.widthMax(screen) - Preferences.interCellPadding) / Preferences.maxCellsPerRow - Preferences.interCellPadding + return ThumbnailsPanel.widthMax(screen) * Preferences.windowMinWidthInRow - Preferences.interCellPadding * 2 } static func height(_ screen: NSScreen) -> CGFloat { diff --git a/src/ui/main-window/ThumbnailsPanel.swift b/src/ui/main-window/ThumbnailsPanel.swift index a08b476c8..9ce917821 100644 --- a/src/ui/main-window/ThumbnailsPanel.swift +++ b/src/ui/main-window/ThumbnailsPanel.swift @@ -33,10 +33,10 @@ class ThumbnailsPanel: NSPanel { } static func widthMax(_ screen: NSScreen) -> CGFloat { - return screen.frame.width * Preferences.maxScreenUsage - Preferences.windowPadding * 2 + return screen.frame.width * Preferences.maxWidthOnScreen - Preferences.windowPadding * 2 } static func heightMax(_ screen: NSScreen) -> CGFloat { - return screen.frame.height * Preferences.maxScreenUsage - Preferences.windowPadding * 2 + return screen.frame.height * Preferences.maxHeightOnScreen - Preferences.windowPadding * 2 } } diff --git a/src/ui/preferences-window/LabelAndControl.swift b/src/ui/preferences-window/LabelAndControl.swift index 61b37cbc5..fde8fe344 100644 --- a/src/ui/preferences-window/LabelAndControl.swift +++ b/src/ui/preferences-window/LabelAndControl.swift @@ -63,7 +63,7 @@ class LabelAndControl: NSObject { } } - static func makeLabelWithSlider(_ labelText: String, _ rawName: String, _ minValue: Double, _ maxValue: Double, _ numberOfTickMarks: Int, _ allowsTickMarkValuesOnly: Bool, _ unitText: String = "") -> [NSView] { + static func makeLabelWithSlider(_ labelText: String, _ rawName: String, _ minValue: Double, _ maxValue: Double, _ numberOfTickMarks: Int, _ allowsTickMarkValuesOnly: Bool, _ unitText: String = "", extraAction: ActionClosure? = nil) -> [NSView] { let value = Preferences.getString(rawName)! let suffixText = MeasurementFormatter().string(from: Measurement(value: Double(value)!, unit: Unit(symbol: unitText))) let slider = NSSlider() @@ -71,7 +71,7 @@ class LabelAndControl: NSObject { slider.maxValue = maxValue slider.stringValue = value slider.isContinuous = true - return makeLabelWithProvidedControl(labelText, rawName, slider, suffixText) + return makeLabelWithProvidedControl(labelText, rawName, slider, suffixText, extraAction: extraAction) } static func makeLabelWithProvidedControl(_ labelText: String, _ rawName: String, _ control: NSControl, _ suffixText: String? = nil, _ suffixUrl: String? = nil, labelPosition: LabelPosition = .leftWithSeparator, extraAction: ActionClosure? = nil) -> [NSView] { diff --git a/src/ui/preferences-window/tabs/AppearanceTab.swift b/src/ui/preferences-window/tabs/AppearanceTab.swift index 32d8ca803..25d4f54e1 100644 --- a/src/ui/preferences-window/tabs/AppearanceTab.swift +++ b/src/ui/preferences-window/tabs/AppearanceTab.swift @@ -1,14 +1,24 @@ import Cocoa class AppearanceTab { + static var rowsCount: [NSView]! + static var minWidthInRow: [NSView]! + static var maxWidthInRow: [NSView]! + static func initTab() -> NSView { + rowsCount = LabelAndControl.makeLabelWithSlider(NSLocalizedString("Rows of thumbnails:", comment: ""), "rowsCount", 1, 20, 20, true) + minWidthInRow = LabelAndControl.makeLabelWithSlider(NSLocalizedString("Window min width in row:", comment: ""), "windowMinWidthInRow", 0, 100, 10, true, "%", extraAction: { _ in capMinMaxWidthInRow() }) + maxWidthInRow = LabelAndControl.makeLabelWithSlider(NSLocalizedString("Window max width in row:", comment: ""), "windowMaxWidthInRow", 0, 100, 10, true, "%", extraAction: { _ in capMinMaxWidthInRow() }) + let grid = GridView([ LabelAndControl.makeLabelWithDropdown(NSLocalizedString("Theme:", comment: ""), "theme", ThemePreference.allCases), LabelAndControl.makeLabelWithDropdown(NSLocalizedString("Align windows:", comment: ""), "alignThumbnails", AlignThumbnailsPreference.allCases), - LabelAndControl.makeLabelWithSlider(NSLocalizedString("Max size on screen:", comment: ""), "maxScreenUsage", 10, 100, 10, true, "%"), - LabelAndControl.makeLabelWithSlider(NSLocalizedString("Rows of windows:", comment: ""), "rowsCount", 1, 20, 20, true), - LabelAndControl.makeLabelWithSlider(NSLocalizedString("Min windows per row:", comment: ""), "minCellsPerRow", 1, 20, 20, true), - LabelAndControl.makeLabelWithSlider(NSLocalizedString("Max windows per row:", comment: ""), "maxCellsPerRow", 1, 40, 20, true), + LabelAndControl.makeLabelWithSlider(NSLocalizedString("Max width on screen:", comment: ""), "maxWidthOnScreen", 10, 100, 10, true, "%"), + LabelAndControl.makeLabelWithSlider(NSLocalizedString("Max height on screen:", comment: ""), "maxHeightOnScreen", 10, 100, 10, true, "%"), + LabelAndControl.makeLabelWithCheckbox(NSLocalizedString("Hide window thumbnails:", comment: ""), "hideThumbnails", extraAction: { _ in toggleRowsCount() }), + rowsCount, + minWidthInRow, + maxWidthInRow, LabelAndControl.makeLabelWithSlider(NSLocalizedString("Window app icon size:", comment: ""), "iconSize", 0, 128, 11, false, "px"), LabelAndControl.makeLabelWithSlider(NSLocalizedString("Window title font size:", comment: ""), "fontHeight", 0, 64, 11, false, "px"), LabelAndControl.makeLabelWithDropdown(NSLocalizedString("Window title truncation:", comment: ""), "titleTruncation", TitleTruncationPreference.allCases), @@ -21,12 +31,26 @@ class AppearanceTab { LabelAndControl.makeLabelWithCheckbox(NSLocalizedString("Hide colored circles on mouse hover:", comment: ""), "hideColoredCircles"), LabelAndControl.makeLabelWithCheckbox(NSLocalizedString("Hide app badges:", comment: ""), "hideAppBadges"), LabelAndControl.makeLabelWithCheckbox(NSLocalizedString("Hide apps with no open window:", comment: ""), "hideWindowlessApps"), - LabelAndControl.makeLabelWithCheckbox(NSLocalizedString("Hide window thumbnails:", comment: ""), "hideThumbnails"), ]) grid.column(at: 0).xPlacement = .trailing grid.rowAlignment = .lastBaseline grid.fit() + toggleRowsCount() + capMinMaxWidthInRow() + return grid } + + static func capMinMaxWidthInRow() { + let minSlider = minWidthInRow[1] as! NSSlider + let maxSlider = maxWidthInRow[1] as! NSSlider + maxSlider.minValue = minSlider.doubleValue + debugPrint(maxSlider.minValue, maxSlider.doubleValue) + LabelAndControl.controlWasChanged(maxSlider, "windowMaxWidthInRow") + } + + static func toggleRowsCount() { + (rowsCount[1] as! NSSlider).isEnabled = !Preferences.hideThumbnails + } }