1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qqmlcomponent.h"
43 #include "qqmlcomponent_p.h"
44 #include "qqmlcomponentattached_p.h"
46 #include "qqmlcompiler_p.h"
47 #include "qqmlcontext_p.h"
48 #include "qqmlengine_p.h"
49 #include "qqmlvme_p.h"
51 #include "qqmlengine.h"
52 #include "qqmlbinding_p.h"
53 #include "qqmlglobal_p.h"
54 #include "qqmlscript_p.h"
55 #include <private/qqmlprofilerservice_p.h>
56 #include <private/qqmlenginedebugservice_p.h>
57 #include "qqmlincubator.h"
58 #include "qqmlincubator_p.h"
59 #include <private/qqmljavascriptexpression_p.h>
61 #include <private/qv8engine_p.h>
62 #include <private/qv8include_p.h>
65 #include <QStringList>
66 #include <QThreadStorage>
67 #include <QtCore/qdebug.h>
69 #include "qqmlmemoryprofiler_p.h"
72 QThreadStorage<int> creationDepth;
77 class QQmlComponentExtension : public QV8Engine::Deletable
80 QQmlComponentExtension(QV8Engine *);
81 virtual ~QQmlComponentExtension();
83 v8::Persistent<v8::Function> incubationConstructor;
84 v8::Persistent<v8::Script> initialProperties;
85 v8::Persistent<v8::Function> forceCompletion;
87 V8_DEFINE_EXTENSION(QQmlComponentExtension, componentExtension);
90 Try to do what's necessary for a reasonable display of the type
91 name, but no more (just enough for the client to do more extensive cleanup).
93 Should only be called when debugging is enabled.
95 static inline QString buildTypeNameForDebug(const QMetaObject *metaObject)
97 static const QString qmlMarker(QLatin1String("_QML"));
98 static const QChar underscore(QLatin1Char('_'));
99 static const QChar asterisk(QLatin1Char('*'));
100 QQmlType *type = QQmlMetaType::qmlType(metaObject);
101 QString typeName = type ? type->qmlTypeName() : QString::fromUtf8(metaObject->className());
103 //### optimize further?
104 int marker = typeName.indexOf(qmlMarker);
105 if (marker != -1 && marker < typeName.count() - 1) {
106 if (typeName[marker + 1] == underscore) {
107 const QString className = typeName.left(marker) + asterisk;
108 type = QQmlMetaType::qmlType(QMetaType::type(className.toUtf8()));
110 typeName = type->qmlTypeName();
123 \brief The QQmlComponent class encapsulates a QML component definition
125 Components are reusable, encapsulated QML types with well-defined interfaces.
127 A QQmlComponent instance can be created from a QML file.
128 For example, if there is a \c main.qml file like this:
139 The following code loads this QML file as a component, creates an instance of
140 this component using create(), and then queries the \l Item's \l {Item::}{width}
144 QQmlEngine *engine = new QQmlEngine;
145 QQmlComponent component(engine, QUrl::fromLocalFile("main.qml"));
147 QObject *myObject = component.create();
148 QQuickItem *item = qobject_cast<QQuickItem*>(myObject);
149 int width = item->width(); // width = 200
153 \section2 Network Components
155 If the URL passed to QQmlComponent is a network resource, or if the QML document references a
156 network resource, the QQmlComponent has to fetch the network data before it is able to create
157 objects. In this case, the QQmlComponent will have a \l {QQmlComponent::Loading}{Loading}
158 \l {QQmlComponent::status()}{status}. An application will have to wait until the component
159 is \l {QQmlComponent::Ready}{Ready} before calling \l {QQmlComponent::create()}.
161 The following example shows how to load a QML file from a network resource. After creating
162 the QQmlComponent, it tests whether the component is loading. If it is, it connects to the
163 QQmlComponent::statusChanged() signal and otherwise calls the \c {continueLoading()} method
164 directly. Note that QQmlComponent::isLoading() may be false for a network component if the
165 component has been cached and is ready immediately.
168 MyApplication::MyApplication()
171 component = new QQmlComponent(engine, QUrl("http://www.example.com/main.qml"));
172 if (component->isLoading())
173 QObject::connect(component, SIGNAL(statusChanged(QQmlComponent::Status)),
174 this, SLOT(continueLoading()));
179 void MyApplication::continueLoading()
181 if (component->isError()) {
182 qWarning() << component->errors();
184 QObject *myObject = component->create();
189 Note that the QtQuick 1 version is named QDeclarativeComponent.
194 \instantiates QQmlComponent
195 \ingroup qml-utility-elements
196 \inqmlmodule QtQuick 2
197 \brief Encapsulates a QML component definition
199 Components are reusable, encapsulated QML types with well-defined interfaces.
201 Components are often defined by \l {{QML Documents}}{component files} -
202 that is, \c .qml files. The \e Component type essentially allows QML components
203 to be defined inline, within a \l {QML Document}{QML document}, rather than as a separate QML file.
204 This may be useful for reusing a small component within a QML file, or for defining
205 a component that logically belongs with other QML components within a file.
207 For example, here is a component that is used by multiple \l Loader objects.
208 It contains a single item, a \l Rectangle:
210 \snippet qml/component.qml 0
212 Notice that while a \l Rectangle by itself would be automatically
213 rendered and displayed, this is not the case for the above rectangle
214 because it is defined inside a \c Component. The component encapsulates the
215 QML types within, as if they were defined in a separate QML
216 file, and is not loaded until requested (in this case, by the
217 two \l Loader objects).
219 Defining a \c Component is similar to defining a \l {QML Document}{QML document}.
220 A QML document has a single top-level item that defines the behaviors and
221 properties of that component, and cannot define properties or behaviors outside
222 of that top-level item. In the same way, a \c Component definition contains a single
223 top level item (which in the above example is a \l Rectangle) and cannot define any
224 data outside of this item, with the exception of an \e id (which in the above example
227 The \c Component type is commonly used to provide graphical components
228 for views. For example, the ListView::delegate property requires a \c Component
229 to specify how each list item is to be displayed.
231 \c Component objects can also be created dynamically using
232 \l{QML:Qt::createComponent()}{Qt.createComponent()}.
234 \section2 Creation Context
236 The creation context of a Component corresponds to the context where the Component was declared.
237 This context is used as the parent context (creating a \l{qtqml-documents-scope.html#component-instance-hierarchy}{context hierarchy})
238 when the component is instantiated by an object such as a ListView or a Loader.
240 In the following example, \c comp1 is created within the root context of MyItem.qml, and any objects
241 instantiated from this component will have access to the ids and properties within that context,
242 such as \c internalSettings.color. When \c comp1 is used as a ListView delegate in another context
243 (as in main.qml below), it will continue to have access to the properties of its creation context
244 (which would otherwise be private to external users).
251 \li \snippet qml/component/MyItem.qml 0
252 \li \snippet qml/component/main.qml 0
257 \qmlattachedsignal Component::onCompleted()
259 Emitted after component "startup" has completed. This can be used to
260 execute script code at startup, once the full QML environment has been
263 The \c {Component::onCompleted} attached property can be declared on
264 any object. The order of running the \c onCompleted scripts is
269 Component.onCompleted: console.log("Completed Running!")
271 Component.onCompleted: console.log("Nested Completed Running!")
278 \qmlattachedsignal Component::onDestruction()
280 Emitted as the component begins destruction. This can be used to undo
281 work done in the onCompleted signal, or other imperative code in your
284 The \c {Component::onDestruction} attached property can be declared on
285 any object. However, it applies to the destruction of the component as
286 a whole, and not the destruction of the specific object. The order of
287 running the \c onDestruction scripts is undefined.
291 Component.onDestruction: console.log("Destruction Beginning!")
293 Component.onDestruction: console.log("Nested Destruction Beginning!")
302 \enum QQmlComponent::Status
304 Specifies the loading status of the QQmlComponent.
306 \value Null This QQmlComponent has no data. Call loadUrl() or setData() to add QML content.
307 \value Ready This QQmlComponent is ready and create() may be called.
308 \value Loading This QQmlComponent is loading network data.
309 \value Error An error has occurred. Call errors() to retrieve a list of \{QQmlError}{errors}.
313 \enum QQmlComponent::CompilationMode
315 Specifies whether the QQmlComponent should load the component immediately, or asynchonously.
317 \value PreferSynchronous Prefer loading/compiling the component immediately, blocking the thread.
318 This is not always possible, e.g. remote URLs will always load asynchronously.
319 \value Asynchronous Load/compile the component in a background thread.
322 void QQmlComponentPrivate::typeDataReady(QQmlTypeData *)
328 fromTypeData(typeData);
332 emit q->statusChanged(q->status());
333 emit q->progressChanged(progress);
336 void QQmlComponentPrivate::typeDataProgress(QQmlTypeData *, qreal p)
342 emit q->progressChanged(p);
345 void QQmlComponentPrivate::fromTypeData(QQmlTypeData *data)
347 url = data->finalUrl();
348 QQmlCompiledData *c = data->compiledData();
351 Q_ASSERT(data->isError());
352 state.errors = data->errors();
361 void QQmlComponentPrivate::clear()
364 typeData->unregisterCallback(this);
378 QQmlComponent::QQmlComponent(QObject *parent)
379 : QObject(*(new QQmlComponentPrivate), parent)
384 Destruct the QQmlComponent.
386 QQmlComponent::~QQmlComponent()
390 if (d->state.completePending) {
391 qWarning("QQmlComponent: Component destroyed while completion pending");
396 d->typeData->unregisterCallback(d);
397 d->typeData->release();
404 \qmlproperty enumeration Component::status
405 This property holds the status of component loading. It can be one of:
407 \li Component.Null - no data is available for the component
408 \li Component.Ready - the component has been loaded, and can be used to create instances.
409 \li Component.Loading - the component is currently being loaded
410 \li Component.Error - an error occurred while loading the component.
411 Calling errorString() will provide a human-readable description of any errors.
416 \property QQmlComponent::status
417 The component's current \l{QQmlComponent::Status} {status}.
419 QQmlComponent::Status QQmlComponent::status() const
421 Q_D(const QQmlComponent);
425 else if (!d->state.errors.isEmpty())
427 else if (d->engine && d->cc)
434 Returns true if status() == QQmlComponent::Null.
436 bool QQmlComponent::isNull() const
438 return status() == Null;
442 Returns true if status() == QQmlComponent::Ready.
444 bool QQmlComponent::isReady() const
446 return status() == Ready;
450 Returns true if status() == QQmlComponent::Error.
452 bool QQmlComponent::isError() const
454 return status() == Error;
458 Returns true if status() == QQmlComponent::Loading.
460 bool QQmlComponent::isLoading() const
462 return status() == Loading;
466 \qmlproperty real Component::progress
467 The progress of loading the component, from 0.0 (nothing loaded)
472 \property QQmlComponent::progress
473 The progress of loading the component, from 0.0 (nothing loaded)
476 qreal QQmlComponent::progress() const
478 Q_D(const QQmlComponent);
483 \fn void QQmlComponent::progressChanged(qreal progress)
485 Emitted whenever the component's loading progress changes. \a progress will be the
486 current progress between 0.0 (nothing loaded) and 1.0 (finished).
490 \fn void QQmlComponent::statusChanged(QQmlComponent::Status status)
492 Emitted whenever the component's status changes. \a status will be the
497 Create a QQmlComponent with no data and give it the specified
498 \a engine and \a parent. Set the data with setData().
500 QQmlComponent::QQmlComponent(QQmlEngine *engine, QObject *parent)
501 : QObject(*(new QQmlComponentPrivate), parent)
508 Create a QQmlComponent from the given \a url and give it the
509 specified \a parent and \a engine.
511 Ensure that the URL provided is full and correct, in particular, use
512 \l QUrl::fromLocalFile() when loading a file from the local filesystem.
516 QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, QObject *parent)
517 : QObject(*(new QQmlComponentPrivate), parent)
525 Create a QQmlComponent from the given \a url and give it the
526 specified \a parent and \a engine. If \a mode is \l Asynchronous,
527 the component will be loaded and compiled asynchronously.
529 Ensure that the URL provided is full and correct, in particular, use
530 \l QUrl::fromLocalFile() when loading a file from the local filesystem.
534 QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, CompilationMode mode,
536 : QObject(*(new QQmlComponentPrivate), parent)
540 d->loadUrl(url, mode);
544 Create a QQmlComponent from the given \a fileName and give it the specified
545 \a parent and \a engine.
549 QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName,
551 : QObject(*(new QQmlComponentPrivate), parent)
555 d->loadUrl(d->engine->baseUrl().resolved(QUrl::fromLocalFile(fileName)));
559 Create a QQmlComponent from the given \a fileName and give it the specified
560 \a parent and \a engine. If \a mode is \l Asynchronous,
561 the component will be loaded and compiled asynchronously.
565 QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName,
566 CompilationMode mode, QObject *parent)
567 : QObject(*(new QQmlComponentPrivate), parent)
571 d->loadUrl(d->engine->baseUrl().resolved(QUrl::fromLocalFile(fileName)), mode);
577 QQmlComponent::QQmlComponent(QQmlEngine *engine, QQmlCompiledData *cc, int start, QObject *parent)
578 : QObject(*(new QQmlComponentPrivate), parent)
590 Sets the QQmlComponent to use the given QML \a data. If \a url
591 is provided, it is used to set the component name and to provide
592 a base path for items resolved by this component.
594 void QQmlComponent::setData(const QByteArray &data, const QUrl &url)
602 QQmlTypeData *typeData = QQmlEnginePrivate::get(d->engine)->typeLoader.getType(data, url);
604 if (typeData->isCompleteOrError()) {
605 d->fromTypeData(typeData);
607 d->typeData = typeData;
608 d->typeData->registerCallback(d);
612 emit statusChanged(status());
613 emit progressChanged(d->progress);
617 Returns the QQmlContext the component was created in. This is only
618 valid for components created directly from QML.
620 QQmlContext *QQmlComponent::creationContext() const
622 Q_D(const QQmlComponent);
623 if(d->creationContext)
624 return d->creationContext->asQQmlContext();
626 return qmlContext(this);
630 Load the QQmlComponent from the provided \a url.
632 Ensure that the URL provided is full and correct, in particular, use
633 \l QUrl::fromLocalFile() when loading a file from the local filesystem.
635 void QQmlComponent::loadUrl(const QUrl &url)
642 Load the QQmlComponent from the provided \a url.
643 If \a mode is \l Asynchronous, the component will be loaded and compiled asynchronously.
645 Ensure that the URL provided is full and correct, in particular, use
646 \l QUrl::fromLocalFile() when loading a file from the local filesystem.
648 void QQmlComponent::loadUrl(const QUrl &url, QQmlComponent::CompilationMode mode)
651 d->loadUrl(url, mode);
654 void QQmlComponentPrivate::loadUrl(const QUrl &newUrl, QQmlComponent::CompilationMode mode)
659 if ((newUrl.isRelative() && !newUrl.isEmpty())
660 || newUrl.scheme() == QLatin1String("file")) // Workaround QTBUG-11929
661 url = engine->baseUrl().resolved(newUrl);
665 if (newUrl.isEmpty()) {
667 error.setDescription(q->tr("Invalid empty URL"));
668 state.errors << error;
672 if (progress != 0.0) {
674 emit q->progressChanged(progress);
677 QQmlDataLoader::Mode loaderMode = (mode == QQmlComponent::Asynchronous)
678 ? QQmlDataLoader::Asynchronous
679 : QQmlDataLoader::PreferSynchronous;
681 QQmlTypeData *data = QQmlEnginePrivate::get(engine)->typeLoader.getType(url, loaderMode);
683 if (data->isCompleteOrError()) {
688 typeData->registerCallback(this);
689 progress = data->progress();
692 emit q->statusChanged(q->status());
694 emit q->progressChanged(progress);
698 Return the list of errors that occurred during the last compile or create
699 operation. An empty list is returned if isError() is not set.
701 QList<QQmlError> QQmlComponent::errors() const
703 Q_D(const QQmlComponent);
705 return d->state.errors;
707 return QList<QQmlError>();
711 \qmlmethod string Component::errorString()
713 Returns a human-readable description of any errors.
715 The string includes the file, location, and description of each error.
716 If multiple errors are present they are separated by a newline character.
718 If no errors are present, an empty string is returned.
723 errorString is only meant as a way to get the errors in script
725 QString QQmlComponent::errorString() const
727 Q_D(const QQmlComponent);
731 foreach(const QQmlError &e, d->state.errors) {
732 ret += e.url().toString() + QLatin1Char(':') +
733 QString::number(e.line()) + QLatin1Char(' ') +
734 e.description() + QLatin1Char('\n');
740 \qmlproperty url Component::url
741 The component URL. This is the URL that was used to construct the component.
745 \property QQmlComponent::url
746 The component URL. This is the URL passed to either the constructor,
747 or the loadUrl() or setData() methods.
749 QUrl QQmlComponent::url() const
751 Q_D(const QQmlComponent);
758 QQmlComponent::QQmlComponent(QQmlComponentPrivate &dd, QObject *parent)
759 : QObject(dd, parent)
764 Create an object instance from this component. Returns 0 if creation
765 failed. \a context specifies the context within which to create the object
768 If \a context is 0 (the default), it will create the instance in the
769 engine' s \l {QQmlEngine::rootContext()}{root context}.
771 QObject *QQmlComponent::create(QQmlContext *context)
774 QML_MEMORY_SCOPE_URL(url());
777 context = d->engine->rootContext();
779 QObject *rv = beginCreate(context);
786 This method provides more advanced control over component instance creation.
787 In general, programmers should use QQmlComponent::create() to create a
790 Create an object instance from this component. Returns 0 if creation
791 failed. \a publicContext specifies the context within which to create the object
794 When QQmlComponent constructs an instance, it occurs in three steps:
796 \li The object hierarchy is created, and constant values are assigned.
797 \li Property bindings are evaluated for the the first time.
798 \li If applicable, QQmlParserStatus::componentComplete() is called on objects.
800 QQmlComponent::beginCreate() differs from QQmlComponent::create() in that it
801 only performs step 1. QQmlComponent::completeCreate() must be called to
802 complete steps 2 and 3.
804 This breaking point is sometimes useful when using attached properties to
805 communicate information to an instantiated component, as it allows their
806 initial values to be configured before property bindings take effect.
810 QObject *QQmlComponent::beginCreate(QQmlContext *publicContext)
814 Q_ASSERT(publicContext);
815 QQmlContextData *context = QQmlContextData::get(publicContext);
817 return d->beginCreate(context);
821 QQmlComponentPrivate::beginCreate(QQmlContextData *context)
825 qWarning("QQmlComponent: Cannot create a component in a null context");
829 if (!context->isValid()) {
830 qWarning("QQmlComponent: Cannot create a component in an invalid context");
834 if (context->engine != engine) {
835 qWarning("QQmlComponent: Must create component in context from the same QQmlEngine");
839 if (state.completePending) {
840 qWarning("QQmlComponent: Cannot create new component instance before completing the previous");
845 qWarning("QQmlComponent: Component is not ready");
849 // Do not create infinite recursion in object creation
850 static const int maxCreationDepth = 10;
851 if (++creationDepth.localData() >= maxCreationDepth) {
852 qWarning("QQmlComponent: Component creation is recursing - aborting");
853 --creationDepth.localData();
856 Q_ASSERT(creationDepth.localData() >= 1);
857 depthIncreased = true;
859 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine);
861 if (enginePriv->inProgressCreations == 0) {
862 // only track root, since further ones might not be properly nested
863 profiler = new QQmlObjectCreatingProfiler();
866 enginePriv->inProgressCreations++;
867 state.errors.clear();
868 state.completePending = true;
870 enginePriv->referenceScarceResources();
871 state.vme.init(context, cc, start, creationContext);
872 QObject *rv = state.vme.execute(&state.errors);
873 enginePriv->dereferenceScarceResources();
876 QQmlData *ddata = QQmlData::get(rv);
878 //top level objects should never get JS ownership.
879 //if JS ownership is needed this needs to be explicitly undone (like in component.createObject())
880 ddata->indestructible = true;
881 ddata->explicitIndestructibleSet = true;
882 ddata->rootObjectInCreation = false;
884 Q_ASSERT(creationDepth.localData() >= 1);
885 --creationDepth.localData();
886 depthIncreased = false;
889 if (enginePriv->isDebugging && rv) {
890 if (!context->isInternal)
891 context->asQQmlContextPrivate()->instances.append(rv);
892 QQmlEngineDebugService::instance()->objectCreated(engine, rv);
894 if (profiler && profiler->enabled) {
895 profiler->setTypeName(buildTypeNameForDebug(rv->metaObject()));
896 QQmlData *data = QQmlData::get(rv);
898 profiler->setLocation(cc->url, data->lineNumber, data->columnNumber);
905 void QQmlComponentPrivate::beginDeferred(QQmlEnginePrivate *enginePriv,
906 QObject *object, ConstructionState *state)
908 enginePriv->inProgressCreations++;
909 state->errors.clear();
910 state->completePending = true;
912 state->vme.initDeferred(object);
913 state->vme.execute(&state->errors);
916 void QQmlComponentPrivate::complete(QQmlEnginePrivate *enginePriv, ConstructionState *state)
918 if (state->completePending) {
919 state->vme.complete();
921 state->completePending = false;
923 enginePriv->inProgressCreations--;
925 if (0 == enginePriv->inProgressCreations) {
926 while (enginePriv->erroredBindings) {
927 enginePriv->warning(enginePriv->erroredBindings);
928 enginePriv->erroredBindings->removeError();
935 This method provides more advanced control over component instance creation.
936 In general, programmers should use QQmlComponent::create() to create a
939 Complete a component creation begin with QQmlComponent::beginCreate().
943 void QQmlComponent::completeCreate()
950 void QQmlComponentPrivate::completeCreate()
952 if (state.completePending) {
953 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
954 complete(ep, &state);
960 if (depthIncreased) {
961 Q_ASSERT(creationDepth.localData() >= 1);
962 --creationDepth.localData();
963 depthIncreased = false;
967 QQmlComponentAttached::QQmlComponentAttached(QObject *parent)
968 : QObject(parent), prev(0), next(0)
972 QQmlComponentAttached::~QQmlComponentAttached()
974 if (prev) *prev = next;
975 if (next) next->prev = prev;
983 QQmlComponentAttached *QQmlComponent::qmlAttachedProperties(QObject *obj)
985 QQmlComponentAttached *a = new QQmlComponentAttached(obj);
987 QQmlEngine *engine = qmlEngine(obj);
991 if (QQmlEnginePrivate::get(engine)->activeVME) { // XXX should only be allowed during begin
992 QQmlEnginePrivate *p = QQmlEnginePrivate::get(engine);
993 a->add(&p->activeVME->componentAttached);
995 QQmlData *d = QQmlData::get(obj);
997 Q_ASSERT(d->context);
998 a->add(&d->context->componentAttached);
1005 Create an object instance from this component using the provided
1006 \a incubator. \a context specifies the context within which to create the object
1009 If \a context is 0 (the default), it will create the instance in the
1010 engine's \l {QQmlEngine::rootContext()}{root context}.
1012 \a forContext specifies a context that this object creation depends upon.
1013 If the \a forContext is being created asynchronously, and the
1014 \l QQmlIncubator::IncubationMode is \l QQmlIncubator::AsynchronousIfNested,
1015 this object will also be created asynchronously. If \a forContext is 0
1016 (the default), the \a context will be used for this decision.
1018 The created object and its creation status are available via the
1024 void QQmlComponent::create(QQmlIncubator &incubator, QQmlContext *context,
1025 QQmlContext *forContext)
1030 context = d->engine->rootContext();
1032 QQmlContextData *contextData = QQmlContextData::get(context);
1033 QQmlContextData *forContextData = contextData;
1034 if (forContext) forContextData = QQmlContextData::get(forContext);
1036 if (!contextData->isValid()) {
1037 qWarning("QQmlComponent: Cannot create a component in an invalid context");
1041 if (contextData->engine != d->engine) {
1042 qWarning("QQmlComponent: Must create component in context from the same QQmlEngine");
1047 qWarning("QQmlComponent: Component is not ready");
1052 QQmlIncubatorPrivate *p = incubator.d;
1054 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(d->engine);
1056 p->compiledData = d->cc;
1057 p->compiledData->addref();
1058 p->vme.init(contextData, d->cc, d->start, d->creationContext);
1060 enginePriv->incubate(incubator, forContextData);
1063 class QV8IncubatorResource : public QV8ObjectResource,
1064 public QQmlIncubator
1066 V8_RESOURCE_TYPE(IncubatorType)
1068 QV8IncubatorResource(QV8Engine *engine, IncubationMode = Asynchronous);
1070 static v8::Handle<v8::Value> StatusChangedGetter(v8::Local<v8::String>,
1071 const v8::AccessorInfo& info);
1072 static v8::Handle<v8::Value> StatusGetter(v8::Local<v8::String>,
1073 const v8::AccessorInfo& info);
1074 static v8::Handle<v8::Value> ObjectGetter(v8::Local<v8::String>,
1075 const v8::AccessorInfo& info);
1076 static v8::Handle<v8::Value> ForceCompletionGetter(v8::Local<v8::String>,
1077 const v8::AccessorInfo& info);
1078 static v8::Handle<v8::Value> ForceCompletion(const v8::Arguments &args);
1080 static void StatusChangedSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
1081 const v8::AccessorInfo& info);
1085 v8::Persistent<v8::Object> me;
1086 QQmlGuard<QObject> parent;
1087 v8::Persistent<v8::Value> valuemap;
1088 v8::Persistent<v8::Object> qmlGlobal;
1090 virtual void statusChanged(Status);
1091 virtual void setInitialState(QObject *);
1094 static void QQmlComponent_setQmlParent(QObject *me, QObject *parent)
1097 me->setParent(parent);
1098 typedef QQmlPrivate::AutoParentFunction APF;
1099 QList<APF> functions = QQmlMetaType::parentFunctions();
1101 bool needParent = false;
1102 for (int ii = 0; ii < functions.count(); ++ii) {
1103 QQmlPrivate::AutoParentResult res = functions.at(ii)(me, parent);
1104 if (res == QQmlPrivate::Parented) {
1107 } else if (res == QQmlPrivate::IncompatibleParent) {
1112 qWarning("QQmlComponent: Created graphical object was not "
1113 "placed in the graphics scene.");
1118 \qmlmethod object Component::createObject(Item parent, object properties)
1120 Creates and returns an object instance of this component that will have
1121 the given \a parent and \a properties. The \a properties argument is optional.
1122 Returns null if object creation fails.
1124 The object will be created in the same context as the one in which the component
1125 was created. This function will always return null when called on components
1126 which were not created in QML.
1128 If you wish to create an object without setting a parent, specify \c null for
1129 the \a parent value. Note that if the returned object is to be displayed, you
1130 must provide a valid \a parent value or set the returned object's \l{Item::parent}{parent}
1131 property, or else the object will not be visible.
1133 If a \a parent is not provided to createObject(), a reference to the returned object must be held so that
1134 it is not destroyed by the garbage collector. This is true regardless of whether \l{Item::parent} is set afterwards,
1135 since setting the Item parent does not change object ownership; only the graphical parent is changed.
1137 As of QtQuick 1.1, this method accepts an optional \a properties argument that specifies a
1138 map of initial property values for the created object. These values are applied before object
1139 creation is finalized. (This is more efficient than setting property values after object creation,
1140 particularly where large sets of property values are defined, and also allows property bindings
1141 to be set up before the object is created.)
1143 The \a properties argument is specified as a map of property-value items. For example, the code
1144 below creates an object with initial \c x and \c y values of 100 and 200, respectively:
1147 var component = Qt.createComponent("Button.qml");
1148 if (component.status == Component.Ready)
1149 component.createObject(parent, {"x": 100, "y": 100});
1152 Dynamically created instances can be deleted with the \c destroy() method.
1153 See \l {Dynamic QML Object Creation from JavaScript} for more information.
1155 \sa incubateObject()
1161 void QQmlComponent::createObject(QQmlV8Function *args)
1164 Q_ASSERT(d->engine);
1167 QObject *parent = 0;
1168 v8::Local<v8::Object> valuemap;
1170 if (args->Length() >= 1)
1171 parent = args->engine()->toQObject((*args)[0]);
1173 if (args->Length() >= 2) {
1174 v8::Local<v8::Value> v = (*args)[1];
1175 if (!v->IsObject() || v->IsArray()) {
1176 qmlInfo(this) << tr("createObject: value is not an object");
1177 args->returnValue(v8::Null());
1180 valuemap = v8::Local<v8::Object>::Cast(v);
1183 QV8Engine *v8engine = args->engine();
1185 QQmlContext *ctxt = creationContext();
1186 if (!ctxt) ctxt = d->engine->rootContext();
1188 QObject *rv = beginCreate(ctxt);
1191 args->returnValue(v8::Null());
1195 QQmlComponent_setQmlParent(rv, parent);
1197 v8::Handle<v8::Value> ov = v8engine->newQObject(rv);
1198 Q_ASSERT(ov->IsObject());
1199 v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(ov);
1201 if (!valuemap.IsEmpty()) {
1202 QQmlComponentExtension *e = componentExtension(v8engine);
1203 // Try catch isn't needed as the function itself is loaded with try/catch
1204 v8::Handle<v8::Value> function = e->initialProperties->Run(args->qmlGlobal());
1205 v8::Handle<v8::Value> args[] = { object, valuemap };
1206 v8::Handle<v8::Function>::Cast(function)->Call(v8engine->global(), 2, args);
1209 d->completeCreate();
1211 Q_ASSERT(QQmlData::get(rv));
1212 QQmlData::get(rv)->explicitIndestructibleSet = false;
1213 QQmlData::get(rv)->indestructible = false;
1216 args->returnValue(v8::Null());
1218 args->returnValue(object);
1222 \qmlmethod object Component::incubateObject(Item parent, object properties, enumeration mode)
1224 Creates an incubator for instance of this component. Incubators allow new component
1225 instances to be instantiated asynchronously and not cause freezes in the UI.
1227 The \a parent argument specifies the parent the created instance will have. Omitting the
1228 parameter or passing null will create anobject with no parent. In this case, a reference
1229 to the created object must be maintained by the application of the object will eventually
1230 be garbage collected.
1232 The \a properties argument is specified as a map of property-value items which will be
1233 set on the created object during its construction. \a mode may be Qt.Synchronous or
1234 Qt.Asynchronous and controls whether the instance is created synchronously or asynchronously.
1235 The default is asynchronously. In some circumstances, even if Qt.Synchronous is specified,
1236 the incubator may create the object asynchronously. This happens if the component calling
1237 incubateObject() is itself being created asynchronously.
1239 All three arguments are optional.
1241 If successful, the method returns an incubator, otherwise null. The incubator has the following
1245 \li status The status of the incubator. Valid values are Component.Ready, Component.Loading and
1247 \li object The created object instance. Will only be available once the incubator is in the
1249 \li onStatusChanged Specifies a callback function to be invoked when the status changes. The
1250 status is passed as a parameter to the callback.
1251 \li forceCompletion() Call to complete incubation synchronously.
1254 The following example demonstrates how to use an incubator:
1257 var component = Qt.createComponent("Button.qml");
1259 var incubator = component.incubateObject(parent, { x: 10, y: 10 });
1260 if (incubator.status != Component.Ready) {
1261 incubator.onStatusChanged = function(status) {
1262 if (status == Component.Ready) {
1263 print ("Object", incubator.object, "is now ready!");
1267 print ("Object", incubator.object, "is ready immediately!");
1271 Dynamically created instances can be deleted with the \c destroy() method.
1272 See \l {Dynamic QML Object Creation from JavaScript} for more information.
1280 void QQmlComponent::incubateObject(QQmlV8Function *args)
1283 Q_ASSERT(d->engine);
1287 QObject *parent = 0;
1288 v8::Local<v8::Object> valuemap;
1289 QQmlIncubator::IncubationMode mode = QQmlIncubator::Asynchronous;
1291 if (args->Length() >= 1)
1292 parent = args->engine()->toQObject((*args)[0]);
1294 if (args->Length() >= 2) {
1295 v8::Local<v8::Value> v = (*args)[1];
1297 } else if (!v->IsObject() || v->IsArray()) {
1298 qmlInfo(this) << tr("createObject: value is not an object");
1299 args->returnValue(v8::Null());
1302 valuemap = v8::Local<v8::Object>::Cast(v);
1306 if (args->Length() >= 3) {
1307 quint32 v = (*args)[2]->Uint32Value();
1309 mode = QQmlIncubator::Asynchronous;
1311 mode = QQmlIncubator::AsynchronousIfNested;
1314 QQmlComponentExtension *e = componentExtension(args->engine());
1316 QV8IncubatorResource *r = new QV8IncubatorResource(args->engine(), mode);
1317 v8::Local<v8::Object> o = e->incubationConstructor->NewInstance();
1318 o->SetExternalResource(r);
1320 if (!valuemap.IsEmpty()) {
1321 r->valuemap = qPersistentNew(valuemap);
1322 r->qmlGlobal = qPersistentNew(args->qmlGlobal());
1325 r->me = qPersistentNew(o);
1327 create(*r, creationContext());
1329 if (r->status() == QQmlIncubator::Null) {
1331 args->returnValue(v8::Null());
1333 args->returnValue(o);
1337 // XXX used by QSGLoader
1338 void QQmlComponentPrivate::initializeObjectWithInitialProperties(v8::Handle<v8::Object> qmlGlobal, v8::Handle<v8::Object> valuemap, QObject *toCreate)
1340 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
1341 QV8Engine *v8engine = ep->v8engine();
1343 v8::HandleScope handle_scope;
1344 v8::Context::Scope scope(v8engine->context());
1345 v8::Handle<v8::Value> ov = v8engine->newQObject(toCreate);
1346 Q_ASSERT(ov->IsObject());
1347 v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(ov);
1349 if (!valuemap.IsEmpty()) {
1350 QQmlComponentExtension *e = componentExtension(v8engine);
1351 // Try catch isn't needed as the function itself is loaded with try/catch
1352 v8::Handle<v8::Value> function = e->initialProperties->Run(qmlGlobal);
1353 v8::Handle<v8::Value> args[] = { object, valuemap };
1354 v8::Handle<v8::Function>::Cast(function)->Call(v8engine->global(), 2, args);
1359 QQmlComponentExtension::QQmlComponentExtension(QV8Engine *engine)
1361 v8::HandleScope handle_scope;
1362 v8::Context::Scope scope(engine->context());
1364 forceCompletion = qPersistentNew(V8FUNCTION(QV8IncubatorResource::ForceCompletion, engine));
1367 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
1368 ft->InstanceTemplate()->SetHasExternalResource(true);
1369 ft->InstanceTemplate()->SetInternalFieldCount(1);
1370 ft->InstanceTemplate()->SetAccessor(v8::String::New("onStatusChanged"),
1371 QV8IncubatorResource::StatusChangedGetter,
1372 QV8IncubatorResource::StatusChangedSetter);
1373 ft->InstanceTemplate()->SetAccessor(v8::String::New("status"),
1374 QV8IncubatorResource::StatusGetter);
1375 ft->InstanceTemplate()->SetAccessor(v8::String::New("object"),
1376 QV8IncubatorResource::ObjectGetter);
1377 ft->InstanceTemplate()->SetAccessor(v8::String::New("forceCompletion"),
1378 QV8IncubatorResource::ForceCompletionGetter);
1379 incubationConstructor = qPersistentNew(ft->GetFunction());
1383 #define INITIALPROPERTIES_SOURCE \
1384 "(function(object, values) {"\
1386 "for(var property in values) {" \
1388 "var properties = property.split(\".\");"\
1390 "for (var ii = 0; ii < properties.length - 1; ++ii) {"\
1391 "o = o[properties[ii]];"\
1393 "o[properties[properties.length - 1]] = values[property];"\
1398 initialProperties = qPersistentNew(engine->qmlModeCompile(QLatin1String(INITIALPROPERTIES_SOURCE)));
1399 #undef INITIALPROPERTIES_SOURCE
1403 v8::Handle<v8::Value> QV8IncubatorResource::ObjectGetter(v8::Local<v8::String>,
1404 const v8::AccessorInfo& info)
1406 QV8IncubatorResource *r = v8_resource_check<QV8IncubatorResource>(info.This());
1407 return r->engine->newQObject(r->object());
1410 v8::Handle<v8::Value> QV8IncubatorResource::ForceCompletionGetter(v8::Local<v8::String>,
1411 const v8::AccessorInfo& info)
1413 QV8IncubatorResource *r = v8_resource_check<QV8IncubatorResource>(info.This());
1414 return componentExtension(r->engine)->forceCompletion;
1417 v8::Handle<v8::Value> QV8IncubatorResource::ForceCompletion(const v8::Arguments &args)
1419 QV8IncubatorResource *r = v8_resource_cast<QV8IncubatorResource>(args.This());
1421 V8THROW_TYPE("Not an incubator object");
1423 r->forceCompletion();
1425 return v8::Undefined();
1428 v8::Handle<v8::Value> QV8IncubatorResource::StatusGetter(v8::Local<v8::String>,
1429 const v8::AccessorInfo& info)
1431 QV8IncubatorResource *r = v8_resource_check<QV8IncubatorResource>(info.This());
1432 return v8::Integer::NewFromUnsigned(r->status());
1435 v8::Handle<v8::Value> QV8IncubatorResource::StatusChangedGetter(v8::Local<v8::String>,
1436 const v8::AccessorInfo& info)
1438 return info.This()->GetInternalField(0);
1441 void QV8IncubatorResource::StatusChangedSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
1442 const v8::AccessorInfo& info)
1444 info.This()->SetInternalField(0, value);
1447 QQmlComponentExtension::~QQmlComponentExtension()
1449 qPersistentDispose(incubationConstructor);
1450 qPersistentDispose(initialProperties);
1451 qPersistentDispose(forceCompletion);
1454 QV8IncubatorResource::QV8IncubatorResource(QV8Engine *engine, IncubationMode m)
1455 : QV8ObjectResource(engine), QQmlIncubator(m)
1459 void QV8IncubatorResource::setInitialState(QObject *o)
1461 QQmlComponent_setQmlParent(o, parent);
1463 if (!valuemap.IsEmpty()) {
1464 QQmlComponentExtension *e = componentExtension(engine);
1466 v8::HandleScope handle_scope;
1467 v8::Context::Scope scope(engine->context());
1469 v8::Handle<v8::Value> function = e->initialProperties->Run(qmlGlobal);
1470 v8::Handle<v8::Value> args[] = { engine->newQObject(o), valuemap };
1471 v8::Handle<v8::Function>::Cast(function)->Call(engine->global(), 2, args);
1473 qPersistentDispose(valuemap);
1474 qPersistentDispose(qmlGlobal);
1478 void QV8IncubatorResource::dispose()
1480 qPersistentDispose(valuemap);
1481 qPersistentDispose(qmlGlobal);
1482 // No further status changes are forthcoming, so we no long need a self reference
1483 qPersistentDispose(me);
1486 void QV8IncubatorResource::statusChanged(Status s)
1489 Q_ASSERT(QQmlData::get(object()));
1490 QQmlData::get(object())->explicitIndestructibleSet = false;
1491 QQmlData::get(object())->indestructible = false;
1494 if (!me.IsEmpty()) { // Will be false in synchronous mode
1495 v8::HandleScope scope;
1496 v8::Local<v8::Value> callback = me->GetInternalField(0);
1498 if (!callback.IsEmpty() && !callback->IsUndefined()) {
1500 if (callback->IsFunction()) {
1501 v8::Context::Scope context_scope(engine->context());
1502 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(callback);
1503 v8::Handle<v8::Value> args[] = { v8::Integer::NewFromUnsigned(s) };
1505 f->Call(me, 1, args);
1506 if (tc.HasCaught()) {
1508 QQmlJavaScriptExpression::exceptionToError(tc.Message(), error);
1509 QQmlEnginePrivate::warning(QQmlEnginePrivate::get(engine->engine()), error);
1515 if (s == Ready || s == Error)