colour -> color, behaviour -> behavior, centre -> center, favourite -> favorite, and about twenty other pairs, applied consistently across comments, docs, error/UI copy, and a handful of QML identifiers that used the British spelling as their actual name: SystemSettings' serialiseValue/serialiseTable/normaliseGradient, Displays' normaliseModes, Wallpaper's normalisePolicy, SettingsBackup's serialiseHomeState, DateTime's ntpSynchronised property, Clipboard's _normalise helper, and ShortcutCapture's cancelled signal (with its onCancelled handler in ShortcutsPage.qml). Every call site and the two tests that assert on the literal source text (settings-ownership and settings-backup-live contracts) were updated in lockstep. Left untouched: config/dot/espanso/match/packages/misspell-en/ is a vendored third-party autocorrect dictionary -- its entries are typo corrections, not our prose, and rewriting them would fight the package's own purpose (and any future re-sync from upstream). The already-American `favorites` property (Home page pinned accessories) was never actually misspelled -- only nearby comments and error strings said "favourites" -- so no data migration was needed there. Claude-Session: https://claude.ai/code/session_01E6TJUAh41HaP25MVHWkhRZ
133 lines
4.0 KiB
QML
133 lines
4.0 KiB
QML
// Captures a key chord for rebinding.
|
|
//
|
|
// Shown in place of a shortcut's chord while it is being changed. It takes
|
|
// keyboard focus, waits for a non-modifier key, and reports the chord in the
|
|
// form hypr/keybinds.lua uses.
|
|
//
|
|
// Modifier-only presses are ignored rather than accepted, because every press
|
|
// of a chord passes through them: holding Super to type Super+K would otherwise
|
|
// be captured as "SUPER" the moment the modifier went down.
|
|
//
|
|
// Escape cancels. Not every Qt key has a keysym name Hyprland would accept, so
|
|
// an unmapped key is refused with a message rather than written as something
|
|
// that would silently fail to bind.
|
|
|
|
import QtQuick
|
|
import qs.config
|
|
|
|
FocusScope {
|
|
id: root
|
|
|
|
signal captured(string chord)
|
|
signal canceled
|
|
|
|
property string message: ""
|
|
|
|
implicitWidth: 230
|
|
implicitHeight: 30
|
|
|
|
// Qt key codes to the keysym names Hyprland expects. Letters and digits
|
|
// fall out of the ASCII range; these are the rest that come up in practice.
|
|
readonly property var namedKeys: ({
|
|
0x01000000: "Escape",
|
|
0x01000001: "Tab",
|
|
0x01000004: "Return",
|
|
0x01000005: "Return",
|
|
0x01000003: "BackSpace",
|
|
0x01000006: "Insert",
|
|
0x01000007: "Delete",
|
|
0x01000010: "Home",
|
|
0x01000011: "End",
|
|
0x01000016: "Page_Up",
|
|
0x01000017: "Page_Down",
|
|
0x01000012: "left",
|
|
0x01000013: "up",
|
|
0x01000014: "right",
|
|
0x01000015: "down",
|
|
0x20: "space",
|
|
0x2c: "comma",
|
|
0x2e: "period",
|
|
0x2f: "slash",
|
|
0x3b: "semicolon",
|
|
0x27: "apostrophe",
|
|
0x5b: "bracketleft",
|
|
0x5d: "bracketright",
|
|
0x5c: "backslash",
|
|
0x60: "grave",
|
|
0x2d: "minus",
|
|
0x3d: "equal",
|
|
0x01000009: "Print"
|
|
})
|
|
|
|
function keysymFor(key: int): string {
|
|
if (key >= 0x41 && key <= 0x5a) // A-Z
|
|
return String.fromCharCode(key);
|
|
if (key >= 0x30 && key <= 0x39) // 0-9
|
|
return String.fromCharCode(key);
|
|
if (key >= 0x01000030 && key <= 0x0100003b) // F1-F12
|
|
return "F" + (key - 0x01000030 + 1);
|
|
return root.namedKeys[key] ?? "";
|
|
}
|
|
|
|
function isModifierOnly(key: int): bool {
|
|
return key === 0x01000020 || key === 0x01000021 || key === 0x01000022
|
|
|| key === 0x01000023 || key === 0x01000024;
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
radius: 9
|
|
color: Theme.alpha(Theme.accent, 0.14)
|
|
border.width: 1
|
|
border.color: Theme.alpha(Theme.accent, 0.5)
|
|
|
|
Text {
|
|
anchors.centerIn: parent
|
|
width: parent.width - 16
|
|
horizontalAlignment: Text.AlignHCenter
|
|
elide: Text.ElideRight
|
|
text: root.message !== "" ? root.message : "Press a shortcut… Esc to cancel"
|
|
color: root.message !== "" ? Theme.warn : Theme.fg
|
|
font.family: Theme.fontFamily
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
}
|
|
}
|
|
|
|
Keys.onPressed: event => {
|
|
event.accepted = true;
|
|
|
|
if (event.key === 0x01000000) { // Escape
|
|
root.canceled();
|
|
return;
|
|
}
|
|
if (root.isModifierOnly(event.key))
|
|
return;
|
|
|
|
const keysym = root.keysymFor(event.key);
|
|
if (keysym === "") {
|
|
root.message = "That key cannot be used";
|
|
return;
|
|
}
|
|
|
|
const parts = [];
|
|
if (event.modifiers & Qt.MetaModifier) parts.push("SUPER");
|
|
if (event.modifiers & Qt.ControlModifier) parts.push("CTRL");
|
|
if (event.modifiers & Qt.AltModifier) parts.push("ALT");
|
|
if (event.modifiers & Qt.ShiftModifier) parts.push("SHIFT");
|
|
|
|
if (parts.length === 0 && keysym.length === 1) {
|
|
// A bare letter or digit would swallow ordinary typing.
|
|
root.message = "Add a modifier";
|
|
return;
|
|
}
|
|
|
|
parts.push(keysym);
|
|
root.captured(parts.join(" + "));
|
|
}
|
|
|
|
onActiveFocusChanged: {
|
|
if (!activeFocus)
|
|
root.canceled();
|
|
}
|
|
}
|