1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-06-15 07:35:20 -04:00

feat(media-control): support scroll and right-click on audio output devices in media popout (#2615)

* feat(media-control): support scroll and right-click on audio output devices in media popout

* feat(media-control): make device list volume scrolling optional
This commit is contained in:
Huỳnh Thiện Lộc
2026-06-14 04:51:03 +07:00
committed by GitHub
parent 3701b3d7a3
commit 9d1a81c93c
7 changed files with 86 additions and 7 deletions
+1
View File
@@ -397,6 +397,7 @@ Singleton {
property bool audioVisualizerEnabled: true
property string audioScrollMode: "volume"
property int audioWheelScrollAmount: 5
property bool audioDeviceScrollVolumeEnabled: false
property bool clockCompactMode: false
property int focusedWindowSize: 1
property bool focusedWindowCompactMode: false
@@ -156,6 +156,7 @@ var SPEC = {
audioVisualizerEnabled: { def: true },
audioScrollMode: { def: "volume" },
audioWheelScrollAmount: { def: 5 },
audioDeviceScrollVolumeEnabled: { def: false },
clockCompactMode: { def: false },
focusedWindowCompactMode: { def: false },
focusedWindowSize: { def: 1 },
@@ -108,9 +108,6 @@ DankPopout {
MprisController.setActivePlayer(player);
root.__hideDropdowns();
}
onDeviceSelected: device => {
root.__hideDropdowns();
}
}
}
@@ -383,7 +383,27 @@ Item {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
acceptedButtons: Qt.LeftButton | Qt.RightButton
onPressed: mouse => {
if (mouse.button === Qt.RightButton) {
mouse.accepted = true;
}
}
onWheel: wheelEvent => {
if (SettingsData.audioDeviceScrollVolumeEnabled && wheelEvent.x >= deviceMouseArea.width / 2) {
AudioService.handleNodeVolumeWheel(modelData, wheelEvent);
} else {
wheelEvent.accepted = false;
}
}
onClicked: mouse => {
if (mouse.button === Qt.RightButton) {
if (modelData && modelData.audio) {
SessionData.suppressOSDTemporarily();
modelData.audio.muted = !modelData.audio.muted;
}
return;
}
if (modelData && modelData.name) {
AudioService.setDefaultSinkByName(modelData.name);
root.deviceSelected(modelData);
+21 -1
View File
@@ -866,7 +866,27 @@ Item {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
acceptedButtons: Qt.LeftButton | Qt.RightButton
onPressed: mouse => {
if (mouse.button === Qt.RightButton) {
mouse.accepted = true;
}
}
onWheel: wheelEvent => {
const delta = wheelEvent.angleDelta.y;
if (delta !== 0) {
AudioService.cycleAudioOutputDirection(delta < 0);
wheelEvent.accepted = true;
}
}
onClicked: mouse => {
if (mouse.button === Qt.RightButton) {
if (AudioService.sink?.audio) {
SessionData.suppressOSDTemporarily();
AudioService.sink.audio.muted = !AudioService.sink.audio.muted;
}
return;
}
if (devicesExpanded) {
const sinks = AudioService.getAvailableSinks();
if (sinks && sinks.length > 1) {
@@ -113,6 +113,13 @@ Item {
}
}
}
SettingsToggleRow {
text: I18n.tr("Device list scroll volume")
description: I18n.tr("Allow adjusting device volume by scrolling on the right half of items in the device list")
checked: SettingsData.audioDeviceScrollVolumeEnabled
onToggled: checked => SettingsData.set("audioDeviceScrollVolumeEnabled", checked)
}
}
}
}
+35 -2
View File
@@ -58,6 +58,8 @@ Singleton {
return SessionData.deviceMaxVolumes[name] ?? 100;
}
readonly property int wheelVolumeStep: SettingsData.audioWheelScrollAmount
signal micMuteChanged
signal audioOutputCycled(string deviceName, string deviceIcon)
signal deviceAliasChanged(string nodeName, string newAlias)
@@ -156,14 +158,19 @@ Singleton {
return false;
}
function cycleAudioOutput() {
function cycleAudioOutputDirection(forward) {
const sinks = getAvailableSinks();
if (sinks.length < 2)
return null;
const currentName = root.sink?.name ?? "";
const currentIndex = sinks.findIndex(s => s.name === currentName);
const nextIndex = (currentIndex + 1) % sinks.length;
let nextIndex;
if (forward) {
nextIndex = (currentIndex + 1) % sinks.length;
} else {
nextIndex = (currentIndex - 1 + sinks.length) % sinks.length;
}
const nextSink = sinks[nextIndex];
setDefaultSinkByName(nextSink.name);
const name = displayName(nextSink);
@@ -171,6 +178,10 @@ Singleton {
return name;
}
function cycleAudioOutput() {
return cycleAudioOutputDirection(true);
}
function getDeviceAlias(nodeName) {
if (!nodeName)
return null;
@@ -833,6 +844,28 @@ EOFCONFIG
return root.sink.audio.muted ? "Audio muted" : "Audio unmuted";
}
function handleNodeVolumeWheel(node, wheelEvent) {
if (!node?.audio)
return;
SessionData.suppressOSDTemporarily();
const delta = wheelEvent.angleDelta.y;
if (delta === 0)
return;
const current = Math.round(node.audio.volume * 100);
const maxVol = getMaxVolumePercent(node);
const newVolume = delta > 0 ? Math.min(maxVol, current + root.wheelVolumeStep) : Math.max(0, current - root.wheelVolumeStep);
node.audio.muted = false;
node.audio.volume = newVolume / 100;
if (node === sink) {
playVolumeChangeSoundIfEnabled();
}
wheelEvent.accepted = true;
}
function setMicVolume(percentage) {
if (!root.source?.audio) {
return "No audio source available";