Warning, /plasma/plasma-pa/src/kcm/ui/DeviceListItem.qml is written in an unsupported language. File is not indexed.

0001 /*
0002     SPDX-FileCopyrightText: 2014-2015 Harald Sitter <sitter@kde.org>
0003     SPDX-FileCopyrightText: 2019 Sefa Eyeoglu <contact@scrumplex.net>
0004 
0005     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006 */
0007 
0008 import QtQuick
0009 import QtQuick.Controls
0010 import QtQuick.Layouts
0011 
0012 import org.kde.kirigami as Kirigami
0013 import org.kde.kitemmodels as KItemModels
0014 import org.kde.plasma.private.volume
0015 
0016 ColumnLayout {
0017     id: delegate
0018     spacing: Kirigami.Units.smallSpacing
0019     width: parent.width
0020 
0021     property bool isPlayback: type.substring(0, 4) == "sink"
0022 
0023     readonly property var currentPort: Ports[ActivePortIndex]
0024 
0025     opacity: (Ports.length === 1 && Ports[0].availability == Port.Unavailable) ? 0.5 : 1
0026 
0027     property var pulseObject: model.PulseObject
0028 
0029     property alias portVisible: portBox.visible
0030     property bool comboBoxLabelsVisible: false
0031 
0032     RowLayout {
0033         spacing: Kirigami.Units.smallSpacing
0034         Layout.fillWidth: true
0035         // To keep the top row the same height as the row below, and stop things moving too much
0036         // as the KCM loads (and the port/profile ComboBoxes may appear)
0037         Layout.minimumHeight: balanceButton.implicitHeight
0038 
0039         RadioButton {
0040             id: defaultButton
0041             Layout.fillWidth: true
0042             Layout.leftMargin: LayoutMirroring.enabled ? 0 : Math.round((muteButton.width - defaultButton.indicator.width) / 2)
0043             Layout.rightMargin: LayoutMirroring.enabled ? Math.round((muteButton.width - defaultButton.indicator.width) / 2) : 0
0044             spacing: Kirigami.Units.smallSpacing + Math.round((muteButton.width - defaultButton.indicator.width) / 2)
0045             checked: Default
0046             visible: delegate.ListView.view.count > 1
0047             onClicked: Default = true
0048             text: !currentPort ? Description : i18ndc("kcm_pulseaudio", "label of device items", "%1 (%2)", currentPort.description, Description)
0049 
0050             ToolTip {
0051                 text: parent.text
0052                 visible: parent.visible && parent.contentItem.truncated && labelMouseArea.containsMouse
0053             }
0054             MouseArea {
0055                 id: labelMouseArea
0056                 anchors.fill: parent.contentItem
0057                 hoverEnabled: true
0058                 acceptedButtons: Qt.NoButton
0059                 z: defaultButton.z + 1
0060             }
0061         }
0062 
0063         Label {
0064             id: soloLabel
0065             Layout.fillWidth: true
0066             text: currentPort.description
0067             visible: delegate.ListView.view.count === 1
0068             elide: Text.ElideRight
0069 
0070             ToolTip {
0071                 text: defaultButton.text
0072                 visible: parent.visible && soloLabelMouseArea.containsMouse
0073             }
0074             MouseArea {
0075                 id: soloLabelMouseArea
0076                 anchors.fill: parent
0077                 hoverEnabled: true
0078             }
0079         }
0080 
0081         Item {
0082             Layout.leftMargin: Kirigami.Units.largeSpacing
0083             Layout.fillWidth: true
0084         }
0085 
0086         Label {
0087             id: portLabel
0088             visible: comboBoxLabelsVisible && portBox.visible
0089             text: i18nd("kcm_pulseaudio", "Port:")
0090             Layout.leftMargin: Kirigami.Units.largeSpacing
0091         }
0092 
0093         ComboBox {
0094             id: portBox
0095 
0096             readonly property var ports: Ports
0097 
0098             Layout.preferredWidth: Kirigami.Units.gridUnit * 10
0099             visible: portBox.count > 1
0100 
0101             onModelChanged: currentIndex = ActivePortIndex
0102             currentIndex: ActivePortIndex
0103             onActivated: ActivePortIndex = index
0104 
0105             onPortsChanged: {
0106                 var items = [];
0107                 for (var i = 0; i < ports.length; ++i) {
0108                     var port = ports[i];
0109                     var text;
0110                     if (port.availability == Port.Unavailable) {
0111                         if (port.name == "analog-output-speaker" || port.name == "analog-input-microphone-internal") {
0112                             text = i18ndc("kcm_pulseaudio", "Port is unavailable", "%1 (unavailable)", port.description);
0113                         } else {
0114                             text = i18ndc("kcm_pulseaudio", "Port is unplugged", "%1 (unplugged)", port.description);
0115                         }
0116                     } else {
0117                         text = port.description;
0118                     }
0119                     items.push(text);
0120                 }
0121                 model = items;
0122             }
0123 
0124             // Ensure the popup is sufficiently wide to list *full* device names
0125             // and is correctly positioned to not go off the page
0126             popup.width: Math.max(implicitWidth, width)
0127             popup.leftMargin: delegate.ListView.view.Layout.leftMargin
0128             popup.rightMargin: delegate.ListView.view.Layout.rightMargin
0129             popup.x: mirrored ? 0 : width - popup.width
0130         }
0131 
0132         Label {
0133             id: profileLabel
0134             visible: comboBoxLabelsVisible && profileBox.visible
0135             text: i18ndc("kcm_pulseaudio", "@label", "Profile:")
0136             Layout.leftMargin: Kirigami.Units.largeSpacing
0137         }
0138 
0139         ComboBox {
0140             id: profileBox
0141 
0142             readonly property var card: paCardModel.data(paCardModel.indexOfCardNumber(CardIndex), paCardModel.KItemModels.KRoleNames.role("PulseObject"))
0143 
0144             Layout.preferredWidth: Kirigami.Units.gridUnit * 12
0145             visible: profileBox.count > 1
0146             textRole: "description"
0147 
0148             model: card ? card.profiles.filter(profile => profile.availability === Profile.Available) : []
0149             currentIndex: card ? model.indexOf(card.profiles[card.activeProfileIndex]) : -1
0150 
0151             onActivated: index => card.activeProfileIndex = card.profiles.indexOf(model[index])
0152 
0153             // Ensure the popup is sufficiently wide to list *full* device names
0154             // and is correctly positioned to not go off the page
0155             popup.width: Math.max(implicitWidth, width)
0156             popup.leftMargin: delegate.ListView.view.Layout.leftMargin
0157             popup.rightMargin: delegate.ListView.view.Layout.rightMargin
0158             popup.x: mirrored ? 0 : width - popup.width
0159         }
0160     }
0161 
0162     RowLayout {
0163         spacing: Kirigami.Units.smallSpacing
0164 
0165         MuteButton {
0166             id: muteButton
0167             Layout.alignment: Qt.AlignTop | Qt.AlignVCenter
0168             muted: Muted
0169             onCheckedChanged: Muted = checked
0170             toolTipText: !currentPort ? Description : currentPort.description
0171         }
0172 
0173         GridLayout {
0174             rows: Math.max(1, channelSliderRepeater.count)
0175             flow: GridLayout.TopToBottom
0176 
0177             VolumeSlider {
0178                 id: volumeSlider
0179                 Layout.columnSpan: 2
0180                 Layout.alignment: Qt.AlignVCenter
0181                 visible: !balanceButton.checked
0182             }
0183 
0184             Repeater {
0185                 model: balanceButton.checked ? Channels : null
0186 
0187                 Label {
0188                     Layout.alignment: Qt.AlignTop | Qt.AlignRight
0189                     text: i18ndc("kcm_pulseaudio", "Placeholder is channel name", "%1:", modelData)
0190                 }
0191             }
0192 
0193             Repeater {
0194                 id: channelSliderRepeater
0195                 model: balanceButton.checked ? RawChannels : null
0196 
0197                 VolumeSlider {
0198                     Layout.fillWidth: true
0199 
0200                     value: ChannelVolumes[index]
0201 
0202                     channel: index
0203                 }
0204             }
0205         }
0206 
0207         Item {
0208             Layout.leftMargin: Kirigami.Units.smallSpacing
0209         }
0210 
0211         Button {
0212             Layout.alignment: Qt.AlignTop
0213             id: testButton
0214             text: i18ndc("kcm_pulseaudio", "Perform an audio test of the device", "Test")
0215             icon.name: "audio-speakers-symbolic"
0216             visible: isPlayback
0217             onClicked: testOverlay.testSink(index);
0218         }
0219 
0220         Button {
0221             Layout.alignment: Qt.AlignTop
0222             id: balanceButton
0223             text: i18ndc("kcm_pulseaudio", "Audio balance (e.g. control left/right volume individually", "Balance")
0224             icon.name: "view-media-equalizer"
0225             checkable: true
0226 
0227             onClicked: {
0228                 if (checked) {
0229                     return;
0230                 }
0231 
0232                 // When unifying channels again, set all of them to the same value
0233                 // as to not keep an odd difference when adjusting them in unison
0234                 let volumes = ChannelVolumes;
0235                 for (let i = 0, count = RawChannels.length; i < count; ++i) {
0236                     volumes[i] = Volume;
0237                 }
0238                 // NOTE "ChannelVolumes = volumes" does not work as the
0239                 // AbstractModel does not have the magic JS Array to Qt List
0240                 // conversion stuff and only sees our Array as a QJSValue
0241                 delegate.pulseObject.channelVolumes = volumes;
0242             }
0243 
0244             // Default to individual mode when a channel has a different volume
0245             Component.onCompleted: {
0246                 checked = ChannelVolumes.some((volume) => {
0247                     return volume !== ChannelVolumes[0];
0248                 });
0249             }
0250         }
0251     }
0252 
0253     Kirigami.Separator {
0254         visible: (delegate.ListView.view.count != 0) && (delegate.ListView.view.count != (index + 1))
0255         Layout.topMargin: Kirigami.Units.smallSpacing
0256         Layout.fillWidth: true
0257     }
0258 }