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