KCMUtils

kquickconfigmodule.cpp
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#include "kquickconfigmodule.h"
13#include "kabstractconfigmodule.h"
14#include "kcmutils_debug.h"
15#include "sharedqmlengine_p.h"
16
17#include <QDebug>
18#include <QQmlContext>
19#include <QQmlEngine>
20#include <QQmlFileSelector>
21#include <QQuickItem>
22#include <QResource>
23#include <QUrl>
24
25#include <KRuntimePlatform>
26#include <KLocalizedContext>
27#include <KLocalizedString>
28
29#include <memory>
30
31class KQuickConfigModulePrivate
32{
33public:
34 KQuickConfigModulePrivate(KQuickConfigModule *module)
35 : q(module)
36 {
37 }
38
39 KQuickConfigModule *q;
40 SharedQmlEngine *engine = nullptr;
41 std::shared_ptr<QQmlEngine> passedInEngine;
42 QList<QQuickItem *> subPages;
43 int columnWidth = -1;
44 int currentIndex = 0;
45 QString errorString;
46
47 static QHash<QQmlContext *, KQuickConfigModule *> rootObjects;
48
49 QString getResourcePath(const QString &file)
50 {
51 return QLatin1String("/kcm/") + q->metaData().pluginId() + QLatin1String("/") + file;
52 }
53 QUrl getResourceUrl(const QString &resourcePath)
54 {
55 return QUrl(QLatin1String("qrc:") + resourcePath);
56 }
57};
58
60
62 : KAbstractConfigModule(parent, metaData)
63 , d(new KQuickConfigModulePrivate(this))
64{
65}
66
67void KQuickConfigModule::setInternalEngine(const std::shared_ptr<QQmlEngine> &engine)
68{
69 d->passedInEngine = engine;
70}
71
73{
74 // in case mainUi was never called
75 if (d->engine) {
76 // delete the mainUi before removing the root object.
77 // Otherwise, we get lots of console errors about trying to read properties of null objects
78 delete d->engine->rootObject();
79 KQuickConfigModulePrivate::rootObjects.remove(d->engine->rootContext());
80 }
81}
82
83KQuickConfigModule *KQuickConfigModule::qmlAttachedProperties(QObject *object)
84{
85 // at the moment of the attached object creation, the root item is the only one that hasn't a parent
86 // only way to avoid creation of this attached for everybody but the root item
87 const QQmlEngine *engine = qmlEngine(object);
88 QQmlContext *ctx = qmlContext(object);
89
90 // Search the qml context that is the "root" for the sharedqmlobject,
91 // which is an ancestor of qmlContext(object) and the direct child of the
92 // engine's root context: we can do this assumption on the internals as
93 // we are distributed on the same repo.
94 while (ctx->parentContext() && ctx->parentContext() != engine->rootContext()) {
95 ctx = ctx->parentContext();
96 }
97
98 if (!object->parent() && KQuickConfigModulePrivate::rootObjects.contains(ctx)) {
99 return KQuickConfigModulePrivate::rootObjects.value(ctx);
100 } else {
101 return nullptr;
102 }
103}
104
105QQuickItem *KQuickConfigModule::mainUi()
106{
107 Q_ASSERT(d->passedInEngine);
108 if (d->engine) {
109 return qobject_cast<QQuickItem *>(d->engine->rootObject());
110 }
111
112 d->errorString.clear();
113 d->engine = new SharedQmlEngine(d->passedInEngine, this);
114
115 const QString componentName = metaData().pluginId();
116 KQuickConfigModulePrivate::rootObjects[d->engine->rootContext()] = this;
117 d->engine->setTranslationDomain(componentName);
118 d->engine->setInitializationDelayed(true);
119
120 QString resourcePath = d->getResourcePath(QStringLiteral("main.qml"));
121
122 qCDebug(KCMUTILS_LOG) << "Current platform is " << KRuntimePlatform::runtimePlatform();
123
124 const auto platforms = KRuntimePlatform::runtimePlatform();
125 for (const QString &platform : platforms) {
126 const QString platformResourcePath = d->getResourcePath(QString(QStringLiteral("main_%1.qml")).arg(platform));
127 if (QResource r(platformResourcePath); r.isValid()) {
128 qCDebug(KCMUTILS_LOG) << "Found platform-specific QML main file at" << platformResourcePath;
129 resourcePath = std::move(platformResourcePath);
130 break;
131 }
132 }
133
134 if (QResource r(resourcePath); !r.isValid()) {
135 d->errorString = i18n("Could not find resource '%1'", resourcePath);
136 qCWarning(KCMUTILS_LOG) << "Could not find resource" << resourcePath;
137 return nullptr;
138 }
139
140 new QQmlFileSelector(d->engine->engine().get(), this);
141 d->engine->setSource(d->getResourceUrl(resourcePath));
142 d->engine->rootContext()->setContextProperty(QStringLiteral("kcm"), this);
143 d->engine->completeInitialization();
144
145 if (d->engine->isError()) {
146 d->errorString = d->engine->errorString();
147 return nullptr;
148 }
149
151
152 return qobject_cast<QQuickItem *>(d->engine->rootObject());
153}
154
155void KQuickConfigModule::push(const QString &fileName, const QVariantMap &initialProperties)
156{
157 // ensure main ui is created
158 if (!mainUi()) {
159 return;
160 }
161
162 const QString resourcePath = d->getResourcePath(fileName);
163 if (QResource r(resourcePath); !r.isValid()) {
164 qCWarning(KCMUTILS_LOG) << "Requested resource" << resourcePath << "does not exist";
165 }
166 QObject *object = d->engine->createObjectFromSource(d->getResourceUrl(resourcePath), d->engine->rootContext(), initialProperties);
167
169 if (!item) {
170 if (object) {
171 object->deleteLater();
172 }
173 return;
174 }
175
176 d->subPages << item;
177 Q_EMIT pagePushed(item);
178 Q_EMIT depthChanged(depth());
179 setCurrentIndex(d->currentIndex + 1);
180}
181
183{
184 // ensure main ui is created
185 if (!mainUi()) {
186 return;
187 }
188
189 d->subPages << item;
190 Q_EMIT pagePushed(item);
191 Q_EMIT depthChanged(depth());
192 setCurrentIndex(d->currentIndex + 1);
193}
194
196{
197 if (QQuickItem *page = takeLast()) {
198 page->deleteLater();
199 }
200}
201
203{
204 if (d->subPages.isEmpty()) {
205 return nullptr;
206 }
207 QQuickItem *page = d->subPages.takeLast();
209 Q_EMIT depthChanged(depth());
210 setCurrentIndex(qMin(d->currentIndex, depth() - 1));
211 return page;
212}
213
214int KQuickConfigModule::columnWidth() const
215{
216 return d->columnWidth;
217}
218
220{
221 if (d->columnWidth == width) {
222 return;
223 }
224
225 d->columnWidth = width;
227}
228
229int KQuickConfigModule::depth() const
230{
231 return d->subPages.count() + 1;
232}
233
235{
236 if (index < 0 || index > d->subPages.count() || index == d->currentIndex) {
237 return;
238 }
239
240 d->currentIndex = index;
241
243}
244
245int KQuickConfigModule::currentIndex() const
246{
247 return d->currentIndex;
248}
249
250std::shared_ptr<QQmlEngine> KQuickConfigModule::engine() const
251{
252 return d->engine->engine();
253}
254
256{
257 return d->errorString;
258}
259
260QQuickItem *KQuickConfigModule::subPage(int index) const
261{
262 return d->subPages[index];
263}
264
265#include "moc_kquickconfigmodule.cpp"
KPluginMetaData metaData() const
Returns the metaData that was used when instantiating the plugin.
QString pluginId() const
The base class for QtQuick configuration modules.
void mainUiReady()
Emitted when the main Ui has loaded successfully and mainUi() is available.
void setColumnWidth(int width)
Sets the column width we want.
void columnWidthChanged(int width)
Emitted when the wanted column width of the kcm changes.
KQuickConfigModule(QObject *parent, const KPluginMetaData &metaData)
Base class for all QtQuick config modules.
void depthChanged(int index)
Emitted when the number of pages changed.
void pop()
pop the last page of the KCM hierarchy, the page is destroyed
void setCurrentIndex(int index)
Sets the current page index this kcm should display.
void currentIndexChanged(int index)
Emitted when the current page changed.
void pagePushed(QQuickItem *page)
Emitted when a new sub page is pushed.
QString errorString() const
The error string in case the mainUi failed to load.
~KQuickConfigModule() override
Destroys the module.
std::shared_ptr< QQmlEngine > engine() const
void pageRemoved()
Emitted when a sub page is popped.
void push(const QString &fileName, const QVariantMap &initialProperties=QVariantMap())
Push a new sub page in the KCM hierarchy: pages will be seen as a Kirigami PageRow.
QQuickItem * takeLast()
remove and return the last page of the KCM hierarchy: the popped page won't be deleted,...
QString i18n(const char *text, const TYPE &arg...)
KCOREADDONS_EXPORT QStringList runtimePlatform()
T value(const Key &key) const const
QObject(QObject *parent)
Q_EMITQ_EMIT
QObject * parent() const const
T qobject_cast(QObject *object)
QQmlContext * parentContext() const const
bool isValid() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Jan 24 2025 11:51:31 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.