Libplasma

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.h"
9#include "appletcontext_p.h"
10
11#include <KLocalizedContext>
12#include <QDebug>
13#include <QQmlContext>
14#include <QQmlEngine>
15#include <QQmlNetworkAccessManagerFactory>
16#include <QQuickItem>
17#include <QTimer>
18
19#include <Plasma/Applet>
20
21#include "debug_p.h"
22
23#include <KLocalizedQmlContext>
24
25namespace PlasmaQuick
26{
27
28class SharedQmlEnginePrivate
29{
30public:
31 SharedQmlEnginePrivate(SharedQmlEngine *parent)
32 : q(parent)
33 , component(nullptr)
34 , delay(false)
35 , m_engine(engine())
36 {
37 executionEndTimer = new QTimer(q);
38 executionEndTimer->setInterval(0);
39 executionEndTimer->setSingleShot(true);
40 QObject::connect(executionEndTimer, &QTimer::timeout, q, [this]() {
41 scheduleExecutionEnd();
42 });
43 }
44
45 ~SharedQmlEnginePrivate() = default;
46
47 void errorPrint(QQmlComponent *component);
48 void beginExecute(const QUrl &source);
49 void beginExecute(QAnyStringView module, QAnyStringView type);
50 void endExecute();
51 void scheduleExecutionEnd();
52 void minimumWidthChanged();
53 void minimumHeightChanged();
54 void maximumWidthChanged();
55 void maximumHeightChanged();
56 void preferredWidthChanged();
57 void preferredHeightChanged();
58
59 SharedQmlEngine *q;
60
61 QPointer<QObject> rootObject;
62 std::unique_ptr<QQmlComponent> component;
63 QTimer *executionEndTimer;
64 KLocalizedQmlContext *context{nullptr};
65 QQmlContext *rootContext;
66 bool delay;
67 std::shared_ptr<QQmlEngine> m_engine;
68
69private:
70 static std::shared_ptr<QQmlEngine> engine()
71 {
72 if (auto locked = s_engine.lock()) {
73 return locked;
74 }
75 auto createdEngine = std::make_shared<QQmlEngine>();
76 s_engine = createdEngine;
77 return createdEngine;
78 }
79
80 static std::weak_ptr<QQmlEngine> s_engine;
81};
82
83std::weak_ptr<QQmlEngine> SharedQmlEnginePrivate::s_engine = {};
84
85void SharedQmlEnginePrivate::errorPrint(QQmlComponent *component)
86{
87 QString errorStr = QStringLiteral("Error loading QML file.\n");
88 if (component->isError()) {
89 const QList<QQmlError> errors = component->errors();
90 for (const QQmlError &error : errors) {
91 errorStr +=
92 (error.line() > 0 ? QString(QString::number(error.line()) + QLatin1String(": ")) : QLatin1String("")) + error.description() + QLatin1Char('\n');
93 }
94 }
95 qWarning(LOG_PLASMAQUICK) << component->url().toString() << '\n' << errorStr;
96}
97
98void SharedQmlEnginePrivate::beginExecute(const QUrl &source)
99{
100 if (source.isEmpty()) {
101 qWarning(LOG_PLASMAQUICK) << "File name empty!";
102 }
103
104 component = std::make_unique<QQmlComponent>(m_engine.get());
105 // Important! Some parts of Plasma are extremely sensitive to status changed
106 // signal being emit in exactly the same way QQmlComponent does it. So this
107 // connection needs to happen before any loading of the component happens.
108 QObject::connect(component.get(), &QQmlComponent::statusChanged, q, &SharedQmlEngine::statusChanged, Qt::QueuedConnection);
109 component->loadUrl(source);
110
111 endExecute();
112}
113
114void SharedQmlEnginePrivate::beginExecute(QAnyStringView module, QAnyStringView type)
115{
116 if (module.isEmpty() || type.isEmpty()) {
117 qWarning(LOG_PLASMAQUICK) << "No module or type specified";
118 return;
119 }
120
121 component = std::make_unique<QQmlComponent>(m_engine.get());
122 // Important! Some parts of Plasma are extremely sensitive to status changed
123 // signal being emit in exactly the same way QQmlComponent does it. So this
124 // connection needs to happen before any loading of the component happens.
125 QObject::connect(component.get(), &QQmlComponent::statusChanged, q, &SharedQmlEngine::statusChanged, Qt::QueuedConnection);
126 component->loadFromModule(module, type);
127
128 endExecute();
129}
130
131void SharedQmlEnginePrivate::endExecute()
132{
133 rootObject = component->beginCreate(rootContext);
134
135 if (delay) {
136 executionEndTimer->start(0);
137 } else {
138 scheduleExecutionEnd();
139 }
140}
141
142void SharedQmlEnginePrivate::scheduleExecutionEnd()
143{
144 if (component->isReady() || component->isError()) {
145 q->completeInitialization();
146 } else {
147 QObject::connect(component.get(), &QQmlComponent::statusChanged, q, [this]() {
148 q->completeInitialization();
149 });
150 }
151}
152
154 : QObject(parent)
155 , d(new SharedQmlEnginePrivate(this))
156{
157 d->rootContext = new QQmlContext(engine().get());
158 d->rootContext->setParent(this); // Delete the context when deleting the shared engine
159
160 d->context = new KLocalizedQmlContext(d->rootContext);
161 d->rootContext->setContextObject(d->context);
162}
163
165 : QObject(parent)
166 , d(new SharedQmlEnginePrivate(this))
167{
168 d->rootContext = new AppletContext(engine().get(), applet, this);
169
170 d->context = new KLocalizedQmlContext(d->rootContext);
171 d->rootContext->setContextObject(d->context);
172}
173
174SharedQmlEngine::~SharedQmlEngine()
175{
177 delete d->rootObject;
178 }
179}
180
181void SharedQmlEngine::setTranslationDomain(const QString &translationDomain)
182{
183 d->context->setTranslationDomain(translationDomain);
184}
185
186QString SharedQmlEngine::translationDomain() const
187{
188 return d->context->translationDomain();
189}
190
192{
193 d->beginExecute(source);
194}
195
197{
198 d->beginExecute(module, type);
199}
200
201QUrl SharedQmlEngine::source() const
202{
203 if (d->component) {
204 return d->component->url();
205 }
206 return QUrl{};
207}
208
210{
211 d->delay = delay;
212}
213
215{
216 return d->delay;
217}
218
219std::shared_ptr<QQmlEngine> SharedQmlEngine::engine()
220{
221 return d->m_engine;
222}
223
224QObject *SharedQmlEngine::rootObject() const
225{
226 return d->rootObject;
227}
228
230{
231 return d->component.get();
232}
233
235{
236 return d->rootContext;
237}
238
239QQmlComponent::Status SharedQmlEngine::status() const
240{
241 if (!d->m_engine) {
243 }
244
245 if (!d->component) {
246 return QQmlComponent::Null;
247 }
248
249 return QQmlComponent::Status(d->component->status());
250}
251
252void SharedQmlEngine::completeInitialization(const QVariantHash &initialProperties)
253{
254 d->executionEndTimer->stop();
255
256 if (!d->component) {
257 qWarning(LOG_PLASMAQUICK) << "No component for" << source();
258 return;
259 }
260
261 if (d->component->status() != QQmlComponent::Ready || d->component->isError()) {
262 d->errorPrint(d->component.get());
263 return;
264 }
265
266 for (auto it = initialProperties.constBegin(); it != initialProperties.constEnd(); ++it) {
267 d->rootObject->setProperty(it.key().toUtf8().data(), it.value());
268 }
269
270 d->component->completeCreate();
272}
273
274QObject *SharedQmlEngine::createObjectFromSource(const QUrl &source, QQmlContext *context, const QVariantHash &initialProperties)
275{
276 QQmlComponent *component = new QQmlComponent(d->m_engine.get(), this);
277 component->loadUrl(source);
278
279 return createObjectFromComponent(component, context, initialProperties);
280}
281
282QObject *SharedQmlEngine::createObjectFromComponent(QQmlComponent *component, QQmlContext *context, const QVariantHash &initialProperties)
283{
284 QObject *object = component->beginCreate(context ? context : d->rootContext);
285
286 for (auto it = initialProperties.constBegin(); it != initialProperties.constEnd(); ++it) {
287 object->setProperty(it.key().toUtf8().data(), it.value());
288 }
289 component->completeCreate();
290
291 if (!component->isError() && object) {
292 // memory management
293 component->setParent(object);
294 // reparent to root object if wasn't specified otherwise by initialProperties
295 if (!initialProperties.contains(QLatin1String("parent"))) {
296 if (qobject_cast<QQuickItem *>(rootObject())) {
297 object->setProperty("parent", QVariant::fromValue(rootObject()));
298 } else {
299 object->setParent(rootObject());
300 }
301 }
302
303 return object;
304
305 } else {
306 d->errorPrint(component);
307 delete object;
308 return nullptr;
309 }
310}
311}
312
313#include "moc_sharedqmlengine.cpp"
SharedQmlEngine(QObject *parent=nullptr)
Construct a new PlasmaQuick::SharedQmlEngine.
QQmlContext * rootContext() const
The components's creation context.
void setInitializationDelayed(const bool delay)
Sets whether the execution of the QML file has to be delayed later in the event loop.
void setSource(const QUrl &source)
Sets the path of the QML file to parse and execute.
void finished()
Emitted when the parsing and execution of the QML file is terminated.
QQmlComponent * mainComponent() const
void setTranslationDomain(const QString &translationDomain)
Call this method before calling setupBindings to install a translation domain for all i18n global fun...
QObject * createObjectFromSource(const QUrl &source, QQmlContext *context=nullptr, const QVariantHash &initialProperties=QVariantHash())
Creates and returns an object based on the provided url to a Qml file with the same QQmlEngine and th...
void completeInitialization(const QVariantHash &initialProperties=QVariantHash())
Finishes the process of initialization.
void setSourceFromModule(QAnyStringView module, QAnyStringView type)
Sets the QML source to execute from a type in a module.
std::shared_ptr< QQmlEngine > engine()
QObject * createObjectFromComponent(QQmlComponent *component, QQmlContext *context=nullptr, const QVariantHash &initialProperties=QVariantHash())
Creates and returns an object based on the provided QQmlComponent with the same QQmlEngine and the sa...
The base Applet class.
Definition applet.h:64
Type type(const QSqlDatabase &db)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
The EdgeEventForwarder class This class forwards edge events to be replayed within the given margin T...
Definition action.h:20
bool isEmpty() const const
ObjectOwnership objectOwnership(QObject *object)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
T qobject_cast(QObject *object)
void setParent(QObject *parent)
virtual QObject * beginCreate(QQmlContext *context)
virtual void completeCreate()
QList< QQmlError > errors() const const
bool isError() const const
bool isReady() const const
void loadFromModule(QAnyStringView uri, QAnyStringView typeName, QQmlComponent::CompilationMode mode)
void loadUrl(const QUrl &url)
void statusChanged(QQmlComponent::Status status)
QString number(double n, char format, int precision)
QueuedConnection
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 Sat Dec 21 2024 17:01:35 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.