Let shortcuts be rebound from Settings
Every bind in keybinds.lua now goes through a small wrapper that substitutes the chord from a stored override. Only the chord is taken from settings; the action is always the Lua value written in that file, so an override can move a shortcut but can never make one do something else. That is the property that makes reading them from a file the user can edit safe, and it is why the alternative -- storing dispatchers -- was not considered. Overrides are keyed by the shipped chord rather than the description. Keying by description moved every bind that shared one: rebinding SUPER+C also moved the XF86Calculator hardware key onto the same chord, silently costing it. Chords are unique; descriptions are not. Applying needs hyprctl reload rather than a live hl.bind. Hyprland reports Lua-defined binds with dispatcher "__lua" and a bytecode offset, so the action cannot be reconstructed from outside to re-bind it; reload re-runs the config, which re-reads the settings file. The capture control ignores modifier-only presses, because every chord passes through them and holding Super would otherwise be captured the moment the modifier went down. It refuses a bare letter, which would swallow ordinary typing, and refuses a key with no keysym name rather than storing something that would fail to bind. Rebinding onto a chord already in use is refused rather than shadowing the existing shortcut. The refactor was verified by snapshotting all 113 binds before and after: the keymap is byte-identical, and identical again after applying an override and resetting it. Claude-Session: https://claude.ai/code/session_01BRvzt4H8XXLPVH5MyYdk9L
This commit is contained in:
@@ -17,6 +17,11 @@ import qs.services
|
||||
SettingsPage {
|
||||
id: root
|
||||
|
||||
// The chord of the bind currently being re-recorded, in the Lua form; empty
|
||||
// when nothing is being captured. Held here rather than per row so that
|
||||
// starting a new capture cancels any other.
|
||||
property string capturingChord: ""
|
||||
|
||||
title: "Input & Shortcuts"
|
||||
lede: "The Forge mental model, carried forward into native tiling."
|
||||
|
||||
@@ -72,19 +77,92 @@ SettingsPage {
|
||||
|
||||
model: groupCard.modelData.binds
|
||||
|
||||
TextRow {
|
||||
SettingRow {
|
||||
id: bindRow
|
||||
|
||||
required property var modelData
|
||||
required property int index
|
||||
|
||||
label: modelData.description
|
||||
value: modelData.chord
|
||||
controlWidth: 230
|
||||
divider: index < bindRows.count - 1
|
||||
readonly property bool capturing: root.capturingChord === bindRow.modelData.luaChord
|
||||
readonly property bool overridden: Keybinds.isOverridden(bindRow.modelData.luaChord)
|
||||
|
||||
label: bindRow.modelData.description
|
||||
detail: bindRow.overridden
|
||||
? "Moved from " + Keybinds.shippedChordFor(bindRow.modelData.luaChord)
|
||||
: ""
|
||||
controlWidth: 300
|
||||
divider: bindRow.index < bindRows.count - 1
|
||||
|
||||
Item {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 300
|
||||
height: 30
|
||||
|
||||
ShortcutCapture {
|
||||
anchors.right: parent.right
|
||||
width: 230
|
||||
height: 30
|
||||
visible: bindRow.capturing
|
||||
focus: bindRow.capturing
|
||||
onCaptured: chord => {
|
||||
Keybinds.rebind(bindRow.modelData.luaChord, chord);
|
||||
root.capturingChord = "";
|
||||
}
|
||||
onCancelled: root.capturingChord = ""
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: !bindRow.capturing
|
||||
spacing: 8
|
||||
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: bindRow.modelData.chord
|
||||
color: bindRow.overridden ? Theme.accent : Theme.fgDim
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSize
|
||||
}
|
||||
|
||||
SettingsButton {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "Change"
|
||||
enabled: !Keybinds.reloading && !bindRow.modelData.mouse
|
||||
onClicked: root.capturingChord = bindRow.modelData.luaChord
|
||||
}
|
||||
|
||||
SettingsButton {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: bindRow.overridden
|
||||
text: "Reset"
|
||||
enabled: !Keybinds.reloading
|
||||
onClicked: Keybinds.resetBind(bindRow.modelData.luaChord)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
visible: Object.keys(Keybinds.overrides).length > 0
|
||||
title: "Changed shortcuts"
|
||||
subtitle: "Rebinding stores only the new chord; what a shortcut does always comes from Panama's configuration."
|
||||
|
||||
ActionRow {
|
||||
label: "Restore every shipped shortcut"
|
||||
detail: Object.keys(Keybinds.overrides).length
|
||||
+ (Object.keys(Keybinds.overrides).length === 1 ? " shortcut moved" : " shortcuts moved")
|
||||
action: "Restore all"
|
||||
divider: false
|
||||
enabled: !Keybinds.reloading
|
||||
onTriggered: Keybinds.resetAll()
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
visible: Keybinds.lastError !== ""
|
||||
title: "Shortcuts unavailable"
|
||||
|
||||
Reference in New Issue
Block a user