aboutsummaryrefslogtreecommitdiffstats
path: root/desktop
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-07 18:52:18 +0200
committerDanilo M. <danix@danix.xyz>2026-10-07 18:52:18 +0200
commit42feb6f8e3f088eb5707f4129eb994df7110a3e5 (patch)
tree9090dbb840cd5e395fe17dcc18de8be320645253 /desktop
parent26418cd5e6f00c4d81c12777b0eb8291cda07bc7 (diff)
downloadquickshell-42feb6f8e3f088eb5707f4129eb994df7110a3e5.tar.gz
quickshell-42feb6f8e3f088eb5707f4129eb994df7110a3e5.zip
feat(island): sound panel with output and input levels
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'desktop')
-rw-r--r--desktop/island/SoundPanel.qml113
1 files changed, 113 insertions, 0 deletions
diff --git a/desktop/island/SoundPanel.qml b/desktop/island/SoundPanel.qml
new file mode 100644
index 0000000..115fd75
--- /dev/null
+++ b/desktop/island/SoundPanel.qml
@@ -0,0 +1,113 @@
+// 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 "Logic.js" as Logic
+
+// Output and input level with mute, and the way into the drawer's sound
+// page. Device pickers and per-app volume stay on that page.
+Column {
+ id: panel
+
+ // The sound module's Service: sink, source, tracked by PwObjectTracker.
+ required property QtObject sound
+ signal openDrawer()
+
+ spacing: 12
+
+ Repeater {
+ model: [{ label: "Output", input: false }, { label: "Input", input: true }]
+
+ delegate: Row {
+ id: row
+ required property var modelData
+ readonly property var audio:
+ (modelData.input ? panel.sound.source : panel.sound.sink)?.audio ?? null
+
+ width: panel.width
+ spacing: 10
+
+ IconImage {
+ id: muteIcon
+ anchors.verticalCenter: parent.verticalCenter
+ implicitSize: 18
+ source: Quickshell.iconPath(row.modelData.input
+ ? (row.audio?.muted ? "microphone-sensitivity-muted" : "audio-input-microphone")
+ : Logic.volumeIcon(row.audio?.volume ?? 0, row.audio?.muted ?? false), true)
+
+ MouseArea {
+ anchors.fill: parent
+ enabled: row.audio !== null
+ cursorShape: Qt.PointingHandCursor
+ onClicked: row.audio.muted = !row.audio.muted
+ }
+ }
+
+ Rectangle {
+ id: track
+ anchors.verticalCenter: parent.verticalCenter
+ width: row.width - muteIcon.width - pct.width - row.spacing * 2
+ height: 8
+ radius: 4
+ color: Theme.surface
+
+ Rectangle {
+ height: parent.height
+ radius: parent.radius
+ width: parent.width * Math.min(1, row.audio?.volume ?? 0)
+ color: row.audio?.muted ? Theme.red : Theme.accent
+ opacity: row.audio?.muted ? 0.5 : 1
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ enabled: row.audio !== null
+ preventStealing: true
+ cursorShape: Qt.PointingHandCursor
+ onPressed: mouse => set(mouse.x)
+ onPositionChanged: mouse => { if (pressed) set(mouse.x); }
+ function set(x) {
+ row.audio.volume = Math.max(0, Math.min(1, x / width));
+ }
+ }
+ }
+
+ Text {
+ id: pct
+ anchors.verticalCenter: parent.verticalCenter
+ width: 44
+ horizontalAlignment: Text.AlignRight
+ text: !row.audio ? "—" : row.audio.muted ? "muted"
+ : Math.round(row.audio.volume * 100) + "%"
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 }
+ color: row.audio?.muted ? Theme.red : Theme.subtext
+ }
+ }
+ }
+
+ Text {
+ anchors.right: parent.right
+ text: "Sound settings ›"
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 }
+ color: drawerLink.containsMouse ? Theme.accent : Theme.subtext
+
+ MouseArea {
+ id: drawerLink
+ anchors.fill: parent
+ hoverEnabled: true
+ cursorShape: Qt.PointingHandCursor
+ onClicked: panel.openDrawer()
+ }
+ }
+}