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
31class QQuickItem;
32class QQmlEngine;
33class 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 */
106class 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
122public:
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
198public 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
227Q_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 pageRemoved();
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
262protected:
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
273private:
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

source code of kcmutils/src/quick/kquickconfigmodule.h