From 30625c5fd55efe666af6c6794e99e751bec554cc Mon Sep 17 00:00:00 2001 From: Lu YaNing Date: Wed, 7 Jan 2026 16:10:22 +0800 Subject: [PATCH] fix: Refactor DConfig wrapper class generation for thread safety and lifecycle management Problem: - Single-threaded design with weak state machine (Invalid -> Succeed/Failed) - No proper handling of object destruction during initialization - Signal emissions in worker thread context (incorrect thread context) - Fragile destructor unable to handle all cleanup scenarios Solution: 1. Introduce Data layer separation (TreelandUserConfigData + TreelandUserConfig) - Clear separation between internal data management and public API - Enables safer object lifecycle management 2. Enhance state machine (3-state -> 5-state model) - Add Initializing and Destroyed states - Use atomic CAS operations for thread-safe state transitions - States: Invalid -> Initializing -> (Succeed | Failed | Destroyed) 3. Improve async initialization and cleanup - Use QPointer for safe backref checks (prevent use-after-free) - Support 4 destruction paths: normal/failed/quick/mid-initialization - Atomic state transitions with proper signal emission guards 4. Separate thread responsibilities - updateValue(): Worker thread reads config values - updateProperty(): Main thread updates properties and emits signals - Use QMetaObject::invokeMethod for correct thread context Improvements: - Thread safety: Complete atomic operations coverage - Memory safety: QPointer guards prevent dangling pointers - Code clarity: Layered architecture with clear responsibilities - Backward compatibility: API unchanged --- src/log/dconfig_org_deepin_dtk_preference.hpp | 966 +++++++----- .../dconfig2cpp/dconf-example_meta.hpp | 1295 ++++++++++++----- ...dconf-example_other_app_configure_meta.hpp | 453 ++++-- .../dconfig2cpp/dconf-global_meta.hpp | 382 +++-- tools/dconfig2cpp/main.cpp | 326 +++-- 5 files changed, 2374 insertions(+), 1048 deletions(-) diff --git a/src/log/dconfig_org_deepin_dtk_preference.hpp b/src/log/dconfig_org_deepin_dtk_preference.hpp index 4fea5196..6455fabd 100644 --- a/src/log/dconfig_org_deepin_dtk_preference.hpp +++ b/src/log/dconfig_org_deepin_dtk_preference.hpp @@ -1,7 +1,7 @@ /** * This file is generated by dconfig2cpp. - * Command line arguments: ./build/tools/dconfig2cpp/dconfig2cpp /usr/share/dsg/configs/org.deepin.dtk.preference.json - * Generation time: 2025-08-20T15:40:55 + * Command line arguments: ./build/tools/dconfig2cpp/dconfig2cpp /usr/share/dsg/configs/org.deepin.dtk.preference.json -o ./src/log/dconfig_org_deepin_dtk_preference.hpp + * Generation time: 2026-01-19T15:29:47 * JSON file version: 1.0 * * WARNING: DO NOT MODIFY THIS FILE MANUALLY. @@ -20,6 +20,7 @@ #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #include #endif +#include #include #include @@ -27,6 +28,8 @@ class dconfig_org_deepin_dtk_preference : public QObject { Q_OBJECT Q_PROPERTY(bool autoDisplayFeature READ autoDisplayFeature WRITE setAutoDisplayFeature NOTIFY autoDisplayFeatureChanged RESET resetAutoDisplayFeature) + Q_PROPERTY(QString colorMode READ colorMode WRITE setColorMode NOTIFY colorModeChanged RESET resetColorMode) + Q_PROPERTY(QString defaultColorMode READ defaultColorMode WRITE setDefaultColorMode NOTIFY defaultColorModeChanged RESET resetDefaultColorMode) Q_PROPERTY(bool enableDtkAnimations READ enableDtkAnimations WRITE setEnableDtkAnimations NOTIFY enableDtkAnimationsChanged RESET resetEnableDtkAnimations) Q_PROPERTY(bool featureUpdated READ featureUpdated WRITE setFeatureUpdated NOTIFY featureUpdatedChanged RESET resetFeatureUpdated) Q_PROPERTY(bool keyboardsearchDisabled READ keyboardsearchDisabled WRITE setKeyboardsearchDisabled NOTIFY keyboardsearchDisabledChanged RESET resetKeyboardsearchDisabled) @@ -36,7 +39,7 @@ class dconfig_org_deepin_dtk_preference : public QObject { Q_PROPERTY(qlonglong themeType READ themeType WRITE setThemeType NOTIFY themeTypeChanged RESET resetThemeType) Q_PROPERTY(qlonglong titlebarHeight READ titlebarHeight WRITE setTitlebarHeight NOTIFY titlebarHeightChanged RESET resetTitlebarHeight) Q_PROPERTY(bool underlineShortcut READ underlineShortcut WRITE setUnderlineShortcut NOTIFY underlineShortcutChanged RESET resetUnderlineShortcut) - Q_CLASSINFO("DConfigKeyList", "autoDisplayFeature;enableDtkAnimations;featureUpdated;keyboardsearchDisabled;rules;scrollBarPolicy;sizeMode;themeType;titlebarHeight;underlineShortcut") + Q_CLASSINFO("DConfigKeyList", "autoDisplayFeature;colorMode;defaultColorMode;enableDtkAnimations;featureUpdated;keyboardsearchDisabled;rules;scrollBarPolicy;sizeMode;themeType;titlebarHeight;underlineShortcut") Q_CLASSINFO("DConfigFileName", "org.deepin.dtk.preference") Q_CLASSINFO("DConfigFileVersion", "1.0") @@ -44,15 +47,35 @@ class dconfig_org_deepin_dtk_preference : public QObject { explicit dconfig_org_deepin_dtk_preference(QThread *thread, DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &name, const QString &appId, const QString &subpath, bool isGeneric, QObject *parent) - : QObject(nullptr) { + : QObject(parent), m_data(new Data) { + m_data->m_userConfig = this; + m_data->moveToThread(this->thread()); + if (!thread->isRunning()) { qWarning() << QLatin1String("Warning: The provided thread is not running."); } Q_ASSERT(QThread::currentThread() != thread); auto worker = new QObject(); worker->moveToThread(thread); - QPointer watcher(parent); - QMetaObject::invokeMethod(worker, [=, this]() { + + QPointer safeData(m_data); + + QMetaObject::invokeMethod(worker, [safeData, backend, name, appId, subpath, isGeneric, worker]() mutable { + delete worker; + worker = nullptr; + + // Check if Data object is still valid + if (!safeData) { + return; + } + + if (!safeData->m_status.testAndSetOrdered(static_cast(Data::Status::Invalid), + static_cast(Data::Status::Initializing))) { + // CAS failed, state already changed - userConfig destructor will handle cleanup + // Do not attempt to delete here as it would race with destructor + return; + } + DTK_CORE_NAMESPACE::DConfig *config = nullptr; if (isGeneric) { if (backend) { @@ -77,21 +100,44 @@ class dconfig_org_deepin_dtk_preference : public QObject { } } } - if (!config) { + + if (!config || !config->isValid()) { qWarning() << QLatin1String("Failed to create DConfig instance."); - worker->deleteLater(); + + if (safeData && safeData->m_status.testAndSetOrdered(static_cast(Data::Status::Initializing), + static_cast(Data::Status::Failed))) { + if (safeData->m_userConfig) { + QMetaObject::invokeMethod(safeData, [safeData]() { + if (safeData && safeData->m_userConfig) { + Q_EMIT safeData->m_userConfig->configInitializeFailed(); + } + }, Qt::QueuedConnection); + } + } else { + // CAS failed, destroy data object + safeData->deleteLater(); + } + + if (config) + delete config; + return; } config->moveToThread(QThread::currentThread()); - initializeInConfigThread(config); - if (watcher != parent) { - // delete this if watcher is changed to nullptr. - deleteLater(); - } else if (!this->parent() && parent) { - // !parent() means that parent is not changed. - this->setParent(watcher); + // Initialize through Data class + safeData->initializeInConfigThread(config); + + // Try to transition from Initializing to Succeeded + if (safeData && safeData->m_status.testAndSetOrdered(static_cast(Data::Status::Initializing), + static_cast(Data::Status::Succeeded))) { + // CAS succeeded, connect destroyed signal + QObject::connect(config, &QObject::destroyed, safeData, &QObject::deleteLater); + } else { + // CAS failed - state changed (e.g., set to Destroyed) + // We must clean up the config we just created + config->deleteLater(); + safeData->deleteLater(); } - worker->deleteLater(); }); } static dconfig_org_deepin_dtk_preference* create(const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) @@ -111,30 +157,39 @@ class dconfig_org_deepin_dtk_preference : public QObject { static dconfig_org_deepin_dtk_preference* createGenericByName(DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &name, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) { return new dconfig_org_deepin_dtk_preference(thread, backend, name, {}, subpath, true, parent); } ~dconfig_org_deepin_dtk_preference() { - if (m_config.loadRelaxed()) { - m_config.loadRelaxed()->deleteLater(); - m_config.storeRelaxed(nullptr); + int oldStatus = m_data->m_status.fetchAndStoreOrdered(static_cast(Data::Status::Destroyed)); + + m_data->m_userConfig = nullptr; + if (oldStatus == static_cast(Data::Status::Succeeded)) { + // When Succeeded, release config object only + auto config = m_data->m_config.loadRelaxed(); + Q_ASSERT(config); + config->deleteLater(); + // m_data will be deleted by config's destroyed signal + } else if (oldStatus == static_cast(Data::Status::Failed) || + oldStatus == static_cast(Data::Status::Invalid)) { + // When Failed or Invalid, directly clean up Data object + m_data->deleteLater(); } } Q_INVOKABLE DTK_CORE_NAMESPACE::DConfig *config() const { - return m_config.loadRelaxed(); + return m_data->m_config.loadRelaxed(); } - Q_INVOKABLE bool isInitializeSucceed() const { - return m_status.loadRelaxed() == static_cast(Status::Succeed); + return m_data->m_status.loadRelaxed() == static_cast(Data::Status::Succeeded); } - Q_INVOKABLE bool isInitializeFailed() const { - return m_status.loadRelaxed() == static_cast(Status::Failed); + return m_data->m_status.loadRelaxed() == static_cast(Data::Status::Failed); } - Q_INVOKABLE bool isInitializing() const { - return m_status.loadRelaxed() == static_cast(Status::Invalid); + return m_data->m_status.loadRelaxed() == static_cast(Data::Status::Initializing); } Q_INVOKABLE QStringList keyList() const { return { QStringLiteral("autoDisplayFeature"), + QStringLiteral("colorMode"), + QStringLiteral("defaultColorMode"), QStringLiteral("enableDtkAnimations"), QStringLiteral("featureUpdated"), QStringLiteral("keyboardsearchDisabled"), @@ -149,6 +204,10 @@ class dconfig_org_deepin_dtk_preference : public QObject { Q_INVOKABLE bool isDefaultValue(const QString &key) const { if (key == QStringLiteral("autoDisplayFeature")) return autoDisplayFeatureIsDefaultValue(); + if (key == QStringLiteral("colorMode")) + return colorModeIsDefaultValue(); + if (key == QStringLiteral("defaultColorMode")) + return defaultColorModeIsDefaultValue(); if (key == QStringLiteral("enableDtkAnimations")) return enableDtkAnimationsIsDefaultValue(); if (key == QStringLiteral("featureUpdated")) @@ -171,26 +230,26 @@ class dconfig_org_deepin_dtk_preference : public QObject { } bool autoDisplayFeature() const { - return p_autoDisplayFeature; + return m_data->p_autoDisplayFeature; } void setAutoDisplayFeature(const bool &value) { - auto oldValue = p_autoDisplayFeature; - p_autoDisplayFeature = value; - markPropertySet(0); - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this, value]() { - m_config.loadRelaxed()->setValue(QStringLiteral("autoDisplayFeature"), value); + auto oldValue = m_data->p_autoDisplayFeature; + m_data->p_autoDisplayFeature = value; + m_data->markPropertySet(0); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("autoDisplayFeature"), value); }); } - if (p_autoDisplayFeature != oldValue) { + if (m_data->p_autoDisplayFeature != oldValue) { Q_EMIT autoDisplayFeatureChanged(); Q_EMIT valueChanged(QStringLiteral("autoDisplayFeature"), value); } } void resetAutoDisplayFeature() { - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this]() { - m_config.loadRelaxed()->reset(QStringLiteral("autoDisplayFeature")); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("autoDisplayFeature")); }); } } @@ -200,29 +259,93 @@ class dconfig_org_deepin_dtk_preference : public QObject { } #endif Q_INVOKABLE bool autoDisplayFeatureIsDefaultValue() const { - return !testPropertySet(0); + return !m_data->testPropertySet(0); + } + QString colorMode() const { + return m_data->p_colorMode; + } + void setColorMode(const QString &value) { + auto oldValue = m_data->p_colorMode; + m_data->p_colorMode = value; + m_data->markPropertySet(1); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("colorMode"), value); + }); + } + if (m_data->p_colorMode != oldValue) { + Q_EMIT colorModeChanged(); + Q_EMIT valueChanged(QStringLiteral("colorMode"), value); + } + } + void resetColorMode() { + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("colorMode")); + }); + } + } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QBindable bindableColorMode() { + return QBindable(this, "colorMode"); + } +#endif + Q_INVOKABLE bool colorModeIsDefaultValue() const { + return !m_data->testPropertySet(1); + } + QString defaultColorMode() const { + return m_data->p_defaultColorMode; + } + void setDefaultColorMode(const QString &value) { + auto oldValue = m_data->p_defaultColorMode; + m_data->p_defaultColorMode = value; + m_data->markPropertySet(2); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("defaultColorMode"), value); + }); + } + if (m_data->p_defaultColorMode != oldValue) { + Q_EMIT defaultColorModeChanged(); + Q_EMIT valueChanged(QStringLiteral("defaultColorMode"), value); + } + } + void resetDefaultColorMode() { + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("defaultColorMode")); + }); + } + } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QBindable bindableDefaultColorMode() { + return QBindable(this, "defaultColorMode"); + } +#endif + Q_INVOKABLE bool defaultColorModeIsDefaultValue() const { + return !m_data->testPropertySet(2); } bool enableDtkAnimations() const { - return p_enableDtkAnimations; + return m_data->p_enableDtkAnimations; } void setEnableDtkAnimations(const bool &value) { - auto oldValue = p_enableDtkAnimations; - p_enableDtkAnimations = value; - markPropertySet(1); - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this, value]() { - m_config.loadRelaxed()->setValue(QStringLiteral("enableDtkAnimations"), value); + auto oldValue = m_data->p_enableDtkAnimations; + m_data->p_enableDtkAnimations = value; + m_data->markPropertySet(3); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("enableDtkAnimations"), value); }); } - if (p_enableDtkAnimations != oldValue) { + if (m_data->p_enableDtkAnimations != oldValue) { Q_EMIT enableDtkAnimationsChanged(); Q_EMIT valueChanged(QStringLiteral("enableDtkAnimations"), value); } } void resetEnableDtkAnimations() { - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this]() { - m_config.loadRelaxed()->reset(QStringLiteral("enableDtkAnimations")); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("enableDtkAnimations")); }); } } @@ -232,29 +355,29 @@ class dconfig_org_deepin_dtk_preference : public QObject { } #endif Q_INVOKABLE bool enableDtkAnimationsIsDefaultValue() const { - return !testPropertySet(1); + return !m_data->testPropertySet(3); } bool featureUpdated() const { - return p_featureUpdated; + return m_data->p_featureUpdated; } void setFeatureUpdated(const bool &value) { - auto oldValue = p_featureUpdated; - p_featureUpdated = value; - markPropertySet(2); - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this, value]() { - m_config.loadRelaxed()->setValue(QStringLiteral("featureUpdated"), value); + auto oldValue = m_data->p_featureUpdated; + m_data->p_featureUpdated = value; + m_data->markPropertySet(4); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("featureUpdated"), value); }); } - if (p_featureUpdated != oldValue) { + if (m_data->p_featureUpdated != oldValue) { Q_EMIT featureUpdatedChanged(); Q_EMIT valueChanged(QStringLiteral("featureUpdated"), value); } } void resetFeatureUpdated() { - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this]() { - m_config.loadRelaxed()->reset(QStringLiteral("featureUpdated")); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("featureUpdated")); }); } } @@ -264,29 +387,29 @@ class dconfig_org_deepin_dtk_preference : public QObject { } #endif Q_INVOKABLE bool featureUpdatedIsDefaultValue() const { - return !testPropertySet(2); + return !m_data->testPropertySet(4); } bool keyboardsearchDisabled() const { - return p_keyboardsearchDisabled; + return m_data->p_keyboardsearchDisabled; } void setKeyboardsearchDisabled(const bool &value) { - auto oldValue = p_keyboardsearchDisabled; - p_keyboardsearchDisabled = value; - markPropertySet(3); - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this, value]() { - m_config.loadRelaxed()->setValue(QStringLiteral("keyboardsearchDisabled"), value); + auto oldValue = m_data->p_keyboardsearchDisabled; + m_data->p_keyboardsearchDisabled = value; + m_data->markPropertySet(5); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("keyboardsearchDisabled"), value); }); } - if (p_keyboardsearchDisabled != oldValue) { + if (m_data->p_keyboardsearchDisabled != oldValue) { Q_EMIT keyboardsearchDisabledChanged(); Q_EMIT valueChanged(QStringLiteral("keyboardsearchDisabled"), value); } } void resetKeyboardsearchDisabled() { - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this]() { - m_config.loadRelaxed()->reset(QStringLiteral("keyboardsearchDisabled")); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("keyboardsearchDisabled")); }); } } @@ -296,29 +419,29 @@ class dconfig_org_deepin_dtk_preference : public QObject { } #endif Q_INVOKABLE bool keyboardsearchDisabledIsDefaultValue() const { - return !testPropertySet(3); + return !m_data->testPropertySet(5); } QString rules() const { - return p_rules; + return m_data->p_rules; } void setRules(const QString &value) { - auto oldValue = p_rules; - p_rules = value; - markPropertySet(4); - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this, value]() { - m_config.loadRelaxed()->setValue(QStringLiteral("rules"), value); + auto oldValue = m_data->p_rules; + m_data->p_rules = value; + m_data->markPropertySet(6); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("rules"), value); }); } - if (p_rules != oldValue) { + if (m_data->p_rules != oldValue) { Q_EMIT rulesChanged(); Q_EMIT valueChanged(QStringLiteral("rules"), value); } } void resetRules() { - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this]() { - m_config.loadRelaxed()->reset(QStringLiteral("rules")); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("rules")); }); } } @@ -328,29 +451,29 @@ class dconfig_org_deepin_dtk_preference : public QObject { } #endif Q_INVOKABLE bool rulesIsDefaultValue() const { - return !testPropertySet(4); + return !m_data->testPropertySet(6); } qlonglong scrollBarPolicy() const { - return p_scrollBarPolicy; + return m_data->p_scrollBarPolicy; } void setScrollBarPolicy(const qlonglong &value) { - auto oldValue = p_scrollBarPolicy; - p_scrollBarPolicy = value; - markPropertySet(5); - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this, value]() { - m_config.loadRelaxed()->setValue(QStringLiteral("scrollBarPolicy"), value); + auto oldValue = m_data->p_scrollBarPolicy; + m_data->p_scrollBarPolicy = value; + m_data->markPropertySet(7); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("scrollBarPolicy"), value); }); } - if (p_scrollBarPolicy != oldValue) { + if (m_data->p_scrollBarPolicy != oldValue) { Q_EMIT scrollBarPolicyChanged(); Q_EMIT valueChanged(QStringLiteral("scrollBarPolicy"), value); } } void resetScrollBarPolicy() { - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this]() { - m_config.loadRelaxed()->reset(QStringLiteral("scrollBarPolicy")); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("scrollBarPolicy")); }); } } @@ -360,29 +483,29 @@ class dconfig_org_deepin_dtk_preference : public QObject { } #endif Q_INVOKABLE bool scrollBarPolicyIsDefaultValue() const { - return !testPropertySet(5); + return !m_data->testPropertySet(7); } qlonglong sizeMode() const { - return p_sizeMode; + return m_data->p_sizeMode; } void setSizeMode(const qlonglong &value) { - auto oldValue = p_sizeMode; - p_sizeMode = value; - markPropertySet(6); - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this, value]() { - m_config.loadRelaxed()->setValue(QStringLiteral("sizeMode"), value); + auto oldValue = m_data->p_sizeMode; + m_data->p_sizeMode = value; + m_data->markPropertySet(8); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("sizeMode"), value); }); } - if (p_sizeMode != oldValue) { + if (m_data->p_sizeMode != oldValue) { Q_EMIT sizeModeChanged(); Q_EMIT valueChanged(QStringLiteral("sizeMode"), value); } } void resetSizeMode() { - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this]() { - m_config.loadRelaxed()->reset(QStringLiteral("sizeMode")); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("sizeMode")); }); } } @@ -392,29 +515,29 @@ class dconfig_org_deepin_dtk_preference : public QObject { } #endif Q_INVOKABLE bool sizeModeIsDefaultValue() const { - return !testPropertySet(6); + return !m_data->testPropertySet(8); } qlonglong themeType() const { - return p_themeType; + return m_data->p_themeType; } void setThemeType(const qlonglong &value) { - auto oldValue = p_themeType; - p_themeType = value; - markPropertySet(7); - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this, value]() { - m_config.loadRelaxed()->setValue(QStringLiteral("themeType"), value); + auto oldValue = m_data->p_themeType; + m_data->p_themeType = value; + m_data->markPropertySet(9); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("themeType"), value); }); } - if (p_themeType != oldValue) { + if (m_data->p_themeType != oldValue) { Q_EMIT themeTypeChanged(); Q_EMIT valueChanged(QStringLiteral("themeType"), value); } } void resetThemeType() { - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this]() { - m_config.loadRelaxed()->reset(QStringLiteral("themeType")); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("themeType")); }); } } @@ -424,29 +547,29 @@ class dconfig_org_deepin_dtk_preference : public QObject { } #endif Q_INVOKABLE bool themeTypeIsDefaultValue() const { - return !testPropertySet(7); + return !m_data->testPropertySet(9); } qlonglong titlebarHeight() const { - return p_titlebarHeight; + return m_data->p_titlebarHeight; } void setTitlebarHeight(const qlonglong &value) { - auto oldValue = p_titlebarHeight; - p_titlebarHeight = value; - markPropertySet(8); - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this, value]() { - m_config.loadRelaxed()->setValue(QStringLiteral("titlebarHeight"), value); + auto oldValue = m_data->p_titlebarHeight; + m_data->p_titlebarHeight = value; + m_data->markPropertySet(10); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("titlebarHeight"), value); }); } - if (p_titlebarHeight != oldValue) { + if (m_data->p_titlebarHeight != oldValue) { Q_EMIT titlebarHeightChanged(); Q_EMIT valueChanged(QStringLiteral("titlebarHeight"), value); } } void resetTitlebarHeight() { - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this]() { - m_config.loadRelaxed()->reset(QStringLiteral("titlebarHeight")); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("titlebarHeight")); }); } } @@ -456,29 +579,29 @@ class dconfig_org_deepin_dtk_preference : public QObject { } #endif Q_INVOKABLE bool titlebarHeightIsDefaultValue() const { - return !testPropertySet(8); + return !m_data->testPropertySet(10); } bool underlineShortcut() const { - return p_underlineShortcut; + return m_data->p_underlineShortcut; } void setUnderlineShortcut(const bool &value) { - auto oldValue = p_underlineShortcut; - p_underlineShortcut = value; - markPropertySet(9); - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this, value]() { - m_config.loadRelaxed()->setValue(QStringLiteral("underlineShortcut"), value); + auto oldValue = m_data->p_underlineShortcut; + m_data->p_underlineShortcut = value; + m_data->markPropertySet(11); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("underlineShortcut"), value); }); } - if (p_underlineShortcut != oldValue) { + if (m_data->p_underlineShortcut != oldValue) { Q_EMIT underlineShortcutChanged(); Q_EMIT valueChanged(QStringLiteral("underlineShortcut"), value); } } void resetUnderlineShortcut() { - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this]() { - m_config.loadRelaxed()->reset(QStringLiteral("underlineShortcut")); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("underlineShortcut")); }); } } @@ -488,14 +611,22 @@ class dconfig_org_deepin_dtk_preference : public QObject { } #endif Q_INVOKABLE bool underlineShortcutIsDefaultValue() const { - return !testPropertySet(9); + return !m_data->testPropertySet(11); + } +protected: + bool event(QEvent *e) override { + if (e->type() == QEvent::ThreadChange) { + Q_ASSERT(this->thread() == m_data->thread()); + } + return QObject::event(e); } Q_SIGNALS: - void configInitializeFailed(DTK_CORE_NAMESPACE::DConfig *config); + void configInitializeFailed(); void configInitializeSucceed(DTK_CORE_NAMESPACE::DConfig *config); void valueChanged(const QString &key, const QVariant &value); - void autoDisplayFeatureChanged(); + void colorModeChanged(); + void defaultColorModeChanged(); void enableDtkAnimationsChanged(); void featureUpdatedChanged(); void keyboardsearchDisabledChanged(); @@ -505,252 +636,421 @@ class dconfig_org_deepin_dtk_preference : public QObject { void themeTypeChanged(); void titlebarHeightChanged(); void underlineShortcutChanged(); + private: - void initializeInConfigThread(DTK_CORE_NAMESPACE::DConfig *config) { - Q_ASSERT(!m_config.loadRelaxed()); - m_config.storeRelaxed(config); - if (!config->isValid()) { - m_status.storeRelaxed(static_cast(Status::Failed)); - Q_EMIT configInitializeFailed(config); - return; - } + // Prevent external moveToThread calls + using QObject::moveToThread; + class Data : public QObject { + public: + enum class Status { + Invalid = 0, + Initializing = 1, + Succeeded = 2, + Failed = 3, + Destroyed = 4 + }; + + explicit Data() + : QObject(nullptr) {} + inline void initializeInConfigThread(DTK_CORE_NAMESPACE::DConfig *config) { + Q_ASSERT(!m_config.loadRelaxed()); + m_config.storeRelaxed(config); if (testPropertySet(0)) { config->setValue(QStringLiteral("autoDisplayFeature"), QVariant::fromValue(p_autoDisplayFeature)); } else { updateValue(QStringLiteral("autoDisplayFeature"), QVariant::fromValue(p_autoDisplayFeature)); } if (testPropertySet(1)) { + config->setValue(QStringLiteral("colorMode"), QVariant::fromValue(p_colorMode)); + } else { + updateValue(QStringLiteral("colorMode"), QVariant::fromValue(p_colorMode)); + } + if (testPropertySet(2)) { + config->setValue(QStringLiteral("defaultColorMode"), QVariant::fromValue(p_defaultColorMode)); + } else { + updateValue(QStringLiteral("defaultColorMode"), QVariant::fromValue(p_defaultColorMode)); + } + if (testPropertySet(3)) { config->setValue(QStringLiteral("enableDtkAnimations"), QVariant::fromValue(p_enableDtkAnimations)); } else { updateValue(QStringLiteral("enableDtkAnimations"), QVariant::fromValue(p_enableDtkAnimations)); } - if (testPropertySet(2)) { + if (testPropertySet(4)) { config->setValue(QStringLiteral("featureUpdated"), QVariant::fromValue(p_featureUpdated)); } else { updateValue(QStringLiteral("featureUpdated"), QVariant::fromValue(p_featureUpdated)); } - if (testPropertySet(3)) { + if (testPropertySet(5)) { config->setValue(QStringLiteral("keyboardsearchDisabled"), QVariant::fromValue(p_keyboardsearchDisabled)); } else { updateValue(QStringLiteral("keyboardsearchDisabled"), QVariant::fromValue(p_keyboardsearchDisabled)); } - if (testPropertySet(4)) { + if (testPropertySet(6)) { config->setValue(QStringLiteral("rules"), QVariant::fromValue(p_rules)); } else { updateValue(QStringLiteral("rules"), QVariant::fromValue(p_rules)); } - if (testPropertySet(5)) { + if (testPropertySet(7)) { config->setValue(QStringLiteral("scrollBarPolicy"), QVariant::fromValue(p_scrollBarPolicy)); } else { updateValue(QStringLiteral("scrollBarPolicy"), QVariant::fromValue(p_scrollBarPolicy)); } - if (testPropertySet(6)) { + if (testPropertySet(8)) { config->setValue(QStringLiteral("sizeMode"), QVariant::fromValue(p_sizeMode)); } else { updateValue(QStringLiteral("sizeMode"), QVariant::fromValue(p_sizeMode)); } - if (testPropertySet(7)) { + if (testPropertySet(9)) { config->setValue(QStringLiteral("themeType"), QVariant::fromValue(p_themeType)); } else { updateValue(QStringLiteral("themeType"), QVariant::fromValue(p_themeType)); } - if (testPropertySet(8)) { + if (testPropertySet(10)) { config->setValue(QStringLiteral("titlebarHeight"), QVariant::fromValue(p_titlebarHeight)); } else { updateValue(QStringLiteral("titlebarHeight"), QVariant::fromValue(p_titlebarHeight)); } - if (testPropertySet(9)) { + if (testPropertySet(11)) { config->setValue(QStringLiteral("underlineShortcut"), QVariant::fromValue(p_underlineShortcut)); } else { updateValue(QStringLiteral("underlineShortcut"), QVariant::fromValue(p_underlineShortcut)); } + connect(config, &DTK_CORE_NAMESPACE::DConfig::valueChanged, this, [this](const QString &key) { + updateValue(key); + }, Qt::DirectConnection); + } + + inline void updateValue(const QString &key, const QVariant &fallback = QVariant()) { + auto config = m_config.loadRelaxed(); + if (!config) + return; + Q_ASSERT(QThread::currentThread() == config->thread()); + const QVariant &v = config->value(key, fallback); + if (key == QStringLiteral("autoDisplayFeature")) { + markPropertySet(0, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + if (key == QStringLiteral("colorMode")) { + markPropertySet(1, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + if (key == QStringLiteral("defaultColorMode")) { + markPropertySet(2, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + if (key == QStringLiteral("enableDtkAnimations")) { + markPropertySet(3, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + if (key == QStringLiteral("featureUpdated")) { + markPropertySet(4, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + if (key == QStringLiteral("keyboardsearchDisabled")) { + markPropertySet(5, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + if (key == QStringLiteral("rules")) { + markPropertySet(6, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + if (key == QStringLiteral("scrollBarPolicy")) { + markPropertySet(7, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + if (key == QStringLiteral("sizeMode")) { + markPropertySet(8, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + if (key == QStringLiteral("themeType")) { + markPropertySet(9, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + if (key == QStringLiteral("titlebarHeight")) { + markPropertySet(10, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + if (key == QStringLiteral("underlineShortcut")) { + markPropertySet(11, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + } - if (!m_config.loadRelaxed()) - return; - connect(config, &DTK_CORE_NAMESPACE::DConfig::valueChanged, this, [this](const QString &key) { - updateValue(key); - }, Qt::DirectConnection); - - m_status.storeRelaxed(static_cast(Status::Succeed)); - Q_EMIT configInitializeSucceed(config); - } - void updateValue(const QString &key, const QVariant &fallback = QVariant()) { - if (!m_config.loadRelaxed()) - return; - Q_ASSERT(QThread::currentThread() == m_config.loadRelaxed()->thread()); - const QVariant &value = m_config.loadRelaxed()->value(key, fallback); - if (key == QStringLiteral("autoDisplayFeature")) { - markPropertySet(0, !m_config.loadRelaxed()->isDefaultValue(key)); - auto newValue = qvariant_cast(value); - QMetaObject::invokeMethod(this, [this, newValue, key, value]() { - Q_ASSERT(QThread::currentThread() == this->thread()); - if (p_autoDisplayFeature != newValue) { - p_autoDisplayFeature = newValue; - Q_EMIT autoDisplayFeatureChanged(); - Q_EMIT valueChanged(key, value); + inline void updateProperty(const QString &key, const QVariant &v) { + if (key == QStringLiteral("autoDisplayFeature")) { + bool nv = qvariant_cast(v); + if (p_autoDisplayFeature != nv) { + p_autoDisplayFeature = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->autoDisplayFeatureChanged(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } } - }); - return; - } - if (key == QStringLiteral("enableDtkAnimations")) { - markPropertySet(1, !m_config.loadRelaxed()->isDefaultValue(key)); - auto newValue = qvariant_cast(value); - QMetaObject::invokeMethod(this, [this, newValue, key, value]() { - Q_ASSERT(QThread::currentThread() == this->thread()); - if (p_enableDtkAnimations != newValue) { - p_enableDtkAnimations = newValue; - Q_EMIT enableDtkAnimationsChanged(); - Q_EMIT valueChanged(key, value); + return; + } + if (key == QStringLiteral("colorMode")) { + QString nv = qvariant_cast(v); + if (p_colorMode != nv) { + p_colorMode = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->colorModeChanged(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } } - }); - return; - } - if (key == QStringLiteral("featureUpdated")) { - markPropertySet(2, !m_config.loadRelaxed()->isDefaultValue(key)); - auto newValue = qvariant_cast(value); - QMetaObject::invokeMethod(this, [this, newValue, key, value]() { - Q_ASSERT(QThread::currentThread() == this->thread()); - if (p_featureUpdated != newValue) { - p_featureUpdated = newValue; - Q_EMIT featureUpdatedChanged(); - Q_EMIT valueChanged(key, value); + return; + } + if (key == QStringLiteral("defaultColorMode")) { + QString nv = qvariant_cast(v); + if (p_defaultColorMode != nv) { + p_defaultColorMode = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->defaultColorModeChanged(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } } - }); - return; - } - if (key == QStringLiteral("keyboardsearchDisabled")) { - markPropertySet(3, !m_config.loadRelaxed()->isDefaultValue(key)); - auto newValue = qvariant_cast(value); - QMetaObject::invokeMethod(this, [this, newValue, key, value]() { - Q_ASSERT(QThread::currentThread() == this->thread()); - if (p_keyboardsearchDisabled != newValue) { - p_keyboardsearchDisabled = newValue; - Q_EMIT keyboardsearchDisabledChanged(); - Q_EMIT valueChanged(key, value); + return; + } + if (key == QStringLiteral("enableDtkAnimations")) { + bool nv = qvariant_cast(v); + if (p_enableDtkAnimations != nv) { + p_enableDtkAnimations = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->enableDtkAnimationsChanged(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } } - }); - return; - } - if (key == QStringLiteral("rules")) { - markPropertySet(4, !m_config.loadRelaxed()->isDefaultValue(key)); - auto newValue = qvariant_cast(value); - QMetaObject::invokeMethod(this, [this, newValue, key, value]() { - Q_ASSERT(QThread::currentThread() == this->thread()); - if (p_rules != newValue) { - p_rules = newValue; - Q_EMIT rulesChanged(); - Q_EMIT valueChanged(key, value); + return; + } + if (key == QStringLiteral("featureUpdated")) { + bool nv = qvariant_cast(v); + if (p_featureUpdated != nv) { + p_featureUpdated = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->featureUpdatedChanged(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } } - }); - return; - } - if (key == QStringLiteral("scrollBarPolicy")) { - markPropertySet(5, !m_config.loadRelaxed()->isDefaultValue(key)); - auto newValue = qvariant_cast(value); - QMetaObject::invokeMethod(this, [this, newValue, key, value]() { - Q_ASSERT(QThread::currentThread() == this->thread()); - if (p_scrollBarPolicy != newValue) { - p_scrollBarPolicy = newValue; - Q_EMIT scrollBarPolicyChanged(); - Q_EMIT valueChanged(key, value); + return; + } + if (key == QStringLiteral("keyboardsearchDisabled")) { + bool nv = qvariant_cast(v); + if (p_keyboardsearchDisabled != nv) { + p_keyboardsearchDisabled = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->keyboardsearchDisabledChanged(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } } - }); - return; - } - if (key == QStringLiteral("sizeMode")) { - markPropertySet(6, !m_config.loadRelaxed()->isDefaultValue(key)); - auto newValue = qvariant_cast(value); - QMetaObject::invokeMethod(this, [this, newValue, key, value]() { - Q_ASSERT(QThread::currentThread() == this->thread()); - if (p_sizeMode != newValue) { - p_sizeMode = newValue; - Q_EMIT sizeModeChanged(); - Q_EMIT valueChanged(key, value); + return; + } + if (key == QStringLiteral("rules")) { + QString nv = qvariant_cast(v); + if (p_rules != nv) { + p_rules = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->rulesChanged(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } } - }); - return; - } - if (key == QStringLiteral("themeType")) { - markPropertySet(7, !m_config.loadRelaxed()->isDefaultValue(key)); - auto newValue = qvariant_cast(value); - QMetaObject::invokeMethod(this, [this, newValue, key, value]() { - Q_ASSERT(QThread::currentThread() == this->thread()); - if (p_themeType != newValue) { - p_themeType = newValue; - Q_EMIT themeTypeChanged(); - Q_EMIT valueChanged(key, value); + return; + } + if (key == QStringLiteral("scrollBarPolicy")) { + qlonglong nv = qvariant_cast(v); + if (p_scrollBarPolicy != nv) { + p_scrollBarPolicy = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->scrollBarPolicyChanged(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } } - }); - return; - } - if (key == QStringLiteral("titlebarHeight")) { - markPropertySet(8, !m_config.loadRelaxed()->isDefaultValue(key)); - auto newValue = qvariant_cast(value); - QMetaObject::invokeMethod(this, [this, newValue, key, value]() { - Q_ASSERT(QThread::currentThread() == this->thread()); - if (p_titlebarHeight != newValue) { - p_titlebarHeight = newValue; - Q_EMIT titlebarHeightChanged(); - Q_EMIT valueChanged(key, value); + return; + } + if (key == QStringLiteral("sizeMode")) { + qlonglong nv = qvariant_cast(v); + if (p_sizeMode != nv) { + p_sizeMode = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->sizeModeChanged(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } } - }); - return; - } - if (key == QStringLiteral("underlineShortcut")) { - markPropertySet(9, !m_config.loadRelaxed()->isDefaultValue(key)); - auto newValue = qvariant_cast(value); - QMetaObject::invokeMethod(this, [this, newValue, key, value]() { - Q_ASSERT(QThread::currentThread() == this->thread()); - if (p_underlineShortcut != newValue) { - p_underlineShortcut = newValue; - Q_EMIT underlineShortcutChanged(); - Q_EMIT valueChanged(key, value); + return; + } + if (key == QStringLiteral("themeType")) { + qlonglong nv = qvariant_cast(v); + if (p_themeType != nv) { + p_themeType = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->themeTypeChanged(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } } - }); - return; + return; + } + if (key == QStringLiteral("titlebarHeight")) { + qlonglong nv = qvariant_cast(v); + if (p_titlebarHeight != nv) { + p_titlebarHeight = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->titlebarHeightChanged(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } + } + return; + } + if (key == QStringLiteral("underlineShortcut")) { + bool nv = qvariant_cast(v); + if (p_underlineShortcut != nv) { + p_underlineShortcut = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->underlineShortcutChanged(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } + } + return; + } } - } - inline void markPropertySet(const int index, bool on = true) { - if (index < 32) { - if (on) - m_propertySetStatus0.fetchAndOrOrdered(1 << (index - 0)); - else - m_propertySetStatus0.fetchAndAndOrdered(~(1 << (index - 0))); - return; + + inline void markPropertySet(const int index, bool on = true) { + if (index < 32) { + if (on) + m_propertySetStatus0.fetchAndOrOrdered(1 << (index - 0)); + else + m_propertySetStatus0.fetchAndAndOrdered(~(1 << (index - 0))); + return; + } + Q_UNREACHABLE(); } - Q_UNREACHABLE(); - } - inline bool testPropertySet(const int index) const { - if (index < 32) { - return (m_propertySetStatus0.loadRelaxed() & (1 << (index - 0))); + + inline bool testPropertySet(const int index) const { + if (index < 32) { + return (m_propertySetStatus0.loadRelaxed() & (1 << (index - 0))); + } + Q_UNREACHABLE(); } - Q_UNREACHABLE(); - } - QAtomicPointer m_config = nullptr; + // Member variables + QAtomicPointer m_config = nullptr; + QAtomicInteger m_status = static_cast(Status::Invalid); + QPointer m_userConfig = nullptr; + QAtomicInteger m_propertySetStatus0 = 0; -public: - enum class Status { - Invalid = 0, - Succeed = 1, - Failed = 2 + // Property storage + bool p_autoDisplayFeature { false }; + // Default value: "" + QString p_colorMode { QLatin1String("") }; + // Default value: "color" + QString p_defaultColorMode { QStringLiteral(u"\u0063\u006f\u006c\u006f\u0072") }; + bool p_enableDtkAnimations { false }; + bool p_featureUpdated { false }; + bool p_keyboardsearchDisabled { false }; + // Default value: "*.debug=false" + QString p_rules { QStringLiteral(u"\u002a\u002e\u0064\u0065\u0062\u0075\u0067\u003d\u0066\u0061\u006c\u0073\u0065") }; + qlonglong p_scrollBarPolicy { 0 }; + qlonglong p_sizeMode { 0 }; + qlonglong p_themeType { 0 }; + qlonglong p_titlebarHeight { -1 }; + bool p_underlineShortcut { false }; }; -private: - QAtomicInteger m_status = static_cast(Status::Invalid); - - bool p_autoDisplayFeature { false }; - bool p_enableDtkAnimations { false }; - bool p_featureUpdated { false }; - bool p_keyboardsearchDisabled { false }; - // Default value: "*.debug=false" - QString p_rules { QStringLiteral(u"\u002a\u002e\u0064\u0065\u0062\u0075\u0067\u003d\u0066\u0061\u006c\u0073\u0065") }; - qlonglong p_scrollBarPolicy { 0 }; - qlonglong p_sizeMode { 0 }; - qlonglong p_themeType { 0 }; - qlonglong p_titlebarHeight { -1 }; - bool p_underlineShortcut { false }; - QAtomicInteger m_propertySetStatus0 = 0; + + Data *m_data; }; #endif // DCONFIG_ORG_DEEPIN_DTK_PREFERENCE_H diff --git a/toolGenerate/dconfig2cpp/dconf-example_meta.hpp b/toolGenerate/dconfig2cpp/dconf-example_meta.hpp index 3ceef68c..cdc1bc4e 100644 --- a/toolGenerate/dconfig2cpp/dconf-example_meta.hpp +++ b/toolGenerate/dconfig2cpp/dconf-example_meta.hpp @@ -1,322 +1,701 @@ /** * This file is generated by dconfig2cpp. - * Command line arguments: ./dconfig2cpp -p ./dtkcore/toolGenerate/dconfig2cpp ./dtkcore/tests/data/dconf-example.meta.json - * Generation time: 2025-01-14T10:54:59 + * Command line arguments: ./build/tools/dconfig2cpp/dconfig2cpp ./tests/data/dconf-example.meta.json -o ./toolGenerate/dconfig2cpp/dconf-example_meta.hpp + * Generation time: 2026-01-19T15:29:47 * JSON file version: 1.0 - * + * * WARNING: DO NOT MODIFY THIS FILE MANUALLY. * If you need to change the content, please modify the dconfig2cpp tool. */ -#ifndef DCONF-EXAMPLE_META_H -#define DCONF-EXAMPLE_META_H +#ifndef DCONFIG_DCONF-EXAMPLE_META_H +#define DCONFIG_DCONF-EXAMPLE_META_H #include #include +#include #include #include #include +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +#include +#endif +#include +#include #include -class dconf-example_meta : public QObject { +class dconfig_dconf-example_meta : public QObject { Q_OBJECT - Q_PROPERTY(QList array READ array WRITE setArray NOTIFY arrayChanged) - Q_PROPERTY(QList array_map READ array_map WRITE setArray_map NOTIFY array_mapChanged) - Q_PROPERTY(QList array_map_struct READ array_map_struct WRITE setArray_map_struct NOTIFY array_map_structChanged) - Q_PROPERTY(bool canExit READ canExit WRITE setCanExit NOTIFY canExitChanged) - Q_PROPERTY(QString key2 READ key2 WRITE setKey2 NOTIFY key2Changed) - Q_PROPERTY(QString key3 READ key3 WRITE setKey3 NOTIFY key3Changed) - Q_PROPERTY(QVariantMap map READ map WRITE setMap NOTIFY mapChanged) - Q_PROPERTY(QVariantMap map_array READ map_array WRITE setMap_array NOTIFY map_arrayChanged) - Q_PROPERTY(double number READ number WRITE setNumber NOTIFY numberChanged) - Q_PROPERTY(double numberDouble READ numberDouble WRITE setNumberDouble NOTIFY numberDoubleChanged) - Q_PROPERTY(bool publicConfig READ publicConfig WRITE setPublicConfig NOTIFY publicConfigChanged) - Q_PROPERTY(QVariantMap struct READ struct WRITE setStruct NOTIFY structChanged) + Q_PROPERTY(QList array READ array WRITE setArray NOTIFY arrayChanged RESET resetArray) + Q_PROPERTY(QList array_map READ array_map WRITE setArray_map NOTIFY array_mapChanged RESET resetArray_map) + Q_PROPERTY(QList array_map_struct READ array_map_struct WRITE setArray_map_struct NOTIFY array_map_structChanged RESET resetArray_map_struct) + Q_PROPERTY(bool canExit READ canExit WRITE setCanExit NOTIFY canExitChanged RESET resetCanExit) + Q_PROPERTY(QString key2 READ key2 WRITE setKey2 NOTIFY key2Changed RESET resetKey2) + Q_PROPERTY(QString key3 READ key3 WRITE setKey3 NOTIFY key3Changed RESET resetKey3) + Q_PROPERTY(QVariantMap map READ map WRITE setMap NOTIFY mapChanged RESET resetMap) + Q_PROPERTY(QVariantMap map_array READ map_array WRITE setMap_array NOTIFY map_arrayChanged RESET resetMap_array) + Q_PROPERTY(qlonglong number READ number WRITE setNumber NOTIFY numberChanged RESET resetNumber) + Q_PROPERTY(double numberDouble READ numberDouble WRITE setNumberDouble NOTIFY numberDoubleChanged RESET resetNumberDouble) + Q_PROPERTY(bool publicConfig READ publicConfig WRITE setPublicConfig NOTIFY publicConfigChanged RESET resetPublicConfig) + Q_PROPERTY(bool readonly READ readonly WRITE setReadonly NOTIFY readonlyChanged RESET resetReadonly) + Q_PROPERTY(bool readwrite READ readwrite WRITE setReadwrite NOTIFY readwriteChanged RESET resetReadwrite) + Q_PROPERTY(QVariantMap struct READ struct WRITE setStruct NOTIFY structChanged RESET resetStruct) + Q_CLASSINFO("DConfigKeyList", "array;array_map;array_map_struct;canExit;key2;key3;map;map_array;number;numberDouble;publicConfig;readonly;readwrite;struct") + Q_CLASSINFO("DConfigFileName", "dconf-example.meta") + Q_CLASSINFO("DConfigFileVersion", "1.0") + public: - explicit dconf-example_meta(QThread *thread, const QString &appId, const QString &name, const QString &subpath, QObject *parent = nullptr) - : QObject(parent) { + explicit dconfig_dconf-example_meta(QThread *thread, DTK_CORE_NAMESPACE::DConfigBackend *backend, + const QString &name, const QString &appId, const QString &subpath, + bool isGeneric, QObject *parent) + : QObject(parent), m_data(new Data) { + m_data->m_userConfig = this; + m_data->moveToThread(this->thread()); if (!thread->isRunning()) { - qWarning() << QStringLiteral("Warning: The provided thread is not running."); + qWarning() << QLatin1String("Warning: The provided thread is not running."); } Q_ASSERT(QThread::currentThread() != thread); auto worker = new QObject(); worker->moveToThread(thread); - QMetaObject::invokeMethod(worker, [=]() { - auto config = DTK_CORE_NAMESPACE::DConfig::create(appId, name, subpath, nullptr); - if (!config) { - qWarning() << QStringLiteral("Failed to create DConfig instance."); - worker->deleteLater(); + + QPointer safeData(m_data); + + QMetaObject::invokeMethod(worker, [safeData, backend, name, appId, subpath, isGeneric, worker]() mutable { + delete worker; + worker = nullptr; + + // Check if Data object is still valid + if (!safeData) { return; } - config->moveToThread(QThread::currentThread()); - initialize(config); - worker->deleteLater(); - }); - } - explicit dconf-example_meta(QThread *thread, DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &appId, const QString &name, const QString &subpath, QObject *parent = nullptr) - : QObject(parent) { - if (!thread->isRunning()) { - qWarning() << QStringLiteral("Warning: The provided thread is not running."); - } - Q_ASSERT(QThread::currentThread() != thread); - auto worker = new QObject(); - worker->moveToThread(thread); - QMetaObject::invokeMethod(worker, [=]() { - auto config = DTK_CORE_NAMESPACE::DConfig::create(backend, appId, name, subpath, nullptr); - if (!config) { - qWarning() << QStringLiteral("Failed to create DConfig instance."); - worker->deleteLater(); + if (!safeData->m_status.testAndSetOrdered(static_cast(Data::Status::Invalid), + static_cast(Data::Status::Initializing))) { + // CAS failed, state already changed - userConfig destructor will handle cleanup + // Do not attempt to delete here as it would race with destructor return; } - config->moveToThread(QThread::currentThread()); - initialize(config); - worker->deleteLater(); - }); - } - explicit dconf-example_meta(QThread *thread, const QString &name, const QString &subpath, QObject *parent = nullptr) - : QObject(parent) { - if (!thread->isRunning()) { - qWarning() << QStringLiteral("Warning: The provided thread is not running."); - } - Q_ASSERT(QThread::currentThread() != thread); - auto worker = new QObject(); - worker->moveToThread(thread); - QMetaObject::invokeMethod(worker, [=]() { - auto config = DTK_CORE_NAMESPACE::DConfig::create(name, subpath, nullptr); - if (!config) { - qWarning() << QStringLiteral("Failed to create DConfig instance."); - worker->deleteLater(); - return; + DTK_CORE_NAMESPACE::DConfig *config = nullptr; + if (isGeneric) { + if (backend) { + config = DTK_CORE_NAMESPACE::DConfig::createGeneric(backend, name, subpath, nullptr); + } else { + config = DTK_CORE_NAMESPACE::DConfig::createGeneric(name, subpath, nullptr); + } + } else { + if (backend) { + if (appId.isNull()) { + config = DTK_CORE_NAMESPACE::DConfig::create(backend, DTK_CORE_NAMESPACE::DSGApplication::id(), + name, subpath, nullptr); + } else { + config = DTK_CORE_NAMESPACE::DConfig::create(backend, appId, name, subpath, nullptr); + } + } else { + if (appId.isNull()) { + config = DTK_CORE_NAMESPACE::DConfig::create(DTK_CORE_NAMESPACE::DSGApplication::id(), + name, subpath, nullptr); + } else { + config = DTK_CORE_NAMESPACE::DConfig::create(appId, name, subpath, nullptr); + } + } } - config->moveToThread(QThread::currentThread()); - initialize(config); - worker->deleteLater(); - }); - } - explicit dconf-example_meta(QThread *thread, DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &name, const QString &subpath, QObject *parent = nullptr) - : QObject(parent) { - if (!thread->isRunning()) { - qWarning() << QStringLiteral("Warning: The provided thread is not running."); - } - Q_ASSERT(QThread::currentThread() != thread); - auto worker = new QObject(); - worker->moveToThread(thread); - QMetaObject::invokeMethod(worker, [=]() { - auto config = DTK_CORE_NAMESPACE::DConfig::create(backend, name, subpath, nullptr); - if (!config) { - qWarning() << QStringLiteral("Failed to create DConfig instance."); - worker->deleteLater(); + if (!config || !config->isValid()) { + qWarning() << QLatin1String("Failed to create DConfig instance."); + + if (safeData && safeData->m_status.testAndSetOrdered(static_cast(Data::Status::Initializing), + static_cast(Data::Status::Failed))) { + if (safeData->m_userConfig) { + QMetaObject::invokeMethod(safeData, [safeData]() { + if (safeData && safeData->m_userConfig) { + Q_EMIT safeData->m_userConfig->configInitializeFailed(); + } + }, Qt::QueuedConnection); + } + } else { + // CAS failed, destroy data object + safeData->deleteLater(); + } + + if (config) + delete config; + return; } config->moveToThread(QThread::currentThread()); - initialize(config); - worker->deleteLater(); + // Initialize through Data class + safeData->initializeInConfigThread(config); + + // Try to transition from Initializing to Succeeded + if (safeData && safeData->m_status.testAndSetOrdered(static_cast(Data::Status::Initializing), + static_cast(Data::Status::Succeeded))) { + // CAS succeeded, connect destroyed signal + QObject::connect(config, &QObject::destroyed, safeData, &QObject::deleteLater); + } else { + // CAS failed - state changed (e.g., set to Destroyed) + // We must clean up the config we just created + config->deleteLater(); + safeData->deleteLater(); + } }); } - ~dconf-example_meta() { - if (m_config.loadRelaxed()) { - m_config.loadRelaxed()->deleteLater(); + static dconfig_dconf-example_meta* create(const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_dconf-example_meta(thread, nullptr, QStringLiteral(u"\u0064\u0063\u006f\u006e\u0066\u002d\u0065\u0078\u0061\u006d\u0070\u006c\u0065\u002e\u006d\u0065\u0074\u0061"), appId, subpath, false, parent); } + static dconfig_dconf-example_meta* create(DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_dconf-example_meta(thread, backend, QStringLiteral(u"\u0064\u0063\u006f\u006e\u0066\u002d\u0065\u0078\u0061\u006d\u0070\u006c\u0065\u002e\u006d\u0065\u0074\u0061"), appId, subpath, false, parent); } + static dconfig_dconf-example_meta* createByName(const QString &name, const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_dconf-example_meta(thread, nullptr, name, appId, subpath, false, parent); } + static dconfig_dconf-example_meta* createByName(DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &name, const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_dconf-example_meta(thread, backend, name, appId, subpath, false, parent); } + static dconfig_dconf-example_meta* createGeneric(const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_dconf-example_meta(thread, nullptr, QStringLiteral(u"\u0064\u0063\u006f\u006e\u0066\u002d\u0065\u0078\u0061\u006d\u0070\u006c\u0065\u002e\u006d\u0065\u0074\u0061"), {}, subpath, true, parent); } + static dconfig_dconf-example_meta* create(DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_dconf-example_meta(thread, backend, QStringLiteral(u"\u0064\u0063\u006f\u006e\u0066\u002d\u0065\u0078\u0061\u006d\u0070\u006c\u0065\u002e\u006d\u0065\u0074\u0061"), {}, subpath, true, parent); } + static dconfig_dconf-example_meta* createGenericByName(const QString &name, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_dconf-example_meta(thread, nullptr, name, {}, subpath, true, parent); } + static dconfig_dconf-example_meta* createGenericByName(DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &name, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_dconf-example_meta(thread, backend, name, {}, subpath, true, parent); } + ~dconfig_dconf-example_meta() { + int oldStatus = m_data->m_status.fetchAndStoreOrdered(static_cast(Data::Status::Destroyed)); + + m_data->m_userConfig = nullptr; + if (oldStatus == static_cast(Data::Status::Succeeded)) { + // When Succeeded, release config object only + auto config = m_data->m_config.loadRelaxed(); + Q_ASSERT(config); + config->deleteLater(); + // m_data will be deleted by config's destroyed signal + } else if (oldStatus == static_cast(Data::Status::Failed) || + oldStatus == static_cast(Data::Status::Invalid)) { + // When Failed or Invalid, directly clean up Data object + m_data->deleteLater(); } } + Q_INVOKABLE DTK_CORE_NAMESPACE::DConfig *config() const { + return m_data->m_config.loadRelaxed(); + } + Q_INVOKABLE bool isInitializeSucceed() const { + return m_data->m_status.loadRelaxed() == static_cast(Data::Status::Succeeded); + } + Q_INVOKABLE bool isInitializeFailed() const { + return m_data->m_status.loadRelaxed() == static_cast(Data::Status::Failed); + } + Q_INVOKABLE bool isInitializing() const { + return m_data->m_status.loadRelaxed() == static_cast(Data::Status::Initializing); + } + + Q_INVOKABLE QStringList keyList() const { + return { QStringLiteral("array"), + QStringLiteral("array_map"), + QStringLiteral("array_map_struct"), + QStringLiteral("canExit"), + QStringLiteral("key2"), + QStringLiteral("key3"), + QStringLiteral("map"), + QStringLiteral("map_array"), + QStringLiteral("number"), + QStringLiteral("numberDouble"), + QStringLiteral("publicConfig"), + QStringLiteral("readonly"), + QStringLiteral("readwrite"), + QStringLiteral("struct")}; + } + + Q_INVOKABLE bool isDefaultValue(const QString &key) const { + if (key == QStringLiteral("array")) + return arrayIsDefaultValue(); + if (key == QStringLiteral("array_map")) + return array_mapIsDefaultValue(); + if (key == QStringLiteral("array_map_struct")) + return array_map_structIsDefaultValue(); + if (key == QStringLiteral("canExit")) + return canExitIsDefaultValue(); + if (key == QStringLiteral("key2")) + return key2IsDefaultValue(); + if (key == QStringLiteral("key3")) + return key3IsDefaultValue(); + if (key == QStringLiteral("map")) + return mapIsDefaultValue(); + if (key == QStringLiteral("map_array")) + return map_arrayIsDefaultValue(); + if (key == QStringLiteral("number")) + return numberIsDefaultValue(); + if (key == QStringLiteral("numberDouble")) + return numberDoubleIsDefaultValue(); + if (key == QStringLiteral("publicConfig")) + return publicConfigIsDefaultValue(); + if (key == QStringLiteral("readonly")) + return readonlyIsDefaultValue(); + if (key == QStringLiteral("readwrite")) + return readwriteIsDefaultValue(); + if (key == QStringLiteral("struct")) + return structIsDefaultValue(); + return false; + } + QList array() const { - return p_array; + return m_data->p_array; } void setArray(const QList &value) { - auto oldValue = p_array; - p_array = value; - markPropertySet(0); - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this, value]() { - m_config.loadRelaxed()->setValue(QStringLiteral("array"), value); + auto oldValue = m_data->p_array; + m_data->p_array = value; + m_data->markPropertySet(0); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("array"), value); }); } - if (p_array != oldValue) { + if (m_data->p_array != oldValue) { Q_EMIT arrayChanged(); + Q_EMIT valueChanged(QStringLiteral("array"), value); + } + } + void resetArray() { + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("array")); + }); } } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QBindable> bindableArray() { + return QBindable>(this, "array"); + } +#endif + Q_INVOKABLE bool arrayIsDefaultValue() const { + return !m_data->testPropertySet(0); + } QList array_map() const { - return p_array_map; + return m_data->p_array_map; } void setArray_map(const QList &value) { - auto oldValue = p_array_map; - p_array_map = value; - markPropertySet(1); - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this, value]() { - m_config.loadRelaxed()->setValue(QStringLiteral("array_map"), value); + auto oldValue = m_data->p_array_map; + m_data->p_array_map = value; + m_data->markPropertySet(1); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("array_map"), value); }); } - if (p_array_map != oldValue) { + if (m_data->p_array_map != oldValue) { Q_EMIT array_mapChanged(); + Q_EMIT valueChanged(QStringLiteral("array_map"), value); } } + void resetArray_map() { + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("array_map")); + }); + } + } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QBindable> bindableArray_map() { + return QBindable>(this, "array_map"); + } +#endif + Q_INVOKABLE bool array_mapIsDefaultValue() const { + return !m_data->testPropertySet(1); + } QList array_map_struct() const { - return p_array_map_struct; + return m_data->p_array_map_struct; } void setArray_map_struct(const QList &value) { - auto oldValue = p_array_map_struct; - p_array_map_struct = value; - markPropertySet(2); - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this, value]() { - m_config.loadRelaxed()->setValue(QStringLiteral("array_map_struct"), value); + auto oldValue = m_data->p_array_map_struct; + m_data->p_array_map_struct = value; + m_data->markPropertySet(2); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("array_map_struct"), value); }); } - if (p_array_map_struct != oldValue) { + if (m_data->p_array_map_struct != oldValue) { Q_EMIT array_map_structChanged(); + Q_EMIT valueChanged(QStringLiteral("array_map_struct"), value); + } + } + void resetArray_map_struct() { + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("array_map_struct")); + }); } } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QBindable> bindableArray_map_struct() { + return QBindable>(this, "array_map_struct"); + } +#endif + Q_INVOKABLE bool array_map_structIsDefaultValue() const { + return !m_data->testPropertySet(2); + } bool canExit() const { - return p_canExit; + return m_data->p_canExit; } void setCanExit(const bool &value) { - auto oldValue = p_canExit; - p_canExit = value; - markPropertySet(3); - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this, value]() { - m_config.loadRelaxed()->setValue(QStringLiteral("canExit"), value); + auto oldValue = m_data->p_canExit; + m_data->p_canExit = value; + m_data->markPropertySet(3); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("canExit"), value); }); } - if (p_canExit != oldValue) { + if (m_data->p_canExit != oldValue) { Q_EMIT canExitChanged(); + Q_EMIT valueChanged(QStringLiteral("canExit"), value); + } + } + void resetCanExit() { + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("canExit")); + }); } } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QBindable bindableCanExit() { + return QBindable(this, "canExit"); + } +#endif + Q_INVOKABLE bool canExitIsDefaultValue() const { + return !m_data->testPropertySet(3); + } QString key2() const { - return p_key2; + return m_data->p_key2; } void setKey2(const QString &value) { - auto oldValue = p_key2; - p_key2 = value; - markPropertySet(4); - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this, value]() { - m_config.loadRelaxed()->setValue(QStringLiteral("key2"), value); + auto oldValue = m_data->p_key2; + m_data->p_key2 = value; + m_data->markPropertySet(4); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("key2"), value); }); } - if (p_key2 != oldValue) { + if (m_data->p_key2 != oldValue) { Q_EMIT key2Changed(); + Q_EMIT valueChanged(QStringLiteral("key2"), value); + } + } + void resetKey2() { + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("key2")); + }); } } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QBindable bindableKey2() { + return QBindable(this, "key2"); + } +#endif + Q_INVOKABLE bool key2IsDefaultValue() const { + return !m_data->testPropertySet(4); + } QString key3() const { - return p_key3; + return m_data->p_key3; } void setKey3(const QString &value) { - auto oldValue = p_key3; - p_key3 = value; - markPropertySet(5); - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this, value]() { - m_config.loadRelaxed()->setValue(QStringLiteral("key3"), value); + auto oldValue = m_data->p_key3; + m_data->p_key3 = value; + m_data->markPropertySet(5); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("key3"), value); }); } - if (p_key3 != oldValue) { + if (m_data->p_key3 != oldValue) { Q_EMIT key3Changed(); + Q_EMIT valueChanged(QStringLiteral("key3"), value); } } + void resetKey3() { + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("key3")); + }); + } + } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QBindable bindableKey3() { + return QBindable(this, "key3"); + } +#endif + Q_INVOKABLE bool key3IsDefaultValue() const { + return !m_data->testPropertySet(5); + } QVariantMap map() const { - return p_map; + return m_data->p_map; } void setMap(const QVariantMap &value) { - auto oldValue = p_map; - p_map = value; - markPropertySet(6); - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this, value]() { - m_config.loadRelaxed()->setValue(QStringLiteral("map"), value); + auto oldValue = m_data->p_map; + m_data->p_map = value; + m_data->markPropertySet(6); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("map"), value); }); } - if (p_map != oldValue) { + if (m_data->p_map != oldValue) { Q_EMIT mapChanged(); + Q_EMIT valueChanged(QStringLiteral("map"), value); } } + void resetMap() { + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("map")); + }); + } + } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QBindable bindableMap() { + return QBindable(this, "map"); + } +#endif + Q_INVOKABLE bool mapIsDefaultValue() const { + return !m_data->testPropertySet(6); + } QVariantMap map_array() const { - return p_map_array; + return m_data->p_map_array; } void setMap_array(const QVariantMap &value) { - auto oldValue = p_map_array; - p_map_array = value; - markPropertySet(7); - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this, value]() { - m_config.loadRelaxed()->setValue(QStringLiteral("map_array"), value); + auto oldValue = m_data->p_map_array; + m_data->p_map_array = value; + m_data->markPropertySet(7); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("map_array"), value); }); } - if (p_map_array != oldValue) { + if (m_data->p_map_array != oldValue) { Q_EMIT map_arrayChanged(); + Q_EMIT valueChanged(QStringLiteral("map_array"), value); } } - double number() const { - return p_number; + void resetMap_array() { + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("map_array")); + }); + } } - void setNumber(const double &value) { - auto oldValue = p_number; - p_number = value; - markPropertySet(8); - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this, value]() { - m_config.loadRelaxed()->setValue(QStringLiteral("number"), value); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QBindable bindableMap_array() { + return QBindable(this, "map_array"); + } +#endif + Q_INVOKABLE bool map_arrayIsDefaultValue() const { + return !m_data->testPropertySet(7); + } + qlonglong number() const { + return m_data->p_number; + } + void setNumber(const qlonglong &value) { + auto oldValue = m_data->p_number; + m_data->p_number = value; + m_data->markPropertySet(8); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("number"), value); }); } - if (p_number != oldValue) { + if (m_data->p_number != oldValue) { Q_EMIT numberChanged(); + Q_EMIT valueChanged(QStringLiteral("number"), value); } } + void resetNumber() { + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("number")); + }); + } + } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QBindable bindableNumber() { + return QBindable(this, "number"); + } +#endif + Q_INVOKABLE bool numberIsDefaultValue() const { + return !m_data->testPropertySet(8); + } double numberDouble() const { - return p_numberDouble; + return m_data->p_numberDouble; } void setNumberDouble(const double &value) { - auto oldValue = p_numberDouble; - p_numberDouble = value; - markPropertySet(9); - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this, value]() { - m_config.loadRelaxed()->setValue(QStringLiteral("numberDouble"), value); + auto oldValue = m_data->p_numberDouble; + m_data->p_numberDouble = value; + m_data->markPropertySet(9); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("numberDouble"), value); }); } - if (p_numberDouble != oldValue) { + if (m_data->p_numberDouble != oldValue) { Q_EMIT numberDoubleChanged(); + Q_EMIT valueChanged(QStringLiteral("numberDouble"), value); + } + } + void resetNumberDouble() { + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("numberDouble")); + }); } } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QBindable bindableNumberDouble() { + return QBindable(this, "numberDouble"); + } +#endif + Q_INVOKABLE bool numberDoubleIsDefaultValue() const { + return !m_data->testPropertySet(9); + } bool publicConfig() const { - return p_publicConfig; + return m_data->p_publicConfig; } void setPublicConfig(const bool &value) { - auto oldValue = p_publicConfig; - p_publicConfig = value; - markPropertySet(10); - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this, value]() { - m_config.loadRelaxed()->setValue(QStringLiteral("publicConfig"), value); + auto oldValue = m_data->p_publicConfig; + m_data->p_publicConfig = value; + m_data->markPropertySet(10); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("publicConfig"), value); }); } - if (p_publicConfig != oldValue) { + if (m_data->p_publicConfig != oldValue) { Q_EMIT publicConfigChanged(); + Q_EMIT valueChanged(QStringLiteral("publicConfig"), value); + } + } + void resetPublicConfig() { + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("publicConfig")); + }); + } + } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QBindable bindablePublicConfig() { + return QBindable(this, "publicConfig"); + } +#endif + Q_INVOKABLE bool publicConfigIsDefaultValue() const { + return !m_data->testPropertySet(10); + } + bool readonly() const { + return m_data->p_readonly; + } + void setReadonly(const bool &value) { + auto oldValue = m_data->p_readonly; + m_data->p_readonly = value; + m_data->markPropertySet(11); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("readonly"), value); + }); + } + if (m_data->p_readonly != oldValue) { + Q_EMIT readonlyChanged(); + Q_EMIT valueChanged(QStringLiteral("readonly"), value); + } + } + void resetReadonly() { + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("readonly")); + }); + } + } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QBindable bindableReadonly() { + return QBindable(this, "readonly"); + } +#endif + Q_INVOKABLE bool readonlyIsDefaultValue() const { + return !m_data->testPropertySet(11); + } + bool readwrite() const { + return m_data->p_readwrite; + } + void setReadwrite(const bool &value) { + auto oldValue = m_data->p_readwrite; + m_data->p_readwrite = value; + m_data->markPropertySet(12); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("readwrite"), value); + }); + } + if (m_data->p_readwrite != oldValue) { + Q_EMIT readwriteChanged(); + Q_EMIT valueChanged(QStringLiteral("readwrite"), value); + } + } + void resetReadwrite() { + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("readwrite")); + }); } } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QBindable bindableReadwrite() { + return QBindable(this, "readwrite"); + } +#endif + Q_INVOKABLE bool readwriteIsDefaultValue() const { + return !m_data->testPropertySet(12); + } QVariantMap struct() const { - return p_struct; + return m_data->p_struct; } void setStruct(const QVariantMap &value) { - auto oldValue = p_struct; - p_struct = value; - markPropertySet(11); - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this, value]() { - m_config.loadRelaxed()->setValue(QStringLiteral("struct"), value); + auto oldValue = m_data->p_struct; + m_data->p_struct = value; + m_data->markPropertySet(13); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("struct"), value); }); } - if (p_struct != oldValue) { + if (m_data->p_struct != oldValue) { Q_EMIT structChanged(); + Q_EMIT valueChanged(QStringLiteral("struct"), value); + } + } + void resetStruct() { + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("struct")); + }); } } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QBindable bindableStruct() { + return QBindable(this, "struct"); + } +#endif + Q_INVOKABLE bool structIsDefaultValue() const { + return !m_data->testPropertySet(13); + } +protected: + bool event(QEvent *e) override { + if (e->type() == QEvent::ThreadChange) { + Q_ASSERT(this->thread() == m_data->thread()); + } + return QObject::event(e); + } Q_SIGNALS: + void configInitializeFailed(); + void configInitializeSucceed(DTK_CORE_NAMESPACE::DConfig *config); + void valueChanged(const QString &key, const QVariant &value); void arrayChanged(); void array_mapChanged(); void array_map_structChanged(); @@ -328,11 +707,29 @@ class dconf-example_meta : public QObject { void numberChanged(); void numberDoubleChanged(); void publicConfigChanged(); + void readonlyChanged(); + void readwriteChanged(); void structChanged(); + private: - void initialize(DTK_CORE_NAMESPACE::DConfig *config) { - Q_ASSERT(!m_config.loadRelaxed()); - m_config.storeRelaxed(config); + // Prevent external moveToThread calls + using QObject::moveToThread; + class Data : public QObject { + public: + enum class Status { + Invalid = 0, + Initializing = 1, + Succeeded = 2, + Failed = 3, + Destroyed = 4 + }; + + explicit Data() + : QObject(nullptr) {} + + inline void initializeInConfigThread(DTK_CORE_NAMESPACE::DConfig *config) { + Q_ASSERT(!m_config.loadRelaxed()); + m_config.storeRelaxed(config); if (testPropertySet(0)) { config->setValue(QStringLiteral("array"), QVariant::fromValue(p_array)); } else { @@ -389,166 +786,402 @@ class dconf-example_meta : public QObject { updateValue(QStringLiteral("publicConfig"), QVariant::fromValue(p_publicConfig)); } if (testPropertySet(11)) { + config->setValue(QStringLiteral("readonly"), QVariant::fromValue(p_readonly)); + } else { + updateValue(QStringLiteral("readonly"), QVariant::fromValue(p_readonly)); + } + if (testPropertySet(12)) { + config->setValue(QStringLiteral("readwrite"), QVariant::fromValue(p_readwrite)); + } else { + updateValue(QStringLiteral("readwrite"), QVariant::fromValue(p_readwrite)); + } + if (testPropertySet(13)) { config->setValue(QStringLiteral("struct"), QVariant::fromValue(p_struct)); } else { updateValue(QStringLiteral("struct"), QVariant::fromValue(p_struct)); } + connect(config, &DTK_CORE_NAMESPACE::DConfig::valueChanged, this, [this](const QString &key) { + updateValue(key); + }, Qt::DirectConnection); + } - connect(config, &DTK_CORE_NAMESPACE::DConfig::valueChanged, this, [this](const QString &key) { - updateValue(key); - }, Qt::DirectConnection); - } - void updateValue(const QString &key, const QVariant &fallback = QVariant()) { - Q_ASSERT(QThread::currentThread() == m_config.loadRelaxed()->thread()); - const QVariant &value = m_config.loadRelaxed()->value(key, fallback); - if (key == QStringLiteral("array")) { - auto newValue = qvariant_cast>(value); - QMetaObject::invokeMethod(this, [this, newValue]() { - if (p_array != newValue) { - p_array = newValue; - Q_EMIT arrayChanged(); + inline void updateValue(const QString &key, const QVariant &fallback = QVariant()) { + auto config = m_config.loadRelaxed(); + if (!config) + return; + Q_ASSERT(QThread::currentThread() == config->thread()); + const QVariant &v = config->value(key, fallback); + if (key == QStringLiteral("array")) { + markPropertySet(0, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + if (key == QStringLiteral("array_map")) { + markPropertySet(1, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + if (key == QStringLiteral("array_map_struct")) { + markPropertySet(2, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + if (key == QStringLiteral("canExit")) { + markPropertySet(3, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + if (key == QStringLiteral("key2")) { + markPropertySet(4, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + if (key == QStringLiteral("key3")) { + markPropertySet(5, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + if (key == QStringLiteral("map")) { + markPropertySet(6, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + if (key == QStringLiteral("map_array")) { + markPropertySet(7, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + if (key == QStringLiteral("number")) { + markPropertySet(8, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + if (key == QStringLiteral("numberDouble")) { + markPropertySet(9, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + if (key == QStringLiteral("publicConfig")) { + markPropertySet(10, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + if (key == QStringLiteral("readonly")) { + markPropertySet(11, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + if (key == QStringLiteral("readwrite")) { + markPropertySet(12, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + if (key == QStringLiteral("struct")) { + markPropertySet(13, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + } + + inline void updateProperty(const QString &key, const QVariant &v) { + if (key == QStringLiteral("array")) { + QList nv = qvariant_cast>(v); + if (p_array != nv) { + p_array = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->arrayChanged(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } } - }); - return; - } - if (key == QStringLiteral("array_map")) { - auto newValue = qvariant_cast>(value); - QMetaObject::invokeMethod(this, [this, newValue]() { - if (p_array_map != newValue) { - p_array_map = newValue; - Q_EMIT array_mapChanged(); + return; + } + if (key == QStringLiteral("array_map")) { + QList nv = qvariant_cast>(v); + if (p_array_map != nv) { + p_array_map = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->array_mapChanged(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } } - }); - return; - } - if (key == QStringLiteral("array_map_struct")) { - auto newValue = qvariant_cast>(value); - QMetaObject::invokeMethod(this, [this, newValue]() { - if (p_array_map_struct != newValue) { - p_array_map_struct = newValue; - Q_EMIT array_map_structChanged(); + return; + } + if (key == QStringLiteral("array_map_struct")) { + QList nv = qvariant_cast>(v); + if (p_array_map_struct != nv) { + p_array_map_struct = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->array_map_structChanged(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } } - }); - return; - } - if (key == QStringLiteral("canExit")) { - auto newValue = qvariant_cast(value); - QMetaObject::invokeMethod(this, [this, newValue]() { - if (p_canExit != newValue) { - p_canExit = newValue; - Q_EMIT canExitChanged(); + return; + } + if (key == QStringLiteral("canExit")) { + bool nv = qvariant_cast(v); + if (p_canExit != nv) { + p_canExit = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->canExitChanged(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } } - }); - return; - } - if (key == QStringLiteral("key2")) { - auto newValue = qvariant_cast(value); - QMetaObject::invokeMethod(this, [this, newValue]() { - if (p_key2 != newValue) { - p_key2 = newValue; - Q_EMIT key2Changed(); + return; + } + if (key == QStringLiteral("key2")) { + QString nv = qvariant_cast(v); + if (p_key2 != nv) { + p_key2 = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->key2Changed(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } } - }); - return; - } - if (key == QStringLiteral("key3")) { - auto newValue = qvariant_cast(value); - QMetaObject::invokeMethod(this, [this, newValue]() { - if (p_key3 != newValue) { - p_key3 = newValue; - Q_EMIT key3Changed(); + return; + } + if (key == QStringLiteral("key3")) { + QString nv = qvariant_cast(v); + if (p_key3 != nv) { + p_key3 = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->key3Changed(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } } - }); - return; - } - if (key == QStringLiteral("map")) { - auto newValue = qvariant_cast(value); - QMetaObject::invokeMethod(this, [this, newValue]() { - if (p_map != newValue) { - p_map = newValue; - Q_EMIT mapChanged(); + return; + } + if (key == QStringLiteral("map")) { + QVariantMap nv = qvariant_cast(v); + if (p_map != nv) { + p_map = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->mapChanged(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } } - }); - return; - } - if (key == QStringLiteral("map_array")) { - auto newValue = qvariant_cast(value); - QMetaObject::invokeMethod(this, [this, newValue]() { - if (p_map_array != newValue) { - p_map_array = newValue; - Q_EMIT map_arrayChanged(); + return; + } + if (key == QStringLiteral("map_array")) { + QVariantMap nv = qvariant_cast(v); + if (p_map_array != nv) { + p_map_array = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->map_arrayChanged(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } } - }); - return; - } - if (key == QStringLiteral("number")) { - auto newValue = qvariant_cast(value); - QMetaObject::invokeMethod(this, [this, newValue]() { - if (p_number != newValue) { - p_number = newValue; - Q_EMIT numberChanged(); + return; + } + if (key == QStringLiteral("number")) { + qlonglong nv = qvariant_cast(v); + if (p_number != nv) { + p_number = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->numberChanged(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } } - }); - return; - } - if (key == QStringLiteral("numberDouble")) { - auto newValue = qvariant_cast(value); - QMetaObject::invokeMethod(this, [this, newValue]() { - if (p_numberDouble != newValue) { - p_numberDouble = newValue; - Q_EMIT numberDoubleChanged(); + return; + } + if (key == QStringLiteral("numberDouble")) { + double nv = qvariant_cast(v); + if (p_numberDouble != nv) { + p_numberDouble = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->numberDoubleChanged(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } } - }); - return; - } - if (key == QStringLiteral("publicConfig")) { - auto newValue = qvariant_cast(value); - QMetaObject::invokeMethod(this, [this, newValue]() { - if (p_publicConfig != newValue) { - p_publicConfig = newValue; - Q_EMIT publicConfigChanged(); + return; + } + if (key == QStringLiteral("publicConfig")) { + bool nv = qvariant_cast(v); + if (p_publicConfig != nv) { + p_publicConfig = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->publicConfigChanged(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } } - }); - return; - } - if (key == QStringLiteral("struct")) { - auto newValue = qvariant_cast(value); - QMetaObject::invokeMethod(this, [this, newValue]() { - if (p_struct != newValue) { - p_struct = newValue; - Q_EMIT structChanged(); + return; + } + if (key == QStringLiteral("readonly")) { + bool nv = qvariant_cast(v); + if (p_readonly != nv) { + p_readonly = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->readonlyChanged(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } } - }); - return; - } - } - inline void markPropertySet(const int index) { - if (index < 32) { - m_propertySetStatus0.fetchAndOrOrdered(1 << (index - 0)); - return; - } - Q_UNREACHABLE(); - } - inline bool testPropertySet(const int index) const { - if (index < 32) { - return (m_propertySetStatus0.loadRelaxed() & (1 << (index - 0))); - } - Q_UNREACHABLE(); - } - QAtomicPointer m_config = nullptr; - QList p_array { QList{QVariant(QStringLiteral("value1")), QVariant(QStringLiteral("value2"))} }; - QList p_array_map { QList{QVariant(QVariantMap{{QStringLiteral("key1"), QVariant(QStringLiteral("value1"))}, {QStringLiteral("key2"), QVariant(QStringLiteral("value2"))}})} }; - QList p_array_map_struct { QList{QVariant(QVariantMap{{QStringLiteral("key1"), QVariant(QVariantMap{{QStringLiteral("field1"), QVariant(QStringLiteral("value1"))}})}, {QStringLiteral("key2"), QVariant(QStringLiteral("value2"))}})} }; - bool p_canExit { true }; - QString p_key2 { QStringLiteral("125") }; - QString p_key3 { QStringLiteral("application") }; - QVariantMap p_map { QVariantMap{{QStringLiteral("key1"), QVariant(QStringLiteral("value1"))}, {QStringLiteral("key2"), QVariant(QStringLiteral("value2"))}} }; - QVariantMap p_map_array { QVariantMap{{QStringLiteral("key1"), QVariant(QList{QVariant(QStringLiteral("value1"))})}, {QStringLiteral("key2"), QVariant(QList{QVariant(QStringLiteral("value2"))})}} }; - double p_number { 1 }; - double p_numberDouble { 1 }; - bool p_publicConfig { true }; - QVariantMap p_struct { QVariantMap{{QStringLiteral("key1"), QVariant(QStringLiteral("value1"))}, {QStringLiteral("key2"), QVariant(QStringLiteral("value2"))}} }; - QAtomicInteger m_propertySetStatus0 = 0; + return; + } + if (key == QStringLiteral("readwrite")) { + bool nv = qvariant_cast(v); + if (p_readwrite != nv) { + p_readwrite = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->readwriteChanged(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } + } + return; + } + if (key == QStringLiteral("struct")) { + QVariantMap nv = qvariant_cast(v); + if (p_struct != nv) { + p_struct = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->structChanged(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } + } + return; + } + } + + inline void markPropertySet(const int index, bool on = true) { + if (index < 32) { + if (on) + m_propertySetStatus0.fetchAndOrOrdered(1 << (index - 0)); + else + m_propertySetStatus0.fetchAndAndOrdered(~(1 << (index - 0))); + return; + } + Q_UNREACHABLE(); + } + + inline bool testPropertySet(const int index) const { + if (index < 32) { + return (m_propertySetStatus0.loadRelaxed() & (1 << (index - 0))); + } + Q_UNREACHABLE(); + } + + // Member variables + QAtomicPointer m_config = nullptr; + QAtomicInteger m_status = static_cast(Status::Invalid); + QPointer m_userConfig = nullptr; + QAtomicInteger m_propertySetStatus0 = 0; + + // Property storage + QList p_array { QList{QVariant(QStringLiteral(u"\u0076\u0061\u006c\u0075\u0065\u0031")), QVariant(QStringLiteral(u"\u0076\u0061\u006c\u0075\u0065\u0032"))} }; + QList p_array_map { QList{QVariant(QVariantMap{{QStringLiteral(u"\u006b\u0065\u0079\u0031"), QVariant(QStringLiteral(u"\u0076\u0061\u006c\u0075\u0065\u0031"))}, {QStringLiteral(u"\u006b\u0065\u0079\u0032"), QVariant(QStringLiteral(u"\u0076\u0061\u006c\u0075\u0065\u0032"))}})} }; + QList p_array_map_struct { QList{QVariant(QVariantMap{{QStringLiteral(u"\u006b\u0065\u0079\u0031"), QVariant(QVariantMap{{QStringLiteral(u"\u0066\u0069\u0065\u006c\u0064\u0031"), QVariant(QStringLiteral(u"\u0076\u0061\u006c\u0075\u0065\u0031"))}})}, {QStringLiteral(u"\u006b\u0065\u0079\u0032"), QVariant(QStringLiteral(u"\u0076\u0061\u006c\u0075\u0065\u0032"))}})} }; + bool p_canExit { true }; + // Default value: "125" + QString p_key2 { QStringLiteral(u"\u0031\u0032\u0035") }; + // Default value: "application" + QString p_key3 { QStringLiteral(u"\u0061\u0070\u0070\u006c\u0069\u0063\u0061\u0074\u0069\u006f\u006e") }; + QVariantMap p_map { QVariantMap{{QStringLiteral(u"\u006b\u0065\u0079\u0031"), QVariant(QStringLiteral(u"\u0076\u0061\u006c\u0075\u0065\u0031"))}, {QStringLiteral(u"\u006b\u0065\u0079\u0032"), QVariant(QStringLiteral(u"\u0076\u0061\u006c\u0075\u0065\u0032"))}} }; + QVariantMap p_map_array { QVariantMap{{QStringLiteral(u"\u006b\u0065\u0079\u0031"), QVariant(QList{QVariant(QStringLiteral(u"\u0076\u0061\u006c\u0075\u0065\u0031"))})}, {QStringLiteral(u"\u006b\u0065\u0079\u0032"), QVariant(QList{QVariant(QStringLiteral(u"\u0076\u0061\u006c\u0075\u0065\u0032"))})}} }; + qlonglong p_number { 1 }; + double p_numberDouble { 1 }; + bool p_publicConfig { true }; + bool p_readonly { true }; + bool p_readwrite { true }; + QVariantMap p_struct { QVariantMap{{QStringLiteral(u"\u006b\u0065\u0079\u0031"), QVariant(QStringLiteral(u"\u0076\u0061\u006c\u0075\u0065\u0031"))}, {QStringLiteral(u"\u006b\u0065\u0079\u0032"), QVariant(QStringLiteral(u"\u0076\u0061\u006c\u0075\u0065\u0032"))}} }; + }; + + Data *m_data; }; -#endif // DCONF-EXAMPLE_META_H +#endif // DCONFIG_DCONF-EXAMPLE_META_H diff --git a/toolGenerate/dconfig2cpp/dconf-example_other_app_configure_meta.hpp b/toolGenerate/dconfig2cpp/dconf-example_other_app_configure_meta.hpp index 1f9a54eb..56933093 100644 --- a/toolGenerate/dconfig2cpp/dconf-example_other_app_configure_meta.hpp +++ b/toolGenerate/dconfig2cpp/dconf-example_other_app_configure_meta.hpp @@ -1,158 +1,291 @@ /** * This file is generated by dconfig2cpp. - * Command line arguments: ./dconfig2cpp -p ./dtkcore/toolGenerate/dconfig2cpp ./dtkcore/tests/data/dconf-example_other_app_configure.meta.json - * Generation time: 2025-01-14T10:54:59 + * Command line arguments: ./build/tools/dconfig2cpp/dconfig2cpp ./tests/data/dconf-example_other_app_configure.meta.json -o ./toolGenerate/dconfig2cpp/dconf-example_other_app_configure_meta.hpp + * Generation time: 2026-01-19T15:29:47 * JSON file version: 1.0 - * + * * WARNING: DO NOT MODIFY THIS FILE MANUALLY. * If you need to change the content, please modify the dconfig2cpp tool. */ -#ifndef DCONF-EXAMPLE_OTHER_APP_CONFIGURE_META_H -#define DCONF-EXAMPLE_OTHER_APP_CONFIGURE_META_H +#ifndef DCONFIG_DCONF-EXAMPLE_OTHER_APP_CONFIGURE_META_H +#define DCONFIG_DCONF-EXAMPLE_OTHER_APP_CONFIGURE_META_H #include #include +#include #include #include #include +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +#include +#endif +#include +#include #include -class dconf-example_other_app_configure_meta : public QObject { +class dconfig_dconf-example_other_app_configure_meta : public QObject { Q_OBJECT - Q_PROPERTY(QString appPrivate READ appPrivate WRITE setAppPrivate NOTIFY appPrivateChanged) - Q_PROPERTY(QString appPublic READ appPublic WRITE setAppPublic NOTIFY appPublicChanged) + Q_PROPERTY(QString appPrivate READ appPrivate WRITE setAppPrivate NOTIFY appPrivateChanged RESET resetAppPrivate) + Q_PROPERTY(QString appPublic READ appPublic WRITE setAppPublic NOTIFY appPublicChanged RESET resetAppPublic) + Q_CLASSINFO("DConfigKeyList", "appPrivate;appPublic") + Q_CLASSINFO("DConfigFileName", "dconf-example_other_app_configure.meta") + Q_CLASSINFO("DConfigFileVersion", "1.0") + public: - explicit dconf-example_other_app_configure_meta(QThread *thread, const QString &appId, const QString &name, const QString &subpath, QObject *parent = nullptr) - : QObject(parent) { + explicit dconfig_dconf-example_other_app_configure_meta(QThread *thread, DTK_CORE_NAMESPACE::DConfigBackend *backend, + const QString &name, const QString &appId, const QString &subpath, + bool isGeneric, QObject *parent) + : QObject(parent), m_data(new Data) { + m_data->m_userConfig = this; + m_data->moveToThread(this->thread()); if (!thread->isRunning()) { - qWarning() << QStringLiteral("Warning: The provided thread is not running."); + qWarning() << QLatin1String("Warning: The provided thread is not running."); } Q_ASSERT(QThread::currentThread() != thread); auto worker = new QObject(); worker->moveToThread(thread); - QMetaObject::invokeMethod(worker, [=]() { - auto config = DTK_CORE_NAMESPACE::DConfig::create(appId, name, subpath, nullptr); - if (!config) { - qWarning() << QStringLiteral("Failed to create DConfig instance."); - worker->deleteLater(); + + QPointer safeData(m_data); + + QMetaObject::invokeMethod(worker, [safeData, backend, name, appId, subpath, isGeneric, worker]() mutable { + delete worker; + worker = nullptr; + + // Check if Data object is still valid + if (!safeData) { return; } - config->moveToThread(QThread::currentThread()); - initialize(config); - worker->deleteLater(); - }); - } - explicit dconf-example_other_app_configure_meta(QThread *thread, DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &appId, const QString &name, const QString &subpath, QObject *parent = nullptr) - : QObject(parent) { - if (!thread->isRunning()) { - qWarning() << QStringLiteral("Warning: The provided thread is not running."); - } - Q_ASSERT(QThread::currentThread() != thread); - auto worker = new QObject(); - worker->moveToThread(thread); - QMetaObject::invokeMethod(worker, [=]() { - auto config = DTK_CORE_NAMESPACE::DConfig::create(backend, appId, name, subpath, nullptr); - if (!config) { - qWarning() << QStringLiteral("Failed to create DConfig instance."); - worker->deleteLater(); + if (!safeData->m_status.testAndSetOrdered(static_cast(Data::Status::Invalid), + static_cast(Data::Status::Initializing))) { + // CAS failed, state already changed - userConfig destructor will handle cleanup + // Do not attempt to delete here as it would race with destructor return; } - config->moveToThread(QThread::currentThread()); - initialize(config); - worker->deleteLater(); - }); - } - explicit dconf-example_other_app_configure_meta(QThread *thread, const QString &name, const QString &subpath, QObject *parent = nullptr) - : QObject(parent) { - if (!thread->isRunning()) { - qWarning() << QStringLiteral("Warning: The provided thread is not running."); - } - Q_ASSERT(QThread::currentThread() != thread); - auto worker = new QObject(); - worker->moveToThread(thread); - QMetaObject::invokeMethod(worker, [=]() { - auto config = DTK_CORE_NAMESPACE::DConfig::create(name, subpath, nullptr); - if (!config) { - qWarning() << QStringLiteral("Failed to create DConfig instance."); - worker->deleteLater(); - return; + DTK_CORE_NAMESPACE::DConfig *config = nullptr; + if (isGeneric) { + if (backend) { + config = DTK_CORE_NAMESPACE::DConfig::createGeneric(backend, name, subpath, nullptr); + } else { + config = DTK_CORE_NAMESPACE::DConfig::createGeneric(name, subpath, nullptr); + } + } else { + if (backend) { + if (appId.isNull()) { + config = DTK_CORE_NAMESPACE::DConfig::create(backend, DTK_CORE_NAMESPACE::DSGApplication::id(), + name, subpath, nullptr); + } else { + config = DTK_CORE_NAMESPACE::DConfig::create(backend, appId, name, subpath, nullptr); + } + } else { + if (appId.isNull()) { + config = DTK_CORE_NAMESPACE::DConfig::create(DTK_CORE_NAMESPACE::DSGApplication::id(), + name, subpath, nullptr); + } else { + config = DTK_CORE_NAMESPACE::DConfig::create(appId, name, subpath, nullptr); + } + } } - config->moveToThread(QThread::currentThread()); - initialize(config); - worker->deleteLater(); - }); - } - explicit dconf-example_other_app_configure_meta(QThread *thread, DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &name, const QString &subpath, QObject *parent = nullptr) - : QObject(parent) { - if (!thread->isRunning()) { - qWarning() << QStringLiteral("Warning: The provided thread is not running."); - } - Q_ASSERT(QThread::currentThread() != thread); - auto worker = new QObject(); - worker->moveToThread(thread); - QMetaObject::invokeMethod(worker, [=]() { - auto config = DTK_CORE_NAMESPACE::DConfig::create(backend, name, subpath, nullptr); - if (!config) { - qWarning() << QStringLiteral("Failed to create DConfig instance."); - worker->deleteLater(); + if (!config || !config->isValid()) { + qWarning() << QLatin1String("Failed to create DConfig instance."); + + if (safeData && safeData->m_status.testAndSetOrdered(static_cast(Data::Status::Initializing), + static_cast(Data::Status::Failed))) { + if (safeData->m_userConfig) { + QMetaObject::invokeMethod(safeData, [safeData]() { + if (safeData && safeData->m_userConfig) { + Q_EMIT safeData->m_userConfig->configInitializeFailed(); + } + }, Qt::QueuedConnection); + } + } else { + // CAS failed, destroy data object + safeData->deleteLater(); + } + + if (config) + delete config; + return; } config->moveToThread(QThread::currentThread()); - initialize(config); - worker->deleteLater(); + // Initialize through Data class + safeData->initializeInConfigThread(config); + + // Try to transition from Initializing to Succeeded + if (safeData && safeData->m_status.testAndSetOrdered(static_cast(Data::Status::Initializing), + static_cast(Data::Status::Succeeded))) { + // CAS succeeded, connect destroyed signal + QObject::connect(config, &QObject::destroyed, safeData, &QObject::deleteLater); + } else { + // CAS failed - state changed (e.g., set to Destroyed) + // We must clean up the config we just created + config->deleteLater(); + safeData->deleteLater(); + } }); } - ~dconf-example_other_app_configure_meta() { - if (m_config.loadRelaxed()) { - m_config.loadRelaxed()->deleteLater(); + static dconfig_dconf-example_other_app_configure_meta* create(const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_dconf-example_other_app_configure_meta(thread, nullptr, QStringLiteral(u"\u0064\u0063\u006f\u006e\u0066\u002d\u0065\u0078\u0061\u006d\u0070\u006c\u0065\u005f\u006f\u0074\u0068\u0065\u0072\u005f\u0061\u0070\u0070\u005f\u0063\u006f\u006e\u0066\u0069\u0067\u0075\u0072\u0065\u002e\u006d\u0065\u0074\u0061"), appId, subpath, false, parent); } + static dconfig_dconf-example_other_app_configure_meta* create(DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_dconf-example_other_app_configure_meta(thread, backend, QStringLiteral(u"\u0064\u0063\u006f\u006e\u0066\u002d\u0065\u0078\u0061\u006d\u0070\u006c\u0065\u005f\u006f\u0074\u0068\u0065\u0072\u005f\u0061\u0070\u0070\u005f\u0063\u006f\u006e\u0066\u0069\u0067\u0075\u0072\u0065\u002e\u006d\u0065\u0074\u0061"), appId, subpath, false, parent); } + static dconfig_dconf-example_other_app_configure_meta* createByName(const QString &name, const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_dconf-example_other_app_configure_meta(thread, nullptr, name, appId, subpath, false, parent); } + static dconfig_dconf-example_other_app_configure_meta* createByName(DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &name, const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_dconf-example_other_app_configure_meta(thread, backend, name, appId, subpath, false, parent); } + static dconfig_dconf-example_other_app_configure_meta* createGeneric(const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_dconf-example_other_app_configure_meta(thread, nullptr, QStringLiteral(u"\u0064\u0063\u006f\u006e\u0066\u002d\u0065\u0078\u0061\u006d\u0070\u006c\u0065\u005f\u006f\u0074\u0068\u0065\u0072\u005f\u0061\u0070\u0070\u005f\u0063\u006f\u006e\u0066\u0069\u0067\u0075\u0072\u0065\u002e\u006d\u0065\u0074\u0061"), {}, subpath, true, parent); } + static dconfig_dconf-example_other_app_configure_meta* create(DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_dconf-example_other_app_configure_meta(thread, backend, QStringLiteral(u"\u0064\u0063\u006f\u006e\u0066\u002d\u0065\u0078\u0061\u006d\u0070\u006c\u0065\u005f\u006f\u0074\u0068\u0065\u0072\u005f\u0061\u0070\u0070\u005f\u0063\u006f\u006e\u0066\u0069\u0067\u0075\u0072\u0065\u002e\u006d\u0065\u0074\u0061"), {}, subpath, true, parent); } + static dconfig_dconf-example_other_app_configure_meta* createGenericByName(const QString &name, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_dconf-example_other_app_configure_meta(thread, nullptr, name, {}, subpath, true, parent); } + static dconfig_dconf-example_other_app_configure_meta* createGenericByName(DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &name, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_dconf-example_other_app_configure_meta(thread, backend, name, {}, subpath, true, parent); } + ~dconfig_dconf-example_other_app_configure_meta() { + int oldStatus = m_data->m_status.fetchAndStoreOrdered(static_cast(Data::Status::Destroyed)); + + m_data->m_userConfig = nullptr; + if (oldStatus == static_cast(Data::Status::Succeeded)) { + // When Succeeded, release config object only + auto config = m_data->m_config.loadRelaxed(); + Q_ASSERT(config); + config->deleteLater(); + // m_data will be deleted by config's destroyed signal + } else if (oldStatus == static_cast(Data::Status::Failed) || + oldStatus == static_cast(Data::Status::Invalid)) { + // When Failed or Invalid, directly clean up Data object + m_data->deleteLater(); } } + Q_INVOKABLE DTK_CORE_NAMESPACE::DConfig *config() const { + return m_data->m_config.loadRelaxed(); + } + Q_INVOKABLE bool isInitializeSucceed() const { + return m_data->m_status.loadRelaxed() == static_cast(Data::Status::Succeeded); + } + Q_INVOKABLE bool isInitializeFailed() const { + return m_data->m_status.loadRelaxed() == static_cast(Data::Status::Failed); + } + Q_INVOKABLE bool isInitializing() const { + return m_data->m_status.loadRelaxed() == static_cast(Data::Status::Initializing); + } + + Q_INVOKABLE QStringList keyList() const { + return { QStringLiteral("appPrivate"), + QStringLiteral("appPublic")}; + } + + Q_INVOKABLE bool isDefaultValue(const QString &key) const { + if (key == QStringLiteral("appPrivate")) + return appPrivateIsDefaultValue(); + if (key == QStringLiteral("appPublic")) + return appPublicIsDefaultValue(); + return false; + } + QString appPrivate() const { - return p_appPrivate; + return m_data->p_appPrivate; } void setAppPrivate(const QString &value) { - auto oldValue = p_appPrivate; - p_appPrivate = value; - markPropertySet(0); - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this, value]() { - m_config.loadRelaxed()->setValue(QStringLiteral("appPrivate"), value); + auto oldValue = m_data->p_appPrivate; + m_data->p_appPrivate = value; + m_data->markPropertySet(0); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("appPrivate"), value); }); } - if (p_appPrivate != oldValue) { + if (m_data->p_appPrivate != oldValue) { Q_EMIT appPrivateChanged(); + Q_EMIT valueChanged(QStringLiteral("appPrivate"), value); + } + } + void resetAppPrivate() { + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("appPrivate")); + }); } } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QBindable bindableAppPrivate() { + return QBindable(this, "appPrivate"); + } +#endif + Q_INVOKABLE bool appPrivateIsDefaultValue() const { + return !m_data->testPropertySet(0); + } QString appPublic() const { - return p_appPublic; + return m_data->p_appPublic; } void setAppPublic(const QString &value) { - auto oldValue = p_appPublic; - p_appPublic = value; - markPropertySet(1); - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this, value]() { - m_config.loadRelaxed()->setValue(QStringLiteral("appPublic"), value); + auto oldValue = m_data->p_appPublic; + m_data->p_appPublic = value; + m_data->markPropertySet(1); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("appPublic"), value); }); } - if (p_appPublic != oldValue) { + if (m_data->p_appPublic != oldValue) { Q_EMIT appPublicChanged(); + Q_EMIT valueChanged(QStringLiteral("appPublic"), value); + } + } + void resetAppPublic() { + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("appPublic")); + }); } } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QBindable bindableAppPublic() { + return QBindable(this, "appPublic"); + } +#endif + Q_INVOKABLE bool appPublicIsDefaultValue() const { + return !m_data->testPropertySet(1); + } +protected: + bool event(QEvent *e) override { + if (e->type() == QEvent::ThreadChange) { + Q_ASSERT(this->thread() == m_data->thread()); + } + return QObject::event(e); + } Q_SIGNALS: + void configInitializeFailed(); + void configInitializeSucceed(DTK_CORE_NAMESPACE::DConfig *config); + void valueChanged(const QString &key, const QVariant &value); void appPrivateChanged(); void appPublicChanged(); + private: - void initialize(DTK_CORE_NAMESPACE::DConfig *config) { - Q_ASSERT(!m_config.loadRelaxed()); - m_config.storeRelaxed(config); + // Prevent external moveToThread calls + using QObject::moveToThread; + class Data : public QObject { + public: + enum class Status { + Invalid = 0, + Initializing = 1, + Succeeded = 2, + Failed = 3, + Destroyed = 4 + }; + + explicit Data() + : QObject(nullptr) {} + + inline void initializeInConfigThread(DTK_CORE_NAMESPACE::DConfig *config) { + Q_ASSERT(!m_config.loadRelaxed()); + m_config.storeRelaxed(config); if (testPropertySet(0)) { config->setValue(QStringLiteral("appPrivate"), QVariant::fromValue(p_appPrivate)); } else { @@ -163,52 +296,100 @@ class dconf-example_other_app_configure_meta : public QObject { } else { updateValue(QStringLiteral("appPublic"), QVariant::fromValue(p_appPublic)); } + connect(config, &DTK_CORE_NAMESPACE::DConfig::valueChanged, this, [this](const QString &key) { + updateValue(key); + }, Qt::DirectConnection); + } - connect(config, &DTK_CORE_NAMESPACE::DConfig::valueChanged, this, [this](const QString &key) { - updateValue(key); - }, Qt::DirectConnection); - } - void updateValue(const QString &key, const QVariant &fallback = QVariant()) { - Q_ASSERT(QThread::currentThread() == m_config.loadRelaxed()->thread()); - const QVariant &value = m_config.loadRelaxed()->value(key, fallback); - if (key == QStringLiteral("appPrivate")) { - auto newValue = qvariant_cast(value); - QMetaObject::invokeMethod(this, [this, newValue]() { - if (p_appPrivate != newValue) { - p_appPrivate = newValue; - Q_EMIT appPrivateChanged(); + inline void updateValue(const QString &key, const QVariant &fallback = QVariant()) { + auto config = m_config.loadRelaxed(); + if (!config) + return; + Q_ASSERT(QThread::currentThread() == config->thread()); + const QVariant &v = config->value(key, fallback); + if (key == QStringLiteral("appPrivate")) { + markPropertySet(0, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + if (key == QStringLiteral("appPublic")) { + markPropertySet(1, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + } + + inline void updateProperty(const QString &key, const QVariant &v) { + if (key == QStringLiteral("appPrivate")) { + QString nv = qvariant_cast(v); + if (p_appPrivate != nv) { + p_appPrivate = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->appPrivateChanged(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } } - }); - return; - } - if (key == QStringLiteral("appPublic")) { - auto newValue = qvariant_cast(value); - QMetaObject::invokeMethod(this, [this, newValue]() { - if (p_appPublic != newValue) { - p_appPublic = newValue; - Q_EMIT appPublicChanged(); + return; + } + if (key == QStringLiteral("appPublic")) { + QString nv = qvariant_cast(v); + if (p_appPublic != nv) { + p_appPublic = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->appPublicChanged(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } } - }); - return; + return; + } } - } - inline void markPropertySet(const int index) { - if (index < 32) { - m_propertySetStatus0.fetchAndOrOrdered(1 << (index - 0)); - return; + + inline void markPropertySet(const int index, bool on = true) { + if (index < 32) { + if (on) + m_propertySetStatus0.fetchAndOrOrdered(1 << (index - 0)); + else + m_propertySetStatus0.fetchAndAndOrdered(~(1 << (index - 0))); + return; + } + Q_UNREACHABLE(); } - Q_UNREACHABLE(); - } - inline bool testPropertySet(const int index) const { - if (index < 32) { - return (m_propertySetStatus0.loadRelaxed() & (1 << (index - 0))); + + inline bool testPropertySet(const int index) const { + if (index < 32) { + return (m_propertySetStatus0.loadRelaxed() & (1 << (index - 0))); + } + Q_UNREACHABLE(); } - Q_UNREACHABLE(); - } - QAtomicPointer m_config = nullptr; - QString p_appPrivate { QStringLiteral("appPrivate") }; - QString p_appPublic { QStringLiteral("publicValue") }; - QAtomicInteger m_propertySetStatus0 = 0; + + // Member variables + QAtomicPointer m_config = nullptr; + QAtomicInteger m_status = static_cast(Status::Invalid); + QPointer m_userConfig = nullptr; + QAtomicInteger m_propertySetStatus0 = 0; + + // Property storage + // Default value: "appPrivate" + QString p_appPrivate { QStringLiteral(u"\u0061\u0070\u0070\u0050\u0072\u0069\u0076\u0061\u0074\u0065") }; + // Default value: "publicValue" + QString p_appPublic { QStringLiteral(u"\u0070\u0075\u0062\u006c\u0069\u0063\u0056\u0061\u006c\u0075\u0065") }; + }; + + Data *m_data; }; -#endif // DCONF-EXAMPLE_OTHER_APP_CONFIGURE_META_H +#endif // DCONFIG_DCONF-EXAMPLE_OTHER_APP_CONFIGURE_META_H diff --git a/toolGenerate/dconfig2cpp/dconf-global_meta.hpp b/toolGenerate/dconfig2cpp/dconf-global_meta.hpp index 50071ad4..933930d2 100644 --- a/toolGenerate/dconfig2cpp/dconf-global_meta.hpp +++ b/toolGenerate/dconfig2cpp/dconf-global_meta.hpp @@ -1,180 +1,328 @@ /** * This file is generated by dconfig2cpp. - * Command line arguments: ./dconfig2cpp -p ./dtkcore/toolGenerate/dconfig2cpp ./dtkcore/tests/data/dconf-global.meta.json - * Generation time: 2025-01-14T10:54:59 + * Command line arguments: ./build/tools/dconfig2cpp/dconfig2cpp ./tests/data/dconf-global.meta.json -o ./toolGenerate/dconfig2cpp/dconf-global_meta.hpp + * Generation time: 2026-01-19T15:29:47 * JSON file version: 1.0 - * + * * WARNING: DO NOT MODIFY THIS FILE MANUALLY. * If you need to change the content, please modify the dconfig2cpp tool. */ -#ifndef DCONF-GLOBAL_META_H -#define DCONF-GLOBAL_META_H +#ifndef DCONFIG_DCONF-GLOBAL_META_H +#define DCONFIG_DCONF-GLOBAL_META_H #include #include +#include #include #include #include +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +#include +#endif +#include +#include #include -class dconf-global_meta : public QObject { +class dconfig_dconf-global_meta : public QObject { Q_OBJECT - Q_PROPERTY(QString key3 READ key3 WRITE setKey3 NOTIFY key3Changed) + Q_PROPERTY(QString key3 READ key3 WRITE setKey3 NOTIFY key3Changed RESET resetKey3) + Q_CLASSINFO("DConfigKeyList", "key3") + Q_CLASSINFO("DConfigFileName", "dconf-global.meta") + Q_CLASSINFO("DConfigFileVersion", "1.0") + public: - explicit dconf-global_meta(QThread *thread, const QString &appId, const QString &name, const QString &subpath, QObject *parent = nullptr) - : QObject(parent) { + explicit dconfig_dconf-global_meta(QThread *thread, DTK_CORE_NAMESPACE::DConfigBackend *backend, + const QString &name, const QString &appId, const QString &subpath, + bool isGeneric, QObject *parent) + : QObject(parent), m_data(new Data) { + m_data->m_userConfig = this; + m_data->moveToThread(this->thread()); if (!thread->isRunning()) { - qWarning() << QStringLiteral("Warning: The provided thread is not running."); + qWarning() << QLatin1String("Warning: The provided thread is not running."); } Q_ASSERT(QThread::currentThread() != thread); auto worker = new QObject(); worker->moveToThread(thread); - QMetaObject::invokeMethod(worker, [=]() { - auto config = DTK_CORE_NAMESPACE::DConfig::create(appId, name, subpath, nullptr); - if (!config) { - qWarning() << QStringLiteral("Failed to create DConfig instance."); - worker->deleteLater(); + + QPointer safeData(m_data); + + QMetaObject::invokeMethod(worker, [safeData, backend, name, appId, subpath, isGeneric, worker]() mutable { + delete worker; + worker = nullptr; + + // Check if Data object is still valid + if (!safeData) { return; } - config->moveToThread(QThread::currentThread()); - initialize(config); - worker->deleteLater(); - }); - } - explicit dconf-global_meta(QThread *thread, DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &appId, const QString &name, const QString &subpath, QObject *parent = nullptr) - : QObject(parent) { - if (!thread->isRunning()) { - qWarning() << QStringLiteral("Warning: The provided thread is not running."); - } - Q_ASSERT(QThread::currentThread() != thread); - auto worker = new QObject(); - worker->moveToThread(thread); - QMetaObject::invokeMethod(worker, [=]() { - auto config = DTK_CORE_NAMESPACE::DConfig::create(backend, appId, name, subpath, nullptr); - if (!config) { - qWarning() << QStringLiteral("Failed to create DConfig instance."); - worker->deleteLater(); + if (!safeData->m_status.testAndSetOrdered(static_cast(Data::Status::Invalid), + static_cast(Data::Status::Initializing))) { + // CAS failed, state already changed - userConfig destructor will handle cleanup + // Do not attempt to delete here as it would race with destructor return; } - config->moveToThread(QThread::currentThread()); - initialize(config); - worker->deleteLater(); - }); - } - explicit dconf-global_meta(QThread *thread, const QString &name, const QString &subpath, QObject *parent = nullptr) - : QObject(parent) { - if (!thread->isRunning()) { - qWarning() << QStringLiteral("Warning: The provided thread is not running."); - } - Q_ASSERT(QThread::currentThread() != thread); - auto worker = new QObject(); - worker->moveToThread(thread); - QMetaObject::invokeMethod(worker, [=]() { - auto config = DTK_CORE_NAMESPACE::DConfig::create(name, subpath, nullptr); - if (!config) { - qWarning() << QStringLiteral("Failed to create DConfig instance."); - worker->deleteLater(); - return; + DTK_CORE_NAMESPACE::DConfig *config = nullptr; + if (isGeneric) { + if (backend) { + config = DTK_CORE_NAMESPACE::DConfig::createGeneric(backend, name, subpath, nullptr); + } else { + config = DTK_CORE_NAMESPACE::DConfig::createGeneric(name, subpath, nullptr); + } + } else { + if (backend) { + if (appId.isNull()) { + config = DTK_CORE_NAMESPACE::DConfig::create(backend, DTK_CORE_NAMESPACE::DSGApplication::id(), + name, subpath, nullptr); + } else { + config = DTK_CORE_NAMESPACE::DConfig::create(backend, appId, name, subpath, nullptr); + } + } else { + if (appId.isNull()) { + config = DTK_CORE_NAMESPACE::DConfig::create(DTK_CORE_NAMESPACE::DSGApplication::id(), + name, subpath, nullptr); + } else { + config = DTK_CORE_NAMESPACE::DConfig::create(appId, name, subpath, nullptr); + } + } } - config->moveToThread(QThread::currentThread()); - initialize(config); - worker->deleteLater(); - }); - } - explicit dconf-global_meta(QThread *thread, DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &name, const QString &subpath, QObject *parent = nullptr) - : QObject(parent) { - if (!thread->isRunning()) { - qWarning() << QStringLiteral("Warning: The provided thread is not running."); - } - Q_ASSERT(QThread::currentThread() != thread); - auto worker = new QObject(); - worker->moveToThread(thread); - QMetaObject::invokeMethod(worker, [=]() { - auto config = DTK_CORE_NAMESPACE::DConfig::create(backend, name, subpath, nullptr); - if (!config) { - qWarning() << QStringLiteral("Failed to create DConfig instance."); - worker->deleteLater(); + if (!config || !config->isValid()) { + qWarning() << QLatin1String("Failed to create DConfig instance."); + + if (safeData && safeData->m_status.testAndSetOrdered(static_cast(Data::Status::Initializing), + static_cast(Data::Status::Failed))) { + if (safeData->m_userConfig) { + QMetaObject::invokeMethod(safeData, [safeData]() { + if (safeData && safeData->m_userConfig) { + Q_EMIT safeData->m_userConfig->configInitializeFailed(); + } + }, Qt::QueuedConnection); + } + } else { + // CAS failed, destroy data object + safeData->deleteLater(); + } + + if (config) + delete config; + return; } config->moveToThread(QThread::currentThread()); - initialize(config); - worker->deleteLater(); + // Initialize through Data class + safeData->initializeInConfigThread(config); + + // Try to transition from Initializing to Succeeded + if (safeData && safeData->m_status.testAndSetOrdered(static_cast(Data::Status::Initializing), + static_cast(Data::Status::Succeeded))) { + // CAS succeeded, connect destroyed signal + QObject::connect(config, &QObject::destroyed, safeData, &QObject::deleteLater); + } else { + // CAS failed - state changed (e.g., set to Destroyed) + // We must clean up the config we just created + config->deleteLater(); + safeData->deleteLater(); + } }); } - ~dconf-global_meta() { - if (m_config.loadRelaxed()) { - m_config.loadRelaxed()->deleteLater(); + static dconfig_dconf-global_meta* create(const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_dconf-global_meta(thread, nullptr, QStringLiteral(u"\u0064\u0063\u006f\u006e\u0066\u002d\u0067\u006c\u006f\u0062\u0061\u006c\u002e\u006d\u0065\u0074\u0061"), appId, subpath, false, parent); } + static dconfig_dconf-global_meta* create(DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_dconf-global_meta(thread, backend, QStringLiteral(u"\u0064\u0063\u006f\u006e\u0066\u002d\u0067\u006c\u006f\u0062\u0061\u006c\u002e\u006d\u0065\u0074\u0061"), appId, subpath, false, parent); } + static dconfig_dconf-global_meta* createByName(const QString &name, const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_dconf-global_meta(thread, nullptr, name, appId, subpath, false, parent); } + static dconfig_dconf-global_meta* createByName(DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &name, const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_dconf-global_meta(thread, backend, name, appId, subpath, false, parent); } + static dconfig_dconf-global_meta* createGeneric(const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_dconf-global_meta(thread, nullptr, QStringLiteral(u"\u0064\u0063\u006f\u006e\u0066\u002d\u0067\u006c\u006f\u0062\u0061\u006c\u002e\u006d\u0065\u0074\u0061"), {}, subpath, true, parent); } + static dconfig_dconf-global_meta* create(DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_dconf-global_meta(thread, backend, QStringLiteral(u"\u0064\u0063\u006f\u006e\u0066\u002d\u0067\u006c\u006f\u0062\u0061\u006c\u002e\u006d\u0065\u0074\u0061"), {}, subpath, true, parent); } + static dconfig_dconf-global_meta* createGenericByName(const QString &name, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_dconf-global_meta(thread, nullptr, name, {}, subpath, true, parent); } + static dconfig_dconf-global_meta* createGenericByName(DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &name, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_dconf-global_meta(thread, backend, name, {}, subpath, true, parent); } + ~dconfig_dconf-global_meta() { + int oldStatus = m_data->m_status.fetchAndStoreOrdered(static_cast(Data::Status::Destroyed)); + + m_data->m_userConfig = nullptr; + if (oldStatus == static_cast(Data::Status::Succeeded)) { + // When Succeeded, release config object only + auto config = m_data->m_config.loadRelaxed(); + Q_ASSERT(config); + config->deleteLater(); + // m_data will be deleted by config's destroyed signal + } else if (oldStatus == static_cast(Data::Status::Failed) || + oldStatus == static_cast(Data::Status::Invalid)) { + // When Failed or Invalid, directly clean up Data object + m_data->deleteLater(); } } + Q_INVOKABLE DTK_CORE_NAMESPACE::DConfig *config() const { + return m_data->m_config.loadRelaxed(); + } + Q_INVOKABLE bool isInitializeSucceed() const { + return m_data->m_status.loadRelaxed() == static_cast(Data::Status::Succeeded); + } + Q_INVOKABLE bool isInitializeFailed() const { + return m_data->m_status.loadRelaxed() == static_cast(Data::Status::Failed); + } + Q_INVOKABLE bool isInitializing() const { + return m_data->m_status.loadRelaxed() == static_cast(Data::Status::Initializing); + } + + Q_INVOKABLE QStringList keyList() const { + return { QStringLiteral("key3")}; + } + + Q_INVOKABLE bool isDefaultValue(const QString &key) const { + if (key == QStringLiteral("key3")) + return key3IsDefaultValue(); + return false; + } + QString key3() const { - return p_key3; + return m_data->p_key3; } void setKey3(const QString &value) { - auto oldValue = p_key3; - p_key3 = value; - markPropertySet(0); - if (auto config = m_config.loadRelaxed()) { - QMetaObject::invokeMethod(config, [this, value]() { - m_config.loadRelaxed()->setValue(QStringLiteral("key3"), value); + auto oldValue = m_data->p_key3; + m_data->p_key3 = value; + m_data->markPropertySet(0); + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config, value]() { + config->setValue(QStringLiteral("key3"), value); }); } - if (p_key3 != oldValue) { + if (m_data->p_key3 != oldValue) { Q_EMIT key3Changed(); + Q_EMIT valueChanged(QStringLiteral("key3"), value); + } + } + void resetKey3() { + if (auto config = m_data->m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [config]() { + config->reset(QStringLiteral("key3")); + }); + } + } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QBindable bindableKey3() { + return QBindable(this, "key3"); + } +#endif + Q_INVOKABLE bool key3IsDefaultValue() const { + return !m_data->testPropertySet(0); + } +protected: + bool event(QEvent *e) override { + if (e->type() == QEvent::ThreadChange) { + Q_ASSERT(this->thread() == m_data->thread()); } + return QObject::event(e); } Q_SIGNALS: + void configInitializeFailed(); + void configInitializeSucceed(DTK_CORE_NAMESPACE::DConfig *config); + void valueChanged(const QString &key, const QVariant &value); void key3Changed(); + private: - void initialize(DTK_CORE_NAMESPACE::DConfig *config) { - Q_ASSERT(!m_config.loadRelaxed()); - m_config.storeRelaxed(config); + // Prevent external moveToThread calls + using QObject::moveToThread; + class Data : public QObject { + public: + enum class Status { + Invalid = 0, + Initializing = 1, + Succeeded = 2, + Failed = 3, + Destroyed = 4 + }; + + explicit Data() + : QObject(nullptr) {} + + inline void initializeInConfigThread(DTK_CORE_NAMESPACE::DConfig *config) { + Q_ASSERT(!m_config.loadRelaxed()); + m_config.storeRelaxed(config); if (testPropertySet(0)) { config->setValue(QStringLiteral("key3"), QVariant::fromValue(p_key3)); } else { updateValue(QStringLiteral("key3"), QVariant::fromValue(p_key3)); } + connect(config, &DTK_CORE_NAMESPACE::DConfig::valueChanged, this, [this](const QString &key) { + updateValue(key); + }, Qt::DirectConnection); + } - connect(config, &DTK_CORE_NAMESPACE::DConfig::valueChanged, this, [this](const QString &key) { - updateValue(key); - }, Qt::DirectConnection); - } - void updateValue(const QString &key, const QVariant &fallback = QVariant()) { - Q_ASSERT(QThread::currentThread() == m_config.loadRelaxed()->thread()); - const QVariant &value = m_config.loadRelaxed()->value(key, fallback); - if (key == QStringLiteral("key3")) { - auto newValue = qvariant_cast(value); - QMetaObject::invokeMethod(this, [this, newValue]() { - if (p_key3 != newValue) { - p_key3 = newValue; - Q_EMIT key3Changed(); + inline void updateValue(const QString &key, const QVariant &fallback = QVariant()) { + auto config = m_config.loadRelaxed(); + if (!config) + return; + Q_ASSERT(QThread::currentThread() == config->thread()); + const QVariant &v = config->value(key, fallback); + if (key == QStringLiteral("key3")) { + markPropertySet(0, !config->isDefaultValue(key)); + updateProperty(key, v); + return; + } + } + + inline void updateProperty(const QString &key, const QVariant &v) { + if (key == QStringLiteral("key3")) { + QString nv = qvariant_cast(v); + if (p_key3 != nv) { + p_key3 = nv; + if (m_userConfig) { + QPointer selfData(this); + // Emit signals on main thread (userConfig's thread) + QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() { + Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread()); + if (selfData && selfData->m_userConfig) { + Q_EMIT selfData->m_userConfig->key3Changed(); + Q_EMIT selfData->m_userConfig->valueChanged(key, v); + } + }, Qt::QueuedConnection); + } } - }); - return; + return; + } } - } - inline void markPropertySet(const int index) { - if (index < 32) { - m_propertySetStatus0.fetchAndOrOrdered(1 << (index - 0)); - return; + + inline void markPropertySet(const int index, bool on = true) { + if (index < 32) { + if (on) + m_propertySetStatus0.fetchAndOrOrdered(1 << (index - 0)); + else + m_propertySetStatus0.fetchAndAndOrdered(~(1 << (index - 0))); + return; + } + Q_UNREACHABLE(); } - Q_UNREACHABLE(); - } - inline bool testPropertySet(const int index) const { - if (index < 32) { - return (m_propertySetStatus0.loadRelaxed() & (1 << (index - 0))); + + inline bool testPropertySet(const int index) const { + if (index < 32) { + return (m_propertySetStatus0.loadRelaxed() & (1 << (index - 0))); + } + Q_UNREACHABLE(); } - Q_UNREACHABLE(); - } - QAtomicPointer m_config = nullptr; - QString p_key3 { QStringLiteral("global") }; - QAtomicInteger m_propertySetStatus0 = 0; + + // Member variables + QAtomicPointer m_config = nullptr; + QAtomicInteger m_status = static_cast(Status::Invalid); + QPointer m_userConfig = nullptr; + QAtomicInteger m_propertySetStatus0 = 0; + + // Property storage + // Default value: "global" + QString p_key3 { QStringLiteral(u"\u0067\u006c\u006f\u0062\u0061\u006c") }; + }; + + Data *m_data; }; -#endif // DCONF-GLOBAL_META_H +#endif // DCONFIG_DCONF-GLOBAL_META_H diff --git a/tools/dconfig2cpp/main.cpp b/tools/dconfig2cpp/main.cpp index 85d92a60..1f72e2c6 100644 --- a/tools/dconfig2cpp/main.cpp +++ b/tools/dconfig2cpp/main.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024-2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later @@ -186,7 +186,7 @@ int main(int argc, char *argv[]) { headerStream << "#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)\n" << "#include \n" << "#endif\n"; - + headerStream << "#include \n"; headerStream << "#include \n"; headerStream << "#include \n\n"; headerStream << "class " << className << " : public QObject {\n"; @@ -215,6 +215,7 @@ int main(int argc, char *argv[]) { "isDefaultValue", "m_config", "m_status", + "m_data", }; for (int i = 0; i <= (contents.size()) / 32; ++i) { @@ -281,15 +282,35 @@ int main(int argc, char *argv[]) { << R"((QThread *thread, DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &name, const QString &appId, const QString &subpath, bool isGeneric, QObject *parent) - : QObject(nullptr) { + : QObject(parent), m_data(new Data) { + m_data->m_userConfig = this; + m_data->moveToThread(this->thread()); + if (!thread->isRunning()) { qWarning() << QLatin1String("Warning: The provided thread is not running."); } Q_ASSERT(QThread::currentThread() != thread); auto worker = new QObject(); worker->moveToThread(thread); - QPointer watcher(parent); - QMetaObject::invokeMethod(worker, [=, this]() { + + QPointer safeData(m_data); + + QMetaObject::invokeMethod(worker, [safeData, backend, name, appId, subpath, isGeneric, worker]() mutable { + delete worker; + worker = nullptr; + + // Check if Data object is still valid + if (!safeData) { + return; + } + + if (!safeData->m_status.testAndSetOrdered(static_cast(Data::Status::Invalid), + static_cast(Data::Status::Initializing))) { + // CAS failed, state already changed - userConfig destructor will handle cleanup + // Do not attempt to delete here as it would race with destructor + return; + } + DTK_CORE_NAMESPACE::DConfig *config = nullptr; if (isGeneric) { if (backend) { @@ -314,21 +335,44 @@ int main(int argc, char *argv[]) { } } } - if (!config) { + + if (!config || !config->isValid()) { qWarning() << QLatin1String("Failed to create DConfig instance."); - worker->deleteLater(); + + if (safeData && safeData->m_status.testAndSetOrdered(static_cast(Data::Status::Initializing), + static_cast(Data::Status::Failed))) { + if (safeData->m_userConfig) { + QMetaObject::invokeMethod(safeData, [safeData]() { + if (safeData && safeData->m_userConfig) { + Q_EMIT safeData->m_userConfig->configInitializeFailed(); + } + }, Qt::QueuedConnection); + } + } else { + // CAS failed, destroy data object + safeData->deleteLater(); + } + + if (config) + delete config; + return; } config->moveToThread(QThread::currentThread()); - initializeInConfigThread(config); - if (watcher != parent) { - // delete this if watcher is changed to nullptr. - deleteLater(); - } else if (!this->parent() && parent) { - // !parent() means that parent is not changed. - this->setParent(watcher); + // Initialize through Data class + safeData->initializeInConfigThread(config); + + // Try to transition from Initializing to Succeeded + if (safeData && safeData->m_status.testAndSetOrdered(static_cast(Data::Status::Initializing), + static_cast(Data::Status::Succeeded))) { + // CAS succeeded, connect destroyed signal + QObject::connect(config, &QObject::destroyed, safeData, &QObject::deleteLater); + } else { + // CAS failed - state changed (e.g., set to Destroyed) + // We must clean up the config we just created + config->deleteLater(); + safeData->deleteLater(); } - worker->deleteLater(); }); } )"; @@ -379,26 +423,33 @@ int main(int argc, char *argv[]) { // Destructor headerStream << " ~" << className << R"(() { - if (m_config.loadRelaxed()) { - m_config.loadRelaxed()->deleteLater(); - m_config.storeRelaxed(nullptr); + int oldStatus = m_data->m_status.fetchAndStoreOrdered(static_cast(Data::Status::Destroyed)); + + m_data->m_userConfig = nullptr; + if (oldStatus == static_cast(Data::Status::Succeeded)) { + // When Succeeded, release config object only + auto config = m_data->m_config.loadRelaxed(); + Q_ASSERT(config); + config->deleteLater(); + // m_data will be deleted by config's destroyed signal + } else if (oldStatus == static_cast(Data::Status::Failed) || + oldStatus == static_cast(Data::Status::Invalid)) { + // When Failed or Invalid, directly clean up Data object + m_data->deleteLater(); } } Q_INVOKABLE DTK_CORE_NAMESPACE::DConfig *config() const { - return m_config.loadRelaxed(); + return m_data->m_config.loadRelaxed(); } - Q_INVOKABLE bool isInitializeSucceed() const { - return m_status.loadRelaxed() == static_cast(Status::Succeed); + return m_data->m_status.loadRelaxed() == static_cast(Data::Status::Succeeded); } - Q_INVOKABLE bool isInitializeFailed() const { - return m_status.loadRelaxed() == static_cast(Status::Failed); + return m_data->m_status.loadRelaxed() == static_cast(Data::Status::Failed); } - Q_INVOKABLE bool isInitializing() const { - return m_status.loadRelaxed() == static_cast(Status::Invalid); + return m_data->m_status.loadRelaxed() == static_cast(Data::Status::Initializing); } )"; @@ -424,25 +475,25 @@ int main(int argc, char *argv[]) { assert(!usedKeywords.contains(readFunction)); headerStream << " " << property.typeName << " " << readFunction << "() const {\n" - << " return p_" << property.propertyName << ";\n }\n"; + << " return m_data->p_" << property.propertyName << ";\n }\n"; headerStream << " void set" << property.capitalizedPropertyName << "(const " << property.typeName << " &value) {\n" - << " auto oldValue = p_" << property.propertyName << ";\n" - << " p_" << property.propertyName << " = value;\n" - << " markPropertySet(" << i << ");\n" - << " if (auto config = m_config.loadRelaxed()) {\n" - << " QMetaObject::invokeMethod(config, [this, value]() {\n" - << " m_config.loadRelaxed()->setValue(" << property.propertyNameString << ", value);\n" + << " auto oldValue = m_data->p_" << property.propertyName << ";\n" + << " m_data->p_" << property.propertyName << " = value;\n" + << " m_data->markPropertySet(" << i << ");\n" + << " if (auto config = m_data->m_config.loadRelaxed()) {\n" + << " QMetaObject::invokeMethod(config, [config, value]() {\n" + << " config->setValue(" << property.propertyNameString << ", value);\n" << " });\n" << " }\n" - << " if (p_" << property.propertyName << " != oldValue) {\n" + << " if (m_data->p_" << property.propertyName << " != oldValue) {\n" << " Q_EMIT " << property.propertyName << "Changed();\n" << " Q_EMIT valueChanged(" << property.propertyNameString << ", value);\n" << " }\n" << " }\n" << " void reset" << property.capitalizedPropertyName << "() {\n" - << " if (auto config = m_config.loadRelaxed()) {\n" - << " QMetaObject::invokeMethod(config, [this]() {\n" - << " m_config.loadRelaxed()->reset(" << property.propertyNameString << ");\n" + << " if (auto config = m_data->m_config.loadRelaxed()) {\n" + << " QMetaObject::invokeMethod(config, [config]() {\n" + << " config->reset(" << property.propertyNameString << ");\n" << " });\n" << " }\n" << " }\n"; @@ -453,30 +504,44 @@ int main(int argc, char *argv[]) { headerStream << "#endif\n"; headerStream << " Q_INVOKABLE bool " << property.propertyName << "IsDefaultValue() const {\n" - << " return !testPropertySet(" << i << ");\n" + << " return !m_data->testPropertySet(" << i << ");\n" << " }\n"; } - // Generate signals for property changes - headerStream << "Q_SIGNALS:\n" - << " void configInitializeFailed(DTK_CORE_NAMESPACE::DConfig *config);\n" + headerStream << "protected:\n" + << " bool event(QEvent *e) override {\n" + << " if (e->type() == QEvent::ThreadChange) {\n" + << " Q_ASSERT(this->thread() == m_data->thread());\n" + << " }\n" + << " return QObject::event(e);\n" + << " }\n" + << "Q_SIGNALS:\n" + << " void configInitializeFailed();\n" << " void configInitializeSucceed(DTK_CORE_NAMESPACE::DConfig *config);\n" - << " void valueChanged(const QString &key, const QVariant &value);\n\n"; - for (const Property &property : std::as_const(properties)) { - headerStream << " void " << property.propertyName << "Changed();\n"; - } + << " void valueChanged(const QString &key, const QVariant &value);\n"; + for (const auto &p : properties) + headerStream << " void " << p.propertyName << "Changed();\n"; + + headerStream << "\nprivate:\n" + << " // Prevent external moveToThread calls\n" + << " using QObject::moveToThread;\n" + << " class Data : public QObject {\n" + << " public:\n" + << " enum class Status {\n" + << " Invalid = 0,\n" + << " Initializing = 1,\n" + << " Succeeded = 2,\n" + << " Failed = 3,\n" + << " Destroyed = 4\n" + << " };\n" + << "\n" + << " explicit Data()\n" + << " : QObject(nullptr) {}\n" + << "\n" + << " inline void initializeInConfigThread(DTK_CORE_NAMESPACE::DConfig *config) {\n" + << " Q_ASSERT(!m_config.loadRelaxed());\n" + << " m_config.storeRelaxed(config);\n"; - // Generate private methods and members - headerStream << "private:\n"; - - headerStream << " void initializeInConfigThread(DTK_CORE_NAMESPACE::DConfig *config) {\n" - << " Q_ASSERT(!m_config.loadRelaxed());\n" - << " m_config.storeRelaxed(config);\n" - << " if (!config->isValid()) {\n" - << " m_status.storeRelaxed(static_cast(Status::Failed));\n" - << " Q_EMIT configInitializeFailed(config);\n" - << " return;\n" - << " }\n\n"; for (int i = 0; i < properties.size(); ++i) { const Property &property = properties[i]; headerStream << " if (testPropertySet(" << i << ")) {\n"; @@ -485,97 +550,96 @@ int main(int argc, char *argv[]) { headerStream << " updateValue(" << property.propertyNameString << ", QVariant::fromValue(p_" << property.propertyName << "));\n"; headerStream << " }\n"; } - headerStream << R"( - if (!m_config.loadRelaxed()) - return; - connect(config, &DTK_CORE_NAMESPACE::DConfig::valueChanged, this, [this](const QString &key) { - updateValue(key); - }, Qt::DirectConnection); - - m_status.storeRelaxed(static_cast(Status::Succeed)); - Q_EMIT configInitializeSucceed(config); - } - void updateValue(const QString &key, const QVariant &fallback = QVariant()) { - if (!m_config.loadRelaxed()) - return; - Q_ASSERT(QThread::currentThread() == m_config.loadRelaxed()->thread()); - const QVariant &value = m_config.loadRelaxed()->value(key, fallback); + + headerStream << " connect(config, &DTK_CORE_NAMESPACE::DConfig::valueChanged, this, [this](const QString &key) {\n" + << " updateValue(key);\n" + << " }, Qt::DirectConnection);\n" + << R"( } + + inline void updateValue(const QString &key, const QVariant &fallback = QVariant()) { + auto config = m_config.loadRelaxed(); + if (!config) + return; + Q_ASSERT(QThread::currentThread() == config->thread()); + const QVariant &v = config->value(key, fallback); )"; for (int i = 0; i < properties.size(); ++i) { - const Property &property = properties.at(i); - headerStream << " if (key == " << property.propertyNameString << ") {\n"; - - headerStream << " markPropertySet(" << i << ", !m_config.loadRelaxed()->isDefaultValue(key));\n"; - - headerStream << " auto newValue = qvariant_cast<" << property.typeName << ">(value);\n" - << " QMetaObject::invokeMethod(this, [this, newValue, key, value]() {\n" - << " Q_ASSERT(QThread::currentThread() == this->thread());\n" - << " if (p_" << property.propertyName << " != newValue) {\n" - << " p_" << property.propertyName << " = newValue;\n" - << " Q_EMIT " << property.propertyName << "Changed();\n" - << " Q_EMIT valueChanged(key, value);\n" + const auto &p = properties[i]; + headerStream << " if (key == " << p.propertyNameString << ") {\n" + << " markPropertySet(" << i << ", !config->isDefaultValue(key));\n" + << " updateProperty(key, v);\n" + << " return;\n" + << " }\n"; + } + headerStream << " }\n\n" + << " inline void updateProperty(const QString &key, const QVariant &v) {\n"; + for (const auto &p : properties) { + headerStream << " if (key == " << p.propertyNameString << ") {\n" + << " " << p.typeName << " nv = qvariant_cast<" << p.typeName << ">(v);\n" + << " if (p_" << p.propertyName << " != nv) {\n" + << " p_" << p.propertyName << " = nv;\n" + << " if (m_userConfig) {\n" + << " QPointer selfData(this);\n" + << " // Emit signals on main thread (userConfig's thread)\n" + << " QMetaObject::invokeMethod(m_userConfig, [selfData, key, v]() {\n" + << " Q_ASSERT(QThread::currentThread() == selfData->m_userConfig->thread());\n" + << " if (selfData && selfData->m_userConfig) {\n" + << " Q_EMIT selfData->m_userConfig->" << p.propertyName << "Changed();\n" + << " Q_EMIT selfData->m_userConfig->valueChanged(key, v);\n" + << " }\n" + << " }, Qt::QueuedConnection);\n" + << " }\n" << " }\n" - << " });\n" - << " return;\n" - << " }\n"; + << " return;\n" + << " }\n"; } - headerStream << " }\n"; + headerStream << " }\n" + << "\n inline void markPropertySet(const int index, bool on = true) {\n"; - // Mark property as set - headerStream << " inline void markPropertySet(const int index, bool on = true) {\n"; for (int i = 0; i <= (properties.size()) / 32; ++i) { - headerStream << " if (index < " << (i + 1) * 32 << ") {\n" - << " if (on)\n" - << " m_propertySetStatus" << QString::number(i) << ".fetchAndOrOrdered(1 << (index - " << i * 32 << "));\n" - << " else\n" - << " m_propertySetStatus" << QString::number(i) << ".fetchAndAndOrdered(~(1 << (index - " << i * 32 << ")));\n" - << " return;\n" - << " }\n"; + headerStream << " if (index < " << (i + 1) * 32 << ") {\n" + << " if (on)\n" + << " m_propertySetStatus" << i << ".fetchAndOrOrdered(1 << (index - " << i * 32 << "));\n" + << " else\n" + << " m_propertySetStatus" << i << ".fetchAndAndOrdered(~(1 << (index - " << i * 32 << ")));\n" + << " return;\n" + << " }\n"; } - headerStream << " Q_UNREACHABLE();\n }\n"; + headerStream << " Q_UNREACHABLE();\n" + << " }\n" + << "\n inline bool testPropertySet(const int index) const {\n"; - // Test if property is set - headerStream << " inline bool testPropertySet(const int index) const {\n"; for (int i = 0; i <= (properties.size()) / 32; ++i) { - headerStream << " if (index < " << (i + 1) * 32 << ") {\n"; - headerStream << " return (m_propertySetStatus" << QString::number(i) << ".loadRelaxed() & (1 << (index - " << i * 32 << ")));\n"; - headerStream << " }\n"; + headerStream << " if (index < " << (i + 1) * 32 << ") {\n" + << " return (m_propertySetStatus" << i << ".loadRelaxed() & (1 << (index - " << i * 32 << ")));\n" + << " }\n"; } - headerStream << " Q_UNREACHABLE();\n" - << " }\n"; - - // Member variables - headerStream << R"( - QAtomicPointer m_config = nullptr; - -public: - enum class Status { - Invalid = 0, - Succeed = 1, - Failed = 2 - }; -private: - QAtomicInteger m_status = static_cast(Status::Invalid); + headerStream << " Q_UNREACHABLE();\n" + << " }\n" + << "\n" + << " // Member variables\n" + << " QAtomicPointer m_config = nullptr;\n" + << " QAtomicInteger m_status = static_cast(Status::Invalid);\n" + << " QPointer<" << className << "> m_userConfig = nullptr;\n"; -)"; + for (int i = 0; i <= (properties.size()) / 32; ++i) { + headerStream << " QAtomicInteger m_propertySetStatus" << i << " = 0;\n"; + } - // Property variables - for (const Property &property : std::as_const(properties)) { - if (property.typeName == QLatin1String("int") || property.typeName == QLatin1String("qint64")) { - headerStream << " // Note: If you expect a double type, use XXX.0\n"; - } else if (property.typeName == QLatin1String("QString")) { - headerStream << " // Default value: \"" << property.defaultValue.toString().replace("\n", "\\n").replace("\r", "\\r") << "\"\n"; + headerStream << "\n // Property storage\n"; + for (const Property &property : properties) { + if (property.typeName == QLatin1String("QString")) { + headerStream << " // Default value: \"" + << property.defaultValue.toString().replace("\n", "\\n").replace("\r", "\\r") << "\"\n"; } - headerStream << " " << property.typeName << " p_" << property.propertyName << " { "; - headerStream << jsonValueToCppCode(property.defaultValue) << " };\n"; + headerStream << " " << property.typeName << " p_" << property.propertyName << " { " + << jsonValueToCppCode(property.defaultValue) << " };\n"; } - // Property set status variables - for (int i = 0; i <= (properties.size()) / 32; ++i) { - headerStream << " QAtomicInteger m_propertySetStatus" << QString::number(i) << " = 0;\n"; - } - headerStream << "};\n\n"; - headerStream << "#endif // " << className.toUpper() << "_H\n"; + headerStream << " };\n\n" + << " Data *m_data;\n" + << "};\n\n" + << "#endif // " << className.toUpper() << "_H\n"; return 0; }