KCMUtils

sharedqmlengine.cpp
1/*
2 SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
3 SPDX-FileCopyrightText: 2023 Alexander Lohnau <alexander.lohnau@gmx.de>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "sharedqmlengine_p.h"
9
10#if KI18N_VERSION >= QT_VERSION_CHECK(6, 8, 0)
11#include <KLocalizedQmlContext>
12#else
13#include <KLocalizedContext>
14#endif
15#include <QDebug>
16#include <QQmlContext>
17#include <QQmlEngine>
18#include <QQmlIncubator>
19#include <QQmlNetworkAccessManagerFactory>
20#include <QQuickItem>
21#include <QResource>
22#include <QTimer>
23
24#include "kcmutils_debug.h"
25
26using namespace Qt::StringLiterals;
27
28class SharedQmlEnginePrivate
29{
30public:
31 SharedQmlEnginePrivate(const std::shared_ptr<QQmlEngine> &engine, SharedQmlEngine *parent)
32 : q(parent)
33 , component(nullptr)
34 , delay(false)
35 , m_engine(engine)
36 {
37 executionEndTimer.setInterval(0);
38 executionEndTimer.setSingleShot(true);
39 QObject::connect(&executionEndTimer, &QTimer::timeout, q, [this]() {
40 scheduleExecutionEnd();
41 });
42 }
43
44 ~SharedQmlEnginePrivate()
45 {
46 delete incubator.object();
47 }
48
49 void errorPrint(QQmlComponent *component, QQmlIncubator *incubator = nullptr);
50 void execute(const QUrl &source);
51 void scheduleExecutionEnd();
52 void minimumWidthChanged();
53 void minimumHeightChanged();
54 void maximumWidthChanged();
55 void maximumHeightChanged();
56 void preferredWidthChanged();
57 void preferredHeightChanged();
58 void checkInitializationCompleted();
59
60 SharedQmlEngine *q;
61
62 QUrl source;
63
64 QQmlIncubator incubator;
65 QQmlComponent *component;
66 QTimer executionEndTimer;
67#if KI18N_VERSION >= QT_VERSION_CHECK(6, 8, 0)
68 KLocalizedQmlContext *context{nullptr};
69#else
70 KLocalizedContext *context{nullptr};
71#endif
72 QQmlContext *rootContext;
73 bool delay;
74 std::shared_ptr<QQmlEngine> m_engine;
75};
76
77void SharedQmlEnginePrivate::errorPrint(QQmlComponent *component, QQmlIncubator *incubator)
78{
79 QList<QQmlError> errors;
80 if (component && component->isError()) {
81 errors = component->errors();
82 } else if (incubator && incubator->isError()) {
83 errors = incubator->errors();
84 } else {
85 return;
86 }
87
88 qCWarning(KCMUTILS_LOG).noquote() << "Error loading QML file" << component->url().toString();
89 for (const auto &error : errors) {
90 constexpr const QLatin1String indent(" ");
91 qCWarning(KCMUTILS_LOG).noquote().nospace() << indent << error;
92 }
93}
94
95void SharedQmlEnginePrivate::execute(const QUrl &source)
96{
97 Q_ASSERT(!source.isEmpty());
98 delete component;
99 component = new QQmlComponent(m_engine.get(), q);
100 delete incubator.object();
101
102 m_engine->addImportPath(QStringLiteral("qrc:/"));
103 component->loadUrl(source);
104
105 if (delay) {
106 executionEndTimer.start(0);
107 } else {
108 scheduleExecutionEnd();
109 }
110}
111
112void SharedQmlEnginePrivate::scheduleExecutionEnd()
113{
114 if (component->isReady() || component->isError()) {
115 q->completeInitialization();
116 } else {
117 QObject::connect(component, &QQmlComponent::statusChanged, q, [this]() {
118 q->completeInitialization();
119 });
120 }
121}
122
123SharedQmlEngine::SharedQmlEngine(const std::shared_ptr<QQmlEngine> &engine, QObject *parent)
124 : QObject(parent)
125 , d(new SharedQmlEnginePrivate(engine, this))
126{
127 d->rootContext = new QQmlContext(engine.get());
128 d->rootContext->setParent(this); // Delete the context when deleting the shared engine
129
130#if KI18N_VERSION < QT_VERSION_CHECK(6, 8, 0)
131 d->context = new KLocalizedContext(d->rootContext);
132#else
133 d->context = new KLocalizedQmlContext(d->rootContext);
134#endif
135 d->rootContext->setContextObject(d->context);
136}
137
138SharedQmlEngine::~SharedQmlEngine() = default;
139
140void SharedQmlEngine::setTranslationDomain(const QString &translationDomain)
141{
142 d->context->setTranslationDomain(translationDomain);
143}
144
145QString SharedQmlEngine::translationDomain() const
146{
147 return d->context->translationDomain();
148}
149
150void SharedQmlEngine::setSource(const QUrl &source)
151{
152 d->source = source;
153 d->execute(source);
154}
155
156QUrl SharedQmlEngine::source() const
157{
158 return d->source;
159}
160
161void SharedQmlEngine::setInitializationDelayed(const bool delay)
162{
163 d->delay = delay;
164}
165
166bool SharedQmlEngine::isInitializationDelayed() const
167{
168 return d->delay;
169}
170
171std::shared_ptr<QQmlEngine> SharedQmlEngine::engine()
172{
173 return d->m_engine;
174}
175
176QObject *SharedQmlEngine::rootObject() const
177{
178 if (d->incubator.isLoading()) {
179 qCWarning(KCMUTILS_LOG) << "Trying to use rootObject before initialization is completed, whilst using setInitializationDelayed. Forcing completion";
180 d->incubator.forceCompletion();
181 }
182 return d->incubator.object();
183}
184
185QQmlComponent *SharedQmlEngine::mainComponent() const
186{
187 return d->component;
188}
189
190QQmlContext *SharedQmlEngine::rootContext() const
191{
192 return d->rootContext;
193}
194
195bool SharedQmlEngine::isError() const
196{
197 return !d->m_engine || !d->component || d->component->isError() || d->incubator.isError();
198}
199
200static QString qmlErrorsToString(const QList<QQmlError> &errors)
201{
202 QString ret;
203 for (const auto &e : errors) {
204 ret += e.url().toString() + QLatin1Char(':') + QString::number(e.line()) + QLatin1Char(' ') + e.description() + QLatin1Char('\n');
205 }
206 return ret;
207}
208
209QString SharedQmlEngine::errorString() const
210{
211 if (d->component && d->component->isError()) {
212 return d->component->errorString();
213 } else if (d->incubator.isError()) {
214 return qmlErrorsToString(d->incubator.errors());
215 } else {
216 return {};
217 }
218}
219
220void SharedQmlEnginePrivate::checkInitializationCompleted()
221{
222 if (!incubator.isReady() && !incubator.isError()) {
223 QTimer::singleShot(0, q, [this]() {
224 checkInitializationCompleted();
225 });
226 return;
227 }
228
229 if (!incubator.object()) {
230 errorPrint(component, &incubator);
231 }
232
233 Q_EMIT q->finished();
234}
235
236void SharedQmlEngine::completeInitialization(const QVariantMap &initialProperties)
237{
238 d->executionEndTimer.stop();
239 if (d->incubator.object()) {
240 return;
241 }
242
243 if (!d->component) {
244 qCWarning(KCMUTILS_LOG) << "No component for" << source();
245 return;
246 }
247
248 if (!d->component->isReady()) {
249 d->errorPrint(d->component);
250 return;
251 }
252
253 d->incubator.setInitialProperties(initialProperties);
254 d->component->create(d->incubator, d->rootContext);
255
256 if (d->delay) {
257 d->checkInitializationCompleted();
258 } else {
259 d->incubator.forceCompletion();
260
261 if (!d->incubator.object()) {
262 d->errorPrint(d->component, &d->incubator);
263 }
264 Q_EMIT finished();
265 }
266}
267
268QObject *SharedQmlEngine::createObjectFromSource(const QUrl &source, QQmlContext *context, const QVariantMap &initialProperties)
269{
270 QQmlComponent *component = new QQmlComponent(d->m_engine.get(), this);
271 component->loadUrl(source);
272
273 return createObjectFromComponent(component, context, initialProperties);
274}
275
276QObject *SharedQmlEngine::createObjectFromComponent(QQmlComponent *component, QQmlContext *context, const QVariantMap &initialProperties)
277{
278 QObject *object = component->createWithInitialProperties(initialProperties, context ? context : d->rootContext);
279
280 if (!component->isError() && object) {
281 // memory management
282 const auto root = rootObject();
283 object->setParent(root);
284 component->setParent(object);
285
286 // visually reparent to root object if wasn't specified otherwise by initialProperties
287 if (!initialProperties.contains(QLatin1String("parent")) && root && root->isQuickItemType()) {
288 object->setProperty("parent", QVariant::fromValue(root));
289 }
290 return object;
291 } else {
292 d->errorPrint(component);
293 delete object;
294 return nullptr;
295 }
296}
297
298#include "moc_sharedqmlengine_p.cpp"
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void setParent(QObject *parent)
QObject * createWithInitialProperties(const QVariantMap &initialProperties, QQmlContext *context)
QList< QQmlError > errors() const const
bool isError() const const
bool isReady() const const
void loadUrl(const QUrl &url)
void statusChanged(QQmlComponent::Status status)
QList< QQmlError > errors() const const
bool isError() const const
bool isReady() const const
QObject * object() const const
QString number(double n, char format, int precision)
void setInterval(int msec)
void setSingleShot(bool singleShot)
void start()
void timeout()
bool isEmpty() const const
QVariant fromValue(T &&value)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:15:20 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.