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