diff options
author | Alexander Lohnau <alexander.lohnau@gmx.de> | 2021-11-07 21:34:18 +0100 |
---|---|---|
committer | Alexander Lohnau <alexander.lohnau@gmx.de> | 2021-11-22 09:36:02 +0000 |
commit | 3f29f3d6452f735757cb8f84dfc20cdcba791613 (patch) | |
tree | 11f40ed6a40cc3368fedfea193f4994c3447348b | |
parent | c3be6d02f6c061707c6d93e06889a2e56b994d87 (diff) | |
download | kconfig-3f29f3d6452f735757cb8f84dfc20cdcba791613.tar.gz kconfig-3f29f3d6452f735757cb8f84dfc20cdcba791613.tar.bz2 |
Copy ConfigPropertyMap from KDeclarative to new KConfig QML module
This way consumers which want to use the ConfigPropertyMap don't have to
pull in KDeclarative's entire dependency tree.
Also we can remove the automatic saving of the config, previously
this was opt-out - which makes is difficult to deprecate anything.
This way the API design is also more clear, since the object only takes care of exposing the
data to QML. The writing has to be done manually, which makes more sense anyways when we have
the notify opt-in.
As discussed on the KF6 weekly thread, an optional QML submodule is seen as the best
way to handle the QML dependency.
Task: https://phabricator.kde.org/T12131
Relates to https://phabricator.kde.org/T12126, after his change the KDeclarative stuff can be deprecated.
Because we don't register the property map in any QML plugin, there is
no conflict in duplicating and modifying it now.
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/qml/CMakeLists.txt | 39 | ||||
-rw-r--r-- | src/qml/kconfigpropertymap.cpp | 148 | ||||
-rw-r--r-- | src/qml/kconfigpropertymap.h | 69 |
5 files changed, 260 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 326912b7..64e24647 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,7 @@ option(KCONFIG_USE_GUI "Build components using Qt5Gui" ON) if(KCONFIG_USE_GUI) find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Gui) endif() +find_package(Qt5 ${REQUIRED_QT_VERSION} OPTIONAL_COMPONENTS Qml) if (NOT ANDROID) option(KCONFIG_USE_DBUS "Build components using Qt5DBus" ON) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e6bac82a..89fe7284 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,6 +2,9 @@ add_subdirectory(core) if(TARGET Qt5::Gui) add_subdirectory(gui) endif() +if(TARGET Qt5::Qml) + add_subdirectory(qml) +endif() add_subdirectory(kconfig_compiler) add_subdirectory(kconf_update) add_subdirectory(kreadconfig) diff --git a/src/qml/CMakeLists.txt b/src/qml/CMakeLists.txt new file mode 100644 index 00000000..1cdae88a --- /dev/null +++ b/src/qml/CMakeLists.txt @@ -0,0 +1,39 @@ +add_library(KF5ConfigQml) +add_library(KF5::ConfigQml ALIAS KF5ConfigQml) + +target_sources(KF5ConfigQml PRIVATE + kconfigpropertymap.cpp +) + +ecm_generate_export_header(KF5ConfigQml + BASE_NAME KConfigQml + GROUP_BASE_NAME KF + VERSION ${KF_VERSION} + DEPRECATED_BASE_VERSION 0 + EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT} +) + +target_link_libraries(KF5ConfigQml + PUBLIC + KF5::ConfigCore # KCoreConfigSkeleton, in ConfigPropertyMap + Qt5::Qml +) +set_target_properties(KF5ConfigQml PROPERTIES VERSION ${KCONFIG_VERSION} + SOVERSION ${KCONFIG_SOVERSION} + EXPORT_NAME ConfigQml +) + +ecm_generate_headers(KConfigQml_HEADERS + HEADER_NAMES + KConfigPropertyMap + + REQUIRED_HEADERS KConfigQml_HEADERS +) +target_include_directories(KF5ConfigQml INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF5}/KConfigQml>") + +install(TARGETS KF5ConfigQml EXPORT KF5ConfigTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/kconfigqml_export.h + ${KConfigQml_HEADERS} + DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KConfigQml COMPONENT Devel +) diff --git a/src/qml/kconfigpropertymap.cpp b/src/qml/kconfigpropertymap.cpp new file mode 100644 index 00000000..55b2991d --- /dev/null +++ b/src/qml/kconfigpropertymap.cpp @@ -0,0 +1,148 @@ +/* + SPDX-FileCopyrightText: 2013 Marco Martin <notmart@gmail.com> + SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org> + SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de> + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#include "kconfigpropertymap.h" + +#include <KCoreConfigSkeleton> +#include <QJSValue> +#include <QPointer> + +#include <functional> + +class KConfigPropertyMapPrivate +{ +public: + KConfigPropertyMapPrivate(KConfigPropertyMap *map) + : q(map) + { + } + + enum LoadConfigOption { + DontEmitValueChanged, + EmitValueChanged, + }; + + void loadConfig(LoadConfigOption option); + void writeConfig(); + void writeConfigValue(const QString &key, const QVariant &value); + + KConfigPropertyMap *q; + QPointer<KCoreConfigSkeleton> config; + bool updatingConfigValue = false; + bool autosave = true; + bool notify = false; +}; + +KConfigPropertyMap::KConfigPropertyMap(KCoreConfigSkeleton *config, QObject *parent) + : QQmlPropertyMap(this, parent) + , d(new KConfigPropertyMapPrivate(this)) +{ + Q_ASSERT(config); + d->config = config; + + // Reload the config only if the change signal has *not* been emitted by ourselves updating the config + connect(config, &KCoreConfigSkeleton::configChanged, this, [this]() { + if (!d->updatingConfigValue) { + d->loadConfig(KConfigPropertyMapPrivate::EmitValueChanged); + } + }); + connect(this, &KConfigPropertyMap::valueChanged, this, [this](const QString &key, const QVariant &value) { + d->writeConfigValue(key, value); + }); + + d->loadConfig(KConfigPropertyMapPrivate::DontEmitValueChanged); +} + +KConfigPropertyMap::~KConfigPropertyMap() = default; + +bool KConfigPropertyMap::isNotify() const +{ + return d->notify; +} + +void KConfigPropertyMap::setNotify(bool notify) +{ + d->notify = notify; +} + +void KConfigPropertyMap::writeConfig() +{ + d->writeConfig(); +} + +QVariant KConfigPropertyMap::updateValue(const QString &key, const QVariant &input) +{ + Q_UNUSED(key); + if (input.userType() == qMetaTypeId<QJSValue>()) { + return input.value<QJSValue>().toVariant(); + } + return input; +} + +bool KConfigPropertyMap::isImmutable(const QString &key) const +{ + KConfigSkeletonItem *item = d->config.data()->findItem(key); + if (item) { + return item->isImmutable(); + } + + return false; +} + +void KConfigPropertyMapPrivate::loadConfig(KConfigPropertyMapPrivate::LoadConfigOption option) +{ + if (!config) { + return; + } + + const auto &items = config.data()->items(); + for (KConfigSkeletonItem *item : items) { + q->insert(item->key() + QStringLiteral("Default"), item->getDefault()); + q->insert(item->key(), item->property()); + if (option == EmitValueChanged) { + Q_EMIT q->valueChanged(item->key(), item->property()); + } + } +} + +void KConfigPropertyMapPrivate::writeConfig() +{ + if (!config) { + return; + } + + const auto lstItems = config.data()->items(); + for (KConfigSkeletonItem *item : lstItems) { + item->setWriteFlags(notify ? KConfigBase::Notify : KConfigBase::Normal); + item->setProperty(q->value(item->key())); + } + + if (autosave) { + updatingConfigValue = true; + config.data()->save(); + updatingConfigValue = false; + } +} + +void KConfigPropertyMapPrivate::writeConfigValue(const QString &key, const QVariant &value) +{ + KConfigSkeletonItem *item = config.data()->findItem(key); + if (item) { + updatingConfigValue = true; + item->setWriteFlags(notify ? KConfigBase::Notify : KConfigBase::Normal); + item->setProperty(value); + if (autosave) { + config.data()->save(); + // why read? read will update KConfigSkeletonItem::mLoadedValue, + // allowing a write operation to be performed next time + config.data()->read(); + } + updatingConfigValue = false; + } +} + diff --git a/src/qml/kconfigpropertymap.h b/src/qml/kconfigpropertymap.h new file mode 100644 index 00000000..3795e099 --- /dev/null +++ b/src/qml/kconfigpropertymap.h @@ -0,0 +1,69 @@ +/* + SPDX-FileCopyrightText: 2013 Marco Martin <notmart@gmail.com> + SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org> + SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de> + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#ifndef KCONFIGPROPERTYMAP_H +#define KCONFIGPROPERTYMAP_H + +#include <QQmlPropertyMap> +#include <memory> + +class KCoreConfigSkeleton; + +#include <kconfigqml_export.h> + +class KConfigPropertyMapPrivate; + +/** + * @class KConfigPropertyMap configpropertymap.h ConfigPropertyMap + * + * An object that (optionally) automatically saves changes in a + * property map to a configuration object (e.g. a KConfig file). + * @since 5.89 + */ +class KCONFIGQML_EXPORT KConfigPropertyMap : public QQmlPropertyMap +{ + Q_OBJECT + +public: + KConfigPropertyMap(KCoreConfigSkeleton *config, QObject *parent = nullptr); + ~KConfigPropertyMap() override; + + /** + * Whether notifications on config changes are enabled. Disabled by default. + * @see KConfigBase::Notify + * @return true if writes send (dbus) notifications + */ + bool isNotify() const; + + /** + * Enable or disable notifications on config changes. + * @see KConfigBase::Notify + * @param notify whether to send notifications + */ + void setNotify(bool notify); + + /** + * @brief Whether the value at the given key is immutable + * + * @return true if the value is immutable, false if it isn't or it doesn't exist + */ + Q_INVOKABLE bool isImmutable(const QString &key) const; + + /** + * Saves the state of the property map on disk. + */ + void writeConfig(); + +protected: + QVariant updateValue(const QString &key, const QVariant &input) override; + +private: + std::unique_ptr<KConfigPropertyMapPrivate> const d; +}; + +#endif |