aboutsummaryrefslogtreecommitdiffstats
path: root/keybinds/PadPanel.qml
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-09-25 20:12:48 +0200
committerDanilo M. <danix@danix.xyz>2026-09-25 20:12:48 +0200
commit851dbc76b3ba533b128e4d42d25560a95d6d3722 (patch)
treeced834871dfa997e8be3142ce36b322b28326ccd /keybinds/PadPanel.qml
parent85206cf2939109b2b28044b35b37be510ad2b3fa (diff)
downloadquickshell-851dbc76b3ba533b128e4d42d25560a95d6d3722.tar.gz
quickshell-851dbc76b3ba533b128e4d42d25560a95d6d3722.zip
feat(keybinds): draw the macropad beside the list
The pad glyph on each row was too small to read. The left fifth of the overlay now draws the pad at size, each key labelled with its plain action; hovering a key or an encoder lists everything it does, held keys and turn direction spelled out. padActions() in Binds.js inverts the grouped rows into that per-key list, and PAD's turn values became words for it. The panel is pinned at a fixed height: centred, it moved whenever the hovered key's list changed length. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'keybinds/PadPanel.qml')
-rw-r--r--keybinds/PadPanel.qml162
1 files changed, 162 insertions, 0 deletions
diff --git a/keybinds/PadPanel.qml b/keybinds/PadPanel.qml
new file mode 100644
index 0000000..04c95a5
--- /dev/null
+++ b/keybinds/PadPanel.qml
@@ -0,0 +1,162 @@
+// Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+import QtQuick
+
+// The macropad as it sits on the desk: twelve keys in three rows of four,
+// the two encoders to their right. Each key carries its plain action;
+// hovering a key or an encoder lists everything it does underneath. The
+// last one hovered stays listed, so moving the pointer off does not blank it.
+Item {
+ id: panel
+
+ // Binds.padActions(): "k1".."k12", "top", "bottom" to [{ how, label }].
+ property var actions: ({})
+ property string hovered: ""
+
+ // Keys are unit wide; the pad is four of them, three gaps, a gap and an
+ // encoder: 5.56 units, fitted to most of the panel's width.
+ readonly property int unit: Math.floor(width * 0.85 / 5.56)
+ readonly property int padWidth: Math.round(unit * 5.56)
+
+ function plain(id) {
+ const a = (actions[id] ?? []).find(x => x.how === "Press");
+ return a ? a.label : "";
+ }
+
+ function title(id) {
+ if (id === "top") return "Top encoder";
+ if (id === "bottom") return "Bottom encoder";
+ return "Key " + id.slice(1);
+ }
+
+ // Pinned at a fixed height rather than centred: centred, the pad moved
+ // every time the list under it changed length.
+ Column {
+ anchors.horizontalCenter: parent.horizontalCenter
+ y: parent.height * 0.3
+ spacing: panel.unit * 0.5
+
+ Row {
+ spacing: panel.unit * 0.3
+
+ Grid {
+ id: keys
+ columns: 4
+ spacing: panel.unit * 0.12
+
+ Repeater {
+ model: 12
+
+ Rectangle {
+ required property int index
+ readonly property string padId: "k" + (index + 1)
+
+ width: panel.unit
+ height: panel.unit
+ radius: panel.unit * 0.12
+ color: panel.hovered === padId ? Theme.surfaceAlt : Theme.surface
+ border.color: panel.hovered === padId ? Theme.accent : "transparent"
+ border.width: 2
+
+ Text {
+ anchors.fill: parent
+ anchors.margins: 4
+ text: panel.plain(parent.padId)
+ wrapMode: Text.Wrap
+ elide: Text.ElideRight
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ color: Theme.text
+ font.family: Theme.fontFamily
+ font.pixelSize: Math.max(10, panel.unit * 0.16)
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ hoverEnabled: true
+ onEntered: panel.hovered = parent.padId
+ }
+ }
+ }
+ }
+
+ // One encoder level with the top row, one with the bottom.
+ Item {
+ width: panel.unit * 0.9
+ height: keys.height
+
+ Repeater {
+ model: ["top", "bottom"]
+
+ Rectangle {
+ required property string modelData
+
+ y: modelData === "top" ? 0 : parent.height - height
+ width: parent.width
+ height: width
+ radius: width / 2
+ color: panel.hovered === modelData ? Theme.surfaceAlt : Theme.surface
+ border.color: panel.hovered === modelData ? Theme.accent : Theme.overlay
+ border.width: 2
+
+ MouseArea {
+ anchors.fill: parent
+ hoverEnabled: true
+ onEntered: panel.hovered = parent.modelData
+ }
+ }
+ }
+ }
+ }
+
+ Column {
+ width: panel.padWidth
+ spacing: 6
+
+ Text {
+ text: panel.hovered ? panel.title(panel.hovered) : "Hover a key or an encoder"
+ color: Theme.accent
+ font.family: Theme.fontFamily
+ font.pixelSize: 18
+ font.bold: true
+ bottomPadding: 4
+ }
+
+ Repeater {
+ model: panel.actions[panel.hovered] ?? []
+
+ // Fixed widths summing to the column's: a Row sizes to its
+ // children.
+ Row {
+ required property var modelData
+ spacing: 12
+
+ Text {
+ width: panel.padWidth * 0.4
+ text: modelData.how
+ color: Theme.subtext
+ font.family: Theme.fontFamily
+ font.pixelSize: 15
+ }
+ Text {
+ width: panel.padWidth * 0.6 - 12
+ text: modelData.label
+ wrapMode: Text.Wrap
+ color: Theme.text
+ font.family: Theme.fontFamily
+ font.pixelSize: 15
+ }
+ }
+ }
+ }
+ }
+}