1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qdeclarativecomponent.h"
43 #include "qdeclarativecomponent_p.h"
44 #include "qdeclarativecomponentattached_p.h"
46 #include "qdeclarativecompiler_p.h"
47 #include "qdeclarativecontext_p.h"
48 #include "qdeclarativeengine_p.h"
49 #include "qdeclarativevme_p.h"
50 #include "qdeclarative.h"
51 #include "qdeclarativeengine.h"
52 #include "qdeclarativebinding_p.h"
53 #include "qdeclarativebinding_p_p.h"
54 #include "qdeclarativeglobal_p.h"
55 #include "qdeclarativescript_p.h"
56 #include <private/qdeclarativedebugtrace_p.h>
57 #include <private/qdeclarativeenginedebugservice_p.h>
58 #include "qdeclarativeincubator.h"
59 #include "qdeclarativeincubator_p.h"
61 #include <private/qv8engine_p.h>
62 #include <private/qv8include_p.h>
65 #include <QStringList>
66 #include <QtCore/qdebug.h>
67 #include <qdeclarativeinfo.h>
71 class QDeclarativeComponentExtension : public QV8Engine::Deletable
74 QDeclarativeComponentExtension(QV8Engine *);
75 virtual ~QDeclarativeComponentExtension();
77 v8::Persistent<v8::Function> incubationConstructor;
78 v8::Persistent<v8::Script> initialProperties;
79 v8::Persistent<v8::Function> forceCompletion;
81 static V8_DEFINE_EXTENSION(QDeclarativeComponentExtension, 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 QDeclarativeType *type = QDeclarativeMetaType::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 = QDeclarativeMetaType::qmlType(QMetaType::type(className.toUtf8()));
104 typeName = type->qmlTypeName();
112 \class QDeclarativeComponent
114 \brief The QDeclarativeComponent 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 QDeclarativeComponent 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 QDeclarativeEngine *engine = new QDeclarativeEngine;
138 QDeclarativeComponent component(engine, QUrl::fromLocalFile("main.qml"));
140 QObject *myObject = component.create();
141 QDeclarativeItem *item = qobject_cast<QDeclarativeItem*>(myObject);
142 int width = item->width(); // width = 200
146 \section2 Network Components
148 If the URL passed to QDeclarativeComponent is a network resource, or if the QML document references a
149 network resource, the QDeclarativeComponent has to fetch the network data before it is able to create
150 objects. In this case, the QDeclarativeComponent will have a \l {QDeclarativeComponent::Loading}{Loading}
151 \l {QDeclarativeComponent::status()}{status}. An application will have to wait until the component
152 is \l {QDeclarativeComponent::Ready}{Ready} before calling \l {QDeclarativeComponent::create()}.
154 The following example shows how to load a QML file from a network resource. After creating
155 the QDeclarativeComponent, it tests whether the component is loading. If it is, it connects to the
156 QDeclarativeComponent::statusChanged() signal and otherwise calls the \c {continueLoading()} method
157 directly. Note that QDeclarativeComponent::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 QDeclarativeComponent(engine, QUrl("http://www.example.com/main.qml"));
165 if (component->isLoading())
166 QObject::connect(component, SIGNAL(statusChanged(QDeclarativeComponent::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 QDeclarativeComponent
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/declarative/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 QDeclarativeComponent::Status
275 Specifies the loading status of the QDeclarativeComponent.
277 \value Null This QDeclarativeComponent has no data. Call loadUrl() or setData() to add QML content.
278 \value Ready This QDeclarativeComponent is ready and create() may be called.
279 \value Loading This QDeclarativeComponent is loading network data.
280 \value Error An error has occurred. Call errors() to retrieve a list of \{QDeclarativeError}{errors}.
283 void QDeclarativeComponentPrivate::typeDataReady(QDeclarativeTypeData *)
285 Q_Q(QDeclarativeComponent);
289 fromTypeData(typeData);
292 emit q->statusChanged(q->status());
295 void QDeclarativeComponentPrivate::typeDataProgress(QDeclarativeTypeData *, qreal p)
297 Q_Q(QDeclarativeComponent);
301 emit q->progressChanged(p);
304 void QDeclarativeComponentPrivate::fromTypeData(QDeclarativeTypeData *data)
306 url = data->finalUrl();
307 QDeclarativeCompiledData *c = data->compiledData();
310 Q_ASSERT(data->isError());
311 state.errors = data->errors();
319 void QDeclarativeComponentPrivate::clear()
322 typeData->unregisterCallback(this);
336 QDeclarativeComponent::QDeclarativeComponent(QObject *parent)
337 : QObject(*(new QDeclarativeComponentPrivate), parent)
342 Destruct the QDeclarativeComponent.
344 QDeclarativeComponent::~QDeclarativeComponent()
346 Q_D(QDeclarativeComponent);
348 if (d->state.completePending) {
349 qWarning("QDeclarativeComponent: 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 \o Component.Null - no data is available for the component
366 \o Component.Ready - the component has been loaded, and can be used to create instances.
367 \o Component.Loading - the component is currently being loaded
368 \o Component.Error - an error occurred while loading the component.
369 Calling errorString() will provide a human-readable description of any errors.
374 \property QDeclarativeComponent::status
375 The component's current \l{QDeclarativeComponent::Status} {status}.
377 QDeclarativeComponent::Status QDeclarativeComponent::status() const
379 Q_D(const QDeclarativeComponent);
383 else if (!d->state.errors.isEmpty())
385 else if (d->engine && d->cc)
392 Returns true if status() == QDeclarativeComponent::Null.
394 bool QDeclarativeComponent::isNull() const
396 return status() == Null;
400 Returns true if status() == QDeclarativeComponent::Ready.
402 bool QDeclarativeComponent::isReady() const
404 return status() == Ready;
408 Returns true if status() == QDeclarativeComponent::Error.
410 bool QDeclarativeComponent::isError() const
412 return status() == Error;
416 Returns true if status() == QDeclarativeComponent::Loading.
418 bool QDeclarativeComponent::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 QDeclarativeComponent::progress
431 The progress of loading the component, from 0.0 (nothing loaded)
434 qreal QDeclarativeComponent::progress() const
436 Q_D(const QDeclarativeComponent);
441 \fn void QDeclarativeComponent::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 QDeclarativeComponent::statusChanged(QDeclarativeComponent::Status status)
450 Emitted whenever the component's status changes. \a status will be the
455 Create a QDeclarativeComponent with no data and give it the specified
456 \a engine and \a parent. Set the data with setData().
458 QDeclarativeComponent::QDeclarativeComponent(QDeclarativeEngine *engine, QObject *parent)
459 : QObject(*(new QDeclarativeComponentPrivate), parent)
461 Q_D(QDeclarativeComponent);
466 Create a QDeclarativeComponent 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 QDeclarativeComponent::QDeclarativeComponent(QDeclarativeEngine *engine, const QUrl &url, QObject *parent)
475 : QObject(*(new QDeclarativeComponentPrivate), parent)
477 Q_D(QDeclarativeComponent);
483 Create a QDeclarativeComponent from the given \a fileName and give it the specified
484 \a parent and \a engine.
488 QDeclarativeComponent::QDeclarativeComponent(QDeclarativeEngine *engine, const QString &fileName,
490 : QObject(*(new QDeclarativeComponentPrivate), parent)
492 Q_D(QDeclarativeComponent);
494 loadUrl(d->engine->baseUrl().resolved(QUrl::fromLocalFile(fileName)));
500 QDeclarativeComponent::QDeclarativeComponent(QDeclarativeEngine *engine, QDeclarativeCompiledData *cc, int start, QObject *parent)
501 : QObject(*(new QDeclarativeComponentPrivate), parent)
503 Q_D(QDeclarativeComponent);
513 Sets the QDeclarativeComponent 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 QDeclarativeComponent::setData(const QByteArray &data, const QUrl &url)
519 Q_D(QDeclarativeComponent);
525 QDeclarativeTypeData *typeData = QDeclarativeEnginePrivate::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 QDeclarativeContext the component was created in. This is only
541 valid for components created directly from QML.
543 QDeclarativeContext *QDeclarativeComponent::creationContext() const
545 Q_D(const QDeclarativeComponent);
546 if(d->creationContext)
547 return d->creationContext->asQDeclarativeContext();
549 return qmlContext(this);
553 Load the QDeclarativeComponent 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 QDeclarativeComponent::loadUrl(const QUrl &url)
560 Q_D(QDeclarativeComponent);
564 if ((url.isRelative() && !url.isEmpty())
565 || url.scheme() == QLatin1String("file")) // Workaround QTBUG-11929
566 d->url = d->engine->baseUrl().resolved(url);
571 QDeclarativeError error;
572 error.setDescription(tr("Invalid empty URL"));
573 d->state.errors << error;
577 QDeclarativeTypeData *data = QDeclarativeEnginePrivate::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<QDeclarativeError> QDeclarativeComponent::errors() const
598 Q_D(const QDeclarativeComponent);
600 return d->state.errors;
602 return QList<QDeclarativeError>();
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 QDeclarativeComponent::errorString() const
622 Q_D(const QDeclarativeComponent);
626 foreach(const QDeclarativeError &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 QDeclarativeComponent::url
641 The component URL. This is the URL passed to either the constructor,
642 or the loadUrl() or setData() methods.
644 QUrl QDeclarativeComponent::url() const
646 Q_D(const QDeclarativeComponent);
653 QDeclarativeComponent::QDeclarativeComponent(QDeclarativeComponentPrivate &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 {QDeclarativeEngine::rootContext()}{root context}.
666 QObject *QDeclarativeComponent::create(QDeclarativeContext *context)
668 Q_D(QDeclarativeComponent);
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 QDeclarativeComponent::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 QDeclarativeComponent constructs an instance, it occurs in three steps:
689 \i The object hierarchy is created, and constant values are assigned.
690 \i Property bindings are evaluated for the the first time.
691 \i If applicable, QDeclarativeParserStatus::componentComplete() is called on objects.
693 QDeclarativeComponent::beginCreate() differs from QDeclarativeComponent::create() in that it
694 only performs step 1. QDeclarativeComponent::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 *QDeclarativeComponent::beginCreate(QDeclarativeContext *publicContext)
703 Q_D(QDeclarativeComponent);
705 Q_ASSERT(publicContext);
706 QDeclarativeContextData *context = QDeclarativeContextData::get(publicContext);
708 return d->beginCreate(context);
712 QDeclarativeComponentPrivate::beginCreate(QDeclarativeContextData *context)
714 Q_Q(QDeclarativeComponent);
716 qWarning("QDeclarativeComponent: Cannot create a component in a null context");
720 if (!context->isValid()) {
721 qWarning("QDeclarativeComponent: Cannot create a component in an invalid context");
725 if (context->engine != engine) {
726 qWarning("QDeclarativeComponent: Must create component in context from the same QDeclarativeEngine");
730 if (state.completePending) {
731 qWarning("QDeclarativeComponent: Cannot create new component instance before completing the previous");
736 qWarning("QDeclarativeComponent: Component is not ready");
740 QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine);
742 bool isRoot = enginePriv->inProgressCreations == 0;
743 enginePriv->inProgressCreations++;
744 state.errors.clear();
745 state.completePending = true;
748 QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Creating);
750 enginePriv->referenceScarceResources();
751 state.vme.init(context, cc, start, creationContext);
752 QObject *rv = state.vme.execute(&state.errors);
753 enginePriv->dereferenceScarceResources();
756 QDeclarativeData *ddata = QDeclarativeData::get(rv);
758 ddata->indestructible = true;
761 if (enginePriv->isDebugging && rv) {
762 if (!context->isInternal)
763 context->asQDeclarativeContextPrivate()->instances.append(rv);
764 QDeclarativeEngineDebugService::instance()->objectCreated(engine, rv);
766 QDeclarativeDebugTrace::rangeData(QDeclarativeDebugTrace::Creating,
767 buildTypeNameForDebug(rv->metaObject()));
768 QDeclarativeData *data = QDeclarativeData::get(rv);
770 QDeclarativeDebugTrace::rangeLocation(QDeclarativeDebugTrace::Creating,
771 cc->url, data->lineNumber);
778 void QDeclarativeComponentPrivate::beginDeferred(QDeclarativeEnginePrivate *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 QDeclarativeComponentPrivate::complete(QDeclarativeEnginePrivate *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 QDeclarativeComponent::create() to create a
812 Complete a component creation begin with QDeclarativeComponent::beginCreate().
814 void QDeclarativeComponent::completeCreate()
816 Q_D(QDeclarativeComponent);
821 void QDeclarativeComponentPrivate::completeCreate()
823 if (state.completePending) {
824 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
825 complete(ep, &state);
827 QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Creating);
831 QDeclarativeComponentAttached::QDeclarativeComponentAttached(QObject *parent)
832 : QObject(parent), prev(0), next(0)
836 QDeclarativeComponentAttached::~QDeclarativeComponentAttached()
838 if (prev) *prev = next;
839 if (next) next->prev = prev;
847 QDeclarativeComponentAttached *QDeclarativeComponent::qmlAttachedProperties(QObject *obj)
849 QDeclarativeComponentAttached *a = new QDeclarativeComponentAttached(obj);
851 QDeclarativeEngine *engine = qmlEngine(obj);
855 if (QDeclarativeEnginePrivate::get(engine)->activeVME) { // XXX should only be allowed during begin
856 QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine);
857 a->add(&p->activeVME->componentAttached);
859 QDeclarativeData *d = QDeclarativeData::get(obj);
861 Q_ASSERT(d->context);
862 a->add(&d->context->componentAttached);
868 void QDeclarativeComponent::create(QDeclarativeIncubator &i, QDeclarativeContext *context,
869 QDeclarativeContext *forContext)
871 Q_D(QDeclarativeComponent);
874 context = d->engine->rootContext();
876 QDeclarativeContextData *contextData = QDeclarativeContextData::get(context);
877 QDeclarativeContextData *forContextData = contextData;
878 if (forContext) forContextData = QDeclarativeContextData::get(forContext);
880 if (!contextData->isValid()) {
881 qWarning("QDeclarativeComponent: Cannot create a component in an invalid context");
885 if (contextData->engine != d->engine) {
886 qWarning("QDeclarativeComponent: Must create component in context from the same QDeclarativeEngine");
891 qWarning("QDeclarativeComponent: Component is not ready");
896 QDeclarativeIncubatorPrivate *p = i.d;
898 QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(d->engine);
900 p->component = d->cc; p->component->addref();
901 p->vme.init(contextData, d->cc, d->start, d->creationContext);
903 enginePriv->incubate(i, forContextData);
906 class QV8IncubatorResource : public QV8ObjectResource,
907 public QDeclarativeIncubator
909 V8_RESOURCE_TYPE(IncubatorType)
911 QV8IncubatorResource(QV8Engine *engine, IncubationMode = Asynchronous);
913 static v8::Handle<v8::Value> StatusChangedGetter(v8::Local<v8::String>,
914 const v8::AccessorInfo& info);
915 static v8::Handle<v8::Value> StatusGetter(v8::Local<v8::String>,
916 const v8::AccessorInfo& info);
917 static v8::Handle<v8::Value> ObjectGetter(v8::Local<v8::String>,
918 const v8::AccessorInfo& info);
919 static v8::Handle<v8::Value> ForceCompletionGetter(v8::Local<v8::String>,
920 const v8::AccessorInfo& info);
921 static v8::Handle<v8::Value> ForceCompletion(const v8::Arguments &args);
923 static void StatusChangedSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
924 const v8::AccessorInfo& info);
928 v8::Persistent<v8::Object> me;
929 QDeclarativeGuard<QObject> parent;
930 v8::Persistent<v8::Value> valuemap;
931 v8::Persistent<v8::Object> qmlGlobal;
933 virtual void statusChanged(Status);
934 virtual void setInitialState(QObject *);
937 static void QDeclarativeComponent_setQmlParent(QObject *me, QObject *parent)
940 me->setParent(parent);
941 typedef QDeclarativePrivate::AutoParentFunction APF;
942 QList<APF> functions = QDeclarativeMetaType::parentFunctions();
944 bool needParent = false;
945 for (int ii = 0; ii < functions.count(); ++ii) {
946 QDeclarativePrivate::AutoParentResult res = functions.at(ii)(me, parent);
947 if (res == QDeclarativePrivate::Parented) {
950 } else if (res == QDeclarativePrivate::IncompatibleParent) {
955 qWarning("QDeclarativeComponent: Created graphical object was not "
956 "placed in the graphics scene.");
961 \qmlmethod object Component::createObject(Item parent, object properties)
963 Creates and returns an object instance of this component that will have
964 the given \a parent and \a properties. The \a properties argument is optional.
965 Returns null if object creation fails.
967 The object will be created in the same context as the one in which the component
968 was created. This function will always return null when called on components
969 which were not created in QML.
971 If you wish to create an object without setting a parent, specify \c null for
972 the \a parent value. Note that if the returned object is to be displayed, you
973 must provide a valid \a parent value or set the returned object's \l{Item::parent}{parent}
974 property, or else the object will not be visible.
976 If a \a parent is not provided to createObject(), a reference to the returned object must be held so that
977 it is not destroyed by the garbage collector. This is true regardless of whether \l{Item::parent} is set afterwards,
978 since setting the Item parent does not change object ownership; only the graphical parent is changed.
980 As of QtQuick 1.1, this method accepts an optional \a properties argument that specifies a
981 map of initial property values for the created object. These values are applied before object
982 creation is finalized. (This is more efficient than setting property values after object creation,
983 particularly where large sets of property values are defined, and also allows property bindings
984 to be set up before the object is created.)
986 The \a properties argument is specified as a map of property-value items. For example, the code
987 below creates an object with initial \c x and \c y values of 100 and 200, respectively:
990 var component = Qt.createComponent("Button.qml");
991 if (component.status == Component.Ready)
992 component.createObject(parent, {"x": 100, "y": 100});
995 Dynamically created instances can be deleted with the \c destroy() method.
996 See \l {Dynamic Object Management in QML} for more information.
998 void QDeclarativeComponent::createObject(QDeclarativeV8Function *args)
1000 Q_D(QDeclarativeComponent);
1001 Q_ASSERT(d->engine);
1004 QObject *parent = 0;
1005 v8::Local<v8::Object> valuemap;
1007 if (args->Length() >= 1)
1008 parent = args->engine()->toQObject((*args)[0]);
1010 if (args->Length() >= 2) {
1011 v8::Local<v8::Value> v = (*args)[1];
1012 if (!v->IsObject() || v->IsArray()) {
1013 qmlInfo(this) << tr("createObject: value is not an object");
1014 args->returnValue(v8::Null());
1017 valuemap = v8::Local<v8::Object>::Cast(v);
1020 QV8Engine *v8engine = args->engine();
1022 QDeclarativeContext *ctxt = creationContext();
1023 if (!ctxt) ctxt = d->engine->rootContext();
1025 QObject *rv = beginCreate(ctxt);
1028 args->returnValue(v8::Null());
1032 QDeclarativeComponent_setQmlParent(rv, parent);
1034 v8::Handle<v8::Value> ov = v8engine->newQObject(rv);
1035 Q_ASSERT(ov->IsObject());
1036 v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(ov);
1038 if (!valuemap.IsEmpty()) {
1039 QDeclarativeComponentExtension *e = componentExtension(v8engine);
1040 // Try catch isn't needed as the function itself is loaded with try/catch
1041 v8::Handle<v8::Value> function = e->initialProperties->Run(args->qmlGlobal());
1042 v8::Handle<v8::Value> args[] = { object, valuemap };
1043 v8::Handle<v8::Function>::Cast(function)->Call(v8engine->global(), 2, args);
1046 d->completeCreate();
1048 Q_ASSERT(QDeclarativeData::get(rv));
1049 QDeclarativeData::get(rv)->setImplicitDestructible();
1052 args->returnValue(v8::Null());
1054 args->returnValue(object);
1058 \qmlmethod object Component::incubateObject(Item parent, object properties, enum mode)
1060 Creates an incubator for instance of this component. Incubators allow new component
1061 instances to be instantiated asynchronously and not cause freezes in the UI.
1063 The \a parent argument specifies the parent the created instance will have. Omitting the
1064 parameter or passing null will create anobject with no parent. In this case, a reference
1065 to the created object must be maintained by the application of the object will eventually
1066 be garbage collected.
1068 The \a properties argument is specified as a map of property-value items which will be
1069 set on the created object during its construction. \a mode may be Qt.Synchronous or
1070 Qt.Asynchronous and controls whether the instance is created synchronously or asynchronously.
1071 The default is asynchronously. In some circumstances, even if Qt.Synchronous is specified,
1072 the incubator may create the object asynchronously. This happens if the component calling
1073 incubateObject() is itself being created asynchronously.
1075 All three arguments are optional.
1077 If successful, the method returns an incubator, otherwise null. The incubator has the following
1081 \i status The status of the incubator. Valid values are Component.Ready, Component.Loading and
1083 \i object The created object instance. Will only be available once the incubator is in the
1085 \i onStatusChanged Specifies a callback function to be invoked when the status changes. The
1086 status is passed as a parameter to the callback.
1087 \i forceCompletion() Call to complete incubation synchronously.
1090 The following example demonstrates how to use an incubator:
1093 var component = Qt.createComponent("Button.qml");
1095 var incubator = component.incubateObject(parent, { x: 10, y: 10 });
1096 if (incubator.status != Component.Ready) {
1097 incubator.onStatusChanged = function(status) {
1098 if (status == Component.Ready) {
1099 print ("Object", incubator.object, "is now ready!");
1103 print ("Object", incubator.object, "is ready immediately!");
1108 void QDeclarativeComponent::incubateObject(QDeclarativeV8Function *args)
1110 Q_D(QDeclarativeComponent);
1111 Q_ASSERT(d->engine);
1115 QObject *parent = 0;
1116 v8::Local<v8::Object> valuemap;
1117 QDeclarativeIncubator::IncubationMode mode = QDeclarativeIncubator::Asynchronous;
1119 if (args->Length() >= 1)
1120 parent = args->engine()->toQObject((*args)[0]);
1122 if (args->Length() >= 2) {
1123 v8::Local<v8::Value> v = (*args)[1];
1125 } else if (!v->IsObject() || v->IsArray()) {
1126 qmlInfo(this) << tr("createObject: value is not an object");
1127 args->returnValue(v8::Null());
1130 valuemap = v8::Local<v8::Object>::Cast(v);
1134 if (args->Length() >= 3) {
1135 quint32 v = (*args)[2]->Uint32Value();
1137 mode = QDeclarativeIncubator::Asynchronous;
1139 mode = QDeclarativeIncubator::AsynchronousIfNested;
1142 QDeclarativeComponentExtension *e = componentExtension(args->engine());
1144 QV8IncubatorResource *r = new QV8IncubatorResource(args->engine(), mode);
1145 v8::Local<v8::Object> o = e->incubationConstructor->NewInstance();
1146 o->SetExternalResource(r);
1148 if (!valuemap.IsEmpty()) {
1149 r->valuemap = qPersistentNew(valuemap);
1150 r->qmlGlobal = qPersistentNew(args->qmlGlobal());
1153 r->me = qPersistentNew(o);
1155 create(*r, creationContext());
1157 if (r->status() == QDeclarativeIncubator::Null) {
1159 args->returnValue(v8::Null());
1161 args->returnValue(o);
1165 // XXX used by QSGLoader
1166 void QDeclarativeComponentPrivate::initializeObjectWithInitialProperties(v8::Handle<v8::Object> qmlGlobal, v8::Handle<v8::Object> valuemap, QObject *toCreate)
1168 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
1169 QV8Engine *v8engine = ep->v8engine();
1171 v8::HandleScope handle_scope;
1172 v8::Context::Scope scope(v8engine->context());
1173 v8::Handle<v8::Value> ov = v8engine->newQObject(toCreate);
1174 Q_ASSERT(ov->IsObject());
1175 v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(ov);
1177 if (!valuemap.IsEmpty()) {
1178 QDeclarativeComponentExtension *e = componentExtension(v8engine);
1179 // Try catch isn't needed as the function itself is loaded with try/catch
1180 v8::Handle<v8::Value> function = e->initialProperties->Run(qmlGlobal);
1181 v8::Handle<v8::Value> args[] = { object, valuemap };
1182 v8::Handle<v8::Function>::Cast(function)->Call(v8engine->global(), 2, args);
1185 QDeclarativeData *ddata = QDeclarativeData::get(toCreate);
1187 ddata->setImplicitDestructible();
1191 QDeclarativeComponentExtension::QDeclarativeComponentExtension(QV8Engine *engine)
1193 v8::HandleScope handle_scope;
1194 v8::Context::Scope scope(engine->context());
1196 forceCompletion = qPersistentNew(V8FUNCTION(QV8IncubatorResource::ForceCompletion, engine));
1199 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
1200 ft->InstanceTemplate()->SetHasExternalResource(true);
1201 ft->InstanceTemplate()->SetInternalFieldCount(1);
1202 ft->InstanceTemplate()->SetAccessor(v8::String::New("onStatusChanged"),
1203 QV8IncubatorResource::StatusChangedGetter,
1204 QV8IncubatorResource::StatusChangedSetter);
1205 ft->InstanceTemplate()->SetAccessor(v8::String::New("status"),
1206 QV8IncubatorResource::StatusGetter);
1207 ft->InstanceTemplate()->SetAccessor(v8::String::New("object"),
1208 QV8IncubatorResource::ObjectGetter);
1209 ft->InstanceTemplate()->SetAccessor(v8::String::New("forceCompletion"),
1210 QV8IncubatorResource::ForceCompletionGetter);
1211 incubationConstructor = qPersistentNew(ft->GetFunction());
1215 #define INITIALPROPERTIES_SOURCE \
1216 "(function(object, values) {"\
1218 "for(var property in values) {" \
1220 "var properties = property.split(\".\");"\
1222 "for (var ii = 0; ii < properties.length - 1; ++ii) {"\
1223 "o = o[properties[ii]];"\
1225 "o[properties[properties.length - 1]] = values[property];"\
1230 initialProperties = qPersistentNew(engine->qmlModeCompile(QLatin1String(INITIALPROPERTIES_SOURCE)));
1231 #undef INITIALPROPERTIES_SOURCE
1235 v8::Handle<v8::Value> QV8IncubatorResource::ObjectGetter(v8::Local<v8::String>,
1236 const v8::AccessorInfo& info)
1238 QV8IncubatorResource *r = v8_resource_check<QV8IncubatorResource>(info.This());
1239 return r->engine->newQObject(r->object());
1242 v8::Handle<v8::Value> QV8IncubatorResource::ForceCompletionGetter(v8::Local<v8::String>,
1243 const v8::AccessorInfo& info)
1245 QV8IncubatorResource *r = v8_resource_check<QV8IncubatorResource>(info.This());
1246 return componentExtension(r->engine)->forceCompletion;
1249 v8::Handle<v8::Value> QV8IncubatorResource::ForceCompletion(const v8::Arguments &args)
1251 QV8IncubatorResource *r = v8_resource_cast<QV8IncubatorResource>(args.This());
1253 V8THROW_TYPE("Not an incubator object");
1255 r->forceCompletion();
1257 return v8::Undefined();
1260 v8::Handle<v8::Value> QV8IncubatorResource::StatusGetter(v8::Local<v8::String>,
1261 const v8::AccessorInfo& info)
1263 QV8IncubatorResource *r = v8_resource_check<QV8IncubatorResource>(info.This());
1264 return v8::Integer::NewFromUnsigned(r->status());
1267 v8::Handle<v8::Value> QV8IncubatorResource::StatusChangedGetter(v8::Local<v8::String>,
1268 const v8::AccessorInfo& info)
1270 return info.This()->GetInternalField(0);
1273 void QV8IncubatorResource::StatusChangedSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
1274 const v8::AccessorInfo& info)
1276 info.This()->SetInternalField(0, value);
1279 QDeclarativeComponentExtension::~QDeclarativeComponentExtension()
1281 qPersistentDispose(incubationConstructor);
1282 qPersistentDispose(initialProperties);
1283 qPersistentDispose(forceCompletion);
1286 QV8IncubatorResource::QV8IncubatorResource(QV8Engine *engine, IncubationMode m)
1287 : QV8ObjectResource(engine), QDeclarativeIncubator(m)
1291 void QV8IncubatorResource::setInitialState(QObject *o)
1293 QDeclarativeComponent_setQmlParent(o, parent);
1295 if (!valuemap.IsEmpty()) {
1296 QDeclarativeComponentExtension *e = componentExtension(engine);
1298 v8::HandleScope handle_scope;
1299 v8::Context::Scope scope(engine->context());
1301 v8::Handle<v8::Value> function = e->initialProperties->Run(qmlGlobal);
1302 v8::Handle<v8::Value> args[] = { engine->newQObject(o), valuemap };
1303 v8::Handle<v8::Function>::Cast(function)->Call(engine->global(), 2, args);
1305 qPersistentDispose(valuemap);
1306 qPersistentDispose(qmlGlobal);
1310 void QV8IncubatorResource::dispose()
1312 qPersistentDispose(valuemap);
1313 qPersistentDispose(qmlGlobal);
1314 // No further status changes are forthcoming, so we no long need a self reference
1315 qPersistentDispose(me);
1318 void QV8IncubatorResource::statusChanged(Status s)
1321 Q_ASSERT(QDeclarativeData::get(object()));
1322 QDeclarativeData::get(object())->setImplicitDestructible();
1325 if (!me.IsEmpty()) { // Will be false in synchronous mode
1326 v8::HandleScope scope;
1327 v8::Local<v8::Value> callback = me->GetInternalField(0);
1329 if (!callback.IsEmpty() && !callback->IsUndefined()) {
1331 if (callback->IsFunction()) {
1332 v8::Context::Scope context_scope(engine->context());
1333 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(callback);
1334 v8::Handle<v8::Value> args[] = { v8::Integer::NewFromUnsigned(s) };
1336 f->Call(me, 1, args);
1337 if (tc.HasCaught()) {
1338 QDeclarativeError error;
1339 QDeclarativeExpressionPrivate::exceptionToError(tc.Message(), error);
1340 QDeclarativeEnginePrivate::warning(QDeclarativeEnginePrivate::get(engine->engine()),
1347 if (s == Ready || s == Error)