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 "qqmlbinding_p_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"
60 #include <private/qv8engine_p.h>
61 #include <private/qv8include_p.h>
64 #include <QStringList>
65 #include <QtCore/qdebug.h>
70 class QQmlComponentExtension : public QV8Engine::Deletable
73 QQmlComponentExtension(QV8Engine *);
74 virtual ~QQmlComponentExtension();
76 v8::Persistent<v8::Function> incubationConstructor;
77 v8::Persistent<v8::Script> initialProperties;
78 v8::Persistent<v8::Function> forceCompletion;
80 V8_DEFINE_EXTENSION(QQmlComponentExtension, componentExtension);
83 Try to do what's necessary for a reasonable display of the type
84 name, but no more (just enough for the client to do more extensive cleanup).
86 Should only be called when debugging is enabled.
88 static inline QString buildTypeNameForDebug(const QMetaObject *metaObject)
90 static const QString qmlMarker(QLatin1String("_QML"));
91 static const QChar underscore(QLatin1Char('_'));
92 static const QChar asterisk(QLatin1Char('*'));
93 QQmlType *type = QQmlMetaType::qmlType(metaObject);
94 QString typeName = type ? type->qmlTypeName() : QString::fromUtf8(metaObject->className());
96 //### optimize further?
97 int marker = typeName.indexOf(qmlMarker);
98 if (marker != -1 && marker < typeName.count() - 1) {
99 if (typeName[marker + 1] == underscore) {
100 const QString className = typeName.left(marker) + asterisk;
101 type = QQmlMetaType::qmlType(QMetaType::type(className.toUtf8()));
103 typeName = type->qmlTypeName();
113 \brief The QQmlComponent class encapsulates a QML component definition.
116 Components are reusable, encapsulated QML elements with well-defined interfaces.
117 They are often defined in \l {qdeclarativedocuments.html}{Component Files}.
119 A QQmlComponent instance can be created from a QML file.
120 For example, if there is a \c main.qml file like this:
131 The following code loads this QML file as a component, creates an instance of
132 this component using create(), and then queries the \l Item's \l {Item::}{width}
136 QQmlEngine *engine = new QQmlEngine;
137 QQmlComponent component(engine, QUrl::fromLocalFile("main.qml"));
139 QObject *myObject = component.create();
140 QQuickItem *item = qobject_cast<QQuickItem*>(myObject);
141 int width = item->width(); // width = 200
145 \section2 Network Components
147 If the URL passed to QQmlComponent is a network resource, or if the QML document references a
148 network resource, the QQmlComponent has to fetch the network data before it is able to create
149 objects. In this case, the QQmlComponent will have a \l {QQmlComponent::Loading}{Loading}
150 \l {QQmlComponent::status()}{status}. An application will have to wait until the component
151 is \l {QQmlComponent::Ready}{Ready} before calling \l {QQmlComponent::create()}.
153 The following example shows how to load a QML file from a network resource. After creating
154 the QQmlComponent, it tests whether the component is loading. If it is, it connects to the
155 QQmlComponent::statusChanged() signal and otherwise calls the \c {continueLoading()} method
156 directly. Note that QQmlComponent::isLoading() may be false for a network component if the
157 component has been cached and is ready immediately.
160 MyApplication::MyApplication()
163 component = new QQmlComponent(engine, QUrl("http://www.example.com/main.qml"));
164 if (component->isLoading())
165 QObject::connect(component, SIGNAL(statusChanged(QQmlComponent::Status)),
166 this, SLOT(continueLoading()));
171 void MyApplication::continueLoading()
173 if (component->isError()) {
174 qWarning() << component->errors();
176 QObject *myObject = component->create();
181 \sa {Using QML Bindings in C++ Applications}, {Integrating QML Code with Existing Qt UI Code}
185 \qmlclass Component QQmlComponent
186 \ingroup qml-utility-elements
188 \brief The Component element encapsulates a QML component definition.
190 Components are reusable, encapsulated QML elements with well-defined interfaces.
192 Components are often defined by \l {qdeclarativedocuments.html}{component files} -
193 that is, \c .qml files. The \e Component element essentially allows QML components
194 to be defined inline, within a \l {QML Document}{QML document}, rather than as a separate QML file.
195 This may be useful for reusing a small component within a QML file, or for defining
196 a component that logically belongs with other QML components within a file.
198 For example, here is a component that is used by multiple \l Loader objects.
199 It contains a single item, a \l Rectangle:
201 \snippet doc/src/snippets/qml/component.qml 0
203 Notice that while a \l Rectangle by itself would be automatically
204 rendered and displayed, this is not the case for the above rectangle
205 because it is defined inside a \c Component. The component encapsulates the
206 QML elements within, as if they were defined in a separate QML
207 file, and is not loaded until requested (in this case, by the
208 two \l Loader objects).
210 Defining a \c Component is similar to defining a \l {QML Document}{QML document}.
211 A QML document has a single top-level item that defines the behaviors and
212 properties of that component, and cannot define properties or behaviors outside
213 of that top-level item. In the same way, a \c Component definition contains a single
214 top level item (which in the above example is a \l Rectangle) and cannot define any
215 data outside of this item, with the exception of an \e id (which in the above example
218 The \c Component element is commonly used to provide graphical components
219 for views. For example, the ListView::delegate property requires a \c Component
220 to specify how each list item is to be displayed.
222 \c Component objects can also be created dynamically using
223 \l{QML:Qt::createComponent()}{Qt.createComponent()}.
227 \qmlattachedsignal Component::onCompleted()
229 Emitted after component "startup" has completed. This can be used to
230 execute script code at startup, once the full QML environment has been
233 The \c {Component::onCompleted} attached property can be applied to
234 any element. The order of running the \c onCompleted scripts is
239 Component.onCompleted: console.log("Completed Running!")
241 Component.onCompleted: console.log("Nested Completed Running!")
248 \qmlattachedsignal Component::onDestruction()
250 Emitted as the component begins destruction. This can be used to undo
251 work done in the onCompleted signal, or other imperative code in your
254 The \c {Component::onDestruction} attached property can be applied to
255 any element. However, it applies to the destruction of the component as
256 a whole, and not the destruction of the specific object. The order of
257 running the \c onDestruction scripts is undefined.
261 Component.onDestruction: console.log("Destruction Beginning!")
263 Component.onDestruction: console.log("Nested Destruction Beginning!")
272 \enum QQmlComponent::Status
274 Specifies the loading status of the QQmlComponent.
276 \value Null This QQmlComponent has no data. Call loadUrl() or setData() to add QML content.
277 \value Ready This QQmlComponent is ready and create() may be called.
278 \value Loading This QQmlComponent is loading network data.
279 \value Error An error has occurred. Call errors() to retrieve a list of \{QQmlError}{errors}.
282 void QQmlComponentPrivate::typeDataReady(QQmlTypeData *)
288 fromTypeData(typeData);
291 emit q->statusChanged(q->status());
294 void QQmlComponentPrivate::typeDataProgress(QQmlTypeData *, qreal p)
300 emit q->progressChanged(p);
303 void QQmlComponentPrivate::fromTypeData(QQmlTypeData *data)
305 url = data->finalUrl();
306 QQmlCompiledData *c = data->compiledData();
309 Q_ASSERT(data->isError());
310 state.errors = data->errors();
318 void QQmlComponentPrivate::clear()
321 typeData->unregisterCallback(this);
335 QQmlComponent::QQmlComponent(QObject *parent)
336 : QObject(*(new QQmlComponentPrivate), parent)
341 Destruct the QQmlComponent.
343 QQmlComponent::~QQmlComponent()
347 if (d->state.completePending) {
348 qWarning("QQmlComponent: Component destroyed while completion pending");
353 d->typeData->unregisterCallback(d);
354 d->typeData->release();
361 \qmlproperty enumeration Component::status
362 This property holds the status of component loading. It can be one of:
364 \o Component.Null - no data is available for the component
365 \o Component.Ready - the component has been loaded, and can be used to create instances.
366 \o Component.Loading - the component is currently being loaded
367 \o Component.Error - an error occurred while loading the component.
368 Calling errorString() will provide a human-readable description of any errors.
373 \property QQmlComponent::status
374 The component's current \l{QQmlComponent::Status} {status}.
376 QQmlComponent::Status QQmlComponent::status() const
378 Q_D(const QQmlComponent);
382 else if (!d->state.errors.isEmpty())
384 else if (d->engine && d->cc)
391 Returns true if status() == QQmlComponent::Null.
393 bool QQmlComponent::isNull() const
395 return status() == Null;
399 Returns true if status() == QQmlComponent::Ready.
401 bool QQmlComponent::isReady() const
403 return status() == Ready;
407 Returns true if status() == QQmlComponent::Error.
409 bool QQmlComponent::isError() const
411 return status() == Error;
415 Returns true if status() == QQmlComponent::Loading.
417 bool QQmlComponent::isLoading() const
419 return status() == Loading;
423 \qmlproperty real Component::progress
424 The progress of loading the component, from 0.0 (nothing loaded)
429 \property QQmlComponent::progress
430 The progress of loading the component, from 0.0 (nothing loaded)
433 qreal QQmlComponent::progress() const
435 Q_D(const QQmlComponent);
440 \fn void QQmlComponent::progressChanged(qreal progress)
442 Emitted whenever the component's loading progress changes. \a progress will be the
443 current progress between 0.0 (nothing loaded) and 1.0 (finished).
447 \fn void QQmlComponent::statusChanged(QQmlComponent::Status status)
449 Emitted whenever the component's status changes. \a status will be the
454 Create a QQmlComponent with no data and give it the specified
455 \a engine and \a parent. Set the data with setData().
457 QQmlComponent::QQmlComponent(QQmlEngine *engine, QObject *parent)
458 : QObject(*(new QQmlComponentPrivate), parent)
465 Create a QQmlComponent from the given \a url and give it the
466 specified \a parent and \a engine.
468 Ensure that the URL provided is full and correct, in particular, use
469 \l QUrl::fromLocalFile() when loading a file from the local filesystem.
473 QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, QObject *parent)
474 : QObject(*(new QQmlComponentPrivate), parent)
482 Create a QQmlComponent from the given \a fileName and give it the specified
483 \a parent and \a engine.
487 QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName,
489 : QObject(*(new QQmlComponentPrivate), parent)
493 loadUrl(d->engine->baseUrl().resolved(QUrl::fromLocalFile(fileName)));
499 QQmlComponent::QQmlComponent(QQmlEngine *engine, QQmlCompiledData *cc, int start, QObject *parent)
500 : QObject(*(new QQmlComponentPrivate), parent)
512 Sets the QQmlComponent to use the given QML \a data. If \a url
513 is provided, it is used to set the component name and to provide
514 a base path for items resolved by this component.
516 void QQmlComponent::setData(const QByteArray &data, const QUrl &url)
524 QQmlTypeData *typeData = QQmlEnginePrivate::get(d->engine)->typeLoader.get(data, url);
526 if (typeData->isCompleteOrError()) {
527 d->fromTypeData(typeData);
529 d->typeData = typeData;
530 d->typeData->registerCallback(d);
534 emit statusChanged(status());
535 emit progressChanged(d->progress);
539 Returns the QQmlContext the component was created in. This is only
540 valid for components created directly from QML.
542 QQmlContext *QQmlComponent::creationContext() const
544 Q_D(const QQmlComponent);
545 if(d->creationContext)
546 return d->creationContext->asQQmlContext();
548 return qmlContext(this);
552 Load the QQmlComponent from the provided \a url.
554 Ensure that the URL provided is full and correct, in particular, use
555 \l QUrl::fromLocalFile() when loading a file from the local filesystem.
557 void QQmlComponent::loadUrl(const QUrl &url)
563 if ((url.isRelative() && !url.isEmpty())
564 || url.scheme() == QLatin1String("file")) // Workaround QTBUG-11929
565 d->url = d->engine->baseUrl().resolved(url);
571 error.setDescription(tr("Invalid empty URL"));
572 d->state.errors << error;
576 QQmlTypeData *data = QQmlEnginePrivate::get(d->engine)->typeLoader.get(d->url);
578 if (data->isCompleteOrError()) {
579 d->fromTypeData(data);
583 d->typeData->registerCallback(d);
584 d->progress = data->progress();
587 emit statusChanged(status());
588 emit progressChanged(d->progress);
592 Return the list of errors that occurred during the last compile or create
593 operation. An empty list is returned if isError() is not set.
595 QList<QQmlError> QQmlComponent::errors() const
597 Q_D(const QQmlComponent);
599 return d->state.errors;
601 return QList<QQmlError>();
605 \qmlmethod string Component::errorString()
607 Returns a human-readable description of any errors.
609 The string includes the file, location, and description of each error.
610 If multiple errors are present they are separated by a newline character.
612 If no errors are present, an empty string is returned.
617 errorString is only meant as a way to get the errors in script
619 QString QQmlComponent::errorString() const
621 Q_D(const QQmlComponent);
625 foreach(const QQmlError &e, d->state.errors) {
626 ret += e.url().toString() + QLatin1Char(':') +
627 QString::number(e.line()) + QLatin1Char(' ') +
628 e.description() + QLatin1Char('\n');
634 \qmlproperty url Component::url
635 The component URL. This is the URL that was used to construct the component.
639 \property QQmlComponent::url
640 The component URL. This is the URL passed to either the constructor,
641 or the loadUrl() or setData() methods.
643 QUrl QQmlComponent::url() const
645 Q_D(const QQmlComponent);
652 QQmlComponent::QQmlComponent(QQmlComponentPrivate &dd, QObject *parent)
653 : QObject(dd, parent)
658 Create an object instance from this component. Returns 0 if creation
659 failed. \a context specifies the context within which to create the object
662 If \a context is 0 (the default), it will create the instance in the
663 engine' s \l {QQmlEngine::rootContext()}{root context}.
665 QObject *QQmlComponent::create(QQmlContext *context)
670 context = d->engine->rootContext();
672 QObject *rv = beginCreate(context);
678 This method provides more advanced control over component instance creation.
679 In general, programmers should use QQmlComponent::create() to create a
682 Create an object instance from this component. Returns 0 if creation
683 failed. \a publicContext specifies the context within which to create the object
686 When QQmlComponent constructs an instance, it occurs in three steps:
688 \i The object hierarchy is created, and constant values are assigned.
689 \i Property bindings are evaluated for the the first time.
690 \i If applicable, QQmlParserStatus::componentComplete() is called on objects.
692 QQmlComponent::beginCreate() differs from QQmlComponent::create() in that it
693 only performs step 1. QQmlComponent::completeCreate() must be called to
694 complete steps 2 and 3.
696 This breaking point is sometimes useful when using attached properties to
697 communicate information to an instantiated component, as it allows their
698 initial values to be configured before property bindings take effect.
700 QObject *QQmlComponent::beginCreate(QQmlContext *publicContext)
704 Q_ASSERT(publicContext);
705 QQmlContextData *context = QQmlContextData::get(publicContext);
707 return d->beginCreate(context);
711 QQmlComponentPrivate::beginCreate(QQmlContextData *context)
715 qWarning("QQmlComponent: Cannot create a component in a null context");
719 if (!context->isValid()) {
720 qWarning("QQmlComponent: Cannot create a component in an invalid context");
724 if (context->engine != engine) {
725 qWarning("QQmlComponent: Must create component in context from the same QQmlEngine");
729 if (state.completePending) {
730 qWarning("QQmlComponent: Cannot create new component instance before completing the previous");
735 qWarning("QQmlComponent: Component is not ready");
739 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine);
741 bool isRoot = enginePriv->inProgressCreations == 0;
742 enginePriv->inProgressCreations++;
743 state.errors.clear();
744 state.completePending = true;
747 QQmlProfilerService::startRange(QQmlProfilerService::Creating);
749 enginePriv->referenceScarceResources();
750 state.vme.init(context, cc, start, creationContext);
751 QObject *rv = state.vme.execute(&state.errors);
752 enginePriv->dereferenceScarceResources();
755 QQmlData *ddata = QQmlData::get(rv);
757 ddata->indestructible = true;
760 if (enginePriv->isDebugging && rv) {
761 if (!context->isInternal)
762 context->asQQmlContextPrivate()->instances.append(rv);
763 QQmlEngineDebugService::instance()->objectCreated(engine, rv);
765 QQmlProfilerService::rangeData(QQmlProfilerService::Creating,
766 buildTypeNameForDebug(rv->metaObject()));
767 QQmlData *data = QQmlData::get(rv);
769 QQmlProfilerService::rangeLocation(QQmlProfilerService::Creating,
770 cc->url, data->lineNumber, data->columnNumber);
777 void QQmlComponentPrivate::beginDeferred(QQmlEnginePrivate *enginePriv,
778 QObject *object, ConstructionState *state)
780 enginePriv->inProgressCreations++;
781 state->errors.clear();
782 state->completePending = true;
784 state->vme.initDeferred(object);
785 state->vme.execute(&state->errors);
788 void QQmlComponentPrivate::complete(QQmlEnginePrivate *enginePriv, ConstructionState *state)
790 if (state->completePending) {
791 state->vme.complete();
793 state->completePending = false;
795 enginePriv->inProgressCreations--;
797 if (0 == enginePriv->inProgressCreations) {
798 while (enginePriv->erroredBindings) {
799 enginePriv->warning(enginePriv->erroredBindings->error);
800 enginePriv->erroredBindings->removeError();
807 This method provides more advanced control over component instance creation.
808 In general, programmers should use QQmlComponent::create() to create a
811 Complete a component creation begin with QQmlComponent::beginCreate().
813 void QQmlComponent::completeCreate()
820 void QQmlComponentPrivate::completeCreate()
822 if (state.completePending) {
823 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
824 complete(ep, &state);
826 QQmlProfilerService::endRange(QQmlProfilerService::Creating);
830 QQmlComponentAttached::QQmlComponentAttached(QObject *parent)
831 : QObject(parent), prev(0), next(0)
835 QQmlComponentAttached::~QQmlComponentAttached()
837 if (prev) *prev = next;
838 if (next) next->prev = prev;
846 QQmlComponentAttached *QQmlComponent::qmlAttachedProperties(QObject *obj)
848 QQmlComponentAttached *a = new QQmlComponentAttached(obj);
850 QQmlEngine *engine = qmlEngine(obj);
854 if (QQmlEnginePrivate::get(engine)->activeVME) { // XXX should only be allowed during begin
855 QQmlEnginePrivate *p = QQmlEnginePrivate::get(engine);
856 a->add(&p->activeVME->componentAttached);
858 QQmlData *d = QQmlData::get(obj);
860 Q_ASSERT(d->context);
861 a->add(&d->context->componentAttached);
867 void QQmlComponent::create(QQmlIncubator &i, QQmlContext *context,
868 QQmlContext *forContext)
873 context = d->engine->rootContext();
875 QQmlContextData *contextData = QQmlContextData::get(context);
876 QQmlContextData *forContextData = contextData;
877 if (forContext) forContextData = QQmlContextData::get(forContext);
879 if (!contextData->isValid()) {
880 qWarning("QQmlComponent: Cannot create a component in an invalid context");
884 if (contextData->engine != d->engine) {
885 qWarning("QQmlComponent: Must create component in context from the same QQmlEngine");
890 qWarning("QQmlComponent: Component is not ready");
895 QQmlIncubatorPrivate *p = i.d;
897 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(d->engine);
899 p->component = d->cc; p->component->addref();
900 p->vme.init(contextData, d->cc, d->start, d->creationContext);
902 enginePriv->incubate(i, forContextData);
905 class QV8IncubatorResource : public QV8ObjectResource,
908 V8_RESOURCE_TYPE(IncubatorType)
910 QV8IncubatorResource(QV8Engine *engine, IncubationMode = Asynchronous);
912 static v8::Handle<v8::Value> StatusChangedGetter(v8::Local<v8::String>,
913 const v8::AccessorInfo& info);
914 static v8::Handle<v8::Value> StatusGetter(v8::Local<v8::String>,
915 const v8::AccessorInfo& info);
916 static v8::Handle<v8::Value> ObjectGetter(v8::Local<v8::String>,
917 const v8::AccessorInfo& info);
918 static v8::Handle<v8::Value> ForceCompletionGetter(v8::Local<v8::String>,
919 const v8::AccessorInfo& info);
920 static v8::Handle<v8::Value> ForceCompletion(const v8::Arguments &args);
922 static void StatusChangedSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
923 const v8::AccessorInfo& info);
927 v8::Persistent<v8::Object> me;
928 QQmlGuard<QObject> parent;
929 v8::Persistent<v8::Value> valuemap;
930 v8::Persistent<v8::Object> qmlGlobal;
932 virtual void statusChanged(Status);
933 virtual void setInitialState(QObject *);
936 static void QQmlComponent_setQmlParent(QObject *me, QObject *parent)
939 me->setParent(parent);
940 typedef QQmlPrivate::AutoParentFunction APF;
941 QList<APF> functions = QQmlMetaType::parentFunctions();
943 bool needParent = false;
944 for (int ii = 0; ii < functions.count(); ++ii) {
945 QQmlPrivate::AutoParentResult res = functions.at(ii)(me, parent);
946 if (res == QQmlPrivate::Parented) {
949 } else if (res == QQmlPrivate::IncompatibleParent) {
954 qWarning("QQmlComponent: Created graphical object was not "
955 "placed in the graphics scene.");
960 \qmlmethod object Component::createObject(Item parent, object properties)
962 Creates and returns an object instance of this component that will have
963 the given \a parent and \a properties. The \a properties argument is optional.
964 Returns null if object creation fails.
966 The object will be created in the same context as the one in which the component
967 was created. This function will always return null when called on components
968 which were not created in QML.
970 If you wish to create an object without setting a parent, specify \c null for
971 the \a parent value. Note that if the returned object is to be displayed, you
972 must provide a valid \a parent value or set the returned object's \l{Item::parent}{parent}
973 property, or else the object will not be visible.
975 If a \a parent is not provided to createObject(), a reference to the returned object must be held so that
976 it is not destroyed by the garbage collector. This is true regardless of whether \l{Item::parent} is set afterwards,
977 since setting the Item parent does not change object ownership; only the graphical parent is changed.
979 As of QtQuick 1.1, this method accepts an optional \a properties argument that specifies a
980 map of initial property values for the created object. These values are applied before object
981 creation is finalized. (This is more efficient than setting property values after object creation,
982 particularly where large sets of property values are defined, and also allows property bindings
983 to be set up before the object is created.)
985 The \a properties argument is specified as a map of property-value items. For example, the code
986 below creates an object with initial \c x and \c y values of 100 and 200, respectively:
989 var component = Qt.createComponent("Button.qml");
990 if (component.status == Component.Ready)
991 component.createObject(parent, {"x": 100, "y": 100});
994 Dynamically created instances can be deleted with the \c destroy() method.
995 See \l {Dynamic Object Management in QML} for more information.
997 void QQmlComponent::createObject(QQmlV8Function *args)
1000 Q_ASSERT(d->engine);
1003 QObject *parent = 0;
1004 v8::Local<v8::Object> valuemap;
1006 if (args->Length() >= 1)
1007 parent = args->engine()->toQObject((*args)[0]);
1009 if (args->Length() >= 2) {
1010 v8::Local<v8::Value> v = (*args)[1];
1011 if (!v->IsObject() || v->IsArray()) {
1012 qmlInfo(this) << tr("createObject: value is not an object");
1013 args->returnValue(v8::Null());
1016 valuemap = v8::Local<v8::Object>::Cast(v);
1019 QV8Engine *v8engine = args->engine();
1021 QQmlContext *ctxt = creationContext();
1022 if (!ctxt) ctxt = d->engine->rootContext();
1024 QObject *rv = beginCreate(ctxt);
1027 args->returnValue(v8::Null());
1031 QQmlComponent_setQmlParent(rv, parent);
1033 v8::Handle<v8::Value> ov = v8engine->newQObject(rv);
1034 Q_ASSERT(ov->IsObject());
1035 v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(ov);
1037 if (!valuemap.IsEmpty()) {
1038 QQmlComponentExtension *e = componentExtension(v8engine);
1039 // Try catch isn't needed as the function itself is loaded with try/catch
1040 v8::Handle<v8::Value> function = e->initialProperties->Run(args->qmlGlobal());
1041 v8::Handle<v8::Value> args[] = { object, valuemap };
1042 v8::Handle<v8::Function>::Cast(function)->Call(v8engine->global(), 2, args);
1045 d->completeCreate();
1047 Q_ASSERT(QQmlData::get(rv));
1048 QQmlData::get(rv)->setImplicitDestructible();
1051 args->returnValue(v8::Null());
1053 args->returnValue(object);
1057 \qmlmethod object Component::incubateObject(Item parent, object properties, enum mode)
1059 Creates an incubator for instance of this component. Incubators allow new component
1060 instances to be instantiated asynchronously and not cause freezes in the UI.
1062 The \a parent argument specifies the parent the created instance will have. Omitting the
1063 parameter or passing null will create anobject with no parent. In this case, a reference
1064 to the created object must be maintained by the application of the object will eventually
1065 be garbage collected.
1067 The \a properties argument is specified as a map of property-value items which will be
1068 set on the created object during its construction. \a mode may be Qt.Synchronous or
1069 Qt.Asynchronous and controls whether the instance is created synchronously or asynchronously.
1070 The default is asynchronously. In some circumstances, even if Qt.Synchronous is specified,
1071 the incubator may create the object asynchronously. This happens if the component calling
1072 incubateObject() is itself being created asynchronously.
1074 All three arguments are optional.
1076 If successful, the method returns an incubator, otherwise null. The incubator has the following
1080 \i status The status of the incubator. Valid values are Component.Ready, Component.Loading and
1082 \i object The created object instance. Will only be available once the incubator is in the
1084 \i onStatusChanged Specifies a callback function to be invoked when the status changes. The
1085 status is passed as a parameter to the callback.
1086 \i forceCompletion() Call to complete incubation synchronously.
1089 The following example demonstrates how to use an incubator:
1092 var component = Qt.createComponent("Button.qml");
1094 var incubator = component.incubateObject(parent, { x: 10, y: 10 });
1095 if (incubator.status != Component.Ready) {
1096 incubator.onStatusChanged = function(status) {
1097 if (status == Component.Ready) {
1098 print ("Object", incubator.object, "is now ready!");
1102 print ("Object", incubator.object, "is ready immediately!");
1107 void QQmlComponent::incubateObject(QQmlV8Function *args)
1110 Q_ASSERT(d->engine);
1114 QObject *parent = 0;
1115 v8::Local<v8::Object> valuemap;
1116 QQmlIncubator::IncubationMode mode = QQmlIncubator::Asynchronous;
1118 if (args->Length() >= 1)
1119 parent = args->engine()->toQObject((*args)[0]);
1121 if (args->Length() >= 2) {
1122 v8::Local<v8::Value> v = (*args)[1];
1124 } else if (!v->IsObject() || v->IsArray()) {
1125 qmlInfo(this) << tr("createObject: value is not an object");
1126 args->returnValue(v8::Null());
1129 valuemap = v8::Local<v8::Object>::Cast(v);
1133 if (args->Length() >= 3) {
1134 quint32 v = (*args)[2]->Uint32Value();
1136 mode = QQmlIncubator::Asynchronous;
1138 mode = QQmlIncubator::AsynchronousIfNested;
1141 QQmlComponentExtension *e = componentExtension(args->engine());
1143 QV8IncubatorResource *r = new QV8IncubatorResource(args->engine(), mode);
1144 v8::Local<v8::Object> o = e->incubationConstructor->NewInstance();
1145 o->SetExternalResource(r);
1147 if (!valuemap.IsEmpty()) {
1148 r->valuemap = qPersistentNew(valuemap);
1149 r->qmlGlobal = qPersistentNew(args->qmlGlobal());
1152 r->me = qPersistentNew(o);
1154 create(*r, creationContext());
1156 if (r->status() == QQmlIncubator::Null) {
1158 args->returnValue(v8::Null());
1160 args->returnValue(o);
1164 // XXX used by QSGLoader
1165 void QQmlComponentPrivate::initializeObjectWithInitialProperties(v8::Handle<v8::Object> qmlGlobal, v8::Handle<v8::Object> valuemap, QObject *toCreate)
1167 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
1168 QV8Engine *v8engine = ep->v8engine();
1170 v8::HandleScope handle_scope;
1171 v8::Context::Scope scope(v8engine->context());
1172 v8::Handle<v8::Value> ov = v8engine->newQObject(toCreate);
1173 Q_ASSERT(ov->IsObject());
1174 v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(ov);
1176 if (!valuemap.IsEmpty()) {
1177 QQmlComponentExtension *e = componentExtension(v8engine);
1178 // Try catch isn't needed as the function itself is loaded with try/catch
1179 v8::Handle<v8::Value> function = e->initialProperties->Run(qmlGlobal);
1180 v8::Handle<v8::Value> args[] = { object, valuemap };
1181 v8::Handle<v8::Function>::Cast(function)->Call(v8engine->global(), 2, args);
1184 QQmlData *ddata = QQmlData::get(toCreate);
1186 ddata->setImplicitDestructible();
1190 QQmlComponentExtension::QQmlComponentExtension(QV8Engine *engine)
1192 v8::HandleScope handle_scope;
1193 v8::Context::Scope scope(engine->context());
1195 forceCompletion = qPersistentNew(V8FUNCTION(QV8IncubatorResource::ForceCompletion, engine));
1198 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
1199 ft->InstanceTemplate()->SetHasExternalResource(true);
1200 ft->InstanceTemplate()->SetInternalFieldCount(1);
1201 ft->InstanceTemplate()->SetAccessor(v8::String::New("onStatusChanged"),
1202 QV8IncubatorResource::StatusChangedGetter,
1203 QV8IncubatorResource::StatusChangedSetter);
1204 ft->InstanceTemplate()->SetAccessor(v8::String::New("status"),
1205 QV8IncubatorResource::StatusGetter);
1206 ft->InstanceTemplate()->SetAccessor(v8::String::New("object"),
1207 QV8IncubatorResource::ObjectGetter);
1208 ft->InstanceTemplate()->SetAccessor(v8::String::New("forceCompletion"),
1209 QV8IncubatorResource::ForceCompletionGetter);
1210 incubationConstructor = qPersistentNew(ft->GetFunction());
1214 #define INITIALPROPERTIES_SOURCE \
1215 "(function(object, values) {"\
1217 "for(var property in values) {" \
1219 "var properties = property.split(\".\");"\
1221 "for (var ii = 0; ii < properties.length - 1; ++ii) {"\
1222 "o = o[properties[ii]];"\
1224 "o[properties[properties.length - 1]] = values[property];"\
1229 initialProperties = qPersistentNew(engine->qmlModeCompile(QLatin1String(INITIALPROPERTIES_SOURCE)));
1230 #undef INITIALPROPERTIES_SOURCE
1234 v8::Handle<v8::Value> QV8IncubatorResource::ObjectGetter(v8::Local<v8::String>,
1235 const v8::AccessorInfo& info)
1237 QV8IncubatorResource *r = v8_resource_check<QV8IncubatorResource>(info.This());
1238 return r->engine->newQObject(r->object());
1241 v8::Handle<v8::Value> QV8IncubatorResource::ForceCompletionGetter(v8::Local<v8::String>,
1242 const v8::AccessorInfo& info)
1244 QV8IncubatorResource *r = v8_resource_check<QV8IncubatorResource>(info.This());
1245 return componentExtension(r->engine)->forceCompletion;
1248 v8::Handle<v8::Value> QV8IncubatorResource::ForceCompletion(const v8::Arguments &args)
1250 QV8IncubatorResource *r = v8_resource_cast<QV8IncubatorResource>(args.This());
1252 V8THROW_TYPE("Not an incubator object");
1254 r->forceCompletion();
1256 return v8::Undefined();
1259 v8::Handle<v8::Value> QV8IncubatorResource::StatusGetter(v8::Local<v8::String>,
1260 const v8::AccessorInfo& info)
1262 QV8IncubatorResource *r = v8_resource_check<QV8IncubatorResource>(info.This());
1263 return v8::Integer::NewFromUnsigned(r->status());
1266 v8::Handle<v8::Value> QV8IncubatorResource::StatusChangedGetter(v8::Local<v8::String>,
1267 const v8::AccessorInfo& info)
1269 return info.This()->GetInternalField(0);
1272 void QV8IncubatorResource::StatusChangedSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
1273 const v8::AccessorInfo& info)
1275 info.This()->SetInternalField(0, value);
1278 QQmlComponentExtension::~QQmlComponentExtension()
1280 qPersistentDispose(incubationConstructor);
1281 qPersistentDispose(initialProperties);
1282 qPersistentDispose(forceCompletion);
1285 QV8IncubatorResource::QV8IncubatorResource(QV8Engine *engine, IncubationMode m)
1286 : QV8ObjectResource(engine), QQmlIncubator(m)
1290 void QV8IncubatorResource::setInitialState(QObject *o)
1292 QQmlComponent_setQmlParent(o, parent);
1294 if (!valuemap.IsEmpty()) {
1295 QQmlComponentExtension *e = componentExtension(engine);
1297 v8::HandleScope handle_scope;
1298 v8::Context::Scope scope(engine->context());
1300 v8::Handle<v8::Value> function = e->initialProperties->Run(qmlGlobal);
1301 v8::Handle<v8::Value> args[] = { engine->newQObject(o), valuemap };
1302 v8::Handle<v8::Function>::Cast(function)->Call(engine->global(), 2, args);
1304 qPersistentDispose(valuemap);
1305 qPersistentDispose(qmlGlobal);
1309 void QV8IncubatorResource::dispose()
1311 qPersistentDispose(valuemap);
1312 qPersistentDispose(qmlGlobal);
1313 // No further status changes are forthcoming, so we no long need a self reference
1314 qPersistentDispose(me);
1317 void QV8IncubatorResource::statusChanged(Status s)
1320 Q_ASSERT(QQmlData::get(object()));
1321 QQmlData::get(object())->setImplicitDestructible();
1324 if (!me.IsEmpty()) { // Will be false in synchronous mode
1325 v8::HandleScope scope;
1326 v8::Local<v8::Value> callback = me->GetInternalField(0);
1328 if (!callback.IsEmpty() && !callback->IsUndefined()) {
1330 if (callback->IsFunction()) {
1331 v8::Context::Scope context_scope(engine->context());
1332 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(callback);
1333 v8::Handle<v8::Value> args[] = { v8::Integer::NewFromUnsigned(s) };
1335 f->Call(me, 1, args);
1336 if (tc.HasCaught()) {
1338 QQmlExpressionPrivate::exceptionToError(tc.Message(), error);
1339 QQmlEnginePrivate::warning(QQmlEnginePrivate::get(engine->engine()),
1346 if (s == Ready || s == Error)