From 934fdbeb44ff96d86840e20f5d69497556539aa9 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Tue, 13 Oct 2026 09:53:35 +0200 Subject: feat(island): controls grid with an edit mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four columns, the visible list from Prefs. Edit mode puts a MouseArea over each tile that drags it and drops it into the slot under its centre, a × that hides it, and a strip of hidden tiles with a + each. Co-Authored-By: Claude Sonnet 5.5 --- desktop/island/ControlsPanel.qml | 175 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 desktop/island/ControlsPanel.qml (limited to 'desktop') 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. +// +// 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)) + } + } + } + } +} -- cgit v1.2.3