diff options
Diffstat (limited to 'desktop/modules/island/IslandPillsModule.qml')
| -rw-r--r-- | desktop/modules/island/IslandPillsModule.qml | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/desktop/modules/island/IslandPillsModule.qml b/desktop/modules/island/IslandPillsModule.qml new file mode 100644 index 0000000..9081e9e --- /dev/null +++ b/desktop/modules/island/IslandPillsModule.qml @@ -0,0 +1,140 @@ +// 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 +import "../.." + +// The order of the island's pills, dragged into place. +Module { + id: mod + name: "island-pills" + label: "Pills" + // nf-md-view_column, present in Inconsolata Nerd Font. + icon: "\u{f056d}" + settings: true + group: "Island bar" + keywords: ["island", "pills", "order", "layout", "clock", "player", "status", "obs"] + searchItems: [{ text: "Pill order", key: "pills" }] + + readonly property var names: ({ media: "Player", clock: "Clock", status: "Status", obs: "OBS" }) + property var draft: Prefs.island.pills + dirty: JSON.stringify(draft) !== JSON.stringify(Prefs.island.pills) + function refresh() { if (!mod.dirty) mod.draft = Prefs.island.pills; } + function moveTo(from, to) { + const d = mod.draft.slice(); + d.splice(to, 0, d.splice(from, 1)[0]); + mod.draft = d; + } + + page: Component { + Page { + title: "Pills" + actions: Component { + SaveBar { + dirty: mod.dirty + onSave: Prefs.setIsland({ pills: mod.draft }) + onDiscard: mod.draft = Prefs.island.pills + } + } + Column { + objectName: "pills" + width: parent.width + spacing: 8 + // Fixed slots, the centre bar between the second and third. + // A dropped pill takes the slot nearest where it lands. + Item { + id: strip + readonly property real slot: 96 + readonly property real mid: 24 + function slotX(i) { return i * slot + (i >= 2 ? mid : 0); } + // While a pill is dragged: where it came from and the slot + // it would land in; the others step aside for it. + property int from: -1 + property int to: -1 + function nearest(x) { + let to = 0; + for (let j = 1; j < 4; j++) + if (Math.abs(slotX(j) - x) < Math.abs(slotX(to) - x)) to = j; + return to; + } + function shown(i) { + if (from < 0 || i === from) return i; + if (from < to && i > from && i <= to) return i - 1; + if (to < from && i >= to && i < from) return i + 1; + return i; + } + width: slotX(4) + height: 32 + + Text { + x: strip.slotX(2) - strip.mid + width: strip.mid + anchors.verticalCenter: parent.verticalCenter + horizontalAlignment: Text.AlignHCenter + text: "|" + color: Theme.accent + font { family: Theme.fontFamily; pixelSize: Theme.fontSize } + } + + Repeater { + model: mod.draft + delegate: Rectangle { + id: pill + required property string modelData + required property int index + x: strip.slotX(strip.shown(index)) + Behavior on x { + enabled: !grab.drag.active + NumberAnimation { duration: 150; easing.type: Easing.OutCubic } + } + z: grab.drag.active ? 1 : 0 + width: strip.slot - 6 + height: strip.height + radius: 7 + color: grab.drag.active ? Qt.alpha(Theme.accent, 0.4) : Qt.alpha(Theme.accent, 0.25) + border.width: 1 + border.color: Qt.alpha(Theme.accent, 0.5) + Text { + anchors.centerIn: parent + text: mod.names[pill.modelData] + color: Theme.text + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 } + } + MouseArea { + id: grab + anchors.fill: parent + preventStealing: true + cursorShape: grab.drag.active ? Qt.ClosedHandCursor : Qt.OpenHandCursor + drag { target: pill; axis: Drag.XAxis; minimumX: 0; maximumX: strip.slotX(3) } + onPressed: { strip.from = pill.index; strip.to = pill.index; } + onPositionChanged: if (grab.drag.active) strip.to = strip.nearest(pill.x) + onReleased: { + const from = strip.from, to = strip.nearest(pill.x); + strip.from = -1; + // A move rebuilds the delegates; staying put needs the binding back. + if (to !== from) mod.moveTo(from, to); + else pill.x = Qt.binding(() => strip.slotX(strip.shown(pill.index))); + } + } + } + } + } + Text { + width: parent.width + wrapMode: Text.WordWrap + text: "Drag to reorder, left to right; the bar marks the screen's centre. Player shows only while something plays, OBS only while it runs." + color: Theme.overlay + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } + } + } + } + } +} |
