Warning, /plasma/plasma-mobile/components/mobileshell/qml/volumeosd/ListItemBase.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 * SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 import QtQuick 2.15 0010 import QtQuick.Controls 2.15 as Controls 0011 import QtQuick.Layouts 1.1 0012 import QtQuick.Window 2.2 0013 0014 import org.kde.kirigami 2.20 as Kirigami 0015 import org.kde.ksvg 1.0 as KSvg 0016 import org.kde.kquickcontrolsaddons 2.0 0017 import org.kde.plasma.core as PlasmaCore 0018 import org.kde.plasma.components 3.0 as PlasmaComponents 0019 import org.kde.plasma.private.volume 0.1 0020 0021 import "icon.js" as Icon 0022 0023 // adapted from https://invent.kde.org/plasma/plasma-pa/-/blob/master/applet/contents/ui/ListItemBase.qml 0024 Controls.ItemDelegate { 0025 id: baseItem 0026 0027 property string label 0028 property alias listIcon: clientIcon.source 0029 property string type // sink, source, source-output 0030 0031 onClicked: { 0032 if (selectButton.visible) { 0033 model.PulseObject.default = true; 0034 } 0035 } 0036 0037 contentItem: RowLayout { 0038 id: row 0039 spacing: Kirigami.Units.smallSpacing 0040 0041 PlasmaComponents.RadioButton { 0042 id: selectButton 0043 Layout.alignment: Qt.AlignTop 0044 Layout.topMargin: Math.round(row.height / 2 - implicitHeight - Kirigami.Units.smallSpacing / 2) // align with text 0045 checked: model.PulseObject.hasOwnProperty("default") ? model.PulseObject.default : false 0046 visible: (baseItem.type == "sink" && sinkView.model.count > 1) || (baseItem.type == "source" && sourceView.model.count > 1) 0047 onClicked: model.PulseObject.default = true 0048 } 0049 0050 // application icon 0051 Kirigami.Icon { 0052 id: clientIcon 0053 Layout.alignment: Qt.AlignVCenter 0054 Layout.rightMargin: Kirigami.Units.smallSpacing 0055 Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium 0056 Layout.preferredHeight: Kirigami.Units.iconSizes.smallMedium 0057 visible: type === "sink-input" || type === "source-output" 0058 source: "unknown" 0059 onSourceChanged: { 0060 if (!valid && source != "unknown") { 0061 source = "unknown"; 0062 } 0063 } 0064 } 0065 0066 ColumnLayout { 0067 Layout.alignment: Qt.AlignVCenter 0068 Layout.fillWidth: true 0069 spacing: Kirigami.Units.smallSpacing 0070 0071 RowLayout { 0072 Layout.fillWidth: true 0073 spacing: Kirigami.Units.smallSpacing 0074 Layout.alignment: Qt.AlignBottom 0075 0076 PlasmaComponents.Label { 0077 id: mainLabel 0078 text: baseItem.label 0079 Layout.alignment: Qt.AlignBottom 0080 Layout.fillWidth: true 0081 elide: Text.ElideRight 0082 } 0083 0084 PlasmaComponents.ToolButton { 0085 Layout.alignment: Qt.AlignBottom 0086 Layout.bottomMargin: -Kirigami.Units.smallSpacing 0087 icon.name: "application-menu" 0088 checkable: true 0089 checked: contextMenu.visible && contextMenu.visualParent === this 0090 visible: contextMenu.hasContent 0091 onClicked: { 0092 contextMenu.visualParent = this; 0093 contextMenu.openRelative(); 0094 } 0095 PlasmaComponents.ToolTip { 0096 text: i18n("Show additional options for %1", baseItem.label) 0097 } 0098 0099 ListItemMenu { 0100 id: contextMenu 0101 pulseObject: model.PulseObject 0102 cardModel: paCardModel 0103 itemType: { 0104 switch (baseItem.type) { 0105 case "sink": 0106 return ListItemMenu.Sink; 0107 case "sink-input": 0108 return ListItemMenu.SinkInput; 0109 case "source": 0110 return ListItemMenu.Source; 0111 case "source-output": 0112 return ListItemMenu.SourceOutput; 0113 } 0114 } 0115 sourceModel: { 0116 if (baseItem.type.includes("sink")) { 0117 return sinkView.model; 0118 } else if (baseItem.type.includes("source")) { 0119 return sourceView.model; 0120 } 0121 } 0122 onVisibleChanged: window.suppressActiveClose = visible 0123 } 0124 } 0125 } 0126 0127 RowLayout { 0128 Layout.fillWidth: true 0129 spacing: Kirigami.Units.smallSpacing 0130 0131 // this slider was effectively copied from the source (linked at the top of the file) 0132 PlasmaComponents.Slider { 0133 id: slider 0134 Layout.fillWidth: true 0135 Layout.alignment: Qt.AlignTop 0136 0137 // Helper properties to allow async slider updates. 0138 // While we are sliding we must not react to value updates 0139 // as otherwise we can easily end up in a loop where value 0140 // changes trigger volume changes trigger value changes. 0141 property int volume: Volume 0142 property bool ignoreValueChange: true 0143 readonly property bool forceRaiseMaxVolume: volume >= PulseAudio.NormalVolume * 1.01 0144 0145 from: PulseAudio.MinimalVolume 0146 to: PulseAudio.NormalVolume 0147 stepSize: to / (to / PulseAudio.NormalVolume * 100.0) 0148 visible: HasVolume 0149 enabled: VolumeWritable 0150 opacity: Muted ? 0.5 : 1 0151 0152 Accessible.name: i18nc("Accessibility data on volume slider", "Adjust volume for %1", baseItem.label) 0153 0154 background: KSvg.FrameSvgItem { 0155 imagePath: "widgets/slider" 0156 prefix: "groove" 0157 width: parent.availableWidth 0158 height: margins.top + margins.bottom 0159 anchors.centerIn: parent 0160 scale: parent.mirrored ? -1 : 1 0161 0162 KSvg.FrameSvgItem { 0163 imagePath: "widgets/slider" 0164 prefix: "groove-highlight" 0165 anchors.left: parent.left 0166 y: (parent.height - height) / 2 0167 width: Math.max(margins.left + margins.right, slider.handle.x * meter.volume) 0168 height: Math.max(margins.top + margins.bottom, parent.height) 0169 opacity: meter.available && (meter.volume > 0 || animation.running) 0170 VolumeMonitor { 0171 id: meter 0172 target: parent.visible ? model.PulseObject : null 0173 } 0174 Behavior on width { 0175 NumberAnimation { 0176 id: animation 0177 duration: Kirigami.Units.shortDuration 0178 easing.type: Easing.OutQuad 0179 } 0180 } 0181 } 0182 } 0183 0184 Component.onCompleted: { 0185 ignoreValueChange = false; 0186 } 0187 0188 onVolumeChanged: { 0189 var oldIgnoreValueChange = ignoreValueChange; 0190 ignoreValueChange = true; 0191 value = Volume; 0192 ignoreValueChange = oldIgnoreValueChange; 0193 } 0194 0195 onValueChanged: { 0196 if (!ignoreValueChange) { 0197 Volume = value; 0198 Muted = value == 0; 0199 0200 if (!pressed) { 0201 updateTimer.restart(); 0202 } 0203 } 0204 } 0205 0206 onPressedChanged: { 0207 if (!pressed) { 0208 // Make sure to sync the volume once the button was 0209 // released. 0210 // Otherwise it might be that the slider is at v10 0211 // whereas PA rejected the volume change and is 0212 // still at v15 (e.g.). 0213 updateTimer.restart(); 0214 } 0215 } 0216 0217 Timer { 0218 id: updateTimer 0219 interval: 200 0220 onTriggered: slider.value = Volume 0221 } 0222 } 0223 PlasmaComponents.Label { 0224 id: percentText 0225 readonly property real value: model.PulseObject.volume > slider.to ? model.PulseObject.volume : slider.value 0226 readonly property real displayValue: Math.round(value / PulseAudio.NormalVolume * 100.0) 0227 Layout.alignment: Qt.AlignHCenter 0228 Layout.minimumWidth: percentMetrics.advanceWidth 0229 horizontalAlignment: Qt.AlignRight 0230 text: i18nc("volume percentage", "%1%", displayValue) 0231 color: { 0232 if (displayValue <= 100) { 0233 return Kirigami.Theme.textColor 0234 } else if (displayValue > 100 && displayValue <= 125) { 0235 return Kirigami.Theme.neutralTextColor 0236 } else { 0237 return Kirigami.Theme.negativeTextColor 0238 } 0239 } 0240 } 0241 0242 TextMetrics { 0243 id: percentMetrics 0244 font: percentText.font 0245 text: i18nc("only used for sizing, should be widest possible string", "100%") 0246 } 0247 } 0248 } 0249 } 0250 0251 function setVolumeByPercent(targetPercent) { 0252 model.PulseObject.volume = Math.round(PulseAudio.NormalVolume * (targetPercent/100)); 0253 } 0254 }