Shrink lock screen password dots to fit the field
Long passwords used to overflow the input field and clip with no feedback that typing was still registering. Scale the dot size and letter spacing down as the password grows, like macOS, so every keystroke stays visible. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
ec9f8241c1
commit
12dc7ae558
@@ -0,0 +1,84 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.Commons
|
||||
|
||||
ShellRoot {
|
||||
id: root
|
||||
|
||||
readonly property string resultPath: Quickshell.env("OMARCHY_QML_TEST_RESULT")
|
||||
readonly property string rootPath: Quickshell.env("OMARCHY_PATH")
|
||||
property var failures: []
|
||||
|
||||
function fail(message) {
|
||||
failures.push(String(message))
|
||||
}
|
||||
|
||||
function assertTrue(condition, message) {
|
||||
if (!condition) fail(message)
|
||||
}
|
||||
|
||||
function shellQuote(value) {
|
||||
return "'" + String(value).replace(/'/g, "'\\''") + "'"
|
||||
}
|
||||
|
||||
function writeResult() {
|
||||
var payload = JSON.stringify({
|
||||
ok: failures.length === 0,
|
||||
failures: failures
|
||||
})
|
||||
|
||||
if (resultPath) {
|
||||
Quickshell.execDetached(["bash", "-lc", "printf '%s' " + shellQuote(payload) + " > " + shellQuote(resultPath)])
|
||||
}
|
||||
}
|
||||
|
||||
Item { id: host; width: 800; height: 600 }
|
||||
|
||||
TextMetrics {
|
||||
id: probe
|
||||
font.family: Style.font.family
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 1
|
||||
running: true
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
try {
|
||||
var component = Qt.createComponent("file://" + root.rootPath + "/shell/plugins/lock/LockView.qml", Component.PreferSynchronous)
|
||||
if (component.status !== Component.Ready) {
|
||||
root.fail("LockView failed to load: " + component.errorString())
|
||||
return
|
||||
}
|
||||
|
||||
var view = component.createObject(host, { width: 800, height: 600, loadBackground: false })
|
||||
if (!view) {
|
||||
root.fail("LockView failed to instantiate: " + component.errorString())
|
||||
return
|
||||
}
|
||||
|
||||
view.passwordText = "x".repeat(6)
|
||||
root.assertTrue(view.passwordDotScale === 1, "short passwords keep full-size dots, got scale " + view.passwordDotScale)
|
||||
|
||||
view.passwordText = "x".repeat(40)
|
||||
var longScale = view.passwordDotScale
|
||||
root.assertTrue(longScale > 0 && longScale < 1, "overflowing passwords shrink the dots, got scale " + longScale)
|
||||
|
||||
view.passwordText = "x".repeat(80)
|
||||
var longerScale = view.passwordDotScale
|
||||
root.assertTrue(longerScale < longScale, "dots keep shrinking as the password grows, got " + longerScale + " vs " + longScale)
|
||||
|
||||
probe.font.pixelSize = Math.max(1, Math.floor(view.passwordDotFontSize * longerScale))
|
||||
probe.font.letterSpacing = view.passwordDotLetterSpacing * longerScale
|
||||
probe.text = "●".repeat(80)
|
||||
root.assertTrue(probe.advanceWidth <= view.fieldWidth, "all 80 dots fit inside the field, need " + probe.advanceWidth + "px of " + view.fieldWidth)
|
||||
|
||||
view.destroy()
|
||||
} catch (error) {
|
||||
root.fail("lock password overflow fixture threw: " + error)
|
||||
} finally {
|
||||
root.writeResult()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+71
@@ -0,0 +1,71 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||
|
||||
TMPDIR=""
|
||||
QS_PID=""
|
||||
|
||||
cleanup() {
|
||||
if [[ -n $QS_PID ]] && kill -0 "$QS_PID" 2>/dev/null; then
|
||||
kill "$QS_PID" 2>/dev/null || true
|
||||
wait "$QS_PID" 2>/dev/null || true
|
||||
fi
|
||||
[[ -n $TMPDIR && -d $TMPDIR ]] && rm -rf "$TMPDIR"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
if [[ -z ${WAYLAND_DISPLAY:-} ]]; then
|
||||
pass "no Wayland compositor; skipping lock password overflow test"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! command -v quickshell >/dev/null 2>&1; then
|
||||
pass "quickshell not installed; skipping lock password overflow test"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
require_command jq
|
||||
|
||||
TMPDIR=$(mktemp -d)
|
||||
result="$TMPDIR/result.json"
|
||||
log="$TMPDIR/quickshell.log"
|
||||
config_dir="$TMPDIR/lock-password-overflow"
|
||||
mkdir -p "$config_dir" "$TMPDIR/home"
|
||||
cp "$SHELL_TEST_DIR/fixtures/lock-password-overflow/shell.qml" "$config_dir/shell.qml"
|
||||
ln -s "$ROOT/shell/Ui" "$config_dir/Ui"
|
||||
ln -s "$ROOT/shell/Commons" "$config_dir/Commons"
|
||||
|
||||
OMARCHY_PATH="$ROOT" \
|
||||
OMARCHY_QML_TEST_RESULT="$result" \
|
||||
HOME="$TMPDIR/home" \
|
||||
QML2_IMPORT_PATH="$ROOT/shell${QML2_IMPORT_PATH:+:$QML2_IMPORT_PATH}" \
|
||||
QML_IMPORT_PATH="$ROOT/shell${QML_IMPORT_PATH:+:$QML_IMPORT_PATH}" \
|
||||
PATH="$ROOT/bin:$PATH" \
|
||||
quickshell -p "$config_dir" --no-color >"$log" 2>&1 &
|
||||
QS_PID=$!
|
||||
|
||||
for _ in {1..80}; do
|
||||
[[ -s $result ]] && break
|
||||
if ! kill -0 "$QS_PID" 2>/dev/null; then
|
||||
sed -n '1,220p' "$log" >&2
|
||||
fail "lock password overflow quickshell exited before writing result"
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
[[ -s $result ]] || {
|
||||
sed -n '1,220p' "$log" >&2
|
||||
fail "lock password overflow test timed out"
|
||||
}
|
||||
|
||||
if ! jq -e '.ok == true' "$result" >/dev/null; then
|
||||
printf 'Lock password overflow result:\n' >&2
|
||||
jq . "$result" >&2
|
||||
printf 'Lock password overflow log:\n' >&2
|
||||
sed -n '1,220p' "$log" >&2
|
||||
fail "lock password dots shrink to fit the field"
|
||||
fi
|
||||
|
||||
pass "lock password dots shrink to fit the field"
|
||||
Reference in New Issue
Block a user