mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-05 21:15:38 -05:00
@@ -59,6 +59,7 @@ const DMS_ACTIONS = [
|
||||
{ id: "spawn dms ipc call audio decrement 10", label: "Volume Down (10%)" },
|
||||
{ id: "spawn dms ipc call audio mute", label: "Volume Mute Toggle" },
|
||||
{ id: "spawn dms ipc call audio micmute", label: "Microphone Mute Toggle" },
|
||||
{ id: "spawn dms ipc call audio cycleoutput", label: "Audio Output: Cycle" },
|
||||
{ id: "spawn dms ipc call brightness increment", label: "Brightness Up" },
|
||||
{ id: "spawn dms ipc call brightness increment 1", label: "Brightness Up (1%)" },
|
||||
{ id: "spawn dms ipc call brightness increment 5", label: "Brightness Up (5%)" },
|
||||
|
||||
@@ -313,6 +313,7 @@ Singleton {
|
||||
property bool osdMicMuteEnabled: true
|
||||
property bool osdCapsLockEnabled: true
|
||||
property bool osdPowerProfileEnabled: true
|
||||
property bool osdAudioOutputEnabled: true
|
||||
|
||||
property bool powerActionConfirm: true
|
||||
property int powerActionHoldDuration: 1
|
||||
|
||||
@@ -212,6 +212,7 @@ var SPEC = {
|
||||
osdMicMuteEnabled: { def: true },
|
||||
osdCapsLockEnabled: { def: true },
|
||||
osdPowerProfileEnabled: { def: false },
|
||||
osdAudioOutputEnabled: { def: true },
|
||||
|
||||
powerActionConfirm: { def: true },
|
||||
powerActionHoldDuration: { def: 1 },
|
||||
|
||||
@@ -700,6 +700,14 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Variants {
|
||||
model: SettingsData.getFilteredScreens("osd")
|
||||
|
||||
delegate: AudioOutputOSD {
|
||||
modelData: item
|
||||
}
|
||||
}
|
||||
|
||||
LazyLoader {
|
||||
id: hyprlandOverviewLoader
|
||||
active: CompositorService.isHyprland
|
||||
|
||||
80
quickshell/Modules/OSD/AudioOutputOSD.qml
Normal file
80
quickshell/Modules/OSD/AudioOutputOSD.qml
Normal file
@@ -0,0 +1,80 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
DankOSD {
|
||||
id: root
|
||||
|
||||
property string deviceName: ""
|
||||
property string deviceIcon: "speaker"
|
||||
|
||||
osdWidth: Math.min(Math.max(120, Theme.iconSize + textMetrics.width + Theme.spacingS * 4), Screen.width - Theme.spacingM * 2)
|
||||
osdHeight: 40 + Theme.spacingS * 2
|
||||
autoHideInterval: 2500
|
||||
enableMouseInteraction: false
|
||||
|
||||
TextMetrics {
|
||||
id: textMetrics
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
font.family: Theme.fontFamily
|
||||
text: root.deviceName
|
||||
}
|
||||
|
||||
function getIconForSink(sink) {
|
||||
if (!sink)
|
||||
return "speaker";
|
||||
const name = sink.name || "";
|
||||
if (name.includes("bluez"))
|
||||
return "headset";
|
||||
if (name.includes("hdmi"))
|
||||
return "tv";
|
||||
if (name.includes("usb"))
|
||||
return "headset";
|
||||
return "speaker";
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: AudioService
|
||||
|
||||
function onAudioOutputCycled(name) {
|
||||
if (!SettingsData.osdAudioOutputEnabled)
|
||||
return;
|
||||
root.deviceName = name;
|
||||
root.deviceIcon = getIconForSink(AudioService.sink);
|
||||
root.show();
|
||||
}
|
||||
}
|
||||
|
||||
content: Item {
|
||||
property int gap: Theme.spacingS
|
||||
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - Theme.spacingS * 2
|
||||
height: 40
|
||||
|
||||
DankIcon {
|
||||
id: iconItem
|
||||
width: Theme.iconSize
|
||||
height: Theme.iconSize
|
||||
x: parent.gap
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
name: root.deviceIcon
|
||||
size: Theme.iconSize
|
||||
color: Theme.primary
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: textItem
|
||||
x: parent.gap * 2 + Theme.iconSize
|
||||
width: parent.width - Theme.iconSize - parent.gap * 3
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: root.deviceName
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,6 +141,13 @@ Item {
|
||||
checked: SettingsData.osdPowerProfileEnabled
|
||||
onToggled: checked => SettingsData.set("osdPowerProfileEnabled", checked)
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
text: I18n.tr("Audio Output Switch")
|
||||
description: I18n.tr("Show on-screen display when cycling audio output devices")
|
||||
checked: SettingsData.osdAudioOutputEnabled
|
||||
onToggled: checked => SettingsData.set("osdAudioOutputEnabled", checked)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,33 @@ Singleton {
|
||||
property var mediaDevicesConnections: null
|
||||
|
||||
signal micMuteChanged
|
||||
signal audioOutputCycled(string deviceName)
|
||||
|
||||
function getAvailableSinks() {
|
||||
return Pipewire.nodes.values.filter(node => node.audio && node.isSink && !node.isStream);
|
||||
}
|
||||
|
||||
function cycleAudioOutput() {
|
||||
const sinks = getAvailableSinks();
|
||||
if (sinks.length < 2)
|
||||
return null;
|
||||
|
||||
const currentSink = root.sink;
|
||||
let currentIndex = -1;
|
||||
for (let i = 0; i < sinks.length; i++) {
|
||||
if (sinks[i] === currentSink) {
|
||||
currentIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const nextIndex = (currentIndex + 1) % sinks.length;
|
||||
const nextSink = sinks[nextIndex];
|
||||
Pipewire.preferredDefaultAudioSink = nextSink;
|
||||
const name = displayName(nextSink);
|
||||
audioOutputCycled(name);
|
||||
return name;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.sink?.audio ?? null
|
||||
@@ -595,6 +622,13 @@ Singleton {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function cycleoutput(): string {
|
||||
const result = root.cycleAudioOutput();
|
||||
if (!result)
|
||||
return "Only one audio output available";
|
||||
return `Switched to: ${result}`;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
|
||||
Reference in New Issue
Block a user