aboutsummaryrefslogtreecommitdiffstats
path: root/keybinds/PadPanel.qml
blob: 04c95a5be80a024b158d63c027c6a3c274fc1503 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
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
                    }
                }
            }
        }
    }
}