Add Instantiator type to QtQml
authorAlan Alpert <aalpert@rim.com>
Mon, 24 Dec 2012 06:57:10 +0000 (22:57 -0800)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Wed, 20 Mar 2013 09:32:55 +0000 (10:32 +0100)
Provides a dynamic instantiation type which is not tied to visual items.

Change-Id: I42f7332b29b752dcc94979b6e0ec191fc76b96ef
Reviewed-by: Alan Alpert <aalpert@blackberry.com>
Reviewed-by: Gabriel de Dietrich <gabriel.dedietrich@digia.com>
Reviewed-by: Lars Knoll <lars.knoll@digia.com>

13 files changed:
src/qml/qml/qqmlengine.cpp
src/qml/types/qqmlinstantiator.cpp [new file with mode: 0644]
src/qml/types/qqmlinstantiator_p.h [new file with mode: 0644]
src/qml/types/qqmlinstantiator_p_p.h [new file with mode: 0644]
src/qml/types/types.pri
tests/auto/qml/qml.pro
tests/auto/qml/qqmlinstantiator/data/createMultiple.qml [new file with mode: 0644]
tests/auto/qml/qqmlinstantiator/data/createNone.qml [new file with mode: 0644]
tests/auto/qml/qqmlinstantiator/data/createSingle.qml [new file with mode: 0644]
tests/auto/qml/qqmlinstantiator/data/inactive.qml [new file with mode: 0644]
tests/auto/qml/qqmlinstantiator/data/stringModel.qml [new file with mode: 0644]
tests/auto/qml/qqmlinstantiator/qqmlinstantiator.pro [new file with mode: 0644]
tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp [new file with mode: 0644]

index 96d0192..25ef080 100644 (file)
@@ -96,6 +96,7 @@
 #include <private/qqmldelegatemodel_p.h>
 #include <private/qqmlobjectmodel_p.h>
 #include <private/qquickworkerscript_p.h>
+#include <private/qqmlinstantiator_p.h>
 
 #ifdef Q_OS_WIN // for %APPDATA%
 #include <qt_windows.h>
@@ -183,6 +184,7 @@ void QQmlEnginePrivate::registerBaseTypes(const char *uri, int versionMajor, int
     qmlRegisterType<QQmlBind>(uri, versionMajor, versionMinor,"Binding");
     qmlRegisterType<QQmlConnections>(uri, versionMajor, versionMinor,"Connections");
     qmlRegisterType<QQmlTimer>(uri, versionMajor, versionMinor,"Timer");
+    qmlRegisterType<QQmlInstantiator>(uri, versionMajor, (versionMinor < 1 ? 1 : versionMinor), "Instantiator"); //Only available in >=2.1
     qmlRegisterCustomType<QQmlConnections>(uri, versionMajor, versionMinor,"Connections", new QQmlConnectionsParser);
     qmlRegisterType<QQmlInstanceModel>();
 }
diff --git a/src/qml/types/qqmlinstantiator.cpp b/src/qml/types/qqmlinstantiator.cpp
new file mode 100644 (file)
index 0000000..a2a1fa2
--- /dev/null
@@ -0,0 +1,459 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Research In Motion.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/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 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 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 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlinstantiator_p.h"
+#include "qqmlinstantiator_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
+
+QQmlInstantiatorPrivate::QQmlInstantiatorPrivate()
+    : componentComplete(true)
+    , active(true)
+    , async(false)
+    , ownModel(false)
+    , model(QVariant(1))
+    , instanceModel(0)
+    , delegate(0)
+{
+}
+
+QQmlInstantiatorPrivate::~QQmlInstantiatorPrivate()
+{
+    qDeleteAll(objects);
+}
+
+void QQmlInstantiatorPrivate::clear()
+{
+    Q_Q(QQmlInstantiator);
+    if (!instanceModel)
+        return;
+    if (!objects.count())
+        return;
+
+    for (int i=0; i < objects.count(); i++) {
+        q->objectRemoved(i, objects[i]);
+        instanceModel->release(objects[i]);
+    }
+    objects.clear();
+    q->objectChanged();
+}
+
+void QQmlInstantiatorPrivate::regenerate()
+{
+    Q_Q(QQmlInstantiator);
+    if (!componentComplete)
+        return;
+
+    int prevCount = q->count();
+
+    clear();
+
+    if (!active || !instanceModel || !instanceModel->count() || !instanceModel->isValid()) {
+        if (prevCount)
+            q->countChanged();
+        return;
+    }
+
+    for (int i = 0; i < instanceModel->count(); i++) {
+        QObject *object = instanceModel->object(i, 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 QQmlInstantiatorPrivate::_q_createdItem(int idx, QObject* item)
+{
+    Q_Q(QQmlInstantiator);
+    if (objects.contains(item)) //Case when it was created synchronously in regenerate
+        return;
+    item->setParent(q);
+    objects << item;
+    if (objects.count() == 1)
+        q->objectChanged();
+    q->objectAdded(idx, item);
+}
+
+void QQmlInstantiatorPrivate::_q_modelUpdated(const QQmlChangeSet &changeSet, bool reset)
+{
+    Q_Q(QQmlInstantiator);
+
+    if (componentComplete)
+        return;
+
+    if (reset) {
+        regenerate();
+        if (changeSet.difference() != 0)
+            q->countChanged();
+        return;
+    }
+
+    int difference = 0;
+    QHash<int, QVector<QPointer<QObject> > > moved;
+    foreach (const QQmlChangeSet::Remove &remove, changeSet.removes()) {
+        int index = qMin(remove.index, objects.count());
+        int count = qMin(remove.index + remove.count, objects.count()) - index;
+        if (remove.isMove()) {
+            moved.insert(remove.moveId, objects.mid(index, count));
+            objects.erase(
+                    objects.begin() + index,
+                    objects.begin() + index + count);
+        } else while (count--) {
+            QObject *obj = objects.at(index);
+            objects.remove(index);
+            q->objectRemoved(index, obj);
+            if (obj)
+                instanceModel->release(obj);
+        }
+
+        difference -= remove.count;
+    }
+
+    foreach (const QQmlChangeSet::Insert &insert, changeSet.inserts()) {
+        int index = qMin(insert.index, objects.count());
+        if (insert.isMove()) {
+            QVector<QPointer<QObject> > movedObjects = moved.value(insert.moveId);
+            objects = objects.mid(0, index) + movedObjects + objects.mid(index);
+        } else for (int i = 0; i < insert.count; ++i) {
+            int modelIndex = index + i;
+            QObject* obj = instanceModel->object(i, async);
+            if (obj)
+                _q_createdItem(modelIndex, obj);
+        }
+        difference += insert.count;
+    }
+
+    if (difference != 0)
+        q->countChanged();
+}
+
+void QQmlInstantiatorPrivate::makeModel()
+{
+    Q_Q(QQmlInstantiator);
+    QQmlDelegateModel* delegateModel = new QQmlDelegateModel(qmlContext(q));
+    instanceModel = delegateModel;
+    ownModel = true;
+    delegateModel->setDelegate(delegate);
+    delegateModel->classBegin(); //Pretend it was made in QML
+    if (componentComplete)
+        delegateModel->componentComplete();
+}
+
+
+/*!
+    \qmltype Instantiator
+    \instantiates QQmlInstantiator
+    \inqmlmodule QtQml 2
+    \brief Dynamically creates objects
+
+    A Instantiator can be used to control the dynamic creation of objects, or to dynamically
+    create multiple objects from a template.
+
+    The Instantiator element will manage the objects it creates. Those objects are parented to the
+    Instantiator and can also be deleted by the Instantiator if the Instantiator's properties change. Objects
+    can also be destroyed dynamically through other means, and the Instantiator will not recreate
+    them unless the properties of the Instantiator change.
+
+*/
+QQmlInstantiator::QQmlInstantiator(QObject *parent)
+    : QObject(*(new QQmlInstantiatorPrivate), parent)
+{
+}
+
+QQmlInstantiator::~QQmlInstantiator()
+{
+}
+
+/*!
+    \qmlsignal QtQml2::Instantiator::onObjectAdded(int index, QtObject object)
+
+    This handler is called when an object is added to the Instantiator. The \a index
+    parameter holds the index which the object has been given, and the \a object
+    parameter holds the \l QtObject that has been added.
+*/
+
+/*!
+    \qmlsignal QtQml2::Instantiator::onObjectRemoved(int index, QtObject object)
+
+    This handler is called when an object is added to 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 handler is called.
+*/
+/*!
+    \qmlproperty bool QtQml2::Instantiator::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 QQmlInstantiator::isActive() const
+{
+    Q_D(const QQmlInstantiator);
+    return d->active;
+}
+
+void QQmlInstantiator::setActive(bool newVal)
+{
+    Q_D(QQmlInstantiator);
+    if (newVal == d->active)
+        return;
+    d->active = newVal;
+    emit activeChanged();
+    d->regenerate();
+}
+
+/*!
+    \qmlproperty bool QtQml2::Instantiator::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 QQmlInstantiator::isAsync() const
+{
+    Q_D(const QQmlInstantiator);
+    return d->async;
+}
+
+void QQmlInstantiator::setAsync(bool newVal)
+{
+    Q_D(QQmlInstantiator);
+    if (newVal == d->async)
+        return;
+    d->async = newVal;
+    emit asynchronousChanged();
+}
+
+
+/*!
+    \qmlproperty int QtQml2::Instantiator::count
+
+    The number of objects the Instantiator is currently managing.
+*/
+
+int QQmlInstantiator::count() const
+{
+    Q_D(const QQmlInstantiator);
+    return d->objects.count();
+}
+
+/*!
+    \qmlproperty QtQml2::Component QtQml2::Instantiator::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* QQmlInstantiator::delegate()
+{
+    Q_D(QQmlInstantiator);
+    return d->delegate;
+}
+
+void QQmlInstantiator::setDelegate(QQmlComponent* c)
+{
+    Q_D(QQmlInstantiator);
+    if (c == d->delegate)
+        return;
+
+    d->delegate = c;
+    emit delegateChanged();
+
+    if (!d->ownModel)
+        return;
+
+    if (QQmlDelegateModel *dModel = qobject_cast<QQmlDelegateModel*>(d->instanceModel))
+        dModel->setDelegate(c);
+    if (d->componentComplete)
+        d->regenerate();
+}
+
+/*!
+    \qmlproperty variant QtQml2::Instantiator::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 QQmlInstantiator::model() const
+{
+    Q_D(const QQmlInstantiator);
+    return d->model;
+}
+
+void QQmlInstantiator::setModel(const QVariant &v)
+{
+    Q_D(QQmlInstantiator);
+    if (d->model == v)
+        return;
+
+    d->model = v;
+    //Don't actually set model until componentComplete in case it wants to create its delegates immediately
+    if (!d->componentComplete)
+        return;
+
+    QQmlInstanceModel *prevModel = d->instanceModel;
+    QObject *object = qvariant_cast<QObject*>(v);
+    QQmlInstanceModel *vim = 0;
+    if (object && (vim = qobject_cast<QQmlInstanceModel *>(object))) {
+        if (d->ownModel) {
+            delete d->instanceModel;
+            d->ownModel = false;
+        }
+        d->instanceModel = vim;
+    } else if (v != QVariant(0)){
+        if (!d->ownModel)
+            d->makeModel();
+
+        if (QQmlDelegateModel *dataModel = qobject_cast<QQmlDelegateModel *>(d->instanceModel))
+            dataModel->setModel(v);
+    }
+
+    if (d->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->instanceModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)),
+                this, SLOT(_q_modelUpdated(QQmlChangeSet,bool)));
+        connect(d->instanceModel, SIGNAL(createdItem(int,QObject*)), this, SLOT(_q_createdItem(int,QObject*)));
+        //connect(d->instanceModel, SIGNAL(initItem(int,QObject*)), this, SLOT(initItem(int,QObject*)));
+    }
+
+    d->regenerate();
+    emit modelChanged();
+}
+
+/*!
+    \qmlproperty QtQml2::QtObject QtQml2::Instantiator::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 *QQmlInstantiator::object() const
+{
+    Q_D(const QQmlInstantiator);
+    if (d->objects.count())
+        return d->objects[0];
+    return 0;
+}
+
+/*!
+    \qmlmethod QtQml2::QtObject QtQml2::Instantiator::objectAt
+
+    Returns a reference to the object with the given \a index.
+*/
+QObject *QQmlInstantiator::objectAt(int index) const
+{
+    Q_D(const QQmlInstantiator);
+    if (index >= 0 && index < d->objects.count())
+        return d->objects[index];
+    return 0;
+}
+
+/*!
+ \internal
+*/
+void QQmlInstantiator::classBegin()
+{
+    Q_D(QQmlInstantiator);
+    d->componentComplete = false;
+}
+
+/*!
+ \internal
+*/
+void QQmlInstantiator::componentComplete()
+{
+    Q_D(QQmlInstantiator);
+    d->componentComplete = true;
+    if (d->ownModel) {
+        static_cast<QQmlDelegateModel*>(d->instanceModel)->componentComplete();
+        d->regenerate();
+    } else {
+        QVariant realModel = d->model;
+        d->model = QVariant(0);
+        setModel(realModel); //If realModel == d->model this won't do anything, but that's fine since the model's 0
+        //setModel calls regenerate
+    }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qqmlinstantiator_p.cpp"
diff --git a/src/qml/types/qqmlinstantiator_p.h b/src/qml/types/qqmlinstantiator_p.h
new file mode 100644 (file)
index 0000000..d4eb793
--- /dev/null
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Research In Motion.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/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 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 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 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLINSTANTIATOR_P_H
+#define QQMLINSTANTIATOR_P_H
+
+#include <QtQml/qqmlcomponent.h>
+#include <QtQml/qqmlparserstatus.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlInstantiatorPrivate;
+class Q_AUTOTEST_EXPORT QQmlInstantiator : public QObject, 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:
+    QQmlInstantiator(QObject *parent = 0);
+    virtual ~QQmlInstantiator();
+
+    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);
+
+private:
+    Q_DISABLE_COPY(QQmlInstantiator)
+    Q_DECLARE_PRIVATE(QQmlInstantiator)
+    Q_PRIVATE_SLOT(d_func(), void _q_createdItem(int, QObject *))
+    Q_PRIVATE_SLOT(d_func(), void _q_modelUpdated(const QQmlChangeSet &, bool))
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLCREATOR_P_H
diff --git a/src/qml/types/qqmlinstantiator_p_p.h b/src/qml/types/qqmlinstantiator_p_p.h
new file mode 100644 (file)
index 0000000..7945929
--- /dev/null
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Research In Motion.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/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 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 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 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLINSTANTIATOR_P_P_H
+#define QQMLINSTANTIATOR_P_P_H
+
+//
+//  W A R N I N G
+//  -------------
+//
+// This file is not part of the Qt API.  It exists purely as an
+// implementation detail.  This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qqmlinstantiator_p.h"
+#include <QObject>
+#include <private/qobject_p.h>
+#include <private/qqmlchangeset_p.h>
+#include <private/qqmlobjectmodel_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlInstantiatorPrivate : public QObjectPrivate
+{
+    Q_DECLARE_PUBLIC(QQmlInstantiator)
+
+public:
+    QQmlInstantiatorPrivate();
+    ~QQmlInstantiatorPrivate();
+
+    void clear();
+    void regenerate();
+    void makeModel();
+    void _q_createdItem(int, QObject *);
+    void _q_modelUpdated(const QQmlChangeSet &, bool);
+
+    bool componentComplete;
+    bool active;
+    bool async;
+    bool ownModel;
+    QVariant model;
+    QQmlInstanceModel *instanceModel;
+    QQmlComponent *delegate;
+    QVector<QPointer<QObject> > objects;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLCREATOR_P_P_H
index 22e62ea..3e61537 100644 (file)
@@ -8,7 +8,8 @@ SOURCES += \
     $$PWD/qqmlobjectmodel.cpp \
     $$PWD/qqmltimer.cpp \
     $$PWD/qquickpackage.cpp \
-    $$PWD/qquickworkerscript.cpp
+    $$PWD/qquickworkerscript.cpp \
+    $$PWD/qqmlinstantiator.cpp
 
 HEADERS += \
     $$PWD/qqmlbind_p.h \
@@ -22,5 +23,6 @@ HEADERS += \
     $$PWD/qqmlobjectmodel_p.h \
     $$PWD/qqmltimer_p.h \
     $$PWD/qquickpackage_p.h \
-    $$PWD/qquickworkerscript_p.h
-
+    $$PWD/qquickworkerscript_p.h \
+    $$PWD/qqmlinstantiator_p.h \
+    $$PWD/qqmlinstantiator_p_p.h
index 584533c..b973d44 100644 (file)
@@ -53,7 +53,8 @@ PRIVATETESTS += \
     qqmlbundle \
     qrcqml \
     v4 \
-    qqmltimer
+    qqmltimer \
+    qqmlinstantiator
 
 qtHaveModule(widgets) {
     PUBLICTESTS += \
diff --git a/tests/auto/qml/qqmlinstantiator/data/createMultiple.qml b/tests/auto/qml/qqmlinstantiator/data/createMultiple.qml
new file mode 100644 (file)
index 0000000..facfadf
--- /dev/null
@@ -0,0 +1,9 @@
+import QtQml 2.1
+
+Instantiator {
+    model: 10
+    delegate: QtObject {
+        property bool success: true
+        property int idx: index
+    }
+}
diff --git a/tests/auto/qml/qqmlinstantiator/data/createNone.qml b/tests/auto/qml/qqmlinstantiator/data/createNone.qml
new file mode 100644 (file)
index 0000000..275e27c
--- /dev/null
@@ -0,0 +1,12 @@
+import QtQml 2.1
+
+Instantiator {
+    model: 0
+    property bool success: true
+    QtObject {
+        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/qml/qqmlinstantiator/data/createSingle.qml b/tests/auto/qml/qqmlinstantiator/data/createSingle.qml
new file mode 100644 (file)
index 0000000..b04e62e
--- /dev/null
@@ -0,0 +1,8 @@
+import QtQml 2.1
+
+Instantiator {
+    QtObject {
+        property bool success: true
+        property int idx: index
+    }
+}
diff --git a/tests/auto/qml/qqmlinstantiator/data/inactive.qml b/tests/auto/qml/qqmlinstantiator/data/inactive.qml
new file mode 100644 (file)
index 0000000..8f5e21f
--- /dev/null
@@ -0,0 +1,9 @@
+import QtQml 2.1
+
+Instantiator {
+    active: false
+    QtObject {
+        property bool success: true
+        property int idx: index
+    }
+}
diff --git a/tests/auto/qml/qqmlinstantiator/data/stringModel.qml b/tests/auto/qml/qqmlinstantiator/data/stringModel.qml
new file mode 100644 (file)
index 0000000..ede2705
--- /dev/null
@@ -0,0 +1,9 @@
+import QtQml 2.1
+
+Instantiator {
+    model: ["alpha", "beta", "gamma", "delta"]
+    delegate: QtObject {
+        property bool success: index == 1 ? datum.length == 4 : datum.length == 5
+        property string datum: modelData
+    }
+}
diff --git a/tests/auto/qml/qqmlinstantiator/qqmlinstantiator.pro b/tests/auto/qml/qqmlinstantiator/qqmlinstantiator.pro
new file mode 100644 (file)
index 0000000..aa83da1
--- /dev/null
@@ -0,0 +1,12 @@
+CONFIG += testcase
+TARGET = tst_qqmlinstantiator
+macx:CONFIG -= app_bundle
+
+INCLUDEPATH += ../../shared/
+SOURCES += tst_qqmlinstantiator.cpp
+
+include (../../shared/util.pri)
+
+TESTDATA = data/*
+
+QT += core-private gui-private qml-private testlib
diff --git a/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp b/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp
new file mode 100644 (file)
index 0000000..3e90eb2
--- /dev/null
@@ -0,0 +1,198 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Research In Motion.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/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 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 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 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qtest.h>
+#include <QSignalSpy>
+#include <QDebug>
+
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlcomponent.h>
+#include <QtQml/private/qqmlinstantiator_p.h>
+#include "../../shared/util.h"
+
+class tst_qqmlinstantiator: public QQmlDataTest
+{
+    Q_OBJECT
+
+private slots:
+    void createNone();
+    void createSingle();
+    void createMultiple();
+    void stringModel();
+    void activeProperty();
+    void intModelChange();
+};
+
+void tst_qqmlinstantiator::createNone()
+{
+    QQmlEngine engine;
+    QQmlComponent component(&engine, testFileUrl("createNone.qml"));
+    QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator*>(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_qqmlinstantiator::createSingle()
+{
+    QQmlEngine engine;
+    QQmlComponent component(&engine, testFileUrl("createSingle.qml"));
+    QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator*>(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_qqmlinstantiator::createMultiple()
+{
+    QQmlEngine engine;
+    QQmlComponent component(&engine, testFileUrl("createMultiple.qml"));
+    QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator*>(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_qqmlinstantiator::stringModel()
+{
+    QQmlEngine engine;
+    QQmlComponent component(&engine, testFileUrl("stringModel.qml"));
+    QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator*>(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_qqmlinstantiator::activeProperty()
+{
+    QQmlEngine engine;
+    QQmlComponent component(&engine, testFileUrl("inactive.qml"));
+    QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator*>(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_qqmlinstantiator::intModelChange()
+{
+    QQmlEngine engine;
+    QQmlComponent component(&engine, testFileUrl("createMultiple.qml"));
+    QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator*>(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);
+    }
+}
+
+QTEST_MAIN(tst_qqmlinstantiator)
+
+#include "tst_qqmlinstantiator.moc"