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