diff --git a/CMakeLists.txt b/CMakeLists.txt
index 862014b3..723c7bf9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -136,6 +136,7 @@ plasma_install_package(shell org.kde.plasma.mobileshell shells)
add_subdirectory(bin)
add_subdirectory(components)
add_subdirectory(containments)
+add_subdirectory(devices)
add_subdirectory(quicksettings)
add_subdirectory(kcms)
add_subdirectory(kded)
@@ -146,6 +147,7 @@ add_subdirectory(layout-templates)
if(BUILD_TESTING)
add_subdirectory(tests)
endif()
+
install(FILES org.kde.plasma.mobileshell.metainfo.xml DESTINATION ${KDE_INSTALL_METAINFODIR})
find_program(PlasmaOpenSettings plasma-open-settings)
diff --git a/README.md b/README.md
index eee6da02..13a5c3a9 100644
--- a/README.md
+++ b/README.md
@@ -61,6 +61,10 @@ Useful options:
QT_QPA_PLATFORM=wayland dbus-run-session kwin_wayland --xwayland "plasmashell -p org.kde.plasma.mobileshell" --output-count 2 --width 360 --height 720
```
+### Device specific configuration
+
+See [/devices/README.md] for more details on setting device specific configuration (ex. notches, screen curves).
+
---
diff --git a/components/mobileshell/qml/components/Constants.qml b/components/mobileshell/qml/components/Constants.qml
index 8a76a038..1255f305 100644
--- a/components/mobileshell/qml/components/Constants.qml
+++ b/components/mobileshell/qml/components/Constants.qml
@@ -5,6 +5,7 @@ import QtQuick
import org.kde.kirigami 2.20 as Kirigami
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
+import org.kde.plasma.private.mobileshell.state as MobileShellState
// NOTE: This is a singleton in the mobileshell library, so we need to be careful to
// make this load as fast as possible (since it may be loaded in other processes ex. lockscreen).
@@ -12,8 +13,30 @@ import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
pragma Singleton
QtObject {
- readonly property real topPanelHeight: Math.round(Kirigami.Units.gridUnit * ShellSettings.Settings.statusBarScaleFactor / 2) * 2 + Kirigami.Units.smallSpacing
- readonly property real navigationPanelThickness: ShellSettings.Settings.navigationPanelEnabled ? Kirigami.Units.gridUnit * 2 : 0
+ id: root
+ readonly property var panelSettings: MobileShellState.PanelSettingsDBusClient {
+ screenName: Screen.name
+ }
+
+ readonly property real defaultTopPanelHeight: Math.round(Kirigami.Units.gridUnit * ShellSettings.Settings.statusBarScaleFactor / 2) * 2 + Kirigami.Units.smallSpacing
+
+ readonly property real topPanelHeight: {
+ if (root.panelSettings.statusBarHeight <= 0) {
+ return defaultTopPanelHeight;
+ }
+ return root.panelSettings.statusBarHeight;
+ }
+
+ readonly property real defaultNavigationPanelThickness: Kirigami.Units.gridUnit * 2
+
+ readonly property real navigationPanelThickness: {
+ if (!ShellSettings.Settings.navigationPanelEnabled) {
+ return 0;
+ } else if (root.panelSettings.navigationPanelHeight <= 0) {
+ return defaultNavigationPanelThickness;
+ }
+ return root.panelSettings.navigationPanelHeight;
+ }
function navigationPanelOnSide(screenWidth: real, screenHeight: real): bool {
// TODO: we have this disabled for now, we might consider just removing this feature entirely due to it causing several issues:
diff --git a/components/mobileshell/qml/navigationpanel/NavigationPanel.qml b/components/mobileshell/qml/navigationpanel/NavigationPanel.qml
index d69d8488..77278eac 100644
--- a/components/mobileshell/qml/navigationpanel/NavigationPanel.qml
+++ b/components/mobileshell/qml/navigationpanel/NavigationPanel.qml
@@ -29,11 +29,14 @@ Item {
property NavigationPanelAction leftCornerAction
property NavigationPanelAction rightCornerAction
+ property real leftPadding: 0
+ property real rightPadding: 0
+
property bool isVertical: false
// drop shadow for icons
MultiEffect {
- anchors.fill: root
+ anchors.fill: icons
visible: shadow
source: icons
blurMax: 16
@@ -42,25 +45,25 @@ Item {
shadowOpacity: 0.8
}
+ // background colour
+ Rectangle {
+ anchors.fill: parent
+ color: root.backgroundColor
+ }
+
Item {
id: icons
anchors.fill: parent
property real buttonLength: 0
- // background colour
- Rectangle {
- anchors.fill: parent
- color: root.backgroundColor
- }
-
NavigationPanelButton {
id: leftCornerButton
visible: root.leftCornerAction.visible
Kirigami.Theme.colorSet: root.foregroundColorGroup
Kirigami.Theme.inherit: false
enabled: root.leftCornerAction.enabled
- iconSizeFactor: root.leftCornerAction.iconSizeFactor
+ shrinkSize: root.leftCornerAction.shrinkSize
iconSource: root.leftCornerAction.iconSource
onClicked: {
if (enabled) {
@@ -76,7 +79,7 @@ Item {
Kirigami.Theme.colorSet: root.foregroundColorGroup
Kirigami.Theme.inherit: false
enabled: root.leftAction.enabled
- iconSizeFactor: root.leftAction.iconSizeFactor
+ shrinkSize: root.leftAction.shrinkSize
iconSource: root.leftAction.iconSource
onClicked: {
if (enabled) {
@@ -92,7 +95,7 @@ Item {
Kirigami.Theme.colorSet: root.foregroundColorGroup
Kirigami.Theme.inherit: false
enabled: root.middleAction.enabled
- iconSizeFactor: root.middleAction.iconSizeFactor
+ shrinkSize: root.middleAction.shrinkSize
iconSource: root.middleAction.iconSource
onClicked: {
if (enabled) {
@@ -107,7 +110,7 @@ Item {
Kirigami.Theme.colorSet: root.foregroundColorGroup
Kirigami.Theme.inherit: false
enabled: root.rightAction.enabled
- iconSizeFactor: root.rightAction.iconSizeFactor
+ shrinkSize: root.rightAction.shrinkSize
iconSource: root.rightAction.iconSource
onClicked: {
if (enabled) {
@@ -122,7 +125,7 @@ Item {
Kirigami.Theme.colorSet: root.foregroundColorGroup
Kirigami.Theme.inherit: false
enabled: root.rightCornerAction.enabled
- iconSizeFactor: root.rightCornerAction.iconSizeFactor
+ shrinkSize: root.rightCornerAction.shrinkSize
iconSource: root.rightCornerAction.iconSource
onClicked: {
if (enabled) {
@@ -138,6 +141,10 @@ Item {
when: root.isVertical
PropertyChanges {
target: icons
+ anchors {
+ topMargin: root.leftPadding
+ bottomMargin: root.rightPadding
+ }
buttonLength: Math.min(Kirigami.Units.gridUnit * 10, icons.height * 0.7 / 3)
}
AnchorChanges {
@@ -198,6 +205,10 @@ Item {
when: !root.isVertical
PropertyChanges {
target: icons
+ anchors {
+ leftMargin: root.leftPadding
+ rightMargin: root.rightPadding
+ }
buttonLength: Math.min(Kirigami.Units.gridUnit * 8, icons.width * 0.7 / 3)
}
AnchorChanges {
diff --git a/components/mobileshell/qml/navigationpanel/NavigationPanelAction.qml b/components/mobileshell/qml/navigationpanel/NavigationPanelAction.qml
index dc66f541..1fa3a96d 100644
--- a/components/mobileshell/qml/navigationpanel/NavigationPanelAction.qml
+++ b/components/mobileshell/qml/navigationpanel/NavigationPanelAction.qml
@@ -10,7 +10,7 @@ QtObject {
property bool enabled
property bool visible: true
property string iconSource
- property real iconSizeFactor
+ property real shrinkSize
signal triggered()
}
diff --git a/components/mobileshell/qml/navigationpanel/NavigationPanelButton.qml b/components/mobileshell/qml/navigationpanel/NavigationPanelButton.qml
index 0acc4771..0b1e9e2e 100644
--- a/components/mobileshell/qml/navigationpanel/NavigationPanelButton.qml
+++ b/components/mobileshell/qml/navigationpanel/NavigationPanelButton.qml
@@ -20,7 +20,7 @@ Controls.AbstractButton {
width: Math.min(parent.width, parent.height)
height: width
- property double iconSizeFactor: 1
+ property int shrinkSize: 0
property alias iconSource: icon.source
MobileShell.HapticsEffect {
@@ -79,9 +79,11 @@ Controls.AbstractButton {
Kirigami.Theme.colorSet: button.Kirigami.Theme.colorSet
readonly property real side: Math.min(button.width, button.height)
- anchors {
- fill: parent
- margins: Math.round((side - side * iconSizeFactor * 0.6) / 2)
- }
+ anchors.centerIn: parent
+
+ implicitHeight: Kirigami.Units.iconSizes.smallMedium - shrinkSize
+ implicitWidth: Kirigami.Units.iconSizes.smallMedium - shrinkSize
+ width: implicitWidth
+ height: implicitHeight
}
}
diff --git a/components/mobileshell/qml/statusbar/StatusBar.qml b/components/mobileshell/qml/statusbar/StatusBar.qml
index fec1ed0d..0d6d1fae 100644
--- a/components/mobileshell/qml/statusbar/StatusBar.qml
+++ b/components/mobileshell/qml/statusbar/StatusBar.qml
@@ -21,6 +21,7 @@ import org.kde.plasma.components 3.0 as PlasmaComponents
import org.kde.kitemmodels as KItemModels
import org.kde.plasma.private.mobileshell as MobileShell
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
+import org.kde.plasma.private.mobileshell.state as MobileShellState
Item {
id: root
@@ -70,6 +71,11 @@ Item {
sourceComponent: SystemTray.StatusNotifierModel { }
}
+ MobileShellState.PanelSettingsDBusClient {
+ id: panelSettings
+ screenName: Screen.name
+ }
+
// drop shadow for icons
MultiEffect {
anchors.fill: control
@@ -87,8 +93,8 @@ Item {
z: 1
topPadding: Kirigami.Units.smallSpacing
bottomPadding: Kirigami.Units.smallSpacing
- rightPadding: Kirigami.Units.smallSpacing * 3
- leftPadding: Kirigami.Units.smallSpacing * 3
+ rightPadding: Kirigami.Units.smallSpacing * 3 + panelSettings.statusBarLeftPadding
+ leftPadding: Kirigami.Units.smallSpacing * 3 + + panelSettings.statusBarRightPadding
anchors.fill: parent
background: Rectangle {
@@ -101,9 +107,10 @@ Item {
RowLayout {
id: mainRow
- readonly property real rowHeight: MobileShell.Constants.topPanelHeight - Kirigami.Units.smallSpacing * 2
+ readonly property real rowHeight: MobileShell.Constants.defaultTopPanelHeight - Kirigami.Units.smallSpacing * 2
Layout.fillWidth: true
+ Layout.alignment: Qt.AlignVCenter
Layout.preferredHeight: rowHeight
spacing: 0
diff --git a/components/mobileshell/shellutil.cpp b/components/mobileshell/shellutil.cpp
index 000e4050..a50a9067 100644
--- a/components/mobileshell/shellutil.cpp
+++ b/components/mobileshell/shellutil.cpp
@@ -29,9 +29,10 @@
#define FORMAT24H "HH:mm:ss"
-
-ShellUtil::ShellUtil(QObject *parent) : QObject{parent}, m_localeConfig {
- KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::SimpleConfig) } {
+ShellUtil::ShellUtil(QObject *parent)
+ : QObject{parent}
+ , m_localeConfig{KSharedConfig::openConfig(QStringLiteral("kdeglobals"))}
+{
}
void ShellUtil::stackItemBefore(QQuickItem *item1, QQuickItem *item2)
@@ -119,7 +120,7 @@ void ShellUtil::setInputRegion(QWindow *window, const QRect ®ion) {
qWarning() << "Failed to retrieve Wayland window handle.";
return;
}
-
+
auto waylandDisplay = dynamic_cast(waylandWindow->display());
if (!waylandDisplay) {
qWarning() << "Failed to retrieve Wayland display.";
diff --git a/components/mobileshellstate/CMakeLists.txt b/components/mobileshellstate/CMakeLists.txt
index 32bd87a0..abd53c9d 100644
--- a/components/mobileshellstate/CMakeLists.txt
+++ b/components/mobileshellstate/CMakeLists.txt
@@ -8,20 +8,46 @@ set(mobileshellstateplugin_SRCS
startupfeedbackmodel.cpp
windowlistener.cpp
volumeosdlistener.cpp
+ panelsettingsdbusobjectmanager.cpp
+ panelsettingsdbusclient.cpp
)
+# Add shell dbus API
qt_generate_dbus_interface(
${CMAKE_CURRENT_SOURCE_DIR}/shelldbusobject.h
org.kde.plasmashell.Mobile.xml
OPTIONS -s -m -P
)
-
qt_add_dbus_adaptor(mobileshellstateplugin_SRCS ${CMAKE_CURRENT_BINARY_DIR}/org.kde.plasmashell.Mobile.xml
${CMAKE_CURRENT_SOURCE_DIR}/shelldbusobject.h ShellDBusObject)
qt_add_dbus_interface(mobileshellstateplugin_SRCS ${CMAKE_CURRENT_BINARY_DIR}/org.kde.plasmashell.Mobile.xml plasmashellmobileinterface)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.plasmashell.Mobile.xml DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR})
+# ---
+# Add shell panels dbus API
+qt_generate_dbus_interface(
+ ${CMAKE_CURRENT_SOURCE_DIR}/panelsettingsdbusobjectmanager.h
+ org.kde.plasmashell.Mobile.Panels.xml
+ OPTIONS -s -m -P
+)
+qt_add_dbus_adaptor(mobileshellstateplugin_SRCS
+ ${CMAKE_CURRENT_BINARY_DIR}/org.kde.plasmashell.Mobile.Panels.xml
+ ${CMAKE_CURRENT_SOURCE_DIR}/panelsettingsdbusobjectmanager.h PanelSettingsDBusObject
+)
+set_source_files_properties(
+ ${CMAKE_CURRENT_BINARY_DIR}/org.kde.plasmashell.Mobile.Panels.xml
+ PROPERTIES
+ CLASSNAME "OrgKdePlasmashellMobilePanelsInterface"
+)
+qt_add_dbus_interface(
+ mobileshellstateplugin_SRCS
+ ${CMAKE_CURRENT_BINARY_DIR}/org.kde.plasmashell.Mobile.Panels.xml
+ plasmashellmobilepanelsinterface
+)
+
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.plasmashell.Mobile.Panels.xml DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR})
+# ---
ecm_add_qml_module(mobileshellstateplugin URI org.kde.plasma.private.mobileshell.state GENERATE_PLUGIN_SOURCE)
@@ -40,6 +66,7 @@ target_link_libraries(mobileshellstateplugin
Plasma::KWaylandClient
KF6::I18n
KF6::Notifications
+ KF6::Screen
Plasma::PlasmaQuick
)
diff --git a/components/mobileshellstate/panelsettingsdbusclient.cpp b/components/mobileshellstate/panelsettingsdbusclient.cpp
new file mode 100644
index 00000000..4ad9a59c
--- /dev/null
+++ b/components/mobileshellstate/panelsettingsdbusclient.cpp
@@ -0,0 +1,252 @@
+// SPDX-FileCopyrightText: 2025 Devin Lin
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "panelsettingsdbusclient.h"
+
+#include
+
+PanelSettingsDBusClient::PanelSettingsDBusClient(QObject *parent)
+ : QObject{parent}
+ , m_interface{nullptr}
+ , m_connected{false}
+{
+}
+
+void PanelSettingsDBusClient::connectToDBus()
+{
+ if (m_interface) {
+ return;
+ }
+ m_interface = new OrgKdePlasmashellMobilePanelsInterface{QStringLiteral("org.kde.plasmashell"),
+ QStringLiteral("/Mobile/Panels/") + m_screenName.replace("-", ""),
+ QDBusConnection::sessionBus(),
+ this};
+
+ // Check if the service is already running
+ if (QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.kde.plasmashell"))) {
+ m_connected = true;
+ if (m_interface->isValid()) {
+ connectSignals();
+ }
+ }
+
+ connect(QDBusConnection::sessionBus().interface(),
+ &QDBusConnectionInterface::serviceOwnerChanged,
+ this,
+ [this](const QString &service, const QString &oldOwner, const QString &newOwner) {
+ Q_UNUSED(oldOwner);
+ if (service == QStringLiteral("org.kde.plasmashell")) {
+ if (!newOwner.isEmpty() && !m_connected) {
+ m_connected = true;
+ if (m_interface->isValid()) {
+ connectSignals();
+ }
+ } else if (newOwner.isEmpty() && m_connected) {
+ m_connected = false;
+ }
+ }
+ });
+}
+
+QString PanelSettingsDBusClient::screenName() const
+{
+ return m_screenName;
+}
+
+void PanelSettingsDBusClient::setScreenName(const QString &screenName)
+{
+ if (screenName == m_screenName) {
+ return;
+ }
+ m_screenName = screenName;
+ Q_EMIT screenNameChanged();
+
+ connectToDBus();
+}
+
+void PanelSettingsDBusClient::connectSignals()
+{
+ connect(m_interface, &OrgKdePlasmashellMobilePanelsInterface::statusBarHeightChanged, this, &PanelSettingsDBusClient::updateStatusBarHeight);
+ connect(m_interface, &OrgKdePlasmashellMobilePanelsInterface::statusBarLeftPaddingChanged, this, &PanelSettingsDBusClient::updateStatusBarLeftPadding);
+ connect(m_interface, &OrgKdePlasmashellMobilePanelsInterface::statusBarRightPaddingChanged, this, &PanelSettingsDBusClient::updateStatusBarRightPadding);
+ connect(m_interface, &OrgKdePlasmashellMobilePanelsInterface::statusBarCenterSpacingChanged, this, &PanelSettingsDBusClient::updateStatusBarCenterSpacing);
+ connect(m_interface, &OrgKdePlasmashellMobilePanelsInterface::navigationPanelHeightChanged, this, &PanelSettingsDBusClient::updateNavigationPanelHeight);
+ connect(m_interface,
+ &OrgKdePlasmashellMobilePanelsInterface::navigationPanelLeftPaddingChanged,
+ this,
+ &PanelSettingsDBusClient::updateNavigationPanelLeftPadding);
+ connect(m_interface,
+ &OrgKdePlasmashellMobilePanelsInterface::navigationPanelRightPaddingChanged,
+ this,
+ &PanelSettingsDBusClient::updateNavigationPanelRightPadding);
+
+ // Initial state fetch
+ updateStatusBarHeight();
+ updateStatusBarLeftPadding();
+ updateStatusBarRightPadding();
+ updateStatusBarCenterSpacing();
+ updateNavigationPanelHeight();
+ updateNavigationPanelLeftPadding();
+ updateNavigationPanelRightPadding();
+}
+
+qreal PanelSettingsDBusClient::statusBarHeight() const
+{
+ return m_statusBarHeight;
+}
+
+qreal PanelSettingsDBusClient::statusBarLeftPadding() const
+{
+ return m_statusBarLeftPadding;
+}
+
+qreal PanelSettingsDBusClient::statusBarRightPadding() const
+{
+ return m_statusBarRightPadding;
+}
+
+qreal PanelSettingsDBusClient::statusBarCenterSpacing() const
+{
+ return m_statusBarCenterSpacing;
+}
+
+qreal PanelSettingsDBusClient::navigationPanelHeight() const
+{
+ return m_navigationPanelHeight;
+}
+
+qreal PanelSettingsDBusClient::navigationPanelLeftPadding() const
+{
+ return m_navigationPanelLeftPadding;
+}
+
+qreal PanelSettingsDBusClient::navigationPanelRightPadding() const
+{
+ return m_navigationPanelRightPadding;
+}
+
+void PanelSettingsDBusClient::updateStatusBarHeight()
+{
+ auto reply = m_interface->statusBarHeight();
+ auto watcher = new QDBusPendingCallWatcher(reply, this);
+
+ QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](auto watcher) {
+ QDBusPendingReply reply = *watcher;
+ qreal statusBarHeight = reply.argumentAt<0>();
+
+ if (statusBarHeight != m_statusBarHeight) {
+ m_statusBarHeight = statusBarHeight;
+ Q_EMIT statusBarHeightChanged();
+ }
+
+ watcher->deleteLater();
+ });
+}
+
+void PanelSettingsDBusClient::updateStatusBarLeftPadding()
+{
+ auto reply = m_interface->statusBarLeftPadding();
+ auto watcher = new QDBusPendingCallWatcher(reply, this);
+
+ QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](auto watcher) {
+ QDBusPendingReply reply = *watcher;
+ qreal statusBarLeftPadding = reply.argumentAt<0>();
+
+ if (statusBarLeftPadding != m_statusBarLeftPadding) {
+ m_statusBarLeftPadding = statusBarLeftPadding;
+ Q_EMIT statusBarLeftPaddingChanged();
+ }
+
+ watcher->deleteLater();
+ });
+}
+
+void PanelSettingsDBusClient::updateStatusBarRightPadding()
+{
+ auto reply = m_interface->statusBarRightPadding();
+ auto watcher = new QDBusPendingCallWatcher(reply, this);
+
+ QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](auto watcher) {
+ QDBusPendingReply reply = *watcher;
+ qreal statusBarRightPadding = reply.argumentAt<0>();
+
+ if (statusBarRightPadding != m_statusBarRightPadding) {
+ m_statusBarRightPadding = statusBarRightPadding;
+ Q_EMIT statusBarRightPaddingChanged();
+ }
+
+ watcher->deleteLater();
+ });
+}
+
+void PanelSettingsDBusClient::updateStatusBarCenterSpacing()
+{
+ auto reply = m_interface->statusBarCenterSpacing();
+ auto watcher = new QDBusPendingCallWatcher(reply, this);
+
+ QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](auto watcher) {
+ QDBusPendingReply reply = *watcher;
+ qreal statusBarCenterSpacing = reply.argumentAt<0>();
+
+ if (statusBarCenterSpacing != m_statusBarCenterSpacing) {
+ m_statusBarCenterSpacing = statusBarCenterSpacing;
+ Q_EMIT statusBarCenterSpacingChanged();
+ }
+
+ watcher->deleteLater();
+ });
+}
+
+void PanelSettingsDBusClient::updateNavigationPanelHeight()
+{
+ auto reply = m_interface->navigationPanelHeight();
+ auto watcher = new QDBusPendingCallWatcher(reply, this);
+
+ QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](auto watcher) {
+ QDBusPendingReply reply = *watcher;
+ qreal navigationPanelHeight = reply.argumentAt<0>();
+
+ if (navigationPanelHeight != m_navigationPanelHeight) {
+ m_navigationPanelHeight = navigationPanelHeight;
+ Q_EMIT navigationPanelHeightChanged();
+ }
+
+ watcher->deleteLater();
+ });
+}
+
+void PanelSettingsDBusClient::updateNavigationPanelLeftPadding()
+{
+ auto reply = m_interface->navigationPanelLeftPadding();
+ auto watcher = new QDBusPendingCallWatcher(reply, this);
+
+ QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](auto watcher) {
+ QDBusPendingReply reply = *watcher;
+ qreal navigationPanelLeftPadding = reply.argumentAt<0>();
+
+ if (navigationPanelLeftPadding != m_navigationPanelLeftPadding) {
+ m_navigationPanelLeftPadding = navigationPanelLeftPadding;
+ Q_EMIT navigationPanelLeftPaddingChanged();
+ }
+
+ watcher->deleteLater();
+ });
+}
+
+void PanelSettingsDBusClient::updateNavigationPanelRightPadding()
+{
+ auto reply = m_interface->navigationPanelRightPadding();
+ auto watcher = new QDBusPendingCallWatcher(reply, this);
+
+ QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](auto watcher) {
+ QDBusPendingReply reply = *watcher;
+ qreal navigationPanelRightPadding = reply.argumentAt<0>();
+
+ if (navigationPanelRightPadding != m_navigationPanelRightPadding) {
+ m_navigationPanelRightPadding = navigationPanelRightPadding;
+ Q_EMIT navigationPanelRightPaddingChanged();
+ }
+
+ watcher->deleteLater();
+ });
+}
diff --git a/components/mobileshellstate/panelsettingsdbusclient.h b/components/mobileshellstate/panelsettingsdbusclient.h
new file mode 100644
index 00000000..d9eb8b3d
--- /dev/null
+++ b/components/mobileshellstate/panelsettingsdbusclient.h
@@ -0,0 +1,83 @@
+// SPDX-FileCopyrightText: 2025 Devin Lin
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "plasmashellmobilepanelsinterface.h"
+
+#include
+#include
+#include
+#include
+
+class PanelSettingsDBusClient : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+ // Client must set the screen they want to get details for
+ Q_PROPERTY(QString screenName READ screenName WRITE setScreenName NOTIFY screenNameChanged)
+
+ Q_PROPERTY(qreal statusBarHeight READ statusBarHeight NOTIFY statusBarHeightChanged)
+ Q_PROPERTY(qreal statusBarLeftPadding READ statusBarLeftPadding NOTIFY statusBarLeftPaddingChanged)
+ Q_PROPERTY(qreal statusBarRightPadding READ statusBarRightPadding NOTIFY statusBarRightPaddingChanged)
+ Q_PROPERTY(qreal statusBarCenterSpacing READ statusBarCenterSpacing NOTIFY statusBarCenterSpacingChanged)
+
+ Q_PROPERTY(qreal navigationPanelHeight READ navigationPanelHeight NOTIFY navigationPanelHeightChanged)
+ Q_PROPERTY(qreal navigationPanelLeftPadding READ navigationPanelLeftPadding NOTIFY navigationPanelLeftPaddingChanged)
+ Q_PROPERTY(qreal navigationPanelRightPadding READ navigationPanelRightPadding NOTIFY navigationPanelRightPaddingChanged)
+
+public:
+ explicit PanelSettingsDBusClient(QObject *parent = nullptr);
+
+ void connectToDBus();
+
+ QString screenName() const;
+ void setScreenName(const QString &screenName);
+
+ qreal statusBarHeight() const;
+ qreal statusBarLeftPadding() const;
+ qreal statusBarRightPadding() const;
+ qreal statusBarCenterSpacing() const;
+
+ qreal navigationPanelHeight() const;
+ qreal navigationPanelLeftPadding() const;
+ qreal navigationPanelRightPadding() const;
+
+Q_SIGNALS:
+ void screenNameChanged();
+ void statusBarHeightChanged();
+ void statusBarLeftPaddingChanged();
+ void statusBarRightPaddingChanged();
+ void statusBarCenterSpacingChanged();
+ void navigationPanelHeightChanged();
+ void navigationPanelLeftPaddingChanged();
+ void navigationPanelRightPaddingChanged();
+
+private Q_SLOTS:
+ void updateStatusBarHeight();
+ void updateStatusBarLeftPadding();
+ void updateStatusBarRightPadding();
+ void updateStatusBarCenterSpacing();
+ void updateNavigationPanelHeight();
+ void updateNavigationPanelLeftPadding();
+ void updateNavigationPanelRightPadding();
+
+private:
+ void connectSignals();
+
+ OrgKdePlasmashellMobilePanelsInterface *m_interface;
+ QDBusServiceWatcher *m_watcher;
+
+ QString m_screenName;
+
+ qreal m_statusBarHeight = -1;
+ qreal m_statusBarLeftPadding = 0;
+ qreal m_statusBarRightPadding = 0;
+ qreal m_statusBarCenterSpacing = 0;
+ qreal m_navigationPanelHeight = -1;
+ qreal m_navigationPanelLeftPadding = 0;
+ qreal m_navigationPanelRightPadding = 0;
+
+ bool m_connected = false;
+};
diff --git a/components/mobileshellstate/panelsettingsdbusobjectmanager.cpp b/components/mobileshellstate/panelsettingsdbusobjectmanager.cpp
new file mode 100644
index 00000000..7db52ce8
--- /dev/null
+++ b/components/mobileshellstate/panelsettingsdbusobjectmanager.cpp
@@ -0,0 +1,268 @@
+// SPDX-FileCopyrightText: 2025 Devin Lin
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "panelsettingsdbusobjectmanager.h"
+#include "panelsadaptor.h"
+
+#include
+#include
+#include
+
+using namespace Qt::Literals::StringLiterals;
+
+const QString CONFIG_FILE = u"plasmamobilerc"_s;
+const QString PANELS_CONFIG_GROUP = u"Panels"_s;
+
+// Orientations
+const QString TOP_CONFIG_GROUP = u"WhenOnTop"_s;
+const QString LEFT_CONFIG_GROUP = u"WhenOnLeft"_s;
+const QString RIGHT_CONFIG_GROUP = u"WhenOnRight"_s;
+const QString BOTTOM_CONFIG_GROUP = u"WhenOnBottom"_s;
+
+QString mapRotationToTopPosition(KScreen::Output::Rotation rotation)
+{
+ switch (rotation) {
+ case KScreen::Output::Rotation::Left:
+ return RIGHT_CONFIG_GROUP;
+ case KScreen::Output::Rotation::Inverted:
+ return BOTTOM_CONFIG_GROUP;
+ case KScreen::Output::Rotation::Right:
+ return LEFT_CONFIG_GROUP;
+ default:
+ return TOP_CONFIG_GROUP;
+ }
+}
+
+QString mapRotationToBottomPosition(KScreen::Output::Rotation rotation)
+{
+ switch (rotation) {
+ case KScreen::Output::Rotation::Left:
+ return LEFT_CONFIG_GROUP;
+ case KScreen::Output::Rotation::Inverted:
+ return TOP_CONFIG_GROUP;
+ case KScreen::Output::Rotation::Right:
+ return RIGHT_CONFIG_GROUP;
+ default:
+ return BOTTOM_CONFIG_GROUP;
+ }
+}
+
+PanelSettingsDBusObjectManager::PanelSettingsDBusObjectManager(QObject *parent)
+ : QObject{parent}
+{
+}
+
+void PanelSettingsDBusObjectManager::registerObjects()
+{
+ if (m_initialized) {
+ return;
+ }
+
+ m_initialized = true;
+
+ // Fetch kscreen config
+ connect(new KScreen::GetConfigOperation(), &KScreen::GetConfigOperation::finished, this, [this](auto *op) {
+ m_kscreenConfig = qobject_cast(op)->config();
+ if (!m_kscreenConfig) {
+ return;
+ }
+
+ KScreen::ConfigMonitor::instance()->addConfig(m_kscreenConfig);
+
+ // Listen to all new screens and create a new output
+ connect(m_kscreenConfig.data(), &KScreen::Config::outputAdded, this, [this](const auto &output) {
+ if (!output) {
+ return;
+ }
+
+ auto *obj = new PanelSettingsDBusObject{this};
+ obj->registerObject(output);
+ m_dbusObjects.push_back(obj);
+ });
+
+ // Remove the corresponding object when a screen is removed
+ connect(m_kscreenConfig.data(), &KScreen::Config::outputRemoved, this, [this](const auto outputId) {
+ for (int i = 0; i < m_dbusObjects.size(); ++i) {
+ if (m_dbusObjects[i]->outputId() == outputId) {
+ m_dbusObjects[i]->deleteLater();
+ m_dbusObjects.remove(i);
+ break;
+ }
+ }
+ });
+
+ // Add all current screens as dbus objects
+ for (KScreen::OutputPtr output : m_kscreenConfig->outputs()) {
+ if (!output) {
+ continue;
+ }
+
+ auto *obj = new PanelSettingsDBusObject{this};
+ obj->registerObject(output);
+ m_dbusObjects.push_back(obj);
+ }
+ });
+}
+
+PanelSettingsDBusObject::PanelSettingsDBusObject(QObject *parent)
+ : QObject{parent}
+ , m_config{KSharedConfig::openConfig(CONFIG_FILE)}
+{
+ // Listen to config changes and reload
+ m_configWatcher = KConfigWatcher::create(m_config);
+ connect(m_configWatcher.data(), &KConfigWatcher::configChanged, this, [this](const KConfigGroup &group, const QByteArrayList &names) -> void {
+ Q_UNUSED(names)
+ Q_UNUSED(group)
+ updateFields();
+ });
+}
+
+void PanelSettingsDBusObject::registerObject(KScreen::OutputPtr output)
+{
+ m_output = output;
+
+ if (!m_output) {
+ return;
+ }
+ m_outputId = m_output->id();
+ m_outputName = m_output->name();
+
+ // Listen to when the rotation or scale changes to refresh the fields
+ connect(m_output.data(), &KScreen::Output::rotationChanged, this, &PanelSettingsDBusObject::updateFields);
+ connect(m_output.data(), &KScreen::Output::scaleChanged, this, &PanelSettingsDBusObject::updateFields);
+ updateFields();
+
+ new PlasmashellMobilePanelsAdaptor{this};
+
+ // Register the screen DBus object
+ QString objectName = m_output->name().replace("-", ""); // DBus doesn't allow dashes
+ QDBusConnection::sessionBus().registerObject(QStringLiteral("/Mobile/Panels/") + objectName, this);
+}
+
+void PanelSettingsDBusObject::updateFields()
+{
+ if (!m_output) {
+ return;
+ }
+
+ auto group = KConfigGroup{m_config, PANELS_CONFIG_GROUP};
+ auto topGroup = KConfigGroup{&group, mapRotationToTopPosition(m_output->rotation())};
+ auto bottomGroup = KConfigGroup{&group, mapRotationToBottomPosition(m_output->rotation())};
+
+ // Divide values by the display's scale for scaling independent sizing
+ setStatusBarHeight(topGroup.readEntry(u"statusBarHeight"_s, -1.0) / m_output->scale());
+ setStatusBarLeftPadding(topGroup.readEntry(u"statusBarLeftPadding"_s, 0.0) / m_output->scale());
+ setStatusBarRightPadding(topGroup.readEntry(u"statusBarRightPadding"_s, 0.0) / m_output->scale());
+ setStatusBarCenterSpacing(topGroup.readEntry(u"statusBarCenterSpacing"_s, 0.0) / m_output->scale());
+ setNavigationPanelHeight(bottomGroup.readEntry(u"navigationPanelHeight"_s, -1.0) / m_output->scale());
+ setNavigationPanelLeftPadding(bottomGroup.readEntry(u"navigationPanelLeftPadding"_s, 0.0) / m_output->scale());
+ setNavigationPanelRightPadding(bottomGroup.readEntry(u"navigationPanelRightPadding"_s, 0.0) / m_output->scale());
+}
+
+int PanelSettingsDBusObject::outputId() const
+{
+ return m_outputId;
+}
+
+QString PanelSettingsDBusObject::outputName() const
+{
+ return m_outputName;
+}
+
+qreal PanelSettingsDBusObject::statusBarHeight() const
+{
+ return m_statusBarHeight;
+}
+
+void PanelSettingsDBusObject::setStatusBarHeight(qreal statusBarHeight)
+{
+ if (statusBarHeight == m_statusBarHeight) {
+ return;
+ }
+ m_statusBarHeight = statusBarHeight;
+ Q_EMIT statusBarHeightChanged();
+}
+
+qreal PanelSettingsDBusObject::statusBarLeftPadding() const
+{
+ return m_statusBarLeftPadding;
+}
+
+void PanelSettingsDBusObject::setStatusBarLeftPadding(qreal statusBarLeftPadding)
+{
+ if (statusBarLeftPadding == m_statusBarLeftPadding) {
+ return;
+ }
+ m_statusBarLeftPadding = statusBarLeftPadding;
+ Q_EMIT statusBarLeftPaddingChanged();
+}
+
+qreal PanelSettingsDBusObject::statusBarRightPadding() const
+{
+ return m_statusBarRightPadding;
+}
+
+void PanelSettingsDBusObject::setStatusBarRightPadding(qreal statusBarRightPadding)
+{
+ if (statusBarRightPadding == m_statusBarRightPadding) {
+ return;
+ }
+ m_statusBarRightPadding = statusBarRightPadding;
+ Q_EMIT statusBarRightPaddingChanged();
+}
+
+qreal PanelSettingsDBusObject::statusBarCenterSpacing() const
+{
+ return m_statusBarCenterSpacing;
+}
+
+void PanelSettingsDBusObject::setStatusBarCenterSpacing(qreal statusBarCenterSpacing)
+{
+ if (statusBarCenterSpacing == m_statusBarCenterSpacing) {
+ return;
+ }
+ m_statusBarCenterSpacing = statusBarCenterSpacing;
+ Q_EMIT statusBarCenterSpacingChanged();
+}
+
+qreal PanelSettingsDBusObject::navigationPanelHeight() const
+{
+ return m_navigationPanelHeight;
+}
+
+void PanelSettingsDBusObject::setNavigationPanelHeight(qreal navigationPanelHeight)
+{
+ if (navigationPanelHeight == m_navigationPanelHeight) {
+ return;
+ }
+ m_navigationPanelHeight = navigationPanelHeight;
+ Q_EMIT navigationPanelHeightChanged();
+}
+
+qreal PanelSettingsDBusObject::navigationPanelLeftPadding() const
+{
+ return m_navigationPanelLeftPadding;
+}
+
+void PanelSettingsDBusObject::setNavigationPanelLeftPadding(qreal navigationPanelLeftPadding)
+{
+ if (navigationPanelLeftPadding == m_navigationPanelLeftPadding) {
+ return;
+ }
+ m_navigationPanelLeftPadding = navigationPanelLeftPadding;
+ Q_EMIT navigationPanelLeftPaddingChanged();
+}
+
+qreal PanelSettingsDBusObject::navigationPanelRightPadding() const
+{
+ return m_navigationPanelRightPadding;
+}
+
+void PanelSettingsDBusObject::setNavigationPanelRightPadding(qreal navigationPanelRightPadding)
+{
+ if (navigationPanelRightPadding == m_navigationPanelRightPadding) {
+ return;
+ }
+ m_navigationPanelRightPadding = navigationPanelRightPadding;
+ Q_EMIT navigationPanelRightPaddingChanged();
+}
diff --git a/components/mobileshellstate/panelsettingsdbusobjectmanager.h b/components/mobileshellstate/panelsettingsdbusobjectmanager.h
new file mode 100644
index 00000000..5b375e6d
--- /dev/null
+++ b/components/mobileshellstate/panelsettingsdbusobjectmanager.h
@@ -0,0 +1,95 @@
+// SPDX-FileCopyrightText: 2025 Devin Lin
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+
+class PanelSettingsDBusObject;
+
+class PanelSettingsDBusObjectManager : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_SINGLETON
+
+public:
+ PanelSettingsDBusObjectManager(QObject *parent = nullptr);
+
+ // called by QML
+ Q_INVOKABLE void registerObjects();
+
+private:
+ bool m_initialized = false;
+ QList m_dbusObjects;
+ KScreen::ConfigPtr m_kscreenConfig{nullptr};
+};
+
+class PanelSettingsDBusObject : public QObject
+{
+ Q_OBJECT
+ // HACK: org.kde.plasmashell prefix seems to bug out and not compile with the qt macro, use this for now
+ Q_CLASSINFO("D-Bus Interface", "org.kde.plasmashellMobilePanels")
+
+public:
+ PanelSettingsDBusObject(QObject *parent = nullptr);
+
+ void registerObject(KScreen::OutputPtr output);
+
+ int outputId() const;
+ QString outputName() const;
+
+Q_SIGNALS:
+ Q_SCRIPTABLE void statusBarHeightChanged();
+ Q_SCRIPTABLE void statusBarLeftPaddingChanged();
+ Q_SCRIPTABLE void statusBarRightPaddingChanged();
+ Q_SCRIPTABLE void statusBarCenterSpacingChanged();
+ Q_SCRIPTABLE void navigationPanelHeightChanged();
+ Q_SCRIPTABLE void navigationPanelLeftPaddingChanged();
+ Q_SCRIPTABLE void navigationPanelRightPaddingChanged();
+
+public Q_SLOTS:
+ Q_SCRIPTABLE qreal statusBarHeight() const;
+ Q_SCRIPTABLE qreal statusBarLeftPadding() const;
+ Q_SCRIPTABLE qreal statusBarRightPadding() const;
+ Q_SCRIPTABLE qreal statusBarCenterSpacing() const;
+
+ Q_SCRIPTABLE qreal navigationPanelHeight() const;
+ Q_SCRIPTABLE qreal navigationPanelLeftPadding() const;
+ Q_SCRIPTABLE qreal navigationPanelRightPadding() const;
+
+private:
+ void updateFields();
+
+ void setStatusBarHeight(qreal statusBarHeight);
+ void setStatusBarLeftPadding(qreal statusBarLeftPadding);
+ void setStatusBarRightPadding(qreal statusBarRightPadding);
+ void setStatusBarCenterSpacing(qreal statusBarCenterSpacing);
+
+ void setNavigationPanelHeight(qreal navigationPanelHeight);
+ void setNavigationPanelLeftPadding(qreal navigationPanelLeftPadding);
+ void setNavigationPanelRightPadding(qreal navigationPanelRightPadding);
+
+ int m_outputId = -1;
+ QString m_outputName;
+
+ qreal m_statusBarHeight = -1;
+ qreal m_statusBarLeftPadding = 0;
+ qreal m_statusBarRightPadding = 0;
+ qreal m_statusBarCenterSpacing = 0;
+ qreal m_navigationPanelHeight = -1;
+ qreal m_navigationPanelLeftPadding = 0;
+ qreal m_navigationPanelRightPadding = 0;
+
+ KSharedConfig::Ptr m_config;
+ KConfigWatcher::Ptr m_configWatcher;
+ KScreen::OutputPtr m_output;
+};
diff --git a/components/quicksettingsplugin/quicksettingsconfig.cpp b/components/quicksettingsplugin/quicksettingsconfig.cpp
index 1aba34da..e40a378b 100644
--- a/components/quicksettingsplugin/quicksettingsconfig.cpp
+++ b/components/quicksettingsplugin/quicksettingsconfig.cpp
@@ -13,7 +13,7 @@ const QString QUICKSETTINGS_CONFIG_GROUP = QStringLiteral("QuickSettings");
QuickSettingsConfig::QuickSettingsConfig(QObject *parent)
: QObject{parent}
- , m_config{KSharedConfig::openConfig(CONFIG_FILE, KConfig::SimpleConfig)}
+ , m_config{KSharedConfig::openConfig(CONFIG_FILE)}
{
m_configWatcher = KConfigWatcher::create(m_config);
diff --git a/components/shellsettingsplugin/CMakeLists.txt b/components/shellsettingsplugin/CMakeLists.txt
index 1074bf51..504a22df 100644
--- a/components/shellsettingsplugin/CMakeLists.txt
+++ b/components/shellsettingsplugin/CMakeLists.txt
@@ -2,7 +2,10 @@
# SPDX-License-Identifier: GPL-2.0-or-later
ecm_add_qml_module(shellsettingsplugin URI org.kde.plasma.private.mobileshell.shellsettingsplugin GENERATE_PLUGIN_SOURCE)
-target_sources(shellsettingsplugin PRIVATE kwinsettings.cpp mobileshellsettings.cpp)
+target_sources(shellsettingsplugin PRIVATE
+ kwinsettings.cpp
+ mobileshellsettings.cpp
+)
target_link_libraries(shellsettingsplugin PRIVATE
Qt::Qml
@@ -14,6 +17,7 @@ target_link_libraries(shellsettingsplugin PRIVATE
KF6::Package
KF6::KIOGui
KF6::JobWidgets
+ KF6::Screen
)
ecm_finalize_qml_module(shellsettingsplugin)
diff --git a/components/shellsettingsplugin/kwinsettings.cpp b/components/shellsettingsplugin/kwinsettings.cpp
index e6c2aa40..def45ae3 100644
--- a/components/shellsettingsplugin/kwinsettings.cpp
+++ b/components/shellsettingsplugin/kwinsettings.cpp
@@ -11,7 +11,7 @@ const QString WAYLAND_CONFIG_GROUP = QStringLiteral("Wayland");
KWinSettings::KWinSettings(QObject *parent)
: QObject{parent}
- , m_config{KSharedConfig::openConfig(CONFIG_FILE, KConfig::SimpleConfig)}
+ , m_config{KSharedConfig::openConfig(CONFIG_FILE)}
{
m_configWatcher = KConfigWatcher::create(m_config);
connect(m_configWatcher.data(), &KConfigWatcher::configChanged, this, [this](const KConfigGroup &group, const QByteArrayList &names) -> void {
diff --git a/components/shellsettingsplugin/mobileshellsettings.cpp b/components/shellsettingsplugin/mobileshellsettings.cpp
index 624f9077..8ce0ad5e 100644
--- a/components/shellsettingsplugin/mobileshellsettings.cpp
+++ b/components/shellsettingsplugin/mobileshellsettings.cpp
@@ -23,7 +23,7 @@ const QString QUICKSETTINGS_CONFIG_GROUP = QStringLiteral("QuickSettings");
MobileShellSettings::MobileShellSettings(QObject *parent)
: QObject{parent}
- , m_config{KSharedConfig::openConfig(CONFIG_FILE, KConfig::SimpleConfig)}
+ , m_config{KSharedConfig::openConfig(CONFIG_FILE)}
{
m_configWatcher = KConfigWatcher::create(m_config);
connect(m_configWatcher.data(), &KConfigWatcher::configChanged, this, [this](const KConfigGroup &group, const QByteArrayList &names) -> void {
diff --git a/components/wallpaperimageplugin/wallpaperplugin.cpp b/components/wallpaperimageplugin/wallpaperplugin.cpp
index 8713683f..fe159e08 100644
--- a/components/wallpaperimageplugin/wallpaperplugin.cpp
+++ b/components/wallpaperimageplugin/wallpaperplugin.cpp
@@ -23,8 +23,8 @@ WallpaperPlugin::WallpaperPlugin(QObject *parent)
: QObject{parent}
, m_homescreenConfig{new QQmlPropertyMap{this}}
, m_lockscreenConfig{new QQmlPropertyMap{this}}
- , m_homescreenConfigFile{KSharedConfig::openConfig("plasma-org.kde.plasma.mobileshell-appletsrc", KConfig::SimpleConfig)}
- , m_lockscreenConfigFile{KSharedConfig::openConfig("kscreenlockerrc", KConfig::SimpleConfig)}
+ , m_homescreenConfigFile{KSharedConfig::openConfig("plasma-org.kde.plasma.mobileshell-appletsrc")}
+ , m_lockscreenConfigFile{KSharedConfig::openConfig("kscreenlockerrc")}
{
m_lockscreenConfigWatcher = KConfigWatcher::create(m_lockscreenConfigFile);
diff --git a/containments/panel/qml/main.qml b/containments/panel/qml/main.qml
index fb6e0961..749b5ff3 100644
--- a/containments/panel/qml/main.qml
+++ b/containments/panel/qml/main.qml
@@ -64,11 +64,18 @@ ContainmentItem {
}
}
+ readonly property real panelHeight: MobileShell.Constants.topPanelHeight
+ onPanelHeightChanged: setWindowProperties()
+
function setWindowProperties() {
if (root.panel) {
root.panel.floating = false;
root.panel.maximize(); // maximize first, then we can apply offsets (otherwise they are overridden)
- root.panel.thickness = MobileShell.Constants.topPanelHeight;
+
+ // HACK: set thickness twice, sometimes it doesn't set the first time??
+ root.panel.thickness = root.panelHeight;
+ root.panel.thickness = root.panelHeight;
+
root.panel.visibilityMode = ShellSettings.Settings.autoHidePanelsEnabled ? 3 : 0;
MobileShell.ShellUtil.setWindowLayer(root.panel, LayerShell.Window.LayerOverlay)
root.updateTouchArea();
@@ -86,18 +93,21 @@ ContainmentItem {
}
}
+ Connections {
+ target: root.panel
+
+ function onThicknessChanged() {
+ if (root.panel.thickness !== root.panelHeight) {
+ root.panel.thickness = root.panelHeight;
+ }
+ }
+ }
+
// Overlay the panel over the lockscreen when brought up
LockscreenOverlay {
window: root.Window.window
}
- // Enforce thickness of panel
- Binding {
- target: panel // assumed to be plasma-workspace "PanelView" component
- property: "thickness"
- value: MobileShell.Constants.topPanelHeight
- }
-
Connections {
target: ShellSettings.Settings
diff --git a/containments/taskpanel/qml/NavigationPanelComponent.qml b/containments/taskpanel/qml/NavigationPanelComponent.qml
index a2879488..2dd16f3e 100644
--- a/containments/taskpanel/qml/NavigationPanelComponent.qml
+++ b/containments/taskpanel/qml/NavigationPanelComponent.qml
@@ -30,6 +30,14 @@ MobileShell.NavigationPanel {
foregroundColorGroup: opaqueBar ? Kirigami.Theme.Window : Kirigami.Theme.Complementary
shadow: !opaqueBar
+ MobileShellState.PanelSettingsDBusClient {
+ id: panelSettings
+ screenName: Screen.name
+ }
+
+ leftPadding: panelSettings.navigationPanelLeftPadding
+ rightPadding: panelSettings.navigationPanelRightPadding
+
TaskManager.VirtualDesktopInfo {
id: virtualDesktopInfo
}
@@ -61,7 +69,7 @@ MobileShell.NavigationPanel {
enabled: true
iconSource: "mobile-task-switcher"
- iconSizeFactor: 0.75
+ shrinkSize: 4
onTriggered: {
Plasmoid.triggerTaskSwitcher();
@@ -74,7 +82,6 @@ MobileShell.NavigationPanel {
enabled: true
iconSource: "start-here-kde"
- iconSizeFactor: 1
onTriggered: {
MobileShellState.ShellDBusClient.openHomeScreen();
@@ -88,7 +95,7 @@ MobileShell.NavigationPanel {
enabled: Keyboards.KWinVirtualKeyboard.visible || WindowPlugin.WindowUtil.hasCloseableActiveWindow
iconSource: Keyboards.KWinVirtualKeyboard.visible ? "go-down-symbolic" : "mobile-close-app"
// mobile-close-app (from plasma-frameworks) seems to have fewer margins than icons from breeze-icons
- iconSizeFactor: Keyboards.KWinVirtualKeyboard.visible ? 1 : 0.75
+ shrinkSize: Keyboards.KWinVirtualKeyboard.visible ? 0 : 4
onTriggered: {
if (Keyboards.KWinVirtualKeyboard.visible) {
@@ -108,7 +115,7 @@ MobileShell.NavigationPanel {
visible: RotationPlugin.RotationUtil.showRotationButton
enabled: true
iconSource: "rotation-allowed-symbolic"
- iconSizeFactor: 0.75
+ shrinkSize: 4
onTriggered: {
RotationPlugin.RotationUtil.rotateToSuggestedRotation();
@@ -121,7 +128,7 @@ MobileShell.NavigationPanel {
(Keyboards.KWinVirtualKeyboard.available && !Keyboards.KWinVirtualKeyboard.activeClientSupportsTextInput)
enabled: true
iconSource: "input-keyboard-virtual-symbolic"
- iconSizeFactor: 0.75
+ shrinkSize: 4
onTriggered: {
if (Keyboards.KWinVirtualKeyboard.active) {
diff --git a/containments/taskpanel/qml/main.qml b/containments/taskpanel/qml/main.qml
index 243eb821..ecaa9115 100644
--- a/containments/taskpanel/qml/main.qml
+++ b/containments/taskpanel/qml/main.qml
@@ -39,6 +39,7 @@ ContainmentItem {
readonly property bool inLandscape: MobileShell.Constants.navigationPanelOnSide(Screen.width, Screen.height)
readonly property real navigationPanelHeight: MobileShell.Constants.navigationPanelThickness
+ onNavigationPanelHeightChanged: setWindowProperties()
readonly property real intendedWindowThickness: navigationPanelHeight
readonly property real intendedWindowLength: inLandscape ? Screen.height : Screen.width
diff --git a/devices/CMakeLists.txt b/devices/CMakeLists.txt
new file mode 100644
index 00000000..dc3fb880
--- /dev/null
+++ b/devices/CMakeLists.txt
@@ -0,0 +1,4 @@
+# SPDX-FileCopyrightText: 2025 Devin Lin
+# SPDX-License-Identifier: BSD-2-Clause
+
+install(DIRECTORY configs/ DESTINATION ${KDE_INSTALL_DATADIR}/plasma-mobile-device-presets)
diff --git a/devices/README.md b/devices/README.md
new file mode 100644
index 00000000..455b58b7
--- /dev/null
+++ b/devices/README.md
@@ -0,0 +1,32 @@
+
+
+This folder is where device-specific information is set.
+
+### Usage as a distro
+
+As a distribution, you can ship the device preset with the image by installing a file.
+
+In `/etc/xdg/plasmamobilerc`, write:
+
+```toml
+[Device]
+device=oneplus-enchilada # replace with the device id
+```
+
+This should be a file name that exists in `/usr/share/plasma-mobile-device-presets` (which are the files in the `configs` folder).
+
+### Adding a new device config
+
+See [spec.conf](spec.conf) for more details on the specification. If a setting is omitted in the file, then the system will use the default value.
+
+Add a new config in [configs](configs) with the device id.
+
+In order to test your changes, install the file to `/usr/share/plasma-mobile-device-presets/your-device-id.conf`. Then run envmanager to apply the changes to the running system:
+
+```
+$ plasma-mobile-envmanager --apply-settings
+$ plasmashell --replace
+```
diff --git a/devices/configs/default.conf b/devices/configs/default.conf
new file mode 100644
index 00000000..e525d3bb
--- /dev/null
+++ b/devices/configs/default.conf
@@ -0,0 +1,5 @@
+# SPDX-FileCopyrightText: 2025 Devin Lin
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+[Device]
+name=Default
diff --git a/devices/configs/google-sargo.conf b/devices/configs/google-sargo.conf
new file mode 100644
index 00000000..610096bf
--- /dev/null
+++ b/devices/configs/google-sargo.conf
@@ -0,0 +1,7 @@
+# SPDX-FileCopyrightText: 2025 Devin Lin
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+[Device]
+name=Pixel 3a
+
+# No tweaks needed
diff --git a/devices/configs/oneplus-enchilada.conf b/devices/configs/oneplus-enchilada.conf
new file mode 100644
index 00000000..58e14709
--- /dev/null
+++ b/devices/configs/oneplus-enchilada.conf
@@ -0,0 +1,15 @@
+# SPDX-FileCopyrightText: 2025 Devin Lin
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+[Device]
+name=OnePlus 6
+
+# Match panel heights with the screen's curved area
+
+[Panels]
+statusBarHeight=80
+leftPadding=12
+rightPadding=12
+
+[Panels][Top]
+centerSpacing=64
diff --git a/devices/spec.conf b/devices/spec.conf
new file mode 100644
index 00000000..585f41ba
--- /dev/null
+++ b/devices/spec.conf
@@ -0,0 +1,47 @@
+# SPDX-FileCopyrightText: 2025 Devin Lin
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+[Device]
+# The name of the device.
+name=My Phone
+
+# Dimensions for a panel (fallback for any of the options below)
+[Panels]
+statusBarHeight=
+navigationPanelHeight=
+centerSpacing=
+leftPadding=
+rightPadding=
+
+# Dimensions for a panel if it is mounted on the top of the device
+[Panels][Top]
+statusBarHeight= # The status bar height in pixels if it is mounted on the top side of the device (from its natural orientation).
+navigationPanelHeight= # The navigation bar height in pixels if it is mounted on the top side of the device (from its natural orientation).
+centerSpacing= # The amount of space in pixels to reserve in the center for panels mounted on the top side of the device (for notches, punch holes).
+leftPadding= # The left padding in pixels of panels mounted on the top side of the device (from its natural orientation).
+rightPadding= # The right padding in pixels of panels mounted on the top side of the device (from its natural orientation).
+
+# Dimensions for a panel if it is mounted on the bottom of the device
+[Panels][Bottom]
+statusBarHeight=
+navigationPanelHeight=
+centerSpacing=
+leftPadding=
+rightPadding=
+
+# Dimensions for a panel if it is mounted on the left of the device (landscape)
+[Panels][Left]
+statusBarHeight=
+navigationPanelHeight=
+centerSpacing=
+leftPadding=
+rightPadding=
+
+# Dimensions for a panel if it is mounted on the right of the device (landscape)
+[Panels][Right]
+statusBarHeight=
+navigationPanelHeight=
+centerSpacing=
+leftPadding=
+rightPadding=
+
diff --git a/envmanager/CMakeLists.txt b/envmanager/CMakeLists.txt
index fd0a255d..180145c5 100644
--- a/envmanager/CMakeLists.txt
+++ b/envmanager/CMakeLists.txt
@@ -5,6 +5,7 @@ set(plasma-mobile-envmanager_SRCS
main.cpp
settings.cpp
utils.cpp
+ devicepresets.cpp
config.h
)
diff --git a/envmanager/devicepresets.cpp b/envmanager/devicepresets.cpp
new file mode 100644
index 00000000..3117e380
--- /dev/null
+++ b/envmanager/devicepresets.cpp
@@ -0,0 +1,88 @@
+// SPDX-FileCopyrightText: 2025 Devin Lin
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "devicepresets.h"
+
+#include
+#include
+#include
+#include
+
+using namespace Qt::Literals::StringLiterals;
+
+const QString GENERAL_CONFIG_FILE = u"plasmamobilerc"_s;
+const QString WRITE_TO_CONFIG_FILE = u"plasma-mobile/plasmamobilerc"_s;
+const QString DEVICE_CONFIG_GROUP = u"Device"_s;
+
+DevicePresets::DevicePresets(QObject *parent)
+ : QObject{parent}
+ , m_mobileConfig{KSharedConfig::openConfig(WRITE_TO_CONFIG_FILE, KConfig::SimpleConfig)}
+{
+}
+
+void setKey(KConfigGroup &fallbackGroup, KConfigGroup &fromGroup, KConfigGroup &toGroup, QString fromKey, QString toKey)
+{
+ if (fromGroup.hasKey(fromKey)) {
+ toGroup.writeEntry(toKey, fromGroup.readEntry(fromKey), KConfigGroup::Notify);
+ } else if (fallbackGroup.hasKey(fromKey)) {
+ toGroup.writeEntry(toKey, fallbackGroup.readEntry(fromKey), KConfigGroup::Notify);
+ } else {
+ toGroup.deleteEntry(toKey, KConfigGroup::Notify);
+ }
+}
+
+void DevicePresets::initialize()
+{
+ // Open mobile config to read from all locations (/etc/xdg/plasmamobilerc, ~/.config/plasmamobilerc, etc.)
+ auto mobileConfig = KSharedConfig::openConfig(GENERAL_CONFIG_FILE);
+ auto deviceGroup = KConfigGroup{mobileConfig, DEVICE_CONFIG_GROUP};
+
+ // Read device id
+ const QString device = deviceGroup.readEntry(u"device"_s, {});
+
+ QString presetFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, u"plasma-mobile-device-presets/"_s + device + ".conf");
+ if (!QFile{presetFile}.exists()) {
+ presetFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, u"plasma-mobile-device-presets/default.conf"_s);
+ if (!QFile{presetFile}.exists()) {
+ qWarning() << "Failed to find any device preset file";
+ return;
+ }
+ }
+
+ // Open preset file /usr/share/plasma-mobile-device-presets/device.conf
+ auto presetConfig = KSharedConfig::openConfig(QFileInfo{presetFile}.absoluteFilePath(), KConfig::SimpleConfig);
+ if (!presetConfig) {
+ return;
+ }
+
+ // Write presets to ~/.config/plasma-mobile/plasmamobilerc
+ // This is then read by components/mobileshellstate (PanelSettingsDBusObjectManager)
+ auto presetPanelsGroup = KConfigGroup{presetConfig, u"Panels"_s};
+ auto mobilePanelsGroup = KConfigGroup{m_mobileConfig, u"Panels"_s};
+
+ //
+ QList> groupPairs = {{u"Top"_s, u"WhenOnTop"_s},
+ {u"Bottom"_s, u"WhenOnBottom"_s},
+ {u"Left"_s, u"WhenOnLeft"_s},
+ {u"Right"_s, u"WhenOnRight"_s}};
+
+ // Convert preset file settings into plasmamobilerc ones
+ for (const auto &p : groupPairs) {
+ auto presetGroup = KConfigGroup{&presetPanelsGroup, p.first};
+ auto writeGroup = KConfigGroup{&mobilePanelsGroup, p.second};
+
+ // Try to read the value from presetGroup first (ex. [Panels][Top] statusBarHeight=...)
+ // If it doesn't exist, then fallback to parent group (ex. [Panels] statusBarHeight=...)
+ setKey(presetPanelsGroup, presetGroup, writeGroup, u"statusBarHeight"_s, u"statusBarHeight"_s);
+ setKey(presetPanelsGroup, presetGroup, writeGroup, u"navigationPanelHeight"_s, u"navigationPanelHeight"_s);
+ setKey(presetPanelsGroup, presetGroup, writeGroup, u"leftPadding"_s, u"statusBarLeftPadding"_s);
+ setKey(presetPanelsGroup, presetGroup, writeGroup, u"leftPadding"_s, u"navigationPanelLeftPadding"_s);
+ setKey(presetPanelsGroup, presetGroup, writeGroup, u"rightPadding"_s, u"statusBarRightPadding"_s);
+ setKey(presetPanelsGroup, presetGroup, writeGroup, u"rightPadding"_s, u"navigationPanelRightPadding"_s);
+ setKey(presetPanelsGroup, presetGroup, writeGroup, u"centerSpacing"_s, u"statusBarCenterSpacing"_s);
+
+ writeGroup.sync();
+ }
+
+ m_mobileConfig->sync();
+}
diff --git a/envmanager/devicepresets.h b/envmanager/devicepresets.h
new file mode 100644
index 00000000..0d5c57e9
--- /dev/null
+++ b/envmanager/devicepresets.h
@@ -0,0 +1,20 @@
+// SPDX-FileCopyrightText: 2025 Devin Lin
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include
+
+#include
+#include
+
+class DevicePresets : public QObject
+{
+ Q_OBJECT
+
+public:
+ DevicePresets(QObject *parent = nullptr);
+
+ void initialize();
+
+private:
+ KSharedConfig::Ptr m_mobileConfig;
+};
diff --git a/envmanager/settings.cpp b/envmanager/settings.cpp
index 24fb11d4..5eda53bf 100644
--- a/envmanager/settings.cpp
+++ b/envmanager/settings.cpp
@@ -4,6 +4,7 @@
#include "settings.h"
#include "config.h"
+#include "devicepresets.h"
#include "utils.h"
#include
@@ -115,8 +116,12 @@ void Settings::applyMobileConfiguration()
setOptionsImmutable(true, MOBILE_KSMSERVERRC_FILE, KSMSERVER_SETTINGS);
}
- // save our changes
+ // Save our changes
m_mobileConfig->sync();
+
+ // Setup device configs
+ DevicePresets devicePresets;
+ devicePresets.initialize();
}
void Settings::writeKeys(const QString &fileName, KSharedConfig::Ptr &config, const QMap> &settings)
diff --git a/initialstart/settings.cpp b/initialstart/settings.cpp
index 138f840a..e415cd3f 100644
--- a/initialstart/settings.cpp
+++ b/initialstart/settings.cpp
@@ -11,7 +11,7 @@ const QString INITIAL_START_CONFIG_GROUP = QStringLiteral("InitialStart");
Settings::Settings(QObject *parent)
: QObject{parent}
- , m_mobileConfig{KSharedConfig::openConfig(CONFIG_FILE, KConfig::SimpleConfig)}
+ , m_mobileConfig{KSharedConfig::openConfig(CONFIG_FILE)}
, m_isMobilePlatform{KRuntimePlatform::runtimePlatform().contains(QStringLiteral("phone"))}
{
}
diff --git a/kwin/mobiletaskswitcher/package/contents/ui/TaskSwitcher.qml b/kwin/mobiletaskswitcher/package/contents/ui/TaskSwitcher.qml
index 13232d96..1abaa633 100644
--- a/kwin/mobiletaskswitcher/package/contents/ui/TaskSwitcher.qml
+++ b/kwin/mobiletaskswitcher/package/contents/ui/TaskSwitcher.qml
@@ -493,10 +493,18 @@ FocusScope {
isVertical: MobileShell.Constants.navigationPanelOnSide(root.width, root.height)
+ MobileShellState.PanelSettingsDBusClient {
+ id: panelSettings
+ screenName: Screen.name
+ }
+
+ leftPadding: panelSettings.navigationPanelLeftPadding
+ rightPadding: panelSettings.navigationPanelRightPadding
+
leftAction: MobileShell.NavigationPanelAction {
enabled: true
iconSource: "mobile-task-switcher"
- iconSizeFactor: 0.75
+ shrinkSize: 4
onTriggered: {
if (taskList.count === 0) {
@@ -519,7 +527,6 @@ FocusScope {
middleAction: MobileShell.NavigationPanelAction {
enabled: true
iconSource: "start-here-kde"
- iconSizeFactor: 1
onTriggered: root.hide()
}
@@ -527,7 +534,7 @@ FocusScope {
rightAction: MobileShell.NavigationPanelAction {
enabled: true
iconSource: "mobile-close-app"
- iconSizeFactor: 0.75
+ shrinkSize: 4
onTriggered: {
taskList.getTaskAt(root.state.currentTaskIndex).closeApp();
diff --git a/layout-templates/org.kde.plasma.mobile.defaultNavigationPanel/contents/layout.js b/layout-templates/org.kde.plasma.mobile.defaultNavigationPanel/contents/layout.js
index 9050f41c..0a501f72 100644
--- a/layout-templates/org.kde.plasma.mobile.defaultNavigationPanel/contents/layout.js
+++ b/layout-templates/org.kde.plasma.mobile.defaultNavigationPanel/contents/layout.js
@@ -3,4 +3,3 @@
const bottomPanel = new Panel("org.kde.plasma.mobile.taskpanel")
bottomPanel.location = "bottom";
-bottomPanel.height = 2 * gridUnit;
diff --git a/layout-templates/org.kde.plasma.mobile.defaultStatusBar/contents/layout.js b/layout-templates/org.kde.plasma.mobile.defaultStatusBar/contents/layout.js
index d9f14cfa..eeabcd2f 100644
--- a/layout-templates/org.kde.plasma.mobile.defaultStatusBar/contents/layout.js
+++ b/layout-templates/org.kde.plasma.mobile.defaultStatusBar/contents/layout.js
@@ -3,4 +3,3 @@
const panel = new Panel("org.kde.plasma.mobile.panel");
panel.location = "top";
-panel.height = 1.25 * gridUnit; // HACK: supposed to be gridUnit + smallSpacing, but it doesn't seem to give the correct number
diff --git a/shell/contents/views/Desktop.qml b/shell/contents/views/Desktop.qml
index f5578b98..fc4154f0 100644
--- a/shell/contents/views/Desktop.qml
+++ b/shell/contents/views/Desktop.qml
@@ -33,6 +33,7 @@ Rectangle {
// HACK: we need to initialize the DBus server somewhere in plasmashell, it might as well be here...
MobileShellState.ShellDBusObject.registerObject();
+ MobileShellState.PanelSettingsDBusObjectManager.registerObjects();
// Initialize the volume osd, and volume keys.
// Initialize notification popups.