diff --git a/README.md b/README.md index 7e3e94a1..104b9c79 100644 --- a/README.md +++ b/README.md @@ -15,10 +15,10 @@ This repository contains shell components for Plasma Mobile. * Development channel: https://matrix.to/#/#plasmamobile:matrix.org ### Locations -* [libmobileshell](libmobileshell) - shell component library +* [libmobileshell](libmobileshell) - shell component library (not guaranteed to be binary compatible between releases!) * [containments](containments) - shell panels (homescreen, status bar, task panel) * [homescreens](homescreens) - homescreen packages -* [kcms]() - settings modules +* [kcms](kcms) - settings modules * [look-and-feel](look-and-feel/contents) - Plasma look-and-feel packages (ex. lockscreen, logout, etc.) * [quicksettings](quicksettings) - quick settings packages for the action drawer diff --git a/libmobileshell/CMakeLists.txt b/libmobileshell/CMakeLists.txt index 23b6c2fe..6794a144 100644 --- a/libmobileshell/CMakeLists.txt +++ b/libmobileshell/CMakeLists.txt @@ -14,11 +14,14 @@ qt_add_dbus_interfaces(DBUS_SRCS dbus/org.kde.KWin.ScreenShot2.xml set(mobileshell_LIB_SRCS displaysmodel.cpp mobileshellsettings.cpp - quicksetting.cpp - quicksettingsmodel.cpp shellutil.cpp kwinvirtualkeyboardinterface.cpp + quicksetting.cpp + quicksettingsmodel.cpp + savedquicksettingsmodel.cpp + savedquicksettings.cpp + ${DBUS_SRCS} ) @@ -59,9 +62,13 @@ install(FILES displaysmodel.h kwinvirtualkeyboardinterface.h mobileshellsettings.h + shellutil.h + quicksetting.h quicksettingsmodel.h - shellutil.h + savedquicksettingsmodel.h + savedquicksettings.h + ${CMAKE_CURRENT_BINARY_DIR}/mobileshell_export.h DESTINATION ${KDE_INSTALL_INCLUDEDIR}/mobileshell COMPONENT Devel ) diff --git a/libmobileshell/declarative/mobileshellplugin.cpp b/libmobileshell/declarative/mobileshellplugin.cpp index 02a9305e..d5bbed09 100644 --- a/libmobileshell/declarative/mobileshellplugin.cpp +++ b/libmobileshell/declarative/mobileshellplugin.cpp @@ -40,6 +40,8 @@ void MobileShellPlugin::registerTypes(const char *uri) qmlRegisterType(uri, 1, 0, "QuickSetting"); qmlRegisterType(uri, 1, 0, "QuickSettingsModel"); + qmlRegisterType(uri, 1, 0, "SavedQuickSettings"); + qmlRegisterType(uri, 1, 0, "SavedQuickSettingsModel"); qmlRegisterType(uri, 1, 0, "DisplaysModel"); qmlRegisterSingletonType(uri, 1, 0, "KWinVirtualKeyboard", [](QQmlEngine *, QJSEngine *) -> QObject * { diff --git a/libmobileshell/mobileshellsettings.cpp b/libmobileshell/mobileshellsettings.cpp index 6cf03b32..f12adb68 100644 --- a/libmobileshell/mobileshellsettings.cpp +++ b/libmobileshell/mobileshellsettings.cpp @@ -10,6 +10,7 @@ using namespace MobileShell; const QString CONFIG_FILE = QStringLiteral("plasmamobilerc"); const QString GENERAL_CONFIG_GROUP = QStringLiteral("General"); +const QString QUICKSETTINGS_CONFIG_GROUP = QStringLiteral("QuickSettings"); MobileShellSettings *MobileShellSettings::self() { @@ -26,6 +27,9 @@ MobileShellSettings::MobileShellSettings(QObject *parent) connect(m_configWatcher.data(), &KConfigWatcher::configChanged, this, [this](const KConfigGroup &group, const QByteArrayList &names) -> void { if (group.name() == GENERAL_CONFIG_GROUP) { Q_EMIT navigationPanelEnabledChanged(); + } else if (group.name() == QUICKSETTINGS_CONFIG_GROUP) { + Q_EMIT enabledQuickSettingsChanged(); + Q_EMIT disabledQuickSettingsChanged(); } }); } @@ -35,3 +39,55 @@ bool MobileShellSettings::navigationPanelEnabled() const auto group = KConfigGroup{m_config, GENERAL_CONFIG_GROUP}; return group.readEntry("navigationPanelEnabled", true); } + +void MobileShellSettings::setNavigationPanelEnabled(bool navigationPanelEnabled) +{ + auto group = KConfigGroup{m_config, GENERAL_CONFIG_GROUP}; + group.writeEntry("navigationPanelEnabled", navigationPanelEnabled, KConfigGroup::Notify); + m_config->sync(); +} + +QList MobileShellSettings::enabledQuickSettings() const +{ + auto group = KConfigGroup{m_config, QUICKSETTINGS_CONFIG_GROUP}; + // TODO move defaults to file + // we aren't worried about quicksettings not showing up though, any that are not specified will be automatically added to the end + return group.readEntry("enabledQuickSettings", + QList{ + QStringLiteral("org.kde.plasma.wifi"), + QStringLiteral("org.kde.plasma.mobiledata"), + QStringLiteral("org.kde.plasma.bluetooth"), + QStringLiteral("org.kde.plasma.flashlight"), + QStringLiteral("org.kde.plasma.screenrotation"), + QStringLiteral("org.kde.plasma.settingsapp"), + QStringLiteral("org.kde.plasma.airplanemode"), + QStringLiteral("org.kde.plasma.audio"), + QStringLiteral("org.kde.plasma.battery"), + QStringLiteral("org.kde.plasma.location"), + QStringLiteral("org.kde.plasma.nightcolor"), + QStringLiteral("org.kde.plasma.screenshot"), + QStringLiteral("org.kde.plasma.powermenu"), + QStringLiteral("org.kde.plasma.keyboardtoggle"), + QStringLiteral("org.kde.plasma.caffeine"), + }); +} + +void MobileShellSettings::setEnabledQuickSettings(QList &list) +{ + auto group = KConfigGroup{m_config, GENERAL_CONFIG_GROUP}; + group.writeEntry("enabledQuickSettings", list, KConfigGroup::Notify); + m_config->sync(); +} + +QList MobileShellSettings::disabledQuickSettings() const +{ + auto group = KConfigGroup{m_config, QUICKSETTINGS_CONFIG_GROUP}; + return group.readEntry("disabledQuickSettings", QList{}); +} + +void MobileShellSettings::setDisabledQuickSettings(QList &list) +{ + auto group = KConfigGroup{m_config, GENERAL_CONFIG_GROUP}; + group.writeEntry("disabledQuickSettings", list, KConfigGroup::Notify); + m_config->sync(); +} diff --git a/libmobileshell/mobileshellsettings.h b/libmobileshell/mobileshellsettings.h index 480ab40e..a0f44706 100644 --- a/libmobileshell/mobileshellsettings.h +++ b/libmobileshell/mobileshellsettings.h @@ -19,7 +19,7 @@ namespace MobileShell class MOBILESHELL_EXPORT MobileShellSettings : public QObject { Q_OBJECT - Q_PROPERTY(bool navigationPanelEnabled READ navigationPanelEnabled NOTIFY navigationPanelEnabledChanged) + Q_PROPERTY(bool navigationPanelEnabled READ navigationPanelEnabled WRITE setNavigationPanelEnabled NOTIFY navigationPanelEnabledChanged) public: static MobileShellSettings *self(); @@ -27,9 +27,18 @@ public: MobileShellSettings(QObject *parent = nullptr); bool navigationPanelEnabled() const; + void setNavigationPanelEnabled(bool navigationPanelEnabled); + + QList enabledQuickSettings() const; + void setEnabledQuickSettings(QList &list); + + QList disabledQuickSettings() const; + void setDisabledQuickSettings(QList &list); Q_SIGNALS: void navigationPanelEnabledChanged(); + void enabledQuickSettingsChanged(); + void disabledQuickSettingsChanged(); private: KConfigWatcher::Ptr m_configWatcher; diff --git a/libmobileshell/quicksettingsmodel.cpp b/libmobileshell/quicksettingsmodel.cpp index 8f9fa1ef..b98b15e9 100644 --- a/libmobileshell/quicksettingsmodel.cpp +++ b/libmobileshell/quicksettingsmodel.cpp @@ -1,5 +1,6 @@ /* * SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez + * SPDX-FileCopyrightText: 2022 Devin Lin * * SPDX-License-Identifier: LGPL-2.0-or-later */ @@ -7,6 +8,7 @@ #include "quicksettingsmodel.h" #include + #include #include #include @@ -14,7 +16,21 @@ using namespace MobileShell; QuickSettingsModel::QuickSettingsModel(QObject *parent) - : QAbstractListModel(parent) + : QAbstractListModel{parent} + , m_savedQuickSettings{new SavedQuickSettings{this}} +{ + connect(m_savedQuickSettings->enabledQuickSettingsModel(), &SavedQuickSettingsModel::dataUpdated, this, [this]() { + loadQuickSettings(); + }); +} + +void QuickSettingsModel::classBegin() +{ + m_loaded = true; + loadQuickSettings(); +} + +void QuickSettingsModel::componentComplete() { } @@ -23,17 +39,10 @@ QHash QuickSettingsModel::roleNames() const return {{Qt::UserRole, "modelData"}}; } -QQmlListProperty QuickSettingsModel::children() -{ - return QQmlListProperty(this, &m_children); -} - int QuickSettingsModel::rowCount(const QModelIndex &parent) const { - if (parent.isValid()) - return 0; - - return m_children.count() + m_external.count(); + Q_UNUSED(parent); + return m_quickSettings.size(); } QVariant QuickSettingsModel::data(const QModelIndex &index, int role) const @@ -42,59 +51,50 @@ QVariant QuickSettingsModel::data(const QModelIndex &index, int role) const return {}; } - QObject *obj = nullptr; - if (index.row() < m_children.count()) { - obj = m_children[index.row()]; - } else { - obj = m_external[index.row() - m_children.count()]; - } - + QObject *obj = m_quickSettings[index.row()]; return QVariant::fromValue(obj); } -void QuickSettingsModel::classBegin() +void QuickSettingsModel::loadQuickSettings() { + if (!m_loaded) { + return; + } + + for (auto *quickSetting : m_quickSettings) { + quickSetting->deleteLater(); + } + m_quickSettings.clear(); + QQmlEngine *engine = qmlEngine(this); + QQmlComponent *c = new QQmlComponent(engine, this); - const auto packages = KPackage::PackageLoader::self()->listPackages(QStringLiteral("KPackage/GenericQML"), "plasma/quicksettings"); - auto c = new QQmlComponent(engine, this); - - for (const auto &metaData : packages) { + // loop through enabled quick settings metadata + for (const auto &metaData : m_savedQuickSettings->enabledQuickSettingsModel()->list()) { + // load kpackage KPackage::Package package = KPackage::PackageLoader::self()->loadPackage("KPackage/GenericQML", QFileInfo(metaData.fileName()).path()); if (!package.isValid()) { - qWarning() << "Quick setting package invalid:" << metaData.fileName(); continue; } + // load QML from kpackage c->loadUrl(package.fileUrl("mainscript"), QQmlComponent::PreferSynchronous); auto created = c->create(engine->rootContext()); auto createdSetting = qobject_cast(created); + // print errors if there were issues loading if (!createdSetting) { - qWarning() << "Could not load" << metaData.fileName() << created; - if (c->isError()) { - for (const auto &error : c->errors()) { - qDebug() << error; - } + qWarning() << "Unable to load quick setting element:" << created; + for (auto error : c->errors()) { + qWarning() << error; } delete created; } else { qDebug() << "Loaded quicksetting" << metaData.fileName(); - m_external += createdSetting; + m_quickSettings.push_back(createdSetting); } } + delete c; } - -void QuickSettingsModel::componentComplete() -{ -} - -void QuickSettingsModel::include(QuickSetting *item) -{ - const int c = m_children.count() + m_external.count(); - beginInsertRows({}, c, c); - m_external.append(item); - endInsertRows(); -} diff --git a/libmobileshell/quicksettingsmodel.h b/libmobileshell/quicksettingsmodel.h index 91e78e9b..c1223805 100644 --- a/libmobileshell/quicksettingsmodel.h +++ b/libmobileshell/quicksettingsmodel.h @@ -1,5 +1,6 @@ /* * SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez + * SPDX-FileCopyrightText: 2022 Devin Lin * * SPDX-License-Identifier: LGPL-2.0-or-later */ @@ -8,6 +9,8 @@ #include "qqml.h" #include "quicksetting.h" +#include "savedquicksettings.h" +#include "savedquicksettingsmodel.h" #include #include @@ -21,8 +24,6 @@ class MOBILESHELL_EXPORT QuickSettingsModel : public QAbstractListModel, public { Q_OBJECT Q_INTERFACES(QQmlParserStatus) - Q_PROPERTY(QQmlListProperty children READ children NOTIFY childrenChanged) - Q_CLASSINFO("DefaultProperty", "children") QML_ELEMENT public: @@ -32,18 +33,14 @@ public: int rowCount(const QModelIndex &parent) const override; QHash roleNames() const override; - QQmlListProperty children(); - void classBegin() override; void componentComplete() override; - Q_SCRIPTABLE void include(QuickSetting *item); - -Q_SIGNALS: - void childrenChanged(); - private: - QList m_children; - QList m_external; + void loadQuickSettings(); + + bool m_loaded = false; + QList m_quickSettings; + SavedQuickSettings *m_savedQuickSettings; }; } // namespace MobileShell diff --git a/libmobileshell/savedquicksettings.cpp b/libmobileshell/savedquicksettings.cpp new file mode 100644 index 00000000..31ad143e --- /dev/null +++ b/libmobileshell/savedquicksettings.cpp @@ -0,0 +1,122 @@ +// SPDX-FileCopyrightText: 2022 Devin Lin +// SPDX-License-Identifier: LGPL-2.0-or-later + +#include "savedquicksettings.h" + +#include + +#include + +using namespace MobileShell; + +SavedQuickSettings::SavedQuickSettings(QObject *parent) + : QObject{parent} + , m_settings{new MobileShellSettings{this}} + , m_validPackages{} + , m_enabledPackages{} + , m_disabledPackages{} + , m_enabledQSModel{new SavedQuickSettingsModel{this}} + , m_disabledQSModel{new SavedQuickSettingsModel{this}} +{ + // load quicksettings packages + auto packages = KPackage::PackageLoader::self()->listPackages(QStringLiteral("KPackage/GenericQML"), "plasma/quicksettings"); + + for (auto &metaData : packages) { + KPackage::Package package = KPackage::PackageLoader::self()->loadPackage("KPackage/GenericQML", QFileInfo(metaData.fileName()).path()); + if (!package.isValid()) { + qWarning() << "Quick setting package invalid:" << metaData.fileName(); + continue; + } + m_validPackages.push_back(metaData); + } + + // subscribe to config changes + connect(m_settings, &MobileShellSettings::enabledQuickSettingsChanged, this, [this]() { + refreshModel(); + }); + connect(m_settings, &MobileShellSettings::disabledQuickSettingsChanged, this, [this]() { + refreshModel(); + }); + + // subscribe to model changes + connect(m_enabledQSModel, &SavedQuickSettingsModel::dataUpdated, this, [this](QList data) -> void { + m_enabledPackages.clear(); + for (auto metaData : data) { + m_enabledPackages.push_back(metaData); + } + }); + connect(m_disabledQSModel, &SavedQuickSettingsModel::dataUpdated, this, [this](QList data) -> void { + m_disabledPackages.clear(); + for (auto metaData : data) { + m_disabledPackages.push_back(metaData); + } + }); + + // load + refreshModel(); +} + +SavedQuickSettingsModel *SavedQuickSettings::enabledQuickSettingsModel() const +{ + return m_enabledQSModel; +} + +SavedQuickSettingsModel *SavedQuickSettings::disabledQuickSettingsModel() const +{ + return m_disabledQSModel; +} + +void SavedQuickSettings::refreshModel() +{ + QList enabledQS = m_settings->enabledQuickSettings(); + QList disabledQS = m_settings->disabledQuickSettings(); + + m_enabledPackages.clear(); + m_disabledPackages.clear(); + + // add enabled quick settings in order + for (const QString &pluginId : enabledQS) { + for (auto &metaData : m_validPackages) { + if (pluginId == metaData.pluginId()) { + m_enabledPackages.push_back(metaData); + break; + } + } + } + + // add disabled quick settings in order + for (const QString &pluginId : disabledQS) { + for (auto &metaData : m_validPackages) { + if (pluginId == metaData.pluginId()) { + m_disabledPackages.push_back(metaData); + break; + } + } + } + + // add undefined quick settings to the back of enabled quick settings + for (auto &metaData : m_validPackages) { + if (!enabledQS.contains(metaData.pluginId()) && !disabledQS.contains(metaData.pluginId())) { + m_enabledPackages.push_back(metaData); + } + } + + m_enabledQSModel->updateData(m_enabledPackages); + m_disabledQSModel->updateData(m_disabledPackages); +} + +void SavedQuickSettings::saveModel() +{ + QList enabledQS; + QList disabledQS; + + for (auto &metaData : m_enabledPackages) { + enabledQS.push_back(metaData.pluginId()); + } + for (auto &metaData : m_disabledPackages) { + disabledQS.push_back(metaData.pluginId()); + } + + m_settings->setEnabledQuickSettings(enabledQS); + m_settings->setDisabledQuickSettings(disabledQS); +} diff --git a/libmobileshell/savedquicksettings.h b/libmobileshell/savedquicksettings.h new file mode 100644 index 00000000..1d828850 --- /dev/null +++ b/libmobileshell/savedquicksettings.h @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2022 Devin Lin +// SPDX-License-Identifier: LGPL-2.0-or-later + +#pragma once + +#include "mobileshellsettings.h" +#include "qqml.h" +#include "quicksetting.h" +#include "savedquicksettingsmodel.h" + +#include +#include + +#include +#include + +#include "mobileshell_export.h" + +namespace MobileShell +{ + +class MOBILESHELL_EXPORT SavedQuickSettings : public QObject +{ + Q_OBJECT + +public: + SavedQuickSettings(QObject *parent = nullptr); + + SavedQuickSettingsModel *enabledQuickSettingsModel() const; + SavedQuickSettingsModel *disabledQuickSettingsModel() const; + +private: + void refreshModel(); + void saveModel(); + + MobileShellSettings *m_settings; + QList m_validPackages; + QList m_enabledPackages; + QList m_disabledPackages; + + SavedQuickSettingsModel *m_enabledQSModel; + SavedQuickSettingsModel *m_disabledQSModel; +}; + +} // namespace MobileShell diff --git a/libmobileshell/savedquicksettingsmodel.cpp b/libmobileshell/savedquicksettingsmodel.cpp new file mode 100644 index 00000000..c824e20a --- /dev/null +++ b/libmobileshell/savedquicksettingsmodel.cpp @@ -0,0 +1,92 @@ +// SPDX-FileCopyrightText: 2022 Devin Lin +// SPDX-License-Identifier: LGPL-2.0-or-later + +#include "savedquicksettingsmodel.h" + +using namespace MobileShell; + +SavedQuickSettingsModel::SavedQuickSettingsModel(QObject *parent) + : QAbstractListModel{parent} +{ +} + +QVariant SavedQuickSettingsModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() >= m_data.count()) { + return QVariant(); + } + + if (role == NameRole) { + return m_data[index.row()].name(); + } else if (role == IconRole) { + return QString(); // TODO m_data[index.row()].icon(); + } else if (role == IdRole) { + return m_data[index.row()].pluginId(); + } + return QVariant(); +} + +int SavedQuickSettingsModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return m_data.count(); +} + +QHash SavedQuickSettingsModel::roleNames() const +{ + return {{NameRole, "name"}, {IdRole, "id"}, {IconRole, "icon"}}; +} + +void SavedQuickSettingsModel::moveRow(int oldIndex, int newIndex) +{ + if (oldIndex < 0 || oldIndex >= m_data.count() || newIndex < 0 || newIndex >= m_data.count()) { + return; + } + + Q_EMIT beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newIndex); + std::iter_swap(m_data.begin() + oldIndex, m_data.end() + newIndex); + Q_EMIT endMoveRows(); + + Q_EMIT dataUpdated(m_data); +} + +void SavedQuickSettingsModel::insertRow(KPluginMetaData metaData, int index) +{ + Q_EMIT beginInsertRows(QModelIndex(), index, index); + m_data.insert(index, metaData); + Q_EMIT endInsertRows(); + + Q_EMIT dataUpdated(m_data); +} + +void SavedQuickSettingsModel::removeRow(int index) +{ + if (index < 0 || index >= m_data.size()) { + return; + } + + Q_EMIT beginRemoveRows(QModelIndex(), index, index); + m_data.erase(m_data.begin() + index); + Q_EMIT endRemoveRows(); + + Q_EMIT dataUpdated(m_data); +} + +QList SavedQuickSettingsModel::list() const +{ + return m_data; +} + +void SavedQuickSettingsModel::updateData(QList data) +{ + Q_EMIT beginResetModel(); + + m_data.clear(); + for (auto metaData : data) { + m_data.push_back(metaData); + } + + Q_EMIT endResetModel(); + + Q_EMIT dataUpdated(m_data); +} diff --git a/libmobileshell/savedquicksettingsmodel.h b/libmobileshell/savedquicksettingsmodel.h new file mode 100644 index 00000000..0cc6003e --- /dev/null +++ b/libmobileshell/savedquicksettingsmodel.h @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: 2022 Devin Lin +// SPDX-License-Identifier: LGPL-2.0-or-later + +#pragma once + +#include "mobileshellsettings.h" +#include "qqml.h" +#include "quicksetting.h" + +#include + +#include +#include + +#include "mobileshell_export.h" + +namespace MobileShell +{ + +class MOBILESHELL_EXPORT SavedQuickSettingsModel : public QAbstractListModel +{ + Q_OBJECT + +public: + SavedQuickSettingsModel(QObject *parent = nullptr); + + enum { + NameRole, + IdRole, + IconRole, + }; + + QVariant data(const QModelIndex &index, int role) const override; + int rowCount(const QModelIndex &parent) const override; + QHash roleNames() const override; + + Q_INVOKABLE void moveRow(int oldIndex, int newIndex); + Q_INVOKABLE void insertRow(KPluginMetaData metaData, int index); + Q_INVOKABLE void removeRow(int index); + + QList list() const; + +public Q_SLOTS: + void updateData(QList data); + +Q_SIGNALS: + void dataUpdated(QList data); + +private: + QList m_data; +}; + +} // namespace MobileShell