1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-04-14 17:52:10 -04:00

audio: New ability to hide input/output devices

- Updated slider presets
- Disabled mouse wheel scrolling on list scroll
This commit is contained in:
purian23
2026-02-17 00:54:32 -05:00
parent f280cd9d3b
commit ef19568dd7
5 changed files with 239 additions and 8 deletions

View File

@@ -123,6 +123,8 @@ Singleton {
property string vpnLastConnected: "" property string vpnLastConnected: ""
property var deviceMaxVolumes: ({}) property var deviceMaxVolumes: ({})
property var hiddenOutputDeviceNames: []
property var hiddenInputDeviceNames: []
Component.onCompleted: { Component.onCompleted: {
if (!isGreeterMode) { if (!isGreeterMode) {
@@ -1069,6 +1071,20 @@ Singleton {
saveSettings(); saveSettings();
} }
function setHiddenOutputDeviceNames(deviceNames) {
if (!Array.isArray(deviceNames))
return;
hiddenOutputDeviceNames = deviceNames;
saveSettings();
}
function setHiddenInputDeviceNames(deviceNames) {
if (!Array.isArray(deviceNames))
return;
hiddenInputDeviceNames = deviceNames;
saveSettings();
}
function getDeviceMaxVolume(nodeName) { function getDeviceMaxVolume(nodeName) {
if (!nodeName) if (!nodeName)
return 100; return 100;

View File

@@ -75,7 +75,9 @@ var SPEC = {
vpnLastConnected: { def: "" }, vpnLastConnected: { def: "" },
deviceMaxVolumes: { def: {} } deviceMaxVolumes: { def: {} },
hiddenOutputDeviceNames: { def: [] },
hiddenInputDeviceNames: { def: [] }
}; };
function getValidKeys() { function getValidKeys() {

View File

@@ -18,6 +18,22 @@ Item {
property string editingDeviceType: "" property string editingDeviceType: ""
property string newDeviceName: "" property string newDeviceName: ""
property bool isReloadingAudio: false property bool isReloadingAudio: false
property var hiddenOutputDeviceNames: SessionData.hiddenOutputDeviceNames ?? []
property var hiddenInputDeviceNames: SessionData.hiddenInputDeviceNames ?? []
property bool showHiddenOutputDevices: false
property bool showHiddenInputDevices: false
function persistHiddenOutputDeviceNames(deviceNames) {
const uniqueNames = [...new Set(deviceNames)];
hiddenOutputDeviceNames = uniqueNames;
SessionData.setHiddenOutputDeviceNames(uniqueNames);
}
function persistHiddenInputDeviceNames(deviceNames) {
const uniqueNames = [...new Set(deviceNames)];
hiddenInputDeviceNames = uniqueNames;
SessionData.setHiddenInputDeviceNames(uniqueNames);
}
function updateDeviceList() { function updateDeviceList() {
const allNodes = Pipewire.nodes.values; const allNodes = Pipewire.nodes.values;
@@ -56,6 +72,8 @@ Item {
} }
Component.onCompleted: { Component.onCompleted: {
hiddenOutputDeviceNames = SessionData.hiddenOutputDeviceNames ?? [];
hiddenInputDeviceNames = SessionData.hiddenInputDeviceNames ?? [];
updateDeviceList(); updateDeviceList();
} }
@@ -132,7 +150,7 @@ Item {
} }
Repeater { Repeater {
model: root.outputDevices model: root.outputDevices.filter(d => !root.hiddenOutputDeviceNames.includes(d.name))
delegate: Column { delegate: Column {
required property var modelData required property var modelData
@@ -142,6 +160,7 @@ Item {
DeviceAliasRow { DeviceAliasRow {
deviceNode: modelData deviceNode: modelData
deviceType: "output" deviceType: "output"
showHideButton: true
onEditRequested: device => { onEditRequested: device => {
root.editingDevice = device; root.editingDevice = device;
@@ -153,6 +172,10 @@ Item {
onResetRequested: device => { onResetRequested: device => {
AudioService.removeDeviceAlias(device.name); AudioService.removeDeviceAlias(device.name);
} }
onHideRequested: device => {
root.persistHiddenOutputDeviceNames([...root.hiddenOutputDeviceNames, device.name]);
}
} }
Item { Item {
@@ -161,7 +184,7 @@ Item {
StyledText { StyledText {
id: maxVolLabel id: maxVolLabel
text: I18n.tr("Max Volume", "Audio settings: maximum volume limit per device") text: I18n.tr("Max Volume", "Audio settings: maximum volume limit per device") + " · " + maxVolSlider.value + "%"
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: Theme.spacingM + Theme.iconSize + Theme.spacingM anchors.leftMargin: Theme.spacingM + Theme.iconSize + Theme.spacingM
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
@@ -182,6 +205,8 @@ Item {
maximum: 200 maximum: 200
step: 5 step: 5
showValue: true showValue: true
wheelEnabled: false
centerMinimum: true
unit: "%" unit: "%"
onSliderValueChanged: newValue => { onSliderValueChanged: newValue => {
SessionData.setDeviceMaxVolume(modelData.name, newValue); SessionData.setDeviceMaxVolume(modelData.name, newValue);
@@ -204,9 +229,87 @@ Item {
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText color: Theme.surfaceVariantText
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
visible: root.outputDevices.length === 0 visible: root.outputDevices.filter(d => !root.hiddenOutputDeviceNames.includes(d.name)).length === 0 && root.hiddenOutputDeviceNames.length === 0
topPadding: Theme.spacingM topPadding: Theme.spacingM
} }
Column {
width: parent.width
spacing: 0
visible: root.hiddenOutputDeviceNames.length > 0
Rectangle {
width: parent.width
height: 1
color: Theme.outline
opacity: 0.15
}
Item {
width: parent.width
height: 36
Row {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingS
DankIcon {
name: "visibility_off"
size: Theme.iconSize - 4
color: Theme.surfaceVariantText
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: I18n.tr("Hidden (%1)").arg(root.hiddenOutputDeviceNames.length)
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
anchors.verticalCenter: parent.verticalCenter
}
}
DankIcon {
name: root.showHiddenOutputDevices ? "expand_less" : "expand_more"
size: Theme.iconSize - 4
color: Theme.surfaceVariantText
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: root.showHiddenOutputDevices = !root.showHiddenOutputDevices
}
}
Column {
width: parent.width
spacing: 0
visible: root.showHiddenOutputDevices
Repeater {
model: root.outputDevices.filter(d => root.hiddenOutputDeviceNames.includes(d.name))
delegate: DeviceAliasRow {
required property var modelData
deviceNode: modelData
deviceType: "output"
isHidden: true
showHideButton: true
onHideRequested: device => {
root.persistHiddenOutputDeviceNames(root.hiddenOutputDeviceNames.filter(n => n !== device.name));
}
onResetRequested: device => {
AudioService.removeDeviceAlias(device.name);
}
}
}
}
}
} }
} }
@@ -238,13 +341,14 @@ Item {
} }
Repeater { Repeater {
model: root.inputDevices model: root.inputDevices.filter(d => !root.hiddenInputDeviceNames.includes(d.name))
delegate: DeviceAliasRow { delegate: DeviceAliasRow {
required property var modelData required property var modelData
deviceNode: modelData deviceNode: modelData
deviceType: "input" deviceType: "input"
showHideButton: true
onEditRequested: device => { onEditRequested: device => {
root.editingDevice = device; root.editingDevice = device;
@@ -256,6 +360,10 @@ Item {
onResetRequested: device => { onResetRequested: device => {
AudioService.removeDeviceAlias(device.name); AudioService.removeDeviceAlias(device.name);
} }
onHideRequested: device => {
root.persistHiddenInputDeviceNames([...root.hiddenInputDeviceNames, device.name]);
}
} }
} }
@@ -265,9 +373,87 @@ Item {
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText color: Theme.surfaceVariantText
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
visible: root.inputDevices.length === 0 visible: root.inputDevices.filter(d => !root.hiddenInputDeviceNames.includes(d.name)).length === 0 && root.hiddenInputDeviceNames.length === 0
topPadding: Theme.spacingM topPadding: Theme.spacingM
} }
Column {
width: parent.width
spacing: 0
visible: root.hiddenInputDeviceNames.length > 0
Rectangle {
width: parent.width
height: 1
color: Theme.outline
opacity: 0.15
}
Item {
width: parent.width
height: 36
Row {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingS
DankIcon {
name: "visibility_off"
size: Theme.iconSize - 4
color: Theme.surfaceVariantText
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: I18n.tr("Hidden (%1)").arg(root.hiddenInputDeviceNames.length)
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
anchors.verticalCenter: parent.verticalCenter
}
}
DankIcon {
name: root.showHiddenInputDevices ? "expand_less" : "expand_more"
size: Theme.iconSize - 4
color: Theme.surfaceVariantText
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: root.showHiddenInputDevices = !root.showHiddenInputDevices
}
}
Column {
width: parent.width
spacing: 0
visible: root.showHiddenInputDevices
Repeater {
model: root.inputDevices.filter(d => root.hiddenInputDeviceNames.includes(d.name))
delegate: DeviceAliasRow {
required property var modelData
deviceNode: modelData
deviceType: "input"
isHidden: true
showHideButton: true
onHideRequested: device => {
root.persistHiddenInputDeviceNames(root.hiddenInputDeviceNames.filter(n => n !== device.name));
}
onResetRequested: device => {
AudioService.removeDeviceAlias(device.name);
}
}
}
}
}
} }
} }
} }

View File

@@ -14,8 +14,12 @@ Rectangle {
required property var deviceNode required property var deviceNode
property string deviceType: "output" property string deviceType: "output"
property bool showHideButton: false
property bool isHidden: false
signal editRequested(var deviceNode) signal editRequested(var deviceNode)
signal resetRequested(var deviceNode) signal resetRequested(var deviceNode)
signal hideRequested(var deviceNode)
width: parent?.width ?? 0 width: parent?.width ?? 0
height: deviceRowContent.height + Theme.spacingM * 2 height: deviceRowContent.height + Theme.spacingM * 2
@@ -127,6 +131,21 @@ Rectangle {
} }
} }
DankActionButton {
id: hideButton
visible: root.showHideButton
buttonSize: 36
iconName: root.isHidden ? "visibility" : "visibility_off"
iconSize: 20
backgroundColor: Theme.surfaceContainerHigh
iconColor: root.isHidden ? Theme.primary : Theme.surfaceVariantText
tooltipText: root.isHidden ? I18n.tr("Show device") : I18n.tr("Hide device")
anchors.verticalCenter: parent.verticalCenter
onClicked: {
root.hideRequested(root.deviceNode);
}
}
DankActionButton { DankActionButton {
id: editButton id: editButton
buttonSize: 36 buttonSize: 36
@@ -136,6 +155,7 @@ Rectangle {
iconColor: Theme.buttonText iconColor: Theme.buttonText
tooltipText: I18n.tr("Set custom name") tooltipText: I18n.tr("Set custom name")
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
visible: !root.isHidden
onClicked: { onClicked: {
root.editRequested(root.deviceNode); root.editRequested(root.deviceNode);
} }

View File

@@ -16,6 +16,7 @@ Item {
property bool showValue: true property bool showValue: true
property bool isDragging: false property bool isDragging: false
property bool wheelEnabled: true property bool wheelEnabled: true
property bool centerMinimum: false
property real valueOverride: -1 property real valueOverride: -1
property bool alwaysShowValue: false property bool alwaysShowValue: false
readonly property bool containsMouse: sliderMouseArea.containsMouse readonly property bool containsMouse: sliderMouseArea.containsMouse
@@ -30,6 +31,8 @@ Item {
function updateValueFromPosition(x) { function updateValueFromPosition(x) {
let ratio = Math.max(0, Math.min(1, (x - sliderHandle.width / 2) / (sliderTrack.width - sliderHandle.width))); let ratio = Math.max(0, Math.min(1, (x - sliderHandle.width / 2) / (sliderTrack.width - sliderHandle.width)));
if (centerMinimum)
ratio = Math.max(0, (ratio - 0.5) * 2);
let rawValue = minimum + ratio * (maximum - minimum); let rawValue = minimum + ratio * (maximum - minimum);
let newValue = step > 1 ? Math.round(rawValue / step) * step : Math.round(rawValue); let newValue = step > 1 ? Math.round(rawValue / step) * step : Math.round(rawValue);
newValue = Math.max(minimum, Math.min(maximum, newValue)); newValue = Math.max(minimum, Math.min(maximum, newValue));
@@ -70,7 +73,9 @@ Item {
height: parent.height height: parent.height
radius: Theme.cornerRadius radius: Theme.cornerRadius
width: { width: {
const ratio = (slider.value - slider.minimum) / (slider.maximum - slider.minimum); const range = slider.maximum - slider.minimum;
const rawRatio = range === 0 ? 0 : (slider.value - slider.minimum) / range;
const ratio = slider.centerMinimum ? (0.5 + rawRatio * 0.5) : rawRatio;
const travel = sliderTrack.width - sliderHandle.width; const travel = sliderTrack.width - sliderHandle.width;
const center = (travel * ratio) + sliderHandle.width / 2; const center = (travel * ratio) + sliderHandle.width / 2;
return Math.max(0, Math.min(sliderTrack.width, center)); return Math.max(0, Math.min(sliderTrack.width, center));
@@ -87,7 +92,9 @@ Item {
height: 24 height: 24
radius: Theme.cornerRadius radius: Theme.cornerRadius
x: { x: {
const ratio = (slider.value - slider.minimum) / (slider.maximum - slider.minimum); const range = slider.maximum - slider.minimum;
const rawRatio = range === 0 ? 0 : (slider.value - slider.minimum) / range;
const ratio = slider.centerMinimum ? (0.5 + rawRatio * 0.5) : rawRatio;
const travel = sliderTrack.width - width; const travel = sliderTrack.width - width;
return Math.max(0, Math.min(travel, travel * ratio)); return Math.max(0, Math.min(travel, travel * ratio));
} }