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:
@@ -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;
|
||||||
|
|||||||
@@ -75,7 +75,9 @@ var SPEC = {
|
|||||||
|
|
||||||
vpnLastConnected: { def: "" },
|
vpnLastConnected: { def: "" },
|
||||||
|
|
||||||
deviceMaxVolumes: { def: {} }
|
deviceMaxVolumes: { def: {} },
|
||||||
|
hiddenOutputDeviceNames: { def: [] },
|
||||||
|
hiddenInputDeviceNames: { def: [] }
|
||||||
};
|
};
|
||||||
|
|
||||||
function getValidKeys() {
|
function getValidKeys() {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user