* Navigate Elsewhen from the keyboard and summon it with Super+Ctrl+Alt+E Up and down walk home and the cities, lighting the picked row and turning the globe to it. Left and right move the clocks an hour, held until Escape or close. Escape now clears a shifted time before leaving the globe. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Reach Add a city from the keyboard and shift time past a day Down past the last city selects Add a city, where Return or Space opens the search. The arrow keys no longer stop at twelve hours, and shifts past a day read in days. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Delete the picked Elsewhen city from the keyboard Delete (or x) removes the picked city and moves the cursor to the one that takes its place. The picked row shows its × so the target is plain. Delete now reaches every panel's deleteRequested, as it already does in the clipboard and the menu. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Center bar glyphs on the open-panel underline The 16px icon canvas in the 27px slot was snapped from 5.5 to 6, and the glyph's fractional centering correction was snapped too, so glyphs sat up to 0.9px right of the underline, which centers on the slot. Both are now placed exactly, and the geometry test measures against the slot to within 0.05px. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
175 lines
6.0 KiB
Bash
175 lines
6.0 KiB
Bash
#!/bin/bash
|
|
|
|
set -euo pipefail
|
|
|
|
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
|
|
|
require_compositor "bar icon geometry test"
|
|
|
|
if ! command -v quickshell >/dev/null 2>&1; then
|
|
skip "quickshell not installed; skipping bar icon geometry test"
|
|
exit 0
|
|
fi
|
|
|
|
test_tmp=$(mktemp -d)
|
|
trap 'rm -rf "$test_tmp"' EXIT
|
|
|
|
ln -s "$ROOT/shell/Ui" "$test_tmp/Ui"
|
|
ln -s "$ROOT/shell/Commons" "$test_tmp/Commons"
|
|
|
|
cat >"$test_tmp/shell.qml" <<'QML'
|
|
import QtQuick
|
|
import Quickshell
|
|
import qs.Commons
|
|
import qs.Ui
|
|
|
|
ShellRoot {
|
|
id: root
|
|
|
|
function fail(message) {
|
|
console.log("RESULT fail " + message)
|
|
Qt.quit()
|
|
}
|
|
|
|
function checkIcon(icon, name) {
|
|
if (icon.implicitWidth !== Style.bar.iconSlot) {
|
|
fail(name + " slot width is " + icon.implicitWidth)
|
|
return false
|
|
}
|
|
if (icon.opticalSize !== Style.bar.iconCanvas) {
|
|
fail(name + " optical canvas is " + icon.opticalSize)
|
|
return false
|
|
}
|
|
// The open-panel underline centers on the slot, so the glyph must too.
|
|
if (Math.abs(icon.opticalCenterErrorX) > 0.05) {
|
|
fail(name + " painted bounds are off the slot's center by " + icon.opticalCenterErrorX)
|
|
return false
|
|
}
|
|
if (icon.glyphFontSize !== Style.bar.iconFont) {
|
|
fail(name + " font size is " + icon.glyphFontSize)
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
Component.onCompleted: Qt.callLater(function() {
|
|
if (!checkIcon(bluetooth, "bluetooth")) return
|
|
if (!checkIcon(network, "network")) return
|
|
if (!checkIcon(audio, "audio")) return
|
|
if (!checkIcon(monitor, "monitor")) return
|
|
if (!checkIcon(power, "power")) return
|
|
if (!checkIcon(elsewhen, "elsewhen")) return
|
|
var baseline = bluetooth.glyphBaselineY
|
|
if (network.glyphBaselineY !== baseline || audio.glyphBaselineY !== baseline
|
|
|| monitor.glyphBaselineY !== baseline || power.glyphBaselineY !== baseline) {
|
|
fail("glyph baselines do not match")
|
|
return
|
|
}
|
|
if (vector.implicitWidth !== Style.bar.iconSlot || vector.opticalSize !== Style.bar.iconCanvas) {
|
|
fail("vector icon does not share glyph geometry")
|
|
return
|
|
}
|
|
if (verticalIcon.implicitWidth !== Style.bar.sizeVertical || verticalIcon.implicitHeight !== Style.bar.iconSlot) {
|
|
fail("vertical icon does not use the shared slot")
|
|
return
|
|
}
|
|
if (verticalIndicator.implicitWidth !== Style.bar.sizeVertical || verticalIndicator.implicitHeight >= Style.bar.iconSlot) {
|
|
fail("vertical indicator does not retain compact spacing")
|
|
return
|
|
}
|
|
if (verticalIndicator.glyphFontSize !== Style.font.caption) {
|
|
fail("indicator does not use the secondary icon scale")
|
|
return
|
|
}
|
|
if (Math.abs(verticalIndicator.opticalCenterErrorX) > 0.05) {
|
|
fail("vertical indicator is not optically centered")
|
|
return
|
|
}
|
|
if (horizontalIndicator.implicitWidth >= Style.bar.iconSlot) {
|
|
fail("horizontal indicator does not retain compact spacing")
|
|
return
|
|
}
|
|
if (horizontalIndicatorPair.implicitWidth >= Style.bar.iconSlot * 2
|
|
|| verticalIndicatorPair.implicitHeight >= Style.bar.iconSlot * 2) {
|
|
fail("indicator groups do not retain compact internal spacing")
|
|
return
|
|
}
|
|
if (compactStatusIcon.implicitWidth !== Style.bar.statusSlot
|
|
|| compactVerticalStatusIcon.implicitHeight !== Style.bar.statusSlot) {
|
|
fail("compact status icons do not use the shared status slot")
|
|
return
|
|
}
|
|
console.log("RESULT pass")
|
|
Qt.quit()
|
|
})
|
|
|
|
QtObject {
|
|
id: testBar
|
|
property bool vertical: false
|
|
property int barSize: Style.bar.sizeHorizontal
|
|
property string fontFamily: Style.font.family
|
|
property color barForeground: "white"
|
|
property color urgent: "red"
|
|
property bool foregroundAnimationEnabled: false
|
|
function registerClickTarget(target) {}
|
|
function unregisterClickTarget(target) {}
|
|
function hideTooltip(target) {}
|
|
function showTooltip(target, text) {}
|
|
}
|
|
|
|
QtObject {
|
|
id: verticalBar
|
|
property bool vertical: true
|
|
property int barSize: Style.bar.sizeVertical
|
|
property string fontFamily: Style.font.family
|
|
property color barForeground: "white"
|
|
property color urgent: "red"
|
|
property bool foregroundAnimationEnabled: false
|
|
function registerClickTarget(target) {}
|
|
function unregisterClickTarget(target) {}
|
|
function hideTooltip(target) {}
|
|
function showTooltip(target, text) {}
|
|
}
|
|
|
|
BarIconButton { id: bluetooth; bar: testBar; text: "" }
|
|
BarIconButton { id: network; bar: testBar; text: "" }
|
|
BarIconButton { id: audio; bar: testBar; text: "" }
|
|
BarIconButton { id: monitor; bar: testBar; text: "" }
|
|
BarIconButton { id: power; bar: testBar; text: "" }
|
|
BarIconButton { id: elsewhen; bar: testBar; text: "" }
|
|
BarIconButton {
|
|
id: vector
|
|
bar: testBar
|
|
iconComponent: Component { Rectangle { width: 12; height: 12 } }
|
|
}
|
|
BarIconButton { id: verticalIcon; bar: verticalBar; text: "\uf021" }
|
|
BarIconButton { id: compactStatusIcon; bar: testBar; text: "\uf021"; slotSize: Style.bar.statusSlot }
|
|
BarIconButton { id: compactVerticalStatusIcon; bar: verticalBar; text: "\uf021"; slotSize: Style.bar.statusSlot }
|
|
Row {
|
|
id: horizontalIndicatorPair
|
|
BarIndicator { id: horizontalIndicator; bar: testBar; active: true; activeText: "" }
|
|
BarIndicator { bar: testBar; active: true; activeText: "" }
|
|
}
|
|
Column {
|
|
id: verticalIndicatorPair
|
|
BarIndicator { id: verticalIndicator; bar: verticalBar; active: true; activeText: "" }
|
|
BarIndicator { bar: verticalBar; active: true; activeText: "" }
|
|
}
|
|
}
|
|
QML
|
|
|
|
output=$(timeout 15 env \
|
|
QML2_IMPORT_PATH="$ROOT/shell${QML2_IMPORT_PATH:+:$QML2_IMPORT_PATH}" \
|
|
QML_IMPORT_PATH="$ROOT/shell${QML_IMPORT_PATH:+:$QML_IMPORT_PATH}" \
|
|
quickshell -p "$test_tmp" --no-color 2>&1) || {
|
|
printf '%s\n' "$output" >&2
|
|
fail "bar icon geometry fixture exits cleanly"
|
|
}
|
|
|
|
if ! grep -q 'RESULT pass' <<<"$output"; then
|
|
printf '%s\n' "$output" >&2
|
|
fail "bar icons share slot and baseline geometry"
|
|
fi
|
|
|
|
pass "bar icons share slot and baseline geometry"
|