Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmlcomponent.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant
4
8
9#include "qqmlengine_p.h"
10#include "qqmlvme_p.h"
11#include "qqml.h"
12#include "qqmlengine.h"
13#include "qqmlincubator.h"
15#include <private/qqmljavascriptexpression_p.h>
16#include <private/qqmlsourcecoordinate_p.h>
17
18#include <private/qv4functionobject_p.h>
19#include <private/qv4script_p.h>
20#include <private/qv4scopedvalue_p.h>
21#include <private/qv4objectiterator_p.h>
22#include <private/qv4qobjectwrapper_p.h>
23#include <private/qv4jscall_p.h>
24
25#include <QDir>
26#include <QStack>
27#include <QStringList>
28#include <QThreadStorage>
29#include <QtCore/qdebug.h>
30#include <QtCore/qloggingcategory.h>
31#include <qqmlinfo.h>
32
33
34using namespace Qt::Literals::StringLiterals;
35
36namespace {
37 Q_CONSTINIT thread_local int creationDepth = 0;
38}
39
40Q_STATIC_LOGGING_CATEGORY(lcQmlComponentGeneral, "qt.qml.qmlcomponent")
41
42QT_BEGIN_NAMESPACE
43
44class QQmlComponentExtension : public QV4::ExecutionEngine::Deletable
45{
46public:
47 QQmlComponentExtension(QV4::ExecutionEngine *v4);
48 virtual ~QQmlComponentExtension();
49
50 QV4::PersistentValue incubationProto;
51};
53
54/*!
55 \class QQmlComponent
56 \since 5.0
57 \inmodule QtQml
58
59 \brief The QQmlComponent class encapsulates a QML component definition.
60
61 Components are reusable, encapsulated QML types with well-defined interfaces.
62
63 A QQmlComponent instance can be created from a QML file.
64 For example, if there is a \c main.qml file like this:
65
66 \qml
67 import QtQuick 2.0
68
69 Item {
70 width: 200
71 height: 200
72 }
73 \endqml
74
75 The following code loads this QML file as a component, creates an instance of
76 this component using create(), and then queries the \l Item's \l {Item::}{width}
77 value:
78
79 \code
80 QQmlEngine *engine = new QQmlEngine;
81 QQmlComponent component(engine, QUrl::fromLocalFile("main.qml"));
82 if (component.isError()) {
83 qWarning() << "Failed to load main.qml:" << component.errors();
84 return 1;
85 }
86
87 QObject *myObject = component.create();
88 if (component.isError()) {
89 qWarning() << "Failed to create instance of main.qml:" << component.errors();
90 return 1;
91 }
92
93 QQuickItem *item = qobject_cast<QQuickItem*>(myObject);
94 int width = item->width(); // width = 200
95 \endcode
96
97 To create instances of a component in code where a QQmlEngine instance is
98 not available, you can use \l qmlContext() or \l qmlEngine(). For example,
99 in the scenario below, child items are being created within a QQuickItem
100 subclass:
101
102 \code
103 void MyCppItem::init()
104 {
105 QQmlEngine *engine = qmlEngine(this);
106 // Or:
107 // QQmlEngine *engine = qmlContext(this)->engine();
108 QQmlComponent component(engine, QUrl::fromLocalFile("MyItem.qml"));
109 QQuickItem *childItem = qobject_cast<QQuickItem*>(component.create());
110 childItem->setParentItem(this);
111 }
112 \endcode
113
114 Note that these functions will return \c null when called inside the
115 constructor of a QObject subclass, as the instance will not yet have
116 a context nor engine.
117
118 \section2 Network Components
119
120 If the URL passed to QQmlComponent is a network resource, or if the QML document references a
121 network resource, the QQmlComponent has to fetch the network data before it is able to create
122 objects. In this case, the QQmlComponent will have a \l {QQmlComponent::Loading}{Loading}
123 \l {QQmlComponent::status()}{status}. An application will have to wait until the component
124 is \l {QQmlComponent::Ready}{Ready} before calling \l {QQmlComponent::create()}.
125
126 The following example shows how to load a QML file from a network resource. After creating
127 the QQmlComponent, it tests whether the component is loading. If it is, it connects to the
128 QQmlComponent::statusChanged() signal and otherwise calls the \c {continueLoading()} method
129 directly. Note that QQmlComponent::isLoading() may be false for a network component if the
130 component has been cached and is ready immediately.
131
132 \code
133 MyApplication::MyApplication()
134 {
135 // ...
136 component = new QQmlComponent(engine, QUrl("http://www.example.com/main.qml"));
137 if (component->isLoading()) {
138 QObject::connect(component, &QQmlComponent::statusChanged,
139 this, &MyApplication::continueLoading);
140 } else {
141 continueLoading();
142 }
143 }
144
145 void MyApplication::continueLoading()
146 {
147 if (component->isError()) {
148 qWarning() << component->errors();
149 } else {
150 QObject *myObject = component->create();
151 }
152 }
153 \endcode
154*/
155
156/*!
157 \qmltype Component
158 \nativetype QQmlComponent
159 \ingroup qml-utility-elements
160 \inqmlmodule QtQml
161 \brief Encapsulates a QML component definition.
162
163 Components are reusable, encapsulated QML types with well-defined interfaces.
164
165 Components are often defined by \l {{QML Documents}}{component files} -
166 that is, \c .qml files. The \e Component type essentially allows QML components
167 to be defined inline, within a \l {QML Documents}{QML document}, rather than as a separate QML file.
168 This may be useful for reusing a small component within a QML file, or for defining
169 a component that logically belongs with other QML components within a file.
170
171 For example, here is a component that is used by multiple \l Loader objects.
172 It contains a single item, a \l Rectangle:
173
174 \snippet qml/component.qml 0
175
176 Notice that while a \l Rectangle by itself would be automatically
177 rendered and displayed, this is not the case for the above rectangle
178 because it is defined inside a \c Component. The component encapsulates the
179 QML types within, as if they were defined in a separate QML
180 file, and is not loaded until requested (in this case, by the
181 two \l Loader objects). Because Component is not derived from Item, you cannot
182 anchor anything to it.
183
184 Defining a \c Component is similar to defining a \l {QML Documents}{QML document}.
185 A QML document has a single top-level item that defines the behavior and
186 properties of that component, and cannot define properties or behavior outside
187 of that top-level item. In the same way, a \c Component definition contains a single
188 top level item (which in the above example is a \l Rectangle) and cannot define any
189 data outside of this item, with the exception of an \e id (which in the above example
190 is \e redSquare).
191
192 The \c Component type is commonly used to provide graphical components
193 for views. For example, the ListView::delegate property requires a \c Component
194 to specify how each list item is to be displayed.
195
196 \c Component objects can also be created dynamically using
197 \l{QtQml::Qt::createComponent()}{Qt.createComponent()}.
198
199 \c {Component}s are useful to declare a type where you only need an
200 instance of the type without having to add an entire new file. However,
201 you cannot name this type and consequently can't use it to declare a
202 property or use it in a type annotation. If you need this, prefer using
203 \l{Defining Object Types through QML Documents#Inline Components}{inline components}.
204
205 \section2 Creation Context
206
207 The creation context of a Component corresponds to the context where the Component was declared.
208 This context is used as the parent context (creating a \l{qtqml-documents-scope.html#component-instance-hierarchy}{context hierarchy})
209 when the component is instantiated by an object such as a ListView or a Loader.
210
211 In the following example, \c comp1 is created within the root context of MyItem.qml, and any objects
212 instantiated from this component will have access to the ids and properties within that context,
213 such as \c internalSettings.color. When \c comp1 is used as a ListView delegate in another context
214 (as in main.qml below), it will continue to have access to the properties of its creation context
215 (which would otherwise be private to external users).
216
217 \table
218 \row
219 \li MyItem.qml
220 \li \snippet qml/component/MyItem.qml 0
221 \row
222 \li main.qml
223 \li \snippet qml/component/main.qml 0
224 \endtable
225
226 It is important that the lifetime of the creation context outlive any created objects. See
227 \l{Maintaining Dynamically Created Objects} for more details.
228*/
229
230/*!
231 \qmlattachedsignal Component::completed()
232
233 Emitted after the object has been instantiated. This can be used to
234 execute script code at startup, once the full QML environment has been
235 established.
236
237 The \c onCompleted signal handler can be declared on any object. The order
238 of running the handlers is undefined.
239
240 \qml
241 Rectangle {
242 Component.onCompleted: console.log("Completed Running!")
243 Rectangle {
244 Component.onCompleted: console.log("Nested Completed Running!")
245 }
246 }
247 \endqml
248*/
249
250/*!
251 \qmlattachedsignal Component::destruction()
252
253 Emitted as the object begins destruction. This can be used to undo
254 work done in response to the \l {completed}{completed()} signal, or other
255 imperative code in your application.
256
257 The \c onDestruction signal handler can be declared on any object. The
258 order of running the handlers is undefined.
259
260 \qml
261 Rectangle {
262 Component.onDestruction: console.log("Destruction Beginning!")
263 Rectangle {
264 Component.onDestruction: console.log("Nested Destruction Beginning!")
265 }
266 }
267 \endqml
268
269 \sa {Qt Qml}
270*/
271
272/*!
273 \enum QQmlComponent::Status
274
275 Specifies the loading status of the QQmlComponent.
276
277 \value Null This QQmlComponent has no data. Call loadUrl() or setData() to add QML content.
278 \value Ready This QQmlComponent is ready and create() may be called.
279 \value Loading This QQmlComponent is loading network data.
280 \value Error An error has occurred. Call errors() to retrieve a list of \l {QQmlError}{errors}.
281*/
282
283/*!
284 \enum QQmlComponent::CompilationMode
285
286 Specifies whether the QQmlComponent should load the component immediately, or asynchonously.
287
288 \value PreferSynchronous Prefer loading/compiling the component immediately, blocking the thread.
289 This is not always possible; for example, remote URLs will always load asynchronously.
290 \value Asynchronous Load/compile the component in a background thread.
291*/
292
293void QQmlComponentPrivate::ready(QQmlNotifyingBlob *)
294{
295 Q_Q(QQmlComponent);
296
297 Q_ASSERT(m_typeData);
298
299 fromTypeData(m_typeData);
300 m_typeData.reset();
301 setProgress(1.0);
302 emit q->statusChanged(q->status());
303}
304
305void QQmlComponentPrivate::progress(QQmlNotifyingBlob *, qreal p)
306{
307 setProgress(p);
308}
309
310void QQmlComponentPrivate::fromTypeData(const QQmlRefPointer<QQmlTypeData> &data)
311{
312 m_url = data->finalUrl();
313 if (auto cu = data->compilationUnit())
314 m_compilationUnit = m_engine->handle()->executableCompilationUnit(std::move(cu));
315
316 if (!m_compilationUnit) {
317 Q_ASSERT(data->isError());
318 m_state.errors.clear();
319 m_state.appendErrors(data->errors());
320 }
321}
322
323bool QQmlComponentPrivate::hadTopLevelRequiredProperties() const
324{
325 return m_state.creator()->componentHadTopLevelRequiredProperties();
326}
327
328void QQmlComponentPrivate::clear()
329{
330 if (m_typeData) {
331 m_typeData->unregisterCallback(this);
332 m_typeData.reset();
333 }
334
335 if (m_loadHelper) {
336 m_loadHelper->unregisterCallback(this);
337 m_loadHelper.reset();
338 }
339
340 m_compilationUnit.reset();
341 m_inlineComponentName.reset();
342}
343
344QObject *QQmlComponentPrivate::doBeginCreate(QQmlComponent *q, QQmlContext *context)
345{
346 if (!m_engine) {
347 // ###Qt6: In Qt 6, it should be impossible for users to create a QQmlComponent without an engine, and we can remove this check
348 qWarning("QQmlComponent: Must provide an engine before calling create");
349 return nullptr;
350 }
351 if (!context)
352 context = m_engine->rootContext();
353 return q->beginCreate(context);
354}
355
357 QV4::Value *object, const QString &propertyName, const QQmlObjectCreator *creator)
358{
359 if (!creator)
360 return;
361
362 QV4::QObjectWrapper *wrapper = object->as<QV4::QObjectWrapper>();
363 if (!wrapper)
364 return;
365
366 QObject *o = wrapper->object();
367 if (!o)
368 return;
369
370 if (QQmlData *ddata = QQmlData::get(o)) {
371 const QQmlPropertyData *propData = ddata->propertyCache->property(
372 propertyName, o, ddata->outerContext);
373 if (propData && propData->acceptsQBinding())
374 creator->removePendingBinding(o, propData->coreIndex());
375 return;
376 }
377
378 const QMetaObject *meta = o->metaObject();
379 Q_ASSERT(meta);
380 const int index = meta->indexOfProperty(propertyName.toUtf8());
381 if (index != -1 && meta->property(index).isBindable())
382 creator->removePendingBinding(o, index);
383}
384
385bool QQmlComponentPrivate::setInitialProperty(
386 QObject *base, const QString &name, const QVariant &value)
387{
388 bool isComplexProperty = name.contains(u'.');
389 QQmlProperty prop;
390 // we don't allow "fixing" inner required properties - that would seriously hamper local reasoning
391 if (m_state.hasUnsetRequiredProperties() && !isComplexProperty)
392 prop = QQmlComponentPrivate::removePropertyFromRequired(
393 base, name, m_state.requiredProperties(), m_engine);
394 else if (QQmlContext *ctxt = qmlContext(base); ctxt)
395 prop = QQmlProperty(base, name, ctxt);
396 else
397 prop = QQmlProperty(base, name, m_engine);
398
399 if (!prop.isValid()) {
400 // Having extra properties in the object that contains initial properties is not a problem.
401 // In JavaScript you can hardly ensure the _absence_ of properties. There are implicitly
402 // added properties on many objects, properties added via prototype, etc.
403 if (!isComplexProperty)
404 return true;
405
406 // QQmlProperty can't handle accesses on value types
407 const QStringList properties = name.split(u'.');
408 QV4::Scope scope(m_engine->handle());
409 QV4::ScopedObject object(scope, QV4::QObjectWrapper::wrap(scope.engine, base));
410 QV4::ScopedString segment(scope);
411 for (int i = 0; i < properties.size() - 1; ++i) {
412 segment = scope.engine->newString(properties.at(i));
413 object = object->get(segment);
414 if (scope.engine->hasException || object->isNullOrUndefined())
415 break;
416 }
417
418 const QString lastProperty = properties.last();
419 if (!object->isNullOrUndefined()) {
420 segment = scope.engine->newString(lastProperty);
421 QV4::ScopedValue v(scope, scope.engine->metaTypeToJS(value.metaType(), value.constData()));
422 object->put(segment, v);
423 } else {
424 return false;
425 }
426
427 if (scope.engine->hasException) {
428 qmlWarning(base, scope.engine->catchExceptionAsQmlError());
429 scope.engine->hasException = false;
430 return false;
431 }
432
433 removePendingQPropertyBinding(object, lastProperty, m_state.creator());
434 return true;
435 }
436
437 if (QQmlPropertyPrivate::get(prop)->writeValueProperty(value, {})) {
438 if (prop.isBindable()) {
439 if (QQmlObjectCreator *creator = m_state.creator())
440 creator->removePendingBinding(prop.object(), prop.index());
441 }
442 return true;
443 }
444
445 QQmlError error{};
446 error.setUrl(m_url);
447 error.setDescription(QStringLiteral("Could not set initial property %1").arg(name));
448 qmlWarning(base, error);
449 return false;
450}
451
452/*!
453 \internal
454*/
455QQmlComponent::QQmlComponent(QObject *parent)
456 : QObject(*(new QQmlComponentPrivate), parent)
457{
458}
459
460/*!
461 Destruct the QQmlComponent.
462*/
463QQmlComponent::~QQmlComponent()
464{
465 Q_D(QQmlComponent);
466
467 if (d->m_state.isCompletePending()) {
468 qWarning("QQmlComponent: Component destroyed while completion pending");
469
470 if (isError()) {
471 qWarning() << "This may have been caused by one of the following errors:";
472 for (const QQmlComponentPrivate::AnnotatedQmlError &e : std::as_const(d->m_state.errors))
473 qWarning().nospace().noquote() << QLatin1String(" ") << e.error;
474 }
475
476 // we might not have the creator anymore if the engine is gone
477 if (d->m_state.hasCreator())
478 d->completeCreate();
479 }
480
481 if (d->m_typeData) {
482 d->m_typeData->unregisterCallback(d);
483 if (d->m_engine && !d->m_typeData->isCompleteOrError()) {
484 // In this case we have to send it to the type loader thread to be dropped. It will
485 // manipulate its "waiting" lists that other blobs may be using concurrently.
486 QQmlTypeLoader::get(d->m_engine)->drop(QQmlDataBlob::Ptr(d->m_typeData.data()));
487 }
488 d->m_typeData.reset();
489 }
490}
491
492/*!
493 \qmlproperty enumeration Component::status
494
495 This property holds the status of component loading. The status can be one of the
496 following:
497
498 \value Component.Null no data is available for the component
499 \value Component.Ready the component has been loaded, and can be used to create instances.
500 \value Component.Loading the component is currently being loaded
501 \value Component.Error an error occurred while loading the component.
502 Calling \l errorString() will provide a human-readable description of any errors.
503 */
504
505/*!
506 \property QQmlComponent::status
507 The component's current \l{QQmlComponent::Status} {status}.
508 */
509QQmlComponent::Status QQmlComponent::status() const
510{
511 Q_D(const QQmlComponent);
512
513 if (d->m_typeData)
514 return Loading;
515 else if (!d->m_state.errors.isEmpty())
516 return Error;
517 else if (d->m_engine && (d->m_compilationUnit || d->loadedType().isValid()))
518 return Ready;
519 else if (d->m_loadHelper)
520 return Loading;
521 else
522 return Null;
523}
524
525/*!
526 Returns true if status() == QQmlComponent::Null.
527*/
528bool QQmlComponent::isNull() const
529{
530 return status() == Null;
531}
532
533/*!
534 Returns true if status() == QQmlComponent::Ready.
535*/
536bool QQmlComponent::isReady() const
537{
538 return status() == Ready;
539}
540
541/*!
542 Returns true if status() == QQmlComponent::Error.
543*/
544bool QQmlComponent::isError() const
545{
546 return status() == Error;
547}
548
549/*!
550 Returns true if status() == QQmlComponent::Loading.
551*/
552bool QQmlComponent::isLoading() const
553{
554 return status() == Loading;
555}
556
557/*!
558 Returns true if the component was created in a QML files that specifies
559 \c{pragma ComponentBehavior: Bound}, otherwise returns false.
560
561 \since 6.5
562 */
563bool QQmlComponent::isBound() const
564{
565 Q_D(const QQmlComponent);
566 return d->isBound();
567}
568
569/*!
570 \qmlproperty real Component::progress
571 The progress of loading the component, from 0.0 (nothing loaded)
572 to 1.0 (finished).
573*/
574
575/*!
576 \property QQmlComponent::progress
577 The progress of loading the component, from 0.0 (nothing loaded)
578 to 1.0 (finished).
579*/
580qreal QQmlComponent::progress() const
581{
582 Q_D(const QQmlComponent);
583 return d->m_progress;
584}
585
586/*!
587 \fn void QQmlComponent::progressChanged(qreal progress)
588
589 Emitted whenever the component's loading progress changes. \a progress will be the
590 current progress between 0.0 (nothing loaded) and 1.0 (finished).
591*/
592
593/*!
594 \fn void QQmlComponent::statusChanged(QQmlComponent::Status status)
595
596 Emitted whenever the component's status changes. \a status will be the
597 new status.
598*/
599
600/*!
601 Create a QQmlComponent with no data and give it the specified
602 \a engine and \a parent. Set the data with setData().
603*/
604QQmlComponent::QQmlComponent(QQmlEngine *engine, QObject *parent)
605 : QObject(*(new QQmlComponentPrivate), parent)
606{
607 Q_D(QQmlComponent);
608 d->m_engine = engine;
609 QObject::connect(engine, &QObject::destroyed, this, [d]() {
610 d->m_state.clear();
611 d->m_engine = nullptr;
612 });
613}
614
615/*!
616 Create a QQmlComponent from the given \a url and give it the
617 specified \a parent and \a engine.
618
619 \include qqmlcomponent.qdoc url-note
620
621 \sa loadUrl()
622*/
623QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, QObject *parent)
624 : QQmlComponent(engine, url, QQmlComponent::PreferSynchronous, parent)
625{
626}
627
628/*!
629 Create a QQmlComponent from the given \a url and give it the
630 specified \a parent and \a engine. If \a mode is \l Asynchronous,
631 the component will be loaded and compiled asynchronously.
632
633 \include qqmlcomponent.qdoc url-note
634
635 \sa loadUrl()
636*/
637QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, CompilationMode mode,
638 QObject *parent)
639 : QQmlComponent(engine, parent)
640{
641 Q_D(QQmlComponent);
642 d->loadUrl(url, mode);
643}
644
645/*!
646 Create a QQmlComponent from the given \a uri and \a typeName and give it
647 the specified \a parent and \a engine. If possible, the component will
648 be loaded synchronously.
649
650 \sa loadFromModule()
651 \since 6.5
652 \overload
653*/
654QQmlComponent::QQmlComponent(QQmlEngine *engine, QAnyStringView uri, QAnyStringView typeName, QObject *parent)
655 : QQmlComponent(engine, uri, typeName, QQmlComponent::PreferSynchronous, parent)
656{
657
658}
659
660/*!
661 Create a QQmlComponent from the given \a uri and \a typeName and give it
662 the specified \a parent and \a engine. If \a mode is \l Asynchronous,
663 the component will be loaded and compiled asynchronously.
664
665 \sa loadFromModule()
666 \since 6.5
667 \overload
668*/
669QQmlComponent::QQmlComponent(QQmlEngine *engine, QAnyStringView uri, QAnyStringView typeName, CompilationMode mode, QObject *parent)
670 : QQmlComponent(engine, parent)
671{
672 loadFromModule(uri, typeName, mode);
673}
674
675/*!
676 Create a QQmlComponent from the given \a fileName and give it the specified
677 \a parent and \a engine.
678
679 \sa loadUrl()
680*/
681QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName,
682 QObject *parent)
683 : QQmlComponent(engine, fileName, QQmlComponent::PreferSynchronous, parent)
684{
685}
686
687/*!
688 Create a QQmlComponent from the given \a fileName and give it the specified
689 \a parent and \a engine. If \a mode is \l Asynchronous,
690 the component will be loaded and compiled asynchronously.
691
692 \sa loadUrl()
693*/
694QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName,
695 CompilationMode mode, QObject *parent)
696 : QQmlComponent(engine, parent)
697{
698 Q_D(QQmlComponent);
699 if (fileName.startsWith(u':'))
700 d->loadUrl(QUrl(QLatin1String("qrc") + fileName), mode);
701 else if (QDir::isAbsolutePath(fileName))
702 d->loadUrl(QUrl::fromLocalFile(fileName), mode);
703 else
704 d->loadUrl(QUrl(fileName), mode);
705}
706
707/*!
708 \internal
709*/
710QQmlComponent::QQmlComponent(QQmlEngine *engine, QV4::ExecutableCompilationUnit *compilationUnit,
711 int start, QObject *parent)
712 : QQmlComponent(engine, parent)
713{
714 Q_D(QQmlComponent);
715 d->m_compilationUnit.reset(compilationUnit);
716 d->m_start = start;
717 d->m_url = compilationUnit->finalUrl();
718 d->m_progress = 1.0;
719}
720
721/*!
722 Sets the QQmlComponent to use the given QML \a data. If \a url
723 is provided, it is used to set the component name and to provide
724 a base path for items resolved by this component.
725
726 \warning The new component will shadow any existing component of
727 the same URL. You should not pass a URL of an existing component.
728*/
729void QQmlComponent::setData(const QByteArray &data, const QUrl &url)
730{
731 Q_D(QQmlComponent);
732
733 if (!d->m_engine) {
734 // ###Qt6: In Qt 6, it should be impossible for users to create a QQmlComponent without an engine, and we can remove this check
735 qWarning("QQmlComponent: Must provide an engine before calling setData");
736 return;
737 }
738
739 d->clear();
740
741 d->m_url = url;
742
743 QQmlRefPointer<QQmlTypeData> typeData = QQmlTypeLoader::get(d->m_engine)->getType(data, url);
744
745 if (typeData->isCompleteOrError()) {
746 d->fromTypeData(typeData);
747 } else {
748 d->m_typeData = typeData;
749 d->m_typeData->registerCallback(d);
750 }
751
752 d->setProgress(1.0);
753 emit statusChanged(status());
754}
755
756/*!
757 Returns the QQmlContext the component was created in. This is only
758 valid for components created directly from QML.
759*/
760QQmlContext *QQmlComponent::creationContext() const
761{
762 Q_D(const QQmlComponent);
763 if (!d->m_creationContext.isNull())
764 return d->m_creationContext->asQQmlContext();
765
766 return qmlContext(this);
767}
768
769/*!
770 Returns the QQmlEngine of this component.
771
772 \since 5.12
773*/
774QQmlEngine *QQmlComponent::engine() const
775{
776 Q_D(const QQmlComponent);
777 return d->m_engine;
778}
779
780/*!
781 Load the QQmlComponent from the provided \a url.
782
783 \include qqmlcomponent.qdoc url-note
784*/
785void QQmlComponent::loadUrl(const QUrl &url)
786{
787 Q_D(QQmlComponent);
788 d->loadUrl(url);
789}
790
791/*!
792 Load the QQmlComponent from the provided \a url.
793 If \a mode is \l Asynchronous, the component will be loaded and compiled asynchronously.
794
795 \include qqmlcomponent.qdoc url-note
796*/
797void QQmlComponent::loadUrl(const QUrl &url, QQmlComponent::CompilationMode mode)
798{
799 Q_D(QQmlComponent);
800 d->loadUrl(url, mode);
801}
802
803void QQmlComponentPrivate::loadUrl(const QUrl &newUrl, QQmlComponent::CompilationMode mode)
804{
805 Q_Q(QQmlComponent);
806 clear();
807
808 if (newUrl.isRelative()) {
809 // The new URL is a relative URL like QUrl("main.qml").
810 m_url = m_engine->baseUrl().resolved(QUrl(newUrl.toString()));
811 } else if (m_engine->baseUrl().isLocalFile() && newUrl.isLocalFile() && !QDir::isAbsolutePath(newUrl.toLocalFile())) {
812 // The new URL is a file on disk but it's a relative path; e.g.:
813 // QUrl::fromLocalFile("main.qml") or QUrl("file:main.qml")
814 // We need to remove the scheme so that it becomes a relative URL with a relative path:
815 QUrl fixedUrl(newUrl);
816 fixedUrl.setScheme(QString());
817 // Then, turn it into an absolute URL with an absolute path by resolving it against the engine's baseUrl().
818 // This is a compatibility hack for QTBUG-58837.
819 m_url = m_engine->baseUrl().resolved(fixedUrl);
820 } else {
821 m_url = newUrl;
822 }
823
824 if (m_url.scheme() == "qrc"_L1 && !m_url.path().startsWith("/"_L1)) {
825 qWarning().nospace().noquote()
826 << "QQmlComponent: attempted to load via a relative URL '" << m_url.toString()
827 << "' in resource file system. This is not fully supported and may not work";
828 }
829
830 if (newUrl.isEmpty()) {
831 QQmlError error;
832 error.setDescription(QQmlComponent::tr("Invalid empty URL"));
833 m_state.errors.emplaceBack(error);
834 return;
835 }
836
837 setProgress(0.0);
838
839 QQmlTypeLoader::Mode loaderMode = (mode == QQmlComponent::Asynchronous)
840 ? QQmlTypeLoader::Asynchronous
841 : QQmlTypeLoader::PreferSynchronous;
842 QQmlRefPointer<QQmlTypeData> data = QQmlTypeLoader::get(m_engine)->getType(m_url, loaderMode);
843
844 if (data->isCompleteOrError()) {
845 fromTypeData(data);
846 setProgress(1.0);
847 } else {
848 m_typeData = data;
849 m_typeData->registerCallback(this);
850 setProgress(data->progress());
851 }
852
853 emit q->statusChanged(q->status());
854}
855
856/*!
857 Returns the list of errors that occurred during the last compile or create
858 operation. An empty list is returned if isError() is not set.
859*/
860QList<QQmlError> QQmlComponent::errors() const
861{
862 Q_D(const QQmlComponent);
863 QList<QQmlError> errors;
864 errors.reserve(d->m_state.errors.size());
865 for (const QQmlComponentPrivate::AnnotatedQmlError &annotated : d->m_state.errors)
866 errors.emplaceBack(annotated.error);
867 return errors;
868}
869
870/*!
871 \qmlmethod string Component::errorString()
872
873 Returns a human-readable description of any error.
874
875 The string includes the file, location, and description of each error.
876 If multiple errors are present, they are separated by a newline character.
877
878 If no errors are present, an empty string is returned.
879*/
880
881/*!
882 \internal
883 errorString() is only meant as a way to get the errors from QML side.
884*/
885QString QQmlComponent::errorString() const
886{
887 Q_D(const QQmlComponent);
888 QString ret;
889 if(!isError())
890 return ret;
891 for (const QQmlComponentPrivate::AnnotatedQmlError &annotated : d->m_state.errors) {
892 ret += annotated.error.toString() + QLatin1Char('\n');
893 }
894 return ret;
895}
896
897/*!
898 \qmlproperty url Component::url
899 The component URL. This is the URL that was used to construct the component.
900*/
901
902/*!
903 \property QQmlComponent::url
904 The component URL. This is the URL passed to either the constructor,
905 or the loadUrl(), or setData() methods.
906*/
907QUrl QQmlComponent::url() const
908{
909 Q_D(const QQmlComponent);
910 return d->m_url;
911}
912
913/*!
914 \internal
915*/
916QQmlComponent::QQmlComponent(QQmlComponentPrivate &dd, QObject *parent)
917 : QObject(dd, parent)
918{
919}
920
921/*!
922 Create an object instance from this component, within the specified \a context.
923 Returns \nullptr if creation failed.
924
925 If \a context is \nullptr (the default), it will create the instance in the
926 \l {QQmlEngine::rootContext()}{root context} of the engine.
927
928 The ownership of the returned object instance is transferred to the caller.
929
930 If the object being created from this component is a visual item, it must
931 have a visual parent, which can be set by calling
932 QQuickItem::setParentItem(). See \l {Concepts - Visual Parent in Qt Quick}
933 for more details.
934
935 \sa QQmlEngine::ObjectOwnership
936*/
937QObject *QQmlComponent::create(QQmlContext *context)
938{
939 Q_D(QQmlComponent);
940 return d->createWithProperties(
941 nullptr, QVariantMap {}, context, QQmlComponentPrivate::CreateBehavior::Cpp);
942}
943
944/*!
945 Create an object instance of this component, within the specified \a context,
946 and initialize its top-level properties with \a initialProperties.
947
948 \omit
949 TODO: also mention errorString() when QTBUG-93239 is fixed
950 \endomit
951
952 If any of the \a initialProperties cannot be set, a warning is issued. If
953 there are unset required properties, the object creation fails and returns
954 \c nullptr, in which case \l isError() will return \c true.
955
956 If \a context is \nullptr (the default), it will create the instance in the
957 \l {QQmlEngine::rootContext()}{root context} of the engine.
958
959 The ownership of the returned object instance is transferred to the caller.
960
961 \sa QQmlComponent::create
962 \since 5.14
963*/
964QObject *QQmlComponent::createWithInitialProperties(
965 const QVariantMap& initialProperties, QQmlContext *context)
966{
967 Q_D(QQmlComponent);
968 return d->createWithProperties(
969 nullptr, initialProperties, context, QQmlComponentPrivate::CreateBehavior::Cpp);
970}
971
972static void QQmlComponent_setQmlParent(QObject *me, QObject *parent); // forward declaration
973
974/*! \internal
975 */
976QObject *QQmlComponentPrivate::createWithProperties(
977 QObject *parent, const QVariantMap &properties,
978 QQmlContext *context, CreateBehavior behavior)
979{
980 Q_Q(QQmlComponent);
981
982 QObject *rv = doBeginCreate(q, context);
983 if (!rv) {
984 if (m_state.isCompletePending()) {
985 // overridden completCreate might assume that
986 // the object has actually been created
987 ++creationDepth;
988 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(m_engine);
989 complete(ep, &m_state);
990 --creationDepth;
991 }
992 return nullptr;
993 }
994
995 QQmlComponent_setQmlParent(rv, parent); // internally checks if parent is nullptr
996
997 if (behavior == CreateBehavior::Qml) {
998 bool ok = true;
999 for (auto it = properties.cbegin(), end = properties.cend(); it != end; ++it)
1000 ok = setInitialProperty(rv, it.key(), it.value()) && ok;
1001 q->completeCreate();
1002 if (m_state.hasUnsetRequiredProperties()) {
1003 for (const auto &unsetRequiredProperty : std::as_const(*m_state.requiredProperties())) {
1004 const QQmlError error = unsetRequiredPropertyToQQmlError(unsetRequiredProperty);
1005 qmlWarning(rv, error);
1006 }
1007 delete std::exchange(rv, nullptr);
1008 } else if (!ok) {
1009 // We've already warned about this before
1010 delete std::exchange(rv, nullptr);
1011 }
1012 } else {
1013 setInitialProperties(rv, properties);
1014 q->completeCreate();
1015 if (m_state.hasUnsetRequiredProperties())
1016 delete std::exchange(rv, nullptr);
1017 }
1018
1019 return rv;
1020}
1021
1022/*!
1023 Create an object instance from this component, within the specified \a context.
1024 Returns \nullptr if creation failed.
1025
1026 \note This method provides advanced control over component instance creation.
1027 In general, programmers should use QQmlComponent::create() to create object
1028 instances.
1029
1030 When QQmlComponent constructs an instance, it occurs in three steps:
1031
1032 \list 1
1033 \li The object hierarchy is created, and constant values are assigned.
1034 \li Property bindings are evaluated for the first time.
1035 \li If applicable, QQmlParserStatus::componentComplete() is called on objects.
1036 \endlist
1037
1038 QQmlComponent::beginCreate() differs from QQmlComponent::create() in that it
1039 only performs step 1. QQmlComponent::completeCreate() must be called to
1040 complete steps 2 and 3.
1041
1042 This breaking point is sometimes useful when using attached properties to
1043 communicate information to an instantiated component, as it allows their
1044 initial values to be configured before property bindings take effect.
1045
1046 The ownership of the returned object instance is transferred to the caller.
1047
1048 \note The categorization of bindings into constant values and actual
1049 bindings is intentionally unspecified and may change between versions of Qt
1050 and depending on whether and how you are using \l{qmlcachegen}. You should
1051 not rely on any particular binding to be evaluated either before or after
1052 beginCreate() returns. For example a constant expression like
1053 \e{MyType.EnumValue} may be recognized as such at compile time or deferred
1054 to be executed as binding. The same holds for constant expressions like
1055 \e{-(5)} or \e{"a" + " constant string"}.
1056
1057 \sa completeCreate(), QQmlEngine::ObjectOwnership
1058*/
1059QObject *QQmlComponent::beginCreate(QQmlContext *context)
1060{
1061 Q_D(QQmlComponent);
1062 Q_ASSERT(context);
1063 return d->beginCreate(QQmlContextData::get(context));
1064}
1065
1066static QQmlParserStatus *parserStatusCast(const QQmlType &type, QObject *rv)
1067{
1068 const int parserStatusCast = type.parserStatusCast();
1069 return parserStatusCast == -1
1070 ? nullptr
1071 : reinterpret_cast<QQmlParserStatus *>(reinterpret_cast<char *>(rv) + parserStatusCast);
1072}
1073
1074QObject *QQmlComponentPrivate::beginCreate(QQmlRefPointer<QQmlContextData> context)
1075{
1076 Q_Q(QQmlComponent);
1077 auto cleanup = qScopeGuard([this] {
1078 if (!m_state.errors.isEmpty() && lcQmlComponentGeneral().isDebugEnabled()) {
1079 for (const auto &e : std::as_const(m_state.errors)) {
1080 qCDebug(lcQmlComponentGeneral) << "QQmlComponent: " << e.error.toString();
1081 }
1082 }
1083 });
1084 if (!context) {
1085 qWarning("QQmlComponent: Cannot create a component in a null context");
1086 return nullptr;
1087 }
1088
1089 if (!context->isValid()) {
1090 qWarning("QQmlComponent: Cannot create a component in an invalid context");
1091 return nullptr;
1092 }
1093
1094 if (context->engine() != m_engine) {
1095 qWarning("QQmlComponent: Must create component in context from the same QQmlEngine");
1096 return nullptr;
1097 }
1098
1099 if (m_state.isCompletePending()) {
1100 qWarning("QQmlComponent: Cannot create new component instance before completing the previous");
1101 return nullptr;
1102 }
1103
1104 // filter out temporary errors as they do not really affect component's
1105 // state (they are not part of the document compilation)
1106 m_state.errors.removeIf([](const auto &e) { return e.isTransient; });
1107 m_state.clearRequiredProperties();
1108
1109 if (!q->isReady()) {
1110 qWarning("QQmlComponent: Component is not ready");
1111 return nullptr;
1112 }
1113
1114 // Do not create infinite recursion in object creation
1115 static const int maxCreationDepth = 10;
1116 if (creationDepth >= maxCreationDepth) {
1117 qWarning("QQmlComponent: Component creation is recursing - aborting");
1118 return nullptr;
1119 }
1120
1121 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(m_engine);
1122
1123 enginePriv->inProgressCreations++;
1124 m_state.errors.clear();
1125 m_state.setCompletePending(true);
1126
1127 QObject *rv = nullptr;
1128
1129 const QQmlType type = loadedType();
1130 if (!type.isValid()) {
1131 enginePriv->referenceScarceResources();
1132 const QString *icName = m_inlineComponentName.get();
1133 m_state.initCreator(
1134 context, m_compilationUnit, m_creationContext, icName ? *icName : QString());
1135
1136 QQmlObjectCreator::CreationFlags flags;
1137 if (icName) {
1138 flags = QQmlObjectCreator::InlineComponent;
1139 if (m_start == -1)
1140 m_start = m_compilationUnit->inlineComponentId(*icName);
1141 Q_ASSERT(m_start > 0);
1142 } else {
1143 flags = QQmlObjectCreator::NormalObject;
1144 }
1145
1146 rv = m_state.creator()->create(m_start, nullptr, nullptr, flags);
1147 if (!rv)
1148 m_state.appendCreatorErrors();
1149 enginePriv->dereferenceScarceResources();
1150 } else {
1151 // TODO: extract into function
1152 rv = type.createWithQQmlData();
1153 QQmlPropertyCache::ConstPtr propertyCache = QQmlData::ensurePropertyCache(rv);
1154 if (QQmlParserStatus *parserStatus = parserStatusCast(type, rv)) {
1155 parserStatus->classBegin();
1156 m_state.ensureRequiredPropertyStorage(rv);
1157 } else if (type.finalizerCast() != -1) {
1158 m_state.ensureRequiredPropertyStorage(rv);
1159 }
1160
1161 if (propertyCache) {
1162 for (int i = 0, propertyCount = propertyCache->propertyCount(); i < propertyCount; ++i) {
1163 if (const QQmlPropertyData *propertyData = propertyCache->property(i); propertyData->isRequired()) {
1164 m_state.ensureRequiredPropertyStorage(rv);
1165 RequiredPropertyInfo info;
1166 info.propertyName = propertyData->name(rv);
1167 m_state.addPendingRequiredProperty(rv, propertyData, info);
1168 }
1169 }
1170 } else {
1171 // we couldn't get a propertyCache from ensurePropertyCache
1172 // it is unclear what we can do in that case
1173 // ### TOOD: QTBUG-136560
1174 }
1175 }
1176
1177 if (rv) {
1178 QQmlData *ddata = QQmlData::get(rv);
1179 Q_ASSERT(ddata);
1180 // top-level objects should never get JS ownership.
1181 // if JS ownership is needed this needs to be explicitly undone (like in createObject())
1182 ddata->indestructible = true;
1183 ddata->explicitIndestructibleSet = true;
1184 ddata->rootObjectInCreation = false;
1185
1186 // Assign parent context to the object if we haven't created one.
1187 if (!ddata->outerContext)
1188 ddata->outerContext = context.data();
1189 if (!ddata->context)
1190 ddata->context = context.data();
1191 }
1192
1193 return rv;
1194}
1195
1196void QQmlComponentPrivate::beginDeferred(QQmlEnginePrivate *enginePriv,
1197 QObject *object, DeferredState *deferredState)
1198{
1199 QQmlData *ddata = QQmlData::get(object);
1200 Q_ASSERT(!ddata->deferredData.isEmpty());
1201
1202 deferredState->reserve(ddata->deferredData.size());
1203
1204 for (QQmlData::DeferredData *deferredData : std::as_const(ddata->deferredData)) {
1205 enginePriv->inProgressCreations++;
1206
1207 ConstructionState state;
1208 state.setCompletePending(true);
1209
1210 auto creator = state.initCreator(
1211 deferredData->context->parent(),
1212 deferredData->compilationUnit,
1213 QQmlRefPointer<QQmlContextData>(),
1214 deferredData->inlineComponentName
1215 );
1216
1217 if (!creator->populateDeferredProperties(object, deferredData))
1218 state.appendCreatorErrors();
1219 deferredData->bindings.clear();
1220
1221 deferredState->push_back(std::move(state));
1222 }
1223}
1224
1225void QQmlComponentPrivate::completeDeferred(QQmlEnginePrivate *enginePriv, QQmlComponentPrivate::DeferredState *deferredState)
1226{
1227 for (ConstructionState &state : *deferredState)
1228 complete(enginePriv, &state);
1229}
1230
1231void QQmlComponentPrivate::complete(QQmlEnginePrivate *enginePriv, ConstructionState *state)
1232{
1233 if (state->isCompletePending()) {
1234 QQmlInstantiationInterrupt interrupt;
1235 state->creator()->finalize(interrupt);
1236
1237 state->setCompletePending(false);
1238
1239 enginePriv->inProgressCreations--;
1240
1241 if (0 == enginePriv->inProgressCreations) {
1242 while (enginePriv->erroredBindings) {
1243 enginePriv->warning(enginePriv->erroredBindings->removeError());
1244 }
1245 }
1246 }
1247}
1248
1249/*!
1250 \internal
1251 Finds the matching top-level property with name \a name of the component \a createdComponent.
1252 If it was a required property or an alias to a required property contained in \a
1253 requiredProperties, it is removed from it.
1254 \a requiredProperties must be non-null.
1255
1256 If wasInRequiredProperties is non-null, the referenced boolean is set to true iff the property
1257 was found in requiredProperties.
1258
1259 Returns the QQmlProperty with name \a name (which might be invalid if there is no such property),
1260 for further processing (for instance, actually setting the property value).
1261
1262 Note: This method is used in QQmlComponent and QQmlIncubator to manage required properties. Most
1263 classes which create components should not need it and should only need to call
1264 setInitialProperties.
1265 */
1266QQmlProperty QQmlComponentPrivate::removePropertyFromRequired(
1267 QObject *createdComponent, const QString &name,
1268 RequiredProperties *requiredProperties, QQmlEngine *engine,
1269 bool *wasInRequiredProperties)
1270{
1271 Q_ASSERT(requiredProperties);
1272 QQmlProperty prop(createdComponent, name, engine);
1273 auto privProp = QQmlPropertyPrivate::get(prop);
1274 if (prop.isValid()) {
1275 // resolve outstanding required properties
1276 const QQmlPropertyData *targetProp = &privProp->core;
1277 if (targetProp->isAlias()) {
1278 auto target = createdComponent;
1279 QQmlPropertyIndex originalIndex(targetProp->coreIndex());
1280 QQmlPropertyIndex propIndex;
1281 QQmlPropertyPrivate::findAliasTarget(target, originalIndex, &target, &propIndex);
1282 QQmlData *data = QQmlData::get(target);
1283 Q_ASSERT(data && data->propertyCache);
1284 targetProp = data->propertyCache->property(propIndex.coreIndex());
1285 } else {
1286 // we need to get the pointer from the property cache instead of directly using
1287 // targetProp else the lookup will fail
1288 QQmlData *data = QQmlData::get(createdComponent);
1289 Q_ASSERT(data && data->propertyCache);
1290 targetProp = data->propertyCache->property(targetProp->coreIndex());
1291 }
1292 auto it = requiredProperties->constFind({createdComponent, targetProp});
1293 if (it != requiredProperties->cend()) {
1294 if (wasInRequiredProperties)
1295 *wasInRequiredProperties = true;
1296 requiredProperties->erase(it);
1297 } else {
1298 if (wasInRequiredProperties)
1299 *wasInRequiredProperties = false;
1300 }
1301 }
1302 return prop;
1303}
1304
1305/*!
1306 This method provides advanced control over component instance creation.
1307 In general, programmers should use QQmlComponent::create() to create a
1308 component.
1309
1310 This function completes the component creation begun with QQmlComponent::beginCreate()
1311 and must be called afterwards.
1312
1313 \sa beginCreate()
1314*/
1315void QQmlComponent::completeCreate()
1316{
1317 Q_D(QQmlComponent);
1318
1319 d->completeCreate();
1320}
1321
1322void QQmlComponentPrivate::completeCreate()
1323{
1324 if (m_state.hasUnsetRequiredProperties()) {
1325 for (const auto& unsetRequiredProperty: std::as_const(*m_state.requiredProperties())) {
1326 QQmlError error = unsetRequiredPropertyToQQmlError(unsetRequiredProperty);
1327 m_state.errors.push_back(QQmlComponentPrivate::AnnotatedQmlError { error, true });
1328 }
1329 }
1330
1331 const QQmlType type = loadedType();
1332 if (type.isValid()) {
1333 QObject *rv = m_state.target();
1334 if (QQmlParserStatus *parserStatus = parserStatusCast(type, rv))
1335 parserStatus->componentComplete();
1336
1337 if (const int finalizerCast = type.finalizerCast(); finalizerCast != -1) {
1338 auto *hook = reinterpret_cast<QQmlFinalizerHook *>(
1339 reinterpret_cast<char *>(rv) + finalizerCast);
1340 hook->componentFinalized();
1341 }
1342
1343 /*
1344 We can directly set completePending to false, as finalize is only concerned
1345 with setting up pending bindings, but that cannot happen here, as we're
1346 dealing with a pure C++ type, which cannot have pending bindings
1347 */
1348 m_state.setCompletePending(false);
1349 QQmlEnginePrivate::get(m_engine)->inProgressCreations--;
1350 } else if (m_state.isCompletePending()) {
1351 ++creationDepth;
1352 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(m_engine);
1353 complete(ep, &m_state);
1354 --creationDepth;
1355 }
1356}
1357
1358QQmlComponentAttached::QQmlComponentAttached(QObject *parent)
1359: QObject(parent), m_prev(nullptr), m_next(nullptr)
1360{
1361}
1362
1363QQmlComponentAttached::~QQmlComponentAttached()
1364{
1365 if (m_prev) *m_prev = m_next;
1366 if (m_next) m_next->m_prev = m_prev;
1367 m_prev = nullptr;
1368 m_next = nullptr;
1369}
1370
1371/*!
1372 \internal
1373*/
1374QQmlComponentAttached *QQmlComponent::qmlAttachedProperties(QObject *obj)
1375{
1376 QQmlComponentAttached *a = new QQmlComponentAttached(obj);
1377
1378 QQmlEngine *engine = qmlEngine(obj);
1379 if (!engine)
1380 return a;
1381
1382 QQmlEnginePrivate *p = QQmlEnginePrivate::get(engine);
1383 if (p->activeObjectCreator) { // XXX should only be allowed during begin
1384 a->insertIntoList(p->activeObjectCreator->componentAttachment());
1385 } else {
1386 QQmlData *d = QQmlData::get(obj);
1387 Q_ASSERT(d);
1388 Q_ASSERT(d->context);
1389 d->context->addComponentAttached(a);
1390 }
1391
1392 return a;
1393}
1394
1395/*!
1396 Load the QQmlComponent for \a typeName in the module \a uri.
1397 If the type is implemented via a QML file, \a mode is used to
1398 load it. Types backed by C++ are always loaded synchronously.
1399
1400 \code
1401 QQmlEngine engine;
1402 QQmlComponent component(&engine);
1403 component.loadFromModule("QtQuick", "Item");
1404 // once the component is ready
1405 std::unique_ptr<QObject> item(component.create());
1406 Q_ASSERT(item->metaObject() == &QQuickItem::staticMetaObject);
1407 \endcode
1408
1409 \since 6.5
1410 \sa loadUrl()
1411 */
1412void QQmlComponent::loadFromModule(QAnyStringView uri, QAnyStringView typeName,
1413 QQmlComponent::CompilationMode mode)
1414{
1415 Q_D(QQmlComponent);
1416
1417 QQmlTypeLoader::Mode typeLoaderMode = QQmlTypeLoader::Synchronous;
1418 switch (mode) {
1419 case QQmlComponent::PreferSynchronous:
1420 typeLoaderMode = QQmlTypeLoader::PreferSynchronous;
1421 break;
1422 case QQmlComponent::Asynchronous:
1423 typeLoaderMode = QQmlTypeLoader::Asynchronous;
1424 break;
1425 }
1426
1427 d->prepareLoadFromModule(uri, typeName, typeLoaderMode);
1428 if (d->m_loadHelper->isCompleteOrError())
1429 d->completeLoadFromModule(uri, typeName);
1430 else
1431 d->m_loadHelper->registerCallback(d);
1432}
1433
1434void QQmlComponentPrivate::prepareLoadFromModule(
1435 QAnyStringView uri, QAnyStringView typeName, QQmlTypeLoader::Mode mode)
1436{
1437 // Don't let any old loadHelper call us back anymore.
1438 if (m_loadHelper)
1439 m_loadHelper->unregisterCallback(this);
1440
1441 // LoadHelper must be on the Heap as it derives from QQmlRefCount
1442 m_loadHelper = QQml::makeRefPointer<LoadHelper>(QQmlTypeLoader::get(m_engine), uri, typeName, mode);
1443}
1444
1445void QQmlComponentPrivate::completeLoadFromModule(QAnyStringView uri, QAnyStringView typeName)
1446{
1447 Q_Q(QQmlComponent);
1448
1449 // we always mimic the progressChanged behavior from loadUrl
1450 auto reportError = [&](QString msg) {
1451 QQmlError error;
1452 error.setDescription(msg);
1453 m_state.errors.push_back(std::move(error));
1454 setProgress(1);
1455 emit q->statusChanged(q->Error);
1456 };
1457 auto emitComplete = [&]() {
1458 setProgress(1);
1459 emit q->statusChanged(q->status());
1460 };
1461
1462 setProgress(0);
1463
1464 const QQmlType type = m_loadHelper->type();
1465
1466 if (m_loadHelper->resolveTypeResult() == LoadHelper::ResolveTypeResult::NoSuchModule) {
1467 reportError(QLatin1String(R"(No module named "%1" found)").arg(uri.toString()));
1468 } else if (!type.isValid()) {
1469 reportError(QLatin1String(R"(Module "%1" contains no type named "%2")")
1470 .arg(uri.toString(), typeName.toString()));
1471 } else if (type.isCreatable()) {
1472 emitComplete();
1473 } else if (type.isComposite()) {
1474 QQmlComponent::CompilationMode mode = QQmlComponent::PreferSynchronous;
1475 switch (m_loadHelper->mode()) {
1476 case QQmlTypeLoader::Asynchronous:
1477 mode = QQmlComponent::Asynchronous;
1478 break;
1479 case QQmlTypeLoader::PreferSynchronous:
1480 case QQmlTypeLoader::Synchronous:
1481 mode = QQmlComponent::PreferSynchronous;
1482 break;
1483 }
1484
1485 // loadUrl takes care of signal emission
1486 loadUrl(type.sourceUrl(), mode);
1487 } else if (type.isInlineComponentType()) {
1488 auto baseUrl = type.sourceUrl();
1489 baseUrl.setFragment(QString());
1490
1491 Q_ASSERT(m_progress == 0.0);
1492 {
1493 // we don't want to emit status changes from the "helper" loadUrl below
1494 // because it would signal success to early
1495 QSignalBlocker blockSignals(q);
1496 // we really need to continue in a synchronous way, otherwise we can't check the CU
1497 loadUrl(baseUrl, QQmlComponent::PreferSynchronous);
1498 }
1499 // We do want to emit any progress change that happened in the "helper" loadUrl.
1500 if (m_progress != 0.0)
1501 emit q->progressChanged(m_progress);
1502
1503 if (q->isError()) {
1504 emitComplete();
1505 return;
1506 }
1507 QString elementName = type.elementName();
1508 if (m_compilationUnit->inlineComponentId(elementName) == -1) {
1509 QString realTypeName = typeName.toString();
1510 realTypeName.truncate(realTypeName.indexOf(u'.'));
1511 QString errorMessage = R"(Type "%1" from module "%2" contains no inline component named "%3".)"_L1.arg(
1512 realTypeName, uri.toString(), elementName);
1513 if (elementName == u"qml")
1514 errorMessage += " To load the type \"%1\", drop the \".qml\" extension."_L1.arg(realTypeName);
1515 reportError(std::move(errorMessage));
1516 } else {
1517 m_inlineComponentName = std::make_unique<QString>(std::move(elementName));
1518 emitComplete();
1519 }
1520 } else if (type.isSingleton() || type.isCompositeSingleton()) {
1521 reportError(QLatin1String(R"(%1 is a singleton, and cannot be loaded)").arg(typeName.toString()));
1522 } else {
1523 reportError(QLatin1String("Could not load %1, as the type is uncreatable").arg(typeName.toString()));
1524 }
1525}
1526
1527/*!
1528 Create an object instance from this component using the provided
1529 \a incubator. \a context specifies the context within which to create the object
1530 instance.
1531
1532 If \a context is \nullptr (the default), it will create the instance in the
1533 \l {QQmlEngine::rootContext()}{root context} of the engine.
1534
1535 \a forContext specifies a context that this object creation depends upon.
1536 If the \a forContext is being created asynchronously, and the
1537 \l QQmlIncubator::IncubationMode is \l QQmlIncubator::AsynchronousIfNested,
1538 this object will also be created asynchronously.
1539 If \a forContext is \nullptr (by default), the \a context will be used for this decision.
1540
1541 The created object and its creation status are available via the
1542 \a incubator.
1543
1544 \sa QQmlIncubator
1545*/
1546
1547void QQmlComponent::create(QQmlIncubator &incubator, QQmlContext *context, QQmlContext *forContext)
1548{
1549 Q_D(QQmlComponent);
1550
1551 if (!context)
1552 context = d->m_engine->rootContext();
1553
1554 QQmlRefPointer<QQmlContextData> contextData = QQmlContextData::get(context);
1555 QQmlRefPointer<QQmlContextData> forContextData =
1556 forContext ? QQmlContextData::get(forContext) : contextData;
1557
1558 if (!contextData->isValid()) {
1559 qWarning("QQmlComponent: Cannot create a component in an invalid context");
1560 return;
1561 }
1562
1563 if (contextData->engine() != d->m_engine) {
1564 qWarning("QQmlComponent: Must create component in context from the same QQmlEngine");
1565 return;
1566 }
1567
1568 if (!isReady()) {
1569 qWarning("QQmlComponent: Component is not ready");
1570 return;
1571 }
1572
1573 incubator.clear();
1574 QExplicitlySharedDataPointer<QQmlIncubatorPrivate> p(incubator.d);
1575
1576 if (d->loadedType().isValid()) {
1577 // there isn't really an incubation process for C++ backed types
1578 // so just create the object and signal that we are ready
1579
1580 p->incubateCppBasedComponent(this, context);
1581 return;
1582 }
1583
1584 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(d->m_engine);
1585
1586 p->compilationUnit = d->m_compilationUnit;
1587 p->enginePriv = enginePriv;
1588 p->creator.reset(new QQmlObjectCreator(
1589 contextData, d->m_compilationUnit, d->m_creationContext,
1590 d->m_inlineComponentName ? *d->m_inlineComponentName : QString(), p.data()));
1591 p->subComponentToCreate = d->m_start;
1592
1593 enginePriv->incubate(incubator, forContextData);
1594}
1595
1596/*!
1597 Set top-level \a properties of the \a object that was created from a
1598 QQmlComponent.
1599
1600 This method provides advanced control over component instance creation.
1601 In general, programmers should use
1602 \l QQmlComponent::createWithInitialProperties to create an object instance
1603 from a component.
1604
1605 Use this method after beginCreate and before completeCreate has been called.
1606 If a provided property does not exist, a warning is issued.
1607
1608 This method does not allow setting initial nested properties directly.
1609 Instead, setting an initial value for value type properties with nested
1610 properties can be achieved by creating that value type, assigning its nested
1611 property and then passing the value type as an initial property of the
1612 object to be constructed.
1613
1614 For example, in order to set fond.bold, you can create a QFont, set its
1615 weight to bold and then pass the font as an initial property.
1616
1617 \since 5.14
1618*/
1619void QQmlComponent::setInitialProperties(QObject *object, const QVariantMap &properties)
1620{
1621 Q_D(QQmlComponent);
1622 d->setInitialProperties(object, properties);
1623}
1624
1625bool QQmlComponentPrivate::setInitialProperties(QObject *object, const QVariantMap &properties)
1626{
1627 bool result = true;
1628 for (auto it = properties.constBegin(); it != properties.constEnd(); ++it) {
1629 if (it.key().contains(u'.')) {
1630 auto segments = it.key().split(u'.');
1631 QString description = u"Setting initial properties failed: Cannot initialize nested "_s
1632 u"property."_s;
1633 if (segments.size() >= 2) {
1634 QString s = u" To set %1.%2 as an initial property, create %1, set its "_s
1635 u"property %2, and pass %1 as an initial property."_s;
1636 description += s.arg(segments[0], segments[1]);
1637 }
1638 QQmlError error{};
1639 error.setUrl(m_url);
1640 error.setDescription(description);
1641 qmlWarning(object, error);
1642 return false;
1643 }
1644
1645 // Still try to set them, even if a previous one has failed.
1646 result = setInitialProperty(object, it.key(), it.value()) && result;
1647 }
1648 return result;
1649}
1650
1651/*
1652 This is essentially a copy of QQmlComponent::create(); except it takes the QQmlContextData
1653 arguments instead of QQmlContext which means we don't have to construct the rather weighty
1654 wrapper class for every delegate item.
1655
1656 This is used by QQmlDelegateModel.
1657*/
1658void QQmlComponentPrivate::incubateObject(
1659 QQmlIncubator *incubationTask,
1660 QQmlComponent *component,
1661 QQmlEngine *engine,
1662 const QQmlRefPointer<QQmlContextData> &context,
1663 const QQmlRefPointer<QQmlContextData> &forContext)
1664{
1665 QQmlIncubatorPrivate *incubatorPriv = QQmlIncubatorPrivate::get(incubationTask);
1666 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine);
1667 QQmlComponentPrivate *componentPriv = QQmlComponentPrivate::get(component);
1668
1669 incubatorPriv->compilationUnit = componentPriv->m_compilationUnit;
1670 incubatorPriv->enginePriv = enginePriv;
1671 incubatorPriv->creator.reset(new QQmlObjectCreator(
1672 context, componentPriv->m_compilationUnit, componentPriv->m_creationContext,
1673 m_inlineComponentName ? *m_inlineComponentName : QString()));
1674
1675 if (m_start == -1) {
1676 if (const QString *icName = componentPriv->m_inlineComponentName.get()) {
1677 m_start = m_compilationUnit->inlineComponentId(*icName);
1678 Q_ASSERT(m_start > 0);
1679 }
1680 }
1681 incubatorPriv->subComponentToCreate = componentPriv->m_start;
1682
1683 enginePriv->incubate(*incubationTask, forContext);
1684}
1685
1686
1687
1689
1690namespace QV4 {
1691
1692namespace Heap {
1693
1694#define QmlIncubatorObjectMembers(class, Member)
1695 Member(class, HeapValue, HeapValue, valuemapOrObject)
1696 Member(class, HeapValue, HeapValue, statusChanged)
1697 Member(class, Pointer, QmlContext *, qmlContext)
1698 Member(class, NoMark, QQmlComponentIncubator *, incubator)
1699 Member(class, NoMark, QV4QPointer<QObject>, parent)
1700
1702 DECLARE_MARKOBJECTS(QmlIncubatorObject)
1703
1704 void init(QQmlIncubator::IncubationMode = QQmlIncubator::Asynchronous);
1705 inline void destroy();
1706};
1707
1708}
1709
1711{
1712 V4_OBJECT2(QmlIncubatorObject, Object)
1714
1716 static ReturnedValue method_set_statusChanged(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
1717 static ReturnedValue method_get_status(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
1718 static ReturnedValue method_get_object(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
1719 static ReturnedValue method_forceCompletion(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
1720
1722 void setInitialState(QObject *, RequiredProperties *requiredProperties);
1723};
1724
1725}
1726
1728
1730{
1731public:
1732 QQmlComponentIncubator(QV4::Heap::QmlIncubatorObject *inc, IncubationMode mode)
1734 {
1735 incubatorObject.set(inc->internalClass->engine, inc);
1736 }
1737
1739 QV4::Scope scope(incubatorObject.engine());
1740 QV4::Scoped<QV4::QmlIncubatorObject> i(scope, incubatorObject.as<QV4::QmlIncubatorObject>());
1741 i->statusChanged(s);
1742 }
1743
1744 void setInitialState(QObject *o) override {
1745 QV4::Scope scope(incubatorObject.engine());
1746 QV4::Scoped<QV4::QmlIncubatorObject> i(scope, incubatorObject.as<QV4::QmlIncubatorObject>());
1747 auto d = QQmlIncubatorPrivate::get(this);
1748 i->setInitialState(o, d->requiredProperties());
1749 }
1750
1751 QV4::PersistentValue incubatorObject; // keep a strong internal reference while incubating
1752};
1753
1754
1755static void QQmlComponent_setQmlParent(QObject *me, QObject *parent)
1756{
1757 if (parent) {
1758 me->setParent(parent);
1759 typedef QQmlPrivate::AutoParentFunction APF;
1760 QList<APF> functions = QQmlMetaType::parentFunctions();
1761
1762 bool needParent = false;
1763 for (int ii = 0; ii < functions.size(); ++ii) {
1764 QQmlPrivate::AutoParentResult res = functions.at(ii)(me, parent);
1765 if (res == QQmlPrivate::Parented) {
1766 needParent = false;
1767 break;
1768 } else if (res == QQmlPrivate::IncompatibleParent) {
1769 needParent = true;
1770 }
1771 }
1772 if (needParent)
1773 qmlWarning(me) << "Created graphical object was not placed in the graphics scene.";
1774 }
1775}
1776
1777/*!
1778 \qmlmethod QtObject Component::createObject(QtObject parent, object properties)
1779
1780 Creates and returns an object instance of this component that will have
1781 the given \a parent and \a properties. The \a properties argument is optional.
1782 Returns null if object creation fails.
1783
1784 The object will be created in the same context as the one in which the component
1785 was created. This function will always return null when called on components
1786 which were not created in QML.
1787
1788 If you wish to create an object without setting a parent, specify \c null for
1789 the \a parent value. Note that if the returned object is to be displayed, you
1790 must provide a valid \a parent value or set the returned object's \l{Item::parent}{parent}
1791 property, otherwise the object will not be visible.
1792
1793 If a \a parent is not provided to createObject(), a reference to the returned object must be held so that
1794 it is not destroyed by the garbage collector. This is true regardless of whether \l{Item::parent} is set afterwards,
1795 because setting the Item parent does not change object ownership. Only the graphical parent is changed.
1796
1797 This method accepts an optional \a properties argument that specifies a
1798 map of initial property values for the created object. These values are applied before the object
1799 creation is finalized. This is more efficient than setting property values after object creation,
1800 particularly where large sets of property values are defined, and also allows property bindings
1801 to be set up (using \l{Qt::binding}{Qt.binding}) before the object is created.
1802
1803 The \a properties argument is specified as a map of property-value items. For example, the code
1804 below creates an object with initial \c x and \c y values of 100 and 100, respectively:
1805
1806 \qml
1807 const component = Qt.createComponent("Button.qml");
1808 if (component.status === Component.Ready) {
1809 component.createObject(parent, { x: 100, y: 100 });
1810 }
1811 \endqml
1812
1813 Dynamically created instances can be deleted with the \c destroy() method.
1814 See \l {Dynamic QML Object Creation from JavaScript} for more information.
1815
1816 \sa incubateObject()
1817*/
1818
1819
1820void QQmlComponentPrivate::setInitialProperties(
1821 QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o,
1822 const QV4::Value &v, RequiredProperties *requiredProperties, QObject *createdComponent,
1823 const QQmlObjectCreator *creator)
1824{
1825 QV4::Scope scope(engine);
1826 QV4::ScopedObject object(scope);
1827 QV4::ScopedObject valueMap(scope, v);
1828 QV4::ObjectIterator it(scope, valueMap, QV4::ObjectIterator::EnumerableOnly);
1829 QV4::ScopedString name(scope);
1830 QV4::ScopedValue val(scope);
1831 if (engine->hasException)
1832 return;
1833
1834 // js modules (mjs) have no qmlContext
1835 QV4::ScopedStackFrame frame(scope, qmlContext ? qmlContext : engine->scriptContext());
1836
1837 while (1) {
1838 name = it.nextPropertyNameAsString(val);
1839 if (!name)
1840 break;
1841 object = o;
1842 const QStringList properties = name->toQString().split(QLatin1Char('.'));
1843 bool isTopLevelProperty = properties.size() == 1;
1844 for (int i = 0; i < properties.size() - 1; ++i) {
1845 name = engine->newString(properties.at(i));
1846 object = object->get(name);
1847 if (engine->hasException || !object) {
1848 break;
1849 }
1850 }
1851 if (engine->hasException) {
1852 qmlWarning(createdComponent, engine->catchExceptionAsQmlError());
1853 continue;
1854 }
1855 if (!object) {
1856 QQmlError error;
1857 error.setUrl(qmlContext ? qmlContext->qmlContext()->url() : QUrl());
1858 error.setDescription(QLatin1String("Cannot resolve property \"%1\".")
1859 .arg(properties.join(u'.')));
1860 qmlWarning(createdComponent, error);
1861 continue;
1862 }
1863 const QString lastProperty = properties.last();
1864 name = engine->newString(lastProperty);
1865 object->put(name, val);
1866 if (engine->hasException) {
1867 qmlWarning(createdComponent, engine->catchExceptionAsQmlError());
1868 continue;
1869 } else if (isTopLevelProperty && requiredProperties) {
1870 auto prop = removePropertyFromRequired(createdComponent, name->toQString(),
1871 requiredProperties, engine->qmlEngine());
1872 }
1873
1874 removePendingQPropertyBinding(object, lastProperty, creator);
1875 }
1876
1877 engine->hasException = false;
1878}
1879
1880QQmlError QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(const RequiredPropertyInfo &unsetRequiredProperty)
1881{
1882 QQmlError error;
1883 QString description = QLatin1String("Required property %1 was not initialized").arg(unsetRequiredProperty.propertyName);
1884 switch (unsetRequiredProperty.aliasesToRequired.size()) {
1885 case 0:
1886 break;
1887 case 1: {
1888 const auto info = unsetRequiredProperty.aliasesToRequired.first();
1889 description += QLatin1String("\nIt can be set via the alias property %1 from %2\n").arg(info.propertyName, info.fileUrl.toString());
1890 break;
1891 }
1892 default:
1893 description += QLatin1String("\nIt can be set via one of the following alias properties:");
1894 for (auto aliasInfo: unsetRequiredProperty.aliasesToRequired) {
1895 description += QLatin1String("\n- %1 (%2)").arg(aliasInfo.propertyName, aliasInfo.fileUrl.toString());
1896 }
1897 description += QLatin1Char('\n');
1898 }
1899 error.setDescription(description);
1900 error.setUrl(unsetRequiredProperty.fileUrl);
1901 error.setLine(qmlConvertSourceCoordinate<quint32, int>(
1902 unsetRequiredProperty.location.line()));
1903 error.setColumn(qmlConvertSourceCoordinate<quint32, int>(
1904 unsetRequiredProperty.location.column()));
1905 return error;
1906}
1907
1908#if QT_DEPRECATED_SINCE(6, 3)
1909/*!
1910 \internal
1911*/
1912void QQmlComponent::createObject(QQmlV4FunctionPtr args)
1913{
1914 Q_D(QQmlComponent);
1915 Q_ASSERT(d->m_engine);
1916 Q_ASSERT(args);
1917
1918 qmlWarning(this) << "Unsuitable arguments passed to createObject(). The first argument should "
1919 "be a QObject* or null, and the second argument should be a JavaScript "
1920 "object or a QVariantMap";
1921
1922 QObject *parent = nullptr;
1923 QV4::ExecutionEngine *v4 = args->v4engine();
1924 QV4::Scope scope(v4);
1925 QV4::ScopedValue valuemap(scope, QV4::Value::undefinedValue());
1926
1927 if (args->length() >= 1) {
1928 QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, (*args)[0]);
1929 if (qobjectWrapper)
1930 parent = qobjectWrapper->object();
1931 }
1932
1933 if (args->length() >= 2) {
1934 QV4::ScopedValue v(scope, (*args)[1]);
1935 if (!v->as<QV4::Object>() || v->as<QV4::ArrayObject>()) {
1936 qmlWarning(this) << tr("createObject: value is not an object");
1937 args->setReturnValue(QV4::Encode::null());
1938 return;
1939 }
1940 valuemap = v;
1941 }
1942
1943 QQmlContext *ctxt = creationContext();
1944 if (!ctxt) ctxt = d->m_engine->rootContext();
1945
1946 QObject *rv = beginCreate(ctxt);
1947
1948 if (!rv) {
1949 args->setReturnValue(QV4::Encode::null());
1950 return;
1951 }
1952
1953 QQmlComponent_setQmlParent(rv, parent);
1954
1955 QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(v4, rv));
1956 Q_ASSERT(object->isObject());
1957
1958 if (!valuemap->isUndefined()) {
1959 QV4::Scoped<QV4::QmlContext> qmlContext(scope, v4->qmlContext());
1960 QQmlComponentPrivate::setInitialProperties(
1961 v4, qmlContext, object, valuemap, d->m_state.requiredProperties(), rv,
1962 d->m_state.creator());
1963 }
1964 if (d->m_state.hasUnsetRequiredProperties()) {
1965 QList<QQmlError> errors;
1966 for (const auto &requiredProperty: std::as_const(*d->m_state.requiredProperties())) {
1967 errors.push_back(QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(requiredProperty));
1968 }
1969 qmlWarning(rv, errors);
1970 args->setReturnValue(QV4::Encode::null());
1971 delete rv;
1972 return;
1973 }
1974
1975 d->completeCreate();
1976
1977 Q_ASSERT(QQmlData::get(rv));
1978 QQmlData::get(rv)->explicitIndestructibleSet = false;
1979 QQmlData::get(rv)->indestructible = false;
1980
1981 args->setReturnValue(object->asReturnedValue());
1982}
1983#endif
1984
1985/*!
1986 \internal
1987 */
1988QObject *QQmlComponent::createObject(QObject *parent, const QVariantMap &properties)
1989{
1990 Q_D(QQmlComponent);
1991 Q_ASSERT(d->m_engine);
1992 QObject *rv = d->createWithProperties(
1993 parent, properties, creationContext(), QQmlComponentPrivate::CreateBehavior::Qml);
1994 if (rv) {
1995 QQmlData *qmlData = QQmlData::get(rv);
1996 Q_ASSERT(qmlData);
1997 qmlData->explicitIndestructibleSet = false;
1998 qmlData->indestructible = false;
1999 }
2000 return rv;
2001}
2002
2003/*!
2004 \qmlmethod object Component::incubateObject(QtObject parent, object properties, enumeration mode)
2005
2006 Creates an incubator for an instance of this component. Incubators allow new component
2007 instances to be instantiated asynchronously and do not cause freezes in the UI.
2008
2009 The \a parent argument specifies the parent the created instance will have. Omitting the
2010 parameter or passing null will create an object with no parent. In this case, a reference
2011 to the created object must be held so that it is not destroyed by the garbage collector.
2012
2013 The \a properties argument is specified as a map of property-value items which will be
2014 set on the created object during its construction. \a mode may be Qt.Synchronous or
2015 Qt.Asynchronous, and controls whether the instance is created synchronously or asynchronously.
2016 The default is asynchronous. In some circumstances, even if Qt.Synchronous is specified,
2017 the incubator may create the object asynchronously. This happens if the component calling
2018 incubateObject() is itself being created asynchronously.
2019
2020 All three arguments are optional.
2021
2022 If successful, the method returns an incubator, otherwise null. The incubator has the following
2023 properties:
2024
2025 \list
2026 \li \c status - The status of the incubator. Valid values are Component.Ready, Component.Loading and
2027 Component.Error.
2028 \li \c object - The created object instance. Will only be available once the incubator is in the
2029 Ready status.
2030 \li \c onStatusChanged - Specifies a callback function to be invoked when the status changes. The
2031 status is passed as a parameter to the callback.
2032 \li \c{forceCompletion()} - Call to complete incubation synchronously.
2033 \endlist
2034
2035 The following example demonstrates how to use an incubator:
2036
2037 \qml
2038 const component = Qt.createComponent("Button.qml");
2039
2040 const incubator = component.incubateObject(parent, { x: 10, y: 10 });
2041 if (incubator.status !== Component.Ready) {
2042 incubator.onStatusChanged = function(status) {
2043 if (status === Component.Ready) {
2044 print("Object", incubator.object, "is now ready!");
2045 }
2046 };
2047 } else {
2048 print("Object", incubator.object, "is ready immediately!");
2049 }
2050 \endqml
2051
2052 Dynamically created instances can be deleted with the \c destroy() method.
2053 See \l {Dynamic QML Object Creation from JavaScript} for more information.
2054
2055 \sa createObject()
2056*/
2057
2058/*!
2059 \internal
2060*/
2061void QQmlComponent::incubateObject(QQmlV4FunctionPtr args)
2062{
2063 Q_D(QQmlComponent);
2064 Q_ASSERT(d->m_engine);
2065 Q_UNUSED(d);
2066 Q_ASSERT(args);
2067 QV4::ExecutionEngine *v4 = args->v4engine();
2068 QV4::Scope scope(v4);
2069
2070 QObject *parent = nullptr;
2071 QV4::ScopedValue valuemap(scope, QV4::Value::undefinedValue());
2072 QQmlIncubator::IncubationMode mode = QQmlIncubator::Asynchronous;
2073
2074 if (args->length() >= 1) {
2075 QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, (*args)[0]);
2076 if (qobjectWrapper)
2077 parent = qobjectWrapper->object();
2078 }
2079
2080 if (args->length() >= 2) {
2081 QV4::ScopedValue v(scope, (*args)[1]);
2082 if (v->isNull()) {
2083 } else if (!v->as<QV4::Object>() || v->as<QV4::ArrayObject>()) {
2084 qmlWarning(this) << tr("createObject: value is not an object");
2085 args->setReturnValue(QV4::Encode::null());
2086 return;
2087 } else {
2088 valuemap = v;
2089 }
2090 }
2091
2092 if (args->length() >= 3) {
2093 QV4::ScopedValue val(scope, (*args)[2]);
2094 quint32 v = val->toUInt32();
2095 if (v == 0)
2096 mode = QQmlIncubator::Asynchronous;
2097 else if (v == 1)
2098 mode = QQmlIncubator::AsynchronousIfNested;
2099 }
2100
2101 QQmlComponentExtension *e = componentExtension(args->v4engine());
2102
2103 QV4::Scoped<QV4::QmlIncubatorObject> r(scope, v4->memoryManager->allocate<QV4::QmlIncubatorObject>(mode));
2104 QV4::ScopedObject p(scope, e->incubationProto.value());
2105 r->setPrototypeOf(p);
2106
2107 if (!valuemap->isUndefined())
2108 r->d()->valuemapOrObject.set(scope.engine, valuemap);
2109 r->d()->qmlContext.set(scope.engine, v4->qmlContext());
2110 r->d()->parent = parent;
2111
2112 QQmlIncubator *incubator = r->d()->incubator;
2113 create(*incubator, creationContext());
2114
2115 if (incubator->status() == QQmlIncubator::Null) {
2116 args->setReturnValue(QV4::Encode::null());
2117 } else {
2118 args->setReturnValue(r.asReturnedValue());
2119 }
2120}
2121
2122// XXX used by QSGLoader
2123void QQmlComponentPrivate::initializeObjectWithInitialProperties(QV4::QmlContext *qmlContext, const QV4::Value &valuemap, QObject *toCreate, RequiredProperties *requiredProperties)
2124{
2125 QV4::ExecutionEngine *v4engine = m_engine->handle();
2126 QV4::Scope scope(v4engine);
2127
2128 QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(v4engine, toCreate));
2129 Q_ASSERT(object->as<QV4::Object>());
2130
2131 if (!valuemap.isUndefined()) {
2132 setInitialProperties(
2133 v4engine, qmlContext, object, valuemap, requiredProperties, toCreate, m_state.creator());
2134 }
2135}
2136
2137QQmlComponentExtension::QQmlComponentExtension(QV4::ExecutionEngine *v4)
2138{
2139 QV4::Scope scope(v4);
2140 QV4::ScopedObject proto(scope, v4->newObject());
2141 proto->defineAccessorProperty(QStringLiteral("onStatusChanged"),
2142 QV4::QmlIncubatorObject::method_get_statusChanged, QV4::QmlIncubatorObject::method_set_statusChanged);
2143 proto->defineAccessorProperty(QStringLiteral("status"), QV4::QmlIncubatorObject::method_get_status, nullptr);
2144 proto->defineAccessorProperty(QStringLiteral("object"), QV4::QmlIncubatorObject::method_get_object, nullptr);
2145 proto->defineDefaultProperty(QStringLiteral("forceCompletion"), QV4::QmlIncubatorObject::method_forceCompletion);
2146
2147 incubationProto.set(v4, proto);
2148}
2149
2150QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_object(const FunctionObject *b, const Value *thisObject, const Value *, int)
2151{
2152 QV4::Scope scope(b);
2153 QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>());
2154 if (!o)
2155 THROW_TYPE_ERROR();
2156
2157 return QV4::QObjectWrapper::wrap(scope.engine, o->d()->incubator->object());
2158}
2159
2160QV4::ReturnedValue QV4::QmlIncubatorObject::method_forceCompletion(const FunctionObject *b, const Value *thisObject, const Value *, int)
2161{
2162 QV4::Scope scope(b);
2163 QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>());
2164 if (!o)
2165 THROW_TYPE_ERROR();
2166
2167 o->d()->incubator->forceCompletion();
2168
2169 RETURN_UNDEFINED();
2170}
2171
2172QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_status(const FunctionObject *b, const Value *thisObject, const Value *, int)
2173{
2174 QV4::Scope scope(b);
2175 QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>());
2176 if (!o)
2177 THROW_TYPE_ERROR();
2178
2179 return QV4::Encode(o->d()->incubator->status());
2180}
2181
2182QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_statusChanged(const FunctionObject *b, const Value *thisObject, const Value *, int)
2183{
2184 QV4::Scope scope(b);
2185 QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>());
2186 if (!o)
2187 THROW_TYPE_ERROR();
2188
2189 return QV4::Encode(o->d()->statusChanged);
2190}
2191
2192QV4::ReturnedValue QV4::QmlIncubatorObject::method_set_statusChanged(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
2193{
2194 QV4::Scope scope(b);
2195 QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>());
2196 if (!o || argc < 1)
2197 THROW_TYPE_ERROR();
2198
2199 o->d()->statusChanged.set(scope.engine, argv[0]);
2200
2201 RETURN_UNDEFINED();
2202}
2203
2204QQmlComponentExtension::~QQmlComponentExtension()
2205{
2206}
2207
2208void QV4::Heap::QmlIncubatorObject::init(QQmlIncubator::IncubationMode m)
2209{
2210 Object::init();
2211 valuemapOrObject.set(internalClass->engine, QV4::Value::undefinedValue());
2212 statusChanged.set(internalClass->engine, QV4::Value::undefinedValue());
2213 parent.init();
2214 qmlContext.set(internalClass->engine, nullptr);
2215 incubator = new QQmlComponentIncubator(this, m);
2216}
2217
2218void QV4::Heap::QmlIncubatorObject::destroy() {
2219 delete incubator;
2220 parent.destroy();
2221 Object::destroy();
2222}
2223
2224void QV4::QmlIncubatorObject::setInitialState(QObject *o, RequiredProperties *requiredProperties)
2225{
2226 QQmlComponent_setQmlParent(o, d()->parent);
2227
2228 if (!d()->valuemapOrObject.isUndefined()) {
2229 QV4::ExecutionEngine *v4 = engine();
2230 QV4::Scope scope(v4);
2231 QV4::ScopedObject obj(scope, QV4::QObjectWrapper::wrap(v4, o));
2232 QV4::Scoped<QV4::QmlContext> qmlCtxt(scope, d()->qmlContext);
2233 QQmlComponentPrivate::setInitialProperties(
2234 v4, qmlCtxt, obj, d()->valuemapOrObject, requiredProperties, o,
2235 QQmlIncubatorPrivate::get(d()->incubator)->creator.data());
2236 }
2237}
2238
2239void QV4::QmlIncubatorObject::statusChanged(QQmlIncubator::Status s)
2240{
2241 QV4::Scope scope(engine());
2242
2243 QObject *object = d()->incubator->object();
2244
2245 if (s == QQmlIncubator::Ready) {
2246 // We don't need the arguments anymore, but we still want to hold on to the object so
2247 // that it doesn't get gc'd
2248 d()->valuemapOrObject.set(scope.engine, QV4::QObjectWrapper::wrap(scope.engine, object));
2249
2250 QQmlData *ddata = QQmlData::get(object);
2251 Q_ASSERT(ddata);
2252 ddata->explicitIndestructibleSet = false;
2253 ddata->indestructible = false;
2254 }
2255
2256 QV4::ScopedFunctionObject f(scope, d()->statusChanged);
2257 if (f) {
2258 QV4::JSCallArguments jsCallData(scope, 1);
2259 *jsCallData.thisObject = this;
2260 jsCallData.args[0] = QV4::Value::fromUInt32(s);
2261 f->call(jsCallData);
2262 if (scope.hasException()) {
2263 QQmlError error = scope.engine->catchExceptionAsQmlError();
2264 QQmlEnginePrivate::warning(QQmlEnginePrivate::get(scope.engine->qmlEngine()), error);
2265 }
2266 }
2267
2268 if (s != QQmlIncubator::Loading)
2269 d()->incubator->incubatorObject.clear();
2270}
2271
2272#undef INITIALPROPERTIES_SOURCE
2273
2274QT_END_NAMESPACE
2275
2276#include "moc_qqmlcomponent.cpp"
2277#include "moc_qqmlcomponentattached_p.cpp"
\inmodule QtCore
Definition qobject.h:106
void setInitialState(QObject *o) override
Called after the object is first created, but before complex property bindings are evaluated and,...
void statusChanged(Status s) override
Called when the status of the incubator changes.
QQmlComponentIncubator(QV4::Heap::QmlIncubatorObject *inc, IncubationMode mode)
QV4::PersistentValue incubatorObject
The QQmlError class encapsulates a QML error.
Definition qqmlerror.h:19
Status
Specifies the status of the QQmlIncubator.
DECLARE_HEAP_OBJECT(QmlContext, ExecutionContext)
Definition qjsvalue.h:24
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
static void removePendingQPropertyBinding(QV4::Value *object, const QString &propertyName, const QQmlObjectCreator *creator)
static void QQmlComponent_setQmlParent(QObject *me, QObject *parent)
DEFINE_OBJECT_VTABLE(QV4::QmlIncubatorObject)
V4_DEFINE_EXTENSION(QQmlComponentExtension, componentExtension)
static QQmlParserStatus * parserStatusCast(const QQmlType &type, QObject *rv)
static ReturnedValue method_set_statusChanged(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_status(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_object(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
void statusChanged(QQmlIncubator::Status)
void setInitialState(QObject *, RequiredProperties *requiredProperties)
static ReturnedValue method_forceCompletion(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)