Hide unavailable display scale options

This commit is contained in:
David Heinemeier Hansson
2026-07-23 18:06:19 -07:00
parent 58ec29f20a
commit 63955e787f
3 changed files with 90 additions and 5 deletions
+51
View File
@@ -33,6 +33,55 @@ function cleanScale(scale, width, height) {
return normalizeScale(scaleUnits / 120) return normalizeScale(scaleUnits / 120)
} }
function matchingScaleIndex(scales, currentScale, width, height) {
var current = Number(currentScale)
if (!Array.isArray(scales) || !isFinite(current)) return -1
var bestIndex = -1
var bestDistance = Infinity
var normalizedCurrent = normalizeScale(current)
for (var i = 0; i < scales.length; i++) {
if (cleanScale(scales[i], width, height) !== normalizedCurrent) continue
var distance = Math.abs(Number(scales[i]) - current)
if (distance < bestDistance) {
bestIndex = i
bestDistance = distance
}
}
return bestIndex
}
function availableScales(scales, width, height) {
if (!Array.isArray(scales) || Number(width) <= 0 || Number(height) <= 0) return scales || []
var byEffectiveScale = {}
for (var i = 0; i < scales.length; i++) {
var requested = Number(scales[i])
var effective = Number(cleanScale(requested, width, height))
// Clean scales round upward. If the mode cannot reach the requested
// scale, cleanScale caps at its largest valid value and this preset is
// not actually available.
if (!isFinite(requested) || !isFinite(effective) || effective < requested) continue
var key = normalizeScale(effective)
var existing = byEffectiveScale[key]
if (!existing || Math.abs(requested - effective) < existing.distance) {
byEffectiveScale[key] = {
value: String(scales[i]),
index: i,
distance: Math.abs(requested - effective)
}
}
}
return Object.keys(byEffectiveScale)
.map(function(key) { return byEffectiveScale[key] })
.sort(function(a, b) { return a.index - b.index })
.map(function(candidate) { return candidate.value })
}
function brightnessName(percent) { function brightnessName(percent) {
var p = Math.round(percent) var p = Math.round(percent)
if (p >= 95) return "Sun blast" if (p >= 95) return "Sun blast"
@@ -70,6 +119,8 @@ if (typeof module !== "undefined") {
clampBrightness: clampBrightness, clampBrightness: clampBrightness,
normalizeScale: normalizeScale, normalizeScale: normalizeScale,
cleanScale: cleanScale, cleanScale: cleanScale,
matchingScaleIndex: matchingScaleIndex,
availableScales: availableScales,
brightnessName: brightnessName, brightnessName: brightnessName,
parseDisplays: parseDisplays parseDisplays: parseDisplays
} }
+14 -5
View File
@@ -38,7 +38,15 @@ Panel {
// j/k walks each row. // j/k walks each row.
// Mouse hover on a target updates root state via the components' `hovered` // Mouse hover on a target updates root state via the components' `hovered`
// signal so keyboard cursor and pointer share one highlight. // signal so keyboard cursor and pointer share one highlight.
readonly property var scaleValues: ["1", "1.25", "1.6", "2", "3", "4"] readonly property var scalePresets: ["1", "1.25", "1.6", "2", "3", "4"]
readonly property var scaleValues: {
for (var i = 0; i < displays.length; i++) {
var display = displays[i]
if (display && display.focused)
return Model.availableScales(scalePresets, display.width, display.height)
}
return scalePresets
}
property string focusSection: "scale" property string focusSection: "scale"
property int selectedIndex: 0 property int selectedIndex: 0
property bool cursorActive: false property bool cursorActive: false
@@ -246,13 +254,13 @@ Panel {
return Model.normalizeScale(scale) return Model.normalizeScale(scale)
} }
function effectiveScale(scale) { function activeScaleIndex() {
for (var i = 0; i < displays.length; i++) { for (var i = 0; i < displays.length; i++) {
var display = displays[i] var display = displays[i]
if (display && display.focused) if (display && display.focused)
return Model.cleanScale(scale, display.width, display.height) return Model.matchingScaleIndex(scaleValues, monitorScale, display.width, display.height)
} }
return normalizeScale(scale) return -1
} }
// Playful mood-name for a given brightness percent. Bands intentionally // Playful mood-name for a given brightness percent. Bands intentionally
@@ -342,6 +350,7 @@ Panel {
onBrightnessAvailableChanged: clampCursor() onBrightnessAvailableChanged: clampCursor()
onDisplaysChanged: clampCursor() onDisplaysChanged: clampCursor()
onScaleValuesChanged: clampCursor()
onVisibleSectionsChanged: clampCursor() onVisibleSectionsChanged: clampCursor()
// Only poll while the panel is open; the bar glyph tracks monitor count via // Only poll while the panel is open; the bar glyph tracks monitor count via
@@ -780,7 +789,7 @@ Panel {
verticalPadding: Style.spacing.controlPaddingY verticalPadding: Style.spacing.controlPaddingY
bordered: true bordered: true
active: root.normalizeScale(root.monitorScale) === root.effectiveScale(scaleValue) active: root.activeScaleIndex() === scaleIndex
hasCursor: root.cursorActive && root.focusSection === "scale" && root.selectedIndex === scaleIndex hasCursor: root.cursorActive && root.focusSection === "scale" && root.selectedIndex === scaleIndex
onClicked: root.setScale(scaleValue) onClicked: root.setScale(scaleValue)
+25
View File
@@ -18,6 +18,31 @@ assertEqual(monitor.cleanScale(3, 1280, 800), '3.2', 'monitor matches clean VM s
assertEqual(monitor.cleanScale(1.25, 1280, 800), '1.25', 'monitor preserves an already clean scale') assertEqual(monitor.cleanScale(1.25, 1280, 800), '1.25', 'monitor preserves an already clean scale')
assertEqual(monitor.cleanScale(1.25, 6016, 3384), '1.33', 'monitor matches clean physical display scale') assertEqual(monitor.cleanScale(1.25, 6016, 3384), '1.33', 'monitor matches clean physical display scale')
assertEqual(monitor.cleanScale(1.6, 0, 800), '', 'monitor rejects a missing display mode') assertEqual(monitor.cleanScale(1.6, 0, 800), '', 'monitor rejects a missing display mode')
assertEqual(
monitor.matchingScaleIndex(['1', '1.25', '1.6', '2', '3', '4'], 3.2, 1280, 800),
4,
'monitor selects requested 3x for effective VM scale'
)
assertEqual(
monitor.matchingScaleIndex(['1', '1.25', '1.6', '2', '3', '4'], 4, 4, 4),
5,
'monitor selects only the closest preset when clean scales collide'
)
assertDeepEqual(
monitor.availableScales(['1', '1.25', '1.6', '2', '3', '4'], 1280, 800),
['1', '1.25', '1.6', '2', '3', '4'],
'monitor keeps presets that produce unique reachable VM scales'
)
assertDeepEqual(
monitor.availableScales(['1', '1.25', '1.6', '2', '3', '4'], 5968, 3230),
['1', '2'],
'monitor hides presets the current mode cannot reach'
)
assertDeepEqual(
monitor.availableScales(['1', '1.25', '1.6', '2', '3', '4'], 0, 0),
['1', '1.25', '1.6', '2', '3', '4'],
'monitor keeps presets until display dimensions are known'
)
assertEqual(monitor.brightnessName(96), 'Sun blast', 'monitor names very bright displays') assertEqual(monitor.brightnessName(96), 'Sun blast', 'monitor names very bright displays')
assertEqual(monitor.brightnessName(12), 'Candlelit', 'monitor names dim displays') assertEqual(monitor.brightnessName(12), 'Candlelit', 'monitor names dim displays')