From 4b9643a80229ecf0cf7e82c67124870fe95198f6 Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Wed, 27 Aug 2025 15:52:18 +1000 Subject: osd: add mic volume Disabled by default --- README.md | 1 + config/OsdConfig.qml | 1 + modules/drawers/Panels.qml | 2 +- modules/osd/Content.qml | 122 ++++++++++++++++++++++++++++++++------------- modules/osd/Wrapper.qml | 5 +- services/Audio.qml | 18 +++++++ utils/Icons.qml | 6 +++ 7 files changed, 116 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 536680f..d5d1f83 100644 --- a/README.md +++ b/README.md @@ -326,6 +326,7 @@ default, you must create it manually. "osd": { "enabled": true, "enableBrightness": true, + "enableMicrophone": false, "hideDelay": 2000 }, "paths": { diff --git a/config/OsdConfig.qml b/config/OsdConfig.qml index ea2375b..543fc41 100644 --- a/config/OsdConfig.qml +++ b/config/OsdConfig.qml @@ -4,6 +4,7 @@ JsonObject { property bool enabled: true property int hideDelay: 2000 property bool enableBrightness: true + property bool enableMicrophone: false property Sizes sizes: Sizes {} component Sizes: JsonObject { diff --git a/modules/drawers/Panels.qml b/modules/drawers/Panels.qml index 0e592df..5d42d34 100644 --- a/modules/drawers/Panels.qml +++ b/modules/drawers/Panels.qml @@ -33,7 +33,7 @@ Item { clip: root.visibilities.session screen: root.screen - visibility: root.visibilities.osd + visibilities: root.visibilities anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right diff --git a/modules/osd/Content.qml b/modules/osd/Content.qml index 80fc714..707695d 100644 --- a/modules/osd/Content.qml +++ b/modules/osd/Content.qml @@ -1,73 +1,123 @@ pragma ComponentBehavior: Bound +import qs.components import qs.components.controls import qs.services import qs.config import qs.utils import QtQuick +import QtQuick.Layouts -Column { +Item { id: root required property Brightness.Monitor monitor - - padding: Appearance.padding.large + required property var visibilities anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left - spacing: Appearance.spacing.normal - - // Speaker volume - CustomMouseArea { - implicitWidth: Config.osd.sizes.sliderWidth - implicitHeight: Config.osd.sizes.sliderHeight - - onWheel: event => { - if (event.angleDelta.y > 0) - Audio.incrementVolume(); - else if (event.angleDelta.y < 0) - Audio.decrementVolume(); - } + implicitWidth: layout.implicitWidth + Appearance.padding.large * 2 + implicitHeight: layout.implicitHeight + Appearance.padding.large * 2 - FilledSlider { - anchors.fill: parent - - icon: Icons.getVolumeIcon(value, Audio.muted) - value: Audio.volume - onMoved: Audio.setVolume(value) - } - } + ColumnLayout { + id: layout - // Brightness - WrappedLoader { - active: Config.osd.enableBrightness + anchors.centerIn: parent + spacing: Appearance.spacing.normal - sourceComponent: CustomMouseArea { + // Speaker volume + CustomMouseArea { implicitWidth: Config.osd.sizes.sliderWidth implicitHeight: Config.osd.sizes.sliderHeight onWheel: event => { - const monitor = root.monitor; - if (!monitor) - return; if (event.angleDelta.y > 0) - monitor.setBrightness(monitor.brightness + 0.1); + Audio.incrementVolume(); else if (event.angleDelta.y < 0) - monitor.setBrightness(monitor.brightness - 0.1); + Audio.decrementVolume(); } FilledSlider { anchors.fill: parent - icon: `brightness_${(Math.round(value * 6) + 1)}` - value: root.monitor?.brightness ?? 0 - onMoved: root.monitor?.setBrightness(value) + icon: Icons.getVolumeIcon(value, Audio.muted) + value: Audio.volume + onMoved: Audio.setVolume(value) + } + } + + // Microphone volume + WrappedLoader { + shouldBeActive: Config.osd.enableMicrophone && (!Config.osd.enableBrightness || !root.visibilities.session) + + sourceComponent: CustomMouseArea { + implicitWidth: Config.osd.sizes.sliderWidth + implicitHeight: Config.osd.sizes.sliderHeight + + onWheel: event => { + if (event.angleDelta.y > 0) + Audio.incrementSourceVolume(); + else if (event.angleDelta.y < 0) + Audio.decrementSourceVolume(); + } + + FilledSlider { + anchors.fill: parent + + icon: Icons.getMicVolumeIcon(value, Audio.sourceMuted) + value: Audio.sourceVolume + onMoved: Audio.setSourceVolume(value) + } + } + } + + // Brightness + WrappedLoader { + shouldBeActive: Config.osd.enableBrightness + + sourceComponent: CustomMouseArea { + implicitWidth: Config.osd.sizes.sliderWidth + implicitHeight: Config.osd.sizes.sliderHeight + + onWheel: event => { + const monitor = root.monitor; + if (!monitor) + return; + if (event.angleDelta.y > 0) + monitor.setBrightness(monitor.brightness + 0.1); + else if (event.angleDelta.y < 0) + monitor.setBrightness(monitor.brightness - 0.1); + } + + FilledSlider { + anchors.fill: parent + + icon: `brightness_${(Math.round(value * 6) + 1)}` + value: root.monitor?.brightness ?? 0 + onMoved: root.monitor?.setBrightness(value) + } } } } + component WrappedLoader: Loader { + required property bool shouldBeActive + + Layout.preferredHeight: shouldBeActive ? Config.osd.sizes.sliderHeight : 0 + opacity: shouldBeActive ? 1 : 0 + active: opacity > 0 asynchronous: true visible: active + + Behavior on Layout.preferredHeight { + Anim { + easing.bezierCurve: Appearance.anim.curves.emphasized + } + } + + Behavior on opacity { + Anim {} + } } } diff --git a/modules/osd/Wrapper.qml b/modules/osd/Wrapper.qml index c1e96d6..62d8c4f 100644 --- a/modules/osd/Wrapper.qml +++ b/modules/osd/Wrapper.qml @@ -8,7 +8,7 @@ Item { id: root required property ShellScreen screen - required property bool visibility + required property var visibilities visible: width > 0 implicitWidth: 0 @@ -16,7 +16,7 @@ Item { states: State { name: "visible" - when: root.visibility && Config.osd.enabled + when: root.visibilities.osd && Config.osd.enabled PropertyChanges { root.implicitWidth: content.implicitWidth @@ -50,5 +50,6 @@ Item { id: content monitor: Brightness.getMonitorForScreen(root.screen) + visibilities: root.visibilities } } diff --git a/services/Audio.qml b/services/Audio.qml index e156e6e..a0475d0 100644 --- a/services/Audio.qml +++ b/services/Audio.qml @@ -29,6 +29,9 @@ Singleton { readonly property bool muted: !!sink?.audio?.muted readonly property real volume: sink?.audio?.volume ?? 0 + readonly property bool sourceMuted: !!source?.audio?.muted + readonly property real sourceVolume: source?.audio?.volume ?? 0 + function setVolume(newVolume: real): void { if (sink?.ready && sink?.audio) { sink.audio.muted = false; @@ -44,6 +47,21 @@ Singleton { setVolume(volume - (amount || Config.services.audioIncrement)); } + function setSourceVolume(newVolume: real): void { + if (source?.ready && source?.audio) { + source.audio.muted = false; + source.audio.volume = Math.max(0, Math.min(1, newVolume)); + } + } + + function incrementSourceVolume(amount: real): void { + setSourceVolume(sourceVolume + (amount || Config.services.audioIncrement)); + } + + function decrementSourceVolume(amount: real): void { + setSourceVolume(sourceVolume - (amount || Config.services.audioIncrement)); + } + function setAudioSink(newSink: PwNode): void { Pipewire.preferredDefaultAudioSink = newSink; } diff --git a/utils/Icons.qml b/utils/Icons.qml index cf81d67..826ccbe 100644 --- a/utils/Icons.qml +++ b/utils/Icons.qml @@ -184,6 +184,12 @@ Singleton { return "volume_mute"; } + function getMicVolumeIcon(volume: real, isMuted: bool): string { + if (!isMuted && volume > 0) + return "mic"; + return "mic_off"; + } + function getSpecialWsIcon(name: string): string { name = name.toLowerCase().slice("special:".length); if (name === "special") -- cgit v1.2.3-freya