From de3c9274b5fa90d549abb523c77b044297c7b82b Mon Sep 17 00:00:00 2001 From: tux Date: Mon, 24 Aug 2026 15:43:43 +0530 Subject: [PATCH] feat(bar): add audio indicator widget --- AGENTS.md | 6 ++++-- assets/icons/microphone.svg | 5 +++++ assets/icons/speaker.svg | 5 +++++ config/BarLayout.qml | 1 + modules/bar/AudioWidget.qml | 24 ++++++++++++++++++++++++ services/Audio.qml | 24 ++++++++++++++++++++++++ 6 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 assets/icons/microphone.svg create mode 100644 assets/icons/speaker.svg create mode 100644 modules/bar/AudioWidget.qml create mode 100644 services/Audio.qml diff --git a/AGENTS.md b/AGENTS.md index 9602cb6..3c1f642 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,12 +32,14 @@ shell.qml Entry point: Scope { Bar {}; Notification {} } │ └── Notification.qml Popup notification stack window ├── modules/ Feature widgets, grouped by surface │ ├── bar/ Widgets placed in the bar (Clock, Workspaces, -│ │ BatteryIndicator, CavaVisualizer, SystemTray, -│ │ LauncherButton, PowerProfileWidget, GhostButton) +│ │ BatteryIndicator, AudioWidget, CavaVisualizer, +│ │ SystemTray, LauncherButton, PowerProfileWidget, +│ │ GhostButton) │ └── osd/ NotificationCard.qml ├── services/ QML Singletons exposing system state │ ├── Time.qml SystemClock → formatted time string │ ├── Battery.qml UPower display device → percentage/icon +│ ├── Audio.qml PipeWire default sink/source → device names/icons │ ├── PowerProfile.qml UPower power profiles → name/icon │ ├── Cava.qml Spawns `cava`, parses stdout into `values[]` │ └── Notifications.qml NotificationServer: popups, DND flag, history diff --git a/assets/icons/microphone.svg b/assets/icons/microphone.svg new file mode 100644 index 0000000..9f32151 --- /dev/null +++ b/assets/icons/microphone.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/assets/icons/speaker.svg b/assets/icons/speaker.svg new file mode 100644 index 0000000..fd841bd --- /dev/null +++ b/assets/icons/speaker.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/config/BarLayout.qml b/config/BarLayout.qml index 51888d3..e449fcf 100644 --- a/config/BarLayout.qml +++ b/config/BarLayout.qml @@ -24,6 +24,7 @@ Singleton { ] property var right: [ + root.widget("AudioWidget"), root.widget("BatteryIndicator"), root.widget("SystemTrayWidget"), root.widget("ClockWidget"), diff --git a/modules/bar/AudioWidget.qml b/modules/bar/AudioWidget.qml new file mode 100644 index 0000000..65d9001 --- /dev/null +++ b/modules/bar/AudioWidget.qml @@ -0,0 +1,24 @@ +pragma ComponentBehavior: Bound + +import QtQuick.Layouts +import qs.ui +import qs.services + +RowLayout { +BarButton { + hoverHighlight: false + pointerCursor: false + label: Audio.inputName + iconSource: Audio.inputIcon +} + + +BarButton { + hoverHighlight: false + pointerCursor: false + label: Audio.outputName + iconSource: Audio.outputIcon +} + +} + diff --git a/services/Audio.qml b/services/Audio.qml new file mode 100644 index 0000000..e6b5d7e --- /dev/null +++ b/services/Audio.qml @@ -0,0 +1,24 @@ +pragma Singleton + +import QtQuick +import Quickshell +import Quickshell.Services.Pipewire + +Singleton { + id: root + + readonly property string outputName: root.displayName(Pipewire.defaultAudioSink) + readonly property string inputName: root.displayName(Pipewire.defaultAudioSource) + readonly property url outputIcon: `${Quickshell.shellPath("assets")}/icons/speaker.svg` + readonly property url inputIcon: `${Quickshell.shellPath("assets")}/icons/microphone.svg` + + function displayName(node: PwNode): string { + if (!node) + return "none"; + return node.description || node.name; + } + + PwObjectTracker { + objects: [Pipewire.defaultAudioSink, Pipewire.defaultAudioSource] + } +}