From 5005ba24db4a4bbb32f091aeeaf72a4ece20749c Mon Sep 17 00:00:00 2001 From: lucy Date: Tue, 25 Aug 2026 20:11:19 +0200 Subject: [PATCH] add popup for audio management --- .qmlls.ini | 1 + Colors.js | 32 +++++------ modules/Services/Audio.qml | 34 ++++++++++++ modules/Services/Audio.qml~ | 10 ++++ modules/bar/Bar.qml | 76 ++++++++++++++------------- modules/bar/Pw.qml | 81 +++++----------------------- modules/bar/VolPopup.qml | 102 ++++++++++++++++++++++++++++++++++++ modules/bar/VolPopup.qml~ | 39 ++++++++++++++ modules/bar/volPopup.qml~ | 40 ++++++++++++++ settings/settings.ini | 2 +- shell.qml | 1 + 11 files changed, 298 insertions(+), 120 deletions(-) create mode 120000 .qmlls.ini create mode 100644 modules/Services/Audio.qml create mode 100644 modules/Services/Audio.qml~ create mode 100644 modules/bar/VolPopup.qml create mode 100644 modules/bar/VolPopup.qml~ create mode 100644 modules/bar/volPopup.qml~ diff --git a/.qmlls.ini b/.qmlls.ini new file mode 120000 index 0000000..2d149fd --- /dev/null +++ b/.qmlls.ini @@ -0,0 +1 @@ +/run/user/1000/quickshell/vfs/1b52f593d70d8ccef6fc467cf768a2ef/.qmlls.ini \ No newline at end of file diff --git a/Colors.js b/Colors.js index 9774932..4f4d276 100644 --- a/Colors.js +++ b/Colors.js @@ -1,18 +1,18 @@ const colors = { - base00: "#1e1e2e", - base01: "#181825", - base02: "#313244", - base03: "#45475a", - base04: "#585b70", - base05: "#cdd6f4", - base06: "#f5e0dc", - base07: "#b4befe", - base08: "#f38ba8", - base09: "#fab387", - base0A: "#f9e2af", - base0B: "#a6e3a1", - base0C: "#94e2d5", - base0D: "#89b4fa", - base0E: "#cba6f7", - base0F: "#f2cdcd", + base00: "#161616", + base01: "#262626", + base02: "#393939", + base03: "#525252", + base04: "#dde1e6", + base05: "#f2f4f8", + base06: "#ffffff", + base07: "#08bdba", + base08: "#ee5396", + base09: "#ff7eb6", + base0A: "#ff6f00", + base0B: "#42be65", + base0C: "#3ddbd9", + base0D: "#33b1ff", + base0E: "#be95ff", + base0F: "#82cfff", } diff --git a/modules/Services/Audio.qml b/modules/Services/Audio.qml new file mode 100644 index 0000000..6caff59 --- /dev/null +++ b/modules/Services/Audio.qml @@ -0,0 +1,34 @@ +pragma Singleton +import Quickshell +import QtQuick +import Quickshell.Services.Pipewire + +Singleton { + id: root + property bool ready: Pipewire.ready + property bool muted: defaultAudio.audio.muted + property PwNode defaultAudio: Pipewire.defaultAudioSink + property PwNode preferredAudio: Pipewire.preferredDefaultAudioSink + property real percentage: Math.round(preferredAudio?.audio.volume * 100) + readonly property list sinks: Pipewire.nodes.values.filter(n => n.audio && n.isSink && !n.isStream) + readonly property list sources: Pipewire.nodes.values.filter(n => n.audio && !n.isSink && !n.isStream) + PwObjectTracker { + objects: [...root.sinks, ...root.sources] + } + function getVolumeIcon(sink) { + let mute = sink.audio.muted + let volume = sink.audio.volume + if (mute) { + return "audio-volume-muted-symbolic"; + } + if (volume <= 0.33) { + return "audio-volume-low-symbolic"; + } + if (volume <= 0.66) { + return "audio-volume-medium-symbolic"; + } + if (volume >= 0.66) { + return "audio-volume-high-symbolic"; + } + } +} diff --git a/modules/Services/Audio.qml~ b/modules/Services/Audio.qml~ new file mode 100644 index 0000000..d0575c6 --- /dev/null +++ b/modules/Services/Audio.qml~ @@ -0,0 +1,10 @@ +pragma Singleton +import Quickshell +import QtQuick +import Quickshell.Pipewire + + + +Singleton { + property +} diff --git a/modules/bar/Bar.qml b/modules/bar/Bar.qml index 326dda1..5205ad1 100644 --- a/modules/bar/Bar.qml +++ b/modules/bar/Bar.qml @@ -6,44 +6,48 @@ import qs.settings import qs.widgets import qs - Variants { model: Quickshell.screens delegate: PanelWindow { - required property ShellScreen modelData - id: root - screen: modelData - anchors { - top: true - left: true - right: true - } - color: 'transparent' - implicitHeight: Config.barHeight - Rectangle { - anchors.fill: parent - color: Colors.barBg - RowLayout { - anchors.leftMargin: 10 - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - Ws { barScreen: root.modelData } - ActiveWindow {} - } - RowLayout { - anchors.centerIn: parent - Date {} - } - RowLayout { - anchors.rightMargin: 10 - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - Mpris {} - Bat {} - Pw {} - Tray {} - WallPicker {screenHeight: root.screen.height} - } - } + id: root + required property ShellScreen modelData + screen: modelData + anchors { + top: true + left: true + right: true + } + color: 'transparent' + implicitHeight: Config.barHeight + Rectangle { + anchors.fill: parent + color: Colors.barBg + RowLayout { + anchors.leftMargin: 10 + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + Ws { + barScreen: root.modelData + } + ActiveWindow {} + } + RowLayout { + anchors.centerIn: parent + Date {} + } + RowLayout { + anchors.rightMargin: 10 + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + Mpris {} + Bat {} + + Tray {} + WallPicker { + screenHeight: root.screen.height + } + Pw {} + } + } } } diff --git a/modules/bar/Pw.qml b/modules/bar/Pw.qml index 8db85f0..9fed159 100644 --- a/modules/bar/Pw.qml +++ b/modules/bar/Pw.qml @@ -9,6 +9,7 @@ import QtQuick.Effects import qs import qs.widgets import qs.settings +import qs.modules.Services BarComponent { id: root @@ -17,50 +18,23 @@ BarComponent { property bool mute: Pipewire.ready ? defaultAudio?.audio.muted : null implicitHeight: Config.moduleHeight implicitWidth: pwLayout.implicitWidth + 16 - function getVolumeIcon() { - let volume = Pipewire.ready ? root.defaultAudio.audio.volume : null; - if (root.mute) { - return "audio-volume-muted"; - } - if (volume <= 0.33) { - return "audio-volume-low"; - } - if (volume <= 0.66) { - return "audio-volume-medium"; - } - if (volume >= 0.66) { - return "audio-volume-high"; - } - - } - - PwObjectTracker { - objects: [Pipewire.ready ? Pipewire.defaultAudioSink : null] - } - - Process { - id: pavuLauncher - command: ["pavucontrol"] - running: false - } - RowLayout { id: pwLayout anchors.centerIn: root spacing: 4 - IconImage { - source: Quickshell.iconPath(root.getVolumeIcon() + "-symbolic", root.getVolumeIcon()) - implicitSize: 14 - layer.enabled: true - layer.effect: MultiEffect { - colorization: 1.0 - colorizationColor: Colors.text - brightness: 0.5 - } - } + IconImage { + source: Quickshell.iconPath(Audio.getVolumeIcon(Audio.defaultAudio)) + implicitSize: 14 + layer.enabled: true + layer.effect: MultiEffect { + colorization: 1.0 + colorizationColor: Colors.text + brightness: 0.5 + } + } StyledText { - text: Math.round(root.defaultAudio?.audio.volume * 100) + "%" - opacity: root.defaultAudio?.audio.muted ? 0.6 : 1.0 + text: Audio.percentage + "%" + opacity: Audio.muted ? 0.6 : 1.0 } } MouseArea { @@ -71,35 +45,8 @@ BarComponent { anchors.fill: root onClicked: volPopup.visible = !volPopup.visible } - PopupWindow { + VolPopup { id: volPopup anchor.item: root - implicitWidth: 250 - implicitHeight: 600 - - anchor.edges: Edges.Top - anchor.gravity: Edges.Bottom - anchor.margins.top: 40 - color: 'transparent' - Rectangle { - id: test - anchors.fill: parent - color: Colors.activeModuleBg - radius: Config.rounding - ListView { - id: sourceLayout - - model: Pipewire.nodes - delegate: Rectangle { - implicitHeight: 10 - - required property var modelData - StyledText { - text: parent.modelData.nickname - } - - } - } - } } } diff --git a/modules/bar/VolPopup.qml b/modules/bar/VolPopup.qml new file mode 100644 index 0000000..37e424f --- /dev/null +++ b/modules/bar/VolPopup.qml @@ -0,0 +1,102 @@ +import qs +import qs.settings +import Quickshell +import QtQuick +import qs.widgets +import qs.modules.Services +import QtQuick.Layouts +import QtQuick.Controls +import Quickshell.Widgets +import QtQuick.Effects + +PopupWindow { + implicitWidth: 400 + implicitHeight: audioLayout.implicitHeight + 24 + anchor.edges: Edges.Top | Edges.Right + anchor.gravity: Edges.Bottom | Edges.Left + anchor.margins.top: 40 + color: 'transparent' + Rectangle { + id: test + anchors.fill: parent + color: Colors.moduleBg + radius: Config.rounding + ColumnLayout { + id: audioLayout + anchors.fill: parent + anchors.margins: 12 + spacing: 8 + Repeater { + model: Audio.sinks + delegate: Rectangle { + id: audioDelContainer + color: Colors.activeModuleBg + radius: Config.rounding + required property var modelData + implicitWidth: 380 + implicitHeight: audioRow.implicitHeight + 8 + + RowLayout { + id: audioRow + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + anchors.fill: parent + + StyledText { + text: audioDelContainer.modelData.nickname + Layout.leftMargin: 10 + Layout.fillWidth: true + } + IconImage { + source: Quickshell.iconPath(Audio.getVolumeIcon(audioDelContainer.modelData)) + implicitSize: 16 + layer.enabled: true + layer.effect: MultiEffect { + colorization: 1.0 + colorizationColor: Colors.text + brightness: 0.5 + + } + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + acceptedButtons: Qt.LeftButton + onClicked: { + audioDelContainer.modelData.audio.muted = !audioDelContainer.modelData.audio.muted + } + } + } + IconImage { + source: Quickshell.iconPath(audioDelContainer.modelData === Audio.preferredAudio ? "radio-checked-symbolic" : "radio-symbolic") + implicitSize: 16 + layer.enabled: true + layer.effect: MultiEffect { + colorization: 1.0 + colorizationColor: Colors.text + brightness: 0.5 + + } + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + acceptedButtons: Qt.LeftButton + onClicked: { + Audio.preferredAudio = audioDelContainer.modelData + } + } + } + Slider { + Layout.rightMargin: 10 + value: audioDelContainer.modelData.audio.volume + from: 0 + to: 1 + onMoved: { + audioDelContainer.modelData.audio.volume = value; + } + } + } + } + } + } + } +} diff --git a/modules/bar/VolPopup.qml~ b/modules/bar/VolPopup.qml~ new file mode 100644 index 0000000..18d925a --- /dev/null +++ b/modules/bar/VolPopup.qml~ @@ -0,0 +1,39 @@ +import qs +import qs.settings +import Quickshell +import QtQuick +import qs.widgets + + + +PopupWindow { + id: volPopup + anchor.item: root + implicitWidth: 500 + implicitHeight: 600 + + anchor.edges: Edges.Top | Edges.Right + anchor.gravity: Edges.Bottom | Edges.Left + anchor.margins.top: 40 + anchor.margins.right: 0 + color: 'transparent' + Rectangle { + id: test + anchors.fill: parent + color: Colors.activeModuleBg + radius: Config.rounding + ListView { + id: sourceLayout + + model: Pipewire.nodes + delegate: Rectangle { + implicitHeight: 10 + + required property var modelData + StyledText { + text: parent.modelData.nickname + } + } + } + } + } diff --git a/modules/bar/volPopup.qml~ b/modules/bar/volPopup.qml~ new file mode 100644 index 0000000..582c1d1 --- /dev/null +++ b/modules/bar/volPopup.qml~ @@ -0,0 +1,40 @@ +import qs +import qs.settings +import Quickshell +import Quickshell.Widgets +import QtQuick +import qs.widgets + + + +PopupWindow { + id: volPopup + anchor.item: root + implicitWidth: 500 + implicitHeight: 600 + + anchor.edges: Edges.Top | Edges.Right + anchor.gravity: Edges.Bottom | Edges.Left + anchor.margins.top: 40 + anchor.margins.right: 0 + color: 'transparent' + Rectangle { + id: test + anchors.fill: parent + color: Colors.activeModuleBg + radius: Config.rounding + ListView { + id: sourceLayout + + model: Pipewire.nodes + delegate: Rectangle { + implicitHeight: 10 + + required property var modelData + StyledText { + text: parent.modelData.nickname + } + } + } + } + } diff --git a/settings/settings.ini b/settings/settings.ini index 065a0ff..d7267cc 100644 --- a/settings/settings.ini +++ b/settings/settings.ini @@ -6,7 +6,7 @@ fontSize=16 iconSize=12 opacity=1 primaryMon=eDP-1 -rounding=4 +rounding=16 showCorners=true wallDir=file:///home/lucy/.walls wallpaper=@Variant(\0\0\0\x11\0\0\0!/home/lucy/.walls/frierensuff.png) diff --git a/shell.qml b/shell.qml index badd681..66c9b05 100644 --- a/shell.qml +++ b/shell.qml @@ -4,6 +4,7 @@ import Quickshell import qs.modules.bar import qs.modules.wallpaper import qs.modules.notifications +import qs.modules.Services ShellRoot { Bar {}