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.