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 auto setupDData = [&]() {
1160 QQmlData *ddata = QQmlData::get(rv);
1161 Q_ASSERT(ddata);
1162 // top-level objects should never get JS ownership.
1163 // if JS ownership is needed this needs to be explicitly undone (like in createObject())
1164 ddata->indestructible = true;
1165 ddata->explicitIndestructibleSet = true;
1166 ddata->rootObjectInCreation = false;
1167
1168 // Assign parent context to the object if we haven't created one.
1169 if (!ddata->outerContext)
1170 ddata->outerContext = context.data();
1171 if (!ddata->context)
1172 ddata->context = context.data();
1173 };
1174
1175 const QQmlType type = loadedType();
1176 if (!type.isValid()) {
1177 enginePriv->referenceScarceResources();
1178 const QString *icName = m_inlineComponentName.get();
1179 m_state.initCreator(
1180 context, m_compilationUnit, m_creationContext, icName ? *icName : QString());
1181
1182 QQmlObjectCreator::CreationFlags flags;
1183 if (icName) {
1184 flags = QQmlObjectCreator::InlineComponent;
1185 if (m_start == -1)
1186 m_start = m_compilationUnit->inlineComponentId(*icName);
1187 Q_ASSERT(m_start > 0);
1188 } else {
1189 flags = QQmlObjectCreator::NormalObject;
1190 }
1191
1192 rv = m_state.creator()->create(m_start, nullptr, nullptr, flags);
1193 if (!rv)
1194 m_state.appendCreatorErrors();
1195 else
1196 setupDData();
1197 enginePriv->dereferenceScarceResources();
1198 } else {
1199 // TODO: extract into function
1200 rv = type.createWithQQmlData();
1201 QQmlPropertyCache::ConstPtr propertyCache = QQmlData::ensurePropertyCache(rv);
1202 setupDData();
1203 if (QQmlParserStatus *parserStatus = parserStatusCast(type, rv)) {
1204 parserStatus->classBegin();
1205 m_state.ensureRequiredPropertyStorage(rv);
1206 } else if (type.finalizerCast() != -1) {
1207 m_state.ensureRequiredPropertyStorage(rv);
1208 }
1209
1210 if (propertyCache) {
1211 for (int i = 0, propertyCount = propertyCache->propertyCount(); i < propertyCount; ++i) {
1212 if (const QQmlPropertyData *propertyData = propertyCache->property(i); propertyData->isRequired()) {
1213 m_state.ensureRequiredPropertyStorage(rv);
1214 RequiredPropertyInfo info;
1215 info.propertyName = propertyData->name(rv);
1216 m_state.addPendingRequiredProperty(rv, propertyData, info);
1217 }
1218 }
1219 } else {
1220 // we couldn't get a propertyCache from ensurePropertyCache
1221 // it is unclear what we can do in that case
1222 // ### TOOD: QTBUG-136560
1223 }
1224 }
1225
1226 return rv;
1227}
1228
1229void QQmlComponentPrivate::beginDeferred(QQmlEnginePrivate *enginePriv,
1230 QObject *object, DeferredState *deferredState)
1231{
1232 QQmlData *ddata = QQmlData::get(object);
1233 Q_ASSERT(!ddata->deferredData.isEmpty());
1234
1235 deferredState->reserve(ddata->deferredData.size());
1236
1237 for (QQmlData::DeferredData *deferredData : std::as_const(ddata->deferredData)) {
1238 enginePriv->inProgressCreations++;
1239
1240 ConstructionState state;
1241 state.setCompletePending(true);
1242
1243 auto creator = state.initCreator(
1244 deferredData->context->parent(),
1245 deferredData->compilationUnit,
1246 QQmlRefPointer<QQmlContextData>(),
1247 deferredData->inlineComponentName
1248 );
1249
1250 if (!creator->populateDeferredProperties(object, deferredData))
1251 state.appendCreatorErrors();
1252 deferredData->bindings.clear();
1253
1254 deferredState->push_back(std::move(state));
1255 }
1256}
1257
1258void QQmlComponentPrivate::completeDeferred(QQmlEnginePrivate *enginePriv, QQmlComponentPrivate::DeferredState *deferredState)
1259{
1260 for (ConstructionState &state : *deferredState)
1261 complete(enginePriv, &state);
1262}
1263
1264void QQmlComponentPrivate::complete(QQmlEnginePrivate *enginePriv, ConstructionState *state)
1265{
1266 if (state->isCompletePending()) {
1267 QQmlInstantiationInterrupt interrupt;
1268 state->creator()->finalize(interrupt);
1269
1270 state->setCompletePending(false);
1271
1272 enginePriv->inProgressCreations--;
1273
1274 if (0 == enginePriv->inProgressCreations) {
1275 while (enginePriv->erroredBindings) {
1276 enginePriv->warning(enginePriv->erroredBindings->removeError());
1277 }
1278 }
1279 }
1280}
1281
1282/*!
1283 \internal
1284 Finds the matching top-level property with name \a name of the component \a createdComponent.
1285 If it was a required property or an alias to a required property contained in \a
1286 requiredProperties, it is removed from it.
1287 \a requiredProperties must be non-null.
1288
1289 If wasInRequiredProperties is non-null, the referenced boolean is set to true iff the property
1290 was found in requiredProperties.
1291
1292 Returns the QQmlProperty with name \a name (which might be invalid if there is no such property),
1293 for further processing (for instance, actually setting the property value).
1294
1295 Note: This method is used in QQmlComponent and QQmlIncubator to manage required properties. Most
1296 classes which create components should not need it and should only need to call
1297 setInitialProperties.
1298 */
1299QQmlProperty QQmlComponentPrivate::removePropertyFromRequired(
1300 QObject *target, const QString &name, RequiredProperties *requiredProperties,
1301 QQmlEngine *engine, bool *wasInRequiredProperties)
1302{
1303 Q_ASSERT(requiredProperties);
1304
1305 const QQmlProperty prop(target, name, engine);
1306 if (!prop.isValid()) {
1307 if (wasInRequiredProperties)
1308 *wasInRequiredProperties = false;
1309 return prop;
1310 }
1311
1312 const QQmlPropertyPrivate *privProp = QQmlPropertyPrivate::get(prop);
1313 bool found = false;
1314
1315 // resolve outstanding required properties
1316 const QQmlPropertyData *targetProp = &privProp->core;
1317 QQmlData *data = QQmlData::get(target);
1318 Q_ASSERT(data && data->propertyCache);
1319
1320 if (targetProp->isAlias()) {
1321 if (requiredProperties->remove(
1322 { target, data->propertyCache->property(targetProp->coreIndex()) })) {
1323 found = true;
1324 }
1325
1326 QQmlPropertyIndex originalIndex(targetProp->coreIndex());
1327 QQmlPropertyIndex propIndex;
1328 QQmlPropertyPrivate::findAliasTarget(target, originalIndex, &target, &propIndex);
1329 data = QQmlData::get(target);
1330 Q_ASSERT(data && data->propertyCache);
1331 targetProp = data->propertyCache->property(propIndex.coreIndex());
1332 } else {
1333 // we need to get the pointer from the property cache instead of directly using
1334 // targetProp, or else the lookup will fail
1335 targetProp = data->propertyCache->property(targetProp->coreIndex());
1336 }
1337
1338 // Check if the resolved target property itself is required.
1339 if (requiredProperties->remove({target, targetProp}))
1340 found = true;
1341
1342 if (wasInRequiredProperties)
1343 *wasInRequiredProperties = found;
1344
1345 return prop;
1346}
1347
1348/*!
1349 This method provides advanced control over component instance creation.
1350 In general, programmers should use QQmlComponent::create() to create a
1351 component.
1352
1353 This function completes the component creation begun with QQmlComponent::beginCreate()
1354 and must be called afterwards.
1355
1356 \sa beginCreate()
1357*/
1358void QQmlComponent::completeCreate()
1359{
1360 Q_D(QQmlComponent);
1361
1362 d->completeCreate();
1363}
1364
1365void QQmlComponentPrivate::completeCreate()
1366{
1367 if (m_state.hasUnsetRequiredProperties()) {
1368 for (const auto& unsetRequiredProperty: std::as_const(*m_state.requiredProperties())) {
1369 QQmlError error = unsetRequiredPropertyToQQmlError(unsetRequiredProperty);
1370 m_state.errors.push_back(QQmlComponentPrivate::AnnotatedQmlError { error, true });
1371 }
1372 }
1373
1374 const QQmlType type = loadedType();
1375 if (type.isValid()) {
1376 QObject *rv = m_state.target();
1377 if (QQmlParserStatus *parserStatus = parserStatusCast(type, rv))
1378 parserStatus->componentComplete();
1379
1380 if (const int finalizerCast = type.finalizerCast(); finalizerCast != -1) {
1381 auto *hook = reinterpret_cast<QQmlFinalizerHook *>(
1382 reinterpret_cast<char *>(rv) + finalizerCast);
1383 hook->componentFinalized();
1384 }
1385
1386 /*
1387 We can directly set completePending to false, as finalize is only concerned
1388 with setting up pending bindings, but that cannot happen here, as we're
1389 dealing with a pure C++ type, which cannot have pending bindings
1390 */
1391 m_state.setCompletePending(false);
1392 QQmlEnginePrivate::get(m_engine)->inProgressCreations--;
1393 } else if (m_state.isCompletePending()) {
1394 ++creationDepth;
1395 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(m_engine);
1396 complete(ep, &m_state);
1397 --creationDepth;
1398 }
1399}
1400
1401QQmlComponentAttached::QQmlComponentAttached(QObject *parent)
1402: QObject(parent), m_prev(nullptr), m_next(nullptr)
1403{
1404}
1405
1406QQmlComponentAttached::~QQmlComponentAttached()
1407{
1408 if (m_prev) *m_prev = m_next;
1409 if (m_next) m_next->m_prev = m_prev;
1410 m_prev = nullptr;
1411 m_next = nullptr;
1412}
1413
1414/*!
1415 \internal
1416*/
1417QQmlComponentAttached *QQmlComponent::qmlAttachedProperties(QObject *obj)
1418{
1419 QQmlComponentAttached *a = new QQmlComponentAttached(obj);
1420
1421 QQmlEngine *engine = qmlEngine(obj);
1422 if (!engine)
1423 return a;
1424
1425 QQmlEnginePrivate *p = QQmlEnginePrivate::get(engine);
1426 if (p->activeObjectCreator) { // XXX should only be allowed during begin
1427 a->insertIntoList(p->activeObjectCreator->componentAttachment());
1428 } else {
1429 QQmlData *d = QQmlData::get(obj);
1430 Q_ASSERT(d);
1431 Q_ASSERT(d->context);
1432 d->context->addComponentAttached(a);
1433 }
1434
1435 return a;
1436}
1437
1438/*!
1439 Load the QQmlComponent for \a typeName in the module \a uri.
1440 If the type is implemented via a QML file, \a mode is used to
1441 load it. Types backed by C++ are always loaded synchronously.
1442
1443 \code
1444 QQmlEngine engine;
1445 QQmlComponent component(&engine);
1446 component.loadFromModule("QtQuick", "Item");
1447 // once the component is ready
1448 std::unique_ptr<QObject> item(component.create());
1449 Q_ASSERT(item->metaObject() == &QQuickItem::staticMetaObject);
1450 \endcode
1451
1452 \since 6.5
1453 \sa loadUrl()
1454 */
1455void QQmlComponent::loadFromModule(QAnyStringView uri, QAnyStringView typeName,
1456 QQmlComponent::CompilationMode mode)
1457{
1458 Q_D(QQmlComponent);
1459
1460 QQmlTypeLoader::Mode typeLoaderMode = QQmlTypeLoader::Synchronous;
1461 switch (mode) {
1462 case QQmlComponent::PreferSynchronous:
1463 typeLoaderMode = QQmlTypeLoader::PreferSynchronous;
1464 break;
1465 case QQmlComponent::Asynchronous:
1466 typeLoaderMode = QQmlTypeLoader::Asynchronous;
1467 break;
1468 }
1469
1470 d->prepareLoadFromModule(uri, typeName, typeLoaderMode);
1471 if (d->m_loadHelper->isCompleteOrError())
1472 d->completeLoadFromModule(uri, typeName);
1473 else
1474 d->m_loadHelper->registerCallback(d);
1475}
1476
1477void QQmlComponentPrivate::prepareLoadFromModule(
1478 QAnyStringView uri, QAnyStringView typeName, QQmlTypeLoader::Mode mode)
1479{
1480 // Don't let any old loadHelper call us back anymore.
1481 if (m_loadHelper)
1482 m_loadHelper->unregisterCallback(this);
1483
1484 // LoadHelper must be on the Heap as it derives from QQmlRefCount
1485 m_loadHelper = QQml::makeRefPointer<LoadHelper>(QQmlTypeLoader::get(m_engine), uri, typeName, mode);
1486}
1487
1488void QQmlComponentPrivate::completeLoadFromModule(QAnyStringView uri, QAnyStringView typeName)
1489{
1490 Q_Q(QQmlComponent);
1491
1492 // we always mimic the progressChanged behavior from loadUrl
1493 auto reportError = [&](QString msg) {
1494 QQmlError error;
1495 error.setDescription(msg);
1496 m_state.errors.push_back(std::move(error));
1497 setProgress(1);
1498 emit q->statusChanged(q->Error);
1499 };
1500 auto emitComplete = [&]() {
1501 setProgress(1);
1502 emit q->statusChanged(q->status());
1503 };
1504
1505 setProgress(0);
1506
1507 const QQmlType type = m_loadHelper->type();
1508
1509 if (m_loadHelper->resolveTypeResult() == LoadHelper::ResolveTypeResult::NoSuchModule) {
1510 reportError(QLatin1String(R"(No module named "%1" found)").arg(uri.toString()));
1511 } else if (!type.isValid()) {
1512 reportError(QLatin1String(R"(Module "%1" contains no type named "%2")")
1513 .arg(uri.toString(), typeName.toString()));
1514 } else if (type.isCreatable()) {
1515 emitComplete();
1516 } else if (type.isInlineComponent()) {
1517 auto baseUrl = type.sourceUrl();
1518 baseUrl.setFragment(QString());
1519
1520 Q_ASSERT(m_progress == 0.0);
1521 {
1522 // we don't want to emit status changes from the "helper" loadUrl below
1523 // because it would signal success to early
1524 QSignalBlocker blockSignals(q);
1525 // we really need to continue in a synchronous way, otherwise we can't check the CU
1526 loadUrl(baseUrl, QQmlComponent::PreferSynchronous);
1527 }
1528 // We do want to emit any progress change that happened in the "helper" loadUrl.
1529 if (m_progress != 0.0)
1530 emit q->progressChanged(m_progress);
1531
1532 if (q->isError()) {
1533 emitComplete();
1534 return;
1535 }
1536 QString elementName = type.elementName();
1537 if (m_compilationUnit->inlineComponentId(elementName) == -1) {
1538 QString realTypeName = typeName.toString();
1539 realTypeName.truncate(realTypeName.indexOf(u'.'));
1540 QString errorMessage = R"(Type "%1" from module "%2" contains no inline component named "%3".)"_L1.arg(
1541 realTypeName, uri.toString(), elementName);
1542 if (elementName == u"qml")
1543 errorMessage += " To load the type \"%1\", drop the \".qml\" extension."_L1.arg(realTypeName);
1544 reportError(std::move(errorMessage));
1545 } else {
1546 m_inlineComponentName = std::make_unique<QString>(std::move(elementName));
1547 emitComplete();
1548 }
1549 } else if (type.isComposite()) {
1550 QQmlComponent::CompilationMode mode = QQmlComponent::PreferSynchronous;
1551 switch (m_loadHelper->mode()) {
1552 case QQmlTypeLoader::Asynchronous:
1553 mode = QQmlComponent::Asynchronous;
1554 break;
1555 case QQmlTypeLoader::PreferSynchronous:
1556 case QQmlTypeLoader::Synchronous:
1557 mode = QQmlComponent::PreferSynchronous;
1558 break;
1559 }
1560
1561 // loadUrl takes care of signal emission
1562 loadUrl(type.sourceUrl(), mode);
1563 } else if (type.isSingleton()) {
1564 // TODO: This is meant to cover composite singletons but doesn't.
1565 reportError(QLatin1String(R"(%1 is a singleton, and cannot be loaded)").arg(typeName.toString()));
1566 } else {
1567 reportError(QLatin1String("Could not load %1, as the type is uncreatable").arg(typeName.toString()));
1568 }
1569}
1570
1571/*!
1572 Create an object instance from this component using the provided
1573 \a incubator. \a context specifies the context within which to create the object
1574 instance.
1575
1576 If \a context is \nullptr (the default), it will create the instance in the
1577 \l {QQmlEngine::rootContext()}{root context} of the engine.
1578
1579 \a forContext specifies a context that this object creation depends upon.
1580 If the \a forContext is being created asynchronously, and the
1581 \l QQmlIncubator::IncubationMode is \l QQmlIncubator::AsynchronousIfNested,
1582 this object will also be created asynchronously.
1583 If \a forContext is \nullptr (by default), the \a context will be used for this decision.
1584
1585 The created object and its creation status are available via the
1586 \a incubator.
1587
1588 \sa QQmlIncubator
1589*/
1590
1591void QQmlComponent::create(QQmlIncubator &incubator, QQmlContext *context, QQmlContext *forContext)
1592{
1593 Q_D(QQmlComponent);
1594
1595 if (!context)
1596 context = d->m_engine->rootContext();
1597
1598 QQmlRefPointer<QQmlContextData> contextData = QQmlContextData::get(context);
1599 QQmlRefPointer<QQmlContextData> forContextData =
1600 forContext ? QQmlContextData::get(forContext) : contextData;
1601
1602 if (!contextData->isValid()) {
1603 qWarning("QQmlComponent: Cannot create a component in an invalid context");
1604 return;
1605 }
1606
1607 if (contextData->engine() != d->m_engine) {
1608 qWarning("QQmlComponent: Must create component in context from the same QQmlEngine");
1609 return;
1610 }
1611
1612 if (!isReady()) {
1613 qWarning("QQmlComponent: Component is not ready");
1614 return;
1615 }
1616
1617 incubator.clear();
1618 QExplicitlySharedDataPointer<QQmlIncubatorPrivate> p(incubator.d);
1619
1620 if (d->loadedType().isValid()) {
1621 // there isn't really an incubation process for C++ backed types
1622 // so just create the object and signal that we are ready
1623
1624 p->incubateCppBasedComponent(this, context);
1625 return;
1626 }
1627
1628 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(d->m_engine);
1629
1630 p->compilationUnit = d->m_compilationUnit;
1631 p->enginePriv = enginePriv;
1632 p->creator.reset(new QQmlObjectCreator(
1633 contextData, d->m_compilationUnit, d->m_creationContext,
1634 d->m_inlineComponentName ? *d->m_inlineComponentName : QString(), p.data()));
1635 p->subComponentToCreate = d->m_start;
1636
1637 enginePriv->incubate(incubator, forContextData);
1638}
1639
1640/*!
1641 Set top-level \a properties of the \a object that was created from a
1642 QQmlComponent.
1643
1644 This method provides advanced control over component instance creation.
1645 In general, programmers should use
1646 \l QQmlComponent::createWithInitialProperties to create an object instance
1647 from a component.
1648
1649 Use this method after beginCreate and before completeCreate has been called.
1650 If a provided property does not exist, a warning is issued.
1651
1652 This method does not allow setting initial nested properties directly.
1653 Instead, setting an initial value for value type properties with nested
1654 properties can be achieved by creating that value type, assigning its nested
1655 property and then passing the value type as an initial property of the
1656 object to be constructed.
1657
1658 For example, in order to set fond.bold, you can create a QFont, set its
1659 weight to bold and then pass the font as an initial property.
1660
1661 \since 5.14
1662*/
1663void QQmlComponent::setInitialProperties(QObject *object, const QVariantMap &properties)
1664{
1665 Q_D(QQmlComponent);
1666 d->setInitialProperties(object, properties);
1667}
1668
1669bool QQmlComponentPrivate::setInitialProperties(QObject *object, const QVariantMap &properties)
1670{
1671 bool result = true;
1672 for (auto it = properties.constBegin(); it != properties.constEnd(); ++it) {
1673 if (it.key().contains(u'.')) {
1674 auto segments = it.key().split(u'.');
1675 QString description = u"Setting initial properties failed: Cannot initialize nested "_s
1676 u"property."_s;
1677 if (segments.size() >= 2) {
1678 QString s = u" To set %1.%2 as an initial property, create %1, set its "_s
1679 u"property %2, and pass %1 as an initial property."_s;
1680 description += s.arg(segments[0], segments[1]);
1681 }
1682 QQmlError error{};
1683 error.setUrl(m_url);
1684 error.setDescription(description);
1685 qmlWarning(object, error);
1686 return false;
1687 }
1688
1689 // Still try to set them, even if a previous one has failed.
1690 result = setInitialProperty(object, it.key(), it.value()) && result;
1691 }
1692 return result;
1693}
1694
1695/*
1696 This is essentially a copy of QQmlComponent::create(); except it takes the QQmlContextData
1697 arguments instead of QQmlContext which means we don't have to construct the rather weighty
1698 wrapper class for every delegate item.
1699
1700 This is used by QQmlDelegateModel.
1701*/
1702void QQmlComponentPrivate::incubateObject(
1703 QQmlIncubator *incubationTask,
1704 QQmlComponent *component,
1705 QQmlEngine *engine,
1706 const QQmlRefPointer<QQmlContextData> &context,
1707 const QQmlRefPointer<QQmlContextData> &forContext)
1708{
1709 QQmlIncubatorPrivate *incubatorPriv = QQmlIncubatorPrivate::get(incubationTask);
1710 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine);
1711 QQmlComponentPrivate *componentPriv = QQmlComponentPrivate::get(component);
1712
1713 incubatorPriv->compilationUnit = componentPriv->m_compilationUnit;
1714 incubatorPriv->enginePriv = enginePriv;
1715 incubatorPriv->creator.reset(new QQmlObjectCreator(
1716 context, componentPriv->m_compilationUnit, componentPriv->m_creationContext,
1717 m_inlineComponentName ? *m_inlineComponentName : QString()));
1718
1719 if (m_start == -1) {
1720 if (const QString *icName = componentPriv->m_inlineComponentName.get()) {
1721 m_start = m_compilationUnit->inlineComponentId(*icName);
1722 Q_ASSERT(m_start > 0);
1723 }
1724 }
1725 incubatorPriv->subComponentToCreate = componentPriv->m_start;
1726
1727 enginePriv->incubate(*incubationTask, forContext);
1728}
1729
1730
1731
1733
1734namespace QV4 {
1735
1736namespace Heap {
1737
1738#define QmlIncubatorObjectMembers(class, Member)
1739 Member(class, HeapValue, HeapValue, valuemapOrObject)
1740 Member(class, HeapValue, HeapValue, statusChanged)
1741 Member(class, Pointer, QmlContext *, qmlContext)
1742 Member(class, NoMark, QQmlComponentIncubator *, incubator)
1743 Member(class, NoMark, QV4QPointer<QObject>, parent)
1744
1746 DECLARE_MARKOBJECTS(QmlIncubatorObject)
1747
1748 void init(QQmlIncubator::IncubationMode = QQmlIncubator::Asynchronous);
1749 inline void destroy();
1750};
1751
1752}
1753
1755{
1756 V4_OBJECT2(QmlIncubatorObject, Object)
1758
1760 static ReturnedValue method_set_statusChanged(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
1761 static ReturnedValue method_get_status(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
1762 static ReturnedValue method_get_object(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
1763 static ReturnedValue method_forceCompletion(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
1764
1766 void setInitialState(QObject *, RequiredProperties *requiredProperties);
1767};
1768
1769}
1770
1772
1774{
1775public:
1776 QQmlComponentIncubator(QV4::Heap::QmlIncubatorObject *inc, IncubationMode mode)
1778 {
1779 incubatorObject.set(inc->internalClass->engine, inc);
1780 }
1781
1783 QV4::Scope scope(incubatorObject.engine());
1784 QV4::Scoped<QV4::QmlIncubatorObject> i(scope, incubatorObject.as<QV4::QmlIncubatorObject>());
1785 i->statusChanged(s);
1786 }
1787
1788 void setInitialState(QObject *o) override {
1789 QV4::Scope scope(incubatorObject.engine());
1790 QV4::Scoped<QV4::QmlIncubatorObject> i(scope, incubatorObject.as<QV4::QmlIncubatorObject>());
1791 auto d = QQmlIncubatorPrivate::get(this);
1792 i->setInitialState(o, d->requiredProperties());
1793 }
1794
1795 QV4::PersistentValue incubatorObject; // keep a strong internal reference while incubating
1796};
1797
1798
1799static void QQmlComponent_setQmlParent(QObject *me, QObject *parent)
1800{
1801 if (parent) {
1802 me->setParent(parent);
1803 typedef QQmlPrivate::AutoParentFunction APF;
1804 QList<APF> functions = QQmlMetaType::parentFunctions();
1805
1806 bool needParent = false;
1807 for (int ii = 0; ii < functions.size(); ++ii) {
1808 QQmlPrivate::AutoParentResult res = functions.at(ii)(me, parent);
1809 if (res == QQmlPrivate::Parented) {
1810 needParent = false;
1811 break;
1812 } else if (res == QQmlPrivate::IncompatibleParent) {
1813 needParent = true;
1814 }
1815 }
1816 if (needParent)
1817 qmlWarning(me) << "Created graphical object was not placed in the graphics scene.";
1818 }
1819}
1820
1821/*!
1822 \qmlmethod QtObject Component::createObject(QtObject parent, var properties)
1823
1824 Creates and returns an object instance of this component that will have
1825 the given \a parent and \a properties. The \a properties argument is optional.
1826 Returns null if object creation fails.
1827
1828 The object will be created in the same context as the one in which the component
1829 was created. This function will always return null when called on components
1830 which were not created in QML.
1831
1832 If you wish to create an object without setting a parent, specify \c null for
1833 the \a parent value. Note that if the returned object is to be displayed, you
1834 must provide a valid \a parent value or set the returned object's \l{Item::parent}{parent}
1835 property, otherwise the object will not be visible.
1836
1837 If a \a parent is not provided to createObject(), a reference to the returned object must be held so that
1838 it is not destroyed by the garbage collector. This is true regardless of whether \l{Item::parent} is set afterwards,
1839 because setting the Item parent does not change object ownership. Only the graphical parent is changed.
1840
1841 This method accepts an optional \a properties argument that specifies a
1842 map of initial property values for the created object. These values are applied before the object
1843 creation is finalized. This is more efficient than setting property values after object creation,
1844 particularly where large sets of property values are defined, and also allows property bindings
1845 to be set up (using \l{Qt::binding}{Qt.binding}) before the object is created.
1846
1847 The \a properties argument is specified as a map of property-value items. For example, the code
1848 below creates an object with initial \c x and \c y values of 100 and 100, respectively:
1849
1850 \qml
1851 const component = Qt.createComponent("Button.qml");
1852 if (component.status === Component.Ready) {
1853 component.createObject(parent, { x: 100, y: 100 });
1854 }
1855 \endqml
1856
1857 Dynamically created instances can be deleted with the \c destroy() method.
1858 See \l {Dynamic QML Object Creation from JavaScript} for more information.
1859
1860 \sa incubateObject()
1861*/
1862
1863
1864void QQmlComponentPrivate::setInitialProperties(
1865 QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o,
1866 const QV4::Value &v, RequiredProperties *requiredProperties, QObject *createdComponent,
1867 const QQmlObjectCreator *creator)
1868{
1869 QV4::Scope scope(engine);
1870 QV4::ScopedObject object(scope);
1871 QV4::ScopedObject valueMap(scope, v);
1872 QV4::ObjectIterator it(scope, valueMap, QV4::ObjectIterator::EnumerableOnly);
1873 QV4::ScopedString name(scope);
1874 QV4::ScopedValue val(scope);
1875 if (engine->hasException)
1876 return;
1877
1878 // js modules (mjs) have no qmlContext
1879 QV4::ScopedStackFrame frame(scope, qmlContext ? qmlContext : engine->scriptContext());
1880
1881 while (1) {
1882 name = it.nextPropertyNameAsString(val);
1883 if (!name)
1884 break;
1885 object = o;
1886 const QStringList properties = name->toQString().split(QLatin1Char('.'));
1887 bool isTopLevelProperty = properties.size() == 1;
1888 for (int i = 0; i < properties.size() - 1; ++i) {
1889 name = engine->newString(properties.at(i));
1890 object = object->get(name);
1891 if (engine->hasException || !object) {
1892 break;
1893 }
1894 }
1895 if (engine->hasException) {
1896 qmlWarning(createdComponent, engine->catchExceptionAsQmlError());
1897 continue;
1898 }
1899 if (!object) {
1900 QQmlError error;
1901 error.setUrl(qmlContext ? qmlContext->qmlContext()->url() : QUrl());
1902 error.setDescription(QLatin1String("Cannot resolve property \"%1\".")
1903 .arg(properties.join(u'.')));
1904 qmlWarning(createdComponent, error);
1905 continue;
1906 }
1907 const QString lastProperty = properties.last();
1908 name = engine->newString(lastProperty);
1909 object->put(name, val);
1910 if (engine->hasException) {
1911 qmlWarning(createdComponent, engine->catchExceptionAsQmlError());
1912 continue;
1913 } else if (isTopLevelProperty && requiredProperties) {
1914 auto prop = removePropertyFromRequired(createdComponent, name->toQString(),
1915 requiredProperties, engine->qmlEngine());
1916 }
1917
1918 removePendingQPropertyBinding(object, lastProperty, creator);
1919 }
1920
1921 engine->hasException = false;
1922}
1923
1924QQmlError QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(const RequiredPropertyInfo &unsetRequiredProperty)
1925{
1926 QQmlError error;
1927 QString description = QLatin1String("Required property %1 was not initialized").arg(unsetRequiredProperty.propertyName);
1928 switch (unsetRequiredProperty.aliasesToRequired.size()) {
1929 case 0:
1930 break;
1931 case 1: {
1932 const auto info = unsetRequiredProperty.aliasesToRequired.first();
1933 description += QLatin1String("\nIt can be set via the alias property %1 from %2\n").arg(info.propertyName, info.fileUrl.toString());
1934 break;
1935 }
1936 default:
1937 description += QLatin1String("\nIt can be set via one of the following alias properties:");
1938 for (const auto &aliasInfo: unsetRequiredProperty.aliasesToRequired) {
1939 description += QLatin1String("\n- %1 (%2)").arg(aliasInfo.propertyName, aliasInfo.fileUrl.toString());
1940 }
1941 description += QLatin1Char('\n');
1942 }
1943 error.setDescription(description);
1944 error.setUrl(unsetRequiredProperty.fileUrl);
1945 error.setLine(qmlConvertSourceCoordinate<quint32, int>(
1946 unsetRequiredProperty.location.line()));
1947 error.setColumn(qmlConvertSourceCoordinate<quint32, int>(
1948 unsetRequiredProperty.location.column()));
1949 return error;
1950}
1951
1952#if QT_DEPRECATED_SINCE(6, 3)
1953/*!
1954 \internal
1955*/
1956void QQmlComponent::createObject(QQmlV4FunctionPtr args)
1957{
1958 Q_D(QQmlComponent);
1959 Q_ASSERT(d->m_engine);
1960 Q_ASSERT(args);
1961
1962 qmlWarning(this) << "Unsuitable arguments passed to createObject(). The first argument should "
1963 "be a QObject* or null, and the second argument should be a JavaScript "
1964 "object or a QVariantMap";
1965
1966 QObject *parent = nullptr;
1967 QV4::ExecutionEngine *v4 = args->v4engine();
1968 QV4::Scope scope(v4);
1969 QV4::ScopedValue valuemap(scope, QV4::Value::undefinedValue());
1970
1971 if (args->length() >= 1) {
1972 QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, (*args)[0]);
1973 if (qobjectWrapper)
1974 parent = qobjectWrapper->object();
1975 }
1976
1977 if (args->length() >= 2) {
1978 QV4::ScopedValue v(scope, (*args)[1]);
1979 if (!v->as<QV4::Object>() || v->as<QV4::ArrayObject>()) {
1980 qmlWarning(this) << tr("createObject: value is not an object");
1981 args->setReturnValue(QV4::Encode::null());
1982 return;
1983 }
1984 valuemap = v;
1985 }
1986
1987 QQmlContext *ctxt = creationContext();
1988 if (!ctxt) ctxt = d->m_engine->rootContext();
1989
1990 QObject *rv = beginCreate(ctxt);
1991
1992 if (!rv) {
1993 args->setReturnValue(QV4::Encode::null());
1994 return;
1995 }
1996
1997 QQmlComponent_setQmlParent(rv, parent);
1998
1999 QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(v4, rv));
2000 Q_ASSERT(object->isObject());
2001
2002 if (!valuemap->isUndefined()) {
2003 QV4::Scoped<QV4::QmlContext> qmlContext(scope, v4->qmlContext());
2004 QQmlComponentPrivate::setInitialProperties(
2005 v4, qmlContext, object, valuemap, d->m_state.requiredProperties(), rv,
2006 d->m_state.creator());
2007 }
2008 if (d->m_state.hasUnsetRequiredProperties()) {
2009 QList<QQmlError> errors;
2010 for (const auto &requiredProperty: std::as_const(*d->m_state.requiredProperties())) {
2011 errors.push_back(QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(requiredProperty));
2012 }
2013 qmlWarning(rv, errors);
2014 args->setReturnValue(QV4::Encode::null());
2015 delete rv;
2016 return;
2017 }
2018
2019 d->completeCreate();
2020
2021 Q_ASSERT(QQmlData::get(rv));
2022 QQmlData::get(rv)->explicitIndestructibleSet = false;
2023 QQmlData::get(rv)->indestructible = false;
2024
2025 args->setReturnValue(object->asReturnedValue());
2026}
2027#endif
2028
2029/*!
2030 \internal
2031 */
2032QObject *QQmlComponent::createObject(QObject *parent, const QVariantMap &properties)
2033{
2034 Q_D(QQmlComponent);
2035 Q_ASSERT(d->m_engine);
2036 QObject *rv = d->createWithProperties(
2037 parent, properties, creationContext(), QQmlComponentPrivate::CreateBehavior::Qml);
2038 if (rv) {
2039 QQmlData *qmlData = QQmlData::get(rv);
2040 Q_ASSERT(qmlData);
2041 qmlData->explicitIndestructibleSet = false;
2042 qmlData->indestructible = false;
2043 }
2044 return rv;
2045}
2046
2047/*!
2048 \qmlmethod var Component::incubateObject(QtObject parent, var properties, enumeration mode)
2049
2050 Creates an incubator for an instance of this component. Incubators allow new component
2051 instances to be instantiated asynchronously and do not cause freezes in the UI.
2052
2053 The \a parent argument specifies the parent the created instance will have. Omitting the
2054 parameter or passing null will create an object with no parent. In this case, a reference
2055 to the created object must be held so that it is not destroyed by the garbage collector.
2056
2057 The \a properties argument is specified as a map of property-value items which will be
2058 set on the created object during its construction. \a mode may be Qt.Synchronous or
2059 Qt.Asynchronous, and controls whether the instance is created synchronously or asynchronously.
2060 The default is asynchronous. In some circumstances, even if Qt.Synchronous is specified,
2061 the incubator may create the object asynchronously. This happens if the component calling
2062 incubateObject() is itself being created asynchronously.
2063
2064 All three arguments are optional.
2065
2066 If successful, the method returns an incubator, otherwise null. The incubator has the following
2067 properties:
2068
2069 \list
2070 \li \c status - The status of the incubator. Valid values are Component.Ready, Component.Loading and
2071 Component.Error.
2072 \li \c object - The created object instance. Will only be available once the incubator is in the
2073 Ready status.
2074 \li \c onStatusChanged - Specifies a callback function to be invoked when the status changes. The
2075 status is passed as a parameter to the callback.
2076 \li \c{forceCompletion()} - Call to complete incubation synchronously.
2077 \endlist
2078
2079 The following example demonstrates how to use an incubator:
2080
2081 \qml
2082 const component = Qt.createComponent("Button.qml");
2083
2084 const incubator = component.incubateObject(parent, { x: 10, y: 10 });
2085 if (incubator.status !== Component.Ready) {
2086 incubator.onStatusChanged = function(status) {
2087 if (status === Component.Ready) {
2088 print("Object", incubator.object, "is now ready!");
2089 }
2090 };
2091 } else {
2092 print("Object", incubator.object, "is ready immediately!");
2093 }
2094 \endqml
2095
2096 Dynamically created instances can be deleted with the \c destroy() method.
2097 See \l {Dynamic QML Object Creation from JavaScript} for more information.
2098
2099 \sa createObject()
2100*/
2101
2102/*!
2103 \internal
2104*/
2105void QQmlComponent::incubateObject(QQmlV4FunctionPtr args)
2106{
2107 Q_D(QQmlComponent);
2108 Q_ASSERT(d->m_engine);
2109 Q_UNUSED(d);
2110 Q_ASSERT(args);
2111 QV4::ExecutionEngine *v4 = args->v4engine();
2112 QV4::Scope scope(v4);
2113
2114 QObject *parent = nullptr;
2115 QV4::ScopedValue valuemap(scope, QV4::Value::undefinedValue());
2116 QQmlIncubator::IncubationMode mode = QQmlIncubator::Asynchronous;
2117
2118 if (args->length() >= 1) {
2119 QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, (*args)[0]);
2120 if (qobjectWrapper)
2121 parent = qobjectWrapper->object();
2122 }
2123
2124 if (args->length() >= 2) {
2125 QV4::ScopedValue v(scope, (*args)[1]);
2126 if (v->isNull()) {
2127 } else if (!v->as<QV4::Object>() || v->as<QV4::ArrayObject>()) {
2128 qmlWarning(this) << tr("createObject: value is not an object");
2129 args->setReturnValue(QV4::Encode::null());
2130 return;
2131 } else {
2132 valuemap = v;
2133 }
2134 }
2135
2136 if (args->length() >= 3) {
2137 QV4::ScopedValue val(scope, (*args)[2]);
2138 quint32 v = val->toUInt32();
2139 if (v == 0)
2140 mode = QQmlIncubator::Asynchronous;
2141 else if (v == 1)
2142 mode = QQmlIncubator::AsynchronousIfNested;
2143 }
2144
2145 QQmlComponentExtension *e = componentExtension(args->v4engine());
2146
2147 QV4::Scoped<QV4::QmlIncubatorObject> r(scope, v4->memoryManager->allocate<QV4::QmlIncubatorObject>(mode));
2148 QV4::ScopedObject p(scope, e->incubationProto.value());
2149 r->setPrototypeOf(p);
2150
2151 if (!valuemap->isUndefined())
2152 r->d()->valuemapOrObject.set(scope.engine, valuemap);
2153 r->d()->qmlContext.set(scope.engine, v4->qmlContext());
2154 r->d()->parent = parent;
2155
2156 QQmlIncubator *incubator = r->d()->incubator;
2157 create(*incubator, creationContext());
2158
2159 if (incubator->status() == QQmlIncubator::Null) {
2160 args->setReturnValue(QV4::Encode::null());
2161 } else {
2162 args->setReturnValue(r.asReturnedValue());
2163 }
2164}
2165
2166// XXX used by QSGLoader
2167void QQmlComponentPrivate::initializeObjectWithInitialProperties(QV4::QmlContext *qmlContext, const QV4::Value &valuemap, QObject *toCreate, RequiredProperties *requiredProperties)
2168{
2169 QV4::ExecutionEngine *v4engine = m_engine->handle();
2170 QV4::Scope scope(v4engine);
2171
2172 QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(v4engine, toCreate));
2173 Q_ASSERT(object->as<QV4::Object>());
2174
2175 if (!valuemap.isUndefined()) {
2176 setInitialProperties(
2177 v4engine, qmlContext, object, valuemap, requiredProperties, toCreate, m_state.creator());
2178 }
2179}
2180
2181QQmlComponentExtension::QQmlComponentExtension(QV4::ExecutionEngine *v4)
2182{
2183 QV4::Scope scope(v4);
2184 QV4::ScopedObject proto(scope, v4->newObject());
2185 proto->defineAccessorProperty(QStringLiteral("onStatusChanged"),
2186 QV4::QmlIncubatorObject::method_get_statusChanged, QV4::QmlIncubatorObject::method_set_statusChanged);
2187 proto->defineAccessorProperty(QStringLiteral("status"), QV4::QmlIncubatorObject::method_get_status, nullptr);
2188 proto->defineAccessorProperty(QStringLiteral("object"), QV4::QmlIncubatorObject::method_get_object, nullptr);
2189 proto->defineDefaultProperty(QStringLiteral("forceCompletion"), QV4::QmlIncubatorObject::method_forceCompletion);
2190
2191 incubationProto.set(v4, proto);
2192}
2193
2194QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_object(const FunctionObject *b, const Value *thisObject, const Value *, int)
2195{
2196 QV4::Scope scope(b);
2197 QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>());
2198 if (!o)
2199 THROW_TYPE_ERROR();
2200
2201 return QV4::QObjectWrapper::wrap(scope.engine, o->d()->incubator->object());
2202}
2203
2204QV4::ReturnedValue QV4::QmlIncubatorObject::method_forceCompletion(const FunctionObject *b, const Value *thisObject, const Value *, int)
2205{
2206 QV4::Scope scope(b);
2207 QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>());
2208 if (!o)
2209 THROW_TYPE_ERROR();
2210
2211 o->d()->incubator->forceCompletion();
2212
2213 RETURN_UNDEFINED();
2214}
2215
2216QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_status(const FunctionObject *b, const Value *thisObject, const Value *, int)
2217{
2218 QV4::Scope scope(b);
2219 QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>());
2220 if (!o)
2221 THROW_TYPE_ERROR();
2222
2223 return QV4::Encode(o->d()->incubator->status());
2224}
2225
2226QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_statusChanged(const FunctionObject *b, const Value *thisObject, const Value *, int)
2227{
2228 QV4::Scope scope(b);
2229 QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>());
2230 if (!o)
2231 THROW_TYPE_ERROR();
2232
2233 return QV4::Encode(o->d()->statusChanged);
2234}
2235
2236QV4::ReturnedValue QV4::QmlIncubatorObject::method_set_statusChanged(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
2237{
2238 QV4::Scope scope(b);
2239 QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>());
2240 if (!o || argc < 1)
2241 THROW_TYPE_ERROR();
2242
2243 o->d()->statusChanged.set(scope.engine, argv[0]);
2244
2245 RETURN_UNDEFINED();
2246}
2247
2248QQmlComponentExtension::~QQmlComponentExtension()
2249{
2250}
2251
2252void QV4::Heap::QmlIncubatorObject::init(QQmlIncubator::IncubationMode m)
2253{
2254 Object::init();
2255 valuemapOrObject.set(internalClass->engine, QV4::Value::undefinedValue());
2256 statusChanged.set(internalClass->engine, QV4::Value::undefinedValue());
2257 parent.init();
2258 qmlContext.set(internalClass->engine, nullptr);
2259 incubator = new QQmlComponentIncubator(this, m);
2260}
2261
2262void QV4::Heap::QmlIncubatorObject::destroy() {
2263 delete incubator;
2264 parent.destroy();
2265 Object::destroy();
2266}
2267
2268void QV4::QmlIncubatorObject::setInitialState(QObject *o, RequiredProperties *requiredProperties)
2269{
2270 QQmlComponent_setQmlParent(o, d()->parent);
2271
2272 if (!d()->valuemapOrObject.isUndefined()) {
2273 QV4::ExecutionEngine *v4 = engine();
2274 QV4::Scope scope(v4);
2275 QV4::ScopedObject obj(scope, QV4::QObjectWrapper::wrap(v4, o));
2276 QV4::Scoped<QV4::QmlContext> qmlCtxt(scope, d()->qmlContext);
2277 QQmlComponentPrivate::setInitialProperties(
2278 v4, qmlCtxt, obj, d()->valuemapOrObject, requiredProperties, o,
2279 QQmlIncubatorPrivate::get(d()->incubator)->creator.data());
2280 }
2281}
2282
2283void QV4::QmlIncubatorObject::statusChanged(QQmlIncubator::Status s)
2284{
2285 QV4::Scope scope(engine());
2286
2287 QObject *object = d()->incubator->object();
2288
2289 if (s == QQmlIncubator::Ready) {
2290 // We don't need the arguments anymore, but we still want to hold on to the object so
2291 // that it doesn't get gc'd
2292 d()->valuemapOrObject.set(scope.engine, QV4::QObjectWrapper::wrap(scope.engine, object));
2293
2294 QQmlData *ddata = QQmlData::get(object);
2295 Q_ASSERT(ddata);
2296 ddata->explicitIndestructibleSet = false;
2297 ddata->indestructible = false;
2298 }
2299
2300 QV4::ScopedFunctionObject f(scope, d()->statusChanged);
2301 if (f) {
2302 QV4::JSCallArguments jsCallData(scope, 1);
2303 *jsCallData.thisObject = this;
2304 jsCallData.args[0] = QV4::Value::fromUInt32(s);
2305 f->call(jsCallData);
2306 if (scope.hasException()) {
2307 QQmlError error = scope.engine->catchExceptionAsQmlError();
2308 QQmlEnginePrivate::warning(QQmlEnginePrivate::get(scope.engine->qmlEngine()), error);
2309 }
2310 }
2311
2312 if (s != QQmlIncubator::Loading)
2313 d()->incubator->incubatorObject.clear();
2314}
2315
2316#undef INITIALPROPERTIES_SOURCE
2317
2318QT_END_NAMESPACE
2319
2320#include "moc_qqmlcomponent.cpp"
2321#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)