Use synchrone component loading instead of asynchrone to avoid concurrent bug

This commit is contained in:
Florian RICHER 2025-06-14 22:06:17 +02:00 committed by Florian RICHER (aka MrDev023)
parent 4dec0e8c1d
commit 3e0677b78e
2 changed files with 54 additions and 64 deletions

View file

@ -82,6 +82,50 @@ QVariant QuickSettingsModel::data(const QModelIndex &index, int role) const
return QVariant::fromValue<QObject *>(obj); return QVariant::fromValue<QObject *>(obj);
} }
QuickSetting *QuickSettingsModel::loadQuickSettingComponent(KPluginMetaData metaData)
{
// Load KPackage
const KPackage::Package package = KPackage::PackageLoader::self()->loadPackage("KPackage/GenericQML", QFileInfo(metaData.fileName()).path());
if (!package.isValid()) {
return nullptr;
}
// Create translation context
QQmlEngine *engine = qmlEngine(this);
KLocalizedContext *i18nContext = new KLocalizedContext(engine);
i18nContext->setTranslationDomain(QLatin1String("plasma_") + metaData.pluginId());
engine->rootContext()->setContextObject(i18nContext);
// Create component synchronously
QQmlComponent component(engine, package.fileUrl("mainscript"));
if (component.isError()) {
qWarning() << "Unable to load quick setting element:" << metaData.pluginId();
for (auto error : component.errors()) {
qWarning() << error;
}
return nullptr;
}
// Create object
QObject *object = component.create(engine->rootContext());
if (!object) {
qWarning() << "Unable to create quick setting element:" << metaData.pluginId();
return nullptr;
}
auto createdSetting = qobject_cast<QuickSetting *>(object);
if (createdSetting && createdSetting->isAvailable()) {
// Connect availability signal
connect(createdSetting, &QuickSetting::availableChanged, this, [this, metaData, createdSetting]() {
availabilityChanged(metaData, createdSetting);
});
return createdSetting;
} else {
object->deleteLater();
return nullptr;
}
}
void QuickSettingsModel::loadQuickSettings() void QuickSettingsModel::loadQuickSettings()
{ {
if (!m_loaded) { if (!m_loaded) {
@ -96,10 +140,12 @@ void QuickSettingsModel::loadQuickSettings()
m_quickSettings.clear(); m_quickSettings.clear();
m_quickSettingsMetaData.clear(); m_quickSettingsMetaData.clear();
// Loop through enabled quick settings and start loading them // Loop through enabled quick settings and load them synchronously
for (const auto &metaData : m_savedQuickSettings->enabledQuickSettingsModel()->list()) { for (const auto &metaData : m_savedQuickSettings->enabledQuickSettingsModel()->list()) {
// false - Ensure row insertion signals aren't emitted (since we are resetting the model) if (auto *setting = loadQuickSettingComponent(metaData)) {
loadQuickSetting(metaData, false); m_quickSettings.append(setting);
m_quickSettingsMetaData.append(metaData);
}
} }
endResetModel(); endResetModel();
@ -112,31 +158,8 @@ void QuickSettingsModel::loadQuickSetting(KPluginMetaData metaData, bool emitIns
return; return;
} }
// Load KPackage if (auto *setting = loadQuickSettingComponent(metaData)) {
const KPackage::Package package = KPackage::PackageLoader::self()->loadPackage("KPackage/GenericQML", QFileInfo(metaData.fileName()).path()); insertQuickSettingToModel(metaData, setting, emitInsertSignal);
if (!package.isValid()) {
return;
}
// 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);
} }
} }
@ -151,41 +174,6 @@ void QuickSettingsModel::removeQuickSetting(int index)
} }
} }
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) void QuickSettingsModel::insertQuickSettingToModel(KPluginMetaData metaData, QuickSetting *quickSetting, bool emitInsertSignal)
{ {
// Insert into correct position based on the saved quick settings order // Insert into correct position based on the saved quick settings order

View file

@ -12,9 +12,11 @@
#include "savedquicksettings.h" #include "savedquicksettings.h"
#include "savedquicksettingsmodel.h" #include "savedquicksettingsmodel.h"
#include <KPluginMetaData>
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QQmlComponent> #include <QQmlComponent>
#include <QQmlListProperty> #include <QQmlListProperty>
#include <qqmlregistration.h>
class QuickSettingsModel : public QAbstractListModel, public QQmlParserStatus class QuickSettingsModel : public QAbstractListModel, public QQmlParserStatus
{ {
@ -44,8 +46,8 @@ private:
void loadQuickSetting(KPluginMetaData metaData, bool emitInsertSignal); void loadQuickSetting(KPluginMetaData metaData, bool emitInsertSignal);
void removeQuickSetting(int index); void removeQuickSetting(int index);
void afterQuickSettingLoad(QQmlEngine *engine, KPluginMetaData metaData, QQmlComponent *component, bool emitInsertSignal);
void insertQuickSettingToModel(KPluginMetaData metaData, QuickSetting *quickSetting, bool emitInsertSignal); void insertQuickSettingToModel(KPluginMetaData metaData, QuickSetting *quickSetting, bool emitInsertSignal);
QuickSetting *loadQuickSettingComponent(KPluginMetaData metaData);
bool m_loaded{false}; bool m_loaded{false};