mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-04-29 15:03:09 +00:00
audio: Refactor applet and extract singleton to MobileShellState
The eventual goal is to have as few singletons with state as possible in the mobileshell component when it is imported into components such as the lockscreen. This doesn't fully accomplish it, but moves the audio provider singleton to MobileShellState, which will eventually need to be prevented from importing into non plasmashell processes. This also disables the sound feedback when changing volume, since it can be a source of lag when showing the applet.
This commit is contained in:
parent
076d1c4f0c
commit
158af43fd4
24 changed files with 283 additions and 301 deletions
|
|
@ -76,10 +76,10 @@ void MobileShellPlugin::registerTypes(const char *uri)
|
|||
qmlRegisterType(resolvePath("components/VelocityCalculator.qml"), uri, 1, 0, "VelocityCalculator");
|
||||
|
||||
// /dataproviders
|
||||
qmlRegisterType(resolvePath("dataproviders/AudioInfo.qml"), uri, 1, 0, "AudioInfo");
|
||||
qmlRegisterType(resolvePath("dataproviders/BatteryInfo.qml"), uri, 1, 0, "BatteryInfo");
|
||||
qmlRegisterType(resolvePath("dataproviders/BluetoothInfo.qml"), uri, 1, 0, "BluetoothInfo");
|
||||
qmlRegisterType(resolvePath("dataproviders/SignalStrengthInfo.qml"), uri, 1, 0, "SignalStrengthInfo");
|
||||
qmlRegisterSingletonType(resolvePath("dataproviders/AudioProvider.qml"), uri, 1, 0, "AudioProvider");
|
||||
|
||||
// /homescreen
|
||||
qmlRegisterType(resolvePath("homescreen/HomeScreen.qml"), uri, 1, 0, "HomeScreen");
|
||||
|
|
@ -92,6 +92,9 @@ void MobileShellPlugin::registerTypes(const char *uri)
|
|||
// /statusbar
|
||||
qmlRegisterType(resolvePath("statusbar/StatusBar.qml"), uri, 1, 0, "StatusBar");
|
||||
|
||||
// /volumeosd
|
||||
qmlRegisterType(resolvePath("volumeosd/VolumeOSD.qml"), uri, 1, 0, "VolumeOSD");
|
||||
|
||||
// /widgets
|
||||
qmlRegisterType(resolvePath("widgets/krunner/KRunnerWidget.qml"), uri, 1, 0, "KRunnerWidget");
|
||||
qmlRegisterType(resolvePath("widgets/mediacontrols/MediaControlsWidget.qml"), uri, 1, 0, "MediaControlsWidget");
|
||||
|
|
|
|||
90
components/mobileshell/qml/dataproviders/AudioInfo.qml
Normal file
90
components/mobileshell/qml/dataproviders/AudioInfo.qml
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
// SPDX-FileCopyrightText: 2023 Devin Lin <devin@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
|
||||
import org.kde.plasma.private.volume
|
||||
|
||||
QtObject {
|
||||
property SinkModel paSinkModel: SinkModel {}
|
||||
|
||||
// whether the audio icon should be visible in the status bar
|
||||
readonly property bool isVisible: paSinkModel.preferredSink && paSinkModel.preferredSink.muted
|
||||
|
||||
// the icon that should be displayed in the status bar
|
||||
readonly property string icon: paSinkModel.preferredSink && !isDummyOutput(paSinkModel.preferredSink)
|
||||
? iconName(paSinkModel.preferredSink.volume, paSinkModel.preferredSink.muted)
|
||||
: iconName(0, true)
|
||||
|
||||
// the name of the audio device when it isn't valid
|
||||
readonly property string dummyOutputName: "auto_null"
|
||||
|
||||
// the maximum volume amount
|
||||
readonly property int maxVolumeValue: Math.round(100 * PulseAudio.NormalVolume / 100.0)
|
||||
|
||||
// step that increments when adjusting the volume
|
||||
readonly property int volumeStep: Math.round(5 * PulseAudio.NormalVolume / 100.0)
|
||||
|
||||
function isDummyOutput(output) {
|
||||
return output && output.name === dummyOutputName;
|
||||
}
|
||||
|
||||
function boundVolume(volume) {
|
||||
return Math.max(PulseAudio.MinimalVolume, Math.min(volume, maxVolumeValue));
|
||||
}
|
||||
|
||||
function volumePercent(volume, max){
|
||||
if (!max) {
|
||||
max = PulseAudio.NormalVolume;
|
||||
}
|
||||
return Math.round(volume / max * 100.0);
|
||||
}
|
||||
|
||||
function increaseVolume() {
|
||||
if (!paSinkModel.preferredSink || isDummyOutput(paSinkModel.preferredSink)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var volume = boundVolume(paSinkModel.preferredSink.volume + volumeStep);
|
||||
var percent = volumePercent(volume, maxVolumeValue);
|
||||
paSinkModel.preferredSink.muted = percent == 0;
|
||||
paSinkModel.preferredSink.volume = volume;
|
||||
}
|
||||
|
||||
function decreaseVolume() {
|
||||
if (!paSinkModel.preferredSink || isDummyOutput(paSinkModel.preferredSink)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var volume = boundVolume(paSinkModel.preferredSink.volume - volumeStep);
|
||||
var percent = volumePercent(volume, maxVolumeValue);
|
||||
paSinkModel.preferredSink.muted = percent == 0;
|
||||
paSinkModel.preferredSink.volume = volume;
|
||||
}
|
||||
|
||||
function muteVolume() {
|
||||
if (!paSinkModel.preferredSink || isDummyOutput(paSinkModel.preferredSink)) {
|
||||
return;
|
||||
}
|
||||
|
||||
paSinkModel.preferredSink.muted = !paSinkModel.preferredSink.muted;
|
||||
}
|
||||
|
||||
function iconName(volume, muted, prefix) {
|
||||
if (!prefix) {
|
||||
prefix = "audio-volume";
|
||||
}
|
||||
var icon = null;
|
||||
var percent = volume / maxVolumeValue;
|
||||
if (percent <= 0.0 || muted) {
|
||||
icon = prefix + "-muted";
|
||||
} else if (percent <= 0.25) {
|
||||
icon = prefix + "-low";
|
||||
} else if (percent <= 0.75) {
|
||||
icon = prefix + "-medium";
|
||||
} else {
|
||||
icon = prefix + "-high";
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,195 +0,0 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||
SPDX-FileCopyrightText: 2019 Aditya Mehra <Aix.m@outlook.com>
|
||||
SPDX-FileCopyrightText: 2014-2015 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Layouts 1.4
|
||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||
import org.kde.plasma.private.volume 0.1
|
||||
|
||||
import "../osd/volume"
|
||||
|
||||
pragma Singleton
|
||||
|
||||
QtObject {
|
||||
/**
|
||||
* Whether or not to bind the volume global key shortcuts.
|
||||
* We should never bind the shortcut multiple times in the shell, or else they may not work at all.
|
||||
*
|
||||
* We only set this to true when loaded for the panel containment (and NOT in the lockscreen).
|
||||
*/
|
||||
property bool bindShortcuts: false
|
||||
|
||||
readonly property bool isVisible: paSinkModel.preferredSink && paSinkModel.preferredSink.muted
|
||||
|
||||
readonly property string icon: paSinkModel.preferredSink && !isDummyOutput(paSinkModel.preferredSink)
|
||||
? iconName(paSinkModel.preferredSink.volume, paSinkModel.preferredSink.muted)
|
||||
: iconName(0, true)
|
||||
|
||||
readonly property int maxVolumeValue: Math.round(100 * PulseAudio.NormalVolume / 100.0)
|
||||
readonly property int volumeStep: Math.round(5 * PulseAudio.NormalVolume / 100.0)
|
||||
|
||||
property int volumeValue
|
||||
|
||||
readonly property string dummyOutputName: "auto_null"
|
||||
|
||||
function showVolumeOverlay() {
|
||||
osd.showOverlay();
|
||||
}
|
||||
|
||||
function iconName(volume, muted, prefix) {
|
||||
if (!prefix) {
|
||||
prefix = "audio-volume";
|
||||
}
|
||||
var icon = null;
|
||||
var percent = volume / maxVolumeValue;
|
||||
if (percent <= 0.0 || muted) {
|
||||
icon = prefix + "-muted";
|
||||
} else if (percent <= 0.25) {
|
||||
icon = prefix + "-low";
|
||||
} else if (percent <= 0.75) {
|
||||
icon = prefix + "-medium";
|
||||
} else {
|
||||
icon = prefix + "-high";
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
|
||||
function isDummyOutput(output) {
|
||||
return output && output.name === dummyOutputName;
|
||||
}
|
||||
|
||||
function boundVolume(volume) {
|
||||
return Math.max(PulseAudio.MinimalVolume, Math.min(volume, maxVolumeValue));
|
||||
}
|
||||
|
||||
function volumePercent(volume, max){
|
||||
if(!max) {
|
||||
max = PulseAudio.NormalVolume;
|
||||
}
|
||||
return Math.round(volume / max * 100.0);
|
||||
}
|
||||
|
||||
function playFeedback(sinkIndex) {
|
||||
if (sinkIndex == undefined) {
|
||||
sinkIndex = paSinkModel.preferredSink.index;
|
||||
}
|
||||
feedback.play(sinkIndex)
|
||||
}
|
||||
|
||||
function increaseVolume() {
|
||||
if (!paSinkModel.preferredSink || isDummyOutput(paSinkModel.preferredSink)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var volume = boundVolume(paSinkModel.preferredSink.volume + volumeStep);
|
||||
var percent = volumePercent(volume, maxVolumeValue);
|
||||
paSinkModel.preferredSink.muted = percent == 0;
|
||||
paSinkModel.preferredSink.volume = volume;
|
||||
volumeValue = percent;
|
||||
osd.showOverlay();
|
||||
playFeedback();
|
||||
|
||||
}
|
||||
|
||||
function decreaseVolume() {
|
||||
if (!paSinkModel.preferredSink || isDummyOutput(paSinkModel.preferredSink)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var volume = boundVolume(paSinkModel.preferredSink.volume - volumeStep);
|
||||
var percent = volumePercent(volume, maxVolumeValue);
|
||||
paSinkModel.preferredSink.muted = percent == 0;
|
||||
paSinkModel.preferredSink.volume = volume;
|
||||
volumeValue = percent;
|
||||
osd.showOverlay();
|
||||
playFeedback();
|
||||
}
|
||||
|
||||
function muteVolume() {
|
||||
if (!paSinkModel.preferredSink || isDummyOutput(paSinkModel.preferredSink)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var toMute = !paSinkModel.preferredSink.muted;
|
||||
paSinkModel.preferredSink.muted = toMute;
|
||||
|
||||
volumeValue = toMute ? 0 : volumePercent(paSinkModel.preferredSink.volume, maxVolumeValue);
|
||||
osd.showOverlay();
|
||||
|
||||
if (!toMute) {
|
||||
playFeedback();
|
||||
}
|
||||
}
|
||||
|
||||
property var updateVolume: Connections {
|
||||
target: paSinkModel.preferredSink
|
||||
|
||||
function onVolumeChanged() {
|
||||
var percent = volumePercent(paSinkModel.preferredSink.volume, maxVolumeValue);
|
||||
volumeValue = percent;
|
||||
}
|
||||
}
|
||||
property var updateVolumeOnSinkChange: Connections {
|
||||
target: paSinkModel
|
||||
|
||||
function onPreferredSinkChanged() {
|
||||
if (paSinkModel.preferredSink) {
|
||||
var percent = volumePercent(paSinkModel.preferredSink.volume, maxVolumeValue);
|
||||
volumeValue = percent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property SinkModel paSinkModel: SinkModel {}
|
||||
|
||||
property var osd: VolumeOSD {
|
||||
volume: volumeValue
|
||||
}
|
||||
|
||||
property VolumeFeedback feedback: VolumeFeedback {}
|
||||
|
||||
// only bind the global shortcuts when told to
|
||||
property var actionCollection: Loader {
|
||||
active: bindShortcuts
|
||||
|
||||
sourceComponent: GlobalActionCollection {
|
||||
// KGlobalAccel cannot transition from kmix to something else, so if
|
||||
// the user had a custom shortcut set for kmix those would get lost.
|
||||
// To avoid this we hijack kmix name and actions. Entirely mental but
|
||||
// best we can do to not cause annoyance for the user.
|
||||
// The display name actually is updated to whatever registered last
|
||||
// though, so as far as user visible strings go we should be fine.
|
||||
// As of 2015-07-21:
|
||||
// componentName: kmix
|
||||
// actions: increase_volume, decrease_volume, mute
|
||||
name: "kmix"
|
||||
displayName: i18n("Audio")
|
||||
|
||||
GlobalAction {
|
||||
objectName: "increase_volume"
|
||||
text: i18n("Increase Volume")
|
||||
shortcut: Qt.Key_VolumeUp
|
||||
onTriggered: increaseVolume()
|
||||
}
|
||||
|
||||
GlobalAction {
|
||||
objectName: "decrease_volume"
|
||||
text: i18n("Decrease Volume")
|
||||
shortcut: Qt.Key_VolumeDown
|
||||
onTriggered: decreaseVolume()
|
||||
}
|
||||
|
||||
GlobalAction {
|
||||
objectName: "mute"
|
||||
text: i18n("Mute")
|
||||
shortcut: Qt.Key_VolumeMute
|
||||
onTriggered: muteVolume()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,15 +5,14 @@
|
|||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick 2.6
|
||||
import QtQuick.Layouts 1.4
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||
import org.kde.plasma.plasma5support 2.0 as P5Support
|
||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||
import org.kde.plasma.workspace.components 2.0 as PW
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
import org.kde.plasma.plasma5support as P5Support
|
||||
import org.kde.plasma.workspace.components as PW
|
||||
|
||||
Item {
|
||||
QtObject {
|
||||
property bool isVisible: pmSource.data["Battery"]["Has Cumulative"]
|
||||
property int percent: pmSource.data["Battery"]["Percent"]
|
||||
property bool pluggedIn: pmSource.data["AC Adapter"] ? pmSource.data["AC Adapter"]["Plugged in"] : false
|
||||
|
|
|
|||
|
|
@ -8,13 +8,14 @@
|
|||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Layouts 1.4
|
||||
|
||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||
import org.kde.plasma.private.volume 0.1
|
||||
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
||||
|
||||
PlasmaCore.IconItem {
|
||||
id: paIcon
|
||||
readonly property var provider: MobileShell.AudioProvider
|
||||
readonly property var provider: MobileShell.AudioInfo {}
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: height
|
||||
|
|
|
|||
|
|
@ -5,42 +5,32 @@
|
|||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15 as Controls
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Window 2.2
|
||||
import QtQuick
|
||||
import QtQuick.Controls as Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Window
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
import org.kde.plasma.core 2.1 as PlasmaCore
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||
import org.kde.plasma.extras 2.0 as PlasmaExtra
|
||||
import org.kde.kquickcontrolsaddons 2.0 as KQCAddons
|
||||
import org.kde.plasma.extras as PlasmaExtra
|
||||
import org.kde.plasma.private.mobileshell as MobileShell
|
||||
import org.kde.kquickcontrolsaddons as KQCAddons
|
||||
|
||||
import org.kde.plasma.private.volume 0.1
|
||||
import org.kde.plasma.private.volume
|
||||
|
||||
// adapted version of https://invent.kde.org/plasma/plasma-pa/-/blob/master/applet/contents/ui/main.qml
|
||||
|
||||
// most audio functions are in VolumeProvider.qml (which will be a parent)
|
||||
// capture presses on the audio applet so it doesn't close the overlay
|
||||
ColumnLayout {
|
||||
spacing: 0
|
||||
|
||||
// pulseaudio models
|
||||
|
||||
function isDummyOutput(output) {
|
||||
return output && output.name === dummyOutputName;
|
||||
}
|
||||
|
||||
SinkModel {
|
||||
id: paSinkModel
|
||||
}
|
||||
required property MobileShell.AudioInfo audioInfo
|
||||
|
||||
PulseObjectFilterModel {
|
||||
id: paSinkFilterModel
|
||||
sortRole: "SortByDefault"
|
||||
sortOrder: Qt.DescendingOrder
|
||||
filterOutInactiveDevices: true
|
||||
sourceModel: paSinkModel
|
||||
sourceModel: audioInfo.paSinkModel
|
||||
}
|
||||
|
||||
SourceModel {
|
||||
|
|
@ -29,9 +29,6 @@ Controls.ItemDelegate {
|
|||
property alias iconUsesPlasmaTheme: clientIcon.usesPlasmaTheme
|
||||
property string type // sink, source, source-output
|
||||
|
||||
// backgroundColor: "transparent" // we use panel background, no need for the same colour to be on top
|
||||
// activeBackgroundColor: selectButton.visible ? PlasmaCore.Theme.highlightColor : "transparent"
|
||||
|
||||
onClicked: {
|
||||
if (selectButton.visible) {
|
||||
model.PulseObject.default = true;
|
||||
|
|
@ -215,10 +212,6 @@ Controls.ItemDelegate {
|
|||
// whereas PA rejected the volume change and is
|
||||
// still at v15 (e.g.).
|
||||
updateTimer.restart();
|
||||
|
||||
if (baseItem.type == "sink") {
|
||||
playFeedback(Index); // goes to providers/VolumeProvider.qml
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -6,10 +6,10 @@
|
|||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15 as Controls
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Window 2.2
|
||||
import QtQuick
|
||||
import QtQuick.Controls as Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Window
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||
|
|
@ -20,27 +20,30 @@ import org.kde.plasma.private.nanoshell 2.0 as NanoShell
|
|||
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
||||
import org.kde.plasma.private.mobileshell.state 1.0 as MobileShellState
|
||||
|
||||
import org.kde.kirigami 2.12 as Kirigami
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
// this is loaded and managed by indicators/providers/VolumeProvider.qml
|
||||
NanoShell.FullScreenOverlay {
|
||||
Window {
|
||||
id: window
|
||||
|
||||
required property int volume
|
||||
|
||||
// used by context menus opened in the applet to not autoclose the osd
|
||||
property bool suppressActiveClose: false
|
||||
|
||||
// whether the applet is showing all devices
|
||||
property bool showFullApplet: false
|
||||
|
||||
visible: false
|
||||
|
||||
color: showFullApplet ? Qt.rgba(0, 0, 0, 0.6) : "transparent"
|
||||
|
||||
property bool suppressActiveClose: false // used by context menus opened in the applet to not autoclose the osd
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {}
|
||||
}
|
||||
|
||||
property int volume: 0
|
||||
property bool showFullApplet: false
|
||||
|
||||
function showOverlay() {
|
||||
if (!window.visible) {
|
||||
window.showFullApplet = false;
|
||||
window.showMaximized();
|
||||
window.showFullScreen();
|
||||
hideTimer.restart();
|
||||
} else if (!window.showFullApplet) { // don't autohide applet when the full applet is showing
|
||||
hideTimer.restart();
|
||||
|
|
@ -63,15 +66,19 @@ NanoShell.FullScreenOverlay {
|
|||
window.showFullApplet = false;
|
||||
}
|
||||
}
|
||||
|
||||
MobileShell.AudioInfo {
|
||||
id: audioInfo
|
||||
}
|
||||
|
||||
Flickable {
|
||||
id: flickable
|
||||
anchors.fill: parent
|
||||
contentHeight: cards.implicitHeight
|
||||
boundsBehavior: window.showFullApplet ? Flickable.DragAndOvershootBounds : Flickable.StopAtBounds
|
||||
|
||||
|
||||
pressDelay: 50
|
||||
|
||||
|
||||
MouseArea {
|
||||
// capture taps behind cards to close
|
||||
anchors.left: parent.left
|
||||
|
|
@ -82,30 +89,30 @@ NanoShell.FullScreenOverlay {
|
|||
hideTimer.stop();
|
||||
hideTimer.triggered();
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
id: cards
|
||||
width: parent.width
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: 0
|
||||
|
||||
|
||||
// osd card
|
||||
PopupCard {
|
||||
id: osd
|
||||
Layout.topMargin: PlasmaCore.Units.largeSpacing
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
|
||||
contentItem: RowLayout {
|
||||
id: containerLayout
|
||||
spacing: PlasmaCore.Units.smallSpacing
|
||||
|
||||
anchors.leftMargin: PlasmaCore.Units.smallSpacing * 2
|
||||
anchors.rightMargin: PlasmaCore.Units.smallSpacing
|
||||
|
||||
|
||||
PlasmaComponents.ToolButton {
|
||||
icon.name: !paSinkModel.preferredSink || paSinkModel.preferredSink.muted ? "audio-volume-muted" : "audio-volume-high"
|
||||
text: !paSinkModel.preferredSink || paSinkModel.preferredSink.muted ? i18n("Unmute") : i18n("Mute")
|
||||
icon.name: !audioInfo.paSinkModel.preferredSink || audioInfo.paSinkModel.preferredSink.muted ? "audio-volume-muted" : "audio-volume-high"
|
||||
text: !audioInfo.paSinkModel.preferredSink || audioInfo.paSinkModel.preferredSink.muted ? i18n("Unmute") : i18n("Mute")
|
||||
display: Controls.AbstractButton.IconOnly
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.preferredWidth: PlasmaCore.Units.iconSizes.medium
|
||||
|
|
@ -113,7 +120,7 @@ NanoShell.FullScreenOverlay {
|
|||
Layout.rightMargin: PlasmaCore.Units.smallSpacing
|
||||
onClicked: muteVolume()
|
||||
}
|
||||
|
||||
|
||||
PlasmaComponents.ProgressBar {
|
||||
id: volumeSlider
|
||||
Layout.fillWidth: true
|
||||
|
|
@ -124,7 +131,7 @@ NanoShell.FullScreenOverlay {
|
|||
to: 100
|
||||
Behavior on value { NumberAnimation { duration: PlasmaCore.Units.shortDuration } }
|
||||
}
|
||||
|
||||
|
||||
// Get the width of a three-digit number so we can size the label
|
||||
// to the maximum width to avoid the progress bar resizing itself
|
||||
TextMetrics {
|
||||
|
|
@ -140,7 +147,7 @@ NanoShell.FullScreenOverlay {
|
|||
Layout.rightMargin: PlasmaCore.Units.smallSpacing
|
||||
level: 3
|
||||
text: i18nc("Percentage value", "%1%", window.volume)
|
||||
|
||||
|
||||
// Display a subtle visual indication that the volume might be
|
||||
// dangerously high
|
||||
// ------------------------------------------------
|
||||
|
|
@ -156,7 +163,7 @@ NanoShell.FullScreenOverlay {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PlasmaComponents.ToolButton {
|
||||
icon.name: "configure"
|
||||
text: i18n("Open audio settings")
|
||||
|
|
@ -167,16 +174,16 @@ NanoShell.FullScreenOverlay {
|
|||
Layout.preferredWidth: PlasmaCore.Units.iconSizes.medium
|
||||
Layout.preferredHeight: PlasmaCore.Units.iconSizes.medium
|
||||
Layout.rightMargin: PlasmaCore.Units.smallSpacing
|
||||
|
||||
|
||||
Behavior on opacity { NumberAnimation { duration: PlasmaCore.Units.shortDuration } }
|
||||
|
||||
|
||||
onClicked: {
|
||||
let coords = mapToItem(flickable, 0, 0);
|
||||
MobileShellState.Shell.openAppLaunchAnimation("audio-volume-high", i18n("Audio Settings"), coords.x, coords.y, PlasmaCore.Units.iconSizes.medium);
|
||||
MobileShell.ShellUtil.executeCommand("plasma-open-settings kcm_pulseaudio");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PlasmaComponents.ToolButton {
|
||||
icon.name: window.showFullApplet ? "arrow-up" : "arrow-down"
|
||||
text: i18n("Toggle showing audio streams")
|
||||
|
|
@ -203,14 +210,16 @@ NanoShell.FullScreenOverlay {
|
|||
Layout.topMargin: PlasmaCore.Units.largeSpacing
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.preferredWidth: cards.width
|
||||
|
||||
|
||||
audioInfo: audioInfo
|
||||
|
||||
opacity: window.showFullApplet ? 1 : 0
|
||||
visible: opacity !== 0
|
||||
transform: Translate {
|
||||
transform: Translate {
|
||||
y: window.showFullApplet ? 0 : -PlasmaCore.Units.gridUnit
|
||||
Behavior on y { NumberAnimation {} }
|
||||
}
|
||||
|
||||
|
||||
Behavior on opacity { NumberAnimation {} }
|
||||
}
|
||||
}
|
||||
|
|
@ -37,7 +37,7 @@
|
|||
<file>qml/dataproviders/BatteryInfo.qml</file>
|
||||
<file>qml/dataproviders/BluetoothInfo.qml</file>
|
||||
<file>qml/dataproviders/SignalStrengthInfo.qml</file>
|
||||
<file>qml/dataproviders/AudioProvider.qml</file>
|
||||
<file>qml/dataproviders/AudioInfo.qml</file>
|
||||
|
||||
<file>qml/homescreen/HomeScreen.qml</file>
|
||||
|
||||
|
|
@ -46,13 +46,13 @@
|
|||
<file>qml/navigationpanel/NavigationPanelAction.qml</file>
|
||||
<file>qml/navigationpanel/NavigationPanelButton.qml</file>
|
||||
|
||||
<file>qml/osd/volume/AudioApplet.qml</file>
|
||||
<file>qml/osd/volume/DeviceListItem.qml</file>
|
||||
<file>qml/osd/volume/icon.js</file>
|
||||
<file>qml/osd/volume/ListItemBase.qml</file>
|
||||
<file>qml/osd/volume/PopupCard.qml</file>
|
||||
<file>qml/osd/volume/StreamListItem.qml</file>
|
||||
<file>qml/osd/volume/VolumeOSD.qml</file>
|
||||
<file>qml/volumeosd/AudioApplet.qml</file>
|
||||
<file>qml/volumeosd/DeviceListItem.qml</file>
|
||||
<file>qml/volumeosd/icon.js</file>
|
||||
<file>qml/volumeosd/ListItemBase.qml</file>
|
||||
<file>qml/volumeosd/PopupCard.qml</file>
|
||||
<file>qml/volumeosd/StreamListItem.qml</file>
|
||||
<file>qml/volumeosd/VolumeOSD.qml</file>
|
||||
|
||||
<file>qml/statusbar/indicators/BatteryIndicator.qml</file>
|
||||
<file>qml/statusbar/indicators/BluetoothIndicator.qml</file>
|
||||
|
|
|
|||
10
components/mobileshellstate/README.md
Normal file
10
components/mobileshellstate/README.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<!--
|
||||
- SPDX-FileCopyrightText: None
|
||||
- SPDX-License-Identifier: CC0-1.0
|
||||
-->
|
||||
|
||||
# MobileShell State Plugin
|
||||
|
||||
This plugin stores the state of the mobile shell.
|
||||
|
||||
It should ONLY be imported by components in the plasmashell process (otherwise it won't be able to get the state).
|
||||
|
|
@ -16,6 +16,7 @@ void MobileShellStatePlugin::registerTypes(const char *uri)
|
|||
Q_ASSERT(QLatin1String(uri) == QLatin1String("org.kde.plasma.private.mobileshell.state"));
|
||||
|
||||
// /
|
||||
qmlRegisterSingletonType(resolvePath("AudioProvider.qml"), uri, 1, 0, "AudioProvider");
|
||||
qmlRegisterSingletonType(resolvePath("HomeScreenControls.qml"), uri, 1, 0, "HomeScreenControls");
|
||||
qmlRegisterSingletonType(resolvePath("Shell.qml"), uri, 1, 0, "Shell");
|
||||
qmlRegisterSingletonType(resolvePath("TopPanelControls.qml"), uri, 1, 0, "TopPanelControls");
|
||||
|
|
|
|||
98
components/mobileshellstate/qml/AudioProvider.qml
Normal file
98
components/mobileshellstate/qml/AudioProvider.qml
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||
SPDX-FileCopyrightText: 2019 Aditya Mehra <Aix.m@outlook.com>
|
||||
SPDX-FileCopyrightText: 2014-2015 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
import org.kde.plasma.private.volume 0.1
|
||||
import org.kde.plasma.private.mobileshell as MobileShell
|
||||
|
||||
pragma Singleton
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
/**
|
||||
* Whether or not to bind the volume global key shortcuts.
|
||||
* We should never bind the shortcut multiple times in the shell, or else they may not work at all.
|
||||
*
|
||||
* We only set this to true when loaded for the panel containment (and NOT in the lockscreen).
|
||||
*/
|
||||
property bool bindShortcuts: false
|
||||
|
||||
property int volumeValue
|
||||
|
||||
function showVolumeOverlay() {
|
||||
osd.showOverlay();
|
||||
}
|
||||
|
||||
property var audioInfo: MobileShell.AudioInfo {}
|
||||
|
||||
property var updateVolume: Connections {
|
||||
target: audioInfo.paSinkModel.preferredSink
|
||||
|
||||
function onVolumeChanged() {
|
||||
var percent = audioInfo.volumePercent(audioInfo.paSinkModel.preferredSink.volume, audioInfo.maxVolumeValue);
|
||||
volumeValue = percent;
|
||||
|
||||
osd.showOverlay();
|
||||
}
|
||||
|
||||
function onMutedChanged() {
|
||||
volumeValue = audioInfo.paSinkModel.preferredSink.muted ? 0 : audioInfo.volumePercent(audioInfo.paSinkModel.preferredSink.volume, audioInfo.maxVolumeValue);
|
||||
osd.showOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
property var updateVolumeOnSinkChange: Connections {
|
||||
target: audioInfo.paSinkModel
|
||||
|
||||
function onPreferredSinkChanged() {
|
||||
if (audioInfo.paSinkModel.preferredSink) {
|
||||
var percent = audioInfo.volumePercent(audioInfo.paSinkModel.preferredSink.volume, audioInfo.maxVolumeValue);
|
||||
volumeValue = percent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property var osd: MobileShell.VolumeOSD {
|
||||
volume: volumeValue
|
||||
}
|
||||
|
||||
// only bind the global shortcuts when told to
|
||||
property var actionCollection: Loader {
|
||||
active: bindShortcuts
|
||||
|
||||
sourceComponent: GlobalActionCollection {
|
||||
name: "kmix"
|
||||
displayName: i18n("Audio")
|
||||
|
||||
GlobalAction {
|
||||
objectName: "increase_volume"
|
||||
text: i18n("Increase Volume")
|
||||
shortcut: Qt.Key_VolumeUp
|
||||
onTriggered: root.audioInfo.increaseVolume()
|
||||
}
|
||||
|
||||
GlobalAction {
|
||||
objectName: "decrease_volume"
|
||||
text: i18n("Decrease Volume")
|
||||
shortcut: Qt.Key_VolumeDown
|
||||
onTriggered: root.audioInfo.decreaseVolume()
|
||||
}
|
||||
|
||||
GlobalAction {
|
||||
objectName: "mute"
|
||||
text: i18n("Mute")
|
||||
shortcut: Qt.Key_VolumeMute
|
||||
onTriggered: root.audioInfo.muteVolume()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,9 +5,6 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.2
|
||||
|
||||
import org.kde.plasma.private.mobileshell as MobileShell
|
||||
import org.kde.plasma.private.mobileshell.windowplugin as WindowPlugin
|
||||
|
||||
pragma Singleton
|
||||
|
||||
/**
|
||||
|
|
@ -26,15 +23,4 @@ QtObject {
|
|||
property var taskSwitcher
|
||||
property QtObject homeScreenWindow
|
||||
property bool taskSwitcherVisible: false
|
||||
|
||||
// this state is updated from WindowUtil
|
||||
property bool homeScreenVisible: true
|
||||
|
||||
property var windowListener: Connections {
|
||||
target: WindowPlugin.WindowUtil
|
||||
|
||||
function onAllWindowsMinimizedChanged() {
|
||||
root.homeScreenVisible = WindowPlugin.WindowUtil.allWindowsMinimized
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,11 +12,6 @@ pragma Singleton
|
|||
QtObject {
|
||||
id: delegate
|
||||
|
||||
/**
|
||||
* Whether the homescreen is currently visible.
|
||||
*/
|
||||
readonly property bool homeScreenVisible: HomeScreenControls.homeScreenVisible
|
||||
|
||||
/**
|
||||
* Whether the action drawer is currently open.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
<file>qml/HomeScreenControls.qml</file>
|
||||
<file>qml/Shell.qml</file>
|
||||
<file>qml/TopPanelControls.qml</file>
|
||||
<file>qml/AudioProvider.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import org.kde.plasma.components 3.0 as PlasmaComponents
|
|||
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
||||
import org.kde.plasma.private.mobileshell.state 1.0 as MobileShellState
|
||||
import org.kde.private.mobile.homescreen.folio 1.0 as Folio
|
||||
import org.kde.plasma.private.mobileshell.windowplugin as WindowPlugin
|
||||
|
||||
MobileShell.HomeScreen {
|
||||
id: root
|
||||
|
|
@ -43,7 +44,7 @@ MobileShell.HomeScreen {
|
|||
// - minimize windows (only if we are in an app)
|
||||
// - open app drawer
|
||||
// - close app drawer and, if necessary, restore windows
|
||||
if (!plasmoid.nativeInterface.showingDesktop && !MobileShellState.Shell.homeScreenVisible
|
||||
if (!plasmoid.nativeInterface.showingDesktop && !WindowPlugin.WindowUtil.allWindowsMinimized
|
||||
|| MobileShellState.Shell.actionDrawerVisible
|
||||
|| searchWidget.isOpen
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import org.kde.draganddrop as DragDrop
|
|||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.plasma.private.mobileshell.state as MobileShellState
|
||||
import org.kde.plasma.private.mobileshell.windowplugin as WindowPlugin
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
|
@ -45,7 +46,7 @@ Item {
|
|||
target: MobileShellState.HomeScreenControls
|
||||
|
||||
function onHomeScreenVisibleChanged(){
|
||||
if (MobileShellState.HomeScreenControls.homeScreenVisible) {
|
||||
if (WindowPlugin.WindowUtil.allWindowsMinimized) {
|
||||
swipeView.focusChild();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,9 +46,7 @@ MobileShell.HomeScreen {
|
|||
// - minimize windows (only if we are in an app)
|
||||
// - open app drawer
|
||||
// - close app drawer and, if necessary, restore windows
|
||||
if (!WindowPlugin.WindowUtil.showDesktop && !MobileShellState.Shell.homeScreenVisible
|
||||
|| search.isOpen
|
||||
) {
|
||||
if (!WindowPlugin.WindowUtil.showDesktop && !WindowPlugin.WindowUtil.allWindowsMinimized || search.isOpen) {
|
||||
// Always close action drawer
|
||||
if (MobileShellState.Shell.actionDrawerVisible) {
|
||||
MobileShellState.Shell.closeActionDrawer();
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ Item {
|
|||
|
||||
Component.onCompleted: {
|
||||
// we want to bind global volume shortcuts here
|
||||
MobileShell.AudioProvider.bindShortcuts = true;
|
||||
MobileShellState.AudioProvider.bindShortcuts = true;
|
||||
}
|
||||
|
||||
TaskManager.VirtualDesktopInfo {
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
// SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick
|
||||
|
||||
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
||||
import org.kde.plasma.private.mobileshell as MobileShell
|
||||
import org.kde.plasma.private.mobileshell.state as MobileShellState
|
||||
|
||||
MobileShell.QuickSetting {
|
||||
text: i18n("Sound")
|
||||
icon: "audio-speakers-symbolic"
|
||||
status: i18n("%1%", MobileShell.AudioProvider.volumeValue)
|
||||
status: i18n("%1%", MobileShellState.AudioProvider.volumeValue)
|
||||
enabled: false
|
||||
settingsCommand: "plasma-open-settings kcm_pulseaudio"
|
||||
function toggle() {
|
||||
MobileShell.AudioProvider.showVolumeOverlay()
|
||||
MobileShellState.AudioProvider.showVolumeOverlay()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue