Shortcuts you invent, rules you write, gestures you own - all still just data
Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
@@ -106,13 +106,46 @@ SettingsPage {
|
||||
readonly property bool filtering: root.filter.trim() !== ""
|
||||
readonly property int overrideCount: Object.keys(Keybinds.overrides).length
|
||||
|
||||
// ── The shortcuts you invented ──────────────────────────────────────────
|
||||
// Rendered from the stored `customBinds` array rather than from the
|
||||
// compositor's report, for two reasons: the stored entry is the only place
|
||||
// the ACTION is written down (Hyprland reports every Lua bind as "__lua"
|
||||
// plus a bytecode offset), and a shortcut has to appear the moment it is
|
||||
// added rather than after the reload settles. The row asks Keybinds whether
|
||||
// the compositor is actually answering it, so nothing here claims a bind
|
||||
// that did not take.
|
||||
property bool adding: false
|
||||
|
||||
// The chord of the custom bind being re-recorded, empty when none is.
|
||||
property string rebindingChord: ""
|
||||
|
||||
readonly property var customBinds: {
|
||||
const needle = root.filter.trim().toLowerCase();
|
||||
if (needle === "")
|
||||
return Keybinds.customBinds;
|
||||
return Keybinds.customBinds.filter(entry =>
|
||||
String(entry?.label ?? "").toLowerCase().indexOf(needle) >= 0
|
||||
|| "custom".indexOf(needle) >= 0);
|
||||
}
|
||||
|
||||
function customMessage(): string {
|
||||
return root.conflict === ""
|
||||
? ""
|
||||
: root.conflictChord + " is already " + root.conflict;
|
||||
}
|
||||
|
||||
// Every group the compositor reports, in Keybinds' own order, narrowed by
|
||||
// the filter. An empty filter narrows nothing: the page's job is to show
|
||||
// the whole keymap, and searching is an extra rather than a gate.
|
||||
//
|
||||
// "Custom" is dropped here: those binds are drawn above from the stored
|
||||
// entries, and showing them twice would read as two shortcuts on one chord.
|
||||
readonly property var groups: {
|
||||
const needle = root.filter.trim().toLowerCase();
|
||||
const out = [];
|
||||
for (const group of Keybinds.grouped()) {
|
||||
if (group.name === "Custom")
|
||||
continue;
|
||||
const hits = needle === ""
|
||||
? group.binds
|
||||
: group.binds.filter(bind =>
|
||||
@@ -264,7 +297,7 @@ SettingsPage {
|
||||
|
||||
SettingsCard {
|
||||
title: "Shortcuts"
|
||||
subtitle: "Click Change and press the new keys. A shortcut another action holds is refused, never stolen."
|
||||
subtitle: "Click Change and press the new keys. A shortcut another action holds is refused, never stolen. Your own shortcuts live in the Custom group — none of them stores a command: each names an application, a shell action, or a window move, and Panama resolves the name when you press it."
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
@@ -283,14 +316,107 @@ SettingsPage {
|
||||
Text {
|
||||
id: counts
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.right: addButton.left
|
||||
anchors.rightMargin: 12
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: Keybinds.binds.length + " bound · " + root.overrideCount + " changed"
|
||||
text: Keybinds.binds.length + " bound · " + root.overrideCount + " changed · "
|
||||
+ Keybinds.customBinds.length + " custom"
|
||||
color: Theme.fgMuted
|
||||
font.family: Theme.fontFamily
|
||||
font.features: Theme.tabularFigures
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
}
|
||||
|
||||
SettingsButton {
|
||||
id: addButton
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
tone: "accent"
|
||||
text: "+ Add shortcut"
|
||||
enabled: !root.adding && !Keybinds.reloading
|
||||
onClicked: {
|
||||
root.capturingChord = "";
|
||||
root.rebindingChord = "";
|
||||
root.conflict = "";
|
||||
root.conflictChord = "";
|
||||
addEditor.reset();
|
||||
root.adding = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomShortcutEditor {
|
||||
id: addEditor
|
||||
|
||||
width: parent.width
|
||||
visible: root.adding
|
||||
|
||||
onCommitted: (chord, kind, target, label) => {
|
||||
if (Keybinds.addCustomBind(chord, kind, target, label))
|
||||
root.adding = false;
|
||||
}
|
||||
|
||||
onCanceled: root.adding = false
|
||||
}
|
||||
|
||||
// The Custom group, pinned above everything the compositor reports.
|
||||
Column {
|
||||
width: parent.width
|
||||
visible: root.customBinds.length > 0
|
||||
spacing: 0
|
||||
|
||||
SectionLabel {
|
||||
text: "Custom — yours"
|
||||
count: root.filtering
|
||||
? "· showing " + root.customBinds.length + " of " + Keybinds.customBinds.length
|
||||
: "· " + Keybinds.customBinds.length
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: customRows
|
||||
|
||||
model: root.customBinds
|
||||
|
||||
CustomShortcutRow {
|
||||
id: customRow
|
||||
|
||||
required property var modelData
|
||||
required property int index
|
||||
|
||||
entry: customRow.modelData
|
||||
capturing: root.rebindingChord === String(customRow.modelData.chord ?? "")
|
||||
message: customRow.capturing ? root.customMessage() : ""
|
||||
divider: customRow.index < customRows.count - 1
|
||||
|
||||
onRebindRequested: {
|
||||
root.conflict = "";
|
||||
root.conflictChord = "";
|
||||
root.capturingChord = "";
|
||||
root.rebindingChord = String(customRow.modelData.chord ?? "");
|
||||
}
|
||||
|
||||
onRemoveRequested: Keybinds.removeCustomBind(String(customRow.modelData.chord ?? ""))
|
||||
|
||||
onCaptured: chord => {
|
||||
const current = String(customRow.modelData.chord ?? "");
|
||||
const taken = Keybinds.boundTo(chord, current);
|
||||
if (taken !== "") {
|
||||
root.conflict = taken;
|
||||
root.conflictChord = chord;
|
||||
return;
|
||||
}
|
||||
root.conflict = "";
|
||||
Keybinds.rebindCustomBind(current, chord);
|
||||
root.rebindingChord = "";
|
||||
}
|
||||
|
||||
onCanceled: {
|
||||
root.conflict = "";
|
||||
root.rebindingChord = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// One Column of Repeaters rather than a Loader per row: at a hundred and
|
||||
@@ -363,6 +489,7 @@ SettingsPage {
|
||||
onChangeRequested: {
|
||||
root.conflict = "";
|
||||
root.conflictChord = "";
|
||||
root.rebindingChord = "";
|
||||
root.capturingChord = shortcutRow.modelData.luaChord;
|
||||
}
|
||||
|
||||
@@ -395,7 +522,7 @@ SettingsPage {
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
visible: root.groups.length === 0 && Keybinds.loaded
|
||||
visible: root.groups.length === 0 && root.customBinds.length === 0 && Keybinds.loaded
|
||||
text: root.filtering
|
||||
? "Nothing matches — the filter searches shortcut names and group names."
|
||||
: "The compositor reported no shortcuts."
|
||||
|
||||
Reference in New Issue
Block a user