summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSean Harmer <sean.harmer@kdab.com>2015-02-15 19:47:25 +0000
committerSean Harmer <sean.harmer@kdab.com>2015-02-28 16:28:39 +0000
commit7ccaf0f9a917f49e1cde551c2a4f4dcff575406c (patch)
tree3dd47490fce2cf99f783b0bad608cf97d84fd6ab
parent66b26f86856ff57713ffab35fb689d087aa91ecc (diff)
Added NodeInstantiator
Hopefully in the long term we can make the renderer smart enough that it can automatically use instancing to render such things. Change-Id: I9f5cfabeea11f5e6b925d5ad2466afa8e601e6f8 Reviewed-by: Sean Harmer <sean.harmer@kdab.com> Reviewed-by: Paul Lemire <paul.lemire@kdab.com>
-rw-r--r--examples/bigmodel-qml/MyEntity.qml52
-rw-r--r--examples/bigmodel-qml/bigmodel-qml.pro6
-rw-r--r--examples/bigmodel-qml/bigmodel-qml.qrc1
-rw-r--r--examples/bigmodel-qml/main.cpp14
-rw-r--r--examples/bigmodel-qml/main.qml58
-rw-r--r--src/quick3d/imports/core/importscore.pro2
-rw-r--r--src/quick3d/imports/core/qt3dquick3dcoreplugin.cpp2
-rw-r--r--src/quick3d/quick3d/items/items.pri7
-rw-r--r--src/quick3d/quick3d/items/quick3dnodeinstantiator.cpp492
-rw-r--r--src/quick3d/quick3d/items/quick3dnodeinstantiator_p.h118
-rw-r--r--src/quick3d/quick3d/items/quick3dnodeinstantiator_p_p.h83
-rw-r--r--tests/auto/auto.pro6
-rw-r--r--tests/auto/quick3d/quick3d.pro6
-rw-r--r--tests/auto/quick3d/quick3dnodeinstantiator/data/createAndRemove.qml20
-rw-r--r--tests/auto/quick3d/quick3dnodeinstantiator/data/createMultiple.qml10
-rw-r--r--tests/auto/quick3d/quick3dnodeinstantiator/data/createNone.qml13
-rw-r--r--tests/auto/quick3d/quick3dnodeinstantiator/data/createSingle.qml9
-rw-r--r--tests/auto/quick3d/quick3dnodeinstantiator/data/inactive.qml10
-rw-r--r--tests/auto/quick3d/quick3dnodeinstantiator/data/stringModel.qml10
-rw-r--r--tests/auto/quick3d/quick3dnodeinstantiator/quick3dnodeinstantiator.pro14
-rw-r--r--tests/auto/quick3d/quick3dnodeinstantiator/stringmodel.h122
-rw-r--r--tests/auto/quick3d/quick3dnodeinstantiator/tst_quick3dnodeinstantiator.cpp223
-rw-r--r--tests/auto/shared/util.cpp130
-rw-r--r--tests/auto/shared/util.h103
-rw-r--r--tests/auto/shared/util.pri5
25 files changed, 1468 insertions, 48 deletions
diff --git a/examples/bigmodel-qml/MyEntity.qml b/examples/bigmodel-qml/MyEntity.qml
new file mode 100644
index 000000000..fd30c1071
--- /dev/null
+++ b/examples/bigmodel-qml/MyEntity.qml
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import Qt3D 2.0
+import Qt3D.Render 2.0
+
+Entity {
+ id: root
+ property alias x: translation.dx
+ property alias y: translation.dy
+ property alias z: translation.dz
+ property alias diffuse: material.diffuse
+
+ components: [
+ Transform { Translate { id: translation } },
+ SphereMesh { radius: 2 },
+ PhongMaterial { id: material }
+ ]
+}
diff --git a/examples/bigmodel-qml/bigmodel-qml.pro b/examples/bigmodel-qml/bigmodel-qml.pro
index e0535a346..7787217f8 100644
--- a/examples/bigmodel-qml/bigmodel-qml.pro
+++ b/examples/bigmodel-qml/bigmodel-qml.pro
@@ -6,7 +6,11 @@ SOURCES += \
main.cpp
OTHER_FILES += \
- main.qml
+ main.qml \
+ MyEntity.qml
RESOURCES += \
bigmodel-qml.qrc
+
+DISTFILES += \
+ MyEntity.qml
diff --git a/examples/bigmodel-qml/bigmodel-qml.qrc b/examples/bigmodel-qml/bigmodel-qml.qrc
index 5f6483ac3..6cc293f70 100644
--- a/examples/bigmodel-qml/bigmodel-qml.qrc
+++ b/examples/bigmodel-qml/bigmodel-qml.qrc
@@ -1,5 +1,6 @@
<RCC>
<qresource prefix="/">
<file>main.qml</file>
+ <file>MyEntity.qml</file>
</qresource>
</RCC>
diff --git a/examples/bigmodel-qml/main.cpp b/examples/bigmodel-qml/main.cpp
index a8d22bcfa..6e76f1f5a 100644
--- a/examples/bigmodel-qml/main.cpp
+++ b/examples/bigmodel-qml/main.cpp
@@ -35,7 +35,7 @@
****************************************************************************/
#include <Qt3DCore/window.h>
-#include <Qt3DRenderer/qrenderaspect.h>
+#include <Qt3DRenderer/QRenderAspect>
#include <Qt3DInput/QInputAspect>
#include <Qt3DQuick/QQmlAspectEngine>
@@ -47,23 +47,11 @@
int main(int argc, char* argv[])
{
QGuiApplication app(argc, argv);
-
- QCommandLineParser parser;
- parser.addPositionalArgument(QStringLiteral("meshfile"), QStringLiteral("The mesh file to load"));
- parser.process(app);
-
- const QStringList meshFileNames = parser.positionalArguments();
- if (meshFileNames.length() == 0) {
- std::cerr << "Please specify mesh files to load" << std::endl;
- return 1;
- }
-
Qt3D::Window view;
Qt3D::Quick::QQmlAspectEngine engine;
engine.aspectEngine()->registerAspect(new Qt3D::QRenderAspect());
engine.aspectEngine()->registerAspect(new Qt3D::QInputAspect());
- engine.qmlEngine()->rootContext()->setContextProperty("_meshFileNames", meshFileNames);
QVariantMap data;
data.insert(QStringLiteral("surface"), QVariant::fromValue(static_cast<QSurface *>(&view)));
data.insert(QStringLiteral("eventSource"), QVariant::fromValue(&view));
diff --git a/examples/bigmodel-qml/main.qml b/examples/bigmodel-qml/main.qml
index 9c8604d2c..9d6a16264 100644
--- a/examples/bigmodel-qml/main.qml
+++ b/examples/bigmodel-qml/main.qml
@@ -41,40 +41,40 @@ import QtQuick 2.2 as QQ2
Entity {
id: sceneRoot
- Camera {
- id: camera
- lens : CameraLens {
- projectionType: CameraLens.PerspectiveProjection
- fieldOfView: 45
- aspectRatio: 16/9
- nearPlane : 0.1
- farPlane : 1000.0
- }
+ Configuration { controlledCamera: mainCamera }
- transform : Transform {
- LookAt {
- position: Qt.vector3d( 0.0, 0.0, -20.0 )
- upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
- viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
- }
- }
- }
+ components: [
+ FrameGraph { activeFrameGraph: ForwardRenderer { camera: mainCamera } }
+ ]
- Configuration {
- controlledCamera: camera
+ Camera {
+ id: mainCamera
+ projectionType: CameraLens.PerspectiveProjection
+ fieldOfView: 22.5
+ aspectRatio: 16 / 9
+ nearPlane: 0.01
+ farPlane: 1000.0
+ position: Qt.vector3d( 0.0, 25.0, 40.0 )
+ viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
+ upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
}
- FrameGraph {
- id : external_forward_renderer
- activeFrameGraph : ForwardRenderer {
- camera: camera
- }
- }
+ NodeInstantiator {
+ id: collection
+ property int count: 64
+ property real spacing: 5
+ property int cols: 8
+ property int _rows: count / cols
- components: [external_forward_renderer]
+ model: count
+ delegate: MyEntity {
+ id: myEntity
+ property real _lightness: 0.2 + 0.7 / collection._rows * Math.floor(index / collection.cols)
+ property real _hue: (index % collection.cols) / collection.cols
- Instantiator {
- model: _meshFileNames
- delegate: SceneLoader { source: modelData; }
+ x: collection.spacing * (index % collection.cols - 0.5 * (collection.cols - 1))
+ z: collection.spacing * (Math.floor(index / collection.cols) - 0.5 * collection._rows)
+ diffuse: Qt.hsla( _hue, 0.5, _lightness, 1.0 )
+ }
}
}
diff --git a/src/quick3d/imports/core/importscore.pro b/src/quick3d/imports/core/importscore.pro
index 887a72147..7cfc2534a 100644
--- a/src/quick3d/imports/core/importscore.pro
+++ b/src/quick3d/imports/core/importscore.pro
@@ -2,7 +2,7 @@ CXX_MODULE = qml
TARGET = quick3dcoreplugin
TARGETPATH = Qt3D
-QT += qml 3dcore 3dquick 3dquick-private
+QT += qml 3dcore 3dcore-private 3dquick 3dquick-private
HEADERS += \
qt3dquick3dcoreplugin.h
diff --git a/src/quick3d/imports/core/qt3dquick3dcoreplugin.cpp b/src/quick3d/imports/core/qt3dquick3dcoreplugin.cpp
index 77eb505de..986431b4a 100644
--- a/src/quick3d/imports/core/qt3dquick3dcoreplugin.cpp
+++ b/src/quick3d/imports/core/qt3dquick3dcoreplugin.cpp
@@ -47,6 +47,7 @@
#include <Qt3DQuick/quick3dentityloader.h>
#include <Qt3DQuick/quick3dtransform.h>
#include <Qt3DQuick/quick3dconfiguration.h>
+#include <private/quick3dnodeinstantiator_p.h>
#include <private/qt3dquick_global_p.h>
QT_BEGIN_NAMESPACE
@@ -61,6 +62,7 @@ void Qt3DQuick3DCorePlugin::registerTypes(const char *uri)
qmlRegisterType<Qt3D::Quick::Quick3DConfiguration>(uri, 2, 0, "Configuration");
qmlRegisterExtendedType<Qt3D::QEntity, Qt3D::Quick::Quick3DEntity>(uri, 2, 0, "Entity");
qmlRegisterType<Qt3D::Quick::Quick3DEntityLoader>(uri, 2, 0, "EntityLoader");
+ qmlRegisterType<Qt3D::Quick::Quick3DNodeInstantiator>(uri, 2, 0, "NodeInstantiator");
qmlRegisterExtendedType<Qt3D::QTransform, Qt3D::Quick::Quick3DTransform>(uri, 2, 0, "Transform");
// Ideally we want to make Node an uncreatable type
// We would need qmlRegisterUncreatableExtendedType for that
diff --git a/src/quick3d/quick3d/items/items.pri b/src/quick3d/quick3d/items/items.pri
index edf00bd5e..0c01fe46f 100644
--- a/src/quick3d/quick3d/items/items.pri
+++ b/src/quick3d/quick3d/items/items.pri
@@ -4,13 +4,16 @@ HEADERS += \
$$PWD/quick3dentityloader.h \
$$PWD/quick3dentityloader_p.h \
$$PWD/quick3dtransform.h \
- $$PWD/quick3dconfiguration.h
+ $$PWD/quick3dconfiguration.h \
+ $$PWD/quick3dnodeinstantiator_p_p.h \
+ $$PWD/quick3dnodeinstantiator_p.h
SOURCES += \
$$PWD/quick3dnode.cpp \
$$PWD/quick3dentity.cpp \
$$PWD/quick3dentityloader.cpp \
$$PWD/quick3dtransform.cpp \
- $$PWD/quick3dconfiguration.cpp
+ $$PWD/quick3dconfiguration.cpp \
+ $$PWD/quick3dnodeinstantiator.cpp
INCLUDEPATH += $$PWD
diff --git a/src/quick3d/quick3d/items/quick3dnodeinstantiator.cpp b/src/quick3d/quick3d/items/quick3dnodeinstantiator.cpp
new file mode 100644
index 000000000..4684bd212
--- /dev/null
+++ b/src/quick3d/quick3d/items/quick3dnodeinstantiator.cpp
@@ -0,0 +1,492 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Research In Motion.
+** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "quick3dnodeinstantiator_p.h"
+#include "quick3dnodeinstantiator_p_p.h"
+
+#include <QtQml/QQmlContext>
+#include <QtQml/QQmlComponent>
+#include <QtQml/QQmlInfo>
+#include <QtQml/QQmlError>
+#include <QtQml/private/qqmlobjectmodel_p.h>
+#include <QtQml/private/qqmldelegatemodel_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3D {
+namespace Quick {
+
+Quick3DNodeInstantiatorPrivate::Quick3DNodeInstantiatorPrivate(QNode *qq)
+ : QNodePrivate(qq)
+ , m_componentComplete(true)
+ , m_effectiveReset(false)
+ , m_active(true)
+ , m_async(false)
+ , m_ownModel(false)
+ , m_model(QVariant(1))
+ , m_instanceModel(0)
+ , m_delegate(0)
+{
+}
+
+Quick3DNodeInstantiatorPrivate::~Quick3DNodeInstantiatorPrivate()
+{
+ qDeleteAll(m_objects);
+}
+
+void Quick3DNodeInstantiatorPrivate::clear()
+{
+ Q_Q(Quick3DNodeInstantiator);
+ if (!m_instanceModel)
+ return;
+ if (!m_objects.count())
+ return;
+
+ for (int i = 0; i < m_objects.count(); i++) {
+ q->objectRemoved(i, m_objects[i]);
+ m_instanceModel->release(m_objects[i]);
+ }
+ m_objects.clear();
+ q->objectChanged();
+}
+
+void Quick3DNodeInstantiatorPrivate::regenerate()
+{
+ Q_Q(Quick3DNodeInstantiator);
+ if (!m_componentComplete)
+ return;
+
+ int prevCount = q->count();
+
+ clear();
+
+ if (!m_active || !m_instanceModel || !m_instanceModel->count() || !m_instanceModel->isValid()) {
+ if (prevCount)
+ q->countChanged();
+ return;
+ }
+
+ for (int i = 0; i < m_instanceModel->count(); i++) {
+ QObject *object = m_instanceModel->object(i, m_async);
+ // If the item was already created we won't get a createdItem
+ if (object)
+ _q_createdItem(i, object);
+ }
+ if (q->count() != prevCount)
+ q->countChanged();
+}
+
+void Quick3DNodeInstantiatorPrivate::_q_createdItem(int idx, QObject *item)
+{
+ Q_Q(Quick3DNodeInstantiator);
+ if (m_objects.contains(item)) //Case when it was created synchronously in regenerate
+ return;
+ item->setParent(q);
+ m_objects.insert(idx, item);
+ if (m_objects.count() == 1)
+ q->objectChanged();
+ q->objectAdded(idx, item);
+}
+
+void Quick3DNodeInstantiatorPrivate::_q_modelUpdated(const QQmlChangeSet &changeSet, bool reset)
+{
+ Q_Q(Quick3DNodeInstantiator);
+
+ if (!m_componentComplete || m_effectiveReset)
+ return;
+
+ if (reset) {
+ regenerate();
+ if (changeSet.difference() != 0)
+ q->countChanged();
+ return;
+ }
+
+ int difference = 0;
+ QHash<int, QVector<QPointer<QObject> > > moved;
+ Q_FOREACH (const QQmlChangeSet::Change &remove, changeSet.removes()) {
+ int index = qMin(remove.index, m_objects.count());
+ int count = qMin(remove.index + remove.count, m_objects.count()) - index;
+ if (remove.isMove()) {
+ moved.insert(remove.moveId, m_objects.mid(index, count));
+ m_objects.erase(
+ m_objects.begin() + index,
+ m_objects.begin() + index + count);
+ } else {
+ while (count--) {
+ QObject *obj = m_objects.at(index);
+ m_objects.remove(index);
+ q->objectRemoved(index, obj);
+ if (obj)
+ m_instanceModel->release(obj);
+ }
+ }
+
+ difference -= remove.count;
+ }
+
+ Q_FOREACH (const QQmlChangeSet::Change &insert, changeSet.inserts()) {
+ int index = qMin(insert.index, m_objects.count());
+ if (insert.isMove()) {
+ QVector<QPointer<QObject> > movedObjects = moved.value(insert.moveId);
+ m_objects = m_objects.mid(0, index) + movedObjects + m_objects.mid(index);
+ } else for (int i = 0; i < insert.count; ++i) {
+ int modelIndex = index + i;
+ QObject *obj = m_instanceModel->object(modelIndex, m_async);
+ if (obj)
+ _q_createdItem(modelIndex, obj);
+ }
+ difference += insert.count;
+ }
+
+ if (difference != 0)
+ q->countChanged();
+}
+
+void Quick3DNodeInstantiatorPrivate::makeModel()
+{
+ Q_Q(Quick3DNodeInstantiator);
+ QQmlDelegateModel* delegateModel = new QQmlDelegateModel(qmlContext(q));
+ m_instanceModel = delegateModel;
+ m_ownModel = true;
+ delegateModel->setDelegate(m_delegate);
+ delegateModel->classBegin(); //Pretend it was made in QML
+ if (m_componentComplete)
+ delegateModel->componentComplete();
+}
+
+/*!
+ \qmltype NodeInstantiator
+ \instantiates Quick3DNodeInstantiator
+ \inqmlmodule Quick3D
+ \brief Dynamically creates nodes
+ \since 5.5
+
+ A NodeInstantiator can be used to control the dynamic creation of nodes, or to dynamically
+ create multiple objects from a template.
+
+ The NodeInstantiator element will manage the objects it creates. Those
+ objects are parented to the Instantiator and can also be deleted by the
+ NodeInstantiator if the NodeInstantiator's properties change. Nodes can
+ also be destroyed dynamically through other means, and the NodeInstantiator
+ will not recreate them unless the properties of the NodeInstantiator
+ change.
+
+*/
+Quick3DNodeInstantiator::Quick3DNodeInstantiator(QNode *parent)
+ : QNode(*(new Quick3DNodeInstantiatorPrivate(this)), parent)
+{
+}
+
+Quick3DNodeInstantiator::~Quick3DNodeInstantiator()
+{
+}
+
+/*!
+ \qmlsignal Quick3D::NodeInstantiator::objectAdded(int index, QtObject node)
+
+ This signal is emitted when a node is added to the NodeInstantiator. The \a index
+ parameter holds the index which the node has been given, and the \a node
+ parameter holds the \l Node that has been added.
+
+ The corresponding handler is \c onNodeAdded.
+*/
+
+/*!
+ \qmlsignal Quick3D::NodeInstantiator::objectRemoved(int index, QtObject object)
+
+ This signal is emitted when an object is removed from the Instantiator. The \a index
+ parameter holds the index which the object had been given, and the \a object
+ parameter holds the \l QtObject that has been removed.
+
+ Do not keep a reference to \a object if it was created by this Instantiator, as
+ in these cases it will be deleted shortly after the signal is handled.
+
+ The corresponding handler is \c onObjectRemoved.
+*/
+/*!
+ \qmlproperty bool Quick3D::NodeInstantiator::active
+
+ When active is true, and the delegate component is ready, the Instantiator will
+ create objects according to the model. When active is false, no objects
+ will be created and any previously created objects will be destroyed.
+
+ Default is true.
+*/
+bool Quick3DNodeInstantiator::isActive() const
+{
+ Q_D(const Quick3DNodeInstantiator);
+ return d->m_active;
+}
+
+void Quick3DNodeInstantiator::setActive(bool newVal)
+{
+ Q_D(Quick3DNodeInstantiator);
+ if (newVal == d->m_active)
+ return;
+ d->m_active = newVal;
+ emit activeChanged();
+ d->regenerate();
+}
+
+/*!
+ \qmlproperty bool Quick3D::NodeInstantiator::asynchronous
+
+ When asynchronous is true the Instantiator will attempt to create objects
+ asynchronously. This means that objects may not be available immediately,
+ even if active is set to true.
+
+ You can use the objectAdded signal to respond to items being created.
+
+ Default is false.
+*/
+bool Quick3DNodeInstantiator::isAsync() const
+{
+ Q_D(const Quick3DNodeInstantiator);
+ return d->m_async;
+}
+
+void Quick3DNodeInstantiator::setAsync(bool newVal)
+{
+ Q_D(Quick3DNodeInstantiator);
+ if (newVal == d->m_async)
+ return;
+ d->m_async = newVal;
+ emit asynchronousChanged();
+}
+
+
+/*!
+ \qmlproperty int Quick3D::NodeInstantiator::count
+
+ The number of objects the Instantiator is currently managing.
+*/
+
+int Quick3DNodeInstantiator::count() const
+{
+ Q_D(const Quick3DNodeInstantiator);
+ return d->m_objects.count();
+}
+
+/*!
+ \qmlproperty QtQml::Component Quick3D::NodeInstantiator::delegate
+ \default
+
+ The component used to create all objects.
+
+ Note that an extra variable, index, will be available inside instances of the
+ delegate. This variable refers to the index of the instance inside the Instantiator,
+ and can be used to obtain the object through the itemAt method of the Instantiator.
+
+ If this property is changed, all instances using the old delegate will be destroyed
+ and new instances will be created using the new delegate.
+*/
+QQmlComponent *Quick3DNodeInstantiator::delegate()
+{
+ Q_D(Quick3DNodeInstantiator);
+ return d->m_delegate;
+}
+
+void Quick3DNodeInstantiator::setDelegate(QQmlComponent *c)
+{
+ Q_D(Quick3DNodeInstantiator);
+ if (c == d->m_delegate)
+ return;
+
+ d->m_delegate = c;
+ emit delegateChanged();
+
+ if (!d->m_ownModel)
+ return;
+
+ if (QQmlDelegateModel *dModel = qobject_cast<QQmlDelegateModel*>(d->m_instanceModel))
+ dModel->setDelegate(c);
+ if (d->m_componentComplete)
+ d->regenerate();
+}
+
+/*!
+ \qmlproperty variant Quick3D::NodeInstantiator::model
+
+ This property can be set to any of the supported \l {qml-data-models}{data models}:
+
+ \list
+ \li A number that indicates the number of delegates to be created by the repeater
+ \li A model (e.g. a ListModel item, or a QAbstractItemModel subclass)
+ \li A string list
+ \li An object list
+ \endlist
+
+ The type of model affects the properties that are exposed to the \l delegate.
+
+ Default value is 1, which creates a single delegate instance.
+
+ \sa {qml-data-models}{Data Models}
+*/
+
+QVariant Quick3DNodeInstantiator::model() const
+{
+ Q_D(const Quick3DNodeInstantiator);
+ return d->m_model;
+}
+
+void Quick3DNodeInstantiator::setModel(const QVariant &v)
+{
+ Q_D(Quick3DNodeInstantiator);
+ if (d->m_model == v)
+ return;
+
+ d->m_model = v;
+ //Don't actually set model until componentComplete in case it wants to create its delegates immediately
+ if (!d->m_componentComplete)
+ return;
+
+ QQmlInstanceModel *prevModel = d->m_instanceModel;
+ QObject *object = qvariant_cast<QObject*>(v);
+ QQmlInstanceModel *vim = 0;
+ if (object && (vim = qobject_cast<QQmlInstanceModel *>(object))) {
+ if (d->m_ownModel) {
+ delete d->m_instanceModel;
+ prevModel = 0;
+ d->m_ownModel = false;
+ }
+ d->m_instanceModel = vim;
+ } else if (v != QVariant(0)){
+ if (!d->m_ownModel)
+ d->makeModel();
+
+ if (QQmlDelegateModel *dataModel = qobject_cast<QQmlDelegateModel *>(d->m_instanceModel)) {
+ d->m_effectiveReset = true;
+ dataModel->setModel(v);
+ d->m_effectiveReset = false;
+ }
+ }
+
+ if (d->m_instanceModel != prevModel) {
+ if (prevModel) {
+ disconnect(prevModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)),
+ this, SLOT(_q_modelUpdated(QQmlChangeSet,bool)));
+ disconnect(prevModel, SIGNAL(createdItem(int,QObject*)), this, SLOT(_q_createdItem(int,QObject*)));
+ //disconnect(prevModel, SIGNAL(initItem(int,QObject*)), this, SLOT(initItem(int,QObject*)));
+ }
+
+ connect(d->m_instanceModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)),
+ this, SLOT(_q_modelUpdated(QQmlChangeSet,bool)));
+ connect(d->m_instanceModel, SIGNAL(createdItem(int,QObject*)), this, SLOT(_q_createdItem(int,QObject*)));
+ //connect(d->m_instanceModel, SIGNAL(initItem(int,QObject*)), this, SLOT(initItem(int,QObject*)));
+ }
+
+ d->regenerate();
+ emit modelChanged();
+}
+
+/*!
+ \qmlproperty QtQml::QtObject Quick3D::NodeInstantiator::object
+
+ This is a reference to the first created object, intended as a convenience
+ for the case where only one object has been created.
+*/
+QObject *Quick3DNodeInstantiator::object() const
+{
+ Q_D(const Quick3DNodeInstantiator);
+ if (d->m_objects.count())
+ return d->m_objects[0];
+ return 0;
+}
+
+/*!
+ \qmlmethod QtQml::QtObject Quick3D::NodeInstantiator::objectAt
+
+ Returns a reference to the object with the given \a index.
+*/
+QObject *Quick3DNodeInstantiator::objectAt(int index) const
+{
+ Q_D(const Quick3DNodeInstantiator);
+ if (index >= 0 && index < d->m_objects.count())
+ return d->m_objects[index];
+ return 0;
+}
+
+/*!
+ \internal
+*/
+void Quick3DNodeInstantiator::classBegin()
+{
+ Q_D(Quick3DNodeInstantiator);
+ d->m_componentComplete = false;
+}
+
+/*!
+ \internal
+*/
+void Quick3DNodeInstantiator::componentComplete()
+{
+ Q_D(Quick3DNodeInstantiator);
+ d->m_componentComplete = true;
+ if (d->m_ownModel) {
+ static_cast<QQmlDelegateModel *>(d->m_instanceModel)->componentComplete();
+ d->regenerate();
+ } else {
+ QVariant realModel = d->m_model;
+ d->m_model = QVariant(0);
+ setModel(realModel); //If realModel == d->m_model this won't do anything, but that's fine since the model's 0
+ //setModel calls regenerate
+ }
+}
+
+void Quick3DNodeInstantiator::copy(const QNode *ref)
+{
+ QNode::copy(ref);
+ const Quick3DNodeInstantiator *instantiator = static_cast<const Quick3DNodeInstantiator*>(ref);
+ // We only need to clone the children as the instantiator itself has no
+ // corresponding backend node type.
+ for (int i = 0; i < instantiator->d_func()->m_objects.size(); ++i) {
+ QNode *n = qobject_cast<QNode *>(instantiator->d_func()->m_objects.at(i));
+ if (!n)
+ continue;
+ QNode *clonedNode = QNode::clone(n);
+ clonedNode->setParent(this);
+ d_func()->m_objects.append(clonedNode);
+ }
+}
+
+} // namespace Quick
+} // namespace Qt3D
+
+QT_END_NAMESPACE
+
+#include "moc_quick3dnodeinstantiator_p.cpp"
diff --git a/src/quick3d/quick3d/items/quick3dnodeinstantiator_p.h b/src/quick3d/quick3d/items/quick3dnodeinstantiator_p.h
new file mode 100644
index 000000000..e4171b303
--- /dev/null
+++ b/src/quick3d/quick3d/items/quick3dnodeinstantiator_p.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Research In Motion.
+** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3D_QUICK_NODEINSTANTIATOR_H
+#define QT3D_QUICK_NODEINSTANTIATOR_H
+
+#include <Qt3DQuick/qt3dquick_global.h>
+#include <Qt3DCore/qnode.h>
+#include <QtQml/qqmlcomponent.h>
+#include <QtQml/qqmlparserstatus.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3D {
+namespace Quick {
+
+class Quick3DNodeInstantiatorPrivate;
+
+class QT3DQUICKSHARED_EXPORT Quick3DNodeInstantiator : public QNode, public QQmlParserStatus
+{
+ Q_OBJECT
+
+ Q_INTERFACES(QQmlParserStatus)
+
+ Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged)
+ Q_PROPERTY(bool asynchronous READ isAsync WRITE setAsync NOTIFY asynchronousChanged)
+ Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged)
+ Q_PROPERTY(int count READ count NOTIFY countChanged)
+ Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
+ Q_PROPERTY(QObject *object READ object NOTIFY objectChanged)
+ Q_CLASSINFO("DefaultProperty", "delegate")
+
+public:
+ Quick3DNodeInstantiator(QNode *parent = 0);
+ ~Quick3DNodeInstantiator();
+
+ bool isActive() const;
+ void setActive(bool newVal);
+
+ bool isAsync() const;
+ void setAsync(bool newVal);
+
+ int count() const;
+
+ QQmlComponent *delegate();
+ void setDelegate(QQmlComponent *c);
+
+ QVariant model() const;
+ void setModel(const QVariant &v);
+
+ QObject *object() const;
+ Q_INVOKABLE QObject *objectAt(int index) const;
+
+ void classBegin();
+ void componentComplete();
+
+Q_SIGNALS:
+ void modelChanged();
+ void delegateChanged();
+ void countChanged();
+ void objectChanged();
+ void activeChanged();
+ void asynchronousChanged();
+
+ void objectAdded(int index, QObject *object);
+ void objectRemoved(int index, QObject *object);
+
+protected:
+ void copy(const QNode *ref) Q_DECL_OVERRIDE;
+
+private:
+ QT3D_CLONEABLE(Quick3DNodeInstantiator)
+ Q_DISABLE_COPY(Quick3DNodeInstantiator)
+ Q_DECLARE_PRIVATE(Quick3DNodeInstantiator)
+ Q_PRIVATE_SLOT(d_func(), void _q_createdItem(int, QObject *))
+ Q_PRIVATE_SLOT(d_func(), void _q_modelUpdated(const QQmlChangeSet &, bool))
+};
+
+} // namespace Quick
+} // namespace Qt3D
+
+QT_END_NAMESPACE
+
+#endif // QT3D_QUICK_NODEINSTANTIATOR_H
diff --git a/src/quick3d/quick3d/items/quick3dnodeinstantiator_p_p.h b/src/quick3d/quick3d/items/quick3dnodeinstantiator_p_p.h
new file mode 100644
index 000000000..274fa752d
--- /dev/null
+++ b/src/quick3d/quick3d/items/quick3dnodeinstantiator_p_p.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Research In Motion.
+** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3D_QUICK_QUICK3DNODEINSTANTIATOR_P_P_H
+#define QT3D_QUICK_QUICK3DNODEINSTANTIATOR_P_P_H
+
+#include <private/qnode_p.h>
+#include <private/qqmlchangeset_p.h>
+#include <private/qqmlobjectmodel_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlComponent;
+
+namespace Qt3D {
+namespace Quick {
+
+class Quick3DNodeInstantiatorPrivate : public QNodePrivate
+{
+ Q_DECLARE_PUBLIC(Quick3DNodeInstantiator)
+
+public:
+ Quick3DNodeInstantiatorPrivate(QNode *qq);
+ ~Quick3DNodeInstantiatorPrivate();
+
+ void clear();
+ void regenerate();
+ void makeModel();
+ void _q_createdItem(int, QObject *);
+ void _q_modelUpdated(const QQmlChangeSet &, bool);
+
+ bool m_componentComplete:1;
+ bool m_effectiveReset:1;
+ bool m_active:1;
+ bool m_async:1;
+ bool m_ownModel:1;
+ QVariant m_model;
+ QQmlInstanceModel *m_instanceModel;
+ QQmlComponent *m_delegate;
+ QVector<QPointer<QObject> > m_objects;
+};
+
+} // namespace Quick
+} // namespace Qt3D
+
+QT_END_NAMESPACE
+
+#endif // QT3D_QUICK_QUICK3DNODEINSTANTIATOR_P_P_H
+
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
index ddf486fbc..43811f84e 100644
--- a/tests/auto/auto.pro
+++ b/tests/auto/auto.pro
@@ -1,4 +1,6 @@
TEMPLATE = subdirs
-SUBDIRS = core \
- render
+SUBDIRS = \
+ core \
+ render \
+ quick3d
diff --git a/tests/auto/quick3d/quick3d.pro b/tests/auto/quick3d/quick3d.pro
new file mode 100644
index 000000000..898265ab6
--- /dev/null
+++ b/tests/auto/quick3d/quick3d.pro
@@ -0,0 +1,6 @@
+TEMPLATE = subdirs
+
+contains(QT_CONFIG, private_tests) {
+ SUBDIRS += \
+ quick3dnodeinstantiator
+}
diff --git a/tests/auto/quick3d/quick3dnodeinstantiator/data/createAndRemove.qml b/tests/auto/quick3d/quick3dnodeinstantiator/data/createAndRemove.qml
new file mode 100644
index 000000000..23084bfb0
--- /dev/null
+++ b/tests/auto/quick3d/quick3dnodeinstantiator/data/createAndRemove.qml
@@ -0,0 +1,20 @@
+import QtQml 2.1
+// To avoid clash with Component.onCompleted
+// TODO: Export Qt3D::QComponent with a different name
+import Qt3D 2.0 as Qt3D
+
+Qt3D.Entity {
+ Qt3D.NodeInstantiator {
+ objectName: "instantiator1"
+ model: model1
+ delegate: Qt3D.Entity {
+ property string datum: model.text
+ }
+ }
+ Component.onCompleted: {
+ model1.add("Delta");
+ model1.add("Gamma");
+ model1.add("Beta");
+ model1.add("Alpha");
+ }
+}
diff --git a/tests/auto/quick3d/quick3dnodeinstantiator/data/createMultiple.qml b/tests/auto/quick3d/quick3dnodeinstantiator/data/createMultiple.qml
new file mode 100644
index 000000000..ad4879c7a
--- /dev/null
+++ b/tests/auto/quick3d/quick3dnodeinstantiator/data/createMultiple.qml
@@ -0,0 +1,10 @@
+import QtQml 2.1
+import Qt3D 2.0
+
+NodeInstantiator {
+ model: 10
+ delegate: Entity {
+ property bool success: true
+ property int idx: index
+ }
+}
diff --git a/tests/auto/quick3d/quick3dnodeinstantiator/data/createNone.qml b/tests/auto/quick3d/quick3dnodeinstantiator/data/createNone.qml
new file mode 100644
index 000000000..c2ea5b650
--- /dev/null
+++ b/tests/auto/quick3d/quick3dnodeinstantiator/data/createNone.qml
@@ -0,0 +1,13 @@
+import QtQml 2.1
+import Qt3D 2.0
+
+NodeInstantiator {
+ model: 0
+ property bool success: true
+ Entity {
+ property bool success: true
+ property int idx: index
+ }
+ onObjectChanged: success = false;//Don't create intermediate objects
+ onCountChanged: success = false;//Don't create intermediate objects
+}
diff --git a/tests/auto/quick3d/quick3dnodeinstantiator/data/createSingle.qml b/tests/auto/quick3d/quick3dnodeinstantiator/data/createSingle.qml
new file mode 100644
index 000000000..2816310ec
--- /dev/null
+++ b/tests/auto/quick3d/quick3dnodeinstantiator/data/createSingle.qml
@@ -0,0 +1,9 @@
+import QtQml 2.1
+import Qt3D 2.0
+
+NodeInstantiator {
+ Entity {
+ property bool success: true
+ property int idx: index
+ }
+}
diff --git a/tests/auto/quick3d/quick3dnodeinstantiator/data/inactive.qml b/tests/auto/quick3d/quick3dnodeinstantiator/data/inactive.qml
new file mode 100644
index 000000000..1edef1e0c
--- /dev/null
+++ b/tests/auto/quick3d/quick3dnodeinstantiator/data/inactive.qml
@@ -0,0 +1,10 @@
+import QtQml 2.1
+import Qt3D 2.0
+
+NodeInstantiator {
+ active: false
+ Entity {
+ property bool success: true
+ property int idx: index
+ }
+}
diff --git a/tests/auto/quick3d/quick3dnodeinstantiator/data/stringModel.qml b/tests/auto/quick3d/quick3dnodeinstantiator/data/stringModel.qml
new file mode 100644
index 000000000..55f536b1f
--- /dev/null
+++ b/tests/auto/quick3d/quick3dnodeinstantiator/data/stringModel.qml
@@ -0,0 +1,10 @@
+import QtQml 2.1
+import Qt3D 2.0
+
+NodeInstantiator {
+ model: ["alpha", "beta", "gamma", "delta"]
+ delegate: Entity {
+ property bool success: index == 1 ? datum.length == 4 : datum.length == 5
+ property string datum: modelData
+ }
+}
diff --git a/tests/auto/quick3d/quick3dnodeinstantiator/quick3dnodeinstantiator.pro b/tests/auto/quick3d/quick3dnodeinstantiator/quick3dnodeinstantiator.pro
new file mode 100644
index 000000000..f690d2c3b
--- /dev/null
+++ b/tests/auto/quick3d/quick3dnodeinstantiator/quick3dnodeinstantiator.pro
@@ -0,0 +1,14 @@
+CONFIG += testcase
+TARGET = tst_qnodeinstantiator
+osx:CONFIG -= app_bundle
+
+INCLUDEPATH += ../../shared/
+SOURCES += \
+ tst_quick3dnodeinstantiator.cpp
+HEADERS += stringmodel.h
+
+include (../../shared/util.pri)
+
+TESTDATA = data/*
+
+QT += core-private gui-private 3dcore 3dquick 3dquick-private testlib
diff --git a/tests/auto/quick3d/quick3dnodeinstantiator/stringmodel.h b/tests/auto/quick3d/quick3dnodeinstantiator/stringmodel.h
new file mode 100644
index 000000000..b429866ff
--- /dev/null
+++ b/tests/auto/quick3d/quick3dnodeinstantiator/stringmodel.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Dmitrii Kosarev aka Kakadu <kakadu.hafanana@gmail.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef STRINGMODEL_H
+#define STRINGMODEL_H
+
+#include <QtCore/QObject>
+#include <QtCore/QAbstractItemModel>
+#include <QtCore/QDebug>
+
+class StringModel : public QAbstractItemModel
+{
+ Q_OBJECT
+ QVector<QString> items;
+ QHash<int, QByteArray> roles;
+ QString name;
+
+public:
+ explicit StringModel(const QString& name) : QAbstractItemModel(), name(name)
+ {
+ roles.insert(555, "text");
+ }
+
+ void drop(int count)
+ {
+ beginRemoveRows(QModelIndex(), 0, count-1);
+ for (int i=0; i<count; i++)
+ items.pop_front();
+ endRemoveRows();
+ }
+
+ Q_INVOKABLE void add(QString s)
+ {
+ beginInsertRows(QModelIndex(), 0, 0);
+ items.push_front(s);
+ endInsertRows();
+ }
+
+ int rowCount(const QModelIndex &) const
+ {
+ return items.count();
+ }
+
+ virtual QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE
+ {
+ return roles;
+ }
+
+ virtual int columnCount(const QModelIndex &) const
+ {
+ return 1;
+ }
+
+ virtual bool hasChildren(const QModelIndex &) const Q_DECL_OVERRIDE
+ {
+ return rowCount(QModelIndex()) > 0;
+ }
+
+ virtual QModelIndex index(int row, int column, const QModelIndex &parent) const
+ {
+ Q_UNUSED(column);
+ if (row>=0 && row<rowCount(parent))
+ return createIndex(row,0);
+ else
+ return QModelIndex();
+ }
+
+ virtual QModelIndex parent(const QModelIndex &) const
+ {
+ return QModelIndex();
+ }
+
+ QVariant data (const QModelIndex & index, int role) const
+ {
+ int row = index.row();
+ if ((row<0) || (row>=items.count()))
+ return QVariant::Invalid;
+
+ switch (role) {
+ case Qt::DisplayRole:
+ case 555:
+ return QVariant::fromValue(items.at(row));
+ default:
+ return QVariant();
+ }
+ }
+};
+
+#endif // STRINGMODEL_H
diff --git a/tests/auto/quick3d/quick3dnodeinstantiator/tst_quick3dnodeinstantiator.cpp b/tests/auto/quick3d/quick3dnodeinstantiator/tst_quick3dnodeinstantiator.cpp
new file mode 100644
index 000000000..37d8ff566
--- /dev/null
+++ b/tests/auto/quick3d/quick3dnodeinstantiator/tst_quick3dnodeinstantiator.cpp
@@ -0,0 +1,223 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Research In Motion.
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "../../shared/util.h"
+#include "stringmodel.h"
+
+#include <qtest.h>
+#include <QSignalSpy>
+#include <QDebug>
+
+#include <Qt3DQuick/private/quick3dnodeinstantiator_p.h>
+
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlcomponent.h>
+#include <QtQml/qqmlcontext.h>
+
+using namespace Qt3D::Quick;
+
+class tst_quick3dnodeinstantiator: public QQmlDataTest
+{
+ Q_OBJECT
+
+private slots:
+ void createNone();
+ void createSingle();
+ void createMultiple();
+ void stringModel();
+ void activeProperty();
+ void intModelChange();
+ void createAndRemove();
+};
+
+void tst_quick3dnodeinstantiator::createNone()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("createNone.qml"));
+ Quick3DNodeInstantiator *instantiator = qobject_cast<Quick3DNodeInstantiator*>(component.create());
+ QVERIFY(instantiator != 0);
+ QCOMPARE(instantiator->isActive(), true);
+ QCOMPARE(instantiator->count(), 0);
+ QCOMPARE(instantiator->property("success").toBool(), true);
+ QVERIFY(instantiator->delegate()->isReady());
+}
+
+void tst_quick3dnodeinstantiator::createSingle()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("createSingle.qml"));
+ Quick3DNodeInstantiator *instantiator = qobject_cast<Quick3DNodeInstantiator*>(component.create());
+ QVERIFY(instantiator != 0);
+ QCOMPARE(instantiator->isActive(), true);
+ QCOMPARE(instantiator->count(), 1);
+ QVERIFY(instantiator->delegate()->isReady());
+
+ QObject *object = instantiator->object();
+ QVERIFY(object);
+ QCOMPARE(object->parent(), instantiator);
+ QCOMPARE(object->property("success").toBool(), true);
+ QCOMPARE(object->property("idx").toInt(), 0);
+}
+
+void tst_quick3dnodeinstantiator::createMultiple()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("createMultiple.qml"));
+ Quick3DNodeInstantiator *instantiator = qobject_cast<Quick3DNodeInstantiator*>(component.create());
+ QVERIFY(instantiator != 0);
+ QCOMPARE(instantiator->isActive(), true);
+ QCOMPARE(instantiator->count(), 10);
+
+ for (int i = 0; i < 10; i++) {
+ QObject *object = instantiator->objectAt(i);
+ QVERIFY(object);
+ QCOMPARE(object->parent(), instantiator);
+ QCOMPARE(object->property("success").toBool(), true);
+ QCOMPARE(object->property("idx").toInt(), i);
+ }
+}
+
+void tst_quick3dnodeinstantiator::stringModel()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("stringModel.qml"));
+ Quick3DNodeInstantiator *instantiator = qobject_cast<Quick3DNodeInstantiator*>(component.create());
+ QVERIFY(instantiator != 0);
+ QCOMPARE(instantiator->isActive(), true);
+ QCOMPARE(instantiator->count(), 4);
+
+ for (int i = 0; i < 4; i++) {
+ QObject *object = instantiator->objectAt(i);
+ QVERIFY(object);
+ QCOMPARE(object->parent(), instantiator);
+ QCOMPARE(object->property("success").toBool(), true);
+ }
+}
+
+void tst_quick3dnodeinstantiator::activeProperty()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("inactive.qml"));
+ Quick3DNodeInstantiator *instantiator = qobject_cast<Quick3DNodeInstantiator*>(component.create());
+ QVERIFY(instantiator != 0);
+ QSignalSpy activeSpy(instantiator, SIGNAL(activeChanged()));
+ QSignalSpy countSpy(instantiator, SIGNAL(countChanged()));
+ QSignalSpy objectSpy(instantiator, SIGNAL(objectChanged()));
+ QSignalSpy modelSpy(instantiator, SIGNAL(modelChanged()));
+ QCOMPARE(instantiator->isActive(), false);
+ QCOMPARE(instantiator->count(), 0);
+ QVERIFY(instantiator->delegate()->isReady());
+
+ QCOMPARE(activeSpy.count(), 0);
+ QCOMPARE(countSpy.count(), 0);
+ QCOMPARE(objectSpy.count(), 0);
+ QCOMPARE(modelSpy.count(), 0);
+
+ instantiator->setActive(true);
+ QCOMPARE(instantiator->isActive(), true);
+ QCOMPARE(instantiator->count(), 1);
+
+ QCOMPARE(activeSpy.count(), 1);
+ QCOMPARE(countSpy.count(), 1);
+ QCOMPARE(objectSpy.count(), 1);
+ QCOMPARE(modelSpy.count(), 0);
+
+ QObject *object = instantiator->object();
+ QVERIFY(object);
+ QCOMPARE(object->parent(), instantiator);
+ QCOMPARE(object->property("success").toBool(), true);
+ QCOMPARE(object->property("idx").toInt(), 0);
+}
+
+void tst_quick3dnodeinstantiator::intModelChange()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("createMultiple.qml"));
+ Quick3DNodeInstantiator *instantiator = qobject_cast<Quick3DNodeInstantiator*>(component.create());
+ QVERIFY(instantiator != 0);
+ QSignalSpy activeSpy(instantiator, SIGNAL(activeChanged()));
+ QSignalSpy countSpy(instantiator, SIGNAL(countChanged()));
+ QSignalSpy objectSpy(instantiator, SIGNAL(objectChanged()));
+ QSignalSpy modelSpy(instantiator, SIGNAL(modelChanged()));
+ QCOMPARE(instantiator->count(), 10);
+
+ QCOMPARE(activeSpy.count(), 0);
+ QCOMPARE(countSpy.count(), 0);
+ QCOMPARE(objectSpy.count(), 0);
+ QCOMPARE(modelSpy.count(), 0);
+
+ instantiator->setModel(QVariant(2));
+ QCOMPARE(instantiator->count(), 2);
+
+ QCOMPARE(activeSpy.count(), 0);
+ QCOMPARE(countSpy.count(), 1);
+ QCOMPARE(objectSpy.count(), 2);
+ QCOMPARE(modelSpy.count(), 1);
+
+ for (int i = 0; i < 2; i++) {
+ QObject *object = instantiator->objectAt(i);
+ QVERIFY(object);
+ QCOMPARE(object->parent(), instantiator);
+ QCOMPARE(object->property("success").toBool(), true);
+ QCOMPARE(object->property("idx").toInt(), i);
+ }
+}
+
+void tst_quick3dnodeinstantiator::createAndRemove()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("createAndRemove.qml"));
+ StringModel *model = new StringModel("model1");
+ engine.rootContext()->setContextProperty("model1", model);
+ QObject *rootObject = component.create();
+ QVERIFY(rootObject != 0);
+
+ Quick3DNodeInstantiator *instantiator =
+ qobject_cast<Quick3DNodeInstantiator*>(rootObject->findChild<QObject*>("instantiator1"));
+ QVERIFY(instantiator != 0);
+ model->drop(1);
+ QVector<QString> names;
+ names << "Beta" << "Gamma" << "Delta";
+ for (int i = 0; i < 3; i++) {
+ QObject *object = instantiator->objectAt(i);
+ QVERIFY(object);
+ QCOMPARE(object->property("datum").toString(), names[i]);
+ }
+}
+QTEST_MAIN(tst_quick3dnodeinstantiator)
+
+#include "tst_quick3dnodeinstantiator.moc"
diff --git a/tests/auto/shared/util.cpp b/tests/auto/shared/util.cpp
new file mode 100644
index 000000000..37468f467
--- /dev/null
+++ b/tests/auto/shared/util.cpp
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "util.h"
+
+#include <QtQml/QQmlComponent>
+#include <QtQml/QQmlError>
+#include <QtQml/QQmlContext>
+#include <QtQml/QQmlEngine>
+#include <QtCore/QTextStream>
+#include <QtCore/QDebug>
+#include <QtCore/QMutexLocker>
+
+QQmlDataTest *QQmlDataTest::m_instance = 0;
+
+QQmlDataTest::QQmlDataTest() :
+#ifdef QT_TESTCASE_BUILDDIR
+ m_dataDirectory(QTest::qFindTestData("data", QT_QMLTEST_DATADIR, 0, QT_TESTCASE_BUILDDIR)),
+#else
+ m_dataDirectory(QTest::qFindTestData("data", QT_QMLTEST_DATADIR, 0)),
+#endif
+
+ m_dataDirectoryUrl(QUrl::fromLocalFile(m_dataDirectory + QLatin1Char('/')))
+{
+ m_instance = this;
+}
+
+QQmlDataTest::~QQmlDataTest()
+{
+ m_instance = 0;
+}
+
+void QQmlDataTest::initTestCase()
+{
+ QVERIFY2(!m_dataDirectory.isEmpty(), "'data' directory not found");
+ m_directory = QFileInfo(m_dataDirectory).absolutePath();
+ QVERIFY2(QDir::setCurrent(m_directory), qPrintable(QLatin1String("Could not chdir to ") + m_directory));
+}
+
+QString QQmlDataTest::testFile(const QString &fileName) const
+{
+ if (m_directory.isEmpty())
+ qFatal("QQmlDataTest::initTestCase() not called.");
+ QString result = m_dataDirectory;
+ result += QLatin1Char('/');
+ result += fileName;
+ return result;
+}
+
+QByteArray QQmlDataTest::msgComponentError(const QQmlComponent &c,
+ const QQmlEngine *engine /* = 0 */)
+{
+ QString result;
+ const QList<QQmlError> errors = c.errors();
+ QTextStream str(&result);
+ str << "Component '" << c.url().toString() << "' has " << errors.size()
+ << " errors: '";
+ for (int i = 0; i < errors.size(); ++i) {
+ if (i)
+ str << ", '";
+ str << errors.at(i).toString() << '\'';
+
+ }
+ if (!engine)
+ if (QQmlContext *context = c.creationContext())
+ engine = context->engine();
+ if (engine) {
+ str << " Import paths: (" << engine->importPathList().join(QStringLiteral(", "))
+ << ") Plugin paths: (" << engine->pluginPathList().join(QStringLiteral(", "))
+ << ')';
+ }
+ return result.toLocal8Bit();
+}
+
+Q_GLOBAL_STATIC(QMutex, qQmlTestMessageHandlerMutex)
+
+QQmlTestMessageHandler *QQmlTestMessageHandler::m_instance = 0;
+
+void QQmlTestMessageHandler::messageHandler(QtMsgType, const QMessageLogContext &, const QString &message)
+{
+ QMutexLocker locker(qQmlTestMessageHandlerMutex());
+ if (QQmlTestMessageHandler::m_instance)
+ QQmlTestMessageHandler::m_instance->m_messages.push_back(message);
+}
+
+QQmlTestMessageHandler::QQmlTestMessageHandler()
+{
+ QMutexLocker locker(qQmlTestMessageHandlerMutex());
+ Q_ASSERT(!QQmlTestMessageHandler::m_instance);
+ QQmlTestMessageHandler::m_instance = this;
+ m_oldHandler = qInstallMessageHandler(messageHandler);
+}
+
+QQmlTestMessageHandler::~QQmlTestMessageHandler()
+{
+ QMutexLocker locker(qQmlTestMessageHandlerMutex());
+ Q_ASSERT(QQmlTestMessageHandler::m_instance);
+ qInstallMessageHandler(m_oldHandler);
+ QQmlTestMessageHandler::m_instance = 0;
+}
diff --git a/tests/auto/shared/util.h b/tests/auto/shared/util.h
new file mode 100644
index 000000000..12a8933c4
--- /dev/null
+++ b/tests/auto/shared/util.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLTESTUTILS_H
+#define QQMLTESTUTILS_H
+
+#include <QtCore/QDir>
+#include <QtCore/QUrl>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QStringList>
+#include <QtTest/QTest>
+
+QT_FORWARD_DECLARE_CLASS(QQmlComponent)
+QT_FORWARD_DECLARE_CLASS(QQmlEngine)
+
+/* Base class for tests with data that are located in a "data" subfolder. */
+
+class QQmlDataTest : public QObject
+{
+ Q_OBJECT
+public:
+ QQmlDataTest();
+ virtual ~QQmlDataTest();
+
+ QString testFile(const QString &fileName) const;
+ inline QString testFile(const char *fileName) const
+ { return testFile(QLatin1String(fileName)); }
+ inline QUrl testFileUrl(const QString &fileName) const
+ { return QUrl::fromLocalFile(testFile(fileName)); }
+ inline QUrl testFileUrl(const char *fileName) const
+ { return testFileUrl(QLatin1String(fileName)); }
+
+ inline QString dataDirectory() const { return m_dataDirectory; }
+ inline QUrl dataDirectoryUrl() const { return m_dataDirectoryUrl; }
+ inline QString directory() const { return m_directory; }
+
+ static inline QQmlDataTest *instance() { return m_instance; }
+
+ static QByteArray msgComponentError(const QQmlComponent &,
+ const QQmlEngine *engine = 0);
+
+public slots:
+ virtual void initTestCase();
+
+private:
+ static QQmlDataTest *m_instance;
+
+ const QString m_dataDirectory;
+ const QUrl m_dataDirectoryUrl;
+ QString m_directory;
+};
+
+class QQmlTestMessageHandler
+{
+ Q_DISABLE_COPY(QQmlTestMessageHandler)
+public:
+ QQmlTestMessageHandler();
+ ~QQmlTestMessageHandler();
+
+ const QStringList &messages() const { return m_messages; }
+ const QString messageString() const { return m_messages.join(QLatin1Char('\n')); }
+
+ void clear() { m_messages.clear(); }
+
+private:
+ static void messageHandler(QtMsgType, const QMessageLogContext &, const QString &message);
+
+ static QQmlTestMessageHandler *m_instance;
+ QStringList m_messages;
+ QtMessageHandler m_oldHandler;
+};
+
+#endif // QQMLTESTUTILS_H
diff --git a/tests/auto/shared/util.pri b/tests/auto/shared/util.pri
new file mode 100644
index 000000000..8e82dcb33
--- /dev/null
+++ b/tests/auto/shared/util.pri
@@ -0,0 +1,5 @@
+
+HEADERS += $$PWD/util.h
+SOURCES += $$PWD/util.cpp
+
+DEFINES += QT_QMLTEST_DATADIR=\\\"$${_PRO_FILE_PWD_}/data\\\"