volumeosd: Extract out singleton so that it's only loaded once in plasmashell

This commit is contained in:
Devin Lin 2023-03-19 21:10:14 -07:00
parent 10cb65fc9c
commit 0e4b1da9a1
18 changed files with 209 additions and 150 deletions

View file

@ -74,6 +74,8 @@ void MobileShellPlugin::registerTypes(const char *uri)
// /volumeosd
qmlRegisterType(resolvePath("volumeosd/VolumeOSD.qml"), uri, 1, 0, "VolumeOSD");
qmlRegisterType(resolvePath("volumeosd/VolumeOSDProvider.qml"), uri, 1, 0, "VolumeOSDProvider");
qmlRegisterSingletonType(resolvePath("volumeosd/VolumeOSDProviderLoader.qml"), uri, 1, 0, "VolumeOSDProviderLoader");
// /widgets
qmlRegisterType(resolvePath("widgets/krunner/KRunnerWidget.qml"), uri, 1, 0, "KRunnerWidget");

View file

@ -6,6 +6,8 @@ import QtQuick
import org.kde.plasma.private.volume
QtObject {
id: root
property SinkModel paSinkModel: SinkModel {}
// whether the audio icon should be visible in the status bar
@ -25,6 +27,9 @@ QtObject {
// step that increments when adjusting the volume
readonly property int volumeStep: Math.round(5 * PulseAudio.NormalVolume / 100.0)
// The current audio volume (updated by connecting to sinks)
property int volumeValue
function isDummyOutput(output) {
return output && output.name === dummyOutputName;
}
@ -87,4 +92,33 @@ QtObject {
}
return icon;
}
// emitted when the volume changed, but not due to sink switching
signal volumeChanged()
property var updateVolume: Connections {
target: root.paSinkModel ? (root.paSinkModel.preferredSink ? root.paSinkModel.preferredSink : null) : null
enabled: target !== null
function onVolumeChanged() {
root.volumeValue = root.volumePercent(root.paSinkModel.preferredSink.volume, root.maxVolumeValue);
root.volumeChanged();
}
function onMutedChanged() {
root.volumeValue = root.paSinkModel.preferredSink.muted ? 0 : root.volumePercent(root.paSinkModel.preferredSink.volume, root.maxVolumeValue);
root.volumeChanged();
}
}
property var updateVolumeOnSinkChange: Connections {
target: root.paSinkModel ? root.paSinkModel : null
enabled: target !== null
function onPreferredSinkChanged() {
if (root.paSinkModel.preferredSink) {
root.volumeValue = root.volumePercent(root.paSinkModel.preferredSink.volume, root.maxVolumeValue);
}
}
}
}

View file

@ -22,7 +22,7 @@ import org.kde.plasma.private.mobileshell.state 1.0 as MobileShellState
import "../dataproviders" as DataProviders
Window {
NanoShell.FullScreenOverlay {
id: window
required property int volume

View file

@ -0,0 +1,70 @@
/*
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.private.volume 0.1 as VolumeLib
import org.kde.plasma.private.mobileshell.state as MobileShellState
import "../dataproviders" as DataProviders
/**
* This imports the volume OSD and also sets up keyboard/hardware button bindings.
*/
QtObject {
id: component
function showVolumeOverlay() {
osd.showOverlay();
}
property var audioInfo: DataProviders.AudioInfo {
onVolumeChanged: {
component.osd.showOverlay();
}
}
property var apiListener: Connections {
target: MobileShellState.ShellDBusClient
function onShowVolumeOSDRequested() {
component.showVolumeOverlay();
}
}
property var osd: VolumeOSD {
volume: component.audioInfo.volumeValue
}
property var actionCollection: VolumeLib.GlobalActionCollection {
name: "kmix"
displayName: i18n("Audio")
VolumeLib.GlobalAction {
objectName: "increase_volume"
text: i18n("Increase Volume")
shortcut: Qt.Key_VolumeUp
onTriggered: component.audioInfo.increaseVolume()
}
VolumeLib.GlobalAction {
objectName: "decrease_volume"
text: i18n("Decrease Volume")
shortcut: Qt.Key_VolumeDown
onTriggered: component.audioInfo.decreaseVolume()
}
VolumeLib.GlobalAction {
objectName: "mute"
text: i18n("Mute")
shortcut: Qt.Key_VolumeMute
onTriggered: component.audioInfo.muteVolume()
}
}
}

View file

@ -0,0 +1,22 @@
// SPDX-FileCopyrightText: 2023 Devin Lin <devin@kde.org>
// SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
import QtQuick
pragma Singleton
/**
* This wraps the VolumeOSDProvider component so that we can avoid loading side
* effects from imports (since this is a singleton and initialized immediately on import).
*/
Loader {
id: root
source: "qrc:/org/kde/plasma/private/mobileshell/qml/volumeosd/VolumeOSDProvider.qml"
// WARNING: only call this load from within the plasmashell process, because
// multiple bindings of the shortcut may break it entirely (hardware volume keys)
function load() {
root.active = true;
}
}

View file

@ -53,6 +53,8 @@
<file>qml/volumeosd/PopupCard.qml</file>
<file>qml/volumeosd/StreamListItem.qml</file>
<file>qml/volumeosd/VolumeOSD.qml</file>
<file>qml/volumeosd/VolumeOSDProvider.qml</file>
<file>qml/volumeosd/VolumeOSDProviderLoader.qml</file>
<file>qml/statusbar/indicators/BatteryIndicator.qml</file>
<file>qml/statusbar/indicators/BluetoothIndicator.qml</file>

View file

@ -22,7 +22,6 @@ qt_add_dbus_interface(mobileshellstateplugin_SRCS ${CMAKE_CURRENT_BINARY_DIR}/or
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.plasmashell.Mobile.xml DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR})
qt_add_resources(RESOURCES resources.qrc)
add_library(mobileshellstateplugin SHARED ${mobileshellstateplugin_SRCS} ${RESOURCES})
target_link_libraries(mobileshellstateplugin

View file

@ -17,11 +17,10 @@ void MobileShellStatePlugin::registerTypes(const char *uri)
{
Q_ASSERT(QLatin1String(uri) == QLatin1String("org.kde.plasma.private.mobileshell.state"));
qmlRegisterType<ShellDBusObject>(uri, 1, 0, "ShellDBusObject");
qmlRegisterSingletonType<ShellDBusClient>(uri, 1, 0, "ShellDBusClient", [](QQmlEngine *, QJSEngine *) -> QObject * {
return ShellDBusClient::self();
});
// /
qmlRegisterSingletonType(resolvePath("AudioProvider.qml"), uri, 1, 0, "AudioProvider");
qmlRegisterSingletonType<ShellDBusObject>(uri, 1, 0, "ShellDBusObject", [](QQmlEngine *, QJSEngine *) -> QObject * {
return ShellDBusObject::self();
});
}

View file

@ -1,98 +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
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()
}
}
}
}

View file

@ -1,10 +0,0 @@
<!--
- Copyright 2022 Devin Lin <devin@kde.org>
- SPDX-License-Identifier: GPL-2.0-or-later
-->
<RCC>
<qresource prefix="/org/kde/plasma/private/mobileshell/state/">
<file>qml/AudioProvider.qml</file>
</qresource>
</RCC>

View file

@ -44,6 +44,7 @@ void ShellDBusClient::connectSignals()
connect(m_interface, &OrgKdePlasmashellInterface::closeAppLaunchAnimationRequested, this, &ShellDBusClient::closeAppLaunchAnimationRequested);
connect(m_interface, &OrgKdePlasmashellInterface::openHomeScreenRequested, this, &ShellDBusClient::openHomeScreenRequested);
connect(m_interface, &OrgKdePlasmashellInterface::resetHomeScreenPositionRequested, this, &ShellDBusClient::resetHomeScreenPositionRequested);
connect(m_interface, &OrgKdePlasmashellInterface::showVolumeOSDRequested, this, &ShellDBusClient::showVolumeOSDRequested);
updateIsActionDrawerOpen();
updateDoNotDisturb();
@ -99,6 +100,11 @@ void ShellDBusClient::resetHomeScreenPosition()
m_interface->resetHomeScreenPosition();
}
void ShellDBusClient::showVolumeOSD()
{
m_interface->showVolumeOSD();
}
void ShellDBusClient::updateDoNotDisturb()
{
m_doNotDisturb = m_interface->doNotDisturb();

View file

@ -33,6 +33,7 @@ public:
Q_INVOKABLE void openHomeScreen();
Q_INVOKABLE void resetHomeScreenPosition();
Q_INVOKABLE void showVolumeOSD();
Q_SIGNALS:
void isActionDrawerOpenChanged();
@ -43,6 +44,7 @@ Q_SIGNALS:
void closeAppLaunchAnimationRequested();
void openHomeScreenRequested();
void resetHomeScreenPositionRequested();
void showVolumeOSDRequested();
private Q_SLOTS:
void updateDoNotDisturb();

View file

@ -9,8 +9,21 @@
ShellDBusObject::ShellDBusObject(QObject *parent)
: QObject{parent}
{
new PlasmashellAdaptor{this};
QDBusConnection::sessionBus().registerObject(QStringLiteral("/Mobile"), this);
}
ShellDBusObject *ShellDBusObject::self()
{
static ShellDBusObject *instance = new ShellDBusObject;
return instance;
}
void ShellDBusObject::registerObject()
{
if (!m_initialized) {
new PlasmashellAdaptor{this};
QDBusConnection::sessionBus().registerObject(QStringLiteral("/Mobile"), this);
m_initialized = true;
}
}
bool ShellDBusObject::doNotDisturb()
@ -68,3 +81,8 @@ void ShellDBusObject::resetHomeScreenPosition()
{
Q_EMIT resetHomeScreenPositionRequested();
}
void ShellDBusObject::showVolumeOSD()
{
Q_EMIT showVolumeOSDRequested();
}

View file

@ -13,6 +13,10 @@ class ShellDBusObject : public QObject
public:
ShellDBusObject(QObject *parent = nullptr);
static ShellDBusObject *self();
// called by QML
Q_INVOKABLE void registerObject();
Q_SIGNALS:
Q_SCRIPTABLE void doNotDisturbChanged();
@ -23,11 +27,13 @@ Q_SIGNALS:
Q_SCRIPTABLE void closeAppLaunchAnimationRequested();
Q_SCRIPTABLE void openHomeScreenRequested();
Q_SCRIPTABLE void resetHomeScreenPositionRequested();
Q_SCRIPTABLE void showVolumeOSDRequested();
public Q_SLOTS:
Q_SCRIPTABLE bool doNotDisturb();
Q_SCRIPTABLE void setDoNotDisturb(bool value);
// TODO: Account for multiple action drawers?
Q_SCRIPTABLE bool isActionDrawerOpen();
Q_SCRIPTABLE void setIsActionDrawerOpen(bool value);
@ -39,8 +45,11 @@ public Q_SLOTS:
Q_SCRIPTABLE void openHomeScreen();
Q_SCRIPTABLE void resetHomeScreenPosition();
Q_SCRIPTABLE void showVolumeOSD();
private:
bool m_initialized = false;
bool m_doNotDisturb = false;
bool m_isActionDrawerOpen = false;
};

View file

@ -116,7 +116,7 @@ MobileShell.HomeScreen {
Connections {
target: Folio.ApplicationListModel
function onLaunchError(msg) {
MobileShellState.Shell.closeAppLaunchAnimation()
MobileShellState.ShellDBusClient.closeAppLaunchAnimation()
}
}
}

View file

@ -52,7 +52,6 @@ Item {
}
function onCloseActionDrawerRequested() {
console.log('action drawer close');
drawer.actionDrawer.close();
}
@ -70,10 +69,14 @@ Item {
}
//END API implementation
Component.onCompleted: {
// we want to bind global volume shortcuts here
MobileShellState.AudioProvider.bindShortcuts = true;
// register dbus
MobileShellState.ShellDBusObject.registerObject();
// HACK: we need to initialize the DBus server somewhere, it might as well be here...
// initialize the volume osd, and volume keys
MobileShell.VolumeOSDProviderLoader.load();
}
// top panel component
@ -86,6 +89,7 @@ Item {
backgroundColor: !root.showingApp ? "transparent" : root.backgroundColor
}
// swiping area for swipe-down drawer
MobileShell.ActionDrawerOpenSurface {
id: swipeArea
actionDrawer: drawer.actionDrawer

View file

@ -10,10 +10,13 @@ import org.kde.plasma.private.mobileshell.quicksettingsplugin as QS
QS.QuickSetting {
text: i18n("Sound")
icon: "audio-speakers-symbolic"
status: i18n("%1%", MobileShellState.AudioProvider.volumeValue)
status: i18n("%1%", audioInfo.volumeValue)
enabled: false
settingsCommand: "plasma-open-settings kcm_pulseaudio"
property var audioInfo: MobileShell.AudioInfo {}
function toggle() {
MobileShellState.AudioProvider.showVolumeOverlay()
MobileShellState.ShellDBusClient.showVolumeOSD()
}
}

View file

@ -9,9 +9,9 @@ import QtQuick 2.15
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.shell 2.0 as Shell
import org.kde.kquickcontrolsaddons 2.0
import org.kde.kirigami 2.20 as Kirigami
import org.kde.plasma.private.mobileshell.state as MobileShellState
Rectangle {
id: root
@ -29,29 +29,6 @@ Rectangle {
}
}
Loader {
id: widgetExplorerStack
z: 99
asynchronous: true
y: containment ? containment.availableScreenRect.y : 0
height: containment ? containment.availableScreenRect.height : parent.height
width: parent.width
onLoaded: {
if (widgetExplorerStack.item) {
item.closed.connect(function() {
widgetExplorerStack.source = ""
});
item.topPanelHeight = containment.availableScreenRect.y
item.bottomPanelHeight = root.height - (containment.availableScreenRect.height + containment.availableScreenRect.y)
item.leftPanelWidth = containment.availableScreenRect.x
item.rightPanelWidth = root.width - (containment.availableScreenRect.width + containment.availableScreenRect.x)
}
}
}
onContainmentChanged: {
if (containment == null) {
return;
@ -62,10 +39,8 @@ Rectangle {
containment.anchors.fill = root;
}
// Load shell dbus object
MobileShellState.ShellDBusObject {}
// This is taken from plasma-desktop's shell package, try to keep it in sync
// Handles taking accent color from wallpaper
Loader {
id: wallpaperColors
@ -108,4 +83,26 @@ Rectangle {
onLoaded: item.update()
}
Loader {
id: widgetExplorerStack
z: 99
asynchronous: true
y: containment ? containment.availableScreenRect.y : 0
height: containment ? containment.availableScreenRect.height : parent.height
width: parent.width
onLoaded: {
if (widgetExplorerStack.item) {
item.closed.connect(function() {
widgetExplorerStack.source = ""
});
item.topPanelHeight = containment.availableScreenRect.y
item.bottomPanelHeight = root.height - (containment.availableScreenRect.height + containment.availableScreenRect.y)
item.leftPanelWidth = containment.availableScreenRect.x
item.rightPanelWidth = root.width - (containment.availableScreenRect.width + containment.availableScreenRect.x)
}
}
}
}