diff options
author | Ulf Hermann <[email protected]> | 2025-09-12 13:41:21 +0200 |
---|---|---|
committer | Ulf Hermann <[email protected]> | 2025-09-15 20:08:03 +0200 |
commit | c66fb45c886781c8af85e15c8e8af7305ab38fed (patch) | |
tree | eea51d1cfecac41d135df9067c63b9a4d395eb32 | |
parent | a5ad373e6e19909a814a813daafd8402cf5153bd (diff) |
QmlModels: Signal list model changes also via QQmlTableInstanceModel
Allow QQmlDelegateModelItemMetaType to store any QQmlInstanceModel, but
also store the kind of model we're dealing with so that we can quickly
produce a QQmlDelegateModel or a QQmlTableInstanceModel when necessary.
This allows us to send the modelChanged() signal. We do not expect to
perform the same trickery that QQmlDelegateModel and
QQmlTableInstanceModel do more often. Therefore this solution does not
need to scale beyond those.
Pick-to: 6.10
Task-number: QTBUG-139941
Change-Id: Id6d2a8ae5f96b755a776eb354e6ae291314dbf7b
Reviewed-by: Sami Shalayel <[email protected]>
-rw-r--r-- | src/qmlmodels/qqmldelegatemodel.cpp | 86 | ||||
-rw-r--r-- | src/qmlmodels/qqmldelegatemodel_p_p.h | 25 | ||||
-rw-r--r-- | src/qmlmodels/qqmldmlistaccessordata.cpp | 3 | ||||
-rw-r--r-- | src/qmlmodels/qqmltableinstancemodel.cpp | 4 | ||||
-rw-r--r-- | tests/auto/quick/qquicktableview/tst_qquicktableview.cpp | 21 |
5 files changed, 103 insertions, 36 deletions
diff --git a/src/qmlmodels/qqmldelegatemodel.cpp b/src/qmlmodels/qqmldelegatemodel.cpp index 51c875465e..032a7b71f9 100644 --- a/src/qmlmodels/qqmldelegatemodel.cpp +++ b/src/qmlmodels/qqmldelegatemodel.cpp @@ -16,6 +16,7 @@ #include <private/qqmlcomponent_p.h> #include <private/qqmlengine_p.h> #include <private/qqmlpropertytopropertybinding_p.h> +#include <private/qqmltableinstancemodel_p.h> #include <private/qquickpackage_p.h> #include <private/qv4functionobject_p.h> #include <private/qv4objectiterator_p.h> @@ -2250,11 +2251,34 @@ QQmlDelegateModelItemMetaType::QQmlDelegateModelItemMetaType( : model(model) , v4Engine(engine) , groupNames(groupNames) + , modelKind(ModelKind::DelegateModel) +{ +} + +QQmlDelegateModelItemMetaType::QQmlDelegateModelItemMetaType( + QV4::ExecutionEngine *engine, QQmlTableInstanceModel *model) + : model(model) + , v4Engine(engine) + , modelKind(ModelKind::TableInstanceModel) { } QQmlDelegateModelItemMetaType::~QQmlDelegateModelItemMetaType() = default; +void QQmlDelegateModelItemMetaType::emitModelChanged() const +{ + switch (modelKind) { + case ModelKind::InstanceModel: + break; + case ModelKind::DelegateModel: + emit static_cast<QQmlDelegateModel *>(model.data())->modelChanged(); + break; + case ModelKind::TableInstanceModel: + emit static_cast<QQmlTableInstanceModel *>(model.data())->modelChanged(); + break; + } +} + void QQmlDelegateModelItemMetaType::initializeAttachedMetaObject() { QMetaObjectBuilder builder; @@ -2417,10 +2441,11 @@ QV4::ReturnedValue QQmlDelegateModelItem::set_groups(const QV4::FunctionObject * if (!argc) THROW_TYPE_ERROR(); - if (!o->d()->item->metaType->model) + QQmlDelegateModel *delegateModel = o->d()->item->metaType->delegateModel(); + if (!delegateModel) RETURN_UNDEFINED(); - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(o->d()->item->metaType->model); + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(delegateModel); const int groupFlags = model->m_cacheMetaType->parseGroups(argv[0]); const int cacheIndex = model->m_cache.indexOf(o->d()->item); Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); @@ -2435,16 +2460,16 @@ QV4::ReturnedValue QQmlDelegateModelItem::get_member(QQmlDelegateModelItem *this QV4::ReturnedValue QQmlDelegateModelItem::set_member(QQmlDelegateModelItem *cacheItem, uint flag, const QV4::Value &arg) { - if (!cacheItem->metaType->model) - return QV4::Encode::undefined(); - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(cacheItem->metaType->model); - bool member = arg.toBoolean(); uint groupFlag = (1 << flag); if (member == ((cacheItem->groups & groupFlag) != 0)) return QV4::Encode::undefined(); + QQmlDelegateModel *delegateModel = cacheItem->metaType->delegateModel(); + if (!delegateModel) + return QV4::Encode::undefined(); + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(delegateModel); const int cacheIndex = model->m_cache.indexOf(cacheItem); Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); if (member) @@ -2510,8 +2535,8 @@ QQmlDelegateModelItem::~QQmlDelegateModelItem() Q_ASSERT(!object); if (incubationTask) { - if (metaType->model) - QQmlDelegateModelPrivate::get(metaType->model)->releaseIncubator(incubationTask); + if (QQmlDelegateModel *delegateModel = metaType->delegateModel()) + QQmlDelegateModelPrivate::get(delegateModel)->releaseIncubator(incubationTask); else delete incubationTask; } @@ -2523,10 +2548,9 @@ void QQmlDelegateModelItem::dispose() if (isReferenced()) return; - if (metaType->model) { - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(metaType->model); - model->removeCacheItem(this); - } + if (QQmlDelegateModel *delegateModel = metaType->delegateModel()) + QQmlDelegateModelPrivate::get(delegateModel)->removeCacheItem(this); + delete this; } @@ -2607,9 +2631,8 @@ QQmlDelegateModelItem *QQmlDelegateModelItem::dataForObject(QObject *object) int QQmlDelegateModelItem::groupIndex(Compositor::Group group) { - if (QQmlDelegateModelPrivate * const model = metaType->model - ? QQmlDelegateModelPrivate::get(metaType->model) - : nullptr) { + if (QQmlDelegateModel *delegateModel = metaType->delegateModel()) { + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(delegateModel); return model->m_compositor.find(Compositor::Cache, model->m_cache.indexOf(this)).index[group]; } return -1; @@ -2656,9 +2679,10 @@ int QQmlDelegateModelAttachedMetaObject::metaCall(QObject *object, QMetaObject:: } } else if (call == QMetaObject::WriteProperty) { if (_id >= memberPropertyOffset) { - if (!metaType->model) + QQmlDelegateModel *delegateModel = metaType->delegateModel(); + if (!delegateModel) return -1; - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(metaType->model); + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(delegateModel); Compositor::Group group = Compositor::Group(_id - memberPropertyOffset + 1); const int groupFlag = 1 << group; const bool member = attached->m_cacheItem->groups & groupFlag; @@ -2714,8 +2738,8 @@ void QQmlDelegateModelAttached::resetCurrentIndex() if (QQDMIncubationTask *incubationTask = m_cacheItem->incubationTask) { for (qsizetype i = 1, end = metaType->groupCount(); i <= end; ++i) m_currentIndex[i] = incubationTask->index[i]; - } else { - QQmlDelegateModelPrivate * const model = QQmlDelegateModelPrivate::get(m_cacheItem->metaType->model); + } else if (QQmlDelegateModel *delegateModel = metaType->delegateModel()) { + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(delegateModel); Compositor::iterator it = model->m_compositor.find( Compositor::Cache, model->m_cache.indexOf(m_cacheItem)); for (qsizetype i = 1, end = metaType->groupCount(); i <= end; ++i) @@ -2745,9 +2769,14 @@ int QQmlDelegateModelAttached::persistedItemsIndex() const void QQmlDelegateModelAttached::setInGroup(QQmlListCompositor::Group group, bool inGroup) { - if (!(m_cacheItem && m_cacheItem->metaType && m_cacheItem->metaType->model)) + if (!m_cacheItem) + return; + + QQmlDelegateModel *delegateModel = m_cacheItem->metaType->delegateModel(); + if (!delegateModel) return; - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_cacheItem->metaType->model); + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(delegateModel); const uint groupFlag = (1 << group); if (inGroup == bool(m_cacheItem->groups & groupFlag)) return; @@ -2791,7 +2820,7 @@ int QQmlDelegateModelAttached::itemsIndex() const QQmlDelegateModel *QQmlDelegateModelAttached::model() const { - return m_cacheItem ? m_cacheItem->metaType->model : nullptr; + return m_cacheItem ? m_cacheItem->metaType->delegateModel() : nullptr; } /*! @@ -2821,8 +2850,11 @@ void QQmlDelegateModelAttached::setGroups(const QStringList &groups) if (!m_cacheItem) return; - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_cacheItem->metaType->model); + QQmlDelegateModel *delegateModel = m_cacheItem->metaType->delegateModel(); + if (!delegateModel) + return; + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(delegateModel); const int groupFlags = model->m_cacheMetaType->parseGroups(groups); const int cacheIndex = model->m_cache.indexOf(m_cacheItem); Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); @@ -3177,10 +3209,8 @@ bool QQmlDelegateModelGroupPrivate::parseIndex(const QV4::Value &value, int *ind if (object) { QQmlDelegateModelItem * const cacheItem = object->d()->item; - if (QQmlDelegateModelPrivate *model = cacheItem->metaType->model - ? QQmlDelegateModelPrivate::get(cacheItem->metaType->model) - : nullptr) { - *index = model->m_cache.indexOf(cacheItem); + if (QQmlDelegateModel *delegateModel = cacheItem->metaType->delegateModel()) { + *index = QQmlDelegateModelPrivate::get(delegateModel)->m_cache.indexOf(cacheItem); *group = Compositor::Cache; return true; } diff --git a/src/qmlmodels/qqmldelegatemodel_p_p.h b/src/qmlmodels/qqmldelegatemodel_p_p.h index 367504d7fc..1eff54162a 100644 --- a/src/qmlmodels/qqmldelegatemodel_p_p.h +++ b/src/qmlmodels/qqmldelegatemodel_p_p.h @@ -37,12 +37,22 @@ typedef QQmlListCompositor Compositor; class QQmlDelegateModelAttachedMetaObject; class QQmlAbstractDelegateComponent; +class QQmlTableInstanceModel; class Q_QMLMODELS_EXPORT QQmlDelegateModelItemMetaType final : public QQmlRefCounted<QQmlDelegateModelItemMetaType> { public: - QQmlDelegateModelItemMetaType(QV4::ExecutionEngine *engine, QQmlDelegateModel *model, const QStringList &groupNames); + enum class ModelKind : quint8 { + InstanceModel, + DelegateModel, + TableInstanceModel, + }; + + QQmlDelegateModelItemMetaType( + QV4::ExecutionEngine *engine, QQmlDelegateModel *model, const QStringList &groupNames); + QQmlDelegateModelItemMetaType( + QV4::ExecutionEngine *engine, QQmlTableInstanceModel *model); ~QQmlDelegateModelItemMetaType(); void initializeAttachedMetaObject(); @@ -51,12 +61,23 @@ public: int parseGroups(const QStringList &groupNames) const; int parseGroups(const QV4::Value &groupNames) const; - QPointer<QQmlDelegateModel> model; + QQmlDelegateModel *delegateModel() const + { + return modelKind == ModelKind::DelegateModel + ? static_cast<QQmlDelegateModel *>(model.get()) + : nullptr; + } + qsizetype groupCount() const { return groupNames.size(); } + + void emitModelChanged() const; + + QPointer<QQmlInstanceModel> model; QV4::ExecutionEngine * const v4Engine; QQmlRefPointer<QQmlDelegateModelAttachedMetaObject> attachedMetaObject; const QStringList groupNames; QV4::PersistentValue modelItemProto; + ModelKind modelKind = ModelKind::InstanceModel; }; class QQmlAdaptorModel; diff --git a/src/qmlmodels/qqmldmlistaccessordata.cpp b/src/qmlmodels/qqmldmlistaccessordata.cpp index 99e1612f80..4eedb8b772 100644 --- a/src/qmlmodels/qqmldmlistaccessordata.cpp +++ b/src/qmlmodels/qqmldmlistaccessordata.cpp @@ -99,8 +99,7 @@ int VDMListDelegateDataType::metaCall( accessor->cachedDataClean = false; } else { model->list.set(accessor->index, data); - if (QQmlDelegateModel *delegateModel = accessor->metaType->model) - emit delegateModel->modelChanged(); + accessor->metaType->emitModelChanged(); } QMetaObject::activate(accessor, this, id - propertyOffset, nullptr); emit accessor->modelDataChanged(); diff --git a/src/qmlmodels/qqmltableinstancemodel.cpp b/src/qmlmodels/qqmltableinstancemodel.cpp index d07808ffe5..c19bcf3138 100644 --- a/src/qmlmodels/qqmltableinstancemodel.cpp +++ b/src/qmlmodels/qqmltableinstancemodel.cpp @@ -36,8 +36,8 @@ void QQmlTableInstanceModel::deleteModelItemLater(QQmlDelegateModelItem *modelIt QQmlTableInstanceModel::QQmlTableInstanceModel(QQmlContext *qmlContext, QObject *parent) : QQmlInstanceModel(*(new QObjectPrivate()), parent) , m_qmlContext(qmlContext) - , m_metaType(new QQmlDelegateModelItemMetaType(m_qmlContext->engine()->handle(), nullptr, QStringList()), - QQmlRefPointer<QQmlDelegateModelItemMetaType>::Adopt) + , m_metaType(QQml::makeRefPointer<QQmlDelegateModelItemMetaType>( + m_qmlContext->engine()->handle(), this)) { } diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp index 7a04a6ab7d..b80aaba10c 100644 --- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp +++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp @@ -8355,6 +8355,8 @@ void tst_QQuickTableView::delegateModelAccess() const QUrl url = testFileUrl("delegateModelAccess.qml"); LOAD_TABLEVIEW("delegateModelAccess.qml"); + QSignalSpy modelChangedSpy(tableView, &QQuickTableView::modelChanged); + if (delegateKind == Delegate::Untyped && modelKind == Model::Array) QSKIP("Properties of objects in arrays are not exposed as context properties"); @@ -8394,24 +8396,38 @@ void tst_QQuickTableView::delegateModelAccess() // (like with DelegateModel). (access == QQmlDelegateModel::Qt5ReadWrite && delegateKind == Delegate::Typed); + // Only the array is actually updated itself. The other models are pointers + const bool writeShouldSignal = modelKind == Model::Kind::Array; + double expected = 11; + // Initial setting of the model, signals one update + int expectedModelUpdates = 1; + QCOMPARE(modelChangedSpy.count(), expectedModelUpdates); + QCOMPARE(delegate->property("immediateX").toDouble(), expected); QCOMPARE(delegate->property("modelX").toDouble(), expected); - if (modelWritable) + if (modelWritable) { expected = 3; + if (writeShouldSignal) + ++expectedModelUpdates; + } QMetaObject::invokeMethod(delegate, "writeThroughModel"); QCOMPARE(delegate->property("immediateX").toDouble(), expected); QCOMPARE(delegate->property("modelX").toDouble(), expected); + QCOMPARE(modelChangedSpy.count(), expectedModelUpdates); double aAt0 = -1; QMetaObject::invokeMethod(tableView, "aAt0", Q_RETURN_ARG(double, aAt0)); QCOMPARE(aAt0, writeShouldPropagate ? expected : 11); - if (immediateWritable) + if (immediateWritable) { expected = 1; + if (writeShouldSignal) + ++expectedModelUpdates; + } QMetaObject::invokeMethod(delegate, "writeImmediate"); @@ -8420,6 +8436,7 @@ void tst_QQuickTableView::delegateModelAccess() delegateKind == Delegate::Untyped ? expected : 1); QCOMPARE(delegate->property("modelX").toDouble(), expected); + QCOMPARE(modelChangedSpy.count(), expectedModelUpdates); aAt0 = -1; QMetaObject::invokeMethod(tableView, "aAt0", Q_RETURN_ARG(double, aAt0)); |