New upstream version 5.7.11
authorBoyuan Yang <byang@debian.org>
Sun, 2 Mar 2025 18:28:14 +0000 (13:28 -0500)
committerBoyuan Yang <byang@debian.org>
Sun, 2 Mar 2025 18:28:14 +0000 (13:28 -0500)
14 files changed:
cmake/DtkTools/DtkDConfigMacros.cmake [new file with mode: 0644]
cmake/DtkTools/DtkToolsConfig.cmake.in
debian/changelog
docs/global/dconfig.zh_CN.dox
dtkcore.cmake
include/global/dconfig.h
include/global/dsysinfo.h
src/dconfig.cpp
src/log/LogManager.cpp
src/log/dconfig_org_deepin_dtk_preference.hpp [new file with mode: 0644]
src/log/log.cmake
tools/CMakeLists.txt
tools/dconfig2cpp/CMakeLists.txt [new file with mode: 0644]
tools/dconfig2cpp/main.cpp [new file with mode: 0644]

diff --git a/cmake/DtkTools/DtkDConfigMacros.cmake b/cmake/DtkTools/DtkDConfigMacros.cmake
new file mode 100644 (file)
index 0000000..f6a385c
--- /dev/null
@@ -0,0 +1,59 @@
+# SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
+# SPDX-License-Identifier: LGPL-3.0-or-later
+
+include(MacroAddFileDependencies)
+include(CMakeParseArguments)
+
+# Define the helper function
+function(dtk_add_config_to_cpp OUTPUT_VAR JSON_FILE)
+    if(NOT EXISTS ${JSON_FILE})
+        message(FATAL_ERROR "JSON file ${JSON_FILE} does not exist.")
+    endif()
+
+    cmake_parse_arguments(
+        "arg"
+        ""
+        "OUTPUT_FILE_NAME;CLASS_NAME;USE_QPROPERTY"
+        ""
+        ${ARGN}
+    )
+
+    # Generate the output header file name
+    get_filename_component(FILE_NAME_WLE ${JSON_FILE} NAME_WLE)
+    if(DEFINED arg_OUTPUT_FILE_NAME)
+        set(OUTPUT_HEADER "${CMAKE_CURRENT_BINARY_DIR}/${arg_OUTPUT_FILE_NAME}")
+    else()
+        set(OUTPUT_HEADER "${CMAKE_CURRENT_BINARY_DIR}/dconfig_${FILE_NAME_WLE}.hpp")
+    endif()
+
+    # Check if CLASS_NAME is set
+    if(DEFINED arg_CLASS_NAME)
+        set(CLASS_NAME_ARG -c ${arg_CLASS_NAME})
+    else()
+        set(CLASS_NAME_ARG "")
+    endif()
+
+    if(NOT DEFINED arg_USE_QPROPERTY)
+        if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
+            set(arg_USE_QPROPERTY ON)
+        endif()
+    endif()
+
+    if (arg_USE_QPROPERTY)
+        set(USE_QPROPERTY_ARG "--use-qproperty")
+    else()
+        set(USE_QPROPERTY_ARG "")
+    endif()
+
+    # Add a custom command to run dconfig2cpp
+    add_custom_command(
+        OUTPUT ${OUTPUT_HEADER}
+        COMMAND ${DTK_DCONFIG2CPP} -o ${OUTPUT_HEADER} ${CLASS_NAME_ARG} ${USE_QPROPERTY_ARG} ${JSON_FILE}
+        DEPENDS ${JSON_FILE} ${DTK_XML2CPP}
+        COMMENT "Generating ${OUTPUT_HEADER} from ${JSON_FILE}"
+        VERBATIM
+    )
+
+    # Add the generated header to the specified output variable
+    set(${OUTPUT_VAR} ${${OUTPUT_VAR}} ${OUTPUT_HEADER} PARENT_SCOPE)
+endfunction()
index b3a14f179c44b74e42f3f866f4cfe46a96b6e0fe..074003b53aa34c6370496e8c2092ead40efbc9eb 100644 (file)
@@ -10,5 +10,7 @@ endif ()
 include("${CMAKE_CURRENT_LIST_DIR}/Dtk@DTK_VERSION_MAJOR@SettingsToolsMacros.cmake")
 include("${CMAKE_CURRENT_LIST_DIR}/Dtk@DTK_VERSION_MAJOR@ToolsTargets.cmake")
 include("${CMAKE_CURRENT_LIST_DIR}/DtkDBusMacros.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/DtkDConfigMacros.cmake")
 
 get_target_property(DTK_XML2CPP Dtk@DTK_VERSION_MAJOR@::Xml2Cpp LOCATION)
+get_target_property(DTK_DCONFIG2CPP Dtk@DTK_VERSION_MAJOR@::DConfig2Cpp LOCATION)
index 3861173c32adb94ee99fece8f45f1b953c43fa77..29ada8171386ebc38e14d2ebdaac1c3fa93c6f1d 100644 (file)
@@ -1,3 +1,18 @@
+dtkcore (5.7.11) unstable; urgency=medium
+
+  * feat: dconfig2cpp generated codes supports qml
+  * feat: export ProductType for QML
+  * feat: async get dconfig value for DLog
+  * feat: support auto generate the Qt code from a dconfig's json
+
+ -- YeShanShan <yeshanshan@uniontech.com>  Thu, 27 Feb 2025 20:47:22 +0800
+
+dtkcore (5.7.10) unstable; urgency=medium
+
+  * Release 5.7.10
+
+ -- YeShanShan <yeshanshan@uniontech.com>  Thu, 13 Feb 2025 17:18:04 +0800
+
 dtkcore (5.7.9) unstable; urgency=medium
 
   [ root ]
index 853d3d0bde4dc9a11c2696103ced160418b14935..a37c82a079647fcb6ab4845ed3cb4bac1c1ff4f7 100644 (file)
@@ -349,6 +349,11 @@ sudo make install
 @param[in] appId 配置文件所属的应用Id
 @note 需要在QCoreApplication构造前设置。
 
+@fn static void DConfig::globalThread()
+@brief 一个服务于 DConfig 的公用线程,一般用于 dconfig2cpp 生成的代码,此线程在构造时会自动调用 QThread::start 以满足 dconfig2cpp 的需求。
+@return 此线程默认为 running 状态
+@note 请不要析构它,它会在应用程序退出时释放
+
 @fn QString Dtk::Core::DConfig::backendName()
 @brief 配置策略后端名称
 @return 配置策略后端名称
index 95c98d41701e82af08b3dd3a83ca0f9bce35d1dc..df7f7cfc6059bb39e2d48182badfb54985a35a43 100644 (file)
@@ -99,6 +99,9 @@ install(FILES cmake/DtkTools/DtkSettingsToolsMacros.cmake
 install(FILES cmake/DtkTools/DtkDBusMacros.cmake
         DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Dtk${DTK_VERSION_MAJOR}Tools")
 
+install(FILES ${CMAKE_SOURCE_DIR}/cmake/DtkTools/DtkDConfigMacros.cmake
+        DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Dtk${DTK_VERSION_MAJOR}Tools")
+
 if (NOT DTK_VERSION_MAJOR)
     set(DCONFIG_DEPRECATED_FUNCS [=[
 # deprecated since dtk6
index 8759a4891caab78b524a54152133c6cde90cdf1d..c4c633b0a5fce187d726782d1de521c7af19a278 100644 (file)
@@ -31,7 +31,7 @@ class LIBDTKCORESHARED_EXPORT DConfig : public QObject, public DObject
     Q_OBJECT
     D_DECLARE_PRIVATE(DConfig)
 
-    Q_PROPERTY(QStringList keyList READ keyList FINAL)
+    Q_PROPERTY(QStringList keyList READ keyList CONSTANT FINAL)
 
 public:
     explicit DConfig(const QString &name, const QString &subpath = QString(),
@@ -50,6 +50,7 @@ public:
                                   QObject *parent = nullptr);
 
     static void setAppId(const QString &appId);
+    static QThread *globalThread();
 
     QString backendName() const;
 
index 89eacf9e83568dae8f88152255f4688d1a7acd18..fe5b35b8bcf956352bff4aee64bb31eeb8f7a1a7 100644 (file)
@@ -33,6 +33,8 @@ public:
         NixOS
     };
 
+    Q_ENUM(ProductType)
+
     enum DeepinType {
         UnknownDeepin = 0,
         DeepinDesktop,
index ef7325af87aa2d25d7de570add63377957181110..2d57ee5df26d1b08f23ea949c9be14b51c9cbdf7 100644 (file)
@@ -87,6 +87,7 @@ static QString NoAppId;
     @fn QString DConfigBackend::name() const = 0
 
     @brief The unique identity of the backend configuration
+ */
 
 /*!
 @~english
@@ -669,6 +670,29 @@ void DConfig::setAppId(const QString &appId)
     qCDebug(cfLog, "Explicitly specify application Id as appId=%s for config.", qPrintable(appId));
 }
 
+class DConfigThread : public QThread
+{
+public:
+    DConfigThread() {
+        setObjectName("DConfigGlobalThread");
+        start();
+    }
+
+    ~DConfigThread() override {
+        if (isRunning()) {
+            quit();
+            wait();
+        }
+    }
+};
+
+Q_GLOBAL_STATIC(DConfigThread, _globalThread)
+
+QThread *DConfig::globalThread()
+{
+    return _globalThread;
+}
+
 /*!
 @~english
  * @brief Use custom configuration policy backend to construct objects
index 5ff0c194323eae83972b53e7dfa2547af7ee2f1e..4af93469414b51aa93a1cc23ed174975723e5024 100644 (file)
@@ -4,13 +4,14 @@
 
 #include <QtCore>
 #include "LogManager.h"
-#include "dconfig.h"
 #include <DSGApplication>
 #include <Logger.h>
 #include <ConsoleAppender.h>
 #include <RollingFileAppender.h>
 #include <JournalAppender.h>
+
 #include "dstandardpaths.h"
+#include "dconfig_org_deepin_dtk_preference.hpp"
 
 DCORE_BEGIN_NAMESPACE
 
@@ -40,7 +41,7 @@ public:
     {
     }
 
-    DConfig *createDConfig(const QString &appId);
+    dconfig_org_deepin_dtk_preference *createDConfig(const QString &appId);
     void initLoggingRules();
     void updateLoggingRules();
 
@@ -49,33 +50,22 @@ public:
     ConsoleAppender* m_consoleAppender = nullptr;
     RollingFileAppender* m_rollingFileAppender = nullptr;
     JournalAppender* m_journalAppender = nullptr;
-    QScopedPointer<DConfig> m_dsgConfig;
-    QScopedPointer<DConfig> m_fallbackConfig;
+    QScopedPointer<dconfig_org_deepin_dtk_preference> m_dsgConfig;
+    QScopedPointer<dconfig_org_deepin_dtk_preference> m_fallbackConfig;
 
     DLogManager *q_ptr = nullptr;
     Q_DECLARE_PUBLIC(DLogManager)
 
 };
 
-DConfig *DLogManagerPrivate::createDConfig(const QString &appId)
+dconfig_org_deepin_dtk_preference *DLogManagerPrivate::createDConfig(const QString &appId)
 {
     if (appId.isEmpty())
         return nullptr;
 
-    DConfig *config = DConfig::create(appId, "org.deepin.dtk.preference");
-    if (!config->isValid()) {
-        qWarning() << "Logging rules config is invalid, please check `appId` [" << appId << "]arg is correct";
-        delete config;
-        config = nullptr;
-        return nullptr;
-    }
-
-    QObject::connect(config, &DConfig::valueChanged, config, [this](const QString &key) {
-        if (key != RULES_KEY)
-            return;
-
-        updateLoggingRules();
-    });
+    auto config = dconfig_org_deepin_dtk_preference::create(appId);
+    QObject::connect(config, &dconfig_org_deepin_dtk_preference::rulesChanged,
+                     config, [this](){ updateLoggingRules(); });
 
     return config;
 }
@@ -89,25 +79,43 @@ void DLogManagerPrivate::initLoggingRules()
     QString dsgAppId = DSGApplication::id();
     m_dsgConfig.reset(createDConfig(dsgAppId));
 
+    if (m_dsgConfig) {
+        QObject::connect(m_dsgConfig.data(), &dconfig_org_deepin_dtk_preference::configInitializeSucceed,
+                         m_dsgConfig.data(), [this](){ updateLoggingRules(); });
+        QObject::connect(m_dsgConfig.data(), &dconfig_org_deepin_dtk_preference::configInitializeFailed,
+                         m_dsgConfig.data(), [this, dsgAppId] {
+                             m_dsgConfig.reset();
+                             qWarning() << "Logging rules config is invalid, please check `appId` [" << dsgAppId << "]arg is correct";
+                         });
+    }
+
     QString fallbackId = qgetenv("DTK_LOGGING_FALLBACK_APPID");
     // 2. fallbackId 和 dsgAppId 非空且不等时,都创建和监听变化
     if (!fallbackId.isEmpty() && fallbackId != dsgAppId)
         m_fallbackConfig.reset(createDConfig(fallbackId));
 
-    // 3. 默认值和非默认值时,非默认值优先
-    updateLoggingRules();
+            // 3. 默认值和非默认值时,非默认值优先
+    if (m_fallbackConfig) {
+        QObject::connect(m_fallbackConfig.data(), &dconfig_org_deepin_dtk_preference::configInitializeSucceed,
+                         m_fallbackConfig.data(), [this](){ updateLoggingRules(); });
+        QObject::connect(m_fallbackConfig.data(), &dconfig_org_deepin_dtk_preference::configInitializeFailed,
+                         m_fallbackConfig.data(), [this, fallbackId] {
+                             m_fallbackConfig.reset();
+                             qWarning() << "Logging rules config is invalid, please check `appId` [" << fallbackId << "]arg is correct";
+                         });
+    }
 }
 
 void DLogManagerPrivate::updateLoggingRules()
 {
     QVariant var;
     // 4. 优先看 dsgConfig 是否默认值,其次 fallback 是否默认值
-    if (m_dsgConfig && !m_dsgConfig->isDefaultValue(RULES_KEY)) {
-        var = m_dsgConfig->value(RULES_KEY);
-    } else if (m_fallbackConfig && !m_fallbackConfig->isDefaultValue(RULES_KEY)) {
-        var = m_fallbackConfig->value(RULES_KEY);
-    } else if (m_dsgConfig) {
-        var = m_dsgConfig->value(RULES_KEY);
+    if (m_dsgConfig && m_dsgConfig->isInitializeSucceed() && !m_dsgConfig->rulesIsDefaultValue()) {
+        var = m_dsgConfig->rules();
+    } else if (m_fallbackConfig && m_dsgConfig->isInitializeSucceed() && !m_fallbackConfig->rulesIsDefaultValue()) {
+        var = m_fallbackConfig->rules();
+    } else if (m_dsgConfig && m_dsgConfig->isInitializeSucceed()) {
+        var = m_dsgConfig->rules();
     }
 
     if (var.isValid())
diff --git a/src/log/dconfig_org_deepin_dtk_preference.hpp b/src/log/dconfig_org_deepin_dtk_preference.hpp
new file mode 100644 (file)
index 0000000..a300e9f
--- /dev/null
@@ -0,0 +1,515 @@
+/**
+ * This file is generated by dconfig2cpp.
+ * Command line arguments: /home/zccrs/projects/dtkcore/build/unknown-Debug/tools/dconfig2cpp/dconfig2cpp /home/zccrs/projects/dtkcommon/configs/org.deepin.dtk.preference.json
+ * Generation time: 2025-02-21T13:49:21
+ * 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 DCONFIG_ORG_DEEPIN_DTK_PREFERENCE_H
+#define DCONFIG_ORG_DEEPIN_DTK_PREFERENCE_H
+
+#include <QThread>
+#include <QVariant>
+#include <QDebug>
+#include <QAtomicPointer>
+#include <QAtomicInteger>
+#include <DConfig>
+
+class dconfig_org_deepin_dtk_preference : public QObject {
+    Q_OBJECT
+
+    Q_PROPERTY(bool autoDisplayFeature READ autoDisplayFeature WRITE setAutoDisplayFeature NOTIFY autoDisplayFeatureChanged RESET resetAutoDisplayFeature)
+    Q_PROPERTY(bool featureUpdated READ featureUpdated WRITE setFeatureUpdated NOTIFY featureUpdatedChanged RESET resetFeatureUpdated)
+    Q_PROPERTY(bool keyboardsearchDisabled READ keyboardsearchDisabled WRITE setKeyboardsearchDisabled NOTIFY keyboardsearchDisabledChanged RESET resetKeyboardsearchDisabled)
+    Q_PROPERTY(QString rules READ rules WRITE setRules NOTIFY rulesChanged RESET resetRules)
+    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;featureUpdated;keyboardsearchDisabled;rules;themeType;titlebarHeight;underlineShortcut")
+    Q_CLASSINFO("DConfigFileName", "org.deepin.dtk.preference")
+    Q_CLASSINFO("DConfigFileVersion", "1.0")
+
+public:
+    explicit dconfig_org_deepin_dtk_preference(QThread *thread, DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &name, const QString &appId, const QString &subpath, QObject *parent)
+                : QObject(parent) {
+        if (!thread->isRunning()) {
+            qWarning() << QLatin1String("Warning: The provided thread is not running.");
+        }
+        Q_ASSERT(QThread::currentThread() != thread);
+        auto worker = new QObject();
+        worker->moveToThread(thread);
+        QMetaObject::invokeMethod(worker, [=, this]() {
+            DTK_CORE_NAMESPACE::DConfig *config = nullptr;
+            if (backend) {
+                if (appId.isNull()) {
+                    config = DTK_CORE_NAMESPACE::DConfig::create(backend, 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(name, subpath, nullptr);
+                } else {
+                    config = DTK_CORE_NAMESPACE::DConfig::create(appId, name, subpath, nullptr);
+                }
+            }
+            if (!config) {
+                qWarning() << QLatin1String("Failed to create DConfig instance.");
+                worker->deleteLater();
+                return;
+            }
+            config->moveToThread(QThread::currentThread());
+            initializeInConfigThread(config);
+            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())
+    { return new dconfig_org_deepin_dtk_preference(thread, nullptr, QStringLiteral(u"\u006f\u0072\u0067\u002e\u0064\u0065\u0065\u0070\u0069\u006e\u002e\u0064\u0074\u006b\u002e\u0070\u0072\u0065\u0066\u0065\u0072\u0065\u006e\u0063\u0065"), appId, subpath, parent); }
+    static dconfig_org_deepin_dtk_preference* 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_org_deepin_dtk_preference(thread, backend, QStringLiteral(u"\u006f\u0072\u0067\u002e\u0064\u0065\u0065\u0070\u0069\u006e\u002e\u0064\u0074\u006b\u002e\u0070\u0072\u0065\u0066\u0065\u0072\u0065\u006e\u0063\u0065"), appId, subpath, parent); }
+    static dconfig_org_deepin_dtk_preference* createByName(const QString &name, const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread())
+    { return new dconfig_org_deepin_dtk_preference(thread, nullptr, name, appId, subpath, parent); }
+    static dconfig_org_deepin_dtk_preference* 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_org_deepin_dtk_preference(thread, backend, name, appId, subpath, parent); }
+    ~dconfig_org_deepin_dtk_preference() {
+        if (m_config.loadRelaxed()) {
+            m_config.loadRelaxed()->deleteLater();
+        }
+    }
+
+    Q_INVOKABLE DTK_CORE_NAMESPACE::DConfig *config() const {
+        return m_config.loadRelaxed();
+    }
+
+    Q_INVOKABLE bool isInitializeSucceed() const {
+        return m_status.loadRelaxed() == static_cast<int>(Status::Succeed);
+    }
+
+    Q_INVOKABLE bool isInitializeFailed() const {
+        return m_status.loadRelaxed() == static_cast<int>(Status::Failed);
+    }
+
+    Q_INVOKABLE bool isInitializing() const {
+        return m_status.loadRelaxed() == static_cast<int>(Status::Invalid);
+    }
+
+    Q_INVOKABLE QStringList keyList() const {
+        return { QStringLiteral(u"\u0061\u0075\u0074\u006f\u0044\u0069\u0073\u0070\u006c\u0061\u0079\u0046\u0065\u0061\u0074\u0075\u0072\u0065"),
+                 QStringLiteral(u"\u0066\u0065\u0061\u0074\u0075\u0072\u0065\u0055\u0070\u0064\u0061\u0074\u0065\u0064"),
+                 QStringLiteral(u"\u006b\u0065\u0079\u0062\u006f\u0061\u0072\u0064\u0073\u0065\u0061\u0072\u0063\u0068\u0044\u0069\u0073\u0061\u0062\u006c\u0065\u0064"),
+                 QStringLiteral(u"\u0072\u0075\u006c\u0065\u0073"),
+                 QStringLiteral(u"\u0074\u0068\u0065\u006d\u0065\u0054\u0079\u0070\u0065"),
+                 QStringLiteral(u"\u0074\u0069\u0074\u006c\u0065\u0062\u0061\u0072\u0048\u0065\u0069\u0067\u0068\u0074"),
+                 QStringLiteral(u"\u0075\u006e\u0064\u0065\u0072\u006c\u0069\u006e\u0065\u0053\u0068\u006f\u0072\u0074\u0063\u0075\u0074")};
+    }
+
+    Q_INVOKABLE bool isDefaultValue(const QString &key) const {
+        if (key == QStringLiteral(u"\u0061\u0075\u0074\u006f\u0044\u0069\u0073\u0070\u006c\u0061\u0079\u0046\u0065\u0061\u0074\u0075\u0072\u0065"))
+            return autoDisplayFeatureIsDefaultValue();
+        if (key == QStringLiteral(u"\u0066\u0065\u0061\u0074\u0075\u0072\u0065\u0055\u0070\u0064\u0061\u0074\u0065\u0064"))
+            return featureUpdatedIsDefaultValue();
+        if (key == QStringLiteral(u"\u006b\u0065\u0079\u0062\u006f\u0061\u0072\u0064\u0073\u0065\u0061\u0072\u0063\u0068\u0044\u0069\u0073\u0061\u0062\u006c\u0065\u0064"))
+            return keyboardsearchDisabledIsDefaultValue();
+        if (key == QStringLiteral(u"\u0072\u0075\u006c\u0065\u0073"))
+            return rulesIsDefaultValue();
+        if (key == QStringLiteral(u"\u0074\u0068\u0065\u006d\u0065\u0054\u0079\u0070\u0065"))
+            return themeTypeIsDefaultValue();
+        if (key == QStringLiteral(u"\u0074\u0069\u0074\u006c\u0065\u0062\u0061\u0072\u0048\u0065\u0069\u0067\u0068\u0074"))
+            return titlebarHeightIsDefaultValue();
+        if (key == QStringLiteral(u"\u0075\u006e\u0064\u0065\u0072\u006c\u0069\u006e\u0065\u0053\u0068\u006f\u0072\u0074\u0063\u0075\u0074"))
+            return underlineShortcutIsDefaultValue();
+        return false;
+    }
+
+    bool autoDisplayFeature() const {
+        return 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(u"\u0061\u0075\u0074\u006f\u0044\u0069\u0073\u0070\u006c\u0061\u0079\u0046\u0065\u0061\u0074\u0075\u0072\u0065"), value);
+            });
+        }
+        if (p_autoDisplayFeature != oldValue) {
+            Q_EMIT autoDisplayFeatureChanged();
+            Q_EMIT valueChanged(QStringLiteral(u"\u0061\u0075\u0074\u006f\u0044\u0069\u0073\u0070\u006c\u0061\u0079\u0046\u0065\u0061\u0074\u0075\u0072\u0065"), value);
+        }
+    }
+    void resetAutoDisplayFeature() {
+        if (auto config = m_config.loadRelaxed()) {
+            QMetaObject::invokeMethod(config, [this]() {
+                m_config.loadRelaxed()->reset(QStringLiteral(u"\u0061\u0075\u0074\u006f\u0044\u0069\u0073\u0070\u006c\u0061\u0079\u0046\u0065\u0061\u0074\u0075\u0072\u0065"));
+            });
+        }
+    }
+    Q_INVOKABLE bool autoDisplayFeatureIsDefaultValue() const {
+        return !testPropertySet(0);
+    }
+    bool featureUpdated() const {
+        return p_featureUpdated;
+    }
+    void setFeatureUpdated(const bool &value) {
+        auto oldValue = p_featureUpdated;
+        p_featureUpdated = value;
+        markPropertySet(1);
+        if (auto config = m_config.loadRelaxed()) {
+            QMetaObject::invokeMethod(config, [this, value]() {
+                m_config.loadRelaxed()->setValue(QStringLiteral(u"\u0066\u0065\u0061\u0074\u0075\u0072\u0065\u0055\u0070\u0064\u0061\u0074\u0065\u0064"), value);
+            });
+        }
+        if (p_featureUpdated != oldValue) {
+            Q_EMIT featureUpdatedChanged();
+            Q_EMIT valueChanged(QStringLiteral(u"\u0066\u0065\u0061\u0074\u0075\u0072\u0065\u0055\u0070\u0064\u0061\u0074\u0065\u0064"), value);
+        }
+    }
+    void resetFeatureUpdated() {
+        if (auto config = m_config.loadRelaxed()) {
+            QMetaObject::invokeMethod(config, [this]() {
+                m_config.loadRelaxed()->reset(QStringLiteral(u"\u0066\u0065\u0061\u0074\u0075\u0072\u0065\u0055\u0070\u0064\u0061\u0074\u0065\u0064"));
+            });
+        }
+    }
+    Q_INVOKABLE bool featureUpdatedIsDefaultValue() const {
+        return !testPropertySet(1);
+    }
+    bool keyboardsearchDisabled() const {
+        return p_keyboardsearchDisabled;
+    }
+    void setKeyboardsearchDisabled(const bool &value) {
+        auto oldValue = p_keyboardsearchDisabled;
+        p_keyboardsearchDisabled = value;
+        markPropertySet(2);
+        if (auto config = m_config.loadRelaxed()) {
+            QMetaObject::invokeMethod(config, [this, value]() {
+                m_config.loadRelaxed()->setValue(QStringLiteral(u"\u006b\u0065\u0079\u0062\u006f\u0061\u0072\u0064\u0073\u0065\u0061\u0072\u0063\u0068\u0044\u0069\u0073\u0061\u0062\u006c\u0065\u0064"), value);
+            });
+        }
+        if (p_keyboardsearchDisabled != oldValue) {
+            Q_EMIT keyboardsearchDisabledChanged();
+            Q_EMIT valueChanged(QStringLiteral(u"\u006b\u0065\u0079\u0062\u006f\u0061\u0072\u0064\u0073\u0065\u0061\u0072\u0063\u0068\u0044\u0069\u0073\u0061\u0062\u006c\u0065\u0064"), value);
+        }
+    }
+    void resetKeyboardsearchDisabled() {
+        if (auto config = m_config.loadRelaxed()) {
+            QMetaObject::invokeMethod(config, [this]() {
+                m_config.loadRelaxed()->reset(QStringLiteral(u"\u006b\u0065\u0079\u0062\u006f\u0061\u0072\u0064\u0073\u0065\u0061\u0072\u0063\u0068\u0044\u0069\u0073\u0061\u0062\u006c\u0065\u0064"));
+            });
+        }
+    }
+    Q_INVOKABLE bool keyboardsearchDisabledIsDefaultValue() const {
+        return !testPropertySet(2);
+    }
+    QString rules() const {
+        return p_rules;
+    }
+    void setRules(const QString &value) {
+        auto oldValue = p_rules;
+        p_rules = value;
+        markPropertySet(3);
+        if (auto config = m_config.loadRelaxed()) {
+            QMetaObject::invokeMethod(config, [this, value]() {
+                m_config.loadRelaxed()->setValue(QStringLiteral(u"\u0072\u0075\u006c\u0065\u0073"), value);
+            });
+        }
+        if (p_rules != oldValue) {
+            Q_EMIT rulesChanged();
+            Q_EMIT valueChanged(QStringLiteral(u"\u0072\u0075\u006c\u0065\u0073"), value);
+        }
+    }
+    void resetRules() {
+        if (auto config = m_config.loadRelaxed()) {
+            QMetaObject::invokeMethod(config, [this]() {
+                m_config.loadRelaxed()->reset(QStringLiteral(u"\u0072\u0075\u006c\u0065\u0073"));
+            });
+        }
+    }
+    Q_INVOKABLE bool rulesIsDefaultValue() const {
+        return !testPropertySet(3);
+    }
+    qlonglong themeType() const {
+        return p_themeType;
+    }
+    void setThemeType(const qlonglong &value) {
+        auto oldValue = p_themeType;
+        p_themeType = value;
+        markPropertySet(4);
+        if (auto config = m_config.loadRelaxed()) {
+            QMetaObject::invokeMethod(config, [this, value]() {
+                m_config.loadRelaxed()->setValue(QStringLiteral(u"\u0074\u0068\u0065\u006d\u0065\u0054\u0079\u0070\u0065"), value);
+            });
+        }
+        if (p_themeType != oldValue) {
+            Q_EMIT themeTypeChanged();
+            Q_EMIT valueChanged(QStringLiteral(u"\u0074\u0068\u0065\u006d\u0065\u0054\u0079\u0070\u0065"), value);
+        }
+    }
+    void resetThemeType() {
+        if (auto config = m_config.loadRelaxed()) {
+            QMetaObject::invokeMethod(config, [this]() {
+                m_config.loadRelaxed()->reset(QStringLiteral(u"\u0074\u0068\u0065\u006d\u0065\u0054\u0079\u0070\u0065"));
+            });
+        }
+    }
+    Q_INVOKABLE bool themeTypeIsDefaultValue() const {
+        return !testPropertySet(4);
+    }
+    qlonglong titlebarHeight() const {
+        return p_titlebarHeight;
+    }
+    void setTitlebarHeight(const qlonglong &value) {
+        auto oldValue = p_titlebarHeight;
+        p_titlebarHeight = value;
+        markPropertySet(5);
+        if (auto config = m_config.loadRelaxed()) {
+            QMetaObject::invokeMethod(config, [this, value]() {
+                m_config.loadRelaxed()->setValue(QStringLiteral(u"\u0074\u0069\u0074\u006c\u0065\u0062\u0061\u0072\u0048\u0065\u0069\u0067\u0068\u0074"), value);
+            });
+        }
+        if (p_titlebarHeight != oldValue) {
+            Q_EMIT titlebarHeightChanged();
+            Q_EMIT valueChanged(QStringLiteral(u"\u0074\u0069\u0074\u006c\u0065\u0062\u0061\u0072\u0048\u0065\u0069\u0067\u0068\u0074"), value);
+        }
+    }
+    void resetTitlebarHeight() {
+        if (auto config = m_config.loadRelaxed()) {
+            QMetaObject::invokeMethod(config, [this]() {
+                m_config.loadRelaxed()->reset(QStringLiteral(u"\u0074\u0069\u0074\u006c\u0065\u0062\u0061\u0072\u0048\u0065\u0069\u0067\u0068\u0074"));
+            });
+        }
+    }
+    Q_INVOKABLE bool titlebarHeightIsDefaultValue() const {
+        return !testPropertySet(5);
+    }
+    bool underlineShortcut() const {
+        return p_underlineShortcut;
+    }
+    void setUnderlineShortcut(const bool &value) {
+        auto oldValue = p_underlineShortcut;
+        p_underlineShortcut = value;
+        markPropertySet(6);
+        if (auto config = m_config.loadRelaxed()) {
+            QMetaObject::invokeMethod(config, [this, value]() {
+                m_config.loadRelaxed()->setValue(QStringLiteral(u"\u0075\u006e\u0064\u0065\u0072\u006c\u0069\u006e\u0065\u0053\u0068\u006f\u0072\u0074\u0063\u0075\u0074"), value);
+            });
+        }
+        if (p_underlineShortcut != oldValue) {
+            Q_EMIT underlineShortcutChanged();
+            Q_EMIT valueChanged(QStringLiteral(u"\u0075\u006e\u0064\u0065\u0072\u006c\u0069\u006e\u0065\u0053\u0068\u006f\u0072\u0074\u0063\u0075\u0074"), value);
+        }
+    }
+    void resetUnderlineShortcut() {
+        if (auto config = m_config.loadRelaxed()) {
+            QMetaObject::invokeMethod(config, [this]() {
+                m_config.loadRelaxed()->reset(QStringLiteral(u"\u0075\u006e\u0064\u0065\u0072\u006c\u0069\u006e\u0065\u0053\u0068\u006f\u0072\u0074\u0063\u0075\u0074"));
+            });
+        }
+    }
+    Q_INVOKABLE bool underlineShortcutIsDefaultValue() const {
+        return !testPropertySet(6);
+    }
+Q_SIGNALS:
+    void configInitializeFailed(DTK_CORE_NAMESPACE::DConfig *config);
+    void configInitializeSucceed(DTK_CORE_NAMESPACE::DConfig *config);
+    void valueChanged(const QString &key, const QVariant &value);
+
+    void autoDisplayFeatureChanged();
+    void featureUpdatedChanged();
+    void keyboardsearchDisabledChanged();
+    void rulesChanged();
+    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<int>(Status::Failed));
+           Q_EMIT configInitializeFailed(config);
+           return;
+        }
+
+        if (testPropertySet(0)) {
+            config->setValue(QStringLiteral(u"\u0061\u0075\u0074\u006f\u0044\u0069\u0073\u0070\u006c\u0061\u0079\u0046\u0065\u0061\u0074\u0075\u0072\u0065"), QVariant::fromValue(p_autoDisplayFeature));
+        } else {
+            updateValue(QStringLiteral(u"\u0061\u0075\u0074\u006f\u0044\u0069\u0073\u0070\u006c\u0061\u0079\u0046\u0065\u0061\u0074\u0075\u0072\u0065"), QVariant::fromValue(p_autoDisplayFeature));
+        }
+        if (testPropertySet(1)) {
+            config->setValue(QStringLiteral(u"\u0066\u0065\u0061\u0074\u0075\u0072\u0065\u0055\u0070\u0064\u0061\u0074\u0065\u0064"), QVariant::fromValue(p_featureUpdated));
+        } else {
+            updateValue(QStringLiteral(u"\u0066\u0065\u0061\u0074\u0075\u0072\u0065\u0055\u0070\u0064\u0061\u0074\u0065\u0064"), QVariant::fromValue(p_featureUpdated));
+        }
+        if (testPropertySet(2)) {
+            config->setValue(QStringLiteral(u"\u006b\u0065\u0079\u0062\u006f\u0061\u0072\u0064\u0073\u0065\u0061\u0072\u0063\u0068\u0044\u0069\u0073\u0061\u0062\u006c\u0065\u0064"), QVariant::fromValue(p_keyboardsearchDisabled));
+        } else {
+            updateValue(QStringLiteral(u"\u006b\u0065\u0079\u0062\u006f\u0061\u0072\u0064\u0073\u0065\u0061\u0072\u0063\u0068\u0044\u0069\u0073\u0061\u0062\u006c\u0065\u0064"), QVariant::fromValue(p_keyboardsearchDisabled));
+        }
+        if (testPropertySet(3)) {
+            config->setValue(QStringLiteral(u"\u0072\u0075\u006c\u0065\u0073"), QVariant::fromValue(p_rules));
+        } else {
+            updateValue(QStringLiteral(u"\u0072\u0075\u006c\u0065\u0073"), QVariant::fromValue(p_rules));
+        }
+        if (testPropertySet(4)) {
+            config->setValue(QStringLiteral(u"\u0074\u0068\u0065\u006d\u0065\u0054\u0079\u0070\u0065"), QVariant::fromValue(p_themeType));
+        } else {
+            updateValue(QStringLiteral(u"\u0074\u0068\u0065\u006d\u0065\u0054\u0079\u0070\u0065"), QVariant::fromValue(p_themeType));
+        }
+        if (testPropertySet(5)) {
+            config->setValue(QStringLiteral(u"\u0074\u0069\u0074\u006c\u0065\u0062\u0061\u0072\u0048\u0065\u0069\u0067\u0068\u0074"), QVariant::fromValue(p_titlebarHeight));
+        } else {
+            updateValue(QStringLiteral(u"\u0074\u0069\u0074\u006c\u0065\u0062\u0061\u0072\u0048\u0065\u0069\u0067\u0068\u0074"), QVariant::fromValue(p_titlebarHeight));
+        }
+        if (testPropertySet(6)) {
+            config->setValue(QStringLiteral(u"\u0075\u006e\u0064\u0065\u0072\u006c\u0069\u006e\u0065\u0053\u0068\u006f\u0072\u0074\u0063\u0075\u0074"), QVariant::fromValue(p_underlineShortcut));
+        } else {
+            updateValue(QStringLiteral(u"\u0075\u006e\u0064\u0065\u0072\u006c\u0069\u006e\u0065\u0053\u0068\u006f\u0072\u0074\u0063\u0075\u0074"), QVariant::fromValue(p_underlineShortcut));
+        }
+
+        connect(config, &DTK_CORE_NAMESPACE::DConfig::valueChanged, this, [this](const QString &key) {
+            updateValue(key);
+        }, Qt::DirectConnection);
+
+        m_status.storeRelaxed(static_cast<int>(Status::Succeed));
+        Q_EMIT configInitializeSucceed(config);
+    }
+    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(u"\u0061\u0075\u0074\u006f\u0044\u0069\u0073\u0070\u006c\u0061\u0079\u0046\u0065\u0061\u0074\u0075\u0072\u0065")) {
+            markPropertySet(0, !m_config.loadRelaxed()->isDefaultValue(key));
+            auto newValue = qvariant_cast<bool>(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);
+                }
+            });
+            return;
+        }
+        if (key == QStringLiteral(u"\u0066\u0065\u0061\u0074\u0075\u0072\u0065\u0055\u0070\u0064\u0061\u0074\u0065\u0064")) {
+            markPropertySet(1, !m_config.loadRelaxed()->isDefaultValue(key));
+            auto newValue = qvariant_cast<bool>(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(u"\u006b\u0065\u0079\u0062\u006f\u0061\u0072\u0064\u0073\u0065\u0061\u0072\u0063\u0068\u0044\u0069\u0073\u0061\u0062\u006c\u0065\u0064")) {
+            markPropertySet(2, !m_config.loadRelaxed()->isDefaultValue(key));
+            auto newValue = qvariant_cast<bool>(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(u"\u0072\u0075\u006c\u0065\u0073")) {
+            markPropertySet(3, !m_config.loadRelaxed()->isDefaultValue(key));
+            auto newValue = qvariant_cast<QString>(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(u"\u0074\u0068\u0065\u006d\u0065\u0054\u0079\u0070\u0065")) {
+            markPropertySet(4, !m_config.loadRelaxed()->isDefaultValue(key));
+            auto newValue = qvariant_cast<qlonglong>(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(u"\u0074\u0069\u0074\u006c\u0065\u0062\u0061\u0072\u0048\u0065\u0069\u0067\u0068\u0074")) {
+            markPropertySet(5, !m_config.loadRelaxed()->isDefaultValue(key));
+            auto newValue = qvariant_cast<qlonglong>(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(u"\u0075\u006e\u0064\u0065\u0072\u006c\u0069\u006e\u0065\u0053\u0068\u006f\u0072\u0074\u0063\u0075\u0074")) {
+            markPropertySet(6, !m_config.loadRelaxed()->isDefaultValue(key));
+            auto newValue = qvariant_cast<bool>(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;
+        }
+    }
+    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();
+    }
+
+    QAtomicPointer<DTK_CORE_NAMESPACE::DConfig> m_config = nullptr;
+
+public:
+    enum class Status {
+        Invalid = 0,
+        Succeed = 1,
+        Failed = 2
+    };
+private:
+    QAtomicInteger<int> m_status = static_cast<int>(Status::Invalid);
+
+    bool p_autoDisplayFeature { false };
+    bool p_featureUpdated { false };
+    bool p_keyboardsearchDisabled { false };
+    // Default value: ""
+    QString p_rules { QLatin1String("") };
+    qlonglong p_themeType { 0 };
+    qlonglong p_titlebarHeight { -1 };
+    bool p_underlineShortcut { false };
+    QAtomicInteger<quint32> m_propertySetStatus0 = 0;
+};
+
+#endif // DCONFIG_ORG_DEEPIN_DTK_PREFERENCE_H
index ae64fcade619b141c21657e2cf21704262b1804d..cfba5e86abd79f89e0bc8e643c8d0f6da01eb3eb 100644 (file)
@@ -3,6 +3,7 @@ file(GLOB LOG_HEADER
 )
 set(LOG_SOURCE
   ${CMAKE_CURRENT_LIST_DIR}/LogManager.cpp
+  ${CMAKE_CURRENT_LIST_DIR}/dconfig_org_deepin_dtk_preference.hpp
 )
 
 set(log_SRCS
index f3ef2a58cad1172165f32f7a8025d2f1c726aef1..63b8fc9272a59299265da28b50af4fde96401c5c 100644 (file)
@@ -3,3 +3,4 @@ add_subdirectory(deepin-os-release)
 add_subdirectory(qdbusxml2cpp)
 add_subdirectory(settings)
 add_subdirectory(ch2py)
+add_subdirectory(dconfig2cpp)
diff --git a/tools/dconfig2cpp/CMakeLists.txt b/tools/dconfig2cpp/CMakeLists.txt
new file mode 100644 (file)
index 0000000..74f1d3c
--- /dev/null
@@ -0,0 +1,26 @@
+set(TARGET_NAME dconfig2cpp)
+set(BIN_NAME ${TARGET_NAME}${DTK_VERSION_MAJOR})
+
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core)
+
+add_executable(${BIN_NAME}
+  main.cpp
+)
+
+target_link_libraries(
+  ${BIN_NAME} PRIVATE
+  Qt${QT_VERSION_MAJOR}::Core
+)
+
+set_target_properties(
+  ${BIN_NAME} PROPERTIES
+  OUTPUT_NAME ${TARGET_NAME}
+  EXPORT_NAME DConfig2Cpp
+)
+
+install(
+  TARGETS ${BIN_NAME}
+  EXPORT Dtk${DTK_VERSION_MAJOR}ToolsTargets
+  DESTINATION ${TOOL_INSTALL_DIR}
+)
diff --git a/tools/dconfig2cpp/main.cpp b/tools/dconfig2cpp/main.cpp
new file mode 100644 (file)
index 0000000..30ee258
--- /dev/null
@@ -0,0 +1,511 @@
+// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include <QCoreApplication>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonValue>
+#include <QJsonArray>
+#include <QFile>
+#include <QDebug>
+#include <QCommandLineParser>
+#include <QFileInfo>
+
+static QString toUnicodeEscape(const QString& input) {
+    QString result;
+    for (QChar ch : input) {
+        result += QString("\\u%1").arg(ch.unicode(), 4, 16, QChar('0'));
+    }
+    return result;
+}
+
+// Converts a QJsonValue to a corresponding C++ code representation
+static QString jsonValueToCppCode(const QJsonValue &value){
+    if (value.isBool()) {
+        return value.toBool() ? QLatin1String("true") : QLatin1String("false");
+    } else if (value.isDouble()) {
+        const auto variantValue = value.toVariant();
+        if (variantValue.userType() == QVariant(static_cast<int>(1)).userType()) {
+            return QString::number(value.toInt());
+        } else if (variantValue.userType() == QVariant(static_cast<qint64>(1)).userType()) {
+            return QString::number(variantValue.toLongLong());
+        }
+
+        return QString::number(value.toDouble());
+    } else if (value.isString()) {
+        const auto string = value.toString();
+        if (string.isEmpty()) {
+            return QLatin1String("QLatin1String(\"\")");
+        }
+        return QString("QStringLiteral(u\"%1\")").arg(toUnicodeEscape(string));
+    } else if (value.isNull()) {
+        return "QVariant::fromValue(nullptr)";
+    } else if (value.isArray()) {
+        QStringList elements;
+        const auto array = value.toArray();
+        for (const QJsonValue &element : array) {
+            elements << "QVariant(" + jsonValueToCppCode(element) + ")";
+        }
+        return "QList<QVariant>{" + elements.join(", ") + "}";
+    } else if (value.isObject()) {
+        QStringList elements;
+        QJsonObject obj = value.toObject();
+        for (auto it = obj.begin(); it != obj.end(); ++it) {
+            elements << QString("{QStringLiteral(u\"%1\"), QVariant(%2)}")
+                            .arg(toUnicodeEscape(it.key()),
+                                 jsonValueToCppCode(it.value()));
+        }
+        return "QVariantMap{" + elements.join(", ") + "}";
+    } else {
+        return "QVariant()";
+    }
+}
+
+int main(int argc, char *argv[]) {
+    QCoreApplication app(argc, argv);
+    QCommandLineParser parser;
+    parser.setApplicationDescription(QLatin1String("DConfig to C++ class generator"));
+    parser.addHelpOption();
+
+    // Define command line options
+    QCommandLineOption classNameOption(QStringList() << QLatin1String("c") << QLatin1String("class-name"),
+                                       QLatin1String("Name of the generated class"),
+                                       QLatin1String("className"));
+    parser.addOption(classNameOption);
+
+    QCommandLineOption sourceFileOption(QStringList() << QLatin1String("o") << QLatin1String("output"),
+                                        QLatin1String("Path to the output source(header only) file"),
+                                        QLatin1String("sourceFile"));
+    parser.addOption(sourceFileOption);
+
+    QCommandLineOption cppPropertyOption(QStringList() << QLatin1String("use-qproperty"),
+                                         QLatin1String("Generate Qt/C++ properties use QProperty"));
+    parser.addOption(cppPropertyOption);
+
+    QCommandLineOption forceRequestThread(QStringList() << QLatin1String("force-request-thread"),
+                                          QLatin1String("Force request thread to create DConfig instance"));
+    parser.addOption(forceRequestThread);
+
+    QCommandLineOption noComment(QStringList() << QLatin1String("no-comment"),
+                                 QLatin1String("Do not generate comments in the generated code"));
+    parser.addOption(noComment);
+
+    parser.addPositionalArgument(QLatin1String("json-file"), QLatin1String("Path to the input JSON file"));
+    parser.process(app);
+
+    const QStringList args = parser.positionalArguments();
+    if (args.size() != 1) {
+        parser.showHelp(-1);
+    }
+
+    QString className = parser.value(classNameOption);
+    const QString jsonFileName = QFileInfo(args.first()).completeBaseName();
+    if (className.isEmpty()) {
+        className = QLatin1String("dconfig_") + QString(jsonFileName).replace('.', '_');
+    }
+
+    QString sourceFilePath = parser.value(sourceFileOption);
+    if (sourceFilePath.isEmpty()) {
+        sourceFilePath = className.toLower() + QLatin1String(".hpp");
+    }
+
+    QFile file(args.first());
+    if (!file.open(QIODevice::ReadOnly)) {
+        qWarning() << QLatin1String("Failed to open file:") << args.first();
+        return -1;
+    }
+
+    QByteArray data = file.readAll();
+    QJsonDocument doc = QJsonDocument::fromJson(data);
+    QJsonObject root = doc.object();
+
+    // Check magic value
+    if (root[QLatin1String("magic")].toString() != QLatin1String("dsg.config.meta")) {
+        qWarning() << QLatin1String("Invalid magic value in JSON file");
+        return -1;
+    }
+
+    // Generate header and source files
+    QFile headerFile(sourceFilePath);
+    if (!headerFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+        qWarning() << QLatin1String("Failed to open file for writing:") << sourceFilePath;
+        return -1;
+    }
+
+    QTextStream headerStream(&headerFile);
+
+    // Extract version and add it as a comment in the generated code
+    QString version = root[QLatin1String("version")].toString();
+
+    // Generate header and source file comments
+    QString commandLineArgs = QCoreApplication::arguments().join(QLatin1String(" "));
+    QString generationTime = QDateTime::currentDateTime().toString(Qt::ISODate);
+
+    if (!parser.isSet(noComment)) {
+        QString headerComment = QString(
+                                    "/**\n"
+                                    " * This file is generated by dconfig2cpp.\n"
+                                    " * Command line arguments: %1\n"
+                                    " * Generation time: %2\n"
+                                    " * JSON file version: %3\n"
+                                    " *\n"
+                                    " * WARNING: DO NOT MODIFY THIS FILE MANUALLY.\n"
+                                    " * If you need to change the content, please modify the dconfig2cpp tool.\n"
+                                    " */\n\n"
+                                    ).arg(commandLineArgs, generationTime, version);
+
+        headerStream << headerComment;
+    }
+
+    QJsonObject contents = root[QLatin1String("contents")].toObject();
+
+    // Write header file content
+    headerStream << "#ifndef " << className.toUpper() << "_H\n";
+    headerStream << "#define " << className.toUpper() << "_H\n\n";
+    headerStream << "#include <QThread>\n";
+    headerStream << "#include <QVariant>\n";
+    headerStream << "#include <QDebug>\n";
+    headerStream << "#include <QAtomicPointer>\n";
+    headerStream << "#include <QAtomicInteger>\n";
+
+    if (parser.isSet(cppPropertyOption)) {
+        headerStream << "#include <QProperty>\n";
+    }
+
+    headerStream << "#include <DConfig>\n\n";
+    headerStream << "class " << className << " : public QObject {\n";
+    headerStream << "    Q_OBJECT\n\n";
+
+    struct Property {
+        QString typeName;
+        QString propertyName;
+        QString capitalizedPropertyName;
+        QString propertyNameString;
+        QJsonValue defaultValue;
+    };
+    QList<Property> properties;
+    QStringList propertyNames;
+    QStringList propertyNameStrings;
+
+    static QStringList usedKeywords = {
+        className,
+        "create",
+        "createByName",
+        "config",
+        "keyList",
+        "isInitializeSucceed",
+        "isInitializeFailed",
+        "isInitializing",
+        "isDefaultValue",
+        "m_config",
+        "m_status",
+    };
+
+    for (int i = 0; i <= (contents.size()) / 32; ++i) {
+        usedKeywords << QLatin1String("m_propertySetStatus") + QString::number(i);
+    }
+
+    // Iterate over JSON contents to extract properties
+    for (auto it = contents.begin(); it != contents.end(); ++it) {
+        QJsonObject obj = it.value().toObject();
+        QString propertyName = it.key();
+        QString typeName;
+        const auto value = obj[QLatin1String("value")];
+        if (value.isBool()) {
+            typeName = "bool";
+        } else if (value.isArray()) {
+            typeName = "QList<QVariant>";
+        } else if (value.isObject()) {
+            typeName = "QVariantMap";
+        } else if (value.isDouble()) {
+            const auto variantValue = value.toVariant();
+            typeName = QString::fromLatin1(variantValue.typeName());
+        } else if (value.isString()) {
+            typeName = "QString";
+        } else {
+            typeName = "QVariant";
+        }
+
+        QString capitalizedPropertyName = propertyName;
+        if (!capitalizedPropertyName.isEmpty() && capitalizedPropertyName[0].isLower()) {
+            capitalizedPropertyName[0] = capitalizedPropertyName[0].toUpper();
+        }
+
+        propertyNames << propertyName;
+        properties.append(Property({
+            typeName,
+            propertyName,
+            capitalizedPropertyName,
+            "QStringLiteral(u\"" + toUnicodeEscape(propertyName) + "\")",
+            obj[QLatin1String("value")]
+        }));
+        propertyNameStrings << properties.last().propertyNameString;
+
+        const  QString readFunction = usedKeywords.contains(propertyName) ? QLatin1String(" READ get") + capitalizedPropertyName
+                                                                           : QLatin1String(" READ ") + propertyName;
+        headerStream << "    Q_PROPERTY(" << typeName << " " << propertyName << readFunction
+                     << " WRITE set" << capitalizedPropertyName << " NOTIFY " << propertyName << "Changed"
+                     << " RESET reset" << capitalizedPropertyName << ")\n";
+    }
+
+    headerStream << "    Q_CLASSINFO(\"DConfigKeyList\", \"" << propertyNames.join(";") <<"\")\n"
+                 << "    Q_CLASSINFO(\"DConfigFileName\", \"" << QString(jsonFileName).replace("\n", "\\n").replace("\r", "\\r") <<"\")\n"
+                 << "    Q_CLASSINFO(\"DConfigFileVersion\", \"" << version <<"\")\n\n"
+                 << "public:\n"
+                 << "    explicit " << className
+                 << R"((QThread *thread, DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &name, const QString &appId, const QString &subpath, QObject *parent)
+                : QObject(parent) {
+        if (!thread->isRunning()) {
+            qWarning() << QLatin1String("Warning: The provided thread is not running.");
+        }
+        Q_ASSERT(QThread::currentThread() != thread);
+        auto worker = new QObject();
+        worker->moveToThread(thread);
+        QMetaObject::invokeMethod(worker, [=, this]() {
+            DTK_CORE_NAMESPACE::DConfig *config = nullptr;
+            if (backend) {
+                if (appId.isNull()) {
+                    config = DTK_CORE_NAMESPACE::DConfig::create(backend, 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(name, subpath, nullptr);
+                } else {
+                    config = DTK_CORE_NAMESPACE::DConfig::create(appId, name, subpath, nullptr);
+                }
+            }
+            if (!config) {
+                qWarning() << QLatin1String("Failed to create DConfig instance.");
+                worker->deleteLater();
+                return;
+            }
+            config->moveToThread(QThread::currentThread());
+            initializeInConfigThread(config);
+            worker->deleteLater();
+        });
+    }
+)";
+
+    const QString jsonFileString = "QStringLiteral(u\"" + toUnicodeEscape(jsonFileName) + "\")";
+    // Generate constructors
+    if (parser.isSet(forceRequestThread))
+        headerStream << "    static " << className << "* create(QThread *thread, const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr)\n";
+    else
+        headerStream << "    static " << className << "* create(const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread())\n";
+    headerStream << "    { return new " << className << "(thread, nullptr, " << jsonFileString << ", appId, subpath, parent); }\n";
+    if (parser.isSet(forceRequestThread))
+        headerStream << "    static " << className << "* create(QThread *thread, DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr)\n";
+    else
+        headerStream << "    static " << className << "* create(DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread())\n";
+    headerStream << "    { return new " << className << "(thread, backend, " << jsonFileString << ", appId, subpath, parent); }\n";
+    if (parser.isSet(forceRequestThread))
+        headerStream << "    static " << className << "* createByName(QThread *thread, const QString &name, const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr)\n";
+    else
+        headerStream << "    static " << className << "* createByName(const QString &name, const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread())\n";
+    headerStream << "    { return new " << className << "(thread, nullptr, name, appId, subpath, parent); }\n";
+    if (parser.isSet(forceRequestThread))
+        headerStream << "    static " << className << "* createByName(QThread *thread, DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &name, const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr)\n";
+    else
+        headerStream << "    static " << className << "* 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())\n";
+    headerStream << "    { return new " << className << "(thread, backend, name, appId, subpath, parent); }\n";
+
+    // Destructor
+    headerStream << "    ~" << className << R"(() {
+        if (m_config.loadRelaxed()) {
+            m_config.loadRelaxed()->deleteLater();
+        }
+    }
+
+    Q_INVOKABLE DTK_CORE_NAMESPACE::DConfig *config() const {
+        return m_config.loadRelaxed();
+    }
+
+    Q_INVOKABLE bool isInitializeSucceed() const {
+        return m_status.loadRelaxed() == static_cast<int>(Status::Succeed);
+    }
+
+    Q_INVOKABLE bool isInitializeFailed() const {
+        return m_status.loadRelaxed() == static_cast<int>(Status::Failed);
+    }
+
+    Q_INVOKABLE bool isInitializing() const {
+        return m_status.loadRelaxed() == static_cast<int>(Status::Invalid);
+    }
+
+)";
+
+    headerStream << "    Q_INVOKABLE QStringList keyList() const {\n"
+                 << "        return { " << propertyNameStrings.join(",\n                 ") << "};\n"
+                 << "    }\n\n";
+
+    headerStream << "    Q_INVOKABLE bool isDefaultValue(const QString &key) const {\n";
+    for (int i = 0; i < properties.size(); ++i) {
+        headerStream << "        if (key == " << properties.at(i).propertyNameString << ")\n"
+                     << "            return " << properties.at(i).propertyName << "IsDefaultValue();\n";
+    }
+    headerStream << "        return false;\n"
+                 << "    }\n\n";
+
+    // Generate property getter and setter methods
+    for (int i = 0; i < properties.size(); ++i) {
+        const Property &property = properties[i];
+        const  QString readFunction = usedKeywords.contains(property.propertyName)
+                                         ? "get" + property.capitalizedPropertyName
+                                         : property.propertyName;
+        assert(!usedKeywords.contains(readFunction));
+
+        headerStream << "    " << property.typeName << " " << readFunction << "() const {\n"
+                     << "        return 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"
+                     << "            });\n"
+                     << "        }\n"
+                     << "        if (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"
+                     << "            });\n"
+                     << "        }\n"
+                     << "    }\n";
+        if (parser.isSet(cppPropertyOption)) {
+            headerStream << "    QBindable<" << property.typeName << "> bindable" << property.capitalizedPropertyName << "() {\n"
+                         << "        return QBindable<" << property.typeName << ">(this, " << property.propertyNameString << ");\n"
+                         << "    }\n";
+        }
+
+        headerStream << "    Q_INVOKABLE bool " << property.propertyName << "IsDefaultValue() const {\n"
+                     << "        return !testPropertySet(" << i << ");\n"
+                     << "    }\n";
+    }
+
+    // Generate signals for property changes
+    headerStream << "Q_SIGNALS:\n"
+                 << "    void configInitializeFailed(DTK_CORE_NAMESPACE::DConfig *config);\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";
+    }
+
+    // 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<int>(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";
+        headerStream << "            config->setValue(" << property.propertyNameString << ", QVariant::fromValue(p_" << property.propertyName << "));\n";
+        headerStream << "        } else {\n";
+        headerStream << "            updateValue(" << property.propertyNameString << ", QVariant::fromValue(p_" << property.propertyName << "));\n";
+        headerStream << "        }\n";
+    }
+    headerStream << R"(
+        connect(config, &DTK_CORE_NAMESPACE::DConfig::valueChanged, this, [this](const QString &key) {
+            updateValue(key);
+        }, Qt::DirectConnection);
+
+        m_status.storeRelaxed(static_cast<int>(Status::Succeed));
+        Q_EMIT configInitializeSucceed(config);
+    }
+    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);
+)";
+    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"
+                     << "                }\n"
+                     << "            });\n"
+                     << "            return;\n"
+                     << "        }\n";
+    }
+    headerStream << "    }\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 << "        Q_UNREACHABLE();\n    }\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 << "        Q_UNREACHABLE();\n"
+                 << "    }\n";
+
+    // Member variables
+    headerStream << R"(
+    QAtomicPointer<DTK_CORE_NAMESPACE::DConfig> m_config = nullptr;
+
+public:
+    enum class Status {
+        Invalid = 0,
+        Succeed = 1,
+        Failed = 2
+    };
+private:
+    QAtomicInteger<int> m_status = static_cast<int>(Status::Invalid);
+
+)";
+
+    // 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, add 'e' to the number in the JSON value field, e.g., \"value\": 1.0e, not just 1.0\n";
+        } else 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";
+    }
+
+    // Property set status variables
+    for (int i = 0; i <= (properties.size()) / 32; ++i) {
+        headerStream << "    QAtomicInteger<quint32> m_propertySetStatus" << QString::number(i) << " = 0;\n";
+    }
+    headerStream << "};\n\n";
+    headerStream << "#endif // " << className.toUpper() << "_H\n";
+
+    return 0;
+}