Verified Commit f0d81fab authored by ivan tkachenko's avatar ivan tkachenko
Browse files

applet: Support RTL layouts in volume slider

Extracted code into VolumeSlider component of its own, and synced it
from a recent PC3.Slider rewrite which implemented better RTL support.

Removed double-property hack with ignoreValueChange backing flag,
because in modern Qt/QML Slider::moved signal should be used to react
to user-driven changes while preserving the binding on `value`
property. Such binding still needs to be reset after user interactions,
so there's a simple rebinding on release in onPressedChanged handler.
Timer is no longer needed, and slider still works just fine without it.

See also: frameworks/plasma-framework!585
parent 2ef8f410
Pipeline #225616 passed with stage
in 57 seconds
/*
SPDX-FileCopyrightText: 2014-2015 Harald Sitter <sitter@kde.org>
SPDX-FileCopyrightText: 2019 Sefa Eyeoglu <contact@scrumplex.net>
SPDX-FileCopyrightText: 2022 ivan (@ratijas) tkachenko <me@ratijas.tk>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
import QtQuick 2.4
import QtQuick.Layouts 1.0
import QtQuick 2.15
import QtQuick.Layouts 1.15
import org.kde.kquickcontrolsaddons 2.0
import org.kde.plasma.components 3.0 as PC3
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.core 2.1 as PlasmaCore
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.plasma.private.volume 0.1
......@@ -208,17 +209,11 @@ PC3.ItemDelegate {
}
}
PC3.Slider {
VolumeSlider {
id: slider
// Helper properties to allow async slider updates.
// While we are sliding we must not react to value updates
// as otherwise we can easily end up in a loop where value
// changes trigger volume changes trigger value changes.
property int volume: item.model.Volume
property bool ignoreValueChange: true
readonly property bool forceRaiseMaxVolume: (raiseMaximumVolumeCheckbox.checked && (item.type === "sink" || item.type === "source"))
|| volume >= PulseAudio.NormalVolume * 1.01
|| item.model.Volume >= PulseAudio.NormalVolume * 1.01
Layout.fillWidth: true
from: PulseAudio.MinimalVolume
......@@ -226,7 +221,8 @@ PC3.ItemDelegate {
stepSize: to / (to / PulseAudio.NormalVolume * 100.0)
visible: item.model.HasVolume
enabled: item.model.VolumeWritable
opacity: item.model.Muted ? 0.5 : 1
muted: item.model.Muted
volumeObject: item.model.PulseObject
Behavior on to {
NumberAnimation {
duration: PlasmaCore.Units.shortDuration
......@@ -235,73 +231,11 @@ PC3.ItemDelegate {
}
Accessible.name: i18nc("Accessibility data on volume slider", "Adjust volume for %1", defaultButton.text)
// Prevents the groove from showing through the handle
layer.enabled: opacity < 1
background: PlasmaCore.FrameSvgItem {
imagePath: "widgets/slider"
prefix: "groove"
width: parent.availableWidth
height: margins.top + margins.bottom
anchors.centerIn: parent
scale: parent.mirrored ? -1 : 1
PlasmaCore.FrameSvgItem {
imagePath: "widgets/slider"
prefix: "groove-highlight"
width: slider.visualPosition * slider.availableWidth
height: parent.height
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
scale: parent.mirrored ? -1 : 1
}
PlasmaCore.FrameSvgItem {
imagePath: "widgets/slider"
prefix: "groove-highlight"
anchors.left: parent.left
y: (parent.height - height) / 2
width: Math.max(margins.left + margins.right, slider.handle.x * meter.volume)
height: Math.max(margins.top + margins.bottom, parent.height)
opacity: meter.available && (meter.volume > 0 || animation.running)
status: PlasmaCore.FrameSvgItem.Selected
clip: true // prevents a visual glitch, BUG 434927
VolumeMonitor {
id: meter
target: slider.visible && item.model.PulseObject ? item.model.PulseObject : null
}
Behavior on width {
NumberAnimation {
id: animation
duration: PlasmaCore.Units.shortDuration
easing.type: Easing.OutQuad
}
}
}
}
Component.onCompleted: {
ignoreValueChange = false;
}
onVolumeChanged: {
var oldIgnoreValueChange = ignoreValueChange;
ignoreValueChange = true;
value = item.model.Volume;
ignoreValueChange = oldIgnoreValueChange;
}
onValueChanged: {
if (!ignoreValueChange) {
item.model.Volume = value;
item.model.Muted = value === 0;
if (!pressed) {
updateTimer.restart();
}
}
value: item.model.Volume
onMoved: {
item.model.Volume = value;
item.model.Muted = value === 0;
}
onPressedChanged: {
if (!pressed) {
// Make sure to sync the volume once the button was
......@@ -309,19 +243,12 @@ PC3.ItemDelegate {
// Otherwise it might be that the slider is at v10
// whereas PA rejected the volume change and is
// still at v15 (e.g.).
updateTimer.restart();
value = Qt.binding(() => item.model.Volume);
if (type === "sink") {
playFeedback(item.model.Index);
}
}
}
Timer {
id: updateTimer
interval: 200
onTriggered: slider.value = item.model.Volume
}
}
PC3.Label {
id: percentText
......
/*
SPDX-FileCopyrightText: 2014-2015 Harald Sitter <sitter@kde.org>
SPDX-FileCopyrightText: 2019 Sefa Eyeoglu <contact@scrumplex.net>
SPDX-FileCopyrightText: 2022 ivan (@ratijas) tkachenko <me@ratijas.tk>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
import QtQuick 2.15
import QtQuick.Layouts 1.15
import org.kde.kquickcontrolsaddons 2.0
import org.kde.plasma.components 3.0 as PC3
import org.kde.plasma.core 2.1 as PlasmaCore
import org.kde.plasma.private.volume 0.1
// Audio volume slider. Value represents desired volume level in
// device-specific units, while volume property reports current volume level
// normalized to 0..1 range.
PC3.Slider {
id: control
property VolumeObject volumeObject
// When muted, the whole slider will appear slightly faded, but remain
// functional and interactive.
property bool muted: false
// Current (monitored) volume. To be animated. Do not update too fast
// (i.e. faster or close to screen refresh rate), otherwise it won't
// animate smoothly.
property real volume: meter.volume
VolumeMonitor {
id: meter
target: control.visible ? control.volumeObject : null
}
Behavior on volume {
NumberAnimation {
id: animate
duration: PlasmaCore.Units.shortDuration
easing.type: Easing.OutQuad
}
}
// When a maximum volume limit is raised/lower, animate the change.
Behavior on to {
NumberAnimation {
duration: PlasmaCore.Units.shortDuration
easing.type: Easing.InOutQuad
}
}
opacity: muted ? 0.5 : 1
// Prevents the groove from showing through the handle
layer.enabled: opacity < 1
background: PlasmaCore.FrameSvgItem {
imagePath: "widgets/slider"
prefix: "groove"
colorGroup: PlasmaCore.ColorScope.colorGroup
implicitWidth: control.horizontal ? PlasmaCore.Units.gridUnit * 12 : fixedMargins.left + fixedMargins.right
implicitHeight: control.vertical ? PlasmaCore.Units.gridUnit * 12 : fixedMargins.top + fixedMargins.bottom
width: control.horizontal ? Math.max(fixedMargins.left + fixedMargins.right, control.availableWidth) : implicitWidth
height: control.vertical ? Math.max(fixedMargins.top + fixedMargins.bottom, control.availableHeight) : implicitHeight
x: control.leftPadding + (control.horizontal ? 0 : Math.round((control.availableWidth - width) / 2))
y: control.topPadding + (control.vertical ? 0 : Math.round((control.availableHeight - height) / 2))
PlasmaCore.FrameSvgItem {
imagePath: "widgets/slider"
prefix: "groove-highlight"
colorGroup: PlasmaCore.ColorScope.colorGroup
anchors.left: parent.left
anchors.bottom: parent.bottom
LayoutMirroring.enabled: control.mirrored
width: control.horizontal ? Math.max(fixedMargins.left + fixedMargins.right, Math.round(control.position * (control.availableWidth - control.handle.width / 2) + (control.handle.width / 2))) : parent.width
height: control.vertical ? Math.max(fixedMargins.top + fixedMargins.bottom, Math.round(control.position * (control.availableHeight - control.handle.height / 2) + (control.handle.height / 2))) : parent.height
}
PlasmaCore.FrameSvgItem {
imagePath: "widgets/slider"
prefix: "groove-highlight"
status: PlasmaCore.FrameSvgItem.Selected
opacity: meter.available && control.volume > 0
clip: true // prevents a visual glitch, BUG 434927
anchors.left: parent.left
anchors.bottom: parent.bottom
LayoutMirroring.enabled: control.mirrored
width: control.horizontal ? Math.max(fixedMargins.left + fixedMargins.right, Math.round(control.volume * control.position * control.availableWidth)) : parent.width
height: control.vertical ? Math.max(fixedMargins.top + fixedMargins.bottom, Math.round(control.volume * control.position * control.availableHeight)) : parent.height
}
}
}
/*
SPDX-FileCopyrightText: 2014-2015 Harald Sitter <sitter@kde.org>
SPDX-FileCopyrightText: 2022 ivan (@ratijas) tkachenko <me@ratijas.tk>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
......@@ -49,6 +50,7 @@ void Plugin::registerTypes(const char *uri)
qmlRegisterType<QPulseAudio::StreamRestoreModel>(uri, 0, 1, "StreamRestoreModel");
qmlRegisterType<QPulseAudio::ModuleModel>(uri, 0, 1, "ModuleModel");
qmlRegisterType<QPulseAudio::VolumeMonitor>(uri, 0, 01, "VolumeMonitor");
qmlRegisterUncreatableType<QPulseAudio::VolumeObject>(uri, 0, 1, "VolumeObject", QString());
qmlRegisterUncreatableType<QPulseAudio::PulseObject>(uri, 0, 1, "PulseObject", QString());
qmlRegisterUncreatableType<QPulseAudio::Profile>(uri, 0, 1, "Profile", QString());
qmlRegisterUncreatableType<QPulseAudio::Port>(uri, 0, 1, "Port", QString());
......@@ -67,5 +69,4 @@ void Plugin::registerTypes(const char *uri)
qmlRegisterAnonymousType<QPulseAudio::Client>(uri, 1);
qmlRegisterAnonymousType<QPulseAudio::Sink>(uri, 1);
qmlRegisterAnonymousType<QPulseAudio::Source>(uri, 1);
qmlRegisterAnonymousType<QPulseAudio::VolumeObject>(uri, 1);
}
  • Very good - separating out the volume slider definitely makes the applet item an easier read, and is what the KCM does already.

  • There's a separate implementation in KCM? Wait, OH SH~

  • Don't get me started! Because it's the QQC slider, you can't use stepSize without getting ugly tickmarks, so there's no scrolling on it and it doesn't snap to 1% values properly (worked around in master/5.26)

    Also it doesn't show the current audio level like the applet...

    Edited by Oliver Beard
  • There's a separate implementation in KCM? Wait, OH SH~

    No, because it uses desktop style it't not as easy implementable as in the applet

Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment