1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-04-03 20:32:07 -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 var deviceMaxVolumes: ({})
property var hiddenOutputDeviceNames: []
property var hiddenInputDeviceNames: []
Component.onCompleted: {
if (!isGreeterMode) {
@@ -1069,6 +1071,20 @@ Singleton {
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) {
if (!nodeName)
return 100;

View File

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

View File

@@ -18,6 +18,22 @@ Item {
property string editingDeviceType: ""
property string newDeviceName: ""
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() {
const allNodes = Pipewire.nodes.values;
@@ -56,6 +72,8 @@ Item {
}
Component.onCompleted: {
hiddenOutputDeviceNames = SessionData.hiddenOutputDeviceNames ?? [];
hiddenInputDeviceNames = SessionData.hiddenInputDeviceNames ?? [];
updateDeviceList();
}
@@ -132,7 +150,7 @@ Item {
}
Repeater {
model: root.outputDevices
model: root.outputDevices.filter(d => !root.hiddenOutputDeviceNames.includes(d.name))
delegate: Column {
required property var modelData
@@ -142,6 +160,7 @@ Item {
DeviceAliasRow {
deviceNode: modelData
deviceType: "output"
showHideButton: true
onEditRequested: device => {
root.editingDevice = device;
@@ -153,6 +172,10 @@ Item {
onResetRequested: device => {
AudioService.removeDeviceAlias(device.name);
}
onHideRequested: device => {
root.persistHiddenOutputDeviceNames([...root.hiddenOutputDeviceNames, device.name]);
}
}
Item {
@@ -161,7 +184,7 @@ Item {
StyledText {
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.leftMargin: Theme.spacingM + Theme.iconSize + Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
@@ -182,6 +205,8 @@ Item {
maximum: 200
step: 5
showValue: true
wheelEnabled: false
centerMinimum: true
unit: "%"
onSliderValueChanged: newValue => {
SessionData.setDeviceMaxVolume(modelData.name, newValue);
@@ -204,9 +229,87 @@ Item {
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
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
}
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 {
model: root.inputDevices
model: root.inputDevices.filter(d => !root.hiddenInputDeviceNames.includes(d.name))
delegate: DeviceAliasRow {
required property var modelData
deviceNode: modelData
deviceType: "input"
showHideButton: true
onEditRequested: device => {
root.editingDevice = device;
@@ -256,6 +360,10 @@ Item {
onResetRequested: device => {
AudioService.removeDeviceAlias(device.name);
}
onHideRequested: device => {
root.persistHiddenInputDeviceNames([...root.hiddenInputDeviceNames, device.name]);
}
}
}
@@ -265,9 +373,87 @@ Item {
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
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
}
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
property string deviceType: "output"
property bool showHideButton: false
property bool isHidden: false
signal editRequested(var deviceNode)
signal resetRequested(var deviceNode)
signal hideRequested(var deviceNode)
width: parent?.width ?? 0
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 {
id: editButton
buttonSize: 36
@@ -136,6 +155,7 @@ Rectangle {
iconColor: Theme.buttonText
tooltipText: I18n.tr("Set custom name")
anchors.verticalCenter: parent.verticalCenter
visible: !root.isHidden
onClicked: {
root.editRequested(root.deviceNode);
}

View File

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