diff --git a/components/mobileshell/mobileshellplugin.cpp b/components/mobileshell/mobileshellplugin.cpp index 11c0bd98..dfebafb5 100644 --- a/components/mobileshell/mobileshellplugin.cpp +++ b/components/mobileshell/mobileshellplugin.cpp @@ -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"); diff --git a/components/mobileshell/qml/dataproviders/AudioInfo.qml b/components/mobileshell/qml/dataproviders/AudioInfo.qml new file mode 100644 index 00000000..433ae769 --- /dev/null +++ b/components/mobileshell/qml/dataproviders/AudioInfo.qml @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: 2023 Devin Lin +// 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; + } +} diff --git a/components/mobileshell/qml/dataproviders/AudioProvider.qml b/components/mobileshell/qml/dataproviders/AudioProvider.qml deleted file mode 100644 index e43ba666..00000000 --- a/components/mobileshell/qml/dataproviders/AudioProvider.qml +++ /dev/null @@ -1,195 +0,0 @@ -/* - SPDX-FileCopyrightText: 2021 Devin Lin - SPDX-FileCopyrightText: 2019 Aditya Mehra - SPDX-FileCopyrightText: 2014-2015 Harald Sitter - - 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() - } - } - } -} diff --git a/components/mobileshell/qml/dataproviders/BatteryInfo.qml b/components/mobileshell/qml/dataproviders/BatteryInfo.qml index 878999bd..bf564072 100644 --- a/components/mobileshell/qml/dataproviders/BatteryInfo.qml +++ b/components/mobileshell/qml/dataproviders/BatteryInfo.qml @@ -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 diff --git a/components/mobileshell/qml/statusbar/indicators/VolumeIndicator.qml b/components/mobileshell/qml/statusbar/indicators/VolumeIndicator.qml index daedfa7b..1c1e576e 100644 --- a/components/mobileshell/qml/statusbar/indicators/VolumeIndicator.qml +++ b/components/mobileshell/qml/statusbar/indicators/VolumeIndicator.qml @@ -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 diff --git a/components/mobileshell/qml/osd/volume/AudioApplet.qml b/components/mobileshell/qml/volumeosd/AudioApplet.qml similarity index 89% rename from components/mobileshell/qml/osd/volume/AudioApplet.qml rename to components/mobileshell/qml/volumeosd/AudioApplet.qml index fa548d52..693fad51 100644 --- a/components/mobileshell/qml/osd/volume/AudioApplet.qml +++ b/components/mobileshell/qml/volumeosd/AudioApplet.qml @@ -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 { diff --git a/components/mobileshell/qml/osd/volume/DeviceListItem.qml b/components/mobileshell/qml/volumeosd/DeviceListItem.qml similarity index 100% rename from components/mobileshell/qml/osd/volume/DeviceListItem.qml rename to components/mobileshell/qml/volumeosd/DeviceListItem.qml diff --git a/components/mobileshell/qml/osd/volume/ListItemBase.qml b/components/mobileshell/qml/volumeosd/ListItemBase.qml similarity index 96% rename from components/mobileshell/qml/osd/volume/ListItemBase.qml rename to components/mobileshell/qml/volumeosd/ListItemBase.qml index a6c40bba..9fdcdfaf 100644 --- a/components/mobileshell/qml/osd/volume/ListItemBase.qml +++ b/components/mobileshell/qml/volumeosd/ListItemBase.qml @@ -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 - } } } diff --git a/components/mobileshell/qml/osd/volume/PopupCard.qml b/components/mobileshell/qml/volumeosd/PopupCard.qml similarity index 100% rename from components/mobileshell/qml/osd/volume/PopupCard.qml rename to components/mobileshell/qml/volumeosd/PopupCard.qml diff --git a/components/mobileshell/qml/osd/volume/StreamListItem.qml b/components/mobileshell/qml/volumeosd/StreamListItem.qml similarity index 100% rename from components/mobileshell/qml/osd/volume/StreamListItem.qml rename to components/mobileshell/qml/volumeosd/StreamListItem.qml diff --git a/components/mobileshell/qml/osd/volume/VolumeOSD.qml b/components/mobileshell/qml/volumeosd/VolumeOSD.qml similarity index 88% rename from components/mobileshell/qml/osd/volume/VolumeOSD.qml rename to components/mobileshell/qml/volumeosd/VolumeOSD.qml index 40bbf3ad..37492749 100644 --- a/components/mobileshell/qml/osd/volume/VolumeOSD.qml +++ b/components/mobileshell/qml/volumeosd/VolumeOSD.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 {} } } } diff --git a/components/mobileshell/qml/osd/volume/icon.js b/components/mobileshell/qml/volumeosd/icon.js similarity index 100% rename from components/mobileshell/qml/osd/volume/icon.js rename to components/mobileshell/qml/volumeosd/icon.js diff --git a/components/mobileshell/resources.qrc b/components/mobileshell/resources.qrc index f67f6a6d..5cb84865 100644 --- a/components/mobileshell/resources.qrc +++ b/components/mobileshell/resources.qrc @@ -37,7 +37,7 @@ qml/dataproviders/BatteryInfo.qml qml/dataproviders/BluetoothInfo.qml qml/dataproviders/SignalStrengthInfo.qml - qml/dataproviders/AudioProvider.qml + qml/dataproviders/AudioInfo.qml qml/homescreen/HomeScreen.qml @@ -46,13 +46,13 @@ qml/navigationpanel/NavigationPanelAction.qml qml/navigationpanel/NavigationPanelButton.qml - qml/osd/volume/AudioApplet.qml - qml/osd/volume/DeviceListItem.qml - qml/osd/volume/icon.js - qml/osd/volume/ListItemBase.qml - qml/osd/volume/PopupCard.qml - qml/osd/volume/StreamListItem.qml - qml/osd/volume/VolumeOSD.qml + qml/volumeosd/AudioApplet.qml + qml/volumeosd/DeviceListItem.qml + qml/volumeosd/icon.js + qml/volumeosd/ListItemBase.qml + qml/volumeosd/PopupCard.qml + qml/volumeosd/StreamListItem.qml + qml/volumeosd/VolumeOSD.qml qml/statusbar/indicators/BatteryIndicator.qml qml/statusbar/indicators/BluetoothIndicator.qml diff --git a/components/mobileshellstate/README.md b/components/mobileshellstate/README.md new file mode 100644 index 00000000..3166de2e --- /dev/null +++ b/components/mobileshellstate/README.md @@ -0,0 +1,10 @@ + + +# 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). diff --git a/components/mobileshellstate/mobileshellstateplugin.cpp b/components/mobileshellstate/mobileshellstateplugin.cpp index 44d459b7..8c9b9304 100644 --- a/components/mobileshellstate/mobileshellstateplugin.cpp +++ b/components/mobileshellstate/mobileshellstateplugin.cpp @@ -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"); diff --git a/components/mobileshellstate/qml/AudioProvider.qml b/components/mobileshellstate/qml/AudioProvider.qml new file mode 100644 index 00000000..2584af1b --- /dev/null +++ b/components/mobileshellstate/qml/AudioProvider.qml @@ -0,0 +1,98 @@ +/* + SPDX-FileCopyrightText: 2021 Devin Lin + SPDX-FileCopyrightText: 2019 Aditya Mehra + SPDX-FileCopyrightText: 2014-2015 Harald Sitter + + 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() + } + } + } +} diff --git a/components/mobileshellstate/qml/HomeScreenControls.qml b/components/mobileshellstate/qml/HomeScreenControls.qml index 530148c0..cf68a416 100644 --- a/components/mobileshellstate/qml/HomeScreenControls.qml +++ b/components/mobileshellstate/qml/HomeScreenControls.qml @@ -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 - } - } } diff --git a/components/mobileshellstate/qml/Shell.qml b/components/mobileshellstate/qml/Shell.qml index 11db8c99..cd35efaf 100644 --- a/components/mobileshellstate/qml/Shell.qml +++ b/components/mobileshellstate/qml/Shell.qml @@ -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. */ diff --git a/components/mobileshellstate/resources.qrc b/components/mobileshellstate/resources.qrc index 7695d3fc..728f27b2 100644 --- a/components/mobileshellstate/resources.qrc +++ b/components/mobileshellstate/resources.qrc @@ -7,6 +7,7 @@ qml/HomeScreenControls.qml qml/Shell.qml qml/TopPanelControls.qml + qml/AudioProvider.qml diff --git a/containments/homescreens/folio/package/contents/ui/main.qml b/containments/homescreens/folio/package/contents/ui/main.qml index a74d2a7e..cf3d8cdc 100644 --- a/containments/homescreens/folio/package/contents/ui/main.qml +++ b/containments/homescreens/folio/package/contents/ui/main.qml @@ -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 ) { diff --git a/containments/homescreens/halcyon/package/contents/ui/HomeScreen.qml b/containments/homescreens/halcyon/package/contents/ui/HomeScreen.qml index e909768e..9169adf6 100644 --- a/containments/homescreens/halcyon/package/contents/ui/HomeScreen.qml +++ b/containments/homescreens/halcyon/package/contents/ui/HomeScreen.qml @@ -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(); } } diff --git a/containments/homescreens/halcyon/package/contents/ui/main.qml b/containments/homescreens/halcyon/package/contents/ui/main.qml index 0574691b..8b6ef4a4 100644 --- a/containments/homescreens/halcyon/package/contents/ui/main.qml +++ b/containments/homescreens/halcyon/package/contents/ui/main.qml @@ -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(); diff --git a/containments/panel/package/contents/ui/main.qml b/containments/panel/package/contents/ui/main.qml index 66ddcd5f..ad9debfd 100644 --- a/containments/panel/package/contents/ui/main.qml +++ b/containments/panel/package/contents/ui/main.qml @@ -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 { diff --git a/quicksettings/audio/contents/ui/main.qml b/quicksettings/audio/contents/ui/main.qml index 431d3311..099501ac 100644 --- a/quicksettings/audio/contents/ui/main.qml +++ b/quicksettings/audio/contents/ui/main.qml @@ -1,17 +1,18 @@ // SPDX-FileCopyrightText: 2022 Devin Lin // 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() } }