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>
68 #include "qqmlmemoryprofiler_p.h"
72 class QQmlComponentExtension : public QV8Engine::Deletable
75 QQmlComponentExtension(QV8Engine *);
76 virtual ~QQmlComponentExtension();
78 v8::Persistent<v8::Function> incubationConstructor;
79 v8::Persistent<v8::Script> initialProperties;
80 v8::Persistent<v8::Function> forceCompletion;
82 V8_DEFINE_EXTENSION(QQmlComponentExtension, componentExtension);
85 Try to do what's necessary for a reasonable display of the type
86 name, but no more (just enough for the client to do more extensive cleanup).
88 Should only be called when debugging is enabled.
90 static inline QString buildTypeNameForDebug(const QMetaObject *metaObject)
92 static const QString qmlMarker(QLatin1String("_QML"));
93 static const QChar underscore(QLatin1Char('_'));
94 static const QChar asterisk(QLatin1Char('*'));
95 QQmlType *type = QQmlMetaType::qmlType(metaObject);
96 QString typeName = type ? type->qmlTypeName() : QString::fromUtf8(metaObject->className());
98 //### optimize further?
99 int marker = typeName.indexOf(qmlMarker);
100 if (marker != -1 && marker < typeName.count() - 1) {
101 if (typeName[marker + 1] == underscore) {
102 const QString className = typeName.left(marker) + asterisk;
103 type = QQmlMetaType::qmlType(QMetaType::type(className.toUtf8()));
105 typeName = type->qmlTypeName();
118 \brief The QQmlComponent class encapsulates a QML component definition
120 Components are reusable, encapsulated QML elements with well-defined interfaces.
122 A QQmlComponent instance can be created from a QML file.
123 For example, if there is a \c main.qml file like this:
134 The following code loads this QML file as a component, creates an instance of
135 this component using create(), and then queries the \l Item's \l {Item::}{width}
139 QQmlEngine *engine = new QQmlEngine;
140 QQmlComponent component(engine, QUrl::fromLocalFile("main.qml"));
142 QObject *myObject = component.create();
143 QQuickItem *item = qobject_cast<QQuickItem*>(myObject);
144 int width = item->width(); // width = 200
148 \section2 Network Components
150 If the URL passed to QQmlComponent is a network resource, or if the QML document references a
151 network resource, the QQmlComponent has to fetch the network data before it is able to create
152 objects. In this case, the QQmlComponent will have a \l {QQmlComponent::Loading}{Loading}
153 \l {QQmlComponent::status()}{status}. An application will have to wait until the component
154 is \l {QQmlComponent::Ready}{Ready} before calling \l {QQmlComponent::create()}.
156 The following example shows how to load a QML file from a network resource. After creating
157 the QQmlComponent, it tests whether the component is loading. If it is, it connects to the
158 QQmlComponent::statusChanged() signal and otherwise calls the \c {continueLoading()} method
159 directly. Note that QQmlComponent::isLoading() may be false for a network component if the
160 component has been cached and is ready immediately.
163 MyApplication::MyApplication()
166 component = new QQmlComponent(engine, QUrl("http://www.example.com/main.qml"));
167 if (component->isLoading())
168 QObject::connect(component, SIGNAL(statusChanged(QQmlComponent::Status)),
169 this, SLOT(continueLoading()));
174 void MyApplication::continueLoading()
176 if (component->isError()) {
177 qWarning() << component->errors();
179 QObject *myObject = component->create();
184 Note that the QtQuick 1 version is named QDeclarativeComponent.
188 \qmlclass Component QQmlComponent
189 \ingroup qml-utility-elements
191 \inqmlmodule QtQuick 2
192 \brief Encapsulates a QML component definition
194 Components are reusable, encapsulated QML elements with well-defined interfaces.
196 Components are often defined by \l {{QML Documents}}{component files} -
197 that is, \c .qml files. The \e Component element essentially allows QML components
198 to be defined inline, within a \l {QML Document}{QML document}, rather than as a separate QML file.
199 This may be useful for reusing a small component within a QML file, or for defining
200 a component that logically belongs with other QML components within a file.
202 For example, here is a component that is used by multiple \l Loader objects.
203 It contains a single item, a \l Rectangle:
205 \snippet qml/component.qml 0
207 Notice that while a \l Rectangle by itself would be automatically
208 rendered and displayed, this is not the case for the above rectangle
209 because it is defined inside a \c Component. The component encapsulates the
210 QML elements within, as if they were defined in a separate QML
211 file, and is not loaded until requested (in this case, by the
212 two \l Loader objects).
214 Defining a \c Component is similar to defining a \l {QML Document}{QML document}.
215 A QML document has a single top-level item that defines the behaviors and
216 properties of that component, and cannot define properties or behaviors outside
217 of that top-level item. In the same way, a \c Component definition contains a single
218 top level item (which in the above example is a \l Rectangle) and cannot define any
219 data outside of this item, with the exception of an \e id (which in the above example
222 The \c Component element is commonly used to provide graphical components
223 for views. For example, the ListView::delegate property requires a \c Component
224 to specify how each list item is to be displayed.
226 \c Component objects can also be created dynamically using
227 \l{QML:Qt::createComponent()}{Qt.createComponent()}.
229 \section2 Creation Context
231 The creation context of a Component corresponds to the context where the Component was declared.
232 This context is used as the parent context (creating a \l{qtqml-documents-scope.html#component-instance-hierarchy}{context hierarchy})
233 when the component is instantiated by an object such as a ListView or a Loader.
235 In the following example, \c comp1 is created within the root context of MyItem.qml, and any objects
236 instantiated from this component will have access to the ids and properties within that context,
237 such as \c internalSettings.color. When \c comp1 is used as a ListView delegate in another context
238 (as in main.qml below), it will continue to have access to the properties of its creation context
239 (which would otherwise be private to external users).
246 \li \snippet qml/component/MyItem.qml 0
247 \li \snippet qml/component/main.qml 0
252 \qmlattachedsignal Component::onCompleted()
254 Emitted after component "startup" has completed. This can be used to
255 execute script code at startup, once the full QML environment has been
258 The \c {Component::onCompleted} attached property can be applied to
259 any element. The order of running the \c onCompleted scripts is
264 Component.onCompleted: console.log("Completed Running!")
266 Component.onCompleted: console.log("Nested Completed Running!")
273 \qmlattachedsignal Component::onDestruction()
275 Emitted as the component begins destruction. This can be used to undo
276 work done in the onCompleted signal, or other imperative code in your
279 The \c {Component::onDestruction} attached property can be applied to
280 any element. However, it applies to the destruction of the component as
281 a whole, and not the destruction of the specific object. The order of
282 running the \c onDestruction scripts is undefined.
286 Component.onDestruction: console.log("Destruction Beginning!")
288 Component.onDestruction: console.log("Nested Destruction Beginning!")
297 \enum QQmlComponent::Status
299 Specifies the loading status of the QQmlComponent.
301 \value Null This QQmlComponent has no data. Call loadUrl() or setData() to add QML content.
302 \value Ready This QQmlComponent is ready and create() may be called.
303 \value Loading This QQmlComponent is loading network data.
304 \value Error An error has occurred. Call errors() to retrieve a list of \{QQmlError}{errors}.
308 \enum QQmlComponent::CompilationMode
310 Specifies whether the QQmlComponent should load the component immediately, or asynchonously.
312 \value PreferSynchronous Prefer loading/compiling the component immediately, blocking the thread.
313 This is not always possible, e.g. remote URLs will always load asynchronously.
314 \value Asynchronous Load/compile the component in a background thread.
317 void QQmlComponentPrivate::typeDataReady(QQmlTypeData *)
323 fromTypeData(typeData);
327 emit q->statusChanged(q->status());
328 emit q->progressChanged(progress);
331 void QQmlComponentPrivate::typeDataProgress(QQmlTypeData *, qreal p)
337 emit q->progressChanged(p);
340 void QQmlComponentPrivate::fromTypeData(QQmlTypeData *data)
342 url = data->finalUrl();
343 QQmlCompiledData *c = data->compiledData();
346 Q_ASSERT(data->isError());
347 state.errors = data->errors();
356 void QQmlComponentPrivate::clear()
359 typeData->unregisterCallback(this);
373 QQmlComponent::QQmlComponent(QObject *parent)
374 : QObject(*(new QQmlComponentPrivate), parent)
379 Destruct the QQmlComponent.
381 QQmlComponent::~QQmlComponent()
385 if (d->state.completePending) {
386 qWarning("QQmlComponent: Component destroyed while completion pending");
391 d->typeData->unregisterCallback(d);
392 d->typeData->release();
399 \qmlproperty enumeration Component::status
400 This property holds the status of component loading. It can be one of:
402 \li Component.Null - no data is available for the component
403 \li Component.Ready - the component has been loaded, and can be used to create instances.
404 \li Component.Loading - the component is currently being loaded
405 \li Component.Error - an error occurred while loading the component.
406 Calling errorString() will provide a human-readable description of any errors.
411 \property QQmlComponent::status
412 The component's current \l{QQmlComponent::Status} {status}.
414 QQmlComponent::Status QQmlComponent::status() const
416 Q_D(const QQmlComponent);
420 else if (!d->state.errors.isEmpty())
422 else if (d->engine && d->cc)
429 Returns true if status() == QQmlComponent::Null.
431 bool QQmlComponent::isNull() const
433 return status() == Null;
437 Returns true if status() == QQmlComponent::Ready.
439 bool QQmlComponent::isReady() const
441 return status() == Ready;
445 Returns true if status() == QQmlComponent::Error.
447 bool QQmlComponent::isError() const
449 return status() == Error;
453 Returns true if status() == QQmlComponent::Loading.
455 bool QQmlComponent::isLoading() const
457 return status() == Loading;
461 \qmlproperty real Component::progress
462 The progress of loading the component, from 0.0 (nothing loaded)
467 \property QQmlComponent::progress
468 The progress of loading the component, from 0.0 (nothing loaded)
471 qreal QQmlComponent::progress() const
473 Q_D(const QQmlComponent);
478 \fn void QQmlComponent::progressChanged(qreal progress)
480 Emitted whenever the component's loading progress changes. \a progress will be the
481 current progress between 0.0 (nothing loaded) and 1.0 (finished).
485 \fn void QQmlComponent::statusChanged(QQmlComponent::Status status)
487 Emitted whenever the component's status changes. \a status will be the
492 Create a QQmlComponent with no data and give it the specified
493 \a engine and \a parent. Set the data with setData().
495 QQmlComponent::QQmlComponent(QQmlEngine *engine, QObject *parent)
496 : QObject(*(new QQmlComponentPrivate), parent)
503 Create a QQmlComponent from the given \a url and give it the
504 specified \a parent and \a engine.
506 Ensure that the URL provided is full and correct, in particular, use
507 \l QUrl::fromLocalFile() when loading a file from the local filesystem.
511 QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, QObject *parent)
512 : QObject(*(new QQmlComponentPrivate), parent)
520 Create a QQmlComponent from the given \a url and give it the
521 specified \a parent and \a engine. If \a mode is \l Asynchronous,
522 the component will be loaded and compiled asynchronously.
524 Ensure that the URL provided is full and correct, in particular, use
525 \l QUrl::fromLocalFile() when loading a file from the local filesystem.
529 QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, CompilationMode mode,
531 : QObject(*(new QQmlComponentPrivate), parent)
535 d->loadUrl(url, mode);
539 Create a QQmlComponent from the given \a fileName and give it the specified
540 \a parent and \a engine.
544 QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName,
546 : QObject(*(new QQmlComponentPrivate), parent)
550 d->loadUrl(d->engine->baseUrl().resolved(QUrl::fromLocalFile(fileName)));
554 Create a QQmlComponent from the given \a fileName and give it the specified
555 \a parent and \a engine. If \a mode is \l Asynchronous,
556 the component will be loaded and compiled asynchronously.
560 QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName,
561 CompilationMode mode, QObject *parent)
562 : QObject(*(new QQmlComponentPrivate), parent)
566 d->loadUrl(d->engine->baseUrl().resolved(QUrl::fromLocalFile(fileName)), mode);
572 QQmlComponent::QQmlComponent(QQmlEngine *engine, QQmlCompiledData *cc, int start, QObject *parent)
573 : QObject(*(new QQmlComponentPrivate), parent)
585 Sets the QQmlComponent to use the given QML \a data. If \a url
586 is provided, it is used to set the component name and to provide
587 a base path for items resolved by this component.
589 void QQmlComponent::setData(const QByteArray &data, const QUrl &url)
597 QQmlTypeData *typeData = QQmlEnginePrivate::get(d->engine)->typeLoader.getType(data, url);
599 if (typeData->isCompleteOrError()) {
600 d->fromTypeData(typeData);
602 d->typeData = typeData;
603 d->typeData->registerCallback(d);
607 emit statusChanged(status());
608 emit progressChanged(d->progress);
612 Returns the QQmlContext the component was created in. This is only
613 valid for components created directly from QML.
615 QQmlContext *QQmlComponent::creationContext() const
617 Q_D(const QQmlComponent);
618 if(d->creationContext)
619 return d->creationContext->asQQmlContext();
621 return qmlContext(this);
625 Load the QQmlComponent from the provided \a url.
627 Ensure that the URL provided is full and correct, in particular, use
628 \l QUrl::fromLocalFile() when loading a file from the local filesystem.
630 void QQmlComponent::loadUrl(const QUrl &url)
637 Load the QQmlComponent from the provided \a url.
638 If \a mode is \l Asynchronous, the component will be loaded and compiled asynchronously.
640 Ensure that the URL provided is full and correct, in particular, use
641 \l QUrl::fromLocalFile() when loading a file from the local filesystem.
643 void QQmlComponent::loadUrl(const QUrl &url, QQmlComponent::CompilationMode mode)
646 d->loadUrl(url, mode);
649 void QQmlComponentPrivate::loadUrl(const QUrl &newUrl, QQmlComponent::CompilationMode mode)
654 if ((newUrl.isRelative() && !newUrl.isEmpty())
655 || newUrl.scheme() == QLatin1String("file")) // Workaround QTBUG-11929
656 url = engine->baseUrl().resolved(newUrl);
660 if (newUrl.isEmpty()) {
662 error.setDescription(q->tr("Invalid empty URL"));
663 state.errors << error;
667 if (progress != 0.0) {
669 emit q->progressChanged(progress);
672 QQmlDataLoader::Mode loaderMode = (mode == QQmlComponent::Asynchronous)
673 ? QQmlDataLoader::Asynchronous
674 : QQmlDataLoader::PreferSynchronous;
676 QQmlTypeData *data = QQmlEnginePrivate::get(engine)->typeLoader.getType(url, loaderMode);
678 if (data->isCompleteOrError()) {
683 typeData->registerCallback(this);
684 progress = data->progress();
687 emit q->statusChanged(q->status());
689 emit q->progressChanged(progress);
693 Return the list of errors that occurred during the last compile or create
694 operation. An empty list is returned if isError() is not set.
696 QList<QQmlError> QQmlComponent::errors() const
698 Q_D(const QQmlComponent);
700 return d->state.errors;
702 return QList<QQmlError>();
706 \qmlmethod string Component::errorString()
708 Returns a human-readable description of any errors.
710 The string includes the file, location, and description of each error.
711 If multiple errors are present they are separated by a newline character.
713 If no errors are present, an empty string is returned.
718 errorString is only meant as a way to get the errors in script
720 QString QQmlComponent::errorString() const
722 Q_D(const QQmlComponent);
726 foreach(const QQmlError &e, d->state.errors) {
727 ret += e.url().toString() + QLatin1Char(':') +
728 QString::number(e.line()) + QLatin1Char(' ') +
729 e.description() + QLatin1Char('\n');
735 \qmlproperty url Component::url
736 The component URL. This is the URL that was used to construct the component.
740 \property QQmlComponent::url
741 The component URL. This is the URL passed to either the constructor,
742 or the loadUrl() or setData() methods.
744 QUrl QQmlComponent::url() const
746 Q_D(const QQmlComponent);
753 QQmlComponent::QQmlComponent(QQmlComponentPrivate &dd, QObject *parent)
754 : QObject(dd, parent)
759 Create an object instance from this component. Returns 0 if creation
760 failed. \a context specifies the context within which to create the object
763 If \a context is 0 (the default), it will create the instance in the
764 engine' s \l {QQmlEngine::rootContext()}{root context}.
766 QObject *QQmlComponent::create(QQmlContext *context)
769 QML_MEMORY_SCOPE_URL(url());
772 context = d->engine->rootContext();
774 QObject *rv = beginCreate(context);
780 This method provides more advanced control over component instance creation.
781 In general, programmers should use QQmlComponent::create() to create a
784 Create an object instance from this component. Returns 0 if creation
785 failed. \a publicContext specifies the context within which to create the object
788 When QQmlComponent constructs an instance, it occurs in three steps:
790 \li The object hierarchy is created, and constant values are assigned.
791 \li Property bindings are evaluated for the the first time.
792 \li If applicable, QQmlParserStatus::componentComplete() is called on objects.
794 QQmlComponent::beginCreate() differs from QQmlComponent::create() in that it
795 only performs step 1. QQmlComponent::completeCreate() must be called to
796 complete steps 2 and 3.
798 This breaking point is sometimes useful when using attached properties to
799 communicate information to an instantiated component, as it allows their
800 initial values to be configured before property bindings take effect.
802 QObject *QQmlComponent::beginCreate(QQmlContext *publicContext)
806 Q_ASSERT(publicContext);
807 QQmlContextData *context = QQmlContextData::get(publicContext);
809 return d->beginCreate(context);
813 QQmlComponentPrivate::beginCreate(QQmlContextData *context)
817 qWarning("QQmlComponent: Cannot create a component in a null context");
821 if (!context->isValid()) {
822 qWarning("QQmlComponent: Cannot create a component in an invalid context");
826 if (context->engine != engine) {
827 qWarning("QQmlComponent: Must create component in context from the same QQmlEngine");
831 if (state.completePending) {
832 qWarning("QQmlComponent: Cannot create new component instance before completing the previous");
837 qWarning("QQmlComponent: Component is not ready");
841 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine);
843 if (enginePriv->inProgressCreations == 0) {
844 // only track root, since further ones might not be properly nested
845 profiler = new QQmlObjectCreatingProfiler();
848 enginePriv->inProgressCreations++;
849 state.errors.clear();
850 state.completePending = true;
852 enginePriv->referenceScarceResources();
853 state.vme.init(context, cc, start, creationContext);
854 QObject *rv = state.vme.execute(&state.errors);
855 enginePriv->dereferenceScarceResources();
858 QQmlData *ddata = QQmlData::get(rv);
860 //top level objects should never get JS ownership.
861 //if JS ownership is needed this needs to be explicitly undone (like in component.createObject())
862 ddata->indestructible = true;
863 ddata->explicitIndestructibleSet = true;
864 ddata->rootObjectInCreation = false;
867 if (enginePriv->isDebugging && rv) {
868 if (!context->isInternal)
869 context->asQQmlContextPrivate()->instances.append(rv);
870 QQmlEngineDebugService::instance()->objectCreated(engine, rv);
872 if (profiler && profiler->enabled) {
873 profiler->setTypeName(buildTypeNameForDebug(rv->metaObject()));
874 QQmlData *data = QQmlData::get(rv);
876 profiler->setLocation(cc->url, data->lineNumber, data->columnNumber);
883 void QQmlComponentPrivate::beginDeferred(QQmlEnginePrivate *enginePriv,
884 QObject *object, ConstructionState *state)
886 enginePriv->inProgressCreations++;
887 state->errors.clear();
888 state->completePending = true;
890 state->vme.initDeferred(object);
891 state->vme.execute(&state->errors);
894 void QQmlComponentPrivate::complete(QQmlEnginePrivate *enginePriv, ConstructionState *state)
896 if (state->completePending) {
897 state->vme.complete();
899 state->completePending = false;
901 enginePriv->inProgressCreations--;
903 if (0 == enginePriv->inProgressCreations) {
904 while (enginePriv->erroredBindings) {
905 enginePriv->warning(enginePriv->erroredBindings);
906 enginePriv->erroredBindings->removeError();
913 This method provides more advanced control over component instance creation.
914 In general, programmers should use QQmlComponent::create() to create a
917 Complete a component creation begin with QQmlComponent::beginCreate().
919 void QQmlComponent::completeCreate()
926 void QQmlComponentPrivate::completeCreate()
928 if (state.completePending) {
929 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
930 complete(ep, &state);
937 QQmlComponentAttached::QQmlComponentAttached(QObject *parent)
938 : QObject(parent), prev(0), next(0)
942 QQmlComponentAttached::~QQmlComponentAttached()
944 if (prev) *prev = next;
945 if (next) next->prev = prev;
953 QQmlComponentAttached *QQmlComponent::qmlAttachedProperties(QObject *obj)
955 QQmlComponentAttached *a = new QQmlComponentAttached(obj);
957 QQmlEngine *engine = qmlEngine(obj);
961 if (QQmlEnginePrivate::get(engine)->activeVME) { // XXX should only be allowed during begin
962 QQmlEnginePrivate *p = QQmlEnginePrivate::get(engine);
963 a->add(&p->activeVME->componentAttached);
965 QQmlData *d = QQmlData::get(obj);
967 Q_ASSERT(d->context);
968 a->add(&d->context->componentAttached);
975 Create an object instance from this component using the provided
976 \a incubator. \a context specifies the context within which to create the object
979 If \a context is 0 (the default), it will create the instance in the
980 engine's \l {QQmlEngine::rootContext()}{root context}.
982 \a forContext specifies a context that this object creation depends upon.
983 If the \a forContext is being created asynchronously, and the
984 \l QQmlIncubator::IncubationMode is \l QQmlIncubator::AsynchronousIfNested,
985 this object will also be created asynchronously. If \a forContext is 0
986 (the default), the \a context will be used for this decision.
988 The created object and its creation status are available via the
994 void QQmlComponent::create(QQmlIncubator &incubator, QQmlContext *context,
995 QQmlContext *forContext)
1000 context = d->engine->rootContext();
1002 QQmlContextData *contextData = QQmlContextData::get(context);
1003 QQmlContextData *forContextData = contextData;
1004 if (forContext) forContextData = QQmlContextData::get(forContext);
1006 if (!contextData->isValid()) {
1007 qWarning("QQmlComponent: Cannot create a component in an invalid context");
1011 if (contextData->engine != d->engine) {
1012 qWarning("QQmlComponent: Must create component in context from the same QQmlEngine");
1017 qWarning("QQmlComponent: Component is not ready");
1022 QQmlIncubatorPrivate *p = incubator.d;
1024 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(d->engine);
1026 p->compiledData = d->cc;
1027 p->compiledData->addref();
1028 p->vme.init(contextData, d->cc, d->start, d->creationContext);
1030 enginePriv->incubate(incubator, forContextData);
1033 class QV8IncubatorResource : public QV8ObjectResource,
1034 public QQmlIncubator
1036 V8_RESOURCE_TYPE(IncubatorType)
1038 QV8IncubatorResource(QV8Engine *engine, IncubationMode = Asynchronous);
1040 static v8::Handle<v8::Value> StatusChangedGetter(v8::Local<v8::String>,
1041 const v8::AccessorInfo& info);
1042 static v8::Handle<v8::Value> StatusGetter(v8::Local<v8::String>,
1043 const v8::AccessorInfo& info);
1044 static v8::Handle<v8::Value> ObjectGetter(v8::Local<v8::String>,
1045 const v8::AccessorInfo& info);
1046 static v8::Handle<v8::Value> ForceCompletionGetter(v8::Local<v8::String>,
1047 const v8::AccessorInfo& info);
1048 static v8::Handle<v8::Value> ForceCompletion(const v8::Arguments &args);
1050 static void StatusChangedSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
1051 const v8::AccessorInfo& info);
1055 v8::Persistent<v8::Object> me;
1056 QQmlGuard<QObject> parent;
1057 v8::Persistent<v8::Value> valuemap;
1058 v8::Persistent<v8::Object> qmlGlobal;
1060 virtual void statusChanged(Status);
1061 virtual void setInitialState(QObject *);
1064 static void QQmlComponent_setQmlParent(QObject *me, QObject *parent)
1067 me->setParent(parent);
1068 typedef QQmlPrivate::AutoParentFunction APF;
1069 QList<APF> functions = QQmlMetaType::parentFunctions();
1071 bool needParent = false;
1072 for (int ii = 0; ii < functions.count(); ++ii) {
1073 QQmlPrivate::AutoParentResult res = functions.at(ii)(me, parent);
1074 if (res == QQmlPrivate::Parented) {
1077 } else if (res == QQmlPrivate::IncompatibleParent) {
1082 qWarning("QQmlComponent: Created graphical object was not "
1083 "placed in the graphics scene.");
1088 \qmlmethod object Component::createObject(Item parent, object properties)
1090 Creates and returns an object instance of this component that will have
1091 the given \a parent and \a properties. The \a properties argument is optional.
1092 Returns null if object creation fails.
1094 The object will be created in the same context as the one in which the component
1095 was created. This function will always return null when called on components
1096 which were not created in QML.
1098 If you wish to create an object without setting a parent, specify \c null for
1099 the \a parent value. Note that if the returned object is to be displayed, you
1100 must provide a valid \a parent value or set the returned object's \l{Item::parent}{parent}
1101 property, or else the object will not be visible.
1103 If a \a parent is not provided to createObject(), a reference to the returned object must be held so that
1104 it is not destroyed by the garbage collector. This is true regardless of whether \l{Item::parent} is set afterwards,
1105 since setting the Item parent does not change object ownership; only the graphical parent is changed.
1107 As of QtQuick 1.1, this method accepts an optional \a properties argument that specifies a
1108 map of initial property values for the created object. These values are applied before object
1109 creation is finalized. (This is more efficient than setting property values after object creation,
1110 particularly where large sets of property values are defined, and also allows property bindings
1111 to be set up before the object is created.)
1113 The \a properties argument is specified as a map of property-value items. For example, the code
1114 below creates an object with initial \c x and \c y values of 100 and 200, respectively:
1117 var component = Qt.createComponent("Button.qml");
1118 if (component.status == Component.Ready)
1119 component.createObject(parent, {"x": 100, "y": 100});
1122 Dynamically created instances can be deleted with the \c destroy() method.
1123 See \l {Dynamic QML Object Creation from JavaScript} for more information.
1125 \sa incubateObject()
1131 void QQmlComponent::createObject(QQmlV8Function *args)
1134 Q_ASSERT(d->engine);
1137 QObject *parent = 0;
1138 v8::Local<v8::Object> valuemap;
1140 if (args->Length() >= 1)
1141 parent = args->engine()->toQObject((*args)[0]);
1143 if (args->Length() >= 2) {
1144 v8::Local<v8::Value> v = (*args)[1];
1145 if (!v->IsObject() || v->IsArray()) {
1146 qmlInfo(this) << tr("createObject: value is not an object");
1147 args->returnValue(v8::Null());
1150 valuemap = v8::Local<v8::Object>::Cast(v);
1153 QV8Engine *v8engine = args->engine();
1155 QQmlContext *ctxt = creationContext();
1156 if (!ctxt) ctxt = d->engine->rootContext();
1158 QObject *rv = beginCreate(ctxt);
1161 args->returnValue(v8::Null());
1165 QQmlComponent_setQmlParent(rv, parent);
1167 v8::Handle<v8::Value> ov = v8engine->newQObject(rv);
1168 Q_ASSERT(ov->IsObject());
1169 v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(ov);
1171 if (!valuemap.IsEmpty()) {
1172 QQmlComponentExtension *e = componentExtension(v8engine);
1173 // Try catch isn't needed as the function itself is loaded with try/catch
1174 v8::Handle<v8::Value> function = e->initialProperties->Run(args->qmlGlobal());
1175 v8::Handle<v8::Value> args[] = { object, valuemap };
1176 v8::Handle<v8::Function>::Cast(function)->Call(v8engine->global(), 2, args);
1179 d->completeCreate();
1181 Q_ASSERT(QQmlData::get(rv));
1182 QQmlData::get(rv)->explicitIndestructibleSet = false;
1183 QQmlData::get(rv)->indestructible = false;
1186 args->returnValue(v8::Null());
1188 args->returnValue(object);
1192 \qmlmethod object Component::incubateObject(Item parent, object properties, enumeration mode)
1194 Creates an incubator for instance of this component. Incubators allow new component
1195 instances to be instantiated asynchronously and not cause freezes in the UI.
1197 The \a parent argument specifies the parent the created instance will have. Omitting the
1198 parameter or passing null will create anobject with no parent. In this case, a reference
1199 to the created object must be maintained by the application of the object will eventually
1200 be garbage collected.
1202 The \a properties argument is specified as a map of property-value items which will be
1203 set on the created object during its construction. \a mode may be Qt.Synchronous or
1204 Qt.Asynchronous and controls whether the instance is created synchronously or asynchronously.
1205 The default is asynchronously. In some circumstances, even if Qt.Synchronous is specified,
1206 the incubator may create the object asynchronously. This happens if the component calling
1207 incubateObject() is itself being created asynchronously.
1209 All three arguments are optional.
1211 If successful, the method returns an incubator, otherwise null. The incubator has the following
1215 \li status The status of the incubator. Valid values are Component.Ready, Component.Loading and
1217 \li object The created object instance. Will only be available once the incubator is in the
1219 \li onStatusChanged Specifies a callback function to be invoked when the status changes. The
1220 status is passed as a parameter to the callback.
1221 \li forceCompletion() Call to complete incubation synchronously.
1224 The following example demonstrates how to use an incubator:
1227 var component = Qt.createComponent("Button.qml");
1229 var incubator = component.incubateObject(parent, { x: 10, y: 10 });
1230 if (incubator.status != Component.Ready) {
1231 incubator.onStatusChanged = function(status) {
1232 if (status == Component.Ready) {
1233 print ("Object", incubator.object, "is now ready!");
1237 print ("Object", incubator.object, "is ready immediately!");
1241 Dynamically created instances can be deleted with the \c destroy() method.
1242 See \l {Dynamic QML Object Creation from JavaScript} for more information.
1250 void QQmlComponent::incubateObject(QQmlV8Function *args)
1253 Q_ASSERT(d->engine);
1257 QObject *parent = 0;
1258 v8::Local<v8::Object> valuemap;
1259 QQmlIncubator::IncubationMode mode = QQmlIncubator::Asynchronous;
1261 if (args->Length() >= 1)
1262 parent = args->engine()->toQObject((*args)[0]);
1264 if (args->Length() >= 2) {
1265 v8::Local<v8::Value> v = (*args)[1];
1267 } else if (!v->IsObject() || v->IsArray()) {
1268 qmlInfo(this) << tr("createObject: value is not an object");
1269 args->returnValue(v8::Null());
1272 valuemap = v8::Local<v8::Object>::Cast(v);
1276 if (args->Length() >= 3) {
1277 quint32 v = (*args)[2]->Uint32Value();
1279 mode = QQmlIncubator::Asynchronous;
1281 mode = QQmlIncubator::AsynchronousIfNested;
1284 QQmlComponentExtension *e = componentExtension(args->engine());
1286 QV8IncubatorResource *r = new QV8IncubatorResource(args->engine(), mode);
1287 v8::Local<v8::Object> o = e->incubationConstructor->NewInstance();
1288 o->SetExternalResource(r);
1290 if (!valuemap.IsEmpty()) {
1291 r->valuemap = qPersistentNew(valuemap);
1292 r->qmlGlobal = qPersistentNew(args->qmlGlobal());
1295 r->me = qPersistentNew(o);
1297 create(*r, creationContext());
1299 if (r->status() == QQmlIncubator::Null) {
1301 args->returnValue(v8::Null());
1303 args->returnValue(o);
1307 // XXX used by QSGLoader
1308 void QQmlComponentPrivate::initializeObjectWithInitialProperties(v8::Handle<v8::Object> qmlGlobal, v8::Handle<v8::Object> valuemap, QObject *toCreate)
1310 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
1311 QV8Engine *v8engine = ep->v8engine();
1313 v8::HandleScope handle_scope;
1314 v8::Context::Scope scope(v8engine->context());
1315 v8::Handle<v8::Value> ov = v8engine->newQObject(toCreate);
1316 Q_ASSERT(ov->IsObject());
1317 v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(ov);
1319 if (!valuemap.IsEmpty()) {
1320 QQmlComponentExtension *e = componentExtension(v8engine);
1321 // Try catch isn't needed as the function itself is loaded with try/catch
1322 v8::Handle<v8::Value> function = e->initialProperties->Run(qmlGlobal);
1323 v8::Handle<v8::Value> args[] = { object, valuemap };
1324 v8::Handle<v8::Function>::Cast(function)->Call(v8engine->global(), 2, args);
1329 QQmlComponentExtension::QQmlComponentExtension(QV8Engine *engine)
1331 v8::HandleScope handle_scope;
1332 v8::Context::Scope scope(engine->context());
1334 forceCompletion = qPersistentNew(V8FUNCTION(QV8IncubatorResource::ForceCompletion, engine));
1337 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
1338 ft->InstanceTemplate()->SetHasExternalResource(true);
1339 ft->InstanceTemplate()->SetInternalFieldCount(1);
1340 ft->InstanceTemplate()->SetAccessor(v8::String::New("onStatusChanged"),
1341 QV8IncubatorResource::StatusChangedGetter,
1342 QV8IncubatorResource::StatusChangedSetter);
1343 ft->InstanceTemplate()->SetAccessor(v8::String::New("status"),
1344 QV8IncubatorResource::StatusGetter);
1345 ft->InstanceTemplate()->SetAccessor(v8::String::New("object"),
1346 QV8IncubatorResource::ObjectGetter);
1347 ft->InstanceTemplate()->SetAccessor(v8::String::New("forceCompletion"),
1348 QV8IncubatorResource::ForceCompletionGetter);
1349 incubationConstructor = qPersistentNew(ft->GetFunction());
1353 #define INITIALPROPERTIES_SOURCE \
1354 "(function(object, values) {"\
1356 "for(var property in values) {" \
1358 "var properties = property.split(\".\");"\
1360 "for (var ii = 0; ii < properties.length - 1; ++ii) {"\
1361 "o = o[properties[ii]];"\
1363 "o[properties[properties.length - 1]] = values[property];"\
1368 initialProperties = qPersistentNew(engine->qmlModeCompile(QLatin1String(INITIALPROPERTIES_SOURCE)));
1369 #undef INITIALPROPERTIES_SOURCE
1373 v8::Handle<v8::Value> QV8IncubatorResource::ObjectGetter(v8::Local<v8::String>,
1374 const v8::AccessorInfo& info)
1376 QV8IncubatorResource *r = v8_resource_check<QV8IncubatorResource>(info.This());
1377 return r->engine->newQObject(r->object());
1380 v8::Handle<v8::Value> QV8IncubatorResource::ForceCompletionGetter(v8::Local<v8::String>,
1381 const v8::AccessorInfo& info)
1383 QV8IncubatorResource *r = v8_resource_check<QV8IncubatorResource>(info.This());
1384 return componentExtension(r->engine)->forceCompletion;
1387 v8::Handle<v8::Value> QV8IncubatorResource::ForceCompletion(const v8::Arguments &args)
1389 QV8IncubatorResource *r = v8_resource_cast<QV8IncubatorResource>(args.This());
1391 V8THROW_TYPE("Not an incubator object");
1393 r->forceCompletion();
1395 return v8::Undefined();
1398 v8::Handle<v8::Value> QV8IncubatorResource::StatusGetter(v8::Local<v8::String>,
1399 const v8::AccessorInfo& info)
1401 QV8IncubatorResource *r = v8_resource_check<QV8IncubatorResource>(info.This());
1402 return v8::Integer::NewFromUnsigned(r->status());
1405 v8::Handle<v8::Value> QV8IncubatorResource::StatusChangedGetter(v8::Local<v8::String>,
1406 const v8::AccessorInfo& info)
1408 return info.This()->GetInternalField(0);
1411 void QV8IncubatorResource::StatusChangedSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
1412 const v8::AccessorInfo& info)
1414 info.This()->SetInternalField(0, value);
1417 QQmlComponentExtension::~QQmlComponentExtension()
1419 qPersistentDispose(incubationConstructor);
1420 qPersistentDispose(initialProperties);
1421 qPersistentDispose(forceCompletion);
1424 QV8IncubatorResource::QV8IncubatorResource(QV8Engine *engine, IncubationMode m)
1425 : QV8ObjectResource(engine), QQmlIncubator(m)
1429 void QV8IncubatorResource::setInitialState(QObject *o)
1431 QQmlComponent_setQmlParent(o, parent);
1433 if (!valuemap.IsEmpty()) {
1434 QQmlComponentExtension *e = componentExtension(engine);
1436 v8::HandleScope handle_scope;
1437 v8::Context::Scope scope(engine->context());
1439 v8::Handle<v8::Value> function = e->initialProperties->Run(qmlGlobal);
1440 v8::Handle<v8::Value> args[] = { engine->newQObject(o), valuemap };
1441 v8::Handle<v8::Function>::Cast(function)->Call(engine->global(), 2, args);
1443 qPersistentDispose(valuemap);
1444 qPersistentDispose(qmlGlobal);
1448 void QV8IncubatorResource::dispose()
1450 qPersistentDispose(valuemap);
1451 qPersistentDispose(qmlGlobal);
1452 // No further status changes are forthcoming, so we no long need a self reference
1453 qPersistentDispose(me);
1456 void QV8IncubatorResource::statusChanged(Status s)
1459 Q_ASSERT(QQmlData::get(object()));
1460 QQmlData::get(object())->explicitIndestructibleSet = false;
1461 QQmlData::get(object())->indestructible = false;
1464 if (!me.IsEmpty()) { // Will be false in synchronous mode
1465 v8::HandleScope scope;
1466 v8::Local<v8::Value> callback = me->GetInternalField(0);
1468 if (!callback.IsEmpty() && !callback->IsUndefined()) {
1470 if (callback->IsFunction()) {
1471 v8::Context::Scope context_scope(engine->context());
1472 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(callback);
1473 v8::Handle<v8::Value> args[] = { v8::Integer::NewFromUnsigned(s) };
1475 f->Call(me, 1, args);
1476 if (tc.HasCaught()) {
1478 QQmlJavaScriptExpression::exceptionToError(tc.Message(), error);
1479 QQmlEnginePrivate::warning(QQmlEnginePrivate::get(engine->engine()), error);
1485 if (s == Ready || s == Error)