aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-07 18:52:19 +0200
committerDanilo M. <danix@danix.xyz>2026-10-07 18:52:19 +0200
commit744802e1ecb44ddb4714f8922b8ee0e9e5def811 (patch)
tree8d66ec6f99a58420038c56c1dcf27a148e008671
parent42feb6f8e3f088eb5707f4129eb994df7110a3e5 (diff)
downloadquickshell-744802e1ecb44ddb4714f8922b8ee0e9e5def811.tar.gz
quickshell-744802e1ecb44ddb4714f8922b8ee0e9e5def811.zip
feat(island): status pill with volume, tray and layout flag
The tray makes the island the session's StatusNotifier host, which waybar was until now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-rw-r--r--desktop/island/StatusPill.qml108
1 files changed, 108 insertions, 0 deletions
diff --git a/desktop/island/StatusPill.qml b/desktop/island/StatusPill.qml
new file mode 100644
index 0000000..faf24dd
--- /dev/null
+++ b/desktop/island/StatusPill.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 Quickshell
+import Quickshell.Widgets
+import Quickshell.Services.SystemTray
+import QtQuick
+import ".."
+import "Logic.js" as Logic
+
+// Volume icon, tray, layout flag. The volume icon is the only way into the
+// sound panel and has no other gesture; the tray is a plain tray; the flag
+// switches layout on scroll and ignores clicks.
+Capsule {
+ id: pill
+
+ required property QtObject sound
+ // The island window, which tray menus are positioned against.
+ required property QtObject window
+ property string panel: ""
+
+ signal openSound()
+ signal openDrawer()
+
+ expanded: panel === "sound"
+ restWidth: restRow.implicitWidth + 24
+ panelWidth: 320
+ panelHeight: soundPanel.implicitHeight + 28
+
+ Row {
+ id: restRow
+ anchors.centerIn: parent
+ spacing: 12
+ opacity: pill.expanded ? 0 : 1
+ visible: opacity > 0
+ Behavior on opacity { NumberAnimation { duration: 120 } }
+
+ IconImage {
+ anchors.verticalCenter: parent.verticalCenter
+ implicitSize: 18
+ source: Quickshell.iconPath(Logic.volumeIcon(pill.sound.volume, pill.sound.muted), true)
+
+ MouseArea {
+ anchors.fill: parent
+ cursorShape: Qt.PointingHandCursor
+ onClicked: pill.openSound()
+ }
+ }
+
+ Repeater {
+ model: SystemTray.items.values
+
+ delegate: IconImage {
+ id: trayIcon
+ required property SystemTrayItem modelData
+ anchors.verticalCenter: parent.verticalCenter
+ implicitSize: 16
+ source: modelData.icon
+
+ MouseArea {
+ anchors.fill: parent
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
+ cursorShape: Qt.PointingHandCursor
+ onClicked: mouse => {
+ const item = trayIcon.modelData;
+ const wantsMenu = mouse.button === Qt.RightButton || item.onlyMenu;
+ if (!wantsMenu) { item.activate(); return; }
+ if (!item.hasMenu) return;
+ const p = trayIcon.mapToItem(null, 0, trayIcon.height + 6);
+ item.display(pill.window, p.x, p.y);
+ }
+ }
+ }
+ }
+
+ Image {
+ anchors.verticalCenter: parent.verticalCenter
+ width: 18
+ height: 18
+ sourceSize: Qt.size(18, 18)
+ source: Qt.resolvedUrl("assets/" + Keyboard.flag)
+
+ WheelHandler {
+ onWheel: event => Keyboard.cycle(event.angleDelta.y > 0 ? "prev" : "next")
+ }
+ }
+ }
+
+ SoundPanel {
+ id: soundPanel
+ x: 14
+ y: 14
+ width: pill.panelWidth - 28
+ sound: pill.sound
+ opacity: pill.expanded ? 1 : 0
+ visible: opacity > 0
+ Behavior on opacity { NumberAnimation { duration: 160 } }
+ onOpenDrawer: pill.openDrawer()
+ }
+}