mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-07-31 00:34:45 +00:00
quicksettingsplugin: Load quicksettings QML async
Resolves https://invent.kde.org/plasma/plasma-mobile/-/issues/379 This loads the quicksettings packages asynchronously. It also adds support for listening to model changes properly (row add/remove/move events) rather than have it reset every time a change is done.
This commit is contained in:
parent
daf43eae27
commit
2f64036a4f
2 changed files with 165 additions and 57 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@kde.org>
|
* SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||||
* SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
|
* SPDX-FileCopyrightText: 2022-2024 Devin Lin <devin@kde.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
@ -9,19 +9,46 @@
|
||||||
|
|
||||||
#include <KPackage/PackageLoader>
|
#include <KPackage/PackageLoader>
|
||||||
|
|
||||||
#include <QFileInfo>
|
|
||||||
#include <QQmlComponent>
|
|
||||||
#include <QQmlEngine>
|
|
||||||
#include <QQmlContext>
|
|
||||||
#include <KLocalizedContext>
|
#include <KLocalizedContext>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QQmlContext>
|
||||||
|
#include <QQmlEngine>
|
||||||
|
|
||||||
QuickSettingsModel::QuickSettingsModel(QObject *parent)
|
QuickSettingsModel::QuickSettingsModel(QObject *parent)
|
||||||
: QAbstractListModel{parent}
|
: QAbstractListModel{parent}
|
||||||
, m_savedQuickSettings{new SavedQuickSettings{this}}
|
, m_savedQuickSettings{new SavedQuickSettings{this}}
|
||||||
{
|
{
|
||||||
connect(m_savedQuickSettings->enabledQuickSettingsModel(), &SavedQuickSettingsModel::dataUpdated, this, [this]() {
|
// Listen to events to enabled quicksettings, and update accordingly
|
||||||
|
connect(m_savedQuickSettings->enabledQuickSettingsModel(), &SavedQuickSettingsModel::modelReset, this, [this]() {
|
||||||
loadQuickSettings();
|
loadQuickSettings();
|
||||||
});
|
});
|
||||||
|
connect(m_savedQuickSettings->enabledQuickSettingsModel(), &SavedQuickSettingsModel::rowsInserted, this, [this](const QModelIndex &, int first, int last) {
|
||||||
|
for (int i = first; i <= last; ++i) {
|
||||||
|
KPluginMetaData metaData = m_savedQuickSettings->enabledQuickSettingsModel()->takeRow(i);
|
||||||
|
loadQuickSetting(metaData, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(m_savedQuickSettings->enabledQuickSettingsModel(),
|
||||||
|
&SavedQuickSettingsModel::rowsAboutToBeRemoved,
|
||||||
|
this,
|
||||||
|
[this](const QModelIndex &, int first, int last) {
|
||||||
|
for (int i = first; i <= last; ++i) {
|
||||||
|
KPluginMetaData metaData = m_savedQuickSettings->enabledQuickSettingsModel()->takeRow(i);
|
||||||
|
auto index = m_quickSettingsMetaData.indexOf(metaData);
|
||||||
|
removeQuickSetting(index);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(m_savedQuickSettings->enabledQuickSettingsModel(),
|
||||||
|
&SavedQuickSettingsModel::rowsMoved,
|
||||||
|
this,
|
||||||
|
[this](const QModelIndex &, int sourceStart, int sourceEnd, const QModelIndex &, int) {
|
||||||
|
for (int i = sourceStart; i <= sourceEnd; ++i) {
|
||||||
|
KPluginMetaData metaData = m_savedQuickSettings->enabledQuickSettingsModel()->takeRow(i);
|
||||||
|
auto index = m_quickSettingsMetaData.indexOf(metaData);
|
||||||
|
removeQuickSetting(index);
|
||||||
|
loadQuickSetting(metaData, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickSettingsModel::classBegin()
|
void QuickSettingsModel::classBegin()
|
||||||
|
|
@ -67,67 +94,137 @@ void QuickSettingsModel::loadQuickSettings()
|
||||||
quickSetting->deleteLater();
|
quickSetting->deleteLater();
|
||||||
}
|
}
|
||||||
m_quickSettings.clear();
|
m_quickSettings.clear();
|
||||||
|
m_quickSettingsMetaData.clear();
|
||||||
|
|
||||||
QQmlEngine *engine = qmlEngine(this);
|
// Loop through enabled quick settings and start loading them
|
||||||
QQmlComponent *c = new QQmlComponent(engine, this);
|
|
||||||
|
|
||||||
// loop through enabled quick settings metadata
|
|
||||||
for (const auto &metaData : m_savedQuickSettings->enabledQuickSettingsModel()->list()) {
|
for (const auto &metaData : m_savedQuickSettings->enabledQuickSettingsModel()->list()) {
|
||||||
// load kpackage
|
// false - Ensure row insertion signals aren't emitted (since we are resetting the model)
|
||||||
KPackage::Package package = KPackage::PackageLoader::self()->loadPackage("KPackage/GenericQML", QFileInfo(metaData.fileName()).path());
|
loadQuickSetting(metaData, false);
|
||||||
if (!package.isValid()) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// load QML from kpackage
|
|
||||||
c->loadUrl(package.fileUrl("mainscript"), QQmlComponent::PreferSynchronous);
|
|
||||||
KLocalizedContext *i18nContext = new KLocalizedContext(engine);
|
|
||||||
i18nContext->setTranslationDomain(QLatin1String("plasma_") + metaData.pluginId());
|
|
||||||
engine->rootContext()->setContextObject(i18nContext);
|
|
||||||
|
|
||||||
auto created = c->create(engine->rootContext());
|
|
||||||
auto createdSetting = qobject_cast<QuickSetting *>(created);
|
|
||||||
|
|
||||||
// print errors if there were issues loading
|
|
||||||
if (!createdSetting) {
|
|
||||||
qWarning() << "Unable to load quick setting element:" << created;
|
|
||||||
for (auto error : c->errors()) {
|
|
||||||
qWarning() << error;
|
|
||||||
}
|
|
||||||
delete created;
|
|
||||||
} else {
|
|
||||||
if (createdSetting->isAvailable()) {
|
|
||||||
m_quickSettings.push_back(createdSetting);
|
|
||||||
}
|
|
||||||
connect(createdSetting, &QuickSetting::availableChanged, this, &QuickSettingsModel::availabilityChanged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete c;
|
|
||||||
|
|
||||||
endResetModel();
|
endResetModel();
|
||||||
Q_EMIT countChanged();
|
Q_EMIT countChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickSettingsModel::availabilityChanged()
|
void QuickSettingsModel::loadQuickSetting(KPluginMetaData metaData, bool emitInsertSignal)
|
||||||
{
|
{
|
||||||
auto setting = qobject_cast<QuickSetting *>(sender());
|
if (!m_loaded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (setting->isAvailable()) {
|
// Load KPackage
|
||||||
if (!m_quickSettings.contains(setting)) {
|
const KPackage::Package package = KPackage::PackageLoader::self()->loadPackage("KPackage/GenericQML", QFileInfo(metaData.fileName()).path());
|
||||||
auto idx = m_quickSettings.count();
|
if (!package.isValid()) {
|
||||||
beginInsertRows({}, idx, idx);
|
return;
|
||||||
m_quickSettings.append(setting);
|
}
|
||||||
|
|
||||||
|
// Create translation context
|
||||||
|
QQmlEngine *engine = qmlEngine(this);
|
||||||
|
KLocalizedContext *i18nContext = new KLocalizedContext(engine);
|
||||||
|
i18nContext->setTranslationDomain(QLatin1String("plasma_") + metaData.pluginId());
|
||||||
|
engine->rootContext()->setContextObject(i18nContext);
|
||||||
|
|
||||||
|
QQmlComponent *component = new QQmlComponent(engine, this);
|
||||||
|
|
||||||
|
// Load QML from KPackage async
|
||||||
|
component->loadUrl(package.fileUrl("mainscript"), QQmlComponent::Asynchronous);
|
||||||
|
|
||||||
|
if (component->isLoading()) {
|
||||||
|
// Listen to load completion
|
||||||
|
connect(component, &QQmlComponent::statusChanged, this, [this, metaData, component, engine]() {
|
||||||
|
afterQuickSettingLoad(engine, metaData, component, true);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Only emit insertion signal if we aren't resetting the model
|
||||||
|
afterQuickSettingLoad(engine, metaData, component, emitInsertSignal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QuickSettingsModel::removeQuickSetting(int index)
|
||||||
|
{
|
||||||
|
if (index >= 0) {
|
||||||
|
beginRemoveRows({}, index, 0);
|
||||||
|
m_quickSettings.removeAt(index);
|
||||||
|
m_quickSettingsMetaData.removeAt(index);
|
||||||
|
endRemoveRows();
|
||||||
|
Q_EMIT countChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QuickSettingsModel::afterQuickSettingLoad(QQmlEngine *engine, KPluginMetaData metaData, QQmlComponent *component, bool emitInsertSignal)
|
||||||
|
{
|
||||||
|
// Create quicksetting component
|
||||||
|
QObject *object = component->create(engine->rootContext());
|
||||||
|
if (!object) {
|
||||||
|
qWarning() << "Unable to load quick setting element:" << metaData.pluginId();
|
||||||
|
component->deleteLater();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component->isError()) {
|
||||||
|
// Print errors
|
||||||
|
qWarning() << "Unable to load quick setting element:" << metaData.pluginId();
|
||||||
|
for (auto error : component->errors()) {
|
||||||
|
qWarning() << error;
|
||||||
|
}
|
||||||
|
|
||||||
|
component->deleteLater();
|
||||||
|
} else if (component->isReady()) {
|
||||||
|
component->deleteLater();
|
||||||
|
|
||||||
|
auto createdSetting = qobject_cast<QuickSetting *>(object);
|
||||||
|
|
||||||
|
// Connect availability signal to insert/remove quicksetting into model
|
||||||
|
connect(createdSetting, &QuickSetting::availableChanged, this, [this, metaData, createdSetting]() {
|
||||||
|
availabilityChanged(metaData, createdSetting);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add quicksetting to model if available
|
||||||
|
if (createdSetting->isAvailable()) {
|
||||||
|
insertQuickSettingToModel(metaData, createdSetting, emitInsertSignal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QuickSettingsModel::insertQuickSettingToModel(KPluginMetaData metaData, QuickSetting *quickSetting, bool emitInsertSignal)
|
||||||
|
{
|
||||||
|
// Insert into correct position based on the saved quick settings order
|
||||||
|
int insertIndex = 0;
|
||||||
|
auto list = m_savedQuickSettings->enabledQuickSettingsModel()->list();
|
||||||
|
for (int i = 0; i < list.size(); ++i) {
|
||||||
|
if (insertIndex >= m_quickSettingsMetaData.size()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list[i].pluginId() == m_quickSettingsMetaData[insertIndex].pluginId()) {
|
||||||
|
if (metaData.pluginId() == list[i].pluginId()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++insertIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emitInsertSignal) {
|
||||||
|
beginInsertRows({}, insertIndex, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_quickSettings.insert(insertIndex, quickSetting);
|
||||||
|
m_quickSettingsMetaData.insert(insertIndex, metaData);
|
||||||
|
|
||||||
|
if (emitInsertSignal) {
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
auto idx = m_quickSettings.indexOf(setting);
|
|
||||||
if (idx >= 0) {
|
|
||||||
beginRemoveRows({}, idx, idx);
|
|
||||||
m_quickSettings.removeAt(idx);
|
|
||||||
endRemoveRows();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_EMIT countChanged();
|
Q_EMIT countChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QuickSettingsModel::availabilityChanged(KPluginMetaData metaData, QuickSetting *quickSetting)
|
||||||
|
{
|
||||||
|
if (quickSetting->isAvailable()) {
|
||||||
|
if (!m_quickSettings.contains(quickSetting)) {
|
||||||
|
insertQuickSettingToModel(metaData, quickSetting, true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto idx = m_quickSettings.indexOf(quickSetting);
|
||||||
|
removeQuickSetting(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
#include "savedquicksettingsmodel.h"
|
#include "savedquicksettingsmodel.h"
|
||||||
|
|
||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
|
#include <QQmlComponent>
|
||||||
#include <QQmlListProperty>
|
#include <QQmlListProperty>
|
||||||
|
|
||||||
class QuickSettingsModel : public QAbstractListModel, public QQmlParserStatus
|
class QuickSettingsModel : public QAbstractListModel, public QQmlParserStatus
|
||||||
|
|
@ -38,9 +39,19 @@ Q_SIGNALS:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void loadQuickSettings();
|
void loadQuickSettings();
|
||||||
void availabilityChanged();
|
void availabilityChanged(KPluginMetaData metaData, QuickSetting *quickSetting);
|
||||||
|
|
||||||
bool m_loaded = false;
|
void loadQuickSetting(KPluginMetaData metaData, bool emitInsertSignal);
|
||||||
|
void removeQuickSetting(int index);
|
||||||
|
|
||||||
|
void afterQuickSettingLoad(QQmlEngine *engine, KPluginMetaData metaData, QQmlComponent *component, bool emitInsertSignal);
|
||||||
|
void insertQuickSettingToModel(KPluginMetaData metaData, QuickSetting *quickSetting, bool emitInsertSignal);
|
||||||
|
|
||||||
|
bool m_loaded{false};
|
||||||
|
|
||||||
|
// m_quickSettings and m_quickSettingsMetaData indices match to same quick setting
|
||||||
QList<QuickSetting *> m_quickSettings;
|
QList<QuickSetting *> m_quickSettings;
|
||||||
SavedQuickSettings *m_savedQuickSettings;
|
QList<KPluginMetaData> m_quickSettingsMetaData;
|
||||||
|
|
||||||
|
SavedQuickSettings *m_savedQuickSettings{nullptr};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue