diff options
7 files changed, 636 insertions, 17 deletions
diff --git a/examples/declarative/modelviews/visualdatamodel/sortedmodel.qml b/examples/declarative/modelviews/visualdatamodel/sortedmodel.qml new file mode 100644 index 0000000000..2a72060422 --- /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 0000000000..d4909f8685 --- /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 7793230f35..f5c86b8faf 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<v8::Value> &groupNames) const; + static v8::Handle<v8::Value> get_model(v8::Local<v8::String>, const v8::AccessorInfo &info); + static v8::Handle<v8::Value> get_groups(v8::Local<v8::String>, const v8::AccessorInfo &info); + static void set_groups( + v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info); + static v8::Handle<v8::Value> get_member(v8::Local<v8::String>, const v8::AccessorInfo &info); + static void set_member( + v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info); + static v8::Handle<v8::Value> get_index(v8::Local<v8::String>, const v8::AccessorInfo &info); + QDeclarativeGuard<QSGVisualDataModel> model; const int groupCount; const int memberPropertyOffset; const int indexPropertyOffset; + QV8Engine * const v8Engine; QMetaObject *metaObject; const QStringList groupNames; + v8::Persistent<v8::Function> 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<QObject> 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<v8::FunctionTemplate> 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<v8::Function>(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<v8::Value> QSGVisualDataModelCacheMetaType::get_model( + v8::Local<v8::String>, const v8::AccessorInfo &info) +{ + QSGVisualDataModelCacheItem *cacheItem = v8_resource_cast<QSGVisualDataModelCacheItem>(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<QSGVisualAdaptorModel>()) + data = list->data(it.modelIndex()); + break; + } + } + if (!data) + return v8::Undefined(); + return cacheItem->engine->newQObject(data); +} + +v8::Handle<v8::Value> QSGVisualDataModelCacheMetaType::get_groups( + v8::Local<v8::String>, const v8::AccessorInfo &info) +{ + QSGVisualDataModelCacheItem *cacheItem = v8_resource_cast<QSGVisualDataModelCacheItem>(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::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info) +{ + QSGVisualDataModelCacheItem *cacheItem = v8_resource_cast<QSGVisualDataModelCacheItem>(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<v8::Value> QSGVisualDataModelCacheMetaType::get_member( + v8::Local<v8::String>, const v8::AccessorInfo &info) +{ + QSGVisualDataModelCacheItem *cacheItem = v8_resource_cast<QSGVisualDataModelCacheItem>(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::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info) +{ + QSGVisualDataModelCacheItem *cacheItem = v8_resource_cast<QSGVisualDataModelCacheItem>(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<v8::Value> QSGVisualDataModelCacheMetaType::get_index( + v8::Local<v8::String>, const v8::AccessorInfo &info) +{ + QSGVisualDataModelCacheItem *cacheItem = v8_resource_cast<QSGVisualDataModelCacheItem>(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<v8::Object> 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 94640a65a0..9b4542d08a 100644 --- a/src/declarative/items/qsgvisualdatamodel_p.h +++ b/src/declarative/items/qsgvisualdatamodel_p.h @@ -50,6 +50,8 @@ #include <QtCore/qabstractitemmodel.h> #include <QtCore/qstringlist.h> +#include <private/qv8engine_p.h> + 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 114584cdc2..185eb545ef 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 0189697a89..a24e223bc5 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 e8659bcf16..50e1b8f9df 100644 --- a/tests/auto/declarative/qsgvisualdatamodel/tst_qsgvisualdatamodel.cpp +++ b/tests/auto/declarative/qsgvisualdatamodel/tst_qsgvisualdatamodel.cpp @@ -52,6 +52,7 @@ #include <private/qsgvisualdatamodel_p.h> #include <private/qdeclarativevaluetype_p.h> #include <private/qdeclarativechangeset_p.h> +#include <private/qdeclarativeengine_p.h> #include <math.h> #include <QtOpenGL/QGLShaderProgram> @@ -133,6 +134,7 @@ private slots: void remove(); void move(); void groups(); + void get(); private: template <int N> void groups_verify( @@ -145,6 +147,18 @@ private: const bool (&vMember)[N], const bool (&sMember)[N]); + template <int N> 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<typename T> @@ -203,13 +217,12 @@ private: template <typename T> static T evaluate(QObject *scope, const QString &expression) { QDeclarativeExpression expr(qmlContext(scope), scope, expression); - QVariant result = expr.evaluate(); + T result = expr.evaluate().value<T>(); if (expr.hasError()) qWarning() << expr.error().toString(); - return result.value<T>(); + return result; } - template <> void evaluate<void>(QObject *scope, const QString &expression) { QDeclarativeExpression expr(qmlContext(scope), scope, expression); @@ -218,7 +231,6 @@ template <> void evaluate<void>(QObject *scope, const QString &expression) qWarning() << expr.error().toString(); } - tst_qsgvisualdatamodel::tst_qsgvisualdatamodel() { } @@ -1183,6 +1195,221 @@ void tst_qsgvisualdatamodel::groups() } } +template <int N> 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<QString>(visualModel, QString("items.get(%1).model.name").arg(i)), model.list.at(mIndex[i])); + QCOMPARE(evaluate<QString>(visualModel, QString("items.get(%1).model.modelData").arg(i)), model.list.at(mIndex[i])); + QCOMPARE(evaluate<int>(visualModel, QString("items.get(%1).model.index").arg(i)), mIndex[i]); + QCOMPARE(evaluate<int>(visualModel, QString("items.get(%1).itemsIndex").arg(i)), iIndex[i]); + QCOMPARE(evaluate<bool>(visualModel, QString("items.get(%1).inItems").arg(i)), true); + QCOMPARE(evaluate<int>(visualModel, QString("items.get(%1).visibleIndex").arg(i)), vIndex[i]); + QCOMPARE(evaluate<bool>(visualModel, QString("items.get(%1).inVisible").arg(i)), vMember[i]); + QCOMPARE(evaluate<int>(visualModel, QString("items.get(%1).selectedIndex").arg(i)), sIndex[i]); + QCOMPARE(evaluate<bool>(visualModel, QString("items.get(%1).inSelected").arg(i)), sMember[i]); + QCOMPARE(evaluate<bool>(visualModel, QString("contains(items.get(%1).groups, \"items\")").arg(i)), true); + QCOMPARE(evaluate<bool>(visualModel, QString("contains(items.get(%1).groups, \"visible\")").arg(i)), vMember[i]); + QCOMPARE(evaluate<bool>(visualModel, QString("contains(items.get(%1).groups, \"selected\")").arg(i)), sMember[i]); + + if (vMember[i]) { + QCOMPARE(evaluate<QString>(visibleItems, QString("get(%1).model.name").arg(vIndex[i])), model.list.at(mIndex[i])); + QCOMPARE(evaluate<QString>(visibleItems, QString("get(%1).model.modelData").arg(vIndex[i])), model.list.at(mIndex[i])); + QCOMPARE(evaluate<int>(visibleItems, QString("get(%1).model.index").arg(vIndex[i])), mIndex[i]); + QCOMPARE(evaluate<int>(visibleItems, QString("get(%1).itemsIndex").arg(vIndex[i])), iIndex[i]); + QCOMPARE(evaluate<bool>(visibleItems, QString("get(%1).inItems").arg(vIndex[i])), true); + QCOMPARE(evaluate<int>(visibleItems, QString("get(%1).visibleIndex").arg(vIndex[i])), vIndex[i]); + QCOMPARE(evaluate<bool>(visibleItems, QString("get(%1).inVisible").arg(vIndex[i])), vMember[i]); + QCOMPARE(evaluate<int>(visibleItems, QString("get(%1).selectedIndex").arg(vIndex[i])), sIndex[i]); + QCOMPARE(evaluate<bool>(visibleItems, QString("get(%1).inSelected").arg(vIndex[i])), sMember[i]); + + QCOMPARE(evaluate<bool>(visibleItems, QString("contains(get(%1).groups, \"items\")").arg(vIndex[i])), true); + QCOMPARE(evaluate<bool>(visibleItems, QString("contains(get(%1).groups, \"visible\")").arg(vIndex[i])), vMember[i]); + QCOMPARE(evaluate<bool>(visibleItems, QString("contains(get(%1).groups, \"selected\")").arg(vIndex[i])), sMember[i]); + } + if (sMember[i]) { + QCOMPARE(evaluate<QString>(selectedItems, QString("get(%1).model.name").arg(sIndex[i])), model.list.at(mIndex[i])); + QCOMPARE(evaluate<QString>(selectedItems, QString("get(%1).model.modelData").arg(sIndex[i])), model.list.at(mIndex[i])); + QCOMPARE(evaluate<int>(selectedItems, QString("get(%1).model.index").arg(sIndex[i])), mIndex[i]); + QCOMPARE(evaluate<int>(selectedItems, QString("get(%1).itemsIndex").arg(sIndex[i])), iIndex[i]); + QCOMPARE(evaluate<bool>(selectedItems, QString("get(%1).inItems").arg(sIndex[i])), true); + QCOMPARE(evaluate<int>(selectedItems, QString("get(%1).visibleIndex").arg(sIndex[i])), vIndex[i]); + QCOMPARE(evaluate<bool>(selectedItems, QString("get(%1).inVisible").arg(sIndex[i])), vMember[i]); + QCOMPARE(evaluate<int>(selectedItems, QString("get(%1).selectedIndex").arg(sIndex[i])), sIndex[i]); + QCOMPARE(evaluate<bool>(selectedItems, QString("get(%1).inSelected").arg(sIndex[i])), sMember[i]); + QCOMPARE(evaluate<bool>(selectedItems, QString("contains(get(%1).groups, \"items\")").arg(sIndex[i])), true); + QCOMPARE(evaluate<bool>(selectedItems, QString("contains(get(%1).groups, \"visible\")").arg(sIndex[i])), vMember[i]); + QCOMPARE(evaluate<bool>(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<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); + + QSGVisualDataGroup *visibleItems = visualModel->findChild<QSGVisualDataGroup *>("visibleItems"); + QVERIFY(visibleItems); + + QSGVisualDataGroup *selectedItems = visualModel->findChild<QSGVisualDataGroup *>("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<void>(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<void>(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<void>(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<void>(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<void>(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<void>(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<void>(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<typename T> T *tst_qsgvisualdatamodel::findItem(QSGItem *parent, const QString &objectName, int index) { |