From 6e7ea2fc3823cb13a366845efe32050b9adefffb Mon Sep 17 00:00:00 2001 From: Andrew den Exter Date: Fri, 23 Sep 2011 13:43:24 +1000 Subject: [PATCH] Add a get function to VisualDataGroup. This returns an object with properties for accessing the model data of an item, membership in the visual data model's groups and the index of the item in each group. Task-number: QTBUG-21512 Change-Id: I878442c8a2e30cc6f9941f7412303581613a6142 Reviewed-on: http://codereview.qt-project.org/5515 Reviewed-by: Qt Sanity Bot Reviewed-by: Martin Jones --- .../modelviews/visualdatamodel/sortedmodel.qml | 141 +++++++++++ .../visualdatamodel/visualdatamodel.qmlproject | 16 ++ src/declarative/items/qsgvisualdatamodel.cpp | 246 +++++++++++++++++++- src/declarative/items/qsgvisualdatamodel_p.h | 5 +- src/declarative/qml/v8/qv8engine_p.h | 2 +- .../declarative/qsgvisualdatamodel/data/groups.qml | 8 + .../qsgvisualdatamodel/tst_qsgvisualdatamodel.cpp | 235 ++++++++++++++++++- 7 files changed, 636 insertions(+), 17 deletions(-) create mode 100644 examples/declarative/modelviews/visualdatamodel/sortedmodel.qml create mode 100644 examples/declarative/modelviews/visualdatamodel/visualdatamodel.qmlproject diff --git a/examples/declarative/modelviews/visualdatamodel/sortedmodel.qml b/examples/declarative/modelviews/visualdatamodel/sortedmodel.qml new file mode 100644 index 0000000..2a72060 --- /dev/null +++ b/examples/declarative/modelviews/visualdatamodel/sortedmodel.qml @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** 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 { + width: 480; height: 640 + + Component { + id: numberDelegate + + Text { + id: numberText + anchors { left: parent.left; right: parent.right } + text: number + + horizontalAlignment: Text.AlignHCenter + font.pixelSize: 18 + + Text { + anchors { left: parent.left; baseline: parent.baseline } + text: index + + horizontalAlignment: Text.AlignLeft + font.pixelSize: 12 + } + Text { + anchors { right: parent.right; baseline: parent.baseline } + text: numberText.VisualDataModel.itemsIndex + + horizontalAlignment: Text.AlignRight + font.pixelSize: 12 + } + } + } + + ListView { + anchors { + left: parent.left; top: parent.top; + right: parent.horizontalCenter; bottom: button.top + leftMargin: 2; topMargin: 2; rightMargin: 1; bottomMargin: 2 + } + + model: ListModel { + id: unsortedModel + } + delegate: numberDelegate + } + ListView { + anchors { + left: parent.horizontalCenter; top: parent.top; + right: parent.right; bottom: button.top + leftMargin: 1; topMargin: 2; rightMargin: 2; bottomMargin: 2 + } + model: VisualDataModel { + model: unsortedModel + delegate: numberDelegate + + items.onChanged: { + for (var i = 0; i < inserted.length; ++i) { + for (var j = inserted[i].index; j < inserted[i].index + inserted[i].count; ++j) { + var number = items.get(j).model.number + for (var l = 0, k = 0; l < unsortedModel.count; ++l) { + if (l == inserted[k].index) { + l += inserted[k].count - 1 + ++k + } else if (number < items.get(l).model.number) { + items.move(j, l, 1) + break + } + } + inserted[i].index += 1; + inserted[i].count -= 1; + } + } + } + } + } + + Rectangle { + id: button + + anchors { left: parent.left; right: parent.right; bottom: parent.bottom; margins: 2 } + height: moreText.implicitHeight + 4 + + color: "black" + + Text { + id: moreText + + anchors.fill: parent + text: "More" + color: "white" + font.pixelSize: 18 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + MouseArea { + anchors.fill: parent + + onClicked: unsortedModel.append({ "number": Math.floor(Math.random() * 100) }) + } + } +} diff --git a/examples/declarative/modelviews/visualdatamodel/visualdatamodel.qmlproject b/examples/declarative/modelviews/visualdatamodel/visualdatamodel.qmlproject new file mode 100644 index 0000000..d4909f8 --- /dev/null +++ b/examples/declarative/modelviews/visualdatamodel/visualdatamodel.qmlproject @@ -0,0 +1,16 @@ +import QmlProject 1.0 + +Project { + /* Include .qml, .js, and image files from current directory and subdirectories */ + QmlFiles { + directory: "." + } + JavaScriptFiles { + directory: "." + } + ImageFiles { + directory: "." + } + /* List of plugin directories passed to QML runtime */ + // importPaths: [ " ../exampleplugin " ] +} diff --git a/src/declarative/items/qsgvisualdatamodel.cpp b/src/declarative/items/qsgvisualdatamodel.cpp index 7793230..f5c86b8 100644 --- a/src/declarative/items/qsgvisualdatamodel.cpp +++ b/src/declarative/items/qsgvisualdatamodel.cpp @@ -290,28 +290,42 @@ QSGVisualDataModelParts::QSGVisualDataModelParts(QSGVisualDataModel *parent) class QSGVisualDataModelCacheMetaType : public QDeclarativeRefCount { public: - QSGVisualDataModelCacheMetaType(QSGVisualDataModel *model, const QStringList &groupNames); + QSGVisualDataModelCacheMetaType(QV8Engine *engine, QSGVisualDataModel *model, const QStringList &groupNames); ~QSGVisualDataModelCacheMetaType(); int parseGroups(const QStringList &groupNames) const; int parseGroups(QV8Engine *engine, const v8::Local &groupNames) const; + static v8::Handle get_model(v8::Local, const v8::AccessorInfo &info); + static v8::Handle get_groups(v8::Local, const v8::AccessorInfo &info); + static void set_groups( + v8::Local, v8::Local value, const v8::AccessorInfo &info); + static v8::Handle get_member(v8::Local, const v8::AccessorInfo &info); + static void set_member( + v8::Local, v8::Local value, const v8::AccessorInfo &info); + static v8::Handle get_index(v8::Local, const v8::AccessorInfo &info); + QDeclarativeGuard model; const int groupCount; const int memberPropertyOffset; const int indexPropertyOffset; + QV8Engine * const v8Engine; QMetaObject *metaObject; const QStringList groupNames; + v8::Persistent constructor; }; -class QSGVisualDataModelCacheItem +class QSGVisualDataModelCacheItem : public QV8ObjectResource { + V8_RESOURCE_TYPE(VisualDataItemType) public: QSGVisualDataModelCacheItem(QSGVisualDataModelCacheMetaType *metaType) - : metaType(metaType) + : QV8ObjectResource(metaType->v8Engine) + , metaType(metaType) , object(0) , attached(0) , objectRef(0) + , scriptRef(0) , groups(0) { metaType->addref(); @@ -319,6 +333,7 @@ public: ~QSGVisualDataModelCacheItem() { + Q_ASSERT(scriptRef == 0); Q_ASSERT(objectRef == 0); Q_ASSERT(!object); @@ -328,12 +343,15 @@ public: void referenceObject() { ++objectRef; } bool releaseObject() { return --objectRef == 0; } - bool isReferenced() const { return objectRef; } + bool isReferenced() const { return objectRef || scriptRef; } + + void Dispose(); QSGVisualDataModelCacheMetaType * const metaType; QDeclarativeGuard object; QSGVisualDataModelAttached *attached; int objectRef; + int scriptRef; int groups; int index[Compositor::MaximumGroupCount]; }; @@ -437,7 +455,8 @@ QSGVisualDataModel::~QSGVisualDataModel() foreach (QSGVisualDataModelCacheItem *cacheItem, d->m_cache) { cacheItem->object = 0; cacheItem->objectRef = 0; - delete cacheItem; + if (!cacheItem->isReferenced()) + delete cacheItem; } delete d->m_adaptorModel; @@ -483,7 +502,8 @@ void QSGVisualDataModel::componentComplete() if (!d->m_context) d->m_context = qmlContext(this); - d->m_cacheMetaType = new QSGVisualDataModelCacheMetaType(this, groupNames); + d->m_cacheMetaType = new QSGVisualDataModelCacheMetaType( + QDeclarativeEnginePrivate::getV8Engine(d->m_context->engine()), this, groupNames); d->m_compositor.setGroupCount(d->m_groupCount); d->m_compositor.setDefaultGroups(defaultGroups); @@ -681,10 +701,12 @@ QSGVisualDataModel::ReleaseFlags QSGVisualDataModelPrivate::release(QObject *obj object->deleteLater(); cacheItem->object = 0; stat |= QSGVisualModel::Destroyed; - m_compositor.clearFlags(Compositor::Cache, cacheIndex, 1, Compositor::CacheFlag); - m_cache.removeAt(cacheIndex); - delete cacheItem; - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + if (!cacheItem->isReferenced()) { + m_compositor.clearFlags(Compositor::Cache, cacheIndex, 1, Compositor::CacheFlag); + m_cache.removeAt(cacheIndex); + delete cacheItem; + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + } } else { stat |= QSGVisualDataModel::Referenced; } @@ -1390,11 +1412,12 @@ QSGVisualDataModelAttached *QSGVisualDataModel::qmlAttachedProperties(QObject *o //============================================================================ QSGVisualDataModelCacheMetaType::QSGVisualDataModelCacheMetaType( - QSGVisualDataModel *model, const QStringList &groupNames) + QV8Engine *engine, QSGVisualDataModel *model, const QStringList &groupNames) : model(model) , groupCount(groupNames.count() + 1) , memberPropertyOffset(QSGVisualDataModelAttached::staticMetaObject.propertyCount()) , indexPropertyOffset(QSGVisualDataModelAttached::staticMetaObject.propertyCount() + groupNames.count()) + , v8Engine(engine) , metaObject(0) , groupNames(groupNames) { @@ -1403,6 +1426,13 @@ QSGVisualDataModelCacheMetaType::QSGVisualDataModelCacheMetaType( builder.setClassName(QSGVisualDataModelAttached::staticMetaObject.className()); builder.setSuperClass(&QSGVisualDataModelAttached::staticMetaObject); + v8::HandleScope handleScope; + v8::Context::Scope contextScope(engine->context()); + v8::Local ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetHasExternalResource(true); + ft->PrototypeTemplate()->SetAccessor(v8::String::New("model"), get_model); + ft->PrototypeTemplate()->SetAccessor(v8::String::New("groups"), get_groups, set_groups); + int notifierId = 0; for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { QString propertyName = QStringLiteral("in") + groupNames.at(i); @@ -1411,6 +1441,9 @@ QSGVisualDataModelCacheMetaType::QSGVisualDataModelCacheMetaType( QMetaPropertyBuilder propertyBuilder = builder.addProperty( propertyName.toUtf8(), "bool", notifierId); propertyBuilder.setWritable(true); + + ft->PrototypeTemplate()->SetAccessor( + engine->toString(propertyName), get_member, set_member, v8::Int32::New(i + 1)); } for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { const QString propertyName = groupNames.at(i) + QStringLiteral("Index"); @@ -1418,14 +1451,20 @@ QSGVisualDataModelCacheMetaType::QSGVisualDataModelCacheMetaType( QMetaPropertyBuilder propertyBuilder = builder.addProperty( propertyName.toUtf8(), "int", notifierId); propertyBuilder.setWritable(true); + + ft->PrototypeTemplate()->SetAccessor( + engine->toString(propertyName), get_index, 0, v8::Int32::New(i + 1)); } metaObject = builder.toMetaObject(); + + constructor = qPersistentNew(ft->GetFunction()); } QSGVisualDataModelCacheMetaType::~QSGVisualDataModelCacheMetaType() { qFree(metaObject); + qPersistentDispose(constructor); } int QSGVisualDataModelCacheMetaType::parseGroups(const QStringList &groups) const @@ -1459,6 +1498,135 @@ int QSGVisualDataModelCacheMetaType::parseGroups(QV8Engine *engine, const v8::Lo return groupFlags; } +v8::Handle QSGVisualDataModelCacheMetaType::get_model( + v8::Local, const v8::AccessorInfo &info) +{ + QSGVisualDataModelCacheItem *cacheItem = v8_resource_cast(info.This()); + if (!cacheItem) + V8THROW_ERROR("Not a valid VisualData object"); + if (!cacheItem->metaType->model) + return v8::Undefined(); + QObject *data = 0; + + QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(cacheItem->metaType->model); + for (int i = 1; i < cacheItem->metaType->groupCount; ++i) { + if (cacheItem->groups & (1 << i)) { + Compositor::iterator it = model->m_compositor.find( + Compositor::Group(i), cacheItem->index[i]); + if (QSGVisualAdaptorModel *list = it.list()) + data = list->data(it.modelIndex()); + break; + } + } + if (!data) + return v8::Undefined(); + return cacheItem->engine->newQObject(data); +} + +v8::Handle QSGVisualDataModelCacheMetaType::get_groups( + v8::Local, const v8::AccessorInfo &info) +{ + QSGVisualDataModelCacheItem *cacheItem = v8_resource_cast(info.This()); + if (!cacheItem) + V8THROW_ERROR("Not a valid VisualData object"); + + QStringList groups; + for (int i = 1; i < cacheItem->metaType->groupCount; ++i) { + if (cacheItem->groups & (1 << i)) + groups.append(cacheItem->metaType->groupNames.at(i - 1)); + } + + return cacheItem->engine->fromVariant(groups); +} + +void QSGVisualDataModelCacheMetaType::set_groups( + v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QSGVisualDataModelCacheItem *cacheItem = v8_resource_cast(info.This()); + if (!cacheItem) + V8THROW_ERROR_SETTER("Not a valid VisualData object"); + + if (!cacheItem->metaType->model) + return; + QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(cacheItem->metaType->model); + + const int groupFlags = model->m_cacheMetaType->parseGroups(cacheItem->engine, value); + for (int i = 1; i < cacheItem->metaType->groupCount; ++i) { + if (cacheItem->groups & (1 << i)) { + model->setGroups(Compositor::Group(i), cacheItem->index[i], 1, groupFlags); + break; + } + } +} + +v8::Handle QSGVisualDataModelCacheMetaType::get_member( + v8::Local, const v8::AccessorInfo &info) +{ + QSGVisualDataModelCacheItem *cacheItem = v8_resource_cast(info.This()); + if (!cacheItem) + V8THROW_ERROR("Not a valid VisualData object"); + + return v8::Boolean::New(cacheItem->groups & (1 << info.Data()->Int32Value())); +} + +void QSGVisualDataModelCacheMetaType::set_member( + v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QSGVisualDataModelCacheItem *cacheItem = v8_resource_cast(info.This()); + if (!cacheItem) + V8THROW_ERROR_SETTER("Not a valid VisualData object"); + + if (!cacheItem->metaType->model) + return; + QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(cacheItem->metaType->model); + + Compositor::Group group = Compositor::Group(info.Data()->Int32Value()); + const bool member = value->BooleanValue(); + const int groupFlag = (1 << group); + if (member == ((cacheItem->groups & groupFlag) != 0)) + return; + + for (int i = 1; i < cacheItem->metaType->groupCount; ++i) { + if (cacheItem->groups & (1 << i)) { + if (member) + model->addGroups(Compositor::Group(i), cacheItem->index[i], 1, groupFlag); + else + model->removeGroups(Compositor::Group(i), cacheItem->index[i], 1, groupFlag); + break; + } + } +} + +v8::Handle QSGVisualDataModelCacheMetaType::get_index( + v8::Local, const v8::AccessorInfo &info) +{ + QSGVisualDataModelCacheItem *cacheItem = v8_resource_cast(info.This()); + if (!cacheItem) + V8THROW_ERROR("Not a valid VisualData object"); + + return v8::Integer::New(cacheItem->index[info.Data()->Int32Value()]); +} + + +//--------------------------------------------------------------------------- + +void QSGVisualDataModelCacheItem::Dispose() +{ + --scriptRef; + if (isReferenced()) + return; + + if (metaType->model) { + QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(metaType->model); + const int cacheIndex = model->m_cache.indexOf(this); + if (cacheIndex != -1) { + model->m_compositor.clearFlags(Compositor::Cache, cacheIndex, 1, Compositor::CacheFlag); + model->m_cache.removeAt(cacheIndex); + } + } + delete this; +} + //--------------------------------------------------------------------------- QSGVisualDataModelAttachedMetaObject::QSGVisualDataModelAttachedMetaObject( @@ -1743,6 +1911,62 @@ void QSGVisualDataGroup::setDefaultInclude(bool include) } /*! + \qmlmethod var QtQuick2::VisualDataGroup::get(int index) + + Returns a javascript object describing the item at \a index in the group. + + The returned object contains the same information that is available to a delegate from the + VisualDataModel attached as well as the model for that item. It has the properties: + + \list + \o \b model The model data of the item. This is the same as the model context property in + 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. + 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 {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. + \endlist +*/ + +QDeclarativeV8Handle QSGVisualDataGroup::get(int index) +{ + Q_D(QSGVisualDataGroup); + if (!d->model) + return QDeclarativeV8Handle::fromHandle(v8::Undefined());; + + QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(d->model); + if (index < 0 || index >= model->m_compositor.count(d->group)) { + qmlInfo(this) << tr("get: index out of range"); + return QDeclarativeV8Handle::fromHandle(v8::Undefined()); + } + + Compositor::iterator it = model->m_compositor.find(d->group, index); + QSGVisualDataModelCacheItem *cacheItem = it->inCache() + ? model->m_cache.at(it.cacheIndex) + : 0; + + if (!cacheItem) { + cacheItem = new QSGVisualDataModelCacheItem(model->m_cacheMetaType); + for (int i = 0; i < model->m_groupCount; ++i) + cacheItem->index[i] = it.index[i]; + cacheItem->groups = it->flags & Compositor::GroupMask; + + model->m_cache.insert(it.cacheIndex, cacheItem); + model->m_compositor.setFlags(it, 1, Compositor::CacheFlag); + } + + ++cacheItem->scriptRef; + + v8::Local rv = model->m_cacheMetaType->constructor->NewInstance(); + rv->SetExternalResource(cacheItem); + return QDeclarativeV8Handle::fromHandle(rv); +} + +/*! \qmlmethod QtQuick2::VisualDataGroup::remove(int index, int count) Removes \a count items starting at \a index from the group. diff --git a/src/declarative/items/qsgvisualdatamodel_p.h b/src/declarative/items/qsgvisualdatamodel_p.h index 94640a6..9b4542d 100644 --- a/src/declarative/items/qsgvisualdatamodel_p.h +++ b/src/declarative/items/qsgvisualdatamodel_p.h @@ -50,6 +50,8 @@ #include #include +#include + QT_BEGIN_HEADER Q_DECLARE_METATYPE(QModelIndex) @@ -63,7 +65,6 @@ class QDeclarativeChangeSet; class QDeclarativeComponent; class QDeclarativePackage; class QDeclarativeV8Function; -class QDeclarativeV8Handle; class QSGVisualDataGroup; class QSGVisualDataModelAttached; class QSGVisualDataModelPrivate; @@ -161,6 +162,8 @@ public: bool defaultInclude() const; void setDefaultInclude(bool include); + Q_INVOKABLE QDeclarativeV8Handle get(int index); + public Q_SLOTS: void remove(QDeclarativeV8Function *); void addGroups(QDeclarativeV8Function *); diff --git a/src/declarative/qml/v8/qv8engine_p.h b/src/declarative/qml/v8/qv8engine_p.h index 114584c..185eb54 100644 --- a/src/declarative/qml/v8/qv8engine_p.h +++ b/src/declarative/qml/v8/qv8engine_p.h @@ -136,7 +136,7 @@ public: enum ResourceType { ContextType, QObjectType, TypeType, ListType, VariantType, ValueTypeType, XMLHttpRequestType, DOMNodeType, SQLDatabaseType, ListModelType, Context2DType, Context2DStyleType, Context2DPixelArrayType, - ParticleDataType, SignalHandlerType, IncubatorType }; + ParticleDataType, SignalHandlerType, IncubatorType, VisualDataItemType }; virtual ResourceType resourceType() const = 0; QV8Engine *engine; diff --git a/tests/auto/declarative/qsgvisualdatamodel/data/groups.qml b/tests/auto/declarative/qsgvisualdatamodel/data/groups.qml index 0189697..a24e223 100644 --- a/tests/auto/declarative/qsgvisualdatamodel/data/groups.qml +++ b/tests/auto/declarative/qsgvisualdatamodel/data/groups.qml @@ -3,6 +3,14 @@ import QtQuick 2.0 ListView { width: 100 height: 100 + + function contains(array, value) { + for (var i = 0; i < array.length; ++i) + if (array[i] == value) + return true + return false + } + model: VisualDataModel { groups: [ VisualDataGroup { id: visibleItems; objectName: "visibleItems"; name: "visible"; includeByDefault: true }, diff --git a/tests/auto/declarative/qsgvisualdatamodel/tst_qsgvisualdatamodel.cpp b/tests/auto/declarative/qsgvisualdatamodel/tst_qsgvisualdatamodel.cpp index e8659bc..50e1b8f 100644 --- a/tests/auto/declarative/qsgvisualdatamodel/tst_qsgvisualdatamodel.cpp +++ b/tests/auto/declarative/qsgvisualdatamodel/tst_qsgvisualdatamodel.cpp @@ -52,6 +52,7 @@ #include #include #include +#include #include #include @@ -133,6 +134,7 @@ private slots: void remove(); void move(); void groups(); + void get(); private: template void groups_verify( @@ -145,6 +147,18 @@ private: const bool (&vMember)[N], const bool (&sMember)[N]); + template void get_verify( + const SingleRoleModel &model, + QSGVisualDataModel *visualModel, + QSGVisualDataGroup *visibleItems, + QSGVisualDataGroup *selectedItems, + const int (&mIndex)[N], + const int (&iIndex)[N], + const int (&vIndex)[N], + const int (&sIndex)[N], + const bool (&vMember)[N], + const bool (&sMember)[N]); + bool failed; QDeclarativeEngine engine; template @@ -203,13 +217,12 @@ private: template static T evaluate(QObject *scope, const QString &expression) { QDeclarativeExpression expr(qmlContext(scope), scope, expression); - QVariant result = expr.evaluate(); + T result = expr.evaluate().value(); if (expr.hasError()) qWarning() << expr.error().toString(); - return result.value(); + return result; } - template <> void evaluate(QObject *scope, const QString &expression) { QDeclarativeExpression expr(qmlContext(scope), scope, expression); @@ -218,7 +231,6 @@ template <> void evaluate(QObject *scope, const QString &expression) qWarning() << expr.error().toString(); } - tst_qsgvisualdatamodel::tst_qsgvisualdatamodel() { } @@ -1183,6 +1195,221 @@ void tst_qsgvisualdatamodel::groups() } } +template void tst_qsgvisualdatamodel::get_verify( + const SingleRoleModel &model, + QSGVisualDataModel *visualModel, + QSGVisualDataGroup *visibleItems, + QSGVisualDataGroup *selectedItems, + const int (&mIndex)[N], + const int (&iIndex)[N], + const int (&vIndex)[N], + const int (&sIndex)[N], + const bool (&vMember)[N], + const bool (&sMember)[N]) +{ + failed = true; + for (int i = 0; i < N; ++i) { + QCOMPARE(evaluate(visualModel, QString("items.get(%1).model.name").arg(i)), model.list.at(mIndex[i])); + QCOMPARE(evaluate(visualModel, QString("items.get(%1).model.modelData").arg(i)), model.list.at(mIndex[i])); + QCOMPARE(evaluate(visualModel, QString("items.get(%1).model.index").arg(i)), mIndex[i]); + QCOMPARE(evaluate(visualModel, QString("items.get(%1).itemsIndex").arg(i)), iIndex[i]); + QCOMPARE(evaluate(visualModel, QString("items.get(%1).inItems").arg(i)), true); + QCOMPARE(evaluate(visualModel, QString("items.get(%1).visibleIndex").arg(i)), vIndex[i]); + QCOMPARE(evaluate(visualModel, QString("items.get(%1).inVisible").arg(i)), vMember[i]); + QCOMPARE(evaluate(visualModel, QString("items.get(%1).selectedIndex").arg(i)), sIndex[i]); + QCOMPARE(evaluate(visualModel, QString("items.get(%1).inSelected").arg(i)), sMember[i]); + QCOMPARE(evaluate(visualModel, QString("contains(items.get(%1).groups, \"items\")").arg(i)), true); + QCOMPARE(evaluate(visualModel, QString("contains(items.get(%1).groups, \"visible\")").arg(i)), vMember[i]); + QCOMPARE(evaluate(visualModel, QString("contains(items.get(%1).groups, \"selected\")").arg(i)), sMember[i]); + + if (vMember[i]) { + QCOMPARE(evaluate(visibleItems, QString("get(%1).model.name").arg(vIndex[i])), model.list.at(mIndex[i])); + QCOMPARE(evaluate(visibleItems, QString("get(%1).model.modelData").arg(vIndex[i])), model.list.at(mIndex[i])); + QCOMPARE(evaluate(visibleItems, QString("get(%1).model.index").arg(vIndex[i])), mIndex[i]); + QCOMPARE(evaluate(visibleItems, QString("get(%1).itemsIndex").arg(vIndex[i])), iIndex[i]); + QCOMPARE(evaluate(visibleItems, QString("get(%1).inItems").arg(vIndex[i])), true); + QCOMPARE(evaluate(visibleItems, QString("get(%1).visibleIndex").arg(vIndex[i])), vIndex[i]); + QCOMPARE(evaluate(visibleItems, QString("get(%1).inVisible").arg(vIndex[i])), vMember[i]); + QCOMPARE(evaluate(visibleItems, QString("get(%1).selectedIndex").arg(vIndex[i])), sIndex[i]); + QCOMPARE(evaluate(visibleItems, QString("get(%1).inSelected").arg(vIndex[i])), sMember[i]); + + QCOMPARE(evaluate(visibleItems, QString("contains(get(%1).groups, \"items\")").arg(vIndex[i])), true); + QCOMPARE(evaluate(visibleItems, QString("contains(get(%1).groups, \"visible\")").arg(vIndex[i])), vMember[i]); + QCOMPARE(evaluate(visibleItems, QString("contains(get(%1).groups, \"selected\")").arg(vIndex[i])), sMember[i]); + } + if (sMember[i]) { + QCOMPARE(evaluate(selectedItems, QString("get(%1).model.name").arg(sIndex[i])), model.list.at(mIndex[i])); + QCOMPARE(evaluate(selectedItems, QString("get(%1).model.modelData").arg(sIndex[i])), model.list.at(mIndex[i])); + QCOMPARE(evaluate(selectedItems, QString("get(%1).model.index").arg(sIndex[i])), mIndex[i]); + QCOMPARE(evaluate(selectedItems, QString("get(%1).itemsIndex").arg(sIndex[i])), iIndex[i]); + QCOMPARE(evaluate(selectedItems, QString("get(%1).inItems").arg(sIndex[i])), true); + QCOMPARE(evaluate(selectedItems, QString("get(%1).visibleIndex").arg(sIndex[i])), vIndex[i]); + QCOMPARE(evaluate(selectedItems, QString("get(%1).inVisible").arg(sIndex[i])), vMember[i]); + QCOMPARE(evaluate(selectedItems, QString("get(%1).selectedIndex").arg(sIndex[i])), sIndex[i]); + QCOMPARE(evaluate(selectedItems, QString("get(%1).inSelected").arg(sIndex[i])), sMember[i]); + QCOMPARE(evaluate(selectedItems, QString("contains(get(%1).groups, \"items\")").arg(sIndex[i])), true); + QCOMPARE(evaluate(selectedItems, QString("contains(get(%1).groups, \"visible\")").arg(sIndex[i])), vMember[i]); + QCOMPARE(evaluate(selectedItems, QString("contains(get(%1).groups, \"selected\")").arg(sIndex[i])), sMember[i]); + } + } + failed = false; +} + +#define VERIFY_GET \ + get_verify(model, visualModel, visibleItems, selectedItems, mIndex, iIndex, vIndex, sIndex, vMember, sMember); \ + QVERIFY(!failed) + +void tst_qsgvisualdatamodel::get() +{ + QSGView view; + + SingleRoleModel model; + model.list = QStringList() + << "one" + << "two" + << "three" + << "four" + << "five" + << "six" + << "seven" + << "eight" + << "nine" + << "ten" + << "eleven" + << "twelve"; + + QDeclarativeContext *ctxt = view.rootContext(); + ctxt->setContextProperty("myModel", &model); + + view.setSource(QUrl::fromLocalFile(SRCDIR "/data/groups.qml")); + + QSGListView *listview = qobject_cast(view.rootObject()); + QVERIFY(listview != 0); + + QSGItem *contentItem = listview->contentItem(); + QVERIFY(contentItem != 0); + + QSGVisualDataModel *visualModel = qobject_cast(qvariant_cast(listview->model())); + QVERIFY(visualModel); + + QSGVisualDataGroup *visibleItems = visualModel->findChild("visibleItems"); + QVERIFY(visibleItems); + + QSGVisualDataGroup *selectedItems = visualModel->findChild("selectedItems"); + QVERIFY(selectedItems); + + QV8Engine *v8Engine = QDeclarativeEnginePrivate::getV8Engine(ctxt->engine()); + QVERIFY(v8Engine); + + const bool f = false; + const bool t = true; + + { + QCOMPARE(listview->count(), 12); + QCOMPARE(visualModel->items()->count(), 12); + QCOMPARE(visibleItems->count(), 12); + QCOMPARE(selectedItems->count(), 0); + static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 }; + static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 }; + static const int vIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 }; + static const bool vMember[] = { t, t, t, t, t, t, t, t, t, t, t, t }; + static const int sIndex [] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + static const bool sMember[] = { f, f, f, f, f, f, f, f, f, f, f, f }; + VERIFY_GET; + } { + evaluate(visualModel, "items.addGroups(8, \"selected\")"); + QCOMPARE(listview->count(), 12); + QCOMPARE(visualModel->items()->count(), 12); + QCOMPARE(visibleItems->count(), 12); + QCOMPARE(selectedItems->count(), 1); + static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 }; + static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 }; + static const int vIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 }; + static const bool vMember[] = { t, t, t, t, t, t, t, t, t, t, t, t }; + static const int sIndex [] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1 }; + static const bool sMember[] = { f, f, f, f, f, f, f, f, t, f, f, f }; + VERIFY_GET; + } { + evaluate(visualModel, "items.addGroups(6, 4, [\"visible\", \"selected\"])"); + QCOMPARE(listview->count(), 12); + QCOMPARE(visualModel->items()->count(), 12); + QCOMPARE(visibleItems->count(), 12); + QCOMPARE(selectedItems->count(), 4); + static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 }; + static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 }; + static const int vIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 }; + static const bool vMember[] = { t, t, t, t, t, t, t, t, t, t, t, t }; + static const int sIndex [] = { 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 4 }; + static const bool sMember[] = { f, f, f, f, f, f, t, t, t, t, f, f }; + VERIFY_GET; + } { + evaluate(visualModel, "items.setGroups(2, [\"items\", \"selected\"])"); + QCOMPARE(listview->count(), 12); + QCOMPARE(visualModel->items()->count(), 12); + QCOMPARE(visibleItems->count(), 11); + QCOMPARE(selectedItems->count(), 5); + static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 }; + static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 }; + static const int vIndex [] = { 0, 1, 2, 2, 3, 4, 5, 6, 7, 8, 9,10 }; + static const bool vMember[] = { t, t, f, t, t, t, t, t, t, t, t, t }; + static const int sIndex [] = { 0, 0, 0, 1, 1, 1, 1, 2, 3, 4, 5, 5 }; + static const bool sMember[] = { f, f, t, f, f, f, t, t, t, t, f, f }; + VERIFY_GET; + } { + evaluate(selectedItems, "setGroups(0, 3, \"items\")"); + QCOMPARE(listview->count(), 12); + QCOMPARE(visualModel->items()->count(), 12); + QCOMPARE(visibleItems->count(), 9); + QCOMPARE(selectedItems->count(), 2); + static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 }; + static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 }; + static const int vIndex [] = { 0, 1, 2, 2, 3, 4, 5, 5, 5, 6, 7, 8 }; + static const bool vMember[] = { t, t, f, t, t, t, f, f, t, t, t, t }; + static const int sIndex [] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2 }; + static const bool sMember[] = { f, f, f, f, f, f, f, f, t, t, f, f }; + VERIFY_GET; + } { + evaluate(visualModel, "items.get(5).inVisible = false"); + QCOMPARE(listview->count(), 12); + QCOMPARE(visualModel->items()->count(), 12); + QCOMPARE(visibleItems->count(), 8); + QCOMPARE(selectedItems->count(), 2); + static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 }; + static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 }; + static const int vIndex [] = { 0, 1, 2, 2, 3, 4, 4, 4, 4, 5, 6, 7 }; + static const bool vMember[] = { t, t, f, t, t, f, f, f, t, t, t, t }; + static const int sIndex [] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2 }; + static const bool sMember[] = { f, f, f, f, f, f, f, f, t, t, f, f }; + VERIFY_GET; + } { + evaluate(visualModel, "items.get(5).inSelected = true"); + QCOMPARE(listview->count(), 12); + QCOMPARE(visualModel->items()->count(), 12); + QCOMPARE(visibleItems->count(), 8); + QCOMPARE(selectedItems->count(), 3); + static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 }; + static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 }; + static const int vIndex [] = { 0, 1, 2, 2, 3, 4, 4, 4, 4, 5, 6, 7 }; + static const bool vMember[] = { t, t, f, t, t, f, f, f, t, t, t, t }; + static const int sIndex [] = { 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 3, 3 }; + static const bool sMember[] = { f, f, f, f, f, t, f, f, t, t, f, f }; + VERIFY_GET; + } { + evaluate(visualModel, "items.get(5).groups = [\"visible\", \"items\"]"); + QCOMPARE(listview->count(), 12); + QCOMPARE(visualModel->items()->count(), 12); + QCOMPARE(visibleItems->count(), 9); + QCOMPARE(selectedItems->count(), 2); + static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 }; + static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 }; + static const int vIndex [] = { 0, 1, 2, 2, 3, 4, 5, 5, 5, 6, 7, 8 }; + static const bool vMember[] = { t, t, f, t, t, t, f, f, t, t, t, t }; + static const int sIndex [] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2 }; + static const bool sMember[] = { f, f, f, f, f, f, f, f, t, t, f, f }; + VERIFY_GET; + } +} + template T *tst_qsgvisualdatamodel::findItem(QSGItem *parent, const QString &objectName, int index) { -- 1.7.2.5