aboutsummaryrefslogtreecommitdiffstats
path: root/desktop
diff options
context:
space:
mode:
Diffstat (limited to 'desktop')
-rw-r--r--desktop/island/ControlsPanel.qml175
1 files changed, 175 insertions, 0 deletions
diff --git a/desktop/island/ControlsPanel.qml b/desktop/island/ControlsPanel.qml
new file mode 100644
index 0000000..a21b892
--- /dev/null
+++ b/desktop/island/ControlsPanel.qml
@@ -0,0 +1,175 @@
+// 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 Quickshell
+import Quickshell.Widgets
+import QtQuick
+import ".."
+import "Controls.js" as Controls
+
+// The grid of controls and its edit mode. In edit mode a tile is dragged to
+// a new slot, a × hides it, and hidden tiles wait in a strip below with a +.
+// Every change is written to Prefs at once.
+Column {
+ id: panel
+
+ required property QtObject sound
+ property bool editing: false
+
+ signal openDrawer(string page)
+
+ spacing: 12
+
+ Item {
+ width: panel.width
+ height: 20
+
+ Text {
+ anchors.verticalCenter: parent.verticalCenter
+ text: panel.editing ? "Edit controls" : "Controls"
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2; bold: true }
+ color: Theme.text
+ }
+
+ IconImage {
+ anchors { right: parent.right; verticalCenter: parent.verticalCenter }
+ implicitSize: 16
+ opacity: panel.editing ? 1 : 0.6
+ source: Quickshell.iconPath("document-edit", true)
+
+ MouseArea {
+ anchors.fill: parent
+ anchors.margins: -4
+ cursorShape: Qt.PointingHandCursor
+ onClicked: panel.editing = !panel.editing
+ }
+ }
+ }
+
+ Item {
+ id: grid
+
+ readonly property int cols: 4
+ readonly property real gap: 8
+ readonly property real cell: (width - gap * (cols - 1)) / cols
+ readonly property real pitch: cell + gap
+ readonly property int rows: Math.ceil(Prefs.controls.length / cols)
+
+ width: panel.width
+ height: Math.max(0, rows * pitch - gap)
+
+ Repeater {
+ model: Prefs.controls
+
+ delegate: Item {
+ id: slot
+ required property string modelData
+ required property int index
+
+ width: grid.cell
+ height: grid.cell
+ x: (index % grid.cols) * grid.pitch
+ y: Math.floor(index / grid.cols) * grid.pitch
+ z: mover.pressed ? 1 : 0
+
+ ControlTile {
+ width: parent.width
+ height: parent.height
+ x: mover.pressed ? mover.dx : 0
+ y: mover.pressed ? mover.dy : 0
+ tileId: slot.modelData
+ sound: panel.sound
+ onOpenPage: page => panel.openDrawer(page)
+ }
+
+ // Edit mode only, and above the tile so it takes the press
+ // instead of the toggle. Offsets the tile while held; on
+ // release the tile lands in the slot under its centre.
+ MouseArea {
+ id: mover
+ anchors.fill: parent
+ enabled: panel.editing
+ preventStealing: true
+ cursorShape: pressed ? Qt.ClosedHandCursor : Qt.OpenHandCursor
+ property real sx: 0
+ property real sy: 0
+ property real dx: 0
+ property real dy: 0
+ onPressed: mouse => { sx = mouse.x; sy = mouse.y; dx = 0; dy = 0; }
+ onPositionChanged: mouse => { dx = mouse.x - sx; dy = mouse.y - sy; }
+ onReleased: {
+ const to = Controls.slotAt(slot.x + dx + width / 2, slot.y + dy + height / 2,
+ grid.pitch, grid.cols, Prefs.controls.length);
+ if (to !== slot.index)
+ Prefs.setControls(Controls.move(Prefs.controls, slot.index, to));
+ }
+ }
+
+ IconImage {
+ visible: panel.editing && !mover.pressed
+ anchors { top: parent.top; right: parent.right; margins: 3 }
+ implicitSize: 14
+ source: Quickshell.iconPath("window-close", true)
+
+ MouseArea {
+ anchors.fill: parent
+ anchors.margins: -4
+ cursorShape: Qt.PointingHandCursor
+ onClicked: Prefs.setControls(Controls.hide(Prefs.controls, slot.modelData))
+ }
+ }
+ }
+ }
+ }
+
+ Flow {
+ width: panel.width
+ spacing: 6
+ visible: panel.editing && Controls.hidden(Prefs.controls).length > 0
+
+ Repeater {
+ model: Controls.hidden(Prefs.controls)
+
+ delegate: Rectangle {
+ id: chip
+ required property string modelData
+ width: chipRow.implicitWidth + 16
+ height: 28
+ radius: 14
+ color: Qt.alpha(Theme.surface, 0.5)
+
+ Row {
+ id: chipRow
+ anchors.centerIn: parent
+ spacing: 4
+
+ IconImage {
+ anchors.verticalCenter: parent.verticalCenter
+ implicitSize: 14
+ source: Quickshell.iconPath("list-add", true)
+ }
+ Text {
+ anchors.verticalCenter: parent.verticalCenter
+ text: Controls.tile(chip.modelData).label
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 }
+ color: Theme.text
+ }
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ cursorShape: Qt.PointingHandCursor
+ onClicked: Prefs.setControls(Controls.show(Prefs.controls, chip.modelData))
+ }
+ }
+ }
+ }
+}