diff options
Diffstat (limited to 'src/qml/types')
-rw-r--r-- | src/qml/types/qqmldelegatemodel.cpp | 65 | ||||
-rw-r--r-- | src/qml/types/qqmldelegatemodel_p_p.h | 6 | ||||
-rw-r--r-- | src/qml/types/qqmllistmodel.cpp | 405 | ||||
-rw-r--r-- | src/qml/types/qqmllistmodel_p.h | 23 | ||||
-rw-r--r-- | src/qml/types/qqmllistmodel_p_p.h | 20 | ||||
-rw-r--r-- | src/qml/types/qqmllistmodelworkeragent.cpp | 89 | ||||
-rw-r--r-- | src/qml/types/qqmllistmodelworkeragent_p.h | 27 | ||||
-rw-r--r-- | src/qml/types/qquickworkerscript.cpp | 49 |
8 files changed, 371 insertions, 313 deletions
diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index dc41a16e3b..ab22f97adf 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -96,17 +96,16 @@ struct DelegateModelGroupFunction : QV4::FunctionObject return scope->engine()->memoryManager->allocObject<DelegateModelGroupFunction>(scope, flag, code); } - static void call(const QV4::Managed *that, QV4::Scope &scope, QV4::CallData *callData) + static ReturnedValue call(const QV4::FunctionObject *that, const Value *thisObject, const Value *argv, int argc) { + QV4::Scope scope(that->engine()); QV4::Scoped<DelegateModelGroupFunction> f(scope, static_cast<const DelegateModelGroupFunction *>(that)); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject); - if (!o) { - scope.result = scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")); - return; - } + QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject); + if (!o) + return scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")); - QV4::ScopedValue v(scope, callData->argument(0)); - scope.result = f->d()->code(o->d()->item, f->d()->flag, v); + QV4::ScopedValue v(scope, argc ? argv[0] : Primitive::undefinedValue()); + return f->d()->code(o->d()->item, f->d()->flag, v); } }; @@ -1814,26 +1813,24 @@ int QQmlDelegateModelItemMetaType::parseGroups(const QV4::Value &groups) const return groupFlags; } -void QQmlDelegateModelItem::get_model(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +QV4::ReturnedValue QQmlDelegateModelItem::get_model(const QV4::BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>()); - if (!o) { - scope.result = scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")); - return; - } + if (!o) + return b->engine()->throwTypeError(QStringLiteral("Not a valid VisualData object")); if (!o->d()->item->metaType->model) RETURN_UNDEFINED(); - scope.result = o->d()->item->get(); + return o->d()->item->get(); } -void QQmlDelegateModelItem::get_groups(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +QV4::ReturnedValue QQmlDelegateModelItem::get_groups(const QV4::BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>()); - if (!o) { - scope.result = scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")); - return; - } + if (!o) + return scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")); QStringList groups; for (int i = 1; i < o->d()->item->metaType->groupCount; ++i) { @@ -1841,18 +1838,17 @@ void QQmlDelegateModelItem::get_groups(const QV4::BuiltinFunction *, QV4::Scope groups.append(o->d()->item->metaType->groupNames.at(i - 1)); } - scope.result = scope.engine->fromVariant(groups); + return scope.engine->fromVariant(groups); } -void QQmlDelegateModelItem::set_groups(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +QV4::ReturnedValue QQmlDelegateModelItem::set_groups(const QV4::BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>()); - if (!o) { - scope.result = scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")); - return; - } + if (!o) + return scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")); - if (!callData->argc) + if (!callData->argc()) THROW_TYPE_ERROR(); if (!o->d()->item->metaType->model) @@ -1863,7 +1859,7 @@ void QQmlDelegateModelItem::set_groups(const QV4::BuiltinFunction *, QV4::Scope const int cacheIndex = model->m_cache.indexOf(o->d()->item); Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); model->setGroups(it, 1, Compositor::Cache, groupFlags); - scope.result = QV4::Encode::undefined(); + return QV4::Encode::undefined(); } QV4::ReturnedValue QQmlDelegateModelItem::get_member(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &) @@ -3245,25 +3241,28 @@ struct QQmlDelegateModelGroupChange : QV4::Object return e->memoryManager->allocObject<QQmlDelegateModelGroupChange>(); } - static void method_get_index(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { + static QV4::ReturnedValue method_get_index(const QV4::BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); QV4::Scoped<QQmlDelegateModelGroupChange> that(scope, callData->thisObject.as<QQmlDelegateModelGroupChange>()); if (!that) THROW_TYPE_ERROR(); - scope.result = QV4::Encode(that->d()->change.index); + return QV4::Encode(that->d()->change.index); } - static void method_get_count(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { + static QV4::ReturnedValue method_get_count(const QV4::BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); QV4::Scoped<QQmlDelegateModelGroupChange> that(scope, callData->thisObject.as<QQmlDelegateModelGroupChange>()); if (!that) THROW_TYPE_ERROR(); - scope.result = QV4::Encode(that->d()->change.count); + return QV4::Encode(that->d()->change.count); } - static void method_get_moveId(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { + static QV4::ReturnedValue method_get_moveId(const QV4::BuiltinFunction *b, QV4::CallData *callData) { + QV4::Scope scope(b); QV4::Scoped<QQmlDelegateModelGroupChange> that(scope, callData->thisObject.as<QQmlDelegateModelGroupChange>()); if (!that) THROW_TYPE_ERROR(); if (that->d()->change.moveId < 0) RETURN_UNDEFINED(); - scope.result = QV4::Encode(that->d()->change.moveId); + return QV4::Encode(that->d()->change.moveId); } }; diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h index 6d061cdce4..5f72bd66be 100644 --- a/src/qml/types/qqmldelegatemodel_p_p.h +++ b/src/qml/types/qqmldelegatemodel_p_p.h @@ -126,9 +126,9 @@ public: virtual void setValue(const QString &role, const QVariant &value) { Q_UNUSED(role); Q_UNUSED(value); } virtual bool resolveIndex(const QQmlAdaptorModel &, int) { return false; } - static void get_model(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void get_groups(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void set_groups(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static QV4::ReturnedValue get_model(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue get_groups(const QV4::BuiltinFunction *, QV4::CallData *callData); + static QV4::ReturnedValue set_groups(const QV4::BuiltinFunction *, QV4::CallData *callData); static QV4::ReturnedValue get_member(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &); static QV4::ReturnedValue set_member(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &arg); static QV4::ReturnedValue get_index(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &arg); diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index d72d2e9487..0f04d48bf8 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -42,6 +42,7 @@ #include <private/qqmlopenmetaobject_p.h> #include <private/qqmljsast_p.h> #include <private/qqmljsengine_p.h> +#include <private/qjsvalue_p.h> #include <private/qqmlcustomparser_p.h> #include <private/qqmlengine_p.h> @@ -84,7 +85,7 @@ static QString roleTypeName(ListLayout::Role::DataType t) static const QString roleTypeNames[] = { QStringLiteral("String"), QStringLiteral("Number"), QStringLiteral("Bool"), QStringLiteral("List"), QStringLiteral("QObject"), QStringLiteral("VariantMap"), - QStringLiteral("DateTime") + QStringLiteral("DateTime"), QStringLiteral("Function") }; if (t > ListLayout::Role::Invalid && t < ListLayout::Role::MaxDataType) @@ -123,8 +124,8 @@ const ListLayout::Role &ListLayout::getRoleOrCreate(QV4::String *key, Role::Data const ListLayout::Role &ListLayout::createRole(const QString &key, ListLayout::Role::DataType type) { - const int dataSizes[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QPointer<QObject>), sizeof(QVariantMap), sizeof(QDateTime) }; - const int dataAlignments[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap), sizeof(QDateTime) }; + const int dataSizes[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QPointer<QObject>), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) }; + const int dataAlignments[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) }; Role *r = new Role; r->name = key; @@ -217,11 +218,20 @@ const ListLayout::Role *ListLayout::getRoleOrCreate(const QString &key, const QV switch (data.type()) { case QVariant::Double: type = Role::Number; break; case QVariant::Int: type = Role::Number; break; - 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; case QVariant::DateTime: type = Role::DateTime; break; + case QVariant::UserType: { + if (data.userType() == qMetaTypeId<QJSValue>() && + data.value<QJSValue>().isCallable()) { + type = Role::Function; + break; + } else { + type = Role::List; + break; + } + } default: type = Role::Invalid; break; } @@ -261,23 +271,24 @@ QObject *ListModel::getOrCreateModelObject(QQmlListModel *model, int elementInde return e->m_objectCache; } -void ListModel::sync(ListModel *src, ListModel *target, QHash<int, ListModel *> *targetModelHash) +bool ListModel::sync(ListModel *src, ListModel *target) { // Sanity check target->m_uid = src->m_uid; - if (targetModelHash) - targetModelHash->insert(target->m_uid, target); + + bool hasChanges = false; // Build hash of elements <-> uid for each of the lists QHash<int, ElementSync> elementHash; - for (int i=0 ; i < target->elements.count() ; ++i) { + for (int i = 0; i < target->elements.count(); ++i) { ListElement *e = target->elements.at(i); int uid = e->getUid(); ElementSync sync; sync.target = e; + sync.targetIndex = i; elementHash.insert(uid, sync); } - for (int i=0 ; i < src->elements.count() ; ++i) { + for (int i = 0; i < src->elements.count(); ++i) { ListElement *e = src->elements.at(i); int uid = e->getUid(); @@ -285,24 +296,39 @@ void ListModel::sync(ListModel *src, ListModel *target, QHash<int, ListModel *> if (it == elementHash.end()) { ElementSync sync; sync.src = e; + sync.srcIndex = i; elementHash.insert(uid, sync); } else { ElementSync &sync = it.value(); sync.src = e; + sync.srcIndex = i; } } + QQmlListModel *targetModel = target->m_modelCache; + // 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 rowsRemoved = 0; + for (int i = 0 ; i < target->elements.count() ; ++i) { + ListElement *element = target->elements.at(i); + ElementSync &s = elementHash.find(element->getUid()).value(); + Q_ASSERT(s.targetIndex >= 0); + // need to update the targetIndex, to keep it correct after removals + s.targetIndex -= rowsRemoved; + if (s.src == nullptr) { + Q_ASSERT(s.targetIndex == i); + hasChanges = true; + if (targetModel) + targetModel->beginRemoveRows(QModelIndex(), i, i); s.target->destroy(target->m_layout); target->elements.removeOne(s.target); delete s.target; + if (targetModel) + targetModel->endRemoveRows(); + ++rowsRemoved; + --i; + continue; } - ++it; } // Sync the layouts @@ -310,15 +336,15 @@ void ListModel::sync(ListModel *src, ListModel *target, QHash<int, ListModel *> // Clear the target list, and append in correct order from the source target->elements.clear(); - for (int i=0 ; i < src->elements.count() ; ++i) { + for (int i = 0; i < src->elements.count(); ++i) { ListElement *srcElement = src->elements.at(i); - it = elementHash.find(srcElement->getUid()); - const ElementSync &s = it.value(); + ElementSync &s = elementHash.find(srcElement->getUid()).value(); + Q_ASSERT(s.srcIndex >= 0); ListElement *targetElement = s.target; - if (targetElement == 0) { + if (targetElement == nullptr) { targetElement = new ListElement(srcElement->getUid()); } - ListElement::sync(srcElement, src->m_layout, targetElement, target->m_layout, targetModelHash); + s.changedRoles = ListElement::sync(srcElement, src->m_layout, targetElement, target->m_layout); target->elements.append(targetElement); } @@ -330,6 +356,39 @@ void ListModel::sync(ListModel *src, ListModel *target, QHash<int, ListModel *> if (ModelNodeMetaObject *mo = e->objectCache()) mo->updateValues(); } + + // now emit the change notifications required. This can be safely done, as we're only emitting changes, moves and inserts, + // so the model indices can't be out of bounds + // + // to ensure things are kept in the correct order, emit inserts and moves first. This shouls ensure all persistent + // model indices are updated correctly + int rowsInserted = 0; + for (int i = 0 ; i < target->elements.count() ; ++i) { + ListElement *element = target->elements.at(i); + ElementSync &s = elementHash.find(element->getUid()).value(); + Q_ASSERT(s.srcIndex >= 0); + s.srcIndex += rowsInserted; + if (s.srcIndex != s.targetIndex) { + if (targetModel) { + if (s.targetIndex == -1) { + targetModel->beginInsertRows(QModelIndex(), i, i); + targetModel->endInsertRows(); + } else { + targetModel->beginMoveRows(QModelIndex(), i, i, QModelIndex(), s.srcIndex); + targetModel->endMoveRows(); + } + } + hasChanges = true; + ++rowsInserted; + } + if (s.targetIndex != -1 && !s.changedRoles.isEmpty()) { + QModelIndex idx = targetModel->createIndex(i, 0); + if (targetModel) + targetModel->dataChanged(idx, idx, s.changedRoles); + hasChanges = true; + } + } + return hasChanges; } ListModel::ListModel(ListLayout *layout, QQmlListModel *modelCache, int uid) : m_layout(layout), m_modelCache(modelCache) @@ -465,6 +524,12 @@ void ListModel::set(int elementIndex, QV4::Object *object, QVector<int> *roles) const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime); QDateTime dt = dd->toQDateTime(); roleIndex = e->setDateTimeProperty(r, dt); + } else if (QV4::FunctionObject *f = propertyValue->as<QV4::FunctionObject>()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Function); + QV4::ScopedFunctionObject func(scope, f); + QJSValue jsv; + QJSValuePrivate::setValue(&jsv, v4, func); + roleIndex = e->setFunctionProperty(r, jsv); } else if (QV4::Object *o = propertyValue->as<QV4::Object>()) { if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) { QObject *o = wrapper->object(); @@ -688,6 +753,17 @@ QDateTime *ListElement::getDateTimeProperty(const ListLayout::Role &role) return dt; } +QJSValue *ListElement::getFunctionProperty(const ListLayout::Role &role) +{ + QJSValue *f = 0; + + char *mem = getPropertyMemory(role); + if (isMemoryUsed<QJSValue>(mem)) + f = reinterpret_cast<QJSValue *>(mem); + + return f; +} + QPointer<QObject> *ListElement::getGuardProperty(const ListLayout::Role &role) { char *mem = getPropertyMemory(role); @@ -781,6 +857,14 @@ QVariant ListElement::getProperty(const ListLayout::Role &role, const QQmlListMo } } break; + case ListLayout::Role::Function: + { + if (isMemoryUsed<QJSValue>(mem)) { + QJSValue *func = reinterpret_cast<QJSValue *>(mem); + data = QVariant::fromValue(*func); + } + } + break; default: break; } @@ -914,7 +998,11 @@ int ListElement::setVariantMapProperty(const ListLayout::Role &role, QVariantMap char *mem = getPropertyMemory(role); if (isMemoryUsed<QVariantMap>(mem)) { QVariantMap *map = reinterpret_cast<QVariantMap *>(mem); + if (m && map->isSharedWith(*m)) + return roleIndex; map->~QMap(); + } else if (!m) { + return roleIndex; } if (m) new (mem) QVariantMap(*m); @@ -943,6 +1031,24 @@ int ListElement::setDateTimeProperty(const ListLayout::Role &role, const QDateTi return roleIndex; } +int ListElement::setFunctionProperty(const ListLayout::Role &role, const QJSValue &f) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::Function) { + char *mem = getPropertyMemory(role); + if (isMemoryUsed<QJSValue>(mem)) { + QJSValue *f = reinterpret_cast<QJSValue *>(mem); + f->~QJSValue(); + } + new (mem) QJSValue(f); + roleIndex = role.index; + } + + return roleIndex; +} + + void ListElement::setStringPropertyFast(const ListLayout::Role &role, const QString &s) { char *mem = getPropertyMemory(role); @@ -989,6 +1095,12 @@ void ListElement::setDateTimePropertyFast(const ListLayout::Role &role, const QD new (mem) QDateTime(dt); } +void ListElement::setFunctionPropertyFast(const ListLayout::Role &role, const QJSValue &f) +{ + char *mem = getPropertyMemory(role); + new (mem) QJSValue(f); +} + void ListElement::clearProperty(const ListLayout::Role &role) { switch (role.type) { @@ -1013,6 +1125,9 @@ void ListElement::clearProperty(const ListLayout::Role &role) case ListLayout::Role::VariantMap: setVariantMapProperty(role, (QVariantMap *)0); break; + case ListLayout::Role::Function: + setFunctionProperty(role, QJSValue()); + break; default: break; } @@ -1039,12 +1154,14 @@ ListElement::~ListElement() delete next; } -void ListElement::sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout, QHash<int, ListModel *> *targetModelHash) +QVector<int> ListElement::sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout) { + QVector<int> changedRoles; for (int i=0 ; i < srcLayout->roleCount() ; ++i) { const ListLayout::Role &srcRole = srcLayout->getExistingRole(i); const ListLayout::Role &targetRole = targetLayout->getExistingRole(i); + int roleIndex = -1; switch (srcRole.type) { case ListLayout::Role::List: { @@ -1056,36 +1173,41 @@ void ListElement::sync(ListElement *src, ListLayout *srcLayout, ListElement *tar targetSubModel = new ListModel(targetRole.subLayout, 0, srcSubModel->getUid()); target->setListPropertyFast(targetRole, targetSubModel); } - ListModel::sync(srcSubModel, targetSubModel, targetModelHash); + if (ListModel::sync(srcSubModel, targetSubModel)) + roleIndex = targetRole.index; } } break; case ListLayout::Role::QObject: { QObject *object = src->getQObjectProperty(srcRole); - target->setQObjectProperty(targetRole, object); + roleIndex = target->setQObjectProperty(targetRole, object); } break; case ListLayout::Role::String: case ListLayout::Role::Number: case ListLayout::Role::Bool: case ListLayout::Role::DateTime: + case ListLayout::Role::Function: { QVariant v = src->getProperty(srcRole, 0, 0); - target->setVariantProperty(targetRole, v); + roleIndex = target->setVariantProperty(targetRole, v); } break; case ListLayout::Role::VariantMap: { QVariantMap *map = src->getVariantMapProperty(srcRole); - target->setVariantMapProperty(targetRole, map); + roleIndex = target->setVariantMapProperty(targetRole, map); } break; default: break; } + if (roleIndex >= 0) + changedRoles << roleIndex; } + return changedRoles; } void ListElement::destroy(ListLayout *layout) @@ -1132,6 +1254,13 @@ void ListElement::destroy(ListLayout *layout) dt->~QDateTime(); } break; + case ListLayout::Role::Function: + { + QJSValue *f = getFunctionProperty(r); + if (f) + f->~QJSValue(); + } + break; default: // other types don't need explicit cleanup. break; @@ -1171,6 +1300,9 @@ int ListElement::setVariantProperty(const ListLayout::Role &role, const QVariant case ListLayout::Role::DateTime: roleIndex = setDateTimeProperty(role, d.toDateTime()); break; + case ListLayout::Role::Function: + roleIndex = setFunctionProperty(role, d.value<QJSValue>()); + break; default: break; } @@ -1213,6 +1345,11 @@ int ListElement::setJsProperty(const ListLayout::Role &role, const QV4::Value &d QV4::Scoped<QV4::DateObject> dd(scope, d); QDateTime dt = dd->toQDateTime(); roleIndex = setDateTimeProperty(role, dt); + } else if (d.as<QV4::FunctionObject>()) { + QV4::ScopedFunctionObject f(scope, d); + QJSValue jsv; + QJSValuePrivate::setValue(&jsv, eng, f); + roleIndex = setFunctionProperty(role, jsv); } else if (d.isObject()) { QV4::ScopedObject o(scope, d); QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>(); @@ -1415,20 +1552,22 @@ DynamicRoleModelNode *DynamicRoleModelNode::create(const QVariantMap &obj, QQmlL return object; } -void DynamicRoleModelNode::sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target, QHash<int, QQmlListModel *> *targetModelHash) +QVector<int> DynamicRoleModelNode::sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target) { - for (int i=0 ; i < src->m_meta->count() ; ++i) { + QVector<int> changedRoles; + 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); QQmlListModel *srcModel = qobject_cast<QQmlListModel *>(value.value<QObject *>()); QQmlListModel *targetModel = qobject_cast<QQmlListModel *>(target->m_meta->value(i).value<QObject *>()); + bool modelHasChanges = false; if (srcModel) { if (targetModel == 0) targetModel = QQmlListModel::createWithOwner(target->m_owner); - QQmlListModel::sync(srcModel, targetModel, targetModelHash); + modelHasChanges = QQmlListModel::sync(srcModel, targetModel); QObject *targetModelObject = targetModel; value = QVariant::fromValue(targetModelObject); @@ -1436,8 +1575,10 @@ void DynamicRoleModelNode::sync(DynamicRoleModelNode *src, DynamicRoleModelNode delete targetModel; } - target->setValue(name, value); + if (target->setValue(name, value) || modelHasChanges) + changedRoles << target->m_owner->m_roles.indexOf(QString::fromUtf8(name)); } + return changedRoles; } void DynamicRoleModelNode::updateValues(const QVariantMap &object, QVector<int> &roles) @@ -1683,9 +1824,9 @@ QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agen m_listModel = new ListModel(m_layout, this, orig->m_listModel->getUid()); if (m_dynamicRoles) - sync(orig, this, 0); + sync(orig, this); else - ListModel::sync(orig->m_listModel, m_listModel, 0); + ListModel::sync(orig->m_listModel, m_listModel); m_engine = 0; } @@ -1736,25 +1877,26 @@ QV4::ExecutionEngine *QQmlListModel::engine() const return m_engine; } -void QQmlListModel::sync(QQmlListModel *src, QQmlListModel *target, QHash<int, QQmlListModel *> *targetModelHash) +bool QQmlListModel::sync(QQmlListModel *src, QQmlListModel *target) { Q_ASSERT(src->m_dynamicRoles && target->m_dynamicRoles); + bool hasChanges = false; + 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) { + 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; + sync.targetIndex = i; elementHash.insert(uid, sync); } - for (int i=0 ; i < src->m_modelObjects.count() ; ++i) { + for (int i = 0 ; i < src->m_modelObjects.count(); ++i) { DynamicRoleModelNode *e = src->m_modelObjects.at(i); int uid = e->getUid(); @@ -1762,118 +1904,102 @@ void QQmlListModel::sync(QQmlListModel *src, QQmlListModel *target, QHash<int, Q if (it == elementHash.end()) { ElementSync sync; sync.src = e; + sync.srcIndex = i; elementHash.insert(uid, sync); } else { ElementSync &sync = it.value(); sync.src = e; + sync.srcIndex = i; } } // 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); + int rowsRemoved = 0; + for (int i = 0 ; i < target->m_modelObjects.count() ; ++i) { + DynamicRoleModelNode *element = target->m_modelObjects.at(i); + ElementSync &s = elementHash.find(element->getUid()).value(); + Q_ASSERT(s.targetIndex >= 0); + // need to update the targetIndex, to keep it correct after removals + s.targetIndex -= rowsRemoved; + if (s.src == nullptr) { + Q_ASSERT(s.targetIndex == i); + hasChanges = true; + target->beginRemoveRows(QModelIndex(), i, i); + target->m_modelObjects.remove(i, 1); + target->endRemoveRows(); delete s.target; + ++rowsRemoved; + --i; + continue; } - ++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(); + for (int i = 0 ; i < src->m_modelObjects.count() ; ++i) { + DynamicRoleModelNode *element = src->m_modelObjects.at(i); + ElementSync &s = elementHash.find(element->getUid()).value(); + Q_ASSERT(s.srcIndex >= 0); DynamicRoleModelNode *targetElement = s.target; if (targetElement == 0) { - targetElement = new DynamicRoleModelNode(target, srcElement->getUid()); + targetElement = new DynamicRoleModelNode(target, element->getUid()); } - DynamicRoleModelNode::sync(srcElement, targetElement, targetModelHash); + s.changedRoles = DynamicRoleModelNode::sync(element, targetElement); target->m_modelObjects.append(targetElement); } -} -void QQmlListModel::emitItemsChanged(int index, int count, const QVector<int> &roles) -{ - if (count <= 0) - return; - - if (m_mainThread) { - emit dataChanged(createIndex(index, 0), createIndex(index + count - 1, 0), roles);; - } else { - int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); - m_agent->data.changedChange(uid, index, count, roles); + // now emit the change notifications required. This can be safely done, as we're only emitting changes, moves and inserts, + // so the model indices can't be out of bounds + // + // to ensure things are kept in the correct order, emit inserts and moves first. This shouls ensure all persistent + // model indices are updated correctly + int rowsInserted = 0; + for (int i = 0 ; i < target->m_modelObjects.count() ; ++i) { + DynamicRoleModelNode *element = target->m_modelObjects.at(i); + ElementSync &s = elementHash.find(element->getUid()).value(); + Q_ASSERT(s.srcIndex >= 0); + s.srcIndex += rowsInserted; + if (s.srcIndex != s.targetIndex) { + if (s.targetIndex == -1) { + target->beginInsertRows(QModelIndex(), i, i); + target->endInsertRows(); + } else { + target->beginMoveRows(QModelIndex(), i, i, QModelIndex(), s.srcIndex); + target->endMoveRows(); + } + hasChanges = true; + ++rowsInserted; + } + if (s.targetIndex != -1 && !s.changedRoles.isEmpty()) { + QModelIndex idx = target->createIndex(i, 0); + emit target->dataChanged(idx, idx, s.changedRoles); + hasChanges = true; + } } + return hasChanges; } -void QQmlListModel::emitItemsAboutToBeRemoved(int index, int count) -{ - if (count <= 0 || !m_mainThread) - return; - - beginRemoveRows(QModelIndex(), index, index + count - 1); -} - -void QQmlListModel::emitItemsRemoved(int index, int count) +void QQmlListModel::emitItemsChanged(int index, int count, const QVector<int> &roles) { if (count <= 0) return; - if (m_mainThread) { - endRemoveRows(); - emit countChanged(); - } else { - int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); - if (index == 0 && count == this->count()) - m_agent->data.clearChange(uid); - m_agent->data.removeChange(uid, index, count); - } + if (m_mainThread) + emit dataChanged(createIndex(index, 0), createIndex(index + count - 1, 0), roles);; } void QQmlListModel::emitItemsAboutToBeInserted(int index, int count) { - if (count <= 0 || !m_mainThread) - return; - - beginInsertRows(QModelIndex(), index, index + count - 1); + Q_ASSERT(index >= 0 && count >= 0); + if (m_mainThread) + beginInsertRows(QModelIndex(), index, index + count - 1); } -void QQmlListModel::emitItemsInserted(int index, int count) +void QQmlListModel::emitItemsInserted() { - if (count <= 0) - return; - if (m_mainThread) { endInsertRows(); emit countChanged(); - } else { - int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); - m_agent->data.insertChange(uid, index, count); - } -} - -void QQmlListModel::emitItemsAboutToBeMoved(int from, int to, int n) -{ - if (n <= 0 || !m_mainThread) - return; - - beginMoveRows(QModelIndex(), from, from + n - 1, QModelIndex(), to > from ? to + n : to); -} - -void QQmlListModel::emitItemsMoved(int from, int to, int n) -{ - if (n <= 0) - return; - - if (m_mainThread) { - endMoveRows(); - } else { - int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); - m_agent->data.moveChange(uid, from, n, to); } } @@ -2055,7 +2181,13 @@ void QQmlListModel::remove(QQmlV4Function *args) void QQmlListModel::removeElements(int index, int removeCount) { - emitItemsAboutToBeRemoved(index, removeCount); + Q_ASSERT(index >= 0 && removeCount >= 0); + + if (!removeCount) + return; + + if (m_mainThread) + beginRemoveRows(QModelIndex(), index, index + removeCount - 1); QVector<std::function<void()>> toDestroy; if (m_dynamicRoles) { @@ -2070,7 +2202,10 @@ void QQmlListModel::removeElements(int index, int removeCount) toDestroy = m_listModel->remove(index, removeCount); } - emitItemsRemoved(index, removeCount); + if (m_mainThread) { + endRemoveRows(); + emit countChanged(); + } for (const auto &destroyer : toDestroy) destroyer(); } @@ -2119,7 +2254,7 @@ void QQmlListModel::insert(QQmlV4Function *args) m_listModel->insert(index+i, argObject); } } - emitItemsInserted(index, objectArrayLength); + emitItemsInserted(); } else if (argObject) { emitItemsAboutToBeInserted(index, 1); @@ -2129,7 +2264,7 @@ void QQmlListModel::insert(QQmlV4Function *args) m_listModel->insert(index, argObject); } - emitItemsInserted(index, 1); + emitItemsInserted(); } else { qmlWarning(this) << tr("insert: value is not an object"); } @@ -2154,14 +2289,15 @@ void QQmlListModel::insert(QQmlV4Function *args) */ void QQmlListModel::move(int from, int to, int n) { - if (n==0 || from==to) + if (n == 0 || from == to) return; if (!canMove(from, to, n)) { qmlWarning(this) << tr("move: out of range"); return; } - emitItemsAboutToBeMoved(from, to, n); + if (m_mainThread) + beginMoveRows(QModelIndex(), from, from + n - 1, QModelIndex(), to > from ? to + n : to); if (m_dynamicRoles) { @@ -2190,7 +2326,8 @@ void QQmlListModel::move(int from, int to, int n) m_listModel->move(from, to, n); } - emitItemsMoved(from, to, n); + if (m_mainThread) + endMoveRows(); } /*! @@ -2230,7 +2367,7 @@ void QQmlListModel::append(QQmlV4Function *args) } } - emitItemsInserted(index, objectArrayLength); + emitItemsInserted(); } else if (argObject) { int index; @@ -2244,7 +2381,7 @@ void QQmlListModel::append(QQmlV4Function *args) m_listModel->append(argObject); } - emitItemsInserted(index, 1); + emitItemsInserted(); } else { qmlWarning(this) << tr("append: value is not an object"); } @@ -2346,7 +2483,7 @@ void QQmlListModel::set(int index, const QQmlV4Handle &handle) m_listModel->insert(index, object); } - emitItemsInserted(index, 1); + emitItemsInserted(); } else { QVector<int> roles; @@ -2443,7 +2580,7 @@ bool QQmlListModelParser::verifyProperty(const QV4::CompiledData::Unit *qmlUnit, } } else if (binding->type == QV4::CompiledData::Binding::Type_Script) { QString scriptStr = binding->valueAsScriptString(qmlUnit); - if (!definesEmptyList(scriptStr)) { + if (!binding->isFunctionExpression() && !definesEmptyList(scriptStr)) { QByteArray script = scriptStr.toUtf8(); bool ok; evaluateEnum(script, &ok); @@ -2457,7 +2594,7 @@ bool QQmlListModelParser::verifyProperty(const QV4::CompiledData::Unit *qmlUnit, return true; } -bool QQmlListModelParser::applyProperty(const QV4::CompiledData::Unit *qmlUnit, const QV4::CompiledData::Binding *binding, ListModel *model, int outterElementIndex) +bool QQmlListModelParser::applyProperty(QV4::CompiledData::CompilationUnit *compilationUnit, const QV4::CompiledData::Unit *qmlUnit, const QV4::CompiledData::Binding *binding, ListModel *model, int outterElementIndex) { const QString elementName = qmlUnit->stringAt(binding->propertyNameIndex); @@ -2485,7 +2622,7 @@ bool QQmlListModelParser::applyProperty(const QV4::CompiledData::Unit *qmlUnit, const QV4::CompiledData::Binding *subBinding = target->bindingTable(); for (quint32 i = 0; i < target->nBindings; ++i, ++subBinding) { - roleSet |= applyProperty(qmlUnit, subBinding, subModel, elementIndex); + roleSet |= applyProperty(compilationUnit, qmlUnit, subBinding, subModel, elementIndex); } } else { @@ -2503,6 +2640,21 @@ bool QQmlListModelParser::applyProperty(const QV4::CompiledData::Unit *qmlUnit, const ListLayout::Role &role = model->getOrCreateListRole(elementName); ListModel *emptyModel = new ListModel(role.subLayout, 0, -1); value = QVariant::fromValue(emptyModel); + } else if (binding->isFunctionExpression()) { + QQmlBinding::Identifier id = binding->value.compiledScriptIndex; + Q_ASSERT(id != QQmlBinding::Invalid); + + auto v4 = compilationUnit->engine; + QV4::Scope scope(v4); + // for now we do not provide a context object; data from the ListElement must be passed to the function + QV4::ScopedContext context(scope, QV4::QmlContext::create(v4->rootContext(), QQmlContextData::get(qmlContext(model->m_modelCache)), nullptr)); + QV4::ScopedFunctionObject function(scope, QV4::FunctionObject::createScriptFunction(context, compilationUnit->runtimeFunctions[id])); + + QV4::ReturnedValue result = function->call(v4->globalObject, nullptr, 0); + + QJSValue v; + QJSValuePrivate::setValue(&v, v4, result); + value.setValue<QJSValue>(v); } else { QByteArray script = scriptStr.toUtf8(); bool ok; @@ -2546,7 +2698,7 @@ void QQmlListModelParser::applyBindings(QObject *obj, QV4::CompiledData::Compila for (const QV4::CompiledData::Binding *binding : bindings) { if (binding->type != QV4::CompiledData::Binding::Type_Object) continue; - setRoles |= applyProperty(qmlUnit, binding, rv->m_listModel, /*outter element index*/-1); + setRoles |= applyProperty(compilationUnit, qmlUnit, binding, rv->m_listModel, /*outter element index*/-1); } if (setRoles == false) @@ -2586,6 +2738,9 @@ bool QQmlListModelParser::definesEmptyList(const QString &s) strings (quoted and optionally within a call to QT_TR_NOOP), boolean values (true, false), numbers, or enumeration values (such as AlignText.AlignHCenter). + Beginning with Qt 5.11 ListElement also allows assigning a function declaration to + a role. This allows the definition of ListElements with callable actions. + \section1 Referencing Roles The role names are used by delegates to obtain data from list elements. diff --git a/src/qml/types/qqmllistmodel_p.h b/src/qml/types/qqmllistmodel_p.h index 1fda703797..18b7b8bb22 100644 --- a/src/qml/types/qqmllistmodel_p.h +++ b/src/qml/types/qqmllistmodel_p.h @@ -147,24 +147,21 @@ private: struct ElementSync { - ElementSync() : src(0), target(0) {} - - DynamicRoleModelNode *src; - DynamicRoleModelNode *target; + DynamicRoleModelNode *src = nullptr; + DynamicRoleModelNode *target = nullptr; + int srcIndex = -1; + int targetIndex = -1; + QVector<int> changedRoles; }; int getUid() const { return m_uid; } - static void sync(QQmlListModel *src, QQmlListModel *target, QHash<int, QQmlListModel *> *targetModelHash); + static bool sync(QQmlListModel *src, QQmlListModel *target); static QQmlListModel *createWithOwner(QQmlListModel *newOwner); void emitItemsChanged(int index, int count, const QVector<int> &roles); - void emitItemsAboutToBeRemoved(int index, int count); - void emitItemsRemoved(int index, int count); void emitItemsAboutToBeInserted(int index, int count); - void emitItemsInserted(int index, int count); - void emitItemsAboutToBeMoved(int from, int to, int n); - void emitItemsMoved(int from, int to, int n); + void emitItemsInserted(); void removeElements(int index, int removeCount); }; @@ -189,13 +186,13 @@ public: QQmlListModelParser() : QQmlCustomParser(QQmlCustomParser::AcceptsSignalHandlers) {} - void verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &bindings) Q_DECL_OVERRIDE; - void applyBindings(QObject *obj, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) Q_DECL_OVERRIDE; + void verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override; + void applyBindings(QObject *obj, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override; private: bool verifyProperty(const QV4::CompiledData::Unit *qmlUnit, const QV4::CompiledData::Binding *binding); // returns true if a role was set - bool applyProperty(const QV4::CompiledData::Unit *qmlUnit, const QV4::CompiledData::Binding *binding, ListModel *model, int outterElementIndex); + bool applyProperty(QV4::CompiledData::CompilationUnit *compilationUnit, const QV4::CompiledData::Unit *qmlUnit, const QV4::CompiledData::Binding *binding, ListModel *model, int outterElementIndex); static bool definesEmptyList(const QString &); diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qml/types/qqmllistmodel_p_p.h index 271437e680..65567ab69a 100644 --- a/src/qml/types/qqmllistmodel_p_p.h +++ b/src/qml/types/qqmllistmodel_p_p.h @@ -108,7 +108,7 @@ public: return m_uid; } - static void sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target, QHash<int, QQmlListModel *> *targetModelHash); + static QVector<int> sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target); private: QQmlListModel *m_owner; @@ -216,6 +216,7 @@ public: QObject, VariantMap, DateTime, + Function, MaxDataType }; @@ -260,7 +261,7 @@ public: ListElement(int existingUid); ~ListElement(); - static void sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout, QHash<int, ListModel *> *targetModelHash); + static QVector<int> sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout); enum { @@ -283,6 +284,7 @@ private: int setVariantMapProperty(const ListLayout::Role &role, QV4::Object *o); int setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m); int setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt); + int setFunctionProperty(const ListLayout::Role &role, const QJSValue &f); void setStringPropertyFast(const ListLayout::Role &role, const QString &s); void setDoublePropertyFast(const ListLayout::Role &role, double n); @@ -291,6 +293,7 @@ private: void setListPropertyFast(const ListLayout::Role &role, ListModel *m); void setVariantMapFast(const ListLayout::Role &role, QV4::Object *o); void setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt); + void setFunctionPropertyFast(const ListLayout::Role &role, const QJSValue &f); void clearProperty(const ListLayout::Role &role); @@ -301,6 +304,7 @@ private: QPointer<QObject> *getGuardProperty(const ListLayout::Role &role); QVariantMap *getVariantMapProperty(const ListLayout::Role &role); QDateTime *getDateTimeProperty(const ListLayout::Role &role); + QJSValue *getFunctionProperty(const ListLayout::Role &role); inline char *getPropertyMemory(const ListLayout::Role &role); @@ -375,7 +379,7 @@ public: int getUid() const { return m_uid; } - static void sync(ListModel *src, ListModel *target, QHash<int, ListModel *> *srcModelHash); + static bool sync(ListModel *src, ListModel *target); QObject *getOrCreateModelObject(QQmlListModel *model, int elementIndex); @@ -388,10 +392,11 @@ private: struct ElementSync { - ElementSync() : src(0), target(0) {} - - ListElement *src; - ListElement *target; + ListElement *src = nullptr; + ListElement *target = nullptr; + int srcIndex = -1; + int targetIndex = -1; + QVector<int> changedRoles; }; void newElement(int index); @@ -400,6 +405,7 @@ private: friend class ListElement; friend class QQmlListModelWorkerAgent; + friend class QQmlListModelParser; }; QT_END_NAMESPACE diff --git a/src/qml/types/qqmllistmodelworkeragent.cpp b/src/qml/types/qqmllistmodelworkeragent.cpp index 0a5adbf292..a2750c926a 100644 --- a/src/qml/types/qqmllistmodelworkeragent.cpp +++ b/src/qml/types/qqmllistmodelworkeragent.cpp @@ -50,39 +50,8 @@ QT_BEGIN_NAMESPACE - -void QQmlListModelWorkerAgent::Data::clearChange(int uid) -{ - for (int i=0 ; i < changes.count() ; ++i) { - if (changes[i].modelUid == uid) { - changes.removeAt(i); - --i; - } - } -} - -void QQmlListModelWorkerAgent::Data::insertChange(int uid, int index, int count) +QQmlListModelWorkerAgent::Sync::~Sync() { - Change c = { uid, Change::Inserted, index, count, 0, QVector<int>() }; - changes << c; -} - -void QQmlListModelWorkerAgent::Data::removeChange(int uid, int index, int count) -{ - Change c = { uid, Change::Removed, index, count, 0, QVector<int>() }; - changes << c; -} - -void QQmlListModelWorkerAgent::Data::moveChange(int uid, int index, int count, int to) -{ - Change c = { uid, Change::Moved, index, count, to, QVector<int>() }; - changes << c; -} - -void QQmlListModelWorkerAgent::Data::changedChange(int uid, int index, int count, const QVector<int> &roles) -{ - Change c = { uid, Change::Changed, index, count, 0, roles }; - changes << c; } QQmlListModelWorkerAgent::QQmlListModelWorkerAgent(QQmlListModel *model) @@ -167,8 +136,7 @@ void QQmlListModelWorkerAgent::move(int from, int to, int count) void QQmlListModelWorkerAgent::sync() { - Sync *s = new Sync(data, m_copy); - data.changes.clear(); + Sync *s = new Sync(m_copy); mutex.lock(); QCoreApplication::postEvent(this, s); @@ -183,61 +151,14 @@ bool QQmlListModelWorkerAgent::event(QEvent *e) QMutexLocker locker(&mutex); if (m_orig) { Sync *s = static_cast<Sync *>(e); - const QList<Change> &changes = s->data.changes; - cc = m_orig->count() != s->list->count(); - - QHash<int, QQmlListModel *> targetModelDynamicHash; - QHash<int, ListModel *> targetModelStaticHash; + cc = (m_orig->count() != s->list->count()); Q_ASSERT(m_orig->m_dynamicRoles == s->list->m_dynamicRoles); if (m_orig->m_dynamicRoles) - QQmlListModel::sync(s->list, m_orig, &targetModelDynamicHash); + QQmlListModel::sync(s->list, m_orig); 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); - - QQmlListModel *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) { - switch (change.type) { - case Change::Inserted: - model->beginInsertRows( - QModelIndex(), change.index, change.index + change.count - 1); - model->endInsertRows(); - break; - case Change::Removed: - model->beginRemoveRows( - QModelIndex(), change.index, change.index + change.count - 1); - model->endRemoveRows(); - break; - case Change::Moved: - model->beginMoveRows( - QModelIndex(), - change.index, - change.index + change.count - 1, - QModelIndex(), - change.to > change.index ? change.to + change.count : change.to); - model->endMoveRows(); - break; - case Change::Changed: - emit model->dataChanged( - model->createIndex(change.index, 0), - model->createIndex(change.index + change.count - 1, 0), - change.roles); - break; - } - } - } + ListModel::sync(s->list->m_listModel, m_orig->m_listModel); } syncDone.wakeAll(); diff --git a/src/qml/types/qqmllistmodelworkeragent_p.h b/src/qml/types/qqmllistmodelworkeragent_p.h index 5a39651bf7..761a467e89 100644 --- a/src/qml/types/qqmllistmodelworkeragent_p.h +++ b/src/qml/types/qqmllistmodelworkeragent_p.h @@ -114,35 +114,12 @@ private: friend class QQuickWorkerScriptEnginePrivate; friend class QQmlListModel; - struct Change - { - int modelUid; - enum { Inserted, Removed, Moved, Changed } type; - int index; // Inserted/Removed/Moved/Changed - int count; // Inserted/Removed/Moved/Changed - int to; // Moved - QVector<int> roles; - }; - - struct Data - { - QList<Change> changes; - - 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 QVector<int> &roles); - }; - Data data; - struct Sync : public QEvent { - Sync(const Data &d, QQmlListModel *l) + Sync(QQmlListModel *l) : QEvent(QEvent::User) - , data(d) , list(l) {} - Data data; + ~Sync(); QQmlListModel *list; }; diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp index 6159355afc..346f2e6a61 100644 --- a/src/qml/types/qquickworkerscript.cpp +++ b/src/qml/types/qquickworkerscript.cpp @@ -65,6 +65,7 @@ #include <private/qv4functionobject_p.h> #include <private/qv4script_p.h> #include <private/qv4scopedvalue_p.h> +#include <private/qv4jscall_p.h> QT_BEGIN_NAMESPACE @@ -185,7 +186,7 @@ public: int m_nextId; - static void method_sendMessage(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static QV4::ReturnedValue method_sendMessage(const QV4::BuiltinFunction *, QV4::CallData *callData); signals: void stopThread(); @@ -239,19 +240,18 @@ void QQuickWorkerScriptEnginePrivate::WorkerEngine::init() QV4::Scope scope(m_v4Engine); QV4::ExecutionContext *globalContext = scope.engine->rootContext(); - onmessage.set(scope.engine, QV4::Script(globalContext, QString::fromUtf8(CALL_ONMESSAGE_SCRIPT)).run()); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro + onmessage.set(scope.engine, QV4::Script(globalContext, QV4::Compiler::GlobalCode, QString::fromUtf8(CALL_ONMESSAGE_SCRIPT)).run()); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro Q_ASSERT(!scope.engine->hasException); - QV4::Script createsendscript(globalContext, QString::fromUtf8(SEND_MESSAGE_CREATE_SCRIPT)); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro + QV4::Script createsendscript(globalContext, QV4::Compiler::GlobalCode, QString::fromUtf8(SEND_MESSAGE_CREATE_SCRIPT)); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro QV4::ScopedFunctionObject createsendconstructor(scope, createsendscript.run()); Q_ASSERT(!scope.engine->hasException); QV4::ScopedString name(scope, m_v4Engine->newString(QStringLiteral("sendMessage"))); QV4::ScopedValue function(scope, QV4::BuiltinFunction::create(globalContext, name, - QQuickWorkerScriptEnginePrivate::method_sendMessage)); - QV4::ScopedCallData callData(scope, 1); - callData->args[0] = function; - callData->thisObject = global(); - createsendconstructor->call(scope, callData); - createsend.set(scope.engine, scope.result.asReturnedValue()); + QQuickWorkerScriptEnginePrivate::method_sendMessage)); + QV4::JSCallData jsCallData(scope, 1); + jsCallData->args[0] = function; + *jsCallData->thisObject = global(); + createsend.set(scope.engine, createsendconstructor->call(jsCallData)); } // Requires handle and context scope @@ -264,13 +264,14 @@ QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::WorkerEngine::sendFunction(i QV4::Scope scope(v4); QV4::ScopedFunctionObject f(scope, createsend.value()); - QV4::ScopedCallData callData(scope, 1); - callData->args[0] = QV4::Primitive::fromInt32(id); - callData->thisObject = global(); - f->call(scope, callData); + QV4::ScopedValue v(scope); + QV4::JSCallData jsCallData(scope, 1); + jsCallData->args[0] = QV4::Primitive::fromInt32(id); + *jsCallData->thisObject = global(); + v = f->call(jsCallData); if (scope.hasException()) - scope.result = scope.engine->catchException(); - return scope.result.asReturnedValue(); + v = scope.engine->catchException(); + return v->asReturnedValue(); } #if QT_CONFIG(qml_network) @@ -292,11 +293,13 @@ QQuickWorkerScriptEnginePrivate::QQuickWorkerScriptEnginePrivate(QQmlEngine *eng { } -void QQuickWorkerScriptEnginePrivate::method_sendMessage(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::method_sendMessage(const QV4::BuiltinFunction *b, + QV4::CallData *callData) { + QV4::Scope scope(b); WorkerEngine *engine = (WorkerEngine*)scope.engine->v8Engine; - int id = callData->argc > 1 ? callData->args[1].toInt32() : 0; + int id = callData->argc() > 1 ? callData->args[1].toInt32() : 0; QV4::ScopedValue v(scope, callData->argument(2)); QByteArray data = QV4::Serialize::serialize(v, scope.engine); @@ -306,7 +309,7 @@ void QQuickWorkerScriptEnginePrivate::method_sendMessage(const QV4::BuiltinFunct if (script && script->owner) QCoreApplication::postEvent(script->owner, new WorkerDataEvent(0, data)); - scope.result = QV4::Encode::undefined(); + return QV4::Encode::undefined(); } QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::getWorker(WorkerScript *script) @@ -363,11 +366,11 @@ void QQuickWorkerScriptEnginePrivate::processMessage(int id, const QByteArray &d QV4::Scoped<QV4::QmlContext> qmlContext(scope, script->qmlContext.value()); Q_ASSERT(!!qmlContext); - QV4::ScopedCallData callData(scope, 2); - callData->thisObject = workerEngine->global(); - callData->args[0] = qmlContext->d()->qml; // ### - callData->args[1] = value; - f->call(scope, callData); + QV4::JSCallData jsCallData(scope, 2); + *jsCallData->thisObject = workerEngine->global(); + jsCallData->args[0] = qmlContext->d()->qml(); // ### + jsCallData->args[1] = value; + f->call(jsCallData); if (scope.hasException()) { QQmlError error = scope.engine->catchExceptionAsQmlError(); reportScriptException(script, error); |