KDeclarative

qmlobject.cpp
1 /*
2  SPDX-FileCopyrightText: 2013 Marco Martin <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "qmlobject.h"
8 #include "private/kdeclarative_p.h"
9 
10 #include <QQmlContext>
11 #include <QQmlEngine>
12 #include <QQmlIncubator>
13 #include <QQmlNetworkAccessManagerFactory>
14 #include <QQuickItem>
15 #include <QTimer>
16 
17 #if KDECLARATIVE_BUILD_DEPRECATED_SINCE(5, 98)
18 #include <KPackage/PackageLoader>
19 #endif
20 #include <QDebug>
21 #include <kdeclarative.h>
22 
23 //#include "packageaccessmanagerfactory.h"
24 //#include "private/declarative/dataenginebindings_p.h"
25 
26 namespace KDeclarative
27 {
28 class QmlObjectIncubator : public QQmlIncubator
29 {
30 public:
31  QVariantHash m_initialProperties;
32 
33 protected:
34  void setInitialState(QObject *object) override
35  {
36  QHashIterator<QString, QVariant> i(m_initialProperties);
37  while (i.hasNext()) {
38  i.next();
39  object->setProperty(i.key().toLatin1().data(), i.value());
40  }
41  }
42 };
43 
44 class QmlObjectPrivate
45 {
46 public:
47  QmlObjectPrivate(QmlObject *parent)
48  : q(parent)
49  , engine(nullptr)
50  , component(nullptr)
51  , delay(false)
52  {
53  executionEndTimer = new QTimer(q);
54  executionEndTimer->setInterval(0);
55  executionEndTimer->setSingleShot(true);
56  QObject::connect(executionEndTimer, &QTimer::timeout, q, [this]() {
57  scheduleExecutionEnd();
58  });
59  }
60 
61  ~QmlObjectPrivate()
62  {
63  delete incubator.object();
64  }
65 
66  void errorPrint(QQmlComponent *component);
67  void execute(const QUrl &source);
68  void scheduleExecutionEnd();
69  void minimumWidthChanged();
70  void minimumHeightChanged();
71  void maximumWidthChanged();
72  void maximumHeightChanged();
73  void preferredWidthChanged();
74  void preferredHeightChanged();
75  void checkInitializationCompleted();
76 
77  QmlObject *q;
78 
79  QUrl source;
80  std::shared_ptr<QQmlEngine> engine;
81 
82  QmlObjectIncubator incubator;
83  QQmlComponent *component;
84  QTimer *executionEndTimer;
85  KLocalizedContext *context{nullptr};
86 #if KDECLARATIVE_BUILD_DEPRECATED_SINCE(5, 98)
87  KPackage::Package package;
88 #endif
89  QQmlContext *rootContext;
90  bool delay : 1;
91 };
92 
93 void QmlObjectPrivate::errorPrint(QQmlComponent *component)
94 {
95  QString errorStr = QStringLiteral("Error loading QML file.\n");
96  if (component->isError()) {
97  const QList<QQmlError> errors = component->errors();
98  for (const QQmlError &error : errors) {
99  errorStr +=
100  (error.line() > 0 ? QString(QString::number(error.line()) + QLatin1String(": ")) : QLatin1String("")) + error.description() + QLatin1Char('\n');
101  }
102  }
103  qWarning() << component->url().toString() << '\n' << errorStr;
104 }
105 
106 void QmlObjectPrivate::execute(const QUrl &source)
107 {
108  if (source.isEmpty()) {
109  qWarning() << "File name empty!";
110  return;
111  }
112 
113  delete component;
114  component = new QQmlComponent(engine.get(), q);
115  QObject::connect(component, &QQmlComponent::statusChanged, q, &QmlObject::statusChanged, Qt::QueuedConnection);
116  delete incubator.object();
117 
118  component->loadUrl(source);
119 
120  if (delay) {
121  executionEndTimer->start(0);
122  } else {
123  scheduleExecutionEnd();
124  }
125 }
126 
127 void QmlObjectPrivate::scheduleExecutionEnd()
128 {
129  if (component->isReady() || component->isError()) {
130  q->completeInitialization();
131  } else {
132  QObject::connect(component, &QQmlComponent::statusChanged, q, [this]() {
133  q->completeInitialization();
134  });
135  }
136 }
137 
139  : QmlObject(nullptr, nullptr, parent)
140 {
141 }
142 
143 #if KDECLARATIVE_BUILD_DEPRECATED_SINCE(5, 95)
145  : QmlObject(std::shared_ptr<QQmlEngine>(engine), nullptr, parent)
146 {
147 }
148 
149 QmlObject::QmlObject(QQmlEngine *engine, QQmlContext *rootContext, QObject *parent)
150  : QmlObject(std::shared_ptr<QQmlEngine>(engine), rootContext, parent)
151 {
152 }
153 
154 QmlObject::QmlObject(QQmlEngine *engine, QQmlContext *rootContext, QmlObject *obj, QObject *parent)
155  : QmlObject(std::shared_ptr<QQmlEngine>(engine), rootContext, parent)
156 {
157  Q_UNUSED(obj);
158 }
159 #endif
160 
161 QmlObject::QmlObject(std::shared_ptr<QQmlEngine> engine, QQmlContext *rootContext, QObject *parent)
162  : QObject(parent)
163  , d(new QmlObjectPrivate(this))
164 {
165  if (engine) {
166  d->engine = engine;
167  } else {
168  d->engine = std::make_shared<QQmlEngine>();
169  }
170 
171 #if KDECLARATIVE_BUILD_DEPRECATED_SINCE(5, 98)
172  if (d->engine.use_count() <= 2) {
173  KDeclarative::setupEngine(d->engine.get());
174  }
175 #endif
176 
177  if (rootContext) {
178  d->rootContext = rootContext;
179  } else {
180  d->rootContext = d->engine->rootContext();
181  }
182 
183  d->context = new KLocalizedContext(this);
184  d->rootContext->setContextObject(d->context);
185 }
186 
187 QmlObject::~QmlObject()
188 {
189  if (d->engine.use_count() == 1) {
190  // QQmlEngine does not take ownership of the QNAM factory so we need to
191  // make sure to clean it, but only if we are the last user of the engine
192  // otherwise we risk resetting the factory on an engine that is still in
193  // use.
194  auto factory = d->engine->networkAccessManagerFactory();
195  d->engine->setNetworkAccessManagerFactory(nullptr);
196  delete factory;
197  }
198 
199  delete d;
200 }
201 
202 void QmlObject::setTranslationDomain(const QString &translationDomain)
203 {
204  d->context->setTranslationDomain(translationDomain);
205 }
206 
207 QString QmlObject::translationDomain() const
208 {
209  return d->context->translationDomain();
210 }
211 
212 void QmlObject::setSource(const QUrl &source)
213 {
214  d->source = source;
215  d->execute(source);
216 }
217 
218 QUrl QmlObject::source() const
219 {
220  return d->source;
221 }
222 
223 #if KDECLARATIVE_BUILD_DEPRECATED_SINCE(5, 98)
224 void QmlObject::loadPackage(const QString &packageName)
225 {
226  d->package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("KPackage/GenericQML"));
227  d->package.setPath(packageName);
228  setSource(QUrl::fromLocalFile(d->package.filePath("mainscript")));
229 }
230 
232 {
233  d->package = package;
235 }
236 
238 {
239  return d->package;
240 }
241 #endif
242 
244 {
245  d->delay = delay;
246 }
247 
249 {
250  return d->delay;
251 }
252 
254 {
255  return d->engine.get();
256 }
257 
258 QObject *QmlObject::rootObject() const
259 {
260  if (d->incubator.status() == QQmlIncubator::Loading) {
261  qWarning() << "Trying to use rootObject before initialization is completed, whilst using setInitializationDelayed. Forcing completion";
262  d->incubator.forceCompletion();
263  }
264  return d->incubator.object();
265 }
266 
268 {
269  return d->component;
270 }
271 
273 {
274  return d->rootContext;
275 }
276 
277 QQmlComponent::Status QmlObject::status() const
278 {
279  if (!d->engine) {
280  return QQmlComponent::Error;
281  }
282 
283  if (!d->component) {
284  return QQmlComponent::Null;
285  }
286 
287  return QQmlComponent::Status(d->component->status());
288 }
289 
290 void QmlObjectPrivate::checkInitializationCompleted()
291 {
292  if (!incubator.isReady() && incubator.status() != QQmlIncubator::Error) {
293  QTimer::singleShot(0, q, SLOT(checkInitializationCompleted()));
294  return;
295  }
296 
297  if (!incubator.object()) {
298  errorPrint(component);
299  }
300 
301  Q_EMIT q->finished();
302 }
303 
304 void QmlObject::completeInitialization(const QVariantHash &initialProperties)
305 {
306  d->executionEndTimer->stop();
307  if (d->incubator.object()) {
308  return;
309  }
310 
311  if (!d->component) {
312  qWarning() << "No component for" << source();
313  return;
314  }
315 
316  if (d->component->status() != QQmlComponent::Ready || d->component->isError()) {
317  d->errorPrint(d->component);
318  return;
319  }
320 
321  d->incubator.m_initialProperties = initialProperties;
322  d->component->create(d->incubator, d->rootContext);
323 
324  if (d->delay) {
325  d->checkInitializationCompleted();
326  } else {
327  d->incubator.forceCompletion();
328 
329  if (!d->incubator.object()) {
330  d->errorPrint(d->component);
331  }
332  Q_EMIT finished();
333  }
334 }
335 
336 QObject *QmlObject::createObjectFromSource(const QUrl &source, QQmlContext *context, const QVariantHash &initialProperties)
337 {
338  QQmlComponent *component = new QQmlComponent(d->engine.get(), this);
339  component->loadUrl(source);
340 
341  return createObjectFromComponent(component, context, initialProperties);
342 }
343 
344 QObject *QmlObject::createObjectFromComponent(QQmlComponent *component, QQmlContext *context, const QVariantHash &initialProperties)
345 {
346  QmlObjectIncubator incubator;
347  incubator.m_initialProperties = initialProperties;
348  component->create(incubator, context ? context : d->rootContext);
349  incubator.forceCompletion();
350 
351  QObject *object = incubator.object();
352 
353  if (!component->isError() && object) {
354  // memory management
355  component->setParent(object);
356  // reparent to root object if wasn't specified otherwise by initialProperties
357  if (!initialProperties.contains(QLatin1String("parent"))) {
358  if (qobject_cast<QQuickItem *>(rootObject())) {
359  object->setProperty("parent", QVariant::fromValue(rootObject()));
360  } else {
361  object->setParent(rootObject());
362  }
363  }
364 
365  return object;
366 
367  } else {
368  d->errorPrint(component);
369  delete object;
370  return nullptr;
371  }
372 }
373 
374 }
375 
376 #include "moc_qmlobject.cpp"
void finished()
Emitted when the parsing and execution of the QML file is terminated.
bool isReady() const const
QString number(int n, int base)
Package loadPackage(const QString &packageFormat, const QString &packagePath=QString())
QVariant fromValue(const T &value)
void setTranslationDomain(const QString &translationDomain)
Call this method before calling setupBindings to install a translation domain for all i18n global fun...
Definition: qmlobject.cpp:202
Q_EMITQ_EMIT
void completeInitialization(const QVariantHash &initialProperties=QVariantHash())
Finishes the process of initialization.
Definition: qmlobject.cpp:304
virtual QObject * create(QQmlContext *context)
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...
Definition: qmlobject.cpp:344
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void setPackage(const KPackage::Package &package)
Sets a package, then loads the mainscript file for that package.
Definition: qmlobject.cpp:231
void setSource(const QUrl &source)
Sets the path of the QML file to parse and execute.
Definition: qmlobject.cpp:212
An object that instantiates an entire QML context, with its own declarative engine.
Definition: qmlobject.h:41
void setInitializationDelayed(const bool delay)
Sets whether the execution of the QML file has to be delayed later in the event loop.
Definition: qmlobject.cpp:243
bool isEmpty() const const
QQmlContext * rootContext() const
The components's creation context.
Definition: qmlobject.cpp:272
static PackageLoader * self()
void loadUrl(const QUrl &url)
void timeout()
QUrl fromLocalFile(const QString &localFile)
QueuedConnection
void loadPackage(const QString &packageName)
Load the package called packageName, then loads the mainscript file for that package.
Definition: qmlobject.cpp:224
QQmlComponent * mainComponent() const
Definition: qmlobject.cpp:267
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
static void setupEngine(QQmlEngine *engine)
Setup a QML engine for use with any KDeclarative object.
QString filePath(const QByteArray &key, const QString &filename=QString()) const
void setParent(QObject *parent)
bool isError() const const
bool isInitializationDelayed() const
Definition: qmlobject.cpp:248
QmlObject(QObject *parent=nullptr)
Constructs a new QmlObject.
Definition: qmlobject.cpp:138
QQmlEngine * engine()
Definition: qmlobject.cpp:253
KPackage::Package package() const
Definition: qmlobject.cpp:237
void statusChanged(QQmlComponent::Status status)
QList< QQmlError > errors() const const
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...
Definition: qmlobject.cpp:336
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Mon Oct 3 2022 04:04:04 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.