diff options
author | Fabian Kosmale <fabian.kosmale@qt.io> | 2021-06-18 09:24:04 +0200 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2021-06-22 14:29:02 +0000 |
commit | 6fe8ed82cdc3e2ec4f6fc9980a41d457b6aed110 (patch) | |
tree | f920dd887bceb224110b50cb6ba09241a7a65f12 | |
parent | 1b2167d935ee19f2cb2c6d7bb0b557e6a8eb3485 (diff) |
Ensure model is in context if required properties are not used
We need to set the set the contextObject already in
QQmlDelegateModelPrivate::object, as the context property might be
accessed in a child component before we are able to run
setRequiredProperties (which would set-up the context in case there are
no required properties).
Instead of delaying setting the context object, we clear it now in
setRequiredProperties.
This has the drawback that one might be able to access context
properties on initial component setup which should not exist
(due to the presence of required properties).
This could be avoided by inspecting the Compilation Unit and only
setting the context if we do not find required properties, however that
needs further work to expose the necessary information.
Fixes: QTBUG-94223
Change-Id: I678de81408539417fc650f94da4c9f0d3167653d
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
(cherry picked from commit 58cd06033cacadab541efaa16a3eecec37dab0fa)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
4 files changed, 76 insertions, 1 deletions
diff --git a/src/qmlmodels/qqmldelegatemodel.cpp b/src/qmlmodels/qqmldelegatemodel.cpp index cbefc5d535..5c5f464cd5 100644 --- a/src/qmlmodels/qqmldelegatemodel.cpp +++ b/src/qmlmodels/qqmldelegatemodel.cpp @@ -963,8 +963,19 @@ void QQDMIncubationTask::initializeRequiredProperties(QQmlDelegateModelItem *mod if (incubatorPriv->hadRequiredProperties()) { QQmlData *d = QQmlData::get(object); auto contextData = d ? d->context : nullptr; - if (contextData) + if (contextData) { contextData->setExtraObject(modelItemToIncubate); + } + + // If we have required properties, we clear the context object + // so that the model role names are not polluting the context + if (incubating) { + Q_ASSERT(incubating->contextData); + incubating->contextData->setContextObject(nullptr); + } + if (proxyContext) { + proxyContext->setContextObject(nullptr); + } if (incubatorPriv->requiredProperties().empty()) return; @@ -1277,6 +1288,7 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQ QQmlRefPointer<QQmlContextData> ctxt = QQmlContextData::createRefCounted( QQmlContextData::get(creationContext ? creationContext : m_context.data())); + ctxt->setContextObject(cacheItem); cacheItem->contextData = ctxt; if (m_adaptorModel.hasProxyObject()) { @@ -1286,6 +1298,7 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQ QObject *proxied = proxy->proxiedObject(); cacheItem->incubationTask->proxiedObject = proxied; cacheItem->incubationTask->proxyContext = ctxt; + ctxt->setContextObject(cacheItem); // We don't own the proxied object. We need to clear it if it goes away. QObject::connect(proxied, &QObject::destroyed, cacheItem, &QQmlDelegateModelItem::childContextObjectDestroyed); diff --git a/tests/auto/qml/qqmldelegatemodel/data/ImageToggle.qml b/tests/auto/qml/qqmldelegatemodel/data/ImageToggle.qml new file mode 100644 index 0000000000..fa154b25f3 --- /dev/null +++ b/tests/auto/qml/qqmldelegatemodel/data/ImageToggle.qml @@ -0,0 +1,21 @@ +import QtQuick 2.0 + +Item { + property var isSelected: null + property string source + implicitWidth: 16 + implicitHeight: 16 + + onSourceChanged: { + updateImageSource() + } + + onIsSelectedChanged: { + updateImageSource() + } + + function updateImageSource() { + let result = isSelected ? source + "_selected_dark.png" : source + "_active_dark.png" + } + +} diff --git a/tests/auto/qml/qqmldelegatemodel/data/contextAccessedByHandler.qml b/tests/auto/qml/qqmldelegatemodel/data/contextAccessedByHandler.qml new file mode 100644 index 0000000000..46d7524527 --- /dev/null +++ b/tests/auto/qml/qqmldelegatemodel/data/contextAccessedByHandler.qml @@ -0,0 +1,31 @@ +import QtQuick 2.0 + +Item { + id: root + width: 640 + height: 480 + property bool works: myView.currentItem.okay + + ListView { + id: myView + model: myModel + anchors.fill: parent + delegate: Row { + property alias okay: image.isSelected + ImageToggle { + id: image + source: "glyph_16_arrow_patch" + isSelected: model.age < 6 + } + Text { + text: "age:" + model.age + " selected:" + image.isSelected + } + } + } + + ListModel { + id: myModel + ListElement { type: "Cat"; age: 3; } + ListElement { type: "Dog"; age: 2; } + } +} diff --git a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp index 0980d8b8ca..2571dadc79 100644 --- a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp +++ b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp @@ -46,6 +46,7 @@ private slots: void valueWithoutCallingObjectFirst(); void qtbug_86017(); void filterOnGroup_removeWhenCompleted(); + void contextAccessedByHandler(); }; class AbstractItemModel : public QAbstractItemModel @@ -165,6 +166,15 @@ void tst_QQmlDelegateModel::filterOnGroup_removeWhenCompleted() QVERIFY(QTest::qWaitFor([=]{ return model->count() == 2; })); } +void tst_QQmlDelegateModel::contextAccessedByHandler() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("contextAccessedByHandler.qml")); + QScopedPointer<QObject> root(component.create()); + QVERIFY2(root, qPrintable(component.errorString())); + QVERIFY(root->property("works").toBool()); +} + QTEST_MAIN(tst_QQmlDelegateModel) #include "tst_qqmldelegatemodel.moc" |