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