| 1 | /* |
| 2 | SPDX-FileCopyrightText: 1999 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> |
| 3 | SPDX-FileCopyrightText: 2001 Michael Goffioul <kdeprint@swing.be> |
| 4 | SPDX-FileCopyrightText: 2004 Frans Englich <frans.englich@telia.com> |
| 5 | SPDX-FileCopyrightText: 2009 Dario Freddi <drf@kde.org> |
| 6 | SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org> |
| 7 | SPDX-FileCopyrightText: 2023 Alexander Lohnau <alexander.lohnau@gmx.de> |
| 8 | |
| 9 | SPDX-License-Identifier: LGPL-2.0-or-later |
| 10 | */ |
| 11 | |
| 12 | #ifndef KQUICKCONFIGMODULE_H |
| 13 | #define KQUICKCONFIGMODULE_H |
| 14 | |
| 15 | #include "kcmutilsquick_export.h" |
| 16 | |
| 17 | #include <QObject> |
| 18 | #include <QQmlComponent> |
| 19 | #include <QStringList> |
| 20 | #include <QVariant> |
| 21 | |
| 22 | #include <KPluginFactory> |
| 23 | #include <KPluginMetaData> |
| 24 | |
| 25 | #include <memory> |
| 26 | #include <qqmlintegration.h> |
| 27 | |
| 28 | #include "kabstractconfigmodule.h" |
| 29 | #include "kquickconfigmoduleloader.h" |
| 30 | |
| 31 | class QQuickItem; |
| 32 | class QQmlEngine; |
| 33 | class KQuickConfigModulePrivate; |
| 34 | |
| 35 | /*! |
| 36 | * \class KQuickConfigModule |
| 37 | * \inmodule KCMUtilsQuick |
| 38 | * \brief The base class for QtQuick configuration modules. |
| 39 | * |
| 40 | * Configuration modules are realized as plugins that are dynamically loaded. |
| 41 | * |
| 42 | * All the necessary glue logic and the GUI bells and whistles |
| 43 | * are provided by the control center and must not concern |
| 44 | * the module author. |
| 45 | * |
| 46 | * To write a config module, you have to create a C++ plugin |
| 47 | * and an accompanying QML user interface. |
| 48 | * |
| 49 | * To allow KCMUtils to load your ConfigModule subclass, you must create a KPluginFactory implementation. |
| 50 | * |
| 51 | * \code |
| 52 | * #include <KPluginFactory> |
| 53 | * |
| 54 | * K_PLUGIN_CLASS_WITH_JSON(MyConfigModule, "yourmetadata.json") |
| 55 | * \endcode |
| 56 | * |
| 57 | * The constructor of the ConfigModule then looks like this: |
| 58 | * \code |
| 59 | * YourConfigModule::YourConfigModule(QObject *parent, const KPluginMetaData &metaData) |
| 60 | * : KQuickConfigModule(parent, metaData) |
| 61 | * { |
| 62 | * } |
| 63 | * \endcode |
| 64 | * |
| 65 | * The QML part must be in the KPackage format, installed under share/kpackage/kcms. |
| 66 | * \sa KPackage::Package |
| 67 | * |
| 68 | * The package must have the same name as the plugin filename, to be installed |
| 69 | * by CMake with the command: |
| 70 | * \badcode |
| 71 | * kpackage_install_package(packagedir kcm_yourconfigmodule kcms) |
| 72 | * \endcode |
| 73 | * The "packagedir" is the subdirectory in the source tree where the package sources are |
| 74 | * located, and "kcm_yourconfigmodule" is id of the plugin. |
| 75 | * Finally "kcms" is the literal string "kcms", so that the package is |
| 76 | * installed as a configuration module (and not some other kind of package). |
| 77 | * |
| 78 | * The QML part can access all the properties of ConfigModule (together with the properties |
| 79 | * defined in its subclass) by accessing to the global object "kcm", or with the |
| 80 | * import of "org.kde.kcmutils" the ConfigModule attached property. |
| 81 | * |
| 82 | * \qml |
| 83 | * import QtQuick |
| 84 | * import QtQuick.Controls as QQC2 |
| 85 | * import org.kde.kcmutils as KCMUtils |
| 86 | * import org.kde.kirigami as Kirigami |
| 87 | * |
| 88 | * Item { |
| 89 | * // implicit size will be used as initial size when loaded in kcmshell6 |
| 90 | * implicitWidth: Kirigami.Units.gridUnit * 30 |
| 91 | * implicitHeight: Kirigami.Units.gridUnit * 30 |
| 92 | * |
| 93 | * KCMUtils.ConfigModule.buttons: KCMUtils.ConfigModule.Help | KCMUtils.ConfigModule.Apply |
| 94 | * |
| 95 | * QQC2.Label { |
| 96 | * // The following two bindings are equivalent: |
| 97 | * text: kcm.needsSave |
| 98 | * enabled: KCMUtils.ConfigModule.needsSave |
| 99 | * } |
| 100 | * } |
| 101 | * \endqml |
| 102 | * |
| 103 | * See https://develop.kde.org/docs/features/configuration/kcm/ for more detailed documentation. |
| 104 | * \since 6.0 |
| 105 | */ |
| 106 | class KCMUTILSQUICK_EXPORT KQuickConfigModule : public KAbstractConfigModule |
| 107 | { |
| 108 | Q_OBJECT |
| 109 | |
| 110 | /*! \property KQuickConfigModule::mainUi */ |
| 111 | Q_PROPERTY(QQuickItem *mainUi READ mainUi CONSTANT) |
| 112 | /*! \property KQuickConfigModule::columnWidth */ |
| 113 | Q_PROPERTY(int columnWidth READ columnWidth WRITE setColumnWidth NOTIFY columnWidthChanged) |
| 114 | /*! \property KQuickConfigModule::depth */ |
| 115 | Q_PROPERTY(int depth READ depth NOTIFY depthChanged) |
| 116 | /*! \property KQuickConfigModule::currentIndex */ |
| 117 | Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) |
| 118 | |
| 119 | QML_NAMED_ELEMENT(ConfigModule) |
| 120 | QML_ATTACHED(KQuickConfigModule) |
| 121 | |
| 122 | public: |
| 123 | ~KQuickConfigModule() override; |
| 124 | |
| 125 | /*! |
| 126 | * \brief Returns the qml engine that built the main config UI. |
| 127 | */ |
| 128 | std::shared_ptr<QQmlEngine> engine() const; |
| 129 | |
| 130 | /*! |
| 131 | * \brief The error string in case the mainUi failed to load. |
| 132 | */ |
| 133 | QString errorString() const; |
| 134 | |
| 135 | // QML property accessors |
| 136 | |
| 137 | /*! |
| 138 | * \brief Returns the main UI for this configuration module. |
| 139 | * |
| 140 | * It's a QQuickItem coming from the QML package named |
| 141 | * the same as the KAboutData's component name for this config module. |
| 142 | * |
| 143 | * Normally, ui/main.qml will be loaded from the qrc baked into the plugin. However, |
| 144 | * if the PLASMA_PLATFORM environmental variable is set, the module will try to load |
| 145 | * a platform-specific QML file as its mainUi starting point. |
| 146 | * |
| 147 | * For example: |
| 148 | * |
| 149 | * environment has set |
| 150 | * \c {export PLASMA_PLATFORM=phone:handset} |
| 151 | * |
| 152 | * The module will try to find main_phone.qml, then main_handset.qml, then main.qml. |
| 153 | * The first file that is found will be used as mainUi. If none is found and main.qml |
| 154 | * doesn't exist, the module is broken and an error will be displayed. |
| 155 | */ |
| 156 | QQuickItem *mainUi(); |
| 157 | |
| 158 | /*! |
| 159 | * \brief Returns a subpage at a given depth \a index. |
| 160 | * \note This does not include the mainUi. That is, a depth of 2 |
| 161 | * is a mainUi and one subPage at index 0. |
| 162 | */ |
| 163 | QQuickItem *subPage(int index) const; |
| 164 | |
| 165 | /*! |
| 166 | * \brief Returns the width the kcm wants in column mode. |
| 167 | * |
| 168 | * If a columnWidth is valid ( > 0 ) and less than the systemsettings' view width, |
| 169 | * more than one will be visible at once, and the first page will be a sidebar to the last page pushed. |
| 170 | * As default, this is -1 which will make the shell always show only one page at a time. |
| 171 | */ |
| 172 | int columnWidth() const; |
| 173 | |
| 174 | /*! |
| 175 | * \brief Sets the column \a width we want. |
| 176 | */ |
| 177 | void setColumnWidth(int width); |
| 178 | |
| 179 | /*! |
| 180 | * \brief Returns how many pages this kcm has. |
| 181 | * |
| 182 | * It is guaranteed to be at least 1 (the main ui) plus how many times a new page has been pushed without pop. |
| 183 | */ |
| 184 | int depth() const; |
| 185 | |
| 186 | /*! |
| 187 | * \brief Sets the current page \a index this KCM should display. |
| 188 | */ |
| 189 | void setCurrentIndex(int index); |
| 190 | |
| 191 | /*! |
| 192 | * \brief Returns the index of the page this KCM should display. |
| 193 | */ |
| 194 | int currentIndex() const; |
| 195 | |
| 196 | static KQuickConfigModule *qmlAttachedProperties(QObject *object); |
| 197 | |
| 198 | public Q_SLOTS: |
| 199 | /*! |
| 200 | * \brief Push a new sub page with the given \a fileName |
| 201 | * with optional \a initialProperties in the KCM hierarchy: |
| 202 | * pages will be seen as a Kirigami PageRow. |
| 203 | */ |
| 204 | void push(const QString &fileName, const QVariantMap &initialProperties = QVariantMap()); |
| 205 | |
| 206 | /*! |
| 207 | * \brief Push a given QQuick \a item as a new sub page in the KCM hierarchy. |
| 208 | * \overload KQuickConfigModule::push() |
| 209 | */ |
| 210 | void push(QQuickItem *item); |
| 211 | |
| 212 | /*! |
| 213 | * \brief Pop the last page of the KCM hierarchy, the page is destroyed. |
| 214 | */ |
| 215 | void pop(); |
| 216 | |
| 217 | /*! |
| 218 | * \brief Remove and return the last page of the KCM hierarchy. |
| 219 | * |
| 220 | * The popped page won't be deleted, it's the caller's responsibility |
| 221 | * to manage the lifetime of the returned item. |
| 222 | * |
| 223 | * Returns the last page if any, \c nullptr otherwise. |
| 224 | */ |
| 225 | QQuickItem *takeLast(); |
| 226 | |
| 227 | Q_SIGNALS: |
| 228 | |
| 229 | // QML NOTIFY signaling |
| 230 | |
| 231 | /*! |
| 232 | * \brief Emitted when a new sub \a page is pushed. |
| 233 | */ |
| 234 | void pagePushed(QQuickItem *page); |
| 235 | |
| 236 | // RFC: page argument? |
| 237 | /*! |
| 238 | * \brief Emitted when a sub page is popped. |
| 239 | */ |
| 240 | void (); |
| 241 | |
| 242 | /*! |
| 243 | * \brief Emitted when the wanted column \a width of the KCM changes. |
| 244 | */ |
| 245 | void columnWidthChanged(int width); |
| 246 | |
| 247 | /*! |
| 248 | * \brief Emitted when the \a index of the current page changed. |
| 249 | */ |
| 250 | void currentIndexChanged(int index); |
| 251 | |
| 252 | /*! |
| 253 | * \brief Emitted when the number of pages changed, and so their \a index. |
| 254 | */ |
| 255 | void depthChanged(int index); |
| 256 | |
| 257 | /*! |
| 258 | * \brief Emitted when the main Ui has loaded successfully and mainUi() is available. |
| 259 | */ |
| 260 | void mainUiReady(); |
| 261 | |
| 262 | protected: |
| 263 | /*! |
| 264 | * \brief Base class for all QtQuick config modules. |
| 265 | * |
| 266 | * Creates a new KQuickConfigModule with the given \a metaData as a child of \a parent. |
| 267 | * Use KQuickConfigModuleLoader to instantiate this class. |
| 268 | * |
| 269 | * \note Do not emit changed signals here, since they are not yet connected to any slot. |
| 270 | */ |
| 271 | explicit KQuickConfigModule(QObject *parent, const KPluginMetaData &metaData); |
| 272 | |
| 273 | private: |
| 274 | void setInternalEngine(const std::shared_ptr<QQmlEngine> &engine); |
| 275 | friend KPluginFactory::Result<KQuickConfigModule> |
| 276 | KQuickConfigModuleLoader::loadModule(const KPluginMetaData &metaData, QObject *parent, const QVariantList &args, const std::shared_ptr<QQmlEngine> &engine); |
| 277 | const std::unique_ptr<KQuickConfigModulePrivate> d; |
| 278 | }; |
| 279 | |
| 280 | #endif // KQUICKCONFIGMODULE_H |
| 281 | |