aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGlenn Watson <glenn.watson@nokia.com>2011-12-01 10:40:44 +1000
committerQt by Nokia <qt-info@nokia.com>2011-12-02 10:13:40 +0100
commit542a7d164ad81973f911a54bc53aa385d09b5c1f (patch)
treef0b46f04f2c026f493d093e3ab787ea3ed2a116b /src
parent63662f7f0e92c080cb45c99149b36f179f9b1360 (diff)
Add dynamicRoles mode to ListModel for extra flexibility.
ListModel contains a new property called dynamicRoles. By default this property is disabled, meaning that the type of a given role cannot be changed dynamically. However, when this property is set, the type of a role can change between elements. This allows more flexibility but comes at a significant performance cost over the default static role mode. Change-Id: I478d48c882f51ba2dde37f88e2aac744e919b68b Reviewed-by: Martin Jones <martin.jones@nokia.com>
Diffstat (limited to 'src')
-rw-r--r--src/declarative/qml/qdeclarativelistmodel.cpp495
-rw-r--r--src/declarative/qml/qdeclarativelistmodel_p.h33
-rw-r--r--src/declarative/qml/qdeclarativelistmodel_p_p.h62
-rw-r--r--src/declarative/qml/qdeclarativelistmodelworkeragent.cpp49
-rw-r--r--src/declarative/qml/qdeclarativelistmodelworkeragent_p.h10
-rw-r--r--src/declarative/qml/qdeclarativeopenmetaobject.cpp7
-rw-r--r--src/declarative/qml/qdeclarativeopenmetaobject_p.h2
7 files changed, 580 insertions, 78 deletions
diff --git a/src/declarative/qml/qdeclarativelistmodel.cpp b/src/declarative/qml/qdeclarativelistmodel.cpp
index 9e4d7269cd..16391a3394 100644
--- a/src/declarative/qml/qdeclarativelistmodel.cpp
+++ b/src/declarative/qml/qdeclarativelistmodel.cpp
@@ -62,7 +62,7 @@ QT_BEGIN_NAMESPACE
// Set to 1024 as a debugging aid - easier to distinguish uids from indices of elements/models.
enum { MIN_LISTMODEL_UID = 1024 };
-QAtomicInt ListModel::uidCounter(MIN_LISTMODEL_UID);
+static QAtomicInt uidCounter(MIN_LISTMODEL_UID);
template <typename T>
static bool isMemoryUsed(const char *mem)
@@ -78,7 +78,7 @@ static bool isMemoryUsed(const char *mem)
static QString roleTypeName(ListLayout::Role::DataType t)
{
QString result;
- const char *roleTypeNames[] = { "String", "Number", "Bool", "List", "QObject" };
+ const char *roleTypeNames[] = { "String", "Number", "Bool", "List", "QObject", "VariantMap" };
if (t > ListLayout::Role::Invalid && t < ListLayout::Role::MaxDataType)
result = QString::fromLatin1(roleTypeNames[t]);
@@ -216,6 +216,7 @@ const ListLayout::Role *ListLayout::getRoleOrCreate(const QString &key, const QV
case QVariant::UserType: type = Role::List; break;
case QVariant::Bool: type = Role::Bool; break;
case QVariant::String: type = Role::String; break;
+ case QVariant::Map: type = Role::VariantMap; break;
default: type = Role::Invalid; break;
}
@@ -326,15 +327,10 @@ void ListModel::sync(ListModel *src, ListModel *target, QHash<int, ListModel *>
}
}
-int ListModel::allocateUid()
-{
- return uidCounter.fetchAndAddOrdered(1);
-}
-
ListModel::ListModel(ListLayout *layout, QDeclarativeListModel *modelCache, int uid) : m_layout(layout), m_modelCache(modelCache)
{
if (uid == -1)
- uid = allocateUid();
+ uid = uidCounter.fetchAndAddOrdered(1);
m_uid = uid;
}
@@ -458,11 +454,11 @@ void ListModel::set(int elementIndex, v8::Handle<v8::Object> object, QList<int>
QObject *o = QV8QObjectWrapper::toQObject(r);
const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject);
if (role.type == ListLayout::Role::QObject)
- e->setQObjectProperty(role, o);
+ roleIndex = e->setQObjectProperty(role, o);
} else {
const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap);
if (role.type == ListLayout::Role::VariantMap)
- e->setVariantMapProperty(role, propertyValue->ToObject(), eng);
+ roleIndex = e->setVariantMapProperty(role, propertyValue->ToObject(), eng);
}
} else if (propertyValue.IsEmpty() || propertyValue->IsUndefined() || propertyValue->IsNull()) {
const ListLayout::Role *r = m_layout->getExistingRole(propertyName);
@@ -583,6 +579,7 @@ int ListModel::setOrCreateProperty(int elementIndex, const QString &key, const Q
if (elementIndex >= 0 && elementIndex < elements.count()) {
ListElement *e = elements[elementIndex];
+
const ListLayout::Role *r = m_layout->getRoleOrCreate(key, data);
if (r) {
roleIndex = e->setVariantProperty(*r, data);
@@ -953,7 +950,7 @@ void ListElement::clearProperty(const ListLayout::Role &role)
ListElement::ListElement()
{
m_objectCache = 0;
- uid = ListModel::allocateUid();
+ uid = uidCounter.fetchAndAddOrdered(1);
next = 0;
qMemSet(data, 0, sizeof(data));
}
@@ -1086,6 +1083,11 @@ int ListElement::setVariantProperty(const ListLayout::Role &role, const QVariant
case ListLayout::Role::List:
roleIndex = setListProperty(role, d.value<ListModel *>());
break;
+ case ListLayout::Role::VariantMap: {
+ QVariantMap map = d.toMap();
+ roleIndex = setVariantMapProperty(role, &map);
+ }
+ break;
default:
break;
}
@@ -1195,6 +1197,153 @@ void ModelNodeMetaObject::propertyWritten(int index)
}
}
+DynamicRoleModelNode::DynamicRoleModelNode(QDeclarativeListModel *owner, int uid) : m_owner(owner), m_uid(uid), m_meta(new DynamicRoleModelNodeMetaObject(this))
+{
+ setNodeUpdatesEnabled(true);
+}
+
+DynamicRoleModelNode *DynamicRoleModelNode::create(const QVariantMap &obj, QDeclarativeListModel *owner)
+{
+ DynamicRoleModelNode *object = new DynamicRoleModelNode(owner, uidCounter.fetchAndAddOrdered(1));
+ QList<int> roles;
+ object->updateValues(obj, roles);
+ return object;
+}
+
+void DynamicRoleModelNode::sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target, QHash<int, QDeclarativeListModel *> *targetModelHash)
+{
+ for (int i=0 ; i < src->m_meta->count() ; ++i) {
+ const QByteArray &name = src->m_meta->name(i);
+ QVariant value = src->m_meta->value(i);
+
+ QDeclarativeListModel *srcModel = qobject_cast<QDeclarativeListModel *>(value.value<QObject *>());
+ QDeclarativeListModel *targetModel = qobject_cast<QDeclarativeListModel *>(target->m_meta->value(i).value<QObject *>());
+
+ if (srcModel) {
+ if (targetModel == 0)
+ targetModel = QDeclarativeListModel::createWithOwner(target->m_owner);
+
+ QDeclarativeListModel::sync(srcModel, targetModel, targetModelHash);
+
+ QObject *targetModelObject = targetModel;
+ value = QVariant::fromValue(targetModelObject);
+ } else if (targetModel) {
+ delete targetModel;
+ }
+
+ target->setValue(name, value);
+ }
+}
+
+void DynamicRoleModelNode::updateValues(const QVariantMap &object, QList<int> &roles)
+{
+ const QList<QString> &keys = object.keys();
+
+ QList<QString>::const_iterator it = keys.begin();
+ QList<QString>::const_iterator end = keys.end();
+
+ while (it != end) {
+ const QString &key = *it;
+
+ int roleIndex = m_owner->m_roles.indexOf(key);
+ if (roleIndex == -1) {
+ roleIndex = m_owner->m_roles.count();
+ m_owner->m_roles.append(key);
+ }
+
+ QVariant value = object[key];
+
+ if (value.type() == QVariant::List) {
+ QDeclarativeListModel *subModel = QDeclarativeListModel::createWithOwner(m_owner);
+
+ QVariantList subArray = value.toList();
+ QVariantList::const_iterator subIt = subArray.begin();
+ QVariantList::const_iterator subEnd = subArray.end();
+ while (subIt != subEnd) {
+ const QVariantMap &subObject = subIt->toMap();
+ subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel));
+ ++subIt;
+ }
+
+ QObject *subModelObject = subModel;
+ value = QVariant::fromValue(subModelObject);
+ }
+
+ const QByteArray &keyUtf8 = key.toUtf8();
+
+ QDeclarativeListModel *existingModel = qobject_cast<QDeclarativeListModel *>(m_meta->value(keyUtf8).value<QObject *>());
+ if (existingModel)
+ delete existingModel;
+
+ if (m_meta->setValue(keyUtf8, value))
+ roles << roleIndex;
+
+ ++it;
+ }
+}
+
+DynamicRoleModelNodeMetaObject::DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object)
+ : QDeclarativeOpenMetaObject(object), m_enabled(false), m_owner(object)
+{
+}
+
+DynamicRoleModelNodeMetaObject::~DynamicRoleModelNodeMetaObject()
+{
+ for (int i=0 ; i < count() ; ++i) {
+ QDeclarativeListModel *subModel = qobject_cast<QDeclarativeListModel *>(value(i).value<QObject *>());
+ if (subModel)
+ delete subModel;
+ }
+}
+
+void DynamicRoleModelNodeMetaObject::propertyWrite(int index)
+{
+ if (!m_enabled)
+ return;
+
+ QVariant v = value(index);
+ QDeclarativeListModel *model = qobject_cast<QDeclarativeListModel *>(v.value<QObject *>());
+ if (model)
+ delete model;
+}
+
+void DynamicRoleModelNodeMetaObject::propertyWritten(int index)
+{
+ if (!m_enabled)
+ return;
+
+ QDeclarativeListModel *parentModel = m_owner->m_owner;
+
+ QVariant v = value(index);
+ if (v.type() == QVariant::List) {
+ QDeclarativeListModel *subModel = QDeclarativeListModel::createWithOwner(parentModel);
+
+ QVariantList subArray = v.toList();
+ QVariantList::const_iterator subIt = subArray.begin();
+ QVariantList::const_iterator subEnd = subArray.end();
+ while (subIt != subEnd) {
+ const QVariantMap &subObject = subIt->toMap();
+ subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel));
+ ++subIt;
+ }
+
+ QObject *subModelObject = subModel;
+ v = QVariant::fromValue(subModelObject);
+
+ setValue(index, v);
+ }
+
+ int elementIndex = parentModel->m_modelObjects.indexOf(m_owner);
+ int roleIndex = parentModel->m_roles.indexOf(QString::fromLatin1(name(index).constData()));
+
+ if (elementIndex != -1 && roleIndex != -1) {
+ QList<int> roles;
+ roles << roleIndex;
+
+ parentModel->emitItemsChanged(elementIndex, 1, roles);
+ }
+}
+
QDeclarativeListModelParser::ListInstruction *QDeclarativeListModelParser::ListModelData::instructions() const
{
return (QDeclarativeListModelParser::ListInstruction *)((char *)this + sizeof(ListModelData));
@@ -1300,6 +1449,8 @@ QDeclarativeListModel::QDeclarativeListModel(QObject *parent)
m_mainThread = true;
m_primary = true;
m_agent = 0;
+ m_uid = uidCounter.fetchAndAddOrdered(1);
+ m_dynamicRoles = false;
m_layout = new ListLayout;
m_listModel = new ListModel(m_layout, this, -1);
@@ -1314,6 +1465,8 @@ QDeclarativeListModel::QDeclarativeListModel(const QDeclarativeListModel *owner,
m_primary = false;
m_agent = owner->m_agent;
+ Q_ASSERT(owner->m_dynamicRoles == false);
+ m_dynamicRoles = false;
m_layout = 0;
m_listModel = data;
@@ -1326,16 +1479,24 @@ QDeclarativeListModel::QDeclarativeListModel(QDeclarativeListModel *orig, QDecla
m_mainThread = false;
m_primary = true;
m_agent = agent;
+ m_dynamicRoles = orig->m_dynamicRoles;
m_layout = new ListLayout(orig->m_layout);
m_listModel = new ListModel(m_layout, this, orig->m_listModel->getUid());
- ListModel::sync(orig->m_listModel, m_listModel, 0);
+
+ if (m_dynamicRoles)
+ sync(orig, this, 0);
+ else
+ ListModel::sync(orig->m_listModel, m_listModel, 0);
m_engine = 0;
}
QDeclarativeListModel::~QDeclarativeListModel()
{
+ for (int i=0 ; i < m_modelObjects.count() ; ++i)
+ delete m_modelObjects[i];
+
if (m_primary) {
m_listModel->destroy();
delete m_listModel;
@@ -1350,6 +1511,20 @@ QDeclarativeListModel::~QDeclarativeListModel()
m_layout = 0;
}
+QDeclarativeListModel *QDeclarativeListModel::createWithOwner(QDeclarativeListModel *newOwner)
+{
+ QDeclarativeListModel *model = new QDeclarativeListModel;
+
+ model->m_mainThread = newOwner->m_mainThread;
+ model->m_engine = newOwner->m_engine;
+ model->m_agent = newOwner->m_agent;
+ model->m_dynamicRoles = newOwner->m_dynamicRoles;
+
+ QDeclarativeEngine::setContextForObject(model, QDeclarativeEngine::contextForObject(newOwner));
+
+ return model;
+}
+
QV8Engine *QDeclarativeListModel::engine() const
{
if (m_engine == 0) {
@@ -1359,12 +1534,74 @@ QV8Engine *QDeclarativeListModel::engine() const
return m_engine;
}
+void QDeclarativeListModel::sync(QDeclarativeListModel *src, QDeclarativeListModel *target, QHash<int, QDeclarativeListModel *> *targetModelHash)
+{
+ Q_ASSERT(src->m_dynamicRoles && target->m_dynamicRoles);
+
+ target->m_uid = src->m_uid;
+ if (targetModelHash)
+ targetModelHash->insert(target->m_uid, target);
+ target->m_roles = src->m_roles;
+
+ // Build hash of elements <-> uid for each of the lists
+ QHash<int, ElementSync> elementHash;
+ for (int i=0 ; i < target->m_modelObjects.count() ; ++i) {
+ DynamicRoleModelNode *e = target->m_modelObjects.at(i);
+ int uid = e->getUid();
+ ElementSync sync;
+ sync.target = e;
+ elementHash.insert(uid, sync);
+ }
+ for (int i=0 ; i < src->m_modelObjects.count() ; ++i) {
+ DynamicRoleModelNode *e = src->m_modelObjects.at(i);
+ int uid = e->getUid();
+
+ QHash<int, ElementSync>::iterator it = elementHash.find(uid);
+ if (it == elementHash.end()) {
+ ElementSync sync;
+ sync.src = e;
+ elementHash.insert(uid, sync);
+ } else {
+ ElementSync &sync = it.value();
+ sync.src = e;
+ }
+ }
+
+ // Get list of elements that are in the target but no longer in the source. These get deleted first.
+ QHash<int, ElementSync>::iterator it = elementHash.begin();
+ QHash<int, ElementSync>::iterator end = elementHash.end();
+ while (it != end) {
+ const ElementSync &s = it.value();
+ if (s.src == 0) {
+ int targetIndex = target->m_modelObjects.indexOf(s.target);
+ target->m_modelObjects.remove(targetIndex, 1);
+ delete s.target;
+ }
+ ++it;
+ }
+
+ // Clear the target list, and append in correct order from the source
+ target->m_modelObjects.clear();
+ for (int i=0 ; i < src->m_modelObjects.count() ; ++i) {
+ DynamicRoleModelNode *srcElement = src->m_modelObjects.at(i);
+ it = elementHash.find(srcElement->getUid());
+ const ElementSync &s = it.value();
+ DynamicRoleModelNode *targetElement = s.target;
+ if (targetElement == 0) {
+ targetElement = new DynamicRoleModelNode(target, srcElement->getUid());
+ }
+ DynamicRoleModelNode::sync(srcElement, targetElement, targetModelHash);
+ target->m_modelObjects.append(targetElement);
+ }
+}
+
void QDeclarativeListModel::emitItemsChanged(int index, int count, const QList<int> &roles)
{
if (m_mainThread) {
emit itemsChanged(index, count, roles);
} else {
- m_agent->data.changedChange(this, index, count, roles);
+ int uid = m_dynamicRoles ? getUid() : m_listModel->getUid();
+ m_agent->data.changedChange(uid, index, count, roles);
}
}
@@ -1374,9 +1611,10 @@ void QDeclarativeListModel::emitItemsRemoved(int index, int count)
emit itemsRemoved(index, count);
emit countChanged();
} else {
+ int uid = m_dynamicRoles ? getUid() : m_listModel->getUid();
if (index == 0 && count == this->count())
- m_agent->data.clearChange(this);
- m_agent->data.removeChange(this, index, count);
+ m_agent->data.clearChange(uid);
+ m_agent->data.removeChange(uid, index, count);
}
}
@@ -1386,7 +1624,8 @@ void QDeclarativeListModel::emitItemsInserted(int index, int count)
emit itemsInserted(index, count);
emit countChanged();
} else {
- m_agent->data.insertChange(this, index, count);
+ int uid = m_dynamicRoles ? getUid() : m_listModel->getUid();
+ m_agent->data.insertChange(uid, index, count);
}
}
@@ -1395,7 +1634,8 @@ void QDeclarativeListModel::emitItemsMoved(int from, int to, int n)
if (m_mainThread) {
emit itemsMoved(from, to, n);
} else {
- m_agent->data.moveChange(this, from, n, to);
+ int uid = m_dynamicRoles ? getUid() : m_listModel->getUid();
+ m_agent->data.moveChange(uid, from, n, to);
}
}
@@ -1412,24 +1652,90 @@ QList<int> QDeclarativeListModel::roles() const
{
QList<int> rolesArray;
- for (int i=0 ; i < m_listModel->roleCount() ; ++i)
- rolesArray << i;
+ if (m_dynamicRoles) {
+ for (int i=0 ; i < m_roles.count() ; ++i)
+ rolesArray << i;
+ } else {
+ for (int i=0 ; i < m_listModel->roleCount() ; ++i)
+ rolesArray << i;
+ }
return rolesArray;
}
QString QDeclarativeListModel::toString(int role) const
{
- const ListLayout::Role &r = m_listModel->getExistingRole(role);
- return r.name;
+ QString roleName;
+
+ if (m_dynamicRoles) {
+ roleName = m_roles[role];
+ } else {
+ const ListLayout::Role &r = m_listModel->getExistingRole(role);
+ roleName = r.name;
+ }
+
+ return roleName;
}
QVariant QDeclarativeListModel::data(int index, int role) const
{
+ QVariant v;
+
if (index >= count() || index < 0)
- return QVariant();
+ return v;
- return m_listModel->getProperty(index, role, this, engine());
+ if (m_dynamicRoles)
+ v = m_modelObjects[index]->getValue(m_roles[role]);
+ else
+ v = m_listModel->getProperty(index, role, this, engine());
+
+ return v;
+}
+
+/*!
+ \qmlproperty bool QtQuick2::ListModel::dynamicRoles
+
+ By default, the type of a role is fixed the first time
+ the role is used. For example, if you create a role called
+ "data" and assign a number to it, you can no longer assign
+ a string to the "data" role. However, when the dynamicRoles
+ property is enabled, the type of a given role is not fixed
+ and can be different between elements.
+
+ The dynamicRoles property must be set before any data is
+ added to the ListModel, and must be set from the main
+ thread.
+
+ A ListModel that has data statically defined (via the
+ ListElement QML syntax) cannot have the dynamicRoles
+ property enabled.
+
+ There is a significant performance cost to using a
+ ListModel with dynamic roles enabled. The cost varies
+ from platform to platform but is typically somewhere
+ between 4-6x slower than using static role types.
+
+ Due to the performance cost of using dynamic roles,
+ they are disabled by default.
+*/
+void QDeclarativeListModel::setDynamicRoles(bool enableDynamicRoles)
+{
+ if (m_mainThread && m_agent == 0) {
+ if (enableDynamicRoles) {
+ if (m_layout->roleCount())
+ qmlInfo(this) << tr("unable to enable dynamic roles as this model is not empty!");
+ else
+ m_dynamicRoles = true;
+ } else {
+ if (m_roles.count()) {
+ qmlInfo(this) << tr("unable to enable static roles as this model is not empty!");
+ } else {
+ m_dynamicRoles = false;
+ }
+ }
+ } else {
+ qmlInfo(this) << tr("dynamic role setting must be made from the main thread, before any worker scripts are created");
+ }
}
/*!
@@ -1438,7 +1744,15 @@ QVariant QDeclarativeListModel::data(int index, int role) const
*/
int QDeclarativeListModel::count() const
{
- return m_listModel->elementCount();
+ int count;
+
+ if (m_dynamicRoles)
+ count = m_modelObjects.count();
+ else {
+ count = m_listModel->elementCount();
+ }
+
+ return count;
}
/*!
@@ -1452,7 +1766,14 @@ void QDeclarativeListModel::clear()
{
int cleared = count();
- m_listModel->clear();
+ if (m_dynamicRoles) {
+ for (int i=0 ; i < m_modelObjects.count() ; ++i)
+ delete m_modelObjects[i];
+ m_modelObjects.clear();
+ } else {
+ m_listModel->clear();
+ }
+
emitItemsRemoved(0, cleared);
}
@@ -1476,7 +1797,14 @@ void QDeclarativeListModel::remove(QDeclarativeV8Function *args)
return;
}
- m_listModel->remove(index, removeCount);
+ if (m_dynamicRoles) {
+ for (int i=0 ; i < removeCount ; ++i)
+ delete m_modelObjects[index+i];
+ m_modelObjects.remove(index, removeCount);
+ } else {
+ m_listModel->remove(index, removeCount);
+ }
+
emitItemsRemoved(index, removeCount);
} else {
qmlInfo(this) << tr("remove: incorrect number of arguments");
@@ -1518,13 +1846,23 @@ void QDeclarativeListModel::insert(QDeclarativeV8Function *args)
int objectArrayLength = objectArray->Length();
for (int i=0 ; i < objectArrayLength ; ++i) {
v8::Handle<v8::Object> argObject = objectArray->Get(i)->ToObject();
- m_listModel->insert(index+i, argObject, args->engine());
+
+ if (m_dynamicRoles) {
+ m_modelObjects.insert(index+i, DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this));
+ } else {
+ m_listModel->insert(index+i, argObject, args->engine());
+ }
}
emitItemsInserted(index, objectArrayLength);
} else if (arg1->IsObject()) {
v8::Handle<v8::Object> argObject = arg1->ToObject();
- m_listModel->insert(index, argObject, args->engine());
+ if (m_dynamicRoles) {
+ m_modelObjects.insert(index, DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this));
+ } else {
+ m_listModel->insert(index, argObject, args->engine());
+ }
+
emitItemsInserted(index, 1);
} else {
qmlInfo(this) << tr("insert: value is not an object");
@@ -1557,7 +1895,33 @@ void QDeclarativeListModel::move(int from, int to, int n)
return;
}
- m_listModel->move(from, to, n);
+ if (m_dynamicRoles) {
+
+ int realFrom = from;
+ int realTo = to;
+ int realN = n;
+
+ if (from > to) {
+ // Only move forwards - flip if backwards moving
+ int tfrom = from;
+ int tto = to;
+ realFrom = tto;
+ realTo = tto+n;
+ realN = tfrom-tto;
+ }
+
+ QPODVector<DynamicRoleModelNode *, 4> store;
+ for (int i=0 ; i < (realTo-realFrom) ; ++i)
+ store.append(m_modelObjects[realFrom+realN+i]);
+ for (int i=0 ; i < realN ; ++i)
+ store.append(m_modelObjects[realFrom+i]);
+ for (int i=0 ; i < store.count() ; ++i)
+ m_modelObjects[realFrom+i] = store[i];
+
+ } else {
+ m_listModel->move(from, to, n);
+ }
+
emitItemsMoved(from, to, n);
}
@@ -1581,18 +1945,32 @@ void QDeclarativeListModel::append(QDeclarativeV8Function *args)
if (arg->IsArray()) {
v8::Handle<v8::Array> objectArray = v8::Handle<v8::Array>::Cast(arg);
int objectArrayLength = objectArray->Length();
- int index = m_listModel->elementCount();
+
+ int index = count();
for (int i=0 ; i < objectArrayLength ; ++i) {
v8::Handle<v8::Object> argObject = objectArray->Get(i)->ToObject();
- m_listModel->append(argObject, args->engine());
+
+ if (m_dynamicRoles) {
+ m_modelObjects.append(DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this));
+ } else {
+ m_listModel->append(argObject, args->engine());
+ }
}
+
emitItemsInserted(index, objectArrayLength);
} else if (arg->IsObject()) {
v8::Handle<v8::Object> argObject = arg->ToObject();
- int index = m_listModel->append(argObject, args->engine());
- emitItemsInserted(index, 1);
+ int index;
+
+ if (m_dynamicRoles) {
+ index = m_modelObjects.count();
+ m_modelObjects.append(DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this));
+ } else {
+ index = m_listModel->append(argObject, args->engine());
+ }
+ emitItemsInserted(index, 1);
} else {
qmlInfo(this) << tr("append: value is not an object");
}
@@ -1636,11 +2014,16 @@ QDeclarativeV8Handle QDeclarativeListModel::get(int index) const
{
v8::Handle<v8::Value> result = v8::Undefined();
- if (index >= 0 && index < m_listModel->elementCount()) {
+ if (index >= 0 && index < count()) {
QV8Engine *v8engine = engine();
- ModelObject *object = m_listModel->getOrCreateModelObject(const_cast<QDeclarativeListModel *>(this), index);
- result = v8engine->newQObject(object);
+ if (m_dynamicRoles) {
+ DynamicRoleModelNode *object = m_modelObjects[index];
+ result = v8engine->newQObject(object);
+ } else {
+ ModelObject *object = m_listModel->getOrCreateModelObject(const_cast<QDeclarativeListModel *>(this), index);
+ result = v8engine->newQObject(object);
+ }
}
return QDeclarativeV8Handle::fromHandle(result);
@@ -1678,12 +2061,23 @@ void QDeclarativeListModel::set(int index, const QDeclarativeV8Handle &handle)
v8::Handle<v8::Object> object = valuemap->ToObject();
if (index == count()) {
- m_listModel->insert(index, object, engine());
+
+ if (m_dynamicRoles) {
+ m_modelObjects.append(DynamicRoleModelNode::create(engine()->variantMapFromJS(object), this));
+ } else {
+ m_listModel->insert(index, object, engine());
+ }
+
emitItemsInserted(index, 1);
} else {
QList<int> roles;
- m_listModel->set(index, object, &roles, engine());
+
+ if (m_dynamicRoles) {
+ m_modelObjects[index]->updateValues(engine()->variantMapFromJS(object), roles);
+ } else {
+ m_listModel->set(index, object, &roles, engine());
+ }
if (roles.count())
emitItemsChanged(index, 1, roles);
@@ -1710,13 +2104,26 @@ void QDeclarativeListModel::setProperty(int index, const QString& property, cons
return;
}
- int roleIndex = m_listModel->setOrCreateProperty(index, property, value);
- if (roleIndex != -1) {
+ if (m_dynamicRoles) {
+ int roleIndex = m_roles.indexOf(property);
+ if (roleIndex == -1) {
+ roleIndex = m_roles.count();
+ m_roles.append(property);
+ }
+ if (m_modelObjects[index]->setValue(property.toUtf8(), value)) {
+ QList<int> roles;
+ roles << roleIndex;
+ emitItemsChanged(index, 1, roles);
+ }
+ } else {
+ int roleIndex = m_listModel->setOrCreateProperty(index, property, value);
+ if (roleIndex != -1) {
- QList<int> roles;
- roles << roleIndex;
+ QList<int> roles;
+ roles << roleIndex;
- emitItemsChanged(index, 1, roles);
+ emitItemsChanged(index, 1, roles);
+ }
}
}
@@ -1920,6 +2327,8 @@ void QDeclarativeListModelParser::setCustomData(QObject *obj, const QByteArray &
switch(instr.type) {
case ListInstruction::Push:
{
+ Q_ASSERT(!rv->m_dynamicRoles);
+
ListModel *subModel = 0;
if (stack.count() == 0) {
diff --git a/src/declarative/qml/qdeclarativelistmodel_p.h b/src/declarative/qml/qdeclarativelistmodel_p.h
index 2360f42dae..3b1e037351 100644
--- a/src/declarative/qml/qdeclarativelistmodel_p.h
+++ b/src/declarative/qml/qdeclarativelistmodel_p.h
@@ -69,6 +69,7 @@ class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeListModel : public QListModelInte
{
Q_OBJECT
Q_PROPERTY(int count READ count NOTIFY countChanged)
+ Q_PROPERTY(bool dynamicRoles READ dynamicRoles WRITE setDynamicRoles)
public:
QDeclarativeListModel(QObject *parent=0);
@@ -91,6 +92,9 @@ public:
QDeclarativeListModelWorkerAgent *agent();
+ bool dynamicRoles() const { return m_dynamicRoles; }
+ void setDynamicRoles(bool enableDynamicRoles);
+
Q_SIGNALS:
void countChanged();
@@ -101,6 +105,8 @@ private:
friend class ModelNodeMetaObject;
friend class ListModel;
friend class ListElement;
+ friend class DynamicRoleModelNode;
+ friend class DynamicRoleModelNodeMetaObject;
// Constructs a flat list model for a worker agent
QDeclarativeListModel(QDeclarativeListModel *orig, QDeclarativeListModelWorkerAgent *agent);
@@ -110,13 +116,32 @@ private:
inline bool canMove(int from, int to, int n) const { return !(from+n > count() || to+n > count() || from < 0 || to < 0 || n < 0); }
- ListLayout *m_layout;
- ListModel *m_listModel;
-
QDeclarativeListModelWorkerAgent *m_agent;
+ mutable QV8Engine *m_engine;
bool m_mainThread;
bool m_primary;
- mutable QV8Engine *m_engine;
+
+ bool m_dynamicRoles;
+
+ ListLayout *m_layout;
+ ListModel *m_listModel;
+
+ QVector<class DynamicRoleModelNode *> m_modelObjects;
+ QVector<QString> m_roles;
+ int m_uid;
+
+ struct ElementSync
+ {
+ ElementSync() : src(0), target(0) {}
+
+ DynamicRoleModelNode *src;
+ DynamicRoleModelNode *target;
+ };
+
+ int getUid() const { return m_uid; }
+
+ static void sync(QDeclarativeListModel *src, QDeclarativeListModel *target, QHash<int, QDeclarativeListModel *> *targetModelHash);
+ static QDeclarativeListModel *createWithOwner(QDeclarativeListModel *newOwner);
void emitItemsChanged(int index, int count, const QList<int> &roles);
void emitItemsRemoved(int index, int count);
diff --git a/src/declarative/qml/qdeclarativelistmodel_p_p.h b/src/declarative/qml/qdeclarativelistmodel_p_p.h
index ab6c2b25cc..1ef1a17fdd 100644
--- a/src/declarative/qml/qdeclarativelistmodel_p_p.h
+++ b/src/declarative/qml/qdeclarativelistmodel_p_p.h
@@ -64,6 +64,64 @@ QT_BEGIN_NAMESPACE
QT_MODULE(Declarative)
+class DynamicRoleModelNode;
+
+class DynamicRoleModelNodeMetaObject : public QDeclarativeOpenMetaObject
+{
+public:
+ DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object);
+ ~DynamicRoleModelNodeMetaObject();
+
+ bool m_enabled;
+
+protected:
+ void propertyWrite(int index);
+ void propertyWritten(int index);
+
+private:
+ DynamicRoleModelNode *m_owner;
+};
+
+class DynamicRoleModelNode : public QObject
+{
+ Q_OBJECT
+public:
+ DynamicRoleModelNode(QDeclarativeListModel *owner, int uid);
+
+ static DynamicRoleModelNode *create(const QVariantMap &obj, QDeclarativeListModel *owner);
+
+ void updateValues(const QVariantMap &object, QList<int> &roles);
+
+ QVariant getValue(const QString &name)
+ {
+ return m_meta->value(name.toUtf8());
+ }
+
+ bool setValue(const QByteArray &name, const QVariant &val)
+ {
+ return m_meta->setValue(name, val);
+ }
+
+ void setNodeUpdatesEnabled(bool enable)
+ {
+ m_meta->m_enabled = enable;
+ }
+
+ int getUid() const
+ {
+ return m_uid;
+ }
+
+ static void sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target, QHash<int, QDeclarativeListModel *> *targetModelHash);
+
+private:
+ QDeclarativeListModel *m_owner;
+ int m_uid;
+ DynamicRoleModelNodeMetaObject *m_meta;
+
+ friend class DynamicRoleModelNodeMetaObject;
+};
+
class ModelObject;
class ModelNodeMetaObject : public QDeclarativeOpenMetaObject
@@ -284,8 +342,6 @@ public:
int getUid() const { return m_uid; }
- static int allocateUid();
-
static void sync(ListModel *src, ListModel *target, QHash<int, ListModel *> *srcModelHash);
ModelObject *getOrCreateModelObject(QDeclarativeListModel *model, int elementIndex);
@@ -311,8 +367,6 @@ private:
friend class ListElement;
friend class QDeclarativeListModelWorkerAgent;
-
- static QAtomicInt uidCounter;
};
QT_END_NAMESPACE
diff --git a/src/declarative/qml/qdeclarativelistmodelworkeragent.cpp b/src/declarative/qml/qdeclarativelistmodelworkeragent.cpp
index c01025cd45..3904d35a92 100644
--- a/src/declarative/qml/qdeclarativelistmodelworkeragent.cpp
+++ b/src/declarative/qml/qdeclarativelistmodelworkeragent.cpp
@@ -53,10 +53,8 @@
QT_BEGIN_NAMESPACE
-void QDeclarativeListModelWorkerAgent::Data::clearChange(QDeclarativeListModel *model)
+void QDeclarativeListModelWorkerAgent::Data::clearChange(int uid)
{
- int uid = model->m_listModel->getUid();
-
for (int i=0 ; i < changes.count() ; ++i) {
if (changes[i].modelUid == uid) {
changes.removeAt(i);
@@ -65,27 +63,27 @@ void QDeclarativeListModelWorkerAgent::Data::clearChange(QDeclarativeListModel *
}
}
-void QDeclarativeListModelWorkerAgent::Data::insertChange(QDeclarativeListModel *model, int index, int count)
+void QDeclarativeListModelWorkerAgent::Data::insertChange(int uid, int index, int count)
{
- Change c = { model->m_listModel->getUid(), Change::Inserted, index, count, 0, QList<int>() };
+ Change c = { uid, Change::Inserted, index, count, 0, QList<int>() };
changes << c;
}
-void QDeclarativeListModelWorkerAgent::Data::removeChange(QDeclarativeListModel *model, int index, int count)
+void QDeclarativeListModelWorkerAgent::Data::removeChange(int uid, int index, int count)
{
- Change c = { model->m_listModel->getUid(), Change::Removed, index, count, 0, QList<int>() };
+ Change c = { uid, Change::Removed, index, count, 0, QList<int>() };
changes << c;
}
-void QDeclarativeListModelWorkerAgent::Data::moveChange(QDeclarativeListModel *model, int index, int count, int to)
+void QDeclarativeListModelWorkerAgent::Data::moveChange(int uid, int index, int count, int to)
{
- Change c = { model->m_listModel->getUid(), Change::Moved, index, count, to, QList<int>() };
+ Change c = { uid, Change::Moved, index, count, to, QList<int>() };
changes << c;
}
-void QDeclarativeListModelWorkerAgent::Data::changedChange(QDeclarativeListModel *model, int index, int count, const QList<int> &roles)
+void QDeclarativeListModelWorkerAgent::Data::changedChange(int uid, int index, int count, const QList<int> &roles)
{
- Change c = { model->m_listModel->getUid(), Change::Changed, index, count, 0, roles };
+ Change c = { uid, Change::Changed, index, count, 0, roles };
changes << c;
}
@@ -181,27 +179,40 @@ bool QDeclarativeListModelWorkerAgent::event(QEvent *e)
bool cc = m_orig->count() != s->list->count();
- QHash<int, ListModel *> targetModelHash;
- ListModel::sync(s->list->m_listModel, m_orig->m_listModel, &targetModelHash);
+ QHash<int, QDeclarativeListModel *> targetModelDynamicHash;
+ QHash<int, ListModel *> targetModelStaticHash;
+
+ Q_ASSERT(m_orig->m_dynamicRoles == s->list->m_dynamicRoles);
+ if (m_orig->m_dynamicRoles)
+ QDeclarativeListModel::sync(s->list, m_orig, &targetModelDynamicHash);
+ else
+ ListModel::sync(s->list->m_listModel, m_orig->m_listModel, &targetModelStaticHash);
for (int ii = 0; ii < changes.count(); ++ii) {
const Change &change = changes.at(ii);
- ListModel *model = targetModelHash.value(change.modelUid);
+ QDeclarativeListModel *model = 0;
+ if (m_orig->m_dynamicRoles) {
+ model = targetModelDynamicHash.value(change.modelUid);
+ } else {
+ ListModel *lm = targetModelStaticHash.value(change.modelUid);
+ if (lm)
+ model = lm->m_modelCache;
+ }
- if (model && model->m_modelCache) {
+ if (model) {
switch (change.type) {
case Change::Inserted:
- emit model->m_modelCache->itemsInserted(change.index, change.count);
+ emit model->itemsInserted(change.index, change.count);
break;
case Change::Removed:
- emit model->m_modelCache->itemsRemoved(change.index, change.count);
+ emit model->itemsRemoved(change.index, change.count);
break;
case Change::Moved:
- emit model->m_modelCache->itemsMoved(change.index, change.to, change.count);
+ emit model->itemsMoved(change.index, change.to, change.count);
break;
case Change::Changed:
- emit model->m_modelCache->itemsChanged(change.index, change.count, change.roles);
+ emit model->itemsChanged(change.index, change.count, change.roles);
break;
}
}
diff --git a/src/declarative/qml/qdeclarativelistmodelworkeragent_p.h b/src/declarative/qml/qdeclarativelistmodelworkeragent_p.h
index 05c36473a5..d27a05f94d 100644
--- a/src/declarative/qml/qdeclarativelistmodelworkeragent_p.h
+++ b/src/declarative/qml/qdeclarativelistmodelworkeragent_p.h
@@ -130,11 +130,11 @@ private:
{
QList<Change> changes;
- void clearChange(QDeclarativeListModel *model);
- void insertChange(QDeclarativeListModel *model, int index, int count);
- void removeChange(QDeclarativeListModel *model, int index, int count);
- void moveChange(QDeclarativeListModel *model, int index, int count, int to);
- void changedChange(QDeclarativeListModel *model, int index, int count, const QList<int> &roles);
+ void clearChange(int uid);
+ void insertChange(int uid, int index, int count);
+ void removeChange(int uid, int index, int count);
+ void moveChange(int uid, int index, int count, int to);
+ void changedChange(int uid, int index, int count, const QList<int> &roles);
};
Data data;
diff --git a/src/declarative/qml/qdeclarativeopenmetaobject.cpp b/src/declarative/qml/qdeclarativeopenmetaobject.cpp
index d4dd8242c4..9e4cfc597d 100644
--- a/src/declarative/qml/qdeclarativeopenmetaobject.cpp
+++ b/src/declarative/qml/qdeclarativeopenmetaobject.cpp
@@ -287,7 +287,7 @@ QVariant &QDeclarativeOpenMetaObject::operator[](int id)
return d->getData(id);
}
-void QDeclarativeOpenMetaObject::setValue(const QByteArray &name, const QVariant &val)
+bool QDeclarativeOpenMetaObject::setValue(const QByteArray &name, const QVariant &val)
{
QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.find(name);
@@ -301,11 +301,14 @@ void QDeclarativeOpenMetaObject::setValue(const QByteArray &name, const QVariant
if (id >= 0) {
QVariant &dataVal = d->getData(id);
if (dataVal == val)
- return;
+ return false;
dataVal = val;
activate(d->object, id + d->type->d->signalOffset, 0);
+ return true;
}
+
+ return false;
}
// returns true if this value has been initialized by a call to either value() or setValue()
diff --git a/src/declarative/qml/qdeclarativeopenmetaobject_p.h b/src/declarative/qml/qdeclarativeopenmetaobject_p.h
index e5cfee7eb6..8f711d1b42 100644
--- a/src/declarative/qml/qdeclarativeopenmetaobject_p.h
+++ b/src/declarative/qml/qdeclarativeopenmetaobject_p.h
@@ -89,7 +89,7 @@ public:
~QDeclarativeOpenMetaObject();
QVariant value(const QByteArray &) const;
- void setValue(const QByteArray &, const QVariant &);
+ bool setValue(const QByteArray &, const QVariant &);
QVariant value(int) const;
void setValue(int, const QVariant &);
QVariant &operator[](const QByteArray &);