diff options
Diffstat (limited to 'src/qmlmodels/qqmldelegatemodel.cpp')
-rw-r--r-- | src/qmlmodels/qqmldelegatemodel.cpp | 303 |
1 files changed, 195 insertions, 108 deletions
diff --git a/src/qmlmodels/qqmldelegatemodel.cpp b/src/qmlmodels/qqmldelegatemodel.cpp index fcbcec15a1..36efadeed7 100644 --- a/src/qmlmodels/qqmldelegatemodel.cpp +++ b/src/qmlmodels/qqmldelegatemodel.cpp @@ -31,7 +31,7 @@ namespace QV4 { namespace Heap { struct DelegateModelGroupFunction : FunctionObject { - void init(QV4::ExecutionContext *scope, uint flag, QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg)); + void init(ExecutionEngine *engine, uint flag, QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg)); QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg); uint flag; @@ -60,9 +60,11 @@ struct DelegateModelGroupFunction : QV4::FunctionObject { V4_OBJECT2(DelegateModelGroupFunction, FunctionObject) - static Heap::DelegateModelGroupFunction *create(QV4::ExecutionContext *scope, uint flag, QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg)) + static Heap::DelegateModelGroupFunction *create( + QV4::ExecutionEngine *engine, uint flag, + QV4::ReturnedValue (*code)(QQmlDelegateModelItem *, uint, const QV4::Value &)) { - return scope->engine()->memoryManager->allocate<DelegateModelGroupFunction>(scope, flag, code); + return engine->memoryManager->allocate<DelegateModelGroupFunction>(engine, flag, code); } static ReturnedValue virtualCall(const QV4::FunctionObject *that, const Value *thisObject, const Value *argv, int argc) @@ -78,9 +80,11 @@ struct DelegateModelGroupFunction : QV4::FunctionObject } }; -void Heap::DelegateModelGroupFunction::init(QV4::ExecutionContext *scope, uint flag, QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg)) +void Heap::DelegateModelGroupFunction::init( + QV4::ExecutionEngine *engine, uint flag, + QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg)) { - QV4::Heap::FunctionObject::init(scope, QStringLiteral("DelegateModelGroupFunction")); + QV4::Heap::FunctionObject::init(engine, QStringLiteral("DelegateModelGroupFunction")); this->flag = flag; this->code = code; } @@ -103,7 +107,7 @@ public: QV4::PersistentValue changeProto; }; -V4_DEFINE_EXTENSION(QQmlDelegateModelEngineData, engineData) +V4_DEFINE_EXTENSION(QQmlDelegateModelEngineData, qdmEngineData) void QQmlDelegateModelPartsMetaObject::propertyCreated(int, QMetaPropertyBuilder &prop) @@ -351,26 +355,16 @@ void QQmlDelegateModelPrivate::connectToAbstractItemModel() auto aim = m_adaptorModel.aim(); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsInserted(QModelIndex,int,int)), - q, QQmlDelegateModel, SLOT(_q_rowsInserted(QModelIndex,int,int))); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), - q, QQmlDelegateModel, SLOT(_q_rowsRemoved(QModelIndex,int,int))); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - q, QQmlDelegateModel, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int))); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(columnsInserted(QModelIndex,int,int)), - q, QQmlDelegateModel, SLOT(_q_columnsInserted(QModelIndex,int,int))); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(columnsRemoved(QModelIndex,int,int)), - q, QQmlDelegateModel, SLOT(_q_columnsRemoved(QModelIndex,int,int))); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)), - q, QQmlDelegateModel, SLOT(_q_columnsMoved(QModelIndex,int,int,QModelIndex,int))); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), - q, QQmlDelegateModel, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector<int>))); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), - q, QQmlDelegateModel, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int))); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(modelReset()), - q, QQmlDelegateModel, SLOT(_q_modelReset())); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - q, QQmlDelegateModel, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); + QObject::connect(aim, &QAbstractItemModel::rowsInserted, q, &QQmlDelegateModel::_q_rowsInserted); + QObject::connect(aim, &QAbstractItemModel::rowsRemoved, q, &QQmlDelegateModel::_q_rowsRemoved); + QObject::connect(aim, &QAbstractItemModel::rowsAboutToBeRemoved, q, &QQmlDelegateModel::_q_rowsAboutToBeRemoved); + QObject::connect(aim, &QAbstractItemModel::columnsInserted, q, &QQmlDelegateModel::_q_columnsInserted); + QObject::connect(aim, &QAbstractItemModel::columnsRemoved, q, &QQmlDelegateModel::_q_columnsRemoved); + QObject::connect(aim, &QAbstractItemModel::columnsMoved, q, &QQmlDelegateModel::_q_columnsMoved); + QObject::connect(aim, &QAbstractItemModel::dataChanged, q, &QQmlDelegateModel::_q_dataChanged); + QObject::connect(aim, &QAbstractItemModel::rowsMoved, q, &QQmlDelegateModel::_q_rowsMoved); + QObject::connect(aim, &QAbstractItemModel::modelAboutToBeReset, q, &QQmlDelegateModel::_q_modelAboutToBeReset); + QObject::connect(aim, &QAbstractItemModel::layoutChanged, q, &QQmlDelegateModel::_q_layoutChanged); } void QQmlDelegateModelPrivate::disconnectFromAbstractItemModel() @@ -381,26 +375,16 @@ void QQmlDelegateModelPrivate::disconnectFromAbstractItemModel() auto aim = m_adaptorModel.aim(); - QObject::disconnect(aim, SIGNAL(rowsInserted(QModelIndex,int,int)), - q, SLOT(_q_rowsInserted(QModelIndex,int,int))); - QObject::disconnect(aim, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - q, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int))); - QObject::disconnect(aim, SIGNAL(rowsRemoved(QModelIndex,int,int)), - q, SLOT(_q_rowsRemoved(QModelIndex,int,int))); - QObject::disconnect(aim, SIGNAL(columnsInserted(QModelIndex,int,int)), q, - SLOT(_q_columnsInserted(QModelIndex,int,int))); - QObject::disconnect(aim, SIGNAL(columnsRemoved(QModelIndex,int,int)), q, - SLOT(_q_columnsRemoved(QModelIndex,int,int))); - QObject::disconnect(aim, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)), q, - SLOT(_q_columnsMoved(QModelIndex,int,int,QModelIndex,int))); - QObject::disconnect(aim, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), - q, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector<int>))); - QObject::disconnect(aim, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), - q, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int))); - QObject::disconnect(aim, SIGNAL(modelReset()), - q, SLOT(_q_modelReset())); - QObject::disconnect(aim, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - q, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); + QObject::disconnect(aim, &QAbstractItemModel::rowsInserted, q, &QQmlDelegateModel::_q_rowsInserted); + QObject::disconnect(aim, &QAbstractItemModel::rowsAboutToBeRemoved, q, &QQmlDelegateModel::_q_rowsAboutToBeRemoved); + QObject::disconnect(aim, &QAbstractItemModel::rowsRemoved, q, &QQmlDelegateModel::_q_rowsRemoved); + QObject::disconnect(aim, &QAbstractItemModel::columnsInserted, q, &QQmlDelegateModel::_q_columnsInserted); + QObject::disconnect(aim, &QAbstractItemModel::columnsRemoved, q, &QQmlDelegateModel::_q_columnsRemoved); + QObject::disconnect(aim, &QAbstractItemModel::columnsMoved, q, &QQmlDelegateModel::_q_columnsMoved); + QObject::disconnect(aim, &QAbstractItemModel::dataChanged, q, &QQmlDelegateModel::_q_dataChanged); + QObject::disconnect(aim, &QAbstractItemModel::rowsMoved, q, &QQmlDelegateModel::_q_rowsMoved); + QObject::disconnect(aim, &QAbstractItemModel::modelAboutToBeReset, q, &QQmlDelegateModel::_q_modelAboutToBeReset); + QObject::disconnect(aim, &QAbstractItemModel::layoutChanged, q, &QQmlDelegateModel::_q_layoutChanged); } void QQmlDelegateModel::setModel(const QVariant &model) @@ -461,7 +445,7 @@ void QQmlDelegateModel::setDelegate(QQmlComponent *delegate) qobject_cast<QQmlAbstractDelegateComponent *>(delegate); if (adc) { d->m_delegateChooser = adc; - d->m_delegateChooserChanged = connect(adc, &QQmlAbstractDelegateComponent::delegateChanged, + d->m_delegateChooserChanged = connect(adc, &QQmlAbstractDelegateComponent::delegateChanged, this, [d](){ d->delegateChanged(); }); } } @@ -922,7 +906,7 @@ void QQDMIncubationTask::initializeRequiredProperties(QQmlDelegateModelItem *mod if (incubatorPriv->hadTopLevelRequiredProperties()) { // If we have required properties, we clear the context object // so that the model role names are not polluting the context. - // Unless the context is bound, in which case we have never set context object. + // Unless the context is bound, in which case we have never set the context object. if (incubating && !isBound) { Q_ASSERT(incubating->contextData); incubating->contextData->setContextObject(nullptr); @@ -931,16 +915,20 @@ void QQDMIncubationTask::initializeRequiredProperties(QQmlDelegateModelItem *mod proxyContext->setContextObject(nullptr); } + // Retrieve the metaObject before the potential return so that the accessors have a chance + // to perform some finalization in case they produce a dynamic metaobject. Here we know for + // sure that we are using required properties. + const QMetaObject *qmlMetaObject = modelItemToIncubate->metaObject(); + if (incubatorPriv->requiredProperties()->empty()) return; RequiredProperties *requiredProperties = incubatorPriv->requiredProperties(); - auto qmlMetaObject = modelItemToIncubate->metaObject(); // if a required property was not in the model, it might still be a static property of the // QQmlDelegateModelItem or one of its derived classes this is the case for index, row, // column, model and more - // the most derived subclass of QQmlDelegateModelItem is QQmlDMAbstractModelData at depth 2, - // so 4 should be plenty + // the most derived subclasses of QQmlDelegateModelItem are QQmlDMAbstractItemModelData and + // QQmlDMObjectData at depth 2, so 4 should be plenty QVarLengthArray<QPair<const QMetaObject *, QObject *>, 4> mos; // we first check the dynamic meta object for properties originating from the model // contains abstractitemmodelproperties @@ -987,6 +975,13 @@ void QQDMIncubationTask::initializeRequiredProperties(QQmlDelegateModelItem *mod modelItemToIncubate->contextData->setContextObject(modelItemToIncubate); if (proxiedObject) proxyContext->setContextObject(proxiedObject); + + // Retrieve the metaObject() once so that the accessors have a chance to perform some + // finalization in case they produce a dynamic metaobject. For example, they might be + // inclined to create a propertyCache now because there are no required properties and any + // revisioned properties should be hidden after all. Here is the first time we know for + // sure whether we are using context properties. + modelItemToIncubate->metaObject(); } } @@ -1203,6 +1198,9 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQ addCacheItem(cacheItem, it); reuseItem(cacheItem, index, flags); cacheItem->referenceObject(); + + if (index == m_compositor.count(group) - 1) + requestMoreIfNecessary(); return cacheItem->object; } @@ -1285,7 +1283,9 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQ if (cacheItem->object && (!cacheItem->incubationTask || isDoneIncubating(cacheItem->incubationTask->status()))) return cacheItem->object; - cacheItem->releaseObject(); + if (cacheItem->objectRef > 0) + cacheItem->releaseObject(); + if (!cacheItem->isReferenced()) { removeCacheItem(cacheItem); delete cacheItem; @@ -1450,6 +1450,50 @@ void QQmlDelegateModel::_q_itemsChanged(int index, int count, const QVector<int> d->itemsChanged(changes); d->emitChanges(); } + const bool needToCheckDelegateChoiceInvalidation = d->m_delegateChooser && !roles.isEmpty(); + if (!needToCheckDelegateChoiceInvalidation) + return; + + // here, we only really can handle AIM based models, because only there + // we can do something sensible with roles + if (!d->m_adaptorModel.adaptsAim()) + return; + + const auto aim = d->m_adaptorModel.aim(); + const auto choiceRole = d->m_delegateChooser->role().toUtf8(); + const auto &roleNames = aim->roleNames(); + auto it = std::find_if(roles.begin(), roles.end(), [&](int role) { + return roleNames[role] == choiceRole; + }); + if (it == roles.end()) + return; + + // Compare handleModelReset - we're doing a more localized version + + /* A role change affecting the DelegateChoice is equivalent to removing all + affected items (including invalidating their cache entries) and afterwards + reinserting them. + */ + QVector<Compositor::Remove> removes; + QVector<Compositor::Insert> inserts; + d->m_compositor.listItemsRemoved(&d->m_adaptorModel, index, count, &removes); + const QList<QQmlDelegateModelItem *> cache = d->m_cache; + for (QQmlDelegateModelItem *item : cache) + item->referenceObject(); + for (const auto& removed: removes) { + if (!d->m_cache.isSharedWith(cache)) + break; + QQmlDelegateModelItem *item = cache.value(removed.cacheIndex(), nullptr); + if (!d->m_cache.contains(item)) + continue; + if (item->modelIndex() != -1) + item->setModelIndex(-1, -1, -1); + } + for (QQmlDelegateModelItem *item : cache) + item->releaseObject(); + d->m_compositor.listItemsInserted(&d->m_adaptorModel, index, count, &inserts); + d->itemsMoved(removes, inserts); + d->emitChanges(); } static void incrementIndexes(QQmlDelegateModelItem *cacheItem, int count, const int *deltas) @@ -1602,8 +1646,8 @@ void QQmlDelegateModelPrivate::itemsRemoved( if (movedItems && remove.isMove()) { movedItems->insert(remove.moveId, m_cache.mid(remove.cacheIndex(), remove.count)); - QList<QQmlDelegateModelItem *>::iterator begin = m_cache.begin() + remove.cacheIndex(); - QList<QQmlDelegateModelItem *>::iterator end = begin + remove.count; + QList<QQmlDelegateModelItem *>::const_iterator begin = m_cache.constBegin() + remove.cacheIndex(); + QList<QQmlDelegateModelItem *>::const_iterator end = begin + remove.count; m_cache.erase(begin, end); } else { for (; cacheIndex < remove.cacheIndex() + remove.count - removedCache; ++cacheIndex) { @@ -1617,7 +1661,7 @@ void QQmlDelegateModelPrivate::itemsRemoved( emitDestroyingItem(object); cacheItem->scriptRef -= 1; } - if (!cacheItem->isReferenced()) { + if (!cacheItem->isReferenced() && !remove.inGroup(Compositor::Persisted)) { m_compositor.clearFlags(Compositor::Cache, cacheIndex, 1, Compositor::CacheFlag); m_cache.removeAt(cacheIndex); delete cacheItem; @@ -1833,14 +1877,41 @@ void QQmlDelegateModelPrivate::emitChanges() for (int i = 1; i < m_groupCount; ++i) QQmlDelegateModelGroupPrivate::get(m_groups[i])->emitModelUpdated(reset); - auto cacheCopy = m_cache; // deliberate; emitChanges may alter m_cache - for (QQmlDelegateModelItem *cacheItem : std::as_const(cacheCopy)) { - if (cacheItem->attached) - cacheItem->attached->emitChanges(); + // emitChanges may alter m_cache and delete items + QVarLengthArray<QPointer<QQmlDelegateModelAttached>> attachedObjects; + attachedObjects.reserve(m_cache.length()); + for (const QQmlDelegateModelItem *cacheItem : std::as_const(m_cache)) + attachedObjects.append(cacheItem->attached); + + for (const QPointer<QQmlDelegateModelAttached> &attached : std::as_const(attachedObjects)) { + if (attached && attached->m_cacheItem) + attached->emitChanges(); } } -void QQmlDelegateModel::_q_modelReset() +void QQmlDelegateModel::_q_modelAboutToBeReset() +{ + auto aim = static_cast<QAbstractItemModel *>(sender()); + auto oldRoleNames = aim->roleNames(); + // this relies on the fact that modelAboutToBeReset must be followed + // by a modelReset signal before any further modelAboutToBeReset can occur + QObject::connect(aim, &QAbstractItemModel::modelReset, this, [&, oldRoleNames](){ + auto aim = static_cast<QAbstractItemModel *>(sender()); + if (oldRoleNames == aim->roleNames()) { + // if the rolenames stayed the same (most common case), then we don't have + // to throw away all the setup that we did + handleModelReset(); + } else { + // If they did change, we give up and just start from scratch via setMode + setModel(QVariant::fromValue(model())); + // but we still have to call handleModelReset, otherwise views will + // not refresh + handleModelReset(); + } + }, Qt::SingleShotConnection); +} + +void QQmlDelegateModel::handleModelReset() { Q_D(QQmlDelegateModel); if (!d->m_delegate) @@ -2005,7 +2076,7 @@ void QQmlDelegateModel::_q_layoutChanged(const QList<QPersistentModelIndex> &par // Ignored } else { // We don't know what's going on, so reset the model - _q_modelReset(); + handleModelReset(); } } @@ -2018,26 +2089,28 @@ QQmlDelegateModelAttached *QQmlDelegateModel::qmlAttachedProperties(QObject *obj return new QQmlDelegateModelAttached(obj); } -bool QQmlDelegateModelPrivate::insert(Compositor::insert_iterator &before, const QV4::Value &object, int groups) +QQmlDelegateModelPrivate::InsertionResult +QQmlDelegateModelPrivate::insert(Compositor::insert_iterator &before, const QV4::Value &object, int groups) { if (!m_context || !m_context->isValid()) - return false; + return InsertionResult::Error; QQmlDelegateModelItem *cacheItem = m_adaptorModel.createItem(m_cacheMetaType, -1); if (!cacheItem) - return false; + return InsertionResult::Error; if (!object.isObject()) - return false; + return InsertionResult::Error; QV4::ExecutionEngine *v4 = object.as<QV4::Object>()->engine(); QV4::Scope scope(v4); QV4::ScopedObject o(scope, object); if (!o) - return false; + return InsertionResult::Error; QV4::ObjectIterator it(scope, o, QV4::ObjectIterator::EnumerableOnly); QV4::ScopedValue propertyName(scope); QV4::ScopedValue v(scope); + const auto oldCache = m_cache; while (1) { propertyName = it.nextPropertyNameAsString(v); if (propertyName->isNull()) @@ -2046,6 +2119,9 @@ bool QQmlDelegateModelPrivate::insert(Compositor::insert_iterator &before, const propertyName->toQStringNoThrow(), QV4::ExecutionEngine::toVariant(v, QMetaType {})); } + const bool cacheModified = !m_cache.isSharedWith(oldCache); + if (cacheModified) + return InsertionResult::Retry; cacheItem->groups = groups | Compositor::UnresolvedFlag | Compositor::CacheFlag; @@ -2055,7 +2131,7 @@ bool QQmlDelegateModelPrivate::insert(Compositor::insert_iterator &before, const m_cache.insert(before.cacheIndex(), cacheItem); m_compositor.insert(before, nullptr, 0, 1, cacheItem->groups); - return true; + return InsertionResult::Success; } //============================================================================ @@ -2115,27 +2191,27 @@ void QQmlDelegateModelItemMetaType::initializePrototype() s = v4Engine->newString(QStringLiteral("isUnresolved")); QV4::ScopedFunctionObject f(scope); - QV4::ExecutionContext *global = scope.engine->rootContext(); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, 30, QQmlDelegateModelItem::get_member))); + QV4::ExecutionEngine *engine = scope.engine; + p->setGetter((f = QV4::DelegateModelGroupFunction::create(engine, 30, QQmlDelegateModelItem::get_member))); p->setSetter(nullptr); proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); s = v4Engine->newString(QStringLiteral("inItems")); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Default, QQmlDelegateModelItem::get_member))); - p->setSetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Default, QQmlDelegateModelItem::set_member))); + p->setGetter((f = QV4::DelegateModelGroupFunction::create(engine, QQmlListCompositor::Default, QQmlDelegateModelItem::get_member))); + p->setSetter((f = QV4::DelegateModelGroupFunction::create(engine, QQmlListCompositor::Default, QQmlDelegateModelItem::set_member))); proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); s = v4Engine->newString(QStringLiteral("inPersistedItems")); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Persisted, QQmlDelegateModelItem::get_member))); - p->setSetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Persisted, QQmlDelegateModelItem::set_member))); + p->setGetter((f = QV4::DelegateModelGroupFunction::create(engine, QQmlListCompositor::Persisted, QQmlDelegateModelItem::get_member))); + p->setSetter((f = QV4::DelegateModelGroupFunction::create(engine, QQmlListCompositor::Persisted, QQmlDelegateModelItem::set_member))); proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); s = v4Engine->newString(QStringLiteral("itemsIndex")); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Default, QQmlDelegateModelItem::get_index))); + p->setGetter((f = QV4::DelegateModelGroupFunction::create(engine, QQmlListCompositor::Default, QQmlDelegateModelItem::get_index))); proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); s = v4Engine->newString(QStringLiteral("persistedItemsIndex")); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Persisted, QQmlDelegateModelItem::get_index))); + p->setGetter((f = QV4::DelegateModelGroupFunction::create(engine, QQmlListCompositor::Persisted, QQmlDelegateModelItem::get_index))); p->setSetter(nullptr); proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); @@ -2143,14 +2219,14 @@ void QQmlDelegateModelItemMetaType::initializePrototype() QString propertyName = QLatin1String("in") + groupNames.at(i); propertyName.replace(2, 1, propertyName.at(2).toUpper()); s = v4Engine->newString(propertyName); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, i + 1, QQmlDelegateModelItem::get_member))); - p->setSetter((f = QV4::DelegateModelGroupFunction::create(global, i + 1, QQmlDelegateModelItem::set_member))); + p->setGetter((f = QV4::DelegateModelGroupFunction::create(engine, i + 1, QQmlDelegateModelItem::get_member))); + p->setSetter((f = QV4::DelegateModelGroupFunction::create(engine, i + 1, QQmlDelegateModelItem::set_member))); proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); } for (int i = 2; i < groupNames.size(); ++i) { const QString propertyName = groupNames.at(i) + QLatin1String("Index"); s = v4Engine->newString(propertyName); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, i + 1, QQmlDelegateModelItem::get_index))); + p->setGetter((f = QV4::DelegateModelGroupFunction::create(engine, i + 1, QQmlDelegateModelItem::get_index))); p->setSetter(nullptr); proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); } @@ -2720,20 +2796,24 @@ void QQmlDelegateModelAttached::emitChanges() m_previousGroups = m_cacheItem->groups; int indexChanges = 0; - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) { + const int groupCount = m_cacheItem->metaType->groupCount; + for (int i = 1; i < groupCount; ++i) { if (m_previousIndex[i] != m_currentIndex[i]) { m_previousIndex[i] = m_currentIndex[i]; indexChanges |= (1 << i); } } + // Don't access m_cacheItem anymore once we've started sending signals. + // We don't own it and someone might delete it. + int notifierId = 0; const QMetaObject *meta = metaObject(); - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { + for (int i = 1; i < groupCount; ++i, ++notifierId) { if (groupChanges & (1 << i)) QMetaObject::activate(this, meta, notifierId, nullptr); } - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { + for (int i = 1; i < groupCount; ++i, ++notifierId) { if (indexChanges & (1 << i)) QMetaObject::activate(this, meta, notifierId, nullptr); } @@ -2762,9 +2842,9 @@ void QQmlDelegateModelGroupPrivate::emitChanges(QV4::ExecutionEngine *v4) Q_Q(QQmlDelegateModelGroup); if (isChangedConnected() && !changeSet.isEmpty()) { emit q->changed(QJSValuePrivate::fromReturnedValue( - engineData(v4)->array(v4, changeSet.removes())), + qdmEngineData(v4)->array(v4, changeSet.removes())), QJSValuePrivate::fromReturnedValue( - engineData(v4)->array(v4, changeSet.inserts()))); + qdmEngineData(v4)->array(v4, changeSet.inserts()))); } if (changeSet.difference() != 0) emit q->countChanged(); @@ -3028,7 +3108,7 @@ bool QQmlDelegateModelGroupPrivate::parseIndex(const QV4::Value &value, int *ind items that are later replaced by actual data. */ -void QQmlDelegateModelGroup::insert(QQmlV4Function *args) +void QQmlDelegateModelGroup::insert(QQmlV4FunctionPtr args) { Q_D(QQmlDelegateModelGroup); QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); @@ -3052,9 +3132,8 @@ void QQmlDelegateModelGroup::insert(QQmlV4Function *args) v = (*args)[i]; } - Compositor::insert_iterator before = index < model->m_compositor.count(group) - ? model->m_compositor.findInsertPosition(group, index) - : model->m_compositor.end(); + if (v->as<QV4::ArrayObject>()) + return; int groups = 1 << d->group; if (++i < args->length()) { @@ -3062,11 +3141,16 @@ void QQmlDelegateModelGroup::insert(QQmlV4Function *args) groups |= model->m_cacheMetaType->parseGroups(val); } - if (v->as<QV4::ArrayObject>()) { - return; - } else if (v->as<QV4::Object>()) { - model->insert(before, v, groups); - model->emitChanges(); + if (v->as<QV4::Object>()) { + auto insertionResult = QQmlDelegateModelPrivate::InsertionResult::Retry; + do { + Compositor::insert_iterator before = index < model->m_compositor.count(group) + ? model->m_compositor.findInsertPosition(group, index) + : model->m_compositor.end(); + insertionResult = model->insert(before, v, groups); + } while (insertionResult == QQmlDelegateModelPrivate::InsertionResult::Retry); + if (insertionResult == QQmlDelegateModelPrivate::InsertionResult::Success) + model->emitChanges(); } } @@ -3087,7 +3171,7 @@ void QQmlDelegateModelGroup::insert(QQmlV4Function *args) group remain instantiated when not referenced by any view. */ -void QQmlDelegateModelGroup::create(QQmlV4Function *args) +void QQmlDelegateModelGroup::create(QQmlV4FunctionPtr args) { Q_D(QQmlDelegateModelGroup); if (!d->model) @@ -3116,16 +3200,19 @@ void QQmlDelegateModelGroup::create(QQmlV4Function *args) groups |= model->m_cacheMetaType->parseGroups(val); } - Compositor::insert_iterator before = index < model->m_compositor.count(group) - ? model->m_compositor.findInsertPosition(group, index) - : model->m_compositor.end(); + auto insertionResult = QQmlDelegateModelPrivate::InsertionResult::Retry; + do { + Compositor::insert_iterator before = index < model->m_compositor.count(group) + ? model->m_compositor.findInsertPosition(group, index) + : model->m_compositor.end(); - index = before.index[d->group]; - group = d->group; + index = before.index[d->group]; + group = d->group; - if (!model->insert(before, v, groups)) { + insertionResult = model->insert(before, v, groups); + } while (insertionResult == QQmlDelegateModelPrivate::InsertionResult::Retry); + if (insertionResult == QQmlDelegateModelPrivate::InsertionResult::Error) return; - } } } if (index < 0 || index >= model->m_compositor.count(group)) { @@ -3162,7 +3249,7 @@ void QQmlDelegateModelGroup::create(QQmlV4Function *args) that the previously unresolved item has simply moved. */ -void QQmlDelegateModelGroup::resolve(QQmlV4Function *args) +void QQmlDelegateModelGroup::resolve(QQmlV4FunctionPtr args) { Q_D(QQmlDelegateModelGroup); if (!d->model) @@ -3266,7 +3353,7 @@ void QQmlDelegateModelGroup::resolve(QQmlV4Function *args) Removes \a count items starting at \a index from the group. */ -void QQmlDelegateModelGroup::remove(QQmlV4Function *args) +void QQmlDelegateModelGroup::remove(QQmlV4FunctionPtr args) { Q_D(QQmlDelegateModelGroup); if (!d->model) @@ -3306,7 +3393,7 @@ void QQmlDelegateModelGroup::remove(QQmlV4Function *args) } bool QQmlDelegateModelGroupPrivate::parseGroupArgs( - QQmlV4Function *args, Compositor::Group *group, int *index, int *count, int *groups) const + QQmlV4FunctionPtr args, Compositor::Group *group, int *index, int *count, int *groups) const { if (!model || !QQmlDelegateModelPrivate::get(model)->m_cacheMetaType) return false; @@ -3340,7 +3427,7 @@ bool QQmlDelegateModelGroupPrivate::parseGroupArgs( Adds \a count items starting at \a index to \a groups. */ -void QQmlDelegateModelGroup::addGroups(QQmlV4Function *args) +void QQmlDelegateModelGroup::addGroups(QQmlV4FunctionPtr args) { Q_D(QQmlDelegateModelGroup); Compositor::Group group = d->group; @@ -3370,7 +3457,7 @@ void QQmlDelegateModelGroup::addGroups(QQmlV4Function *args) Removes \a count items starting at \a index from \a groups. */ -void QQmlDelegateModelGroup::removeGroups(QQmlV4Function *args) +void QQmlDelegateModelGroup::removeGroups(QQmlV4FunctionPtr args) { Q_D(QQmlDelegateModelGroup); Compositor::Group group = d->group; @@ -3401,7 +3488,7 @@ void QQmlDelegateModelGroup::removeGroups(QQmlV4Function *args) their existing groups and added to \a groups. */ -void QQmlDelegateModelGroup::setGroups(QQmlV4Function *args) +void QQmlDelegateModelGroup::setGroups(QQmlV4FunctionPtr args) { Q_D(QQmlDelegateModelGroup); Compositor::Group group = d->group; @@ -3436,7 +3523,7 @@ void QQmlDelegateModelGroup::setGroups(QQmlV4Function *args) reordering you have done via this function. */ -void QQmlDelegateModelGroup::move(QQmlV4Function *args) +void QQmlDelegateModelGroup::move(QQmlV4FunctionPtr args) { Q_D(QQmlDelegateModelGroup); @@ -3926,7 +4013,7 @@ public: const QQmlChangeSet::Change &change = array->at(index); - QV4::ScopedObject changeProto(scope, engineData(v4)->changeProto.value()); + QV4::ScopedObject changeProto(scope, qdmEngineData(v4)->changeProto.value()); QV4::Scoped<QQmlDelegateModelGroupChange> object(scope, QQmlDelegateModelGroupChange::create(v4)); object->setPrototypeOf(changeProto); object->d()->change = change; |