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