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 <QtCore/qdebug.h>
71 class QQmlComponentExtension : public QV8Engine::Deletable
74 QQmlComponentExtension(QV8Engine *);
75 virtual ~QQmlComponentExtension();
77 v8::Persistent<v8::Function> incubationConstructor;
78 v8::Persistent<v8::Script> initialProperties;
79 v8::Persistent<v8::Function> forceCompletion;
81 V8_DEFINE_EXTENSION(QQmlComponentExtension, componentExtension);
84 Try to do what's necessary for a reasonable display of the type
85 name, but no more (just enough for the client to do more extensive cleanup).
87 Should only be called when debugging is enabled.
89 static inline QString buildTypeNameForDebug(const QMetaObject *metaObject)
91 static const QString qmlMarker(QLatin1String("_QML"));
92 static const QChar underscore(QLatin1Char('_'));
93 static const QChar asterisk(QLatin1Char('*'));
94 QQmlType *type = QQmlMetaType::qmlType(metaObject);
95 QString typeName = type ? type->qmlTypeName() : QString::fromUtf8(metaObject->className());
97 //### optimize further?
98 int marker = typeName.indexOf(qmlMarker);
99 if (marker != -1 && marker < typeName.count() - 1) {
100 if (typeName[marker + 1] == underscore) {
101 const QString className = typeName.left(marker) + asterisk;
102 type = QQmlMetaType::qmlType(QMetaType::type(className.toUtf8()));
104 typeName = type->qmlTypeName();
114 \brief The QQmlComponent class encapsulates a QML component definition.
117 Components are reusable, encapsulated QML elements with well-defined interfaces.
118 They are often defined in \l {qdeclarativedocuments.html}{Component Files}.
120 A QQmlComponent instance can be created from a QML file.
121 For example, if there is a \c main.qml file like this:
132 The following code loads this QML file as a component, creates an instance of
133 this component using create(), and then queries the \l Item's \l {Item::}{width}
137 QQmlEngine *engine = new QQmlEngine;
138 QQmlComponent component(engine, QUrl::fromLocalFile("main.qml"));
140 QObject *myObject = component.create();
141 QQuickItem *item = qobject_cast<QQuickItem*>(myObject);
142 int width = item->width(); // width = 200
146 \section2 Network Components
148 If the URL passed to QQmlComponent is a network resource, or if the QML document references a
149 network resource, the QQmlComponent has to fetch the network data before it is able to create
150 objects. In this case, the QQmlComponent will have a \l {QQmlComponent::Loading}{Loading}
151 \l {QQmlComponent::status()}{status}. An application will have to wait until the component
152 is \l {QQmlComponent::Ready}{Ready} before calling \l {QQmlComponent::create()}.
154 The following example shows how to load a QML file from a network resource. After creating
155 the QQmlComponent, it tests whether the component is loading. If it is, it connects to the
156 QQmlComponent::statusChanged() signal and otherwise calls the \c {continueLoading()} method
157 directly. Note that QQmlComponent::isLoading() may be false for a network component if the
158 component has been cached and is ready immediately.
161 MyApplication::MyApplication()
164 component = new QQmlComponent(engine, QUrl("http://www.example.com/main.qml"));
165 if (component->isLoading())
166 QObject::connect(component, SIGNAL(statusChanged(QQmlComponent::Status)),
167 this, SLOT(continueLoading()));
172 void MyApplication::continueLoading()
174 if (component->isError()) {
175 qWarning() << component->errors();
177 QObject *myObject = component->create();
182 \sa {Using QML Bindings in C++ Applications}, {Integrating QML Code with Existing Qt UI Code}
186 \qmlclass Component QQmlComponent
187 \ingroup qml-utility-elements
189 \brief The Component element encapsulates a QML component definition.
191 Components are reusable, encapsulated QML elements with well-defined interfaces.
193 Components are often defined by \l {qdeclarativedocuments.html}{component files} -
194 that is, \c .qml files. The \e Component element essentially allows QML components
195 to be defined inline, within a \l {QML Document}{QML document}, rather than as a separate QML file.
196 This may be useful for reusing a small component within a QML file, or for defining
197 a component that logically belongs with other QML components within a file.
199 For example, here is a component that is used by multiple \l Loader objects.
200 It contains a single item, a \l Rectangle:
202 \snippet doc/src/snippets/qml/component.qml 0
204 Notice that while a \l Rectangle by itself would be automatically
205 rendered and displayed, this is not the case for the above rectangle
206 because it is defined inside a \c Component. The component encapsulates the
207 QML elements within, as if they were defined in a separate QML
208 file, and is not loaded until requested (in this case, by the
209 two \l Loader objects).
211 Defining a \c Component is similar to defining a \l {QML Document}{QML document}.
212 A QML document has a single top-level item that defines the behaviors and
213 properties of that component, and cannot define properties or behaviors outside
214 of that top-level item. In the same way, a \c Component definition contains a single
215 top level item (which in the above example is a \l Rectangle) and cannot define any
216 data outside of this item, with the exception of an \e id (which in the above example
219 The \c Component element is commonly used to provide graphical components
220 for views. For example, the ListView::delegate property requires a \c Component
221 to specify how each list item is to be displayed.
223 \c Component objects can also be created dynamically using
224 \l{QML:Qt::createComponent()}{Qt.createComponent()}.
228 \qmlattachedsignal Component::onCompleted()
230 Emitted after component "startup" has completed. This can be used to
231 execute script code at startup, once the full QML environment has been
234 The \c {Component::onCompleted} attached property can be applied to
235 any element. The order of running the \c onCompleted scripts is
240 Component.onCompleted: console.log("Completed Running!")
242 Component.onCompleted: console.log("Nested Completed Running!")
249 \qmlattachedsignal Component::onDestruction()
251 Emitted as the component begins destruction. This can be used to undo
252 work done in the onCompleted signal, or other imperative code in your
255 The \c {Component::onDestruction} attached property can be applied to
256 any element. However, it applies to the destruction of the component as
257 a whole, and not the destruction of the specific object. The order of
258 running the \c onDestruction scripts is undefined.
262 Component.onDestruction: console.log("Destruction Beginning!")
264 Component.onDestruction: console.log("Nested Destruction Beginning!")
273 \enum QQmlComponent::Status
275 Specifies the loading status of the QQmlComponent.
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 \{QQmlError}{errors}.
283 void QQmlComponentPrivate::typeDataReady(QQmlTypeData *)
289 fromTypeData(typeData);
292 emit q->statusChanged(q->status());
295 void QQmlComponentPrivate::typeDataProgress(QQmlTypeData *, qreal p)
301 emit q->progressChanged(p);
304 void QQmlComponentPrivate::fromTypeData(QQmlTypeData *data)
306 url = data->finalUrl();
307 QQmlCompiledData *c = data->compiledData();
310 Q_ASSERT(data->isError());
311 state.errors = data->errors();
319 void QQmlComponentPrivate::clear()
322 typeData->unregisterCallback(this);
336 QQmlComponent::QQmlComponent(QObject *parent)
337 : QObject(*(new QQmlComponentPrivate), parent)
342 Destruct the QQmlComponent.
344 QQmlComponent::~QQmlComponent()
348 if (d->state.completePending) {
349 qWarning("QQmlComponent: Component destroyed while completion pending");
354 d->typeData->unregisterCallback(d);
355 d->typeData->release();
362 \qmlproperty enumeration Component::status
363 This property holds the status of component loading. It can be one of:
365 \li Component.Null - no data is available for the component
366 \li Component.Ready - the component has been loaded, and can be used to create instances.
367 \li Component.Loading - the component is currently being loaded
368 \li Component.Error - an error occurred while loading the component.
369 Calling errorString() will provide a human-readable description of any errors.
374 \property QQmlComponent::status
375 The component's current \l{QQmlComponent::Status} {status}.
377 QQmlComponent::Status QQmlComponent::status() const
379 Q_D(const QQmlComponent);
383 else if (!d->state.errors.isEmpty())
385 else if (d->engine && d->cc)
392 Returns true if status() == QQmlComponent::Null.
394 bool QQmlComponent::isNull() const
396 return status() == Null;
400 Returns true if status() == QQmlComponent::Ready.
402 bool QQmlComponent::isReady() const
404 return status() == Ready;
408 Returns true if status() == QQmlComponent::Error.
410 bool QQmlComponent::isError() const
412 return status() == Error;
416 Returns true if status() == QQmlComponent::Loading.
418 bool QQmlComponent::isLoading() const
420 return status() == Loading;
424 \qmlproperty real Component::progress
425 The progress of loading the component, from 0.0 (nothing loaded)
430 \property QQmlComponent::progress
431 The progress of loading the component, from 0.0 (nothing loaded)
434 qreal QQmlComponent::progress() const
436 Q_D(const QQmlComponent);
441 \fn void QQmlComponent::progressChanged(qreal progress)
443 Emitted whenever the component's loading progress changes. \a progress will be the
444 current progress between 0.0 (nothing loaded) and 1.0 (finished).
448 \fn void QQmlComponent::statusChanged(QQmlComponent::Status status)
450 Emitted whenever the component's status changes. \a status will be the
455 Create a QQmlComponent with no data and give it the specified
456 \a engine and \a parent. Set the data with setData().
458 QQmlComponent::QQmlComponent(QQmlEngine *engine, QObject *parent)
459 : QObject(*(new QQmlComponentPrivate), parent)
466 Create a QQmlComponent from the given \a url and give it the
467 specified \a parent and \a engine.
469 Ensure that the URL provided is full and correct, in particular, use
470 \l QUrl::fromLocalFile() when loading a file from the local filesystem.
474 QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, QObject *parent)
475 : QObject(*(new QQmlComponentPrivate), parent)
483 Create a QQmlComponent from the given \a fileName and give it the specified
484 \a parent and \a engine.
488 QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName,
490 : QObject(*(new QQmlComponentPrivate), parent)
494 loadUrl(d->engine->baseUrl().resolved(QUrl::fromLocalFile(fileName)));
500 QQmlComponent::QQmlComponent(QQmlEngine *engine, QQmlCompiledData *cc, int start, QObject *parent)
501 : QObject(*(new QQmlComponentPrivate), parent)
513 Sets the QQmlComponent to use the given QML \a data. If \a url
514 is provided, it is used to set the component name and to provide
515 a base path for items resolved by this component.
517 void QQmlComponent::setData(const QByteArray &data, const QUrl &url)
525 QQmlTypeData *typeData = QQmlEnginePrivate::get(d->engine)->typeLoader.get(data, url);
527 if (typeData->isCompleteOrError()) {
528 d->fromTypeData(typeData);
530 d->typeData = typeData;
531 d->typeData->registerCallback(d);
535 emit statusChanged(status());
536 emit progressChanged(d->progress);
540 Returns the QQmlContext the component was created in. This is only
541 valid for components created directly from QML.
543 QQmlContext *QQmlComponent::creationContext() const
545 Q_D(const QQmlComponent);
546 if(d->creationContext)
547 return d->creationContext->asQQmlContext();
549 return qmlContext(this);
553 Load the QQmlComponent from the provided \a url.
555 Ensure that the URL provided is full and correct, in particular, use
556 \l QUrl::fromLocalFile() when loading a file from the local filesystem.
558 void QQmlComponent::loadUrl(const QUrl &url)
564 if ((url.isRelative() && !url.isEmpty())
565 || url.scheme() == QLatin1String("file")) // Workaround QTBUG-11929
566 d->url = d->engine->baseUrl().resolved(url);
572 error.setDescription(tr("Invalid empty URL"));
573 d->state.errors << error;
577 QQmlTypeData *data = QQmlEnginePrivate::get(d->engine)->typeLoader.get(d->url);
579 if (data->isCompleteOrError()) {
580 d->fromTypeData(data);
584 d->typeData->registerCallback(d);
585 d->progress = data->progress();
588 emit statusChanged(status());
589 emit progressChanged(d->progress);
593 Return the list of errors that occurred during the last compile or create
594 operation. An empty list is returned if isError() is not set.
596 QList<QQmlError> QQmlComponent::errors() const
598 Q_D(const QQmlComponent);
600 return d->state.errors;
602 return QList<QQmlError>();
606 \qmlmethod string Component::errorString()
608 Returns a human-readable description of any errors.
610 The string includes the file, location, and description of each error.
611 If multiple errors are present they are separated by a newline character.
613 If no errors are present, an empty string is returned.
618 errorString is only meant as a way to get the errors in script
620 QString QQmlComponent::errorString() const
622 Q_D(const QQmlComponent);
626 foreach(const QQmlError &e, d->state.errors) {
627 ret += e.url().toString() + QLatin1Char(':') +
628 QString::number(e.line()) + QLatin1Char(' ') +
629 e.description() + QLatin1Char('\n');
635 \qmlproperty url Component::url
636 The component URL. This is the URL that was used to construct the component.
640 \property QQmlComponent::url
641 The component URL. This is the URL passed to either the constructor,
642 or the loadUrl() or setData() methods.
644 QUrl QQmlComponent::url() const
646 Q_D(const QQmlComponent);
653 QQmlComponent::QQmlComponent(QQmlComponentPrivate &dd, QObject *parent)
654 : QObject(dd, parent)
659 Create an object instance from this component. Returns 0 if creation
660 failed. \a context specifies the context within which to create the object
663 If \a context is 0 (the default), it will create the instance in the
664 engine' s \l {QQmlEngine::rootContext()}{root context}.
666 QObject *QQmlComponent::create(QQmlContext *context)
671 context = d->engine->rootContext();
673 QObject *rv = beginCreate(context);
679 This method provides more advanced control over component instance creation.
680 In general, programmers should use QQmlComponent::create() to create a
683 Create an object instance from this component. Returns 0 if creation
684 failed. \a publicContext specifies the context within which to create the object
687 When QQmlComponent constructs an instance, it occurs in three steps:
689 \li The object hierarchy is created, and constant values are assigned.
690 \li Property bindings are evaluated for the the first time.
691 \li If applicable, QQmlParserStatus::componentComplete() is called on objects.
693 QQmlComponent::beginCreate() differs from QQmlComponent::create() in that it
694 only performs step 1. QQmlComponent::completeCreate() must be called to
695 complete steps 2 and 3.
697 This breaking point is sometimes useful when using attached properties to
698 communicate information to an instantiated component, as it allows their
699 initial values to be configured before property bindings take effect.
701 QObject *QQmlComponent::beginCreate(QQmlContext *publicContext)
705 Q_ASSERT(publicContext);
706 QQmlContextData *context = QQmlContextData::get(publicContext);
708 return d->beginCreate(context);
712 QQmlComponentPrivate::beginCreate(QQmlContextData *context)
716 qWarning("QQmlComponent: Cannot create a component in a null context");
720 if (!context->isValid()) {
721 qWarning("QQmlComponent: Cannot create a component in an invalid context");
725 if (context->engine != engine) {
726 qWarning("QQmlComponent: Must create component in context from the same QQmlEngine");
730 if (state.completePending) {
731 qWarning("QQmlComponent: Cannot create new component instance before completing the previous");
736 qWarning("QQmlComponent: Component is not ready");
740 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine);
742 if (enginePriv->inProgressCreations == 0) {
743 // only track root, since further ones might not be properly nested
744 profiler = new QQmlObjectCreatingProfiler();
747 enginePriv->inProgressCreations++;
748 state.errors.clear();
749 state.completePending = true;
751 enginePriv->referenceScarceResources();
752 state.vme.init(context, cc, start, creationContext);
753 QObject *rv = state.vme.execute(&state.errors);
754 enginePriv->dereferenceScarceResources();
757 QQmlData *ddata = QQmlData::get(rv);
759 ddata->indestructible = true;
762 if (enginePriv->isDebugging && rv) {
763 if (!context->isInternal)
764 context->asQQmlContextPrivate()->instances.append(rv);
765 QQmlEngineDebugService::instance()->objectCreated(engine, rv);
767 if (profiler && profiler->enabled) {
768 profiler->setTypeName(buildTypeNameForDebug(rv->metaObject()));
769 QQmlData *data = QQmlData::get(rv);
771 profiler->setLocation(cc->url, data->lineNumber, data->columnNumber);
778 void QQmlComponentPrivate::beginDeferred(QQmlEnginePrivate *enginePriv,
779 QObject *object, ConstructionState *state)
781 enginePriv->inProgressCreations++;
782 state->errors.clear();
783 state->completePending = true;
785 state->vme.initDeferred(object);
786 state->vme.execute(&state->errors);
789 void QQmlComponentPrivate::complete(QQmlEnginePrivate *enginePriv, ConstructionState *state)
791 if (state->completePending) {
792 state->vme.complete();
794 state->completePending = false;
796 enginePriv->inProgressCreations--;
798 if (0 == enginePriv->inProgressCreations) {
799 while (enginePriv->erroredBindings) {
800 enginePriv->warning(enginePriv->erroredBindings->error);
801 enginePriv->erroredBindings->removeError();
808 This method provides more advanced control over component instance creation.
809 In general, programmers should use QQmlComponent::create() to create a
812 Complete a component creation begin with QQmlComponent::beginCreate().
814 void QQmlComponent::completeCreate()
821 void QQmlComponentPrivate::completeCreate()
823 if (state.completePending) {
824 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
825 complete(ep, &state);
832 QQmlComponentAttached::QQmlComponentAttached(QObject *parent)
833 : QObject(parent), prev(0), next(0)
837 QQmlComponentAttached::~QQmlComponentAttached()
839 if (prev) *prev = next;
840 if (next) next->prev = prev;
848 QQmlComponentAttached *QQmlComponent::qmlAttachedProperties(QObject *obj)
850 QQmlComponentAttached *a = new QQmlComponentAttached(obj);
852 QQmlEngine *engine = qmlEngine(obj);
856 if (QQmlEnginePrivate::get(engine)->activeVME) { // XXX should only be allowed during begin
857 QQmlEnginePrivate *p = QQmlEnginePrivate::get(engine);
858 a->add(&p->activeVME->componentAttached);
860 QQmlData *d = QQmlData::get(obj);
862 Q_ASSERT(d->context);
863 a->add(&d->context->componentAttached);
869 void QQmlComponent::create(QQmlIncubator &i, QQmlContext *context,
870 QQmlContext *forContext)
875 context = d->engine->rootContext();
877 QQmlContextData *contextData = QQmlContextData::get(context);
878 QQmlContextData *forContextData = contextData;
879 if (forContext) forContextData = QQmlContextData::get(forContext);
881 if (!contextData->isValid()) {
882 qWarning("QQmlComponent: Cannot create a component in an invalid context");
886 if (contextData->engine != d->engine) {
887 qWarning("QQmlComponent: Must create component in context from the same QQmlEngine");
892 qWarning("QQmlComponent: Component is not ready");
897 QQmlIncubatorPrivate *p = i.d;
899 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(d->engine);
901 p->component = d->cc; p->component->addref();
902 p->vme.init(contextData, d->cc, d->start, d->creationContext);
904 enginePriv->incubate(i, forContextData);
907 class QV8IncubatorResource : public QV8ObjectResource,
910 V8_RESOURCE_TYPE(IncubatorType)
912 QV8IncubatorResource(QV8Engine *engine, IncubationMode = Asynchronous);
914 static v8::Handle<v8::Value> StatusChangedGetter(v8::Local<v8::String>,
915 const v8::AccessorInfo& info);
916 static v8::Handle<v8::Value> StatusGetter(v8::Local<v8::String>,
917 const v8::AccessorInfo& info);
918 static v8::Handle<v8::Value> ObjectGetter(v8::Local<v8::String>,
919 const v8::AccessorInfo& info);
920 static v8::Handle<v8::Value> ForceCompletionGetter(v8::Local<v8::String>,
921 const v8::AccessorInfo& info);
922 static v8::Handle<v8::Value> ForceCompletion(const v8::Arguments &args);
924 static void StatusChangedSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
925 const v8::AccessorInfo& info);
929 v8::Persistent<v8::Object> me;
930 QQmlGuard<QObject> parent;
931 v8::Persistent<v8::Value> valuemap;
932 v8::Persistent<v8::Object> qmlGlobal;
934 virtual void statusChanged(Status);
935 virtual void setInitialState(QObject *);
938 static void QQmlComponent_setQmlParent(QObject *me, QObject *parent)
941 me->setParent(parent);
942 typedef QQmlPrivate::AutoParentFunction APF;
943 QList<APF> functions = QQmlMetaType::parentFunctions();
945 bool needParent = false;
946 for (int ii = 0; ii < functions.count(); ++ii) {
947 QQmlPrivate::AutoParentResult res = functions.at(ii)(me, parent);
948 if (res == QQmlPrivate::Parented) {
951 } else if (res == QQmlPrivate::IncompatibleParent) {
956 qWarning("QQmlComponent: Created graphical object was not "
957 "placed in the graphics scene.");
962 \qmlmethod object Component::createObject(Item parent, object properties)
964 Creates and returns an object instance of this component that will have
965 the given \a parent and \a properties. The \a properties argument is optional.
966 Returns null if object creation fails.
968 The object will be created in the same context as the one in which the component
969 was created. This function will always return null when called on components
970 which were not created in QML.
972 If you wish to create an object without setting a parent, specify \c null for
973 the \a parent value. Note that if the returned object is to be displayed, you
974 must provide a valid \a parent value or set the returned object's \l{Item::parent}{parent}
975 property, or else the object will not be visible.
977 If a \a parent is not provided to createObject(), a reference to the returned object must be held so that
978 it is not destroyed by the garbage collector. This is true regardless of whether \l{Item::parent} is set afterwards,
979 since setting the Item parent does not change object ownership; only the graphical parent is changed.
981 As of QtQuick 1.1, this method accepts an optional \a properties argument that specifies a
982 map of initial property values for the created object. These values are applied before object
983 creation is finalized. (This is more efficient than setting property values after object creation,
984 particularly where large sets of property values are defined, and also allows property bindings
985 to be set up before the object is created.)
987 The \a properties argument is specified as a map of property-value items. For example, the code
988 below creates an object with initial \c x and \c y values of 100 and 200, respectively:
991 var component = Qt.createComponent("Button.qml");
992 if (component.status == Component.Ready)
993 component.createObject(parent, {"x": 100, "y": 100});
996 Dynamically created instances can be deleted with the \c destroy() method.
997 See \l {Dynamic Object Management in QML} for more information.
999 void QQmlComponent::createObject(QQmlV8Function *args)
1002 Q_ASSERT(d->engine);
1005 QObject *parent = 0;
1006 v8::Local<v8::Object> valuemap;
1008 if (args->Length() >= 1)
1009 parent = args->engine()->toQObject((*args)[0]);
1011 if (args->Length() >= 2) {
1012 v8::Local<v8::Value> v = (*args)[1];
1013 if (!v->IsObject() || v->IsArray()) {
1014 qmlInfo(this) << tr("createObject: value is not an object");
1015 args->returnValue(v8::Null());
1018 valuemap = v8::Local<v8::Object>::Cast(v);
1021 QV8Engine *v8engine = args->engine();
1023 QQmlContext *ctxt = creationContext();
1024 if (!ctxt) ctxt = d->engine->rootContext();
1026 QObject *rv = beginCreate(ctxt);
1029 args->returnValue(v8::Null());
1033 QQmlComponent_setQmlParent(rv, parent);
1035 v8::Handle<v8::Value> ov = v8engine->newQObject(rv);
1036 Q_ASSERT(ov->IsObject());
1037 v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(ov);
1039 if (!valuemap.IsEmpty()) {
1040 QQmlComponentExtension *e = componentExtension(v8engine);
1041 // Try catch isn't needed as the function itself is loaded with try/catch
1042 v8::Handle<v8::Value> function = e->initialProperties->Run(args->qmlGlobal());
1043 v8::Handle<v8::Value> args[] = { object, valuemap };
1044 v8::Handle<v8::Function>::Cast(function)->Call(v8engine->global(), 2, args);
1047 d->completeCreate();
1049 Q_ASSERT(QQmlData::get(rv));
1050 QQmlData::get(rv)->setImplicitDestructible();
1053 args->returnValue(v8::Null());
1055 args->returnValue(object);
1059 \qmlmethod object Component::incubateObject(Item parent, object properties, enum mode)
1061 Creates an incubator for instance of this component. Incubators allow new component
1062 instances to be instantiated asynchronously and not cause freezes in the UI.
1064 The \a parent argument specifies the parent the created instance will have. Omitting the
1065 parameter or passing null will create anobject with no parent. In this case, a reference
1066 to the created object must be maintained by the application of the object will eventually
1067 be garbage collected.
1069 The \a properties argument is specified as a map of property-value items which will be
1070 set on the created object during its construction. \a mode may be Qt.Synchronous or
1071 Qt.Asynchronous and controls whether the instance is created synchronously or asynchronously.
1072 The default is asynchronously. In some circumstances, even if Qt.Synchronous is specified,
1073 the incubator may create the object asynchronously. This happens if the component calling
1074 incubateObject() is itself being created asynchronously.
1076 All three arguments are optional.
1078 If successful, the method returns an incubator, otherwise null. The incubator has the following
1082 \li status The status of the incubator. Valid values are Component.Ready, Component.Loading and
1084 \li object The created object instance. Will only be available once the incubator is in the
1086 \li onStatusChanged Specifies a callback function to be invoked when the status changes. The
1087 status is passed as a parameter to the callback.
1088 \li forceCompletion() Call to complete incubation synchronously.
1091 The following example demonstrates how to use an incubator:
1094 var component = Qt.createComponent("Button.qml");
1096 var incubator = component.incubateObject(parent, { x: 10, y: 10 });
1097 if (incubator.status != Component.Ready) {
1098 incubator.onStatusChanged = function(status) {
1099 if (status == Component.Ready) {
1100 print ("Object", incubator.object, "is now ready!");
1104 print ("Object", incubator.object, "is ready immediately!");
1109 void QQmlComponent::incubateObject(QQmlV8Function *args)
1112 Q_ASSERT(d->engine);
1116 QObject *parent = 0;
1117 v8::Local<v8::Object> valuemap;
1118 QQmlIncubator::IncubationMode mode = QQmlIncubator::Asynchronous;
1120 if (args->Length() >= 1)
1121 parent = args->engine()->toQObject((*args)[0]);
1123 if (args->Length() >= 2) {
1124 v8::Local<v8::Value> v = (*args)[1];
1126 } else if (!v->IsObject() || v->IsArray()) {
1127 qmlInfo(this) << tr("createObject: value is not an object");
1128 args->returnValue(v8::Null());
1131 valuemap = v8::Local<v8::Object>::Cast(v);
1135 if (args->Length() >= 3) {
1136 quint32 v = (*args)[2]->Uint32Value();
1138 mode = QQmlIncubator::Asynchronous;
1140 mode = QQmlIncubator::AsynchronousIfNested;
1143 QQmlComponentExtension *e = componentExtension(args->engine());
1145 QV8IncubatorResource *r = new QV8IncubatorResource(args->engine(), mode);
1146 v8::Local<v8::Object> o = e->incubationConstructor->NewInstance();
1147 o->SetExternalResource(r);
1149 if (!valuemap.IsEmpty()) {
1150 r->valuemap = qPersistentNew(valuemap);
1151 r->qmlGlobal = qPersistentNew(args->qmlGlobal());
1154 r->me = qPersistentNew(o);
1156 create(*r, creationContext());
1158 if (r->status() == QQmlIncubator::Null) {
1160 args->returnValue(v8::Null());
1162 args->returnValue(o);
1166 // XXX used by QSGLoader
1167 void QQmlComponentPrivate::initializeObjectWithInitialProperties(v8::Handle<v8::Object> qmlGlobal, v8::Handle<v8::Object> valuemap, QObject *toCreate)
1169 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
1170 QV8Engine *v8engine = ep->v8engine();
1172 v8::HandleScope handle_scope;
1173 v8::Context::Scope scope(v8engine->context());
1174 v8::Handle<v8::Value> ov = v8engine->newQObject(toCreate);
1175 Q_ASSERT(ov->IsObject());
1176 v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(ov);
1178 if (!valuemap.IsEmpty()) {
1179 QQmlComponentExtension *e = componentExtension(v8engine);
1180 // Try catch isn't needed as the function itself is loaded with try/catch
1181 v8::Handle<v8::Value> function = e->initialProperties->Run(qmlGlobal);
1182 v8::Handle<v8::Value> args[] = { object, valuemap };
1183 v8::Handle<v8::Function>::Cast(function)->Call(v8engine->global(), 2, args);
1186 QQmlData *ddata = QQmlData::get(toCreate);
1188 ddata->setImplicitDestructible();
1192 QQmlComponentExtension::QQmlComponentExtension(QV8Engine *engine)
1194 v8::HandleScope handle_scope;
1195 v8::Context::Scope scope(engine->context());
1197 forceCompletion = qPersistentNew(V8FUNCTION(QV8IncubatorResource::ForceCompletion, engine));
1200 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
1201 ft->InstanceTemplate()->SetHasExternalResource(true);
1202 ft->InstanceTemplate()->SetInternalFieldCount(1);
1203 ft->InstanceTemplate()->SetAccessor(v8::String::New("onStatusChanged"),
1204 QV8IncubatorResource::StatusChangedGetter,
1205 QV8IncubatorResource::StatusChangedSetter);
1206 ft->InstanceTemplate()->SetAccessor(v8::String::New("status"),
1207 QV8IncubatorResource::StatusGetter);
1208 ft->InstanceTemplate()->SetAccessor(v8::String::New("object"),
1209 QV8IncubatorResource::ObjectGetter);
1210 ft->InstanceTemplate()->SetAccessor(v8::String::New("forceCompletion"),
1211 QV8IncubatorResource::ForceCompletionGetter);
1212 incubationConstructor = qPersistentNew(ft->GetFunction());
1216 #define INITIALPROPERTIES_SOURCE \
1217 "(function(object, values) {"\
1219 "for(var property in values) {" \
1221 "var properties = property.split(\".\");"\
1223 "for (var ii = 0; ii < properties.length - 1; ++ii) {"\
1224 "o = o[properties[ii]];"\
1226 "o[properties[properties.length - 1]] = values[property];"\
1231 initialProperties = qPersistentNew(engine->qmlModeCompile(QLatin1String(INITIALPROPERTIES_SOURCE)));
1232 #undef INITIALPROPERTIES_SOURCE
1236 v8::Handle<v8::Value> QV8IncubatorResource::ObjectGetter(v8::Local<v8::String>,
1237 const v8::AccessorInfo& info)
1239 QV8IncubatorResource *r = v8_resource_check<QV8IncubatorResource>(info.This());
1240 return r->engine->newQObject(r->object());
1243 v8::Handle<v8::Value> QV8IncubatorResource::ForceCompletionGetter(v8::Local<v8::String>,
1244 const v8::AccessorInfo& info)
1246 QV8IncubatorResource *r = v8_resource_check<QV8IncubatorResource>(info.This());
1247 return componentExtension(r->engine)->forceCompletion;
1250 v8::Handle<v8::Value> QV8IncubatorResource::ForceCompletion(const v8::Arguments &args)
1252 QV8IncubatorResource *r = v8_resource_cast<QV8IncubatorResource>(args.This());
1254 V8THROW_TYPE("Not an incubator object");
1256 r->forceCompletion();
1258 return v8::Undefined();
1261 v8::Handle<v8::Value> QV8IncubatorResource::StatusGetter(v8::Local<v8::String>,
1262 const v8::AccessorInfo& info)
1264 QV8IncubatorResource *r = v8_resource_check<QV8IncubatorResource>(info.This());
1265 return v8::Integer::NewFromUnsigned(r->status());
1268 v8::Handle<v8::Value> QV8IncubatorResource::StatusChangedGetter(v8::Local<v8::String>,
1269 const v8::AccessorInfo& info)
1271 return info.This()->GetInternalField(0);
1274 void QV8IncubatorResource::StatusChangedSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
1275 const v8::AccessorInfo& info)
1277 info.This()->SetInternalField(0, value);
1280 QQmlComponentExtension::~QQmlComponentExtension()
1282 qPersistentDispose(incubationConstructor);
1283 qPersistentDispose(initialProperties);
1284 qPersistentDispose(forceCompletion);
1287 QV8IncubatorResource::QV8IncubatorResource(QV8Engine *engine, IncubationMode m)
1288 : QV8ObjectResource(engine), QQmlIncubator(m)
1292 void QV8IncubatorResource::setInitialState(QObject *o)
1294 QQmlComponent_setQmlParent(o, parent);
1296 if (!valuemap.IsEmpty()) {
1297 QQmlComponentExtension *e = componentExtension(engine);
1299 v8::HandleScope handle_scope;
1300 v8::Context::Scope scope(engine->context());
1302 v8::Handle<v8::Value> function = e->initialProperties->Run(qmlGlobal);
1303 v8::Handle<v8::Value> args[] = { engine->newQObject(o), valuemap };
1304 v8::Handle<v8::Function>::Cast(function)->Call(engine->global(), 2, args);
1306 qPersistentDispose(valuemap);
1307 qPersistentDispose(qmlGlobal);
1311 void QV8IncubatorResource::dispose()
1313 qPersistentDispose(valuemap);
1314 qPersistentDispose(qmlGlobal);
1315 // No further status changes are forthcoming, so we no long need a self reference
1316 qPersistentDispose(me);
1319 void QV8IncubatorResource::statusChanged(Status s)
1322 Q_ASSERT(QQmlData::get(object()));
1323 QQmlData::get(object())->setImplicitDestructible();
1326 if (!me.IsEmpty()) { // Will be false in synchronous mode
1327 v8::HandleScope scope;
1328 v8::Local<v8::Value> callback = me->GetInternalField(0);
1330 if (!callback.IsEmpty() && !callback->IsUndefined()) {
1332 if (callback->IsFunction()) {
1333 v8::Context::Scope context_scope(engine->context());
1334 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(callback);
1335 v8::Handle<v8::Value> args[] = { v8::Integer::NewFromUnsigned(s) };
1337 f->Call(me, 1, args);
1338 if (tc.HasCaught()) {
1340 QQmlJavaScriptExpression::exceptionToError(tc.Message(), error);
1341 QQmlEnginePrivate::warning(QQmlEnginePrivate::get(engine->engine()), error);
1347 if (s == Ready || s == Error)