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
23namespace PlasmaQuick
24{
25
26class SharedQmlEnginePrivate
27{
28public:
29 SharedQmlEnginePrivate(SharedQmlEngine *parent)
30 : q(parent)
31 , component(nullptr)
32 , delay(false)
33 , m_engine(engine())
34 {
35 executionEndTimer = new QTimer(q);
36 executionEndTimer->setInterval(0);
37 executionEndTimer->setSingleShot(true);
38 QObject::connect(executionEndTimer, &QTimer::timeout, q, [this]() {
39 scheduleExecutionEnd();
40 });
41 }
42
43 ~SharedQmlEnginePrivate() = default;
44
45 void errorPrint(QQmlComponent *component);
46 void beginExecute(const QUrl &source);
47 void beginExecute(QAnyStringView module, QAnyStringView type);
48 void endExecute();
49 void scheduleExecutionEnd();
50 void minimumWidthChanged();
51 void minimumHeightChanged();
52 void maximumWidthChanged();
53 void maximumHeightChanged();
54 void preferredWidthChanged();
55 void preferredHeightChanged();
56
57 SharedQmlEngine *q;
58
59 QPointer<QObject> rootObject;
60 std::unique_ptr<QQmlComponent> component;
61 QTimer *executionEndTimer;
62 KLocalizedContext *context{nullptr};
63 QQmlContext *rootContext;
64 bool delay;
65 std::shared_ptr<QQmlEngine> m_engine;
66
67private:
68 static std::shared_ptr<QQmlEngine> engine()
69 {
70 if (auto locked = s_engine.lock()) {
71 return locked;
72 }
73 auto createdEngine = std::make_shared<QQmlEngine>();
74 s_engine = createdEngine;
75 return createdEngine;
76 }
77
78 static std::weak_ptr<QQmlEngine> s_engine;
79};
80
81std::weak_ptr<QQmlEngine> SharedQmlEnginePrivate::s_engine = {};
82
83void SharedQmlEnginePrivate::errorPrint(QQmlComponent *component)
84{
85 QString errorStr = QStringLiteral("Error loading QML file.\n");
86 if (component->isError()) {
87 const QList<QQmlError> errors = component->errors();
88 for (const QQmlError &error : errors) {
89 errorStr +=
90 (error.line() > 0 ? QString(QString::number(error.line()) + QLatin1String(": ")) : QLatin1String("")) + error.description() + QLatin1Char('\n');
91 }
92 }
93 qWarning(LOG_PLASMAQUICK) << component->url().toString() << '\n' << errorStr;
94}
95
96void SharedQmlEnginePrivate::beginExecute(const QUrl &source)
97{
98 if (source.isEmpty()) {
99 qWarning(LOG_PLASMAQUICK) << "File name empty!";
100 return;
101 }
102
103 component = std::make_unique<QQmlComponent>(m_engine.get());
104 // Important! Some parts of Plasma are extremely sensitive to status changed
105 // signal being emit in exactly the same way QQmlComponent does it. So this
106 // connection needs to happen before any loading of the component happens.
107 QObject::connect(component.get(), &QQmlComponent::statusChanged, q, &SharedQmlEngine::statusChanged, Qt::QueuedConnection);
108 component->loadUrl(source);
109
110 endExecute();
111}
112
113void SharedQmlEnginePrivate::beginExecute(QAnyStringView module, QAnyStringView type)
114{
115 if (module.isEmpty() || type.isEmpty()) {
116 qWarning(LOG_PLASMAQUICK) << "No module or type specified";
117 return;
118 }
119
120 component = std::make_unique<QQmlComponent>(m_engine.get());
121 // Important! Some parts of Plasma are extremely sensitive to status changed
122 // signal being emit in exactly the same way QQmlComponent does it. So this
123 // connection needs to happen before any loading of the component happens.
124 QObject::connect(component.get(), &QQmlComponent::statusChanged, q, &SharedQmlEngine::statusChanged, Qt::QueuedConnection);
125 component->loadFromModule(module, type);
126
127 endExecute();
128}
129
130void SharedQmlEnginePrivate::endExecute()
131{
132 rootObject = component->beginCreate(rootContext);
133
134 if (delay) {
135 executionEndTimer->start(0);
136 } else {
137 scheduleExecutionEnd();
138 }
139}
140
141void SharedQmlEnginePrivate::scheduleExecutionEnd()
142{
143 if (component->isReady() || component->isError()) {
144 q->completeInitialization();
145 } else {
146 QObject::connect(component.get(), &QQmlComponent::statusChanged, q, [this]() {
147 q->completeInitialization();
148 });
149 }
150}
151
153 : QObject(parent)
154 , d(new SharedQmlEnginePrivate(this))
155{
156 d->rootContext = new QQmlContext(engine().get());
157 d->rootContext->setParent(this); // Delete the context when deleting the shared engine
158
159 d->context = new KLocalizedContext(d->rootContext);
160 d->rootContext->setContextObject(d->context);
161}
162
164 : QObject(parent)
165 , d(new SharedQmlEnginePrivate(this))
166{
167 d->rootContext = new AppletContext(engine().get(), applet, this);
168
169 d->context = new KLocalizedContext(d->rootContext);
170 d->rootContext->setContextObject(d->context);
171}
172
173SharedQmlEngine::~SharedQmlEngine()
174{
176 delete d->rootObject;
177 }
178}
179
180void SharedQmlEngine::setTranslationDomain(const QString &translationDomain)
181{
182 d->context->setTranslationDomain(translationDomain);
183}
184
185QString SharedQmlEngine::translationDomain() const
186{
187 return d->context->translationDomain();
188}
189
191{
192 d->beginExecute(source);
193}
194
196{
197 d->beginExecute(module, type);
198}
199
200QUrl SharedQmlEngine::source() const
201{
202 if (d->component) {
203 return d->component->url();
204 }
205 return QUrl{};
206}
207
209{
210 d->delay = delay;
211}
212
214{
215 return d->delay;
216}
217
218std::shared_ptr<QQmlEngine> SharedQmlEngine::engine()
219{
220 return d->m_engine;
221}
222
223QObject *SharedQmlEngine::rootObject() const
224{
225 return d->rootObject;
226}
227
229{
230 return d->component.get();
231}
232
234{
235 return d->rootContext;
236}
237
238QQmlComponent::Status SharedQmlEngine::status() const
239{
240 if (!d->m_engine) {
242 }
243
244 if (!d->component) {
245 return QQmlComponent::Null;
246 }
247
248 return QQmlComponent::Status(d->component->status());
249}
250
251void SharedQmlEngine::completeInitialization(const QVariantHash &initialProperties)
252{
253 d->executionEndTimer->stop();
254
255 if (!d->component) {
256 qWarning(LOG_PLASMAQUICK) << "No component for" << source();
257 return;
258 }
259
260 if (d->component->status() != QQmlComponent::Ready || d->component->isError()) {
261 d->errorPrint(d->component.get());
262 return;
263 }
264
265 for (auto it = initialProperties.constBegin(); it != initialProperties.constEnd(); ++it) {
266 d->rootObject->setProperty(it.key().toUtf8().data(), it.value());
267 }
268
269 d->component->completeCreate();
271}
272
273QObject *SharedQmlEngine::createObjectFromSource(const QUrl &source, QQmlContext *context, const QVariantHash &initialProperties)
274{
275 QQmlComponent *component = new QQmlComponent(d->m_engine.get(), this);
276 component->loadUrl(source);
277
278 return createObjectFromComponent(component, context, initialProperties);
279}
280
281QObject *SharedQmlEngine::createObjectFromComponent(QQmlComponent *component, QQmlContext *context, const QVariantHash &initialProperties)
282{
283 QObject *object = component->beginCreate(context ? context : d->rootContext);
284
285 for (auto it = initialProperties.constBegin(); it != initialProperties.constEnd(); ++it) {
286 object->setProperty(it.key().toUtf8().data(), it.value());
287 }
288 component->completeCreate();
289
290 if (!component->isError() && object) {
291 // memory management
292 component->setParent(object);
293 // reparent to root object if wasn't specified otherwise by initialProperties
294 if (!initialProperties.contains(QLatin1String("parent"))) {
295 if (qobject_cast<QQuickItem *>(rootObject())) {
296 object->setProperty("parent", QVariant::fromValue(rootObject()));
297 } else {
298 object->setParent(rootObject());
299 }
300 }
301
302 return object;
303
304 } else {
305 d->errorPrint(component);
306 delete object;
307 return nullptr;
308 }
309}
310}
311
312#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 Mon Nov 18 2024 12:10:41 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.