diff options
| author | Danilo M. <danix@danix.xyz> | 2026-11-07 12:00:58 +0100 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-11-07 12:00:58 +0100 |
| commit | d9a76b01d5a50af44e576c62efc1dbbf76db6203 (patch) | |
| tree | 921d6dbbf17f87a64aa8379807fdceab69fa02fb | |
| parent | 0811ff78e1716a7984aaa315bac0b2b4a8de248d (diff) | |
| download | quickshell-d9a76b01d5a50af44e576c62efc1dbbf76db6203.tar.gz quickshell-d9a76b01d5a50af44e576c62efc1dbbf76db6203.zip | |
sonix: drawer tile and the Device tab
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
| -rw-r--r-- | desktop/modules/sonix/SonixDevice.qml | 108 | ||||
| -rw-r--r-- | desktop/modules/sonix/SonixModule.qml | 40 | ||||
| -rw-r--r-- | desktop/modules/sonix/SonixPage.qml | 56 | ||||
| -rw-r--r-- | desktop/modules/sonix/SonixRow.qml | 66 | ||||
| -rw-r--r-- | desktop/modules/sonix/SonixTile.qml | 36 | ||||
| -rw-r--r-- | desktop/shell.qml | 2 |
6 files changed, 308 insertions, 0 deletions
diff --git a/desktop/modules/sonix/SonixDevice.qml b/desktop/modules/sonix/SonixDevice.qml new file mode 100644 index 0000000..c52b2f4 --- /dev/null +++ b/desktop/modules/sonix/SonixDevice.qml @@ -0,0 +1,108 @@ +// 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 "../.." + +Column { + id: dev + signal go(string name) + readonly property var p: Sonix.player + spacing: 10 + + component Line: Row { + id: line + property string k: "" + property string v: "" + spacing: 10 + Text { + width: 110 + text: line.k + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 } + color: Theme.subtext + } + Text { + text: line.v + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 } + color: Theme.text + } + } + + Line { k: "Name"; v: Sonix.name } + Line { k: "Address"; v: `${Sonix.host}:${Sonix.port}` } + Line { + k: "Battery" + v: dev.p.battery === undefined ? "\u2026" : dev.p.battery + "%" + (dev.p.charging ? ", charging" : "") + } + Row { + spacing: 10 + Text { + width: 110 + text: "Accent" + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 } + color: Theme.subtext + } + Rectangle { + width: 18; height: 18; radius: 4 + color: dev.p.accent || "transparent" + border.width: 1 + border.color: Qt.alpha(Theme.text, 0.2) + } + Text { + text: dev.p.accent || "" + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 } + color: Theme.text + } + } + Line { + k: "Library" + v: dev.p.scanning ? `scanning\u2026 ${dev.p.scan_count} found` + : dev.p.tracks === undefined ? "\u2026" : dev.p.tracks + " tracks" + } + Line { + k: "Now" + v: (["stopped", "playing", "paused"][dev.p.state || 0]) + (dev.p.title ? " \u00b7 " + dev.p.title : "") + } + + Row { + spacing: 8 + Button { + text: "Rescan" + enabled: !dev.p.scanning + onClicked: Sonix.command({ do: "scan" }, null, (ok, d, err) => msg.text = ok ? "" : err) + } + Button { + text: "Open Wi-Fi transfer" + onClicked: Qt.openUrlExternally(`http://${Sonix.host}/`) + } + Button { + text: "Boot logo\u2026" + onClicked: dev.go("bootlogo") + } + } + + Text { + width: dev.width + wrapMode: Text.Wrap + text: "Wi-Fi transfer answers only while its page is open on the player." + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 5 } + color: Theme.subtext + } + + Text { + id: msg + visible: text !== "" + width: dev.width + wrapMode: Text.Wrap + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } + color: Theme.red + } +} diff --git a/desktop/modules/sonix/SonixModule.qml b/desktop/modules/sonix/SonixModule.qml new file mode 100644 index 0000000..5146bf8 --- /dev/null +++ b/desktop/modules/sonix/SonixModule.qml @@ -0,0 +1,40 @@ +// 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 Sonix player in the drawer: a tile while it is on the network, and a +// page with its device state, playlists and folders. See README.md. +Module { + id: sonixMod + + name: "sonix" + label: Sonix.name || "Sonix" + icon: String.fromCodePoint(0xF075A) // md-music + tiled: Sonix.present + active: Sonix.present && Sonix.player.state === 1 + + // Names typed under Playlists > New: SonixLink makes a playlist only by + // adding a track to it, so a new one lives here until its first track. + property var draftPlaylists: [] + + tileContent: Component { SonixTile {} } + + page: Component { + Page { + id: pg + title: Sonix.name || "Sonix" + // Not `mod: mod`: inside SonixPage that names its own property. + SonixPage { width: parent.width; mod: sonixMod; onGo: name => pg.go(name) } + } + } +} diff --git a/desktop/modules/sonix/SonixPage.qml b/desktop/modules/sonix/SonixPage.qml new file mode 100644 index 0000000..1ee89a6 --- /dev/null +++ b/desktop/modules/sonix/SonixPage.qml @@ -0,0 +1,56 @@ +// 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 "../.." + +Column { + id: page + required property var mod + signal go(string name) + property string tab: "device" + spacing: 14 + + Component.onCompleted: Sonix.hold() + Component.onDestruction: Sonix.release() + + Text { + visible: !Sonix.present + text: "The player is not on the network." + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 } + color: Theme.subtext + } + + Row { + visible: Sonix.present + spacing: 6 + Repeater { + model: [["device", "Device"], ["playlists", "Playlists"], ["folders", "Folders"]] + delegate: Button { + required property var modelData + text: modelData[1] + border.color: page.tab === modelData[0] ? Theme.accent : Qt.alpha(Theme.text, 0.2) + onClicked: page.tab = modelData[0] + } + } + } + + Loader { + width: page.width + active: Sonix.present + sourceComponent: page.tab === "device" ? deviceTab + : page.tab === "playlists" ? playlistsTab : foldersTab + } + + Component { id: deviceTab; SonixDevice { onGo: name => page.go(name) } } + Component { id: playlistsTab; Item {} } + Component { id: foldersTab; Item {} } +} diff --git a/desktop/modules/sonix/SonixRow.qml b/desktop/modules/sonix/SonixRow.qml new file mode 100644 index 0000000..518aa95 --- /dev/null +++ b/desktop/modules/sonix/SonixRow.qml @@ -0,0 +1,66 @@ +// 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 "../.." + +// A list row: a label, an optional second line, trailing buttons (children), +// and a click on the text. +Item { + id: row + property string text: "" + property string sub: "" + default property alias buttons: trail.data + signal clicked() + + width: parent ? parent.width : implicitWidth + implicitHeight: Math.max(texts.implicitHeight, trail.implicitHeight) + 10 + + Rectangle { + anchors.fill: parent + radius: 6 + color: hit.containsMouse ? Qt.alpha(Theme.accent, 0.12) : "transparent" + } + + Column { + id: texts + anchors { left: parent.left; leftMargin: 8; right: trail.left; rightMargin: 8; verticalCenter: parent.verticalCenter } + Text { + width: parent.width + elide: Text.ElideRight + text: row.text + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 } + color: Theme.text + } + Text { + visible: row.sub !== "" + width: parent.width + elide: Text.ElideRight + text: row.sub + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 5 } + color: Theme.subtext + } + } + + MouseArea { + id: hit + anchors { left: parent.left; right: trail.left; top: parent.top; bottom: parent.bottom } + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: row.clicked() + } + + Row { + id: trail + anchors { right: parent.right; rightMargin: 4; verticalCenter: parent.verticalCenter } + spacing: 2 + } +} diff --git a/desktop/modules/sonix/SonixTile.qml b/desktop/modules/sonix/SonixTile.qml new file mode 100644 index 0000000..4d99d28 --- /dev/null +++ b/desktop/modules/sonix/SonixTile.qml @@ -0,0 +1,36 @@ +// 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 "../.." + +// "95% · playing". Holds the service while the drawer shows the tile. +Item { + width: parent ? parent.width : implicitWidth + implicitHeight: line.implicitHeight + + Component.onCompleted: Sonix.hold() + Component.onDestruction: Sonix.release() + + Text { + id: line + width: parent.width + elide: Text.ElideRight + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } + color: Theme.text + text: { + const p = Sonix.player; + if (p.battery === undefined) return "\u2026"; + return `${p.battery}%${p.charging ? " charging" : ""} \u00b7 ` + + (p.state === 1 ? "playing" : p.state === 2 ? "paused" : "idle"); + } + } +} diff --git a/desktop/shell.qml b/desktop/shell.qml index d9e5b6c..7fc293b 100644 --- a/desktop/shell.qml +++ b/desktop/shell.qml @@ -38,6 +38,7 @@ import "modules/notifications" import "modules/obs" import "modules/perf" import "modules/sound" +import "modules/sonix" import "modules/status" import "modules/updates" import "modules/user" @@ -80,6 +81,7 @@ ShellRoot { NetworkModule {}, BluetoothModule {}, KdeConnectModule {}, + SonixModule {}, MailModule {}, StatusModule {}, PerfModule {}, |
