diff --git a/src/quickaddons/CMakeLists.txt b/src/quickaddons/CMakeLists.txt --- a/src/quickaddons/CMakeLists.txt +++ b/src/quickaddons/CMakeLists.txt @@ -3,6 +3,7 @@ managedtexturenode.cpp quickviewsharedengine.cpp configmodule.cpp + managedconfigmodule.cpp qtquicksettings.cpp) kconfig_add_kcfg_files(KF5QuickAddons_LIB_SRCS renderersettings.kcfgc) @@ -47,6 +48,7 @@ ManagedTextureNode QtQuickSettings ConfigModule + ManagedConfigModule QuickViewSharedEngine PREFIX KQuickAddons diff --git a/src/quickaddons/managedconfigmodule.h b/src/quickaddons/managedconfigmodule.h new file mode 100644 --- /dev/null +++ b/src/quickaddons/managedconfigmodule.h @@ -0,0 +1,216 @@ +/* + This file is part of the KDE libraries + + Copyright (C) 2019 Kevin Ottens + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#ifndef MANAGEDCONFIGMODULE_H +#define MANAGEDCONFIGMODULE_H + +#include + +namespace KQuickAddons { + +class ManagedConfigModulePrivate; + +/** + * @class KQuickAddons::ManagedConfigModule managedconfigmodule.h KQuickAddons/ManagedConfigModule + * + * The base class for configuration modules using KConfigXT settings. + * + * Configuration modules are realized as plugins that are loaded only when + * needed. + * + * The module in principle is a simple widget displaying the + * item to be changed. The module has a very small interface. + * + * All the necessary glue logic and the GUI bells and whistles + * are provided by the control center and must not concern + * the module author. + * + * To write a config module, you have to create a C++ library + * and an accompaning QML user interface. + * The library must contain a factory function like the following: + * + * \code + * #include + * + * K_PLUGIN_FACTORY(MyConfigModuleFactory, registerPlugin() ) + * \endcode + * + * The constructor of the ConfigModule then looks like this: + * \code + * YourConfigModule::YourConfigModule( QObject* parent ) + * : ManagedConfigModule( parent ), + * SettingsObject( this ) + * { + * KAboutData *about = new KAboutData( + * , i18n( "..." ), + * KDE_VERSION_STRING, QString(), KAboutLicense::GPL, + * i18n( "Copyright 2006 ..." ) ); + * about->addAuthor( i18n(...) ); + * setAboutData( about ); + * . + * . + * . + * } + * \endcode + * + * We are assuming here that SettingsObject is a class generated from a kcfg file + * and that it will be somehow exposed as a constant property to be used from the QML side. + * It will be automatically discovered by ManagedConfigModule which will update + * the saveNeeded and defaults inherited properties by itself. Thus by inheriting from + * this class you shall not try to manage those properties yourselves. + * + * The QML part must be in the KPackage format, installed under share/kpackage/kcms. + * @see KPackage::Package + * The package must have the same name as the KAboutData componentName, to be installed + * by CMake with the command: + * kpackage_install_package(package kcm_componentName kcms) + * given "package" is the subdirectory in the source tree where the package sources are + * located, and "kcm_componentName" is the componentname passed to the KAboutData in the + * C++ part. + * The main config dialog UI will be the file + * ui/main.qml from the package (or what X-KPackage-MainScript value is in the + * package metadata desktop file). + * + * The QML part can access all the properties of ManagedConfigModule (together with the properties + * defined in its subclass) by accessing to the global object "kcm", or with the + * import of "org.kde.kcm 1.0" the ConfigModule attached property. + * + * \code + * import QtQuick 2.1 + * import QtQuick.Controls 1.0 as QtControls + * import org.kde.kcm 1.0 + * import org.kde.plasma.core 2.0 as PlasmaCore + * + * Item { + * //implicitWidth and implicitHeight will be used as initial size + * //when loaded in kcmshell5 + * implicitWidth: units.gridUnit * 20 + * implicitHeight: units.gridUnit * 20 + * + * ConfigModule.buttons: ConfigModule.Help|ConfigModule.Apply + * Label { + * text: kcm.needsSave + * } + * } + * \endcode + * + * See http://techbase.kde.org/Development/Tutorials/KCM_HowTo + * for more detailed documentation. + * + * @since 5.65 + */ +class QUICKADDONS_EXPORT ManagedConfigModule : public ConfigModule +{ + Q_OBJECT +public: + /** + * Base class for all modules which manage automatically some of their state. + * + * @param aboutData becomes owned by the ManagedConfigModule + */ + explicit ManagedConfigModule(const KAboutData *aboutData, QObject *parent = nullptr, const QVariantList &args = QVariantList()); + + /** + * @param metaData description for the plugin: it will generate a KAboutData from that + */ + explicit ManagedConfigModule(const KPluginMetaData &metaData, QObject *parent = nullptr, const QVariantList &args = QVariantList()); + + /** + * Base class for all KControlModules. + * + * @note do not emit changed signals here, since they are not yet connected + * to any slot. + */ + explicit ManagedConfigModule(QObject *parent = nullptr, const QVariantList &args = QVariantList()); + + /** + * Destroys the module. + */ + ~ManagedConfigModule(); + +public Q_SLOTS: + /** + * Load the configuration data into the module. + * + * This method is invoked whenever the module should read its configuration + * (most of the times from a config file) and update the user interface. + * This happens when the user clicks the "Reset" button in the control + * center, to undo all of his changes and restore the currently valid + * settings. It is also called right after construction. + * + * By default this will load the settings from the child setting objects + * of this module. + */ + void load() override; + + /** + * Save the configuration data. + * + * The save method stores the config information as shown + * in the user interface in the config files. + * It is called when the user clicks "Apply" or "Ok". + * + * By default this will save the child setting objects + * of this module. + */ + void save() override; + + /** + * Sets the configuration to sensible default values. + * + * This method is called when the user clicks the "Default" + * button. It should set the display to useful values. + * + * By default this will reset to defaults the child setting objects + * of this module. + */ + void defaults() override; + +private: + /** + * Allows to indicate if the module requires saving. + * + * By default this returns false, it needs to be overriden only + * if the module has state outside of the settings declared in + * the KConfigXT classes it uses. + */ + virtual bool isSaveNeeded() const; + + /** + * Allows to indicate if the module state is representing its defaults. + * + * By default this returns true, it needs to be overriden only + * if the module has state outside of the settings declared in + * the KConfigXT classes it uses. + */ + virtual bool isDefaults() const; + + Q_PRIVATE_SLOT(d, void _k_registerSettings()) + Q_PRIVATE_SLOT(d, void _k_settingsChanged()) + ManagedConfigModulePrivate *const d; + friend class ManagedConfigModulePrivate; +}; + +} + +#endif //MANAGEDCONFIGMODULE_H + diff --git a/src/quickaddons/managedconfigmodule.cpp b/src/quickaddons/managedconfigmodule.cpp new file mode 100644 --- /dev/null +++ b/src/quickaddons/managedconfigmodule.cpp @@ -0,0 +1,149 @@ +/* + This file is part of the KDE libraries + + Copyright (C) 2019 Kevin Ottens + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "managedconfigmodule.h" + +#include + +namespace KQuickAddons { + +class ManagedConfigModulePrivate +{ +public: + ManagedConfigModulePrivate(ManagedConfigModule *module): + _q(module) + { + QMetaObject::invokeMethod(_q, "_k_registerSettings", Qt::QueuedConnection); + } + + void _k_registerSettings(); + void _k_settingsChanged(); + + ManagedConfigModule *_q; + QList _skeletons; +}; + +ManagedConfigModule::ManagedConfigModule(const KAboutData *aboutData, QObject *parent, const QVariantList &args) + : ConfigModule(aboutData, parent, args), + d(new ManagedConfigModulePrivate(this)) +{ +} + +ManagedConfigModule::ManagedConfigModule(const KPluginMetaData &metaData, QObject *parent, const QVariantList &args) + : ConfigModule(metaData, parent, args), + d(new ManagedConfigModulePrivate(this)) +{ +} + +ManagedConfigModule::ManagedConfigModule(QObject *parent, const QVariantList &args) + : ConfigModule(parent, args), + d(new ManagedConfigModulePrivate(this)) +{ +} + +ManagedConfigModule::~ManagedConfigModule() +{ + delete d; +} + +void ManagedConfigModule::load() +{ + for (const auto skeleton : qAsConst(d->_skeletons)) { + skeleton->load(); + } +} + +void ManagedConfigModule::save() +{ + for (const auto skeleton : qAsConst(d->_skeletons)) { + skeleton->save(); + } +} + +void ManagedConfigModule::defaults() +{ + for (const auto skeleton : qAsConst(d->_skeletons)) { + skeleton->setDefaults(); + } +} + +bool ManagedConfigModule::isSaveNeeded() const +{ + return false; +} + +bool ManagedConfigModule::isDefaults() const +{ + return true; +} + +void ManagedConfigModulePrivate::_k_registerSettings() +{ + auto settingsChangedSlotIndex = _q->metaObject()->indexOfMethod("_k_settingsChanged()"); + auto settingsChangedSlot = _q->metaObject()->method(settingsChangedSlotIndex); + + _skeletons = _q->findChildren(); + for (auto skeleton : qAsConst(_skeletons)) { + QObject::connect(skeleton, SIGNAL(configChanged()), _q, SLOT(_k_settingsChanged())); + + const auto items = skeleton->items(); + for (auto item : items) { + auto signallingItem = dynamic_cast(item); + if (!signallingItem) { + continue; + } + + const auto metaObject = skeleton->metaObject(); + const auto propertyIndex = metaObject->indexOfProperty(signallingItem->name().toUtf8().constData()); + const auto property = skeleton->metaObject()->property(propertyIndex); + if (!property.hasNotifySignal()) { + continue; + } + + const auto changedSignal = property.notifySignal(); + QObject::connect(skeleton, changedSignal, _q, settingsChangedSlot); + } + } +} + +void ManagedConfigModulePrivate::_k_settingsChanged() +{ + bool needsSave = false; + for (const auto skeleton : qAsConst(_skeletons)) { + needsSave |= skeleton->isSaveNeeded(); + if (needsSave) { + break; + } + } + if (!needsSave) { + needsSave = _q->isSaveNeeded(); + } + _q->setNeedsSave(needsSave); + + // TODO: Also deal with defaults, once we got an equivalent to setNeedsSave + // in ConfigModule +} + +} + +#include "moc_managedconfigmodule.cpp" +