diff options
7 files changed, 430 insertions, 30 deletions
diff --git a/examples/declarative/modelviews/visualdatamodel/slideshow.qml b/examples/declarative/modelviews/visualdatamodel/slideshow.qml new file mode 100644 index 0000000000..edf1f0f057 --- /dev/null +++ b/examples/declarative/modelviews/visualdatamodel/slideshow.qml @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Rectangle { + id: root + + property Item displayItem: null + + width: 300; height: 400 + + color: "black" + + VisualDataModel { + id: visualModel + + model: XmlListModel { + source: "http://api.flickr.com/services/feeds/photos_public.gne?format=rss2" + query: "/rss/channel/item" + namespaceDeclarations: "declare namespace media=\"http://search.yahoo.com/mrss/\";" + + XmlRole { name: "imagePath"; query: "media:thumbnail/@url/string()" } + XmlRole { name: "url"; query: "media:content/@url/string()" } + } + + delegate: Item { + id: delegateItem + + width: 76; height: 76 + + Rectangle { + id: image + x: 0; y: 0; width: 76; height: 76 + border.width: 1 + border.color: "white" + color: "black" + + Image { + anchors.fill: parent + anchors.leftMargin: 1 + anchors.topMargin: 1 + + source: imagePath + fillMode: Image.PreserveAspectFit + + } + + MouseArea { + id: clickArea + anchors.fill: parent + + onClicked: root.displayItem = root.displayItem !== delegateItem ? delegateItem : null + } + + states: [ + State { + when: root.displayItem === delegateItem + name: "inDisplay"; + ParentChange { target: image; parent: imageContainer; x: 75; y: 75; width: 150; height: 150 } + PropertyChanges { target: image; z: 2 } + PropertyChanges { target: delegateItem; VisualDataModel.inItems: false } + }, + State { + when: root.displayItem !== delegateItem + name: "inList"; + ParentChange { target: image; parent: delegateItem; x: 2; y: 2; width: 75; height: 75 } + PropertyChanges { target: image; z: 1 } + PropertyChanges { target: delegateItem; VisualDataModel.inItems: true } + } + ] + + transitions: [ + Transition { + from: "inList" + SequentialAnimation { + PropertyAction { target: delegateItem; property: "VisualDataModel.inPersistedItems"; value: true } + ParentAnimation { + target: image; + via: root + NumberAnimation { target: image; properties: "x,y,width,height"; duration: 1000 } + } + } + }, Transition { + from: "inDisplay" + SequentialAnimation { + ParentAnimation { + target: image + NumberAnimation { target: image; properties: "x,y,width,height"; duration: 1000 } + } + PropertyAction { target: delegateItem; property: "VisualDataModel.inPersistedItems"; value: false } + } + } + ] + } + } + } + + + PathView { + id: imagePath + + anchors { left: parent.left; top: imageContainer.bottom; right: parent.right; bottom: parent.bottom } + model: visualModel + + pathItemCount: 7 + path: Path { + startX: -50; startY: 0 + PathQuad { x: 150; y: 50; controlX: 0; controlY: 50 } + PathQuad { x: 350; y: 0; controlX: 300; controlY: 50 } + } + } + + Item { + id: imageContainer + anchors { fill: parent; bottomMargin: 100 } + } +} diff --git a/src/declarative/items/qsgvisualdatamodel.cpp b/src/declarative/items/qsgvisualdatamodel.cpp index e0d353a93f..47edc5d8f0 100644 --- a/src/declarative/items/qsgvisualdatamodel.cpp +++ b/src/declarative/items/qsgvisualdatamodel.cpp @@ -131,14 +131,14 @@ public: void init(); void connectModel(QSGVisualAdaptorModel *model); - QObject *object(Compositor::Group group, int index, bool complete); - QSGItem *item(Compositor::Group group, int index, bool complete); + QObject *object(Compositor::Group group, int index, bool complete, bool reference); void destroy(QObject *object); QSGVisualDataModel::ReleaseFlags release(QObject *object); QString stringValue(Compositor::Group group, int index, const QString &name); int cacheIndexOf(QObject *object) const; void emitCreatedPackage(Compositor::iterator at, QDeclarativePackage *package); - void emitCreatedItem(int index, QSGItem *item) { emit q_func()->createdItem(index, item); } + void emitCreatedItem(Compositor::iterator at, QSGItem *item) { + emit q_func()->createdItem(at.index[m_compositorGroup], item); } void emitDestroyingPackage(QDeclarativePackage *package); void emitDestroyingItem(QSGItem *item) { emit q_func()->destroyingItem(item); } @@ -194,6 +194,7 @@ public: struct { QSGVisualDataGroup *m_cacheItems; QSGVisualDataGroup *m_items; + QSGVisualDataGroup *m_persistedItems; }; QSGVisualDataGroup *m_groups[Compositor::MaximumGroupCount]; }; @@ -341,9 +342,10 @@ public: } void referenceObject() { ++objectRef; } - bool releaseObject() { return --objectRef == 0; } + bool releaseObject() { return --objectRef == 0 && !(groups & Compositor::PersistedFlag); } + bool isObjectReferenced() const { return objectRef == 0 && !(groups & Compositor::PersistedFlag); } - bool isReferenced() const { return objectRef || scriptRef; } + bool isReferenced() const { return objectRef || scriptRef || (groups & Compositor::PersistedFlag); } void Dispose(); @@ -409,7 +411,7 @@ QSGVisualDataModelPrivate::QSGVisualDataModelPrivate(QDeclarativeContext *ctxt) , m_filterGroup(QStringLiteral("items")) , m_cacheItems(0) , m_items(0) - , m_groupCount(2) + , m_groupCount(3) { } @@ -427,11 +429,14 @@ void QSGVisualDataModelPrivate::connectModel(QSGVisualAdaptorModel *model) void QSGVisualDataModelPrivate::init() { Q_Q(QSGVisualDataModel); + m_compositor.setRemoveGroups(Compositor::GroupMask & ~Compositor::PersistedFlag); + m_adaptorModel = new QSGVisualAdaptorModel; QObject::connect(m_adaptorModel, SIGNAL(rootIndexChanged()), q, SIGNAL(rootIndexChanged())); m_items = new QSGVisualDataGroup(QStringLiteral("items"), q, Compositor::Default, q); m_items->setDefaultInclude(true); + m_persistedItems = new QSGVisualDataGroup(QStringLiteral("persistedItems"), q, Compositor::Persisted, q); QSGVisualDataGroupPrivate::get(m_items)->emitters.insert(this); } @@ -477,9 +482,12 @@ void QSGVisualDataModel::componentComplete() int defaultGroups = 0; QStringList groupNames; groupNames.append(QStringLiteral("items")); + groupNames.append(QStringLiteral("persistedItems")); if (QSGVisualDataGroupPrivate::get(d->m_items)->defaultInclude) defaultGroups |= Compositor::DefaultFlag; - for (int i = 2; i < d->m_groupCount; ++i) { + if (QSGVisualDataGroupPrivate::get(d->m_persistedItems)->defaultInclude) + defaultGroups |= Compositor::PersistedFlag; + for (int i = 3; i < d->m_groupCount; ++i) { QString name = d->m_groups[i]->name(); if (name.isEmpty()) { d->m_groups[i] = d->m_groups[d->m_groupCount - 1]; @@ -683,6 +691,16 @@ int QSGVisualDataModel::count() const return d->m_compositor.count(d->m_compositorGroup); } +void QSGVisualDataModelPrivate::destroy(QObject *object) +{ + QObjectPrivate *p = QObjectPrivate::get(object); + Q_ASSERT(p->declarativeData); + QDeclarativeData *data = static_cast<QDeclarativeData*>(p->declarativeData); + if (data->ownContext && data->context) + data->context->clearContext(); + object->deleteLater(); +} + QSGVisualDataModel::ReleaseFlags QSGVisualDataModelPrivate::release(QObject *object) { QSGVisualDataModel::ReleaseFlags stat = 0; @@ -693,12 +711,7 @@ QSGVisualDataModel::ReleaseFlags QSGVisualDataModelPrivate::release(QObject *obj if (cacheIndex != -1) { QSGVisualDataModelCacheItem *cacheItem = m_cache.at(cacheIndex); if (cacheItem->releaseObject()) { - QObjectPrivate *p = QObjectPrivate::get(object); - Q_ASSERT(p->declarativeData); - QDeclarativeData *data = static_cast<QDeclarativeData*>(p->declarativeData); - if (data->ownContext && data->context) - data->context->clearContext(); - object->deleteLater(); + destroy(object); cacheItem->object = 0; stat |= QSGVisualModel::Destroyed; if (!cacheItem->isReferenced()) { @@ -733,7 +746,7 @@ void QSGVisualDataModelPrivate::group_append( QSGVisualDataModelPrivate *d = static_cast<QSGVisualDataModelPrivate *>(property->data); if (d->m_complete) return; - if (d->m_groupCount == 10) { + if (d->m_groupCount == 11) { qmlInfo(d->q_func()) << QSGVisualDataModel::tr("The maximum number of supported VisualDataGroups is 8"); return; } @@ -799,6 +812,29 @@ QSGVisualDataGroup *QSGVisualDataModel::items() } /*! + \qmlproperty VisualDataGroup QtQuick2::VisualDataModel::persistedItems + + This property holds visual data model's persisted items group. + + Items in this group are not destroyed when released by a view, instead they are persisted + until removed from the group. + + An item can be removed from the persistedItems group by setting the + VisualDataModel.inPersistedItems property to false. If the item is not referenced by a view + at that time it will be destroyed. Adding an item to this group will not create a new + instance. + + Items returned by the \l QtQuick2::VisualDataGroup::create() function are automatically added + to this group. +*/ + +QSGVisualDataGroup *QSGVisualDataModel::persistedItems() +{ + Q_D(QSGVisualDataModel); + return d->m_persistedItems; +} + +/*! \qmlproperty string QtQuick2::VisualDataModel::filterOnGroup This property holds the name of the group used to filter the visual data model. @@ -918,13 +954,9 @@ void QSGVisualDataModelPrivate::emitDestroyingPackage(QDeclarativePackage *packa QSGVisualDataGroupPrivate::get(m_groups[i])->destroyingPackage(package); } -QObject *QSGVisualDataModelPrivate::object(Compositor::Group group, int index, bool complete) +QObject *QSGVisualDataModelPrivate::object(Compositor::Group group, int index, bool complete, bool reference) { Q_Q(QSGVisualDataModel); - if (!m_delegate || index < 0 || index >= m_compositor.count(group)) { - qWarning() << "VisualDataModel::item: index out range" << index << m_compositor.count(group); - return 0; - } Compositor::iterator it = m_compositor.find(group, index); QSGVisualDataModelCacheItem *cacheItem = it->inCache() ? m_cache.at(it.cacheIndex) : 0; @@ -970,8 +1002,12 @@ QObject *QSGVisualDataModelPrivate::object(Compositor::Group group, int index, b new QSGVisualDataModelAttachedMetaObject(cacheItem->attached, m_cacheMetaType); cacheItem->attached->emitChanges(); - if (QDeclarativePackage *package = qobject_cast<QDeclarativePackage *>(cacheItem->object)) + if (QDeclarativePackage *package = qobject_cast<QDeclarativePackage *>(cacheItem->object)) { emitCreatedPackage(it, package); + } else if (!reference) { + if (QSGItem *item = qobject_cast<QSGItem *>(cacheItem->object)) + emitCreatedItem(it, item); + } m_completePending = !complete; if (complete) @@ -987,14 +1023,20 @@ QObject *QSGVisualDataModelPrivate::object(Compositor::Group group, int index, b if (index == m_compositor.count(group) - 1 && m_adaptorModel->canFetchMore()) QCoreApplication::postEvent(q, new QEvent(QEvent::UpdateRequest)); - cacheItem->referenceObject(); + if (reference) + cacheItem->referenceObject(); return cacheItem->object; } QSGItem *QSGVisualDataModel::item(int index, bool complete) { Q_D(QSGVisualDataModel); - QObject *object = d->object(d->m_compositorGroup, index, complete); + if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) { + qWarning() << "VisualDataModel::item: index out range" << index << d->m_compositor.count(d->m_compositorGroup); + return 0; + } + + QObject *object = d->object(d->m_compositorGroup, index, complete, true); if (QSGItem *item = qobject_cast<QSGItem *>(object)) return item; if (d->m_completePending) @@ -1251,6 +1293,14 @@ void QSGVisualDataModelPrivate::itemsRemoved( } else { for (; cacheIndex < remove.cacheIndex + remove.count - removedCache; ++cacheIndex) { QSGVisualDataModelCacheItem *cacheItem = m_cache.at(cacheIndex); + if (remove.inGroup(Compositor::Persisted) && cacheItem->objectRef == 0) { + destroy(cacheItem->object); + if (QDeclarativePackage *package = qobject_cast<QDeclarativePackage *>(cacheItem->object)) + emitDestroyingPackage(package); + else if (QSGItem *item = qobject_cast<QSGItem *>(cacheItem->object)) + emitDestroyingItem(item); + cacheItem->object = 0; + } if (remove.groups() == cacheItem->groups && !cacheItem->isReferenced()) { m_compositor.clearFlags(Compositor::Cache, cacheIndex, 1, Compositor::CacheFlag); m_cache.removeAt(cacheIndex); @@ -1742,6 +1792,26 @@ void QSGVisualDataModelAttached::setGroups(const QStringList &groups) It is attached to each instance of the delegate. */ +/*! + \qmlattachedproperty int QtQuick2::VisualDataModel::inPersistedItems + + This attached property holds whether the item belongs to the \l persistedItems VisualDataGroup. + + Changing this property will add or remove the item from the items group. Change with caution + as removing an item from the persistedItems group will destroy the current instance if it is + not referenced by a model. + + It is attached to each instance of the delegate. +*/ + +/*! + \qmlattachedproperty int QtQuick2::VisualDataModel::persistedItemsIndex + + This attached property holds the index of the item in the \l persistedItems VisualDataGroup. + + It is attached to each instance of the delegate. +*/ + void QSGVisualDataModelAttached::emitChanges() { if (m_modelChanged) { @@ -1925,9 +1995,9 @@ void QSGVisualDataGroup::setDefaultInclude(bool include) a delegate \o \b groups A list the of names of groups the item is a member of. This property can be written to change the item's membership. - \o \b inItems Whether the item belongs to the \l {VisualDataModel::items}{items} group. + \o \b inItems Whether the item belongs to the \l {QtQuick2::VisualDataModel::items}{items} group. Writing to this property will add or remove the item from the group. - \o \b itemsIndex The index of the item within the \l {VisualDataModel::items}{items} group. + \o \b itemsIndex The index of the item within the \l {QtQuick2::VisualDataModel::items}{items} group. \o \b {in\i{GroupName}} Whether the item belongs to the dynamic group \i groupName. Writing to this property will add or remove the item from the group. \o \b {\i{groupName}Index} The index of the item within the dynamic group \i groupName. @@ -1969,6 +2039,33 @@ QDeclarativeV8Handle QSGVisualDataGroup::get(int index) } /*! + \qmlmethod QtQuick2::VisualDataGroup::create(int index) + + Returns a reference to the instantiated item at \a index in the group. + + All items returned by create are added to the persistedItems group. Items in this + group remain instantiated when not referenced by any view. +*/ + +QObject *QSGVisualDataGroup::create(int index) +{ + Q_D(QSGVisualDataGroup); + if (!d->model) + return 0; + + QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(d->model); + if (index < 0 || index >= model->m_compositor.count(d->group)) { + qmlInfo(this) << tr("create: index out of range"); + return 0; + } + + QObject *object = model->object(d->group, index, true, false); + if (object) + model->addGroups(d->group, index, 1, Compositor::PersistedFlag); + return object; +} + +/*! \qmlmethod QtQuick2::VisualDataGroup::remove(int index, int count) Removes \a count items starting at \a index from the group. @@ -2332,7 +2429,12 @@ QSGItem *QSGVisualPartsModel::item(int index, bool complete) { QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(m_model); - QObject *object = model->object(m_compositorGroup, index, complete); + if (!model->m_delegate || index < 0 || index >= model->m_compositor.count(m_compositorGroup)) { + qWarning() << "VisualDataModel::item: index out range" << index << model->m_compositor.count(m_compositorGroup); + return 0; + } + + QObject *object = model->object(m_compositorGroup, index, complete, true); if (QDeclarativePackage *package = qobject_cast<QDeclarativePackage *>(object)) { QObject *part = package->part(m_part); diff --git a/src/declarative/items/qsgvisualdatamodel_p.h b/src/declarative/items/qsgvisualdatamodel_p.h index 9b4542d08a..896c51c22a 100644 --- a/src/declarative/items/qsgvisualdatamodel_p.h +++ b/src/declarative/items/qsgvisualdatamodel_p.h @@ -79,6 +79,7 @@ class Q_DECLARATIVE_EXPORT QSGVisualDataModel : public QSGVisualModel, public QD Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate) Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup) Q_PROPERTY(QSGVisualDataGroup *items READ items CONSTANT) + Q_PROPERTY(QSGVisualDataGroup *persistedItems READ persistedItems CONSTANT) Q_PROPERTY(QDeclarativeListProperty<QSGVisualDataGroup> groups READ groups CONSTANT) Q_PROPERTY(QObject *parts READ parts CONSTANT) Q_PROPERTY(QVariant rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged) @@ -120,6 +121,7 @@ public: void resetFilterGroup(); QSGVisualDataGroup *items(); + QSGVisualDataGroup *persistedItems(); QDeclarativeListProperty<QSGVisualDataGroup> groups(); QObject *parts(); @@ -163,6 +165,7 @@ public: void setDefaultInclude(bool include); Q_INVOKABLE QDeclarativeV8Handle get(int index); + Q_INVOKABLE QObject *create(int index); public Q_SLOTS: void remove(QDeclarativeV8Function *); diff --git a/src/declarative/util/qdeclarativelistcompositor.cpp b/src/declarative/util/qdeclarativelistcompositor.cpp index 7beefdaafa..be0d543368 100644 --- a/src/declarative/util/qdeclarativelistcompositor.cpp +++ b/src/declarative/util/qdeclarativelistcompositor.cpp @@ -283,6 +283,7 @@ QDeclarativeListCompositor::QDeclarativeListCompositor() , m_cacheIt(m_end) , m_groupCount(2) , m_defaultFlags(PrependFlag | DefaultFlag) + , m_removeFlags(AppendFlag | PrependFlag | GroupMask) { } @@ -896,7 +897,7 @@ void QDeclarativeListCompositor::listItemsRemoved( const int offset = qMax(0, relativeIndex); int removeCount = qMin(it->count, relativeIndex + removal->count) - offset; it->count -= removeCount; - int removeFlags = it->flags & RemoveFlags; + int removeFlags = it->flags & m_removeFlags; Remove translatedRemoval(it, removeCount, it->flags); for (int i = 0; i < m_groupCount; ++i) { if (it->inGroup(i)) diff --git a/src/declarative/util/qdeclarativelistcompositor_p.h b/src/declarative/util/qdeclarativelistcompositor_p.h index cd2d9b3f5f..60df819884 100644 --- a/src/declarative/util/qdeclarativelistcompositor_p.h +++ b/src/declarative/util/qdeclarativelistcompositor_p.h @@ -65,23 +65,24 @@ QT_BEGIN_NAMESPACE class Q_AUTOTEST_EXPORT QDeclarativeListCompositor { public: - enum { MaximumGroupCount = 10 }; + enum { MaximumGroupCount = 11 }; enum Group { Cache = 0, - Default = 1 + Default = 1, + Persisted = 2 }; enum Flag { CacheFlag = 0x000001, DefaultFlag = 0x000002, + PersistedFlag = 0x000004, GroupMask = 0x00FFFE, PrependFlag = 0x100000, AppendFlag = 0x200000, - MovedFlag = 0x400000, - RemoveFlags = GroupMask | PrependFlag | AppendFlag + MovedFlag = 0x400000 }; class Range @@ -215,6 +216,7 @@ public: void setDefaultGroups(int groups) { m_defaultFlags = groups | PrependFlag; } void setDefaultGroup(Group group) { m_defaultFlags |= (1 << group); } void clearDefaultGroup(Group group) { m_defaultFlags &= ~(1 << group); } + void setRemoveGroups(int groups) { m_removeFlags = PrependFlag | AppendFlag | groups; } void setGroupCount(int count); int count(Group group) const; @@ -272,6 +274,7 @@ private: iterator m_cacheIt; int m_groupCount; int m_defaultFlags; + int m_removeFlags; inline Range *insert(Range *before, void *list, int index, int count, int flags); inline Range *erase(Range *range); diff --git a/tests/auto/declarative/qsgvisualdatamodel/data/create.qml b/tests/auto/declarative/qsgvisualdatamodel/data/create.qml new file mode 100644 index 0000000000..36ea3baf76 --- /dev/null +++ b/tests/auto/declarative/qsgvisualdatamodel/data/create.qml @@ -0,0 +1,22 @@ +import QtQuick 2.0 + +ListView { + width: 200 + height: 200 + + model: VisualDataModel { + id: visualModel + + model: myModel + delegate: Item { + id: delegate + objectName: "delegate" + width: 200 + height: 20 + + property bool destroyed: false + + Component.onDestruction: destroyed = true + } + } +} diff --git a/tests/auto/declarative/qsgvisualdatamodel/tst_qsgvisualdatamodel.cpp b/tests/auto/declarative/qsgvisualdatamodel/tst_qsgvisualdatamodel.cpp index 50e1b8f9df..63e40cdb60 100644 --- a/tests/auto/declarative/qsgvisualdatamodel/tst_qsgvisualdatamodel.cpp +++ b/tests/auto/declarative/qsgvisualdatamodel/tst_qsgvisualdatamodel.cpp @@ -135,6 +135,7 @@ private slots: void move(); void groups(); void get(); + void create(); private: template <int N> void groups_verify( @@ -1410,6 +1411,119 @@ void tst_qsgvisualdatamodel::get() } } +void tst_qsgvisualdatamodel::create() +{ + QSGView view; + + SingleRoleModel model; + model.list = QStringList() + << "one" + << "two" + << "three" + << "four" + << "five" + << "six" + << "seven" + << "eight" + << "nine" + << "ten" + << "eleven" + << "twelve" + << "thirteen" + << "fourteen" + << "fifteen" + << "sixteen" + << "seventeen" + << "eighteen" + << "nineteen" + << "twenty"; + + QDeclarativeContext *ctxt = view.rootContext(); + ctxt->setContextProperty("myModel", &model); + + view.setSource(QUrl::fromLocalFile(SRCDIR "/data/create.qml")); + + QSGListView *listview = qobject_cast<QSGListView*>(view.rootObject()); + QVERIFY(listview != 0); + + QSGItem *contentItem = listview->contentItem(); + QVERIFY(contentItem != 0); + + QSGVisualDataModel *visualModel = qobject_cast<QSGVisualDataModel *>(qvariant_cast<QObject *>(listview->model())); + QVERIFY(visualModel); + + QCOMPARE(listview->count(), 20); + + QSGItem *delegate; + + // Request an item instantiated by the view. + QVERIFY(findItem<QSGItem>(contentItem, "delegate", 1)); + QVERIFY(delegate = qobject_cast<QSGItem *>(evaluate<QObject *>(visualModel, "items.create(1)"))); + QCOMPARE(delegate, findItem<QSGItem>(contentItem, "delegate", 1)); + QCOMPARE(evaluate<bool>(delegate, "VisualDataModel.inPersistedItems"), true); + QCOMPARE(evaluate<int>(visualModel, "persistedItems.count"), 1); + + evaluate<void>(delegate, "VisualDataModel.inPersistedItems = false"); + QCOMPARE(listview->count(), 20); + QCOMPARE(evaluate<bool>(delegate, "destroyed"), false); + QCOMPARE(evaluate<bool>(delegate, "VisualDataModel.inPersistedItems"), false); + QCOMPARE(evaluate<int>(visualModel, "persistedItems.count"), 0); + + // Request an item not instantiated by the view. + QVERIFY(!findItem<QSGItem>(contentItem, "delegate", 15)); + QVERIFY(delegate = qobject_cast<QSGItem *>(evaluate<QObject *>(visualModel, "items.create(15)"))); + QCOMPARE(delegate, findItem<QSGItem>(contentItem, "delegate", 15)); + QCOMPARE(evaluate<bool>(delegate, "VisualDataModel.inPersistedItems"), true); + QCOMPARE(evaluate<int>(visualModel, "persistedItems.count"), 1); + + evaluate<void>(visualModel, "persistedItems.remove(0)"); + QCOMPARE(evaluate<bool>(delegate, "destroyed"), true); + QCOMPARE(evaluate<int>(visualModel, "persistedItems.count"), 0); + + // Request an item not instantiated by the view, then scroll the view so it will request it. + QVERIFY(!findItem<QSGItem>(contentItem, "delegate", 16)); + QVERIFY(delegate = qobject_cast<QSGItem *>(evaluate<QObject *>(visualModel, "items.create(16)"))); + QCOMPARE(delegate, findItem<QSGItem>(contentItem, "delegate", 16)); + QCOMPARE(evaluate<bool>(delegate, "VisualDataModel.inPersistedItems"), true); + QCOMPARE(evaluate<int>(visualModel, "persistedItems.count"), 1); + + evaluate<void>(listview, "positionViewAtIndex(19, ListView.End)"); + QCOMPARE(listview->count(), 20); + evaluate<void>(delegate, "VisualDataModel.groups = [\"items\"]"); + QCOMPARE(evaluate<bool>(delegate, "destroyed"), false); + QCOMPARE(evaluate<bool>(delegate, "VisualDataModel.inPersistedItems"), false); + QCOMPARE(evaluate<int>(visualModel, "persistedItems.count"), 0); + + // Request and release an item instantiated by the view, then scroll the view so it releases it. + QVERIFY(findItem<QSGItem>(contentItem, "delegate", 17)); + QVERIFY(delegate = qobject_cast<QSGItem *>(evaluate<QObject *>(visualModel, "items.create(17)"))); + QCOMPARE(delegate, findItem<QSGItem>(contentItem, "delegate", 17)); + QCOMPARE(evaluate<bool>(delegate, "VisualDataModel.inPersistedItems"), true); + QCOMPARE(evaluate<int>(visualModel, "persistedItems.count"), 1); + + evaluate<void>(visualModel, "items.removeGroups(17, \"persistedItems\")"); + QCOMPARE(evaluate<bool>(delegate, "destroyed"), false); + QCOMPARE(evaluate<bool>(delegate, "VisualDataModel.inPersistedItems"), false); + QCOMPARE(evaluate<int>(visualModel, "persistedItems.count"), 0); + evaluate<void>(listview, "positionViewAtIndex(1, ListView.Beginning)"); + QCOMPARE(listview->count(), 20); + QCOMPARE(evaluate<bool>(delegate, "destroyed"), true); + + // Adding an item to the persistedItems group won't instantiate it, but if later requested by + // the view it will be persisted. + evaluate<void>(visualModel, "items.addGroups(18, \"persistedItems\")"); + QCOMPARE(evaluate<int>(visualModel, "persistedItems.count"), 1); + QVERIFY(!findItem<QSGItem>(contentItem, "delegate", 18)); + evaluate<void>(listview, "positionViewAtIndex(19, ListView.End)"); + QCOMPARE(listview->count(), 20); + QVERIFY(delegate = findItem<QSGItem>(contentItem, "delegate", 18)); + QCOMPARE(evaluate<bool>(delegate, "VisualDataModel.inPersistedItems"), true); + QCOMPARE(evaluate<bool>(delegate, "destroyed"), false); + evaluate<void>(listview, "positionViewAtIndex(1, ListView.Beginning)"); + QCOMPARE(listview->count(), 20); + QCOMPARE(evaluate<bool>(delegate, "destroyed"), false); +} + template<typename T> T *tst_qsgvisualdatamodel::findItem(QSGItem *parent, const QString &objectName, int index) { |