#include <private/qdeclarativeengine_p.h>
#include <private/qdeclarativeglobal_p.h>
+#include <private/qdeclarativecomponent_p.h>
+
+#include <private/qv8_p.h>
+
QT_BEGIN_NAMESPACE
QSGLoaderPrivate::QSGLoaderPrivate()
: item(0), component(0), ownComponent(false), updatingSize(false),
- itemWidthValid(false), itemHeightValid(false)
+ itemWidthValid(false), itemHeightValid(false), active(true)
{
}
QSGLoaderPrivate::~QSGLoaderPrivate()
{
+ disposeInitialPropertyValues();
}
void QSGLoaderPrivate::itemGeometryChanged(QSGItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry)
void QSGLoaderPrivate::clear()
{
+ disposeInitialPropertyValues();
+
if (ownComponent) {
component->deleteLater();
component = 0;
}
/*!
+ \qmlproperty bool QtQuick2::Loader::active
+ This property is \c true if the Loader is currently active.
+ The default value for the \l active property is \c true.
+
+ If the Loader is inactive, changing the \l source or \l sourceComponent
+ will not cause the item to be instantiated until the Loader is made active.
+
+ Setting the value to inactive will cause any \l item loaded by the loader
+ to be released, but will not affect the \l source or \l sourceComponent.
+
+ The \l status of an inactive loader is always \c Null.
+
+ \sa source, sourceComponent
+ */
+bool QSGLoader::active() const
+{
+ Q_D(const QSGLoader);
+ return d->active;
+}
+
+void QSGLoader::setActive(bool newVal)
+{
+ Q_D(QSGLoader);
+ if (d->active != newVal) {
+ d->active = newVal;
+ if (newVal == true) {
+ if (d->loadingFromSource) {
+ loadFromSource();
+ } else {
+ loadFromSourceComponent();
+ }
+ d->disposeInitialPropertyValues(); // release persistent handles
+ } else {
+ if (d->item) {
+ QSGItemPrivate *p = QSGItemPrivate::get(d->item);
+ p->removeItemChangeListener(d, QSGItemPrivate::Geometry);
+
+ // We can't delete immediately because our item may have triggered
+ // the Loader to load a different item.
+ d->item->setParentItem(0);
+ d->item->setVisible(false);
+ d->item->deleteLater();
+ d->item = 0;
+ emit itemChanged();
+ }
+ emit statusChanged();
+ }
+ emit activeChanged();
+ }
+}
+
+
+/*!
\qmlproperty url QtQuick2::Loader::source
This property holds the URL of the QML component to instantiate.
void QSGLoader::setSource(const QUrl &url)
{
+ setSource(url, true); // clear previous values
+}
+
+void QSGLoader::setSource(const QUrl &url, bool needsClear)
+{
Q_D(QSGLoader);
if (d->source == url)
return;
- d->clear();
+ if (needsClear)
+ d->clear();
d->source = url;
+ d->loadingFromSource = true;
+
+ if (d->active)
+ loadFromSource();
+ else
+ emit sourceChanged();
+}
+
+void QSGLoader::loadFromSource()
+{
+ Q_D(QSGLoader);
if (d->source.isEmpty()) {
emit sourceChanged();
emit statusChanged();
d->component = comp;
d->ownComponent = false;
+ d->loadingFromSource = false;
+
+ if (d->active)
+ loadFromSourceComponent();
+ else
+ emit sourceComponentChanged();
+}
+
+void QSGLoader::resetSourceComponent()
+{
+ setSourceComponent(0);
+}
+
+void QSGLoader::loadFromSourceComponent()
+{
+ Q_D(QSGLoader);
if (!d->component) {
emit sourceComponentChanged();
emit statusChanged();
d->load();
}
-void QSGLoader::resetSourceComponent()
+/*!
+ \qmlmethod object QtQuick2::Loader::setSource(url source, object properties)
+
+ Creates an object instance of the given \a source component that will have
+ the given \a properties. The \a properties argument is optional. The instance
+ will be accessible via the \l item property once loading and instantiation
+ is complete.
+
+ If the \l active property is \c false at the time when this function is called,
+ the given \a source component will not be loaded but the \a source and initial
+ \a properties will be cached. When the loader is made \l active, an instance of
+ the \a source component will be created with the initial \a properties set.
+
+ Setting the initial property values of an instance of a component in this manner
+ will \e not trigger any associated \l{Behavior}s.
+
+ Note that the cached \a properties will be cleared if the \l source or \l sourceComponent
+ is changed after calling this function but prior to setting the loader \l active.
+
+ Example:
+ \table
+ \row
+ \o
+ \qml
+ // ExampleComponent.qml
+ import QtQuick 2.0
+ Rectangle {
+ id: rect
+ color: "red"
+ width: 10
+ height: 10
+
+ Behavior on color {
+ NumberAnimation {
+ target: rect
+ property: "width"
+ to: (rect.width + 20)
+ duration: 0
+ }
+ }
+ }
+ \endqml
+ \o
+ \qml
+ // example.qml
+ import QtQuick 2.0
+ Item {
+ Loader {
+ id: squareLoader
+ onLoaded: console.log(squareLoader.item.width); // prints [10], not [30]
+ }
+
+ Component.onCompleted: {
+ squareLoader.setSource("ExampleComponent.qml", { "color": "blue" });
+ // will trigger the onLoaded code when complete.
+ }
+ }
+ \endqml
+ \endtable
+
+ \sa source, active
+*/
+void QSGLoader::setSource(QDeclarativeV8Function *args)
{
- setSourceComponent(0);
+ Q_ASSERT(args);
+ Q_D(QSGLoader);
+
+ bool ipvError = false;
+ args->returnValue(v8::Undefined());
+ v8::Handle<v8::Object> ipv = d->extractInitialPropertyValues(args, this, &ipvError);
+ if (ipvError)
+ return;
+
+ d->clear();
+ QUrl sourceUrl = d->resolveSourceUrl(args);
+ if (!ipv.IsEmpty()) {
+ d->initialPropertyValues = qPersistentNew(ipv);
+ d->qmlGlobalForIpv = qPersistentNew(args->qmlGlobal());
+ }
+
+ setSource(sourceUrl, false); // already cleared and set ipv above.
+}
+
+void QSGLoaderPrivate::disposeInitialPropertyValues()
+{
+ if (!initialPropertyValues.IsEmpty())
+ qPersistentDispose(initialPropertyValues);
+ if (!qmlGlobalForIpv.IsEmpty())
+ qPersistentDispose(qmlGlobalForIpv);
}
void QSGLoaderPrivate::load()
// component to be set to something else. In that case we just
// need to cleanup.
if (c)
- c->completeCreate();
+ completeCreateWithInitialPropertyValues(c, obj, initialPropertyValues, qmlGlobalForIpv);
delete obj;
delete ctxt;
return;
delete ctxt;
source = QUrl();
}
- component->completeCreate();
+ completeCreateWithInitialPropertyValues(component, obj, initialPropertyValues, qmlGlobalForIpv);
if (ownComponent)
emit q->sourceChanged();
else
This property holds the status of QML loading. It can be one of:
\list
- \o Loader.Null - no QML source has been set
+ \o Loader.Null - the loader is inactive or no QML source has been set
\o Loader.Ready - the QML source has been loaded
\o Loader.Loading - the QML source is currently being loaded
\o Loader.Error - an error occurred while loading the QML source
{
Q_D(const QSGLoader);
+ if (!d->active)
+ return Null;
+
if (d->component)
return static_cast<QSGLoader::Status>(d->component->status());
QSGItem::geometryChanged(newGeometry, oldGeometry);
}
+QUrl QSGLoaderPrivate::resolveSourceUrl(QDeclarativeV8Function *args)
+{
+ QV8Engine *v8engine = args->engine();
+ QString arg = v8engine->toString((*args)[0]->ToString());
+ if (arg.isEmpty())
+ return QUrl();
+
+ QDeclarativeContextData *context = args->context();
+ Q_ASSERT(context);
+ return context->resolvedUrl(QUrl(arg));
+}
+
+v8::Handle<v8::Object> QSGLoaderPrivate::extractInitialPropertyValues(QDeclarativeV8Function *args, QObject *loader, bool *error)
+{
+ v8::Local<v8::Object> valuemap;
+ if (args->Length() >= 2) {
+ v8::Local<v8::Value> v = (*args)[1];
+ if (!v->IsObject() || v->IsArray()) {
+ *error = true;
+ qmlInfo(loader) << loader->tr("setSource: value is not an object");
+ } else {
+ *error = false;
+ valuemap = v8::Local<v8::Object>::Cast(v);
+ }
+ }
+
+ return valuemap;
+}
+
+void QSGLoaderPrivate::completeCreateWithInitialPropertyValues(QDeclarativeComponent *component, QObject *object, v8::Handle<v8::Object> initialPropertyValues, v8::Handle<v8::Object> qmlGlobal)
+{
+ if (initialPropertyValues.IsEmpty()) {
+ component->completeCreate();
+ return;
+ }
+
+ QDeclarativeComponentPrivate *d = QDeclarativeComponentPrivate::get(component);
+ Q_ASSERT(d && d->engine);
+ d->completeCreateObjectWithInitialProperties(qmlGlobal, initialPropertyValues, object);
+}
+
#include <moc_qsgloader_p.cpp>
QT_END_NAMESPACE
Q_OBJECT
Q_ENUMS(Status)
+ Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
Q_PROPERTY(QDeclarativeComponent *sourceComponent READ sourceComponent WRITE setSourceComponent RESET resetSourceComponent NOTIFY sourceComponentChanged)
Q_PROPERTY(QSGItem *item READ item NOTIFY itemChanged)
QSGLoader(QSGItem *parent = 0);
virtual ~QSGLoader();
+ bool active() const;
+ void setActive(bool newVal);
+
+ Q_INVOKABLE void setSource(QDeclarativeV8Function *);
+
QUrl source() const;
void setSource(const QUrl &);
Q_SIGNALS:
void itemChanged();
+ void activeChanged();
void sourceChanged();
void sourceComponentChanged();
void statusChanged();
void componentComplete();
private:
+ void setSource(const QUrl &sourceUrl, bool needsClear);
+ void loadFromSource();
+ void loadFromSourceComponent();
Q_DISABLE_COPY(QSGLoader)
Q_DECLARE_PRIVATE(QSGLoader)
Q_PRIVATE_SLOT(d_func(), void _q_sourceLoaded())
#include "qsgimplicitsizeitem_p_p.h"
#include "qsgitemchangelistener_p.h"
+#include <private/qv8_p.h>
+
QT_BEGIN_NAMESPACE
class QDeclarativeContext;
void initResize();
void load();
+ void disposeInitialPropertyValues();
+ QUrl resolveSourceUrl(QDeclarativeV8Function *args);
+ v8::Handle<v8::Object> extractInitialPropertyValues(QDeclarativeV8Function *args, QObject *loader, bool *error);
+ void completeCreateWithInitialPropertyValues(QDeclarativeComponent *component, QObject *object, v8::Handle<v8::Object> initialPropertyValues, v8::Handle<v8::Object> qmlGlobal);
+
QUrl source;
QSGItem *item;
QDeclarativeComponent *component;
+ v8::Persistent<v8::Object> initialPropertyValues;
+ v8::Persistent<v8::Object> qmlGlobalForIpv;
bool ownComponent : 1;
bool updatingSize: 1;
bool itemWidthValid : 1;
bool itemHeightValid : 1;
+ bool active : 1;
+ bool loadingFromSource : 1;
void _q_sourceLoaded();
void _q_updateSize(bool loaderGeometryChanged = true);
#include "private/qdeclarativedebugtrace_p.h"
#include "private/qdeclarativeenginedebug_p.h"
+#include "private/qv8engine_p.h"
+#include "private/qv8include_p.h"
+
#include <QStack>
#include <QStringList>
#include <QtCore/qdebug.h>
{
Q_ASSERT(args);
-#define RETURN(result) { args->returnValue((result)); return; }
-
Q_D(QDeclarativeComponent);
Q_ASSERT(d->engine);
- QDeclarativeEngine *engine = d->engine;
- QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
- QV8Engine *v8engine = ep->v8engine();
-
- QDeclarativeContext *ctxt = creationContext();
- if (!ctxt) ctxt = engine->rootContext();
+ QObject *parent = args->Length()?QDeclarativeEnginePrivate::get(d->engine)->v8engine()->toQObject((*args)[0]):0;
v8::Local<v8::Object> valuemap;
if (args->Length() >= 2) {
v8::Local<v8::Value> v = (*args)[1];
if (!v->IsObject() || v->IsArray()) {
qmlInfo(this) << tr("createObject: value is not an object");
- RETURN(v8::Null());
+ args->returnValue(v8::Null());
+ return;
}
valuemap = v8::Local<v8::Object>::Cast(v);
}
- QObject *parent = args->Length()?v8engine->toQObject((*args)[0]):0;
+ QV8Engine *v8engine = QDeclarativeEnginePrivate::get(d->engine)->v8engine();
+ QObject *retn = d->createObjectWithInitialProperties(args->qmlGlobal(), valuemap, parent);
+ if (!retn)
+ args->returnValue(v8::Null());
+ else
+ args->returnValue(v8engine->newQObject(retn));
+}
+
+QObject *QDeclarativeComponentPrivate::createObjectWithInitialProperties(v8::Handle<v8::Object> qmlGlobal, v8::Handle<v8::Object> valuemap, QObject *parentObject)
+{
+ Q_Q(QDeclarativeComponent);
+ Q_ASSERT(engine);
- QObject *ret = beginCreate(ctxt);
+ QDeclarativeContext *ctxt = q->creationContext();
+ if (!ctxt) ctxt = engine->rootContext();
+
+ QObject *parent = parentObject;
+
+ QObject *ret = q->beginCreate(ctxt);
if (!ret) {
- completeCreate();
- RETURN(v8::Null());
+ q->completeCreate();
+ return 0;
}
if (parent) {
qWarning("QDeclarativeComponent: Created graphical object was not placed in the graphics scene.");
}
- v8::Handle<v8::Value> ov = v8engine->newQObject(ret);
+ return completeCreateObjectWithInitialProperties(qmlGlobal, valuemap, ret);
+}
+
+QObject *QDeclarativeComponentPrivate::completeCreateObjectWithInitialProperties(v8::Handle<v8::Object> qmlGlobal, v8::Handle<v8::Object> valuemap, QObject *toCreate)
+{
+ QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
+ QV8Engine *v8engine = ep->v8engine();
+ v8::Handle<v8::Value> ov = v8engine->newQObject(toCreate);
Q_ASSERT(ov->IsObject());
v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(ov);
"})"
v8::Local<v8::Script> script = v8engine->qmlModeCompile(SET_ARGS_SOURCE);
- v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(script->Run(args->qmlGlobal()));
+ v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(script->Run(qmlGlobal));
// Try catch isn't needed as the function itself is loaded with try/catch
v8::Handle<v8::Value> args[] = { object, valuemap };
}
completeCreate();
-
- QDeclarativeData *ddata = QDeclarativeData::get(ret);
+
+ QDeclarativeData *ddata = QDeclarativeData::get(toCreate);
Q_ASSERT(ddata);
ddata->setImplicitDestructible();
- RETURN(object);
-
-#undef RETURN
+ return v8engine->toQObject(object);
}
/*!
#include "qdeclarativecomponent.h"
+#include "private/qv8_p.h"
#include "private/qdeclarativeengine_p.h"
#include "private/qdeclarativetypeloader_p.h"
#include "private/qbitfield_p.h"
QT_BEGIN_NAMESPACE
+class QV8Engine;
+
class QDeclarativeComponent;
class QDeclarativeEngine;
class QDeclarativeCompiledData;
QObject *beginCreate(QDeclarativeContextData *, const QBitField &);
void completeCreate();
+ QObject *createObjectWithInitialProperties(v8::Handle<v8::Object> qmlGlobal, v8::Handle<v8::Object> valuemap, QObject *parentObject);
+ QObject *completeCreateObjectWithInitialProperties(v8::Handle<v8::Object> qmlGlobal, v8::Handle<v8::Object> valuemap, QObject *toCreate);
QDeclarativeTypeData *typeData;
virtual void typeDataReady(QDeclarativeTypeData *);
ConstructionState *state);
static void complete(QDeclarativeEnginePrivate *enginePriv, ConstructionState *state);
+
QDeclarativeEngine *engine;
QDeclarativeGuardedContextData creationContext;
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ id: behaviorCounter
+ property int behaviorCount: 0
+ property int canary: 0
+
+ Behavior on canary {
+ NumberAnimation { target: behaviorCounter; property: "behaviorCount"; to: (behaviorCounter.behaviorCount + 1); duration: 0 }
+ }
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ id: behaviorCounter
+ property int behaviorCount: 0
+ property int canary: 0
+
+ Behavior on canary {
+ NumberAnimation { target: behaviorCounter; property: "behaviorCount"; to: (behaviorCounter.behaviorCount + 1); duration: 0 }
+ }
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ RandomError
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ id: root
+
+ Loader {
+ id: loader
+ objectName: "loader"
+ active: false
+ }
+
+ Component {
+ id: inlineTestComponent
+ Item {
+ id: inlineTestItem
+ property int someProperty: 5
+ }
+ }
+
+ function doSetSource() {
+ loader.source = "ActiveComponent.qml";
+ }
+
+ function doSetSourceComponent() {
+ loader.sourceComponent = inlineTestComponent;
+ }
+
+ function doSetActive() {
+ loader.active = true;
+ }
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ id: root
+
+ Loader {
+ id: loader
+ objectName: "loader"
+ source: "ActiveComponent.qml";
+
+ property int statusChangedCount: 0
+ onStatusChanged: statusChangedCount = statusChangedCount + 1
+ }
+
+ function doSetInactive() {
+ loader.active = false;
+ }
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ id: root
+
+ Loader {
+ id: loader
+ objectName: "loader"
+ source: "ActiveComponent.qml";
+
+ property int sourceChangedCount: 0
+ onSourceChanged: sourceChangedCount = sourceChangedCount + 1
+ }
+
+ function doSetInactive() {
+ loader.active = false;
+ }
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ id: root
+
+ Component {
+ id: inlineTestComponent
+ Item {
+ id: inlineTestItem
+ property int someProperty: 5
+ }
+ }
+
+ Loader {
+ id: loader
+ objectName: "loader"
+ sourceComponent: inlineTestComponent
+
+ property int sourceComponentChangedCount: 0
+ onSourceComponentChanged: sourceComponentChangedCount = sourceComponentChangedCount + 1
+ }
+
+ function doSetInactive() {
+ loader.active = false;
+ }
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ id: root
+
+ Loader {
+ id: loader
+ objectName: "loader"
+ source: "ActiveComponent.qml";
+
+ property int itemChangedCount: 0
+ onItemChanged: itemChangedCount = itemChangedCount + 1
+ }
+
+ function doSetInactive() {
+ loader.active = false;
+ }
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ id: root
+
+ Loader {
+ id: loader
+ objectName: "loader"
+
+ property int activeChangedCount: 0
+ onActiveChanged: activeChangedCount = activeChangedCount + 1
+ }
+
+ function doSetActive() {
+ loader.active = true;
+ }
+
+ function doSetInactive() {
+ loader.active = false;
+ }
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ id: root
+ property int initialValue: 0
+ property int behaviorCount: 0
+
+ Loader {
+ id: loader
+ objectName: "loader"
+
+ onLoaded: {
+ loader.item.canary = 1; // will trigger the behavior, setting behaviorCount -> 1
+ }
+ }
+
+ Component.onCompleted: {
+ loader.source = "InitialPropertyValuesComponent.qml";
+ root.initialValue = loader.item.canary; // should be one, since onLoaded will have triggered by now
+ root.behaviorCount = loader.item.behaviorCount; // should be one, since onLoaded will have triggered by now
+ }
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ id: root
+ property int initialValue: 0
+ property int behaviorCount: 0
+
+ Loader {
+ id: loader
+ objectName: "loader"
+ onLoaded: {
+ root.initialValue = loader.item.canary; // should be two
+ root.behaviorCount = loader.item.behaviorCount; // should be zero
+ }
+ }
+
+ Component.onCompleted: {
+ loader.setSource("InitialPropertyValuesComponent.qml", {"canary": 2});
+ }
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ id: root
+ property int initialValue: 0
+ property int behaviorCount: 0
+
+ Loader {
+ id: loader
+ objectName: "loader"
+ active: false
+ }
+
+ Component.onCompleted: {
+ loader.setSource("InitialPropertyValuesComponent.qml", {"canary": 3});
+ root.initialValue = loader.item.canary; // error - item should not yet exist.
+ }
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ id: root
+ property int initialValue: 0
+ property int behaviorCount: 0
+
+ Loader {
+ id: loader
+ objectName: "loader"
+ active: false
+ onLoaded: {
+ root.initialValue = loader.item.canary; // should be four
+ root.behaviorCount = loader.item.behaviorCount; // should be zero
+ }
+ }
+
+ Component.onCompleted: {
+ loader.setSource("InitialPropertyValuesComponent.qml", {"canary": 4});
+ loader.active = true
+ }
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ id: root
+ property int initialValue: 0
+ property int behaviorCount: 0
+
+ Loader {
+ id: loader
+ objectName: "loader"
+ onLoaded: {
+ root.initialValue = loader.item.canary; // should be zero, but no error
+ root.behaviorCount = loader.item.behaviorCount; // should be zero
+ }
+ }
+
+ Component.onCompleted: {
+ loader.setSource("InitialPropertyValuesComponent.qml");
+ }
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ id: root
+ property int initialValue: 0
+ property int behaviorCount: 0
+
+ Loader {
+ id: loader
+ objectName: "loader"
+ onLoaded: {
+ root.initialValue = loader.item.canary; // should be six
+ root.behaviorCount = loader.item.behaviorCount; // should be zero
+ }
+ }
+
+ Item {
+ id: child
+ property int bindable: 6
+ }
+
+ Component.onCompleted: {
+ loader.setSource("InitialPropertyValuesComponent.qml", {"canary": child.bindable});
+ }
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ id: root
+ property int loaderValue: 0
+ property int createObjectValue: 0
+
+ Loader {
+ id: loader
+ objectName: "loader"
+ onLoaded: {
+ root.loaderValue = loader.item.canary; // should still be one
+ }
+ }
+
+ Item {
+ id: child
+ property int bindable: 1;
+ }
+
+ property InitialPropertyValuesComponent ipvc
+ Component.onCompleted: {
+ loader.setSource("InitialPropertyValuesComponent.qml", {"canary": child.bindable});
+ var dynComp = Qt.createComponent("InitialPropertyValuesComponent.qml");
+ ipvc = dynComp.createObject(root, {"canary": child.bindable});
+ child.bindable = 7; // won't cause re-evaluation, since not used in a binding.
+ root.createObjectValue = ipvc.canary; // should still be one
+ }
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ id: root
+
+ property InitialPropertyValuesComponent testInstance
+ testInstance: loader.item
+
+ property int bindable: 1
+ property int canaryValue: testInstance.canary
+ property int behaviorCount: testInstance.behaviorCount
+
+ Loader {
+ id: loader
+ objectName: "loader"
+ }
+
+ Component.onCompleted: {
+ loader.setSource("InitialPropertyValuesComponent.qml", {"canary": (function() { return root.bindable })});
+ }
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ id: root
+
+ Loader {
+ id: loader
+ objectName: "loader"
+ }
+
+ Component.onCompleted: {
+ loader.setSource("InitialPropertyValuesComponent.qml", 3); // invalid initial properties object
+ }
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ id: root
+
+ Loader {
+ id: loader
+ objectName: "loader"
+ }
+
+ Component.onCompleted: {
+ loader.setSource("NonexistentSourceComponent.qml", {"canary":3});
+ }
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ id: root
+
+ Loader {
+ id: loader
+ objectName: "loader"
+ }
+
+ Component.onCompleted: {
+ loader.setSource("InvalidSourceComponent.qml", {"canary":3});
+ }
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ id: root
+ property int canary: loader.item.canary
+
+ Loader {
+ id: loader
+ objectName: "loader"
+ }
+
+ Component.onCompleted: {
+ loader.setSource("InitialPropertyValuesComponent.qml", 3); // invalid initial properties object
+ }
+}
#include <qtest.h>
#include <QSignalSpy>
+
#include <QtDeclarative/qdeclarativeengine.h>
#include <QtDeclarative/qdeclarativecomponent.h>
#include <private/qsgloader_p.h>
void networkRequestUrl();
void failNetworkRequest();
// void networkComponent();
+ void active();
+ void initialPropertyValues_data();
+ void initialPropertyValues();
+ void initialPropertyValuesBinding();
+ void initialPropertyValuesError_data();
+ void initialPropertyValuesError();
void deleteComponentCrash();
void nonItem();
delete loader;
}
+void tst_QSGLoader::active()
+{
+ // check that the item isn't instantiated until active is set to true
+ {
+ QDeclarativeComponent component(&engine, TEST_FILE("active.1.qml"));
+ QObject *object = component.create();
+ QVERIFY(object != 0);
+ QSGLoader *loader = object->findChild<QSGLoader*>("loader");
+
+ QVERIFY(loader->active() == false); // set manually to false
+ QVERIFY(loader->item() == 0);
+ QMetaObject::invokeMethod(object, "doSetSourceComponent");
+ QVERIFY(loader->item() == 0);
+ QMetaObject::invokeMethod(object, "doSetSource");
+ QVERIFY(loader->item() == 0);
+ QMetaObject::invokeMethod(object, "doSetActive");
+ QVERIFY(loader->item() != 0);
+
+ delete object;
+ }
+
+ // check that the status is Null if active is set to false
+ {
+ QDeclarativeComponent component(&engine, TEST_FILE("active.2.qml"));
+ QObject *object = component.create();
+ QVERIFY(object != 0);
+ QSGLoader *loader = object->findChild<QSGLoader*>("loader");
+
+ QVERIFY(loader->active() == true); // active is true by default
+ QCOMPARE(loader->status(), QSGLoader::Ready);
+ int currStatusChangedCount = loader->property("statusChangedCount").toInt();
+ QMetaObject::invokeMethod(object, "doSetInactive");
+ QCOMPARE(loader->status(), QSGLoader::Null);
+ QCOMPARE(loader->property("statusChangedCount").toInt(), (currStatusChangedCount+1));
+
+ delete object;
+ }
+
+ // check that the source is not cleared if active is set to false
+ {
+ QDeclarativeComponent component(&engine, TEST_FILE("active.3.qml"));
+ QObject *object = component.create();
+ QVERIFY(object != 0);
+ QSGLoader *loader = object->findChild<QSGLoader*>("loader");
+
+ QVERIFY(loader->active() == true); // active is true by default
+ QVERIFY(!loader->source().isEmpty());
+ int currSourceChangedCount = loader->property("sourceChangedCount").toInt();
+ QMetaObject::invokeMethod(object, "doSetInactive");
+ QVERIFY(!loader->source().isEmpty());
+ QCOMPARE(loader->property("sourceChangedCount").toInt(), currSourceChangedCount);
+
+ delete object;
+ }
+
+ // check that the sourceComponent is not cleared if active is set to false
+ {
+ QDeclarativeComponent component(&engine, TEST_FILE("active.4.qml"));
+ QObject *object = component.create();
+ QVERIFY(object != 0);
+ QSGLoader *loader = object->findChild<QSGLoader*>("loader");
+
+ QVERIFY(loader->active() == true); // active is true by default
+ QVERIFY(loader->sourceComponent() != 0);
+ int currSourceComponentChangedCount = loader->property("sourceComponentChangedCount").toInt();
+ QMetaObject::invokeMethod(object, "doSetInactive");
+ QVERIFY(loader->sourceComponent() != 0);
+ QCOMPARE(loader->property("sourceComponentChangedCount").toInt(), currSourceComponentChangedCount);
+
+ delete object;
+ }
+
+ // check that the item is released if active is set to false
+ {
+ QDeclarativeComponent component(&engine, TEST_FILE("active.5.qml"));
+ QObject *object = component.create();
+ QVERIFY(object != 0);
+ QSGLoader *loader = object->findChild<QSGLoader*>("loader");
+
+ QVERIFY(loader->active() == true); // active is true by default
+ QVERIFY(loader->item() != 0);
+ int currItemChangedCount = loader->property("itemChangedCount").toInt();
+ QMetaObject::invokeMethod(object, "doSetInactive");
+ QVERIFY(loader->item() == 0);
+ QCOMPARE(loader->property("itemChangedCount").toInt(), (currItemChangedCount+1));
+
+ delete object;
+ }
+
+ // check that the activeChanged signal is emitted correctly
+ {
+ QDeclarativeComponent component(&engine, TEST_FILE("active.6.qml"));
+ QObject *object = component.create();
+ QVERIFY(object != 0);
+ QSGLoader *loader = object->findChild<QSGLoader*>("loader");
+
+ QVERIFY(loader->active() == true); // active is true by default
+ loader->setActive(true); // no effect
+ QCOMPARE(loader->property("activeChangedCount").toInt(), 0);
+ loader->setActive(false); // change signal should be emitted
+ QCOMPARE(loader->property("activeChangedCount").toInt(), 1);
+ loader->setActive(false); // no effect
+ QCOMPARE(loader->property("activeChangedCount").toInt(), 1);
+ loader->setActive(true); // change signal should be emitted
+ QCOMPARE(loader->property("activeChangedCount").toInt(), 2);
+ loader->setActive(false); // change signal should be emitted
+ QCOMPARE(loader->property("activeChangedCount").toInt(), 3);
+ QMetaObject::invokeMethod(object, "doSetActive");
+ QCOMPARE(loader->property("activeChangedCount").toInt(), 4);
+ QMetaObject::invokeMethod(object, "doSetActive");
+ QCOMPARE(loader->property("activeChangedCount").toInt(), 4);
+ QMetaObject::invokeMethod(object, "doSetInactive");
+ QCOMPARE(loader->property("activeChangedCount").toInt(), 5);
+ loader->setActive(true); // change signal should be emitted
+ QCOMPARE(loader->property("activeChangedCount").toInt(), 6);
+
+ delete object;
+ }
+}
+
+void tst_QSGLoader::initialPropertyValues_data()
+{
+ QTest::addColumn<QUrl>("qmlFile");
+ QTest::addColumn<QStringList>("expectedWarnings");
+ QTest::addColumn<QStringList>("propertyNames");
+ QTest::addColumn<QVariantList>("propertyValues");
+
+ QTest::newRow("source url with value set in onLoaded, initially active = true") << TEST_FILE("initialPropertyValues.1.qml")
+ << QStringList()
+ << (QStringList() << "initialValue" << "behaviorCount")
+ << (QVariantList() << 1 << 1);
+
+ QTest::newRow("set source with initial property values specified, active = true") << TEST_FILE("initialPropertyValues.2.qml")
+ << QStringList()
+ << (QStringList() << "initialValue" << "behaviorCount")
+ << (QVariantList() << 2 << 0);
+
+ QTest::newRow("set source with initial property values specified, active = false") << TEST_FILE("initialPropertyValues.3.qml")
+ << (QStringList() << QString(QLatin1String("file://") + TEST_FILE("initialPropertyValues.3.qml").toLocalFile() + QLatin1String(":16: TypeError: Cannot read property 'canary' of null")))
+ << (QStringList())
+ << (QVariantList());
+
+ QTest::newRow("set source with initial property values specified, active = false, with active set true later") << TEST_FILE("initialPropertyValues.4.qml")
+ << QStringList()
+ << (QStringList() << "initialValue" << "behaviorCount")
+ << (QVariantList() << 4 << 0);
+
+ QTest::newRow("set source without initial property values specified, active = true") << TEST_FILE("initialPropertyValues.5.qml")
+ << QStringList()
+ << (QStringList() << "initialValue" << "behaviorCount")
+ << (QVariantList() << 0 << 0);
+
+ QTest::newRow("set source with initial property values specified with binding, active = true") << TEST_FILE("initialPropertyValues.6.qml")
+ << QStringList()
+ << (QStringList() << "initialValue" << "behaviorCount")
+ << (QVariantList() << 6 << 0);
+
+ QTest::newRow("ensure initial property value semantics mimic createObject") << TEST_FILE("initialPropertyValues.7.qml")
+ << QStringList()
+ << (QStringList() << "loaderValue" << "createObjectValue")
+ << (QVariantList() << 1 << 1);
+}
+
+void tst_QSGLoader::initialPropertyValues()
+{
+ QFETCH(QUrl, qmlFile);
+ QFETCH(QStringList, expectedWarnings);
+ QFETCH(QStringList, propertyNames);
+ QFETCH(QVariantList, propertyValues);
+
+ foreach (const QString &warning, expectedWarnings)
+ QTest::ignoreMessage(QtWarningMsg, warning.toAscii().constData());
+
+ QDeclarativeComponent component(&engine, qmlFile);
+ QObject *object = component.create();
+ QVERIFY(object != 0);
+
+ for (int i = 0; i < propertyNames.size(); ++i)
+ QCOMPARE(object->property(propertyNames.at(i).toAscii().constData()), propertyValues.at(i));
+
+ delete object;
+}
+
+void tst_QSGLoader::initialPropertyValuesBinding()
+{
+ QDeclarativeComponent component(&engine, TEST_FILE("initialPropertyValues.binding.qml"));
+ QObject *object = component.create();
+ QVERIFY(object != 0);
+
+ QVERIFY(object->setProperty("bindable", QVariant(8)));
+ QCOMPARE(object->property("canaryValue").toInt(), 8);
+
+ delete object;
+}
+
+void tst_QSGLoader::initialPropertyValuesError_data()
+{
+ QTest::addColumn<QUrl>("qmlFile");
+ QTest::addColumn<QStringList>("expectedWarnings");
+
+ QTest::newRow("invalid initial property values object") << TEST_FILE("initialPropertyValues.error.1.qml")
+ << (QStringList() << QString(TEST_FILE("initialPropertyValues.error.1.qml").toString() + ":6:5: QML Loader: setSource: value is not an object"));
+
+ QTest::newRow("nonexistent source url") << TEST_FILE("initialPropertyValues.error.2.qml")
+ << (QStringList() << QString(TEST_FILE("NonexistentSourceComponent.qml").toString() + ": File not found"));
+
+ QTest::newRow("invalid source url") << TEST_FILE("initialPropertyValues.error.3.qml")
+ << (QStringList() << QString(TEST_FILE("InvalidSourceComponent.qml").toString() + ":5:1: Syntax error"));
+
+ QTest::newRow("invalid initial property values object with invalid property access") << TEST_FILE("initialPropertyValues.error.4.qml")
+ << (QStringList() << QString(TEST_FILE("initialPropertyValues.error.4.qml").toString() + ":7:5: QML Loader: setSource: value is not an object")
+ << QString(TEST_FILE("initialPropertyValues.error.4.qml").toString() + ":5: TypeError: Cannot read property 'canary' of null"));
+}
+
+void tst_QSGLoader::initialPropertyValuesError()
+{
+ QFETCH(QUrl, qmlFile);
+ QFETCH(QStringList, expectedWarnings);
+
+ foreach (const QString &warning, expectedWarnings)
+ QTest::ignoreMessage(QtWarningMsg, warning.toUtf8().constData());
+
+ QDeclarativeComponent component(&engine, qmlFile);
+ QObject *object = component.create();
+ QVERIFY(object != 0);
+ QSGLoader *loader = object->findChild<QSGLoader*>("loader");
+ QVERIFY(loader != 0);
+ QVERIFY(loader->item() == 0);
+ delete object;
+}
+
// QTBUG-9241
void tst_QSGLoader::deleteComponentCrash()
{