diff --git a/applet/contents/code/icon.js b/applet/contents/code/icon.js --- a/applet/contents/code/icon.js +++ b/applet/contents/code/icon.js @@ -19,9 +19,8 @@ */ function name(volume, muted) { - // FIXME: hardcoded max value var icon = null; - var percent = volume / 65536; + var percent = volume / maxVolumeValue; if (percent <= 0.0 || muted) { icon = "audio-volume-muted"; } else if (percent <= 0.25) { diff --git a/applet/contents/ui/ListItemBase.qml b/applet/contents/ui/ListItemBase.qml --- a/applet/contents/ui/ListItemBase.qml +++ b/applet/contents/ui/ListItemBase.qml @@ -19,15 +19,16 @@ */ import QtQuick 2.0 - import QtQuick.Controls 1.0 import QtQuick.Layouts 1.0 import org.kde.kquickcontrolsaddons 2.0 import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.extras 2.0 as PlasmaExtras +import org.kde.plasma.private.volume 0.1 + PlasmaComponents.ListItem { id: item @@ -125,10 +126,9 @@ property bool ignoreValueChange: false Layout.fillWidth: true - minimumValue: 0 - // FIXME: I do wonder if exposing max through the model would be useful at all - maximumValue: 65536 - stepSize: maximumValue / 100 + minimumValue: PulseAudio.MinimalVolume + maximumValue: maxVolumeValue + stepSize: maximumValue / maxVolumePercent visible: PulseObject.hasVolume enabled: { if (typeof PulseObject.volumeWritable === 'undefined') { @@ -172,10 +172,11 @@ } PlasmaComponents.Label { id: percentText + readonly property real value: PulseObject.volume > slider.maximumValue ? PulseObject.volume : slider.value Layout.alignment: Qt.AlignHCenter Layout.minimumWidth: referenceText.width horizontalAlignment: Qt.AlignRight - text: i18nc("volume percentage", "%1%", Math.floor(slider.value / slider.maximumValue * 100.0)) + text: i18nc("volume percentage", "%1%", Math.round(value / slider.maximumValue * 100.0)) } } } diff --git a/applet/contents/ui/VolumeIcon.qml b/applet/contents/ui/VolumeIcon.qml --- a/applet/contents/ui/VolumeIcon.qml +++ b/applet/contents/ui/VolumeIcon.qml @@ -23,6 +23,8 @@ import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.private.volume 0.1 + import "../code/icon.js" as Icon PlasmaCore.SvgItem { diff --git a/applet/contents/ui/main.qml b/applet/contents/ui/main.qml --- a/applet/contents/ui/main.qml +++ b/applet/contents/ui/main.qml @@ -33,7 +33,9 @@ Item { id: main - property int volumeStep: 65536 / 15 + property int maxVolumePercent: 100 + property int maxVolumeValue: Math.round(maxVolumePercent * PulseAudio.NormalVolume / 100.0) + property int volumeStep: PulseAudio.NormalVolume / 15 property string displayName: i18n("Audio Volume") Layout.minimumHeight: units.gridUnit * 12 @@ -47,33 +49,36 @@ Plasmoid.toolTipMainText: displayName Plasmoid.toolTipSubText: sinkModel.defaultSink ? i18n("Volume at %1%\n%2", volumePercent(sinkModel.defaultSink.volume), sinkModel.defaultSink.description) : "" - function bound(value, min, max) { - return Math.max(min, Math.min(value, max)); + function boundVolume(volume) { + return Math.max(PulseAudio.MinimalVolume, Math.min(volume, maxVolumeValue)); } - function volumePercent(volume) { - return Math.round(100 * volume / 65536); + function volumePercent(volume, max) { + if (!max) { + max = PulseAudio.NormalVolume; + } + return Math.round(volume / max * 100.0); } function increaseVolume(showOsd) { if (!sinkModel.defaultSink) { return; } - var volume = bound(sinkModel.defaultSink.volume + volumeStep, 0, 65536); + var volume = boundVolume(sinkModel.defaultSink.volume + volumeStep); sinkModel.defaultSink.volume = volume; if (showOsd) { - osd.show(volumePercent(volume)); + osd.show(volumePercent(volume, maxVolumeValue)); } } function decreaseVolume(showOsd) { if (!sinkModel.defaultSink) { return; } - var volume = bound(sinkModel.defaultSink.volume - volumeStep, 0, 65536); + var volume = boundVolume(sinkModel.defaultSink.volume - volumeStep); sinkModel.defaultSink.volume = volume; if (showOsd) { - osd.show(volumePercent(volume)); + osd.show(volumePercent(volume, maxVolumeValue)); } } @@ -84,7 +89,7 @@ var toMute = !sinkModel.defaultSink.muted; sinkModel.defaultSink.muted = toMute; if (showOsd) { - osd.show(toMute ? 0 : volumePercent(sinkModel.defaultSink.volume)); + osd.show(toMute ? 0 : volumePercent(sinkModel.defaultSink.volume, maxVolumeValue)); } } diff --git a/src/context.h b/src/context.h --- a/src/context.h +++ b/src/context.h @@ -47,6 +47,10 @@ static Context *instance(); + static const qint64 NormalVolume = PA_VOLUME_NORM; + static const qint64 MinimalVolume = 0; + static const qint64 MaximalVolume = (PA_VOLUME_NORM / 100.0) * 150; + void ref(); void unref(); @@ -79,8 +83,7 @@ void setGenericVolume(quint32 index, int channel, qint64 newVolume, pa_cvolume cVolume, PAFunction pa_set_volume) { - // TODO: overdrive - newVolume = qBound(0, newVolume, 65536); + newVolume = qBound(0, newVolume, PA_VOLUME_MAX); pa_cvolume newCVolume = cVolume; if (channel == -1) { // -1 all channels for (int i = 0; i < newCVolume.channels; ++i) { diff --git a/src/kcm/package/contents/ui/VolumeSlider.qml b/src/kcm/package/contents/ui/VolumeSlider.qml --- a/src/kcm/package/contents/ui/VolumeSlider.qml +++ b/src/kcm/package/contents/ui/VolumeSlider.qml @@ -22,6 +22,8 @@ import QtQuick.Layouts 1.0 import QtQuick.Controls 1.0 +import org.kde.plasma.private.volume 0.1 + RowLayout { // VolumeIcon { // Layout.maximumHeight: slider.height * 0.75 @@ -35,6 +37,8 @@ // } // } + Layout.bottomMargin: hundredPercentLabel.height + Slider { id: slider @@ -46,10 +50,8 @@ property bool ignoreValueChange: false Layout.fillWidth: true - minimumValue: 0 - // FIXME: I do wonder if exposing max through the model would be useful at all - maximumValue: 65536 - stepSize: maximumValue / 100 + minimumValue: PulseAudio.MinimalVolume + maximumValue: PulseAudio.MaximalVolume visible: PulseObject.hasVolume enabled: PulseObject.volumeWritable && !PulseObject.muted @@ -80,6 +82,16 @@ } } + Label { + id: hundredPercentLabel + z: slider.z - 1 + x: (slider.width / slider.maximumValue) * PulseAudio.NormalVolume - width / 2 + y: slider.height / 1.2 + opacity: 0.5 + font.pixelSize: slider.height / 2.2 + text: i18n("100%") + } + Timer { id: updateTimer interval: 200 @@ -89,10 +101,11 @@ Label { id: percentText + readonly property real value: PulseObject.volume > slider.maximumValue ? PulseObject.volume : slider.value Layout.alignment: Qt.AlignHCenter Layout.minimumWidth: referenceText.width horizontalAlignment: Qt.AlignRight - text: i18nc("volume percentage", "%1%", Math.floor(slider.value / slider.maximumValue * 100.0)) + text: i18nc("volume percentage", "%1%", Math.round(value / PulseAudio.NormalVolume * 100.0)) } Label { diff --git a/src/qml/plugin.cpp b/src/qml/plugin.cpp --- a/src/qml/plugin.cpp +++ b/src/qml/plugin.cpp @@ -26,10 +26,22 @@ #include "client.h" #include "sink.h" #include "source.h" +#include "context.h" #include "globalactioncollection.h" #include "volumeosd.h" +static QJSValue pulseaudio_singleton(QQmlEngine *engine, QJSEngine *scriptEngine) +{ + Q_UNUSED(engine) + + QJSValue object = scriptEngine->newObject(); + object.setProperty(QStringLiteral("NormalVolume"), (double) QPulseAudio::Context::NormalVolume); + object.setProperty(QStringLiteral("MinimalVolume"), (double) QPulseAudio::Context::MinimalVolume); + object.setProperty(QStringLiteral("MaximalVolume"), (double) QPulseAudio::Context::MaximalVolume); + return object; +} + void Plugin::registerTypes(const char* uri) { qmlRegisterType(uri, 0, 1, "CardModel"); @@ -40,6 +52,7 @@ qmlRegisterType(uri, 0, 1, "GlobalAction"); qmlRegisterType(uri, 0, 1, "GlobalActionCollection"); qmlRegisterType(uri, 0, 1, "VolumeOSD"); + qmlRegisterSingletonType(uri, 0, 1, "PulseAudio", pulseaudio_singleton); qmlRegisterType(); qmlRegisterType();