summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <[email protected]>2025-05-26 17:07:28 +0200
committerUlf Hermann <[email protected]>2025-05-29 19:03:34 +0200
commit58f50363b1e7d21d20b83bac945bd24e85863885 (patch)
tree56ebcb2a1e6f4697742f85e7bcdfa610ac2659a9
parent7142e4bcceb6017191dc22935f0758bd68a25e5c (diff)
Expose delegateModelAccess from QDeclarativeGeoMapItemViewHEADdev
It mirrors the same property from any internal delegate model. By default, the value is "Qt5ReadWrite". [ChangeLog][Location] MapItemView now has a new property delegateModelAccess. Setting it to DelegateModel.ReadWrite allows you to write values into the model via required properties just as you could with context properties. Task-number: QTBUG-132420 Change-Id: Ie0795fc6f793e71def2a9fa2af711dfac942ddfc Reviewed-by: Fabian Kosmale <[email protected]> Reviewed-by: Matthias Rauter <[email protected]>
-rw-r--r--src/location/quickmapitems/qdeclarativegeomapitemview.cpp49
-rw-r--r--src/location/quickmapitems/qdeclarativegeomapitemview_p.h17
-rw-r--r--tests/auto/declarative_ui/Delegate.qml12
-rw-r--r--tests/auto/declarative_ui/Model.qml14
-rw-r--r--tests/auto/declarative_ui/tst_map_itemview.qml224
5 files changed, 312 insertions, 4 deletions
diff --git a/src/location/quickmapitems/qdeclarativegeomapitemview.cpp b/src/location/quickmapitems/qdeclarativegeomapitemview.cpp
index a97174a6..aae41965 100644
--- a/src/location/quickmapitems/qdeclarativegeomapitemview.cpp
+++ b/src/location/quickmapitems/qdeclarativegeomapitemview.cpp
@@ -87,6 +87,7 @@ void QDeclarativeGeoMapItemView::componentComplete()
if (m_delegate)
m_delegateModel->setDelegate(m_delegate);
+ m_delegateModel->setDelegateModelAccess(m_delegateModelAccess);
m_delegateModel->componentComplete();
}
@@ -189,8 +190,14 @@ void QDeclarativeGeoMapItemView::setModel(const QVariant &model)
return;
m_itemModel = model;
- if (m_componentCompleted)
+ if (m_componentCompleted) {
+
+ // Make sure to clear all stale items from the map and the model
+ m_delegateModel->drainReusableItemsPool(0);
+ removeInstantiatedItems(false);
+
m_delegateModel->setModel(m_itemModel);
+ }
emit modelChanged();
}
@@ -315,6 +322,46 @@ bool QDeclarativeGeoMapItemView::incubateDelegates() const
return m_incubationMode == QQmlIncubator::Asynchronous;
}
+/*!
+ \qmlproperty enumeration QtLocation::MapItemView::delegateModelAccess
+ \since 6.10
+
+ This property determines how delegates can access the model.
+
+ \value DelegateModel.ReadOnly
+ Prohibit delegates from writing the model via either context properties,
+ the \c model object, or required properties.
+
+ \value DelegateModel.ReadWrite
+ Allow delegates to write the model via either context properties,
+ the \c model object, or required properties.
+
+ \value DelegateModel.Qt5ReadWrite
+ Allow delegates to write the model via the \c model object and context
+ properties but \e not via required properties.
+
+ The default is \c DelegateModel.Qt5ReadWrite.
+
+ \sa {Models and Views in Qt Quick#Changing Model Data}
+*/
+QQmlDelegateModel::DelegateModelAccess QDeclarativeGeoMapItemView::delegateModelAccess() const
+{
+ return m_delegateModelAccess;
+}
+
+void QDeclarativeGeoMapItemView::setDelegateModelAccess(
+ QQmlDelegateModel::DelegateModelAccess delegateModelAccess)
+{
+ if (m_delegateModelAccess == delegateModelAccess)
+ return;
+
+ m_delegateModelAccess = delegateModelAccess;
+ if (m_componentCompleted)
+ m_delegateModel->setDelegateModelAccess(m_delegateModelAccess);
+
+ emit delegateModelAccessChanged();
+}
+
QList<QQuickItem *> QDeclarativeGeoMapItemView::mapItems()
{
return m_instantiatedItems;
diff --git a/src/location/quickmapitems/qdeclarativegeomapitemview_p.h b/src/location/quickmapitems/qdeclarativegeomapitemview_p.h
index 4b4078ee..57ae5561 100644
--- a/src/location/quickmapitems/qdeclarativegeomapitemview_p.h
+++ b/src/location/quickmapitems/qdeclarativegeomapitemview_p.h
@@ -52,6 +52,9 @@ class Q_LOCATION_EXPORT QDeclarativeGeoMapItemView : public QDeclarativeGeoMapIt
Q_PROPERTY(QQuickTransition *remove MEMBER m_exit REVISION(5, 12))
Q_PROPERTY(QList<QQuickItem *> mapItems READ mapItems REVISION(5, 12))
Q_PROPERTY(bool incubateDelegates READ incubateDelegates WRITE setIncubateDelegates NOTIFY incubateDelegatesChanged REVISION(5, 12))
+ Q_PROPERTY(QQmlDelegateModel::DelegateModelAccess delegateModelAccess READ delegateModelAccess
+ WRITE setDelegateModelAccess NOTIFY delegateModelAccessChanged REVISION(6, 10) FINAL)
+
public:
explicit QDeclarativeGeoMapItemView(QQuickItem *parent = nullptr);
@@ -73,6 +76,9 @@ public:
void setIncubateDelegates(bool useIncubators);
bool incubateDelegates() const;
+ QQmlDelegateModel::DelegateModelAccess delegateModelAccess() const;
+ void setDelegateModelAccess(QQmlDelegateModel::DelegateModelAccess delegateModelAccess);
+
QList<QQuickItem *> mapItems();
// From QQmlParserStatus
@@ -84,6 +90,7 @@ Q_SIGNALS:
void delegateChanged();
void autoFitViewportChanged();
void incubateDelegatesChanged();
+ Q_REVISION(6, 10) void delegateModelAccessChanged();
private Q_SLOTS:
void destroyingItem(QObject *object);
@@ -106,18 +113,22 @@ private:
void addItemGroupToMap(QDeclarativeGeoMapItemGroup *item, int index, bool createdItem);
void addDelegateToMap(QQuickItem *object, int index, bool createdItem = false);
- bool m_componentCompleted = false;
QQmlIncubator::IncubationMode m_incubationMode = QQmlIncubator::Asynchronous;
QQmlComponent *m_delegate = nullptr;
QVariant m_itemModel;
QDeclarativeGeoMap *m_map = nullptr;
QList<QQuickItem *> m_instantiatedItems;
- bool m_fitViewport = false;
- bool m_creatingObject = false;
QQmlDelegateModel *m_delegateModel = nullptr;
QQuickTransition *m_enter = nullptr;
QQuickTransition *m_exit = nullptr;
+ QQmlDelegateModel::DelegateModelAccess m_delegateModelAccess
+ = QQmlDelegateModel::Qt5ReadWrite;
+
+ bool m_componentCompleted = false;
+ bool m_fitViewport = false;
+ bool m_creatingObject = false;
+
friend class QDeclarativeGeoMap;
friend class QDeclarativeGeoMapItemBase;
friend class QDeclarativeGeoMapItemTransitionManager;
diff --git a/tests/auto/declarative_ui/Delegate.qml b/tests/auto/declarative_ui/Delegate.qml
new file mode 100644
index 00000000..210aae29
--- /dev/null
+++ b/tests/auto/declarative_ui/Delegate.qml
@@ -0,0 +1,12 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+
+QtObject {
+ enum Variants {
+ None = -1,
+ Untyped,
+ Typed
+ }
+}
diff --git a/tests/auto/declarative_ui/Model.qml b/tests/auto/declarative_ui/Model.qml
new file mode 100644
index 00000000..fb3681de
--- /dev/null
+++ b/tests/auto/declarative_ui/Model.qml
@@ -0,0 +1,14 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+
+QtObject {
+ enum Variants {
+ None = -1,
+ Singular,
+ List,
+ Array,
+ Object
+ }
+}
diff --git a/tests/auto/declarative_ui/tst_map_itemview.qml b/tests/auto/declarative_ui/tst_map_itemview.qml
index fc4e9472..d4f84b72 100644
--- a/tests/auto/declarative_ui/tst_map_itemview.qml
+++ b/tests/auto/declarative_ui/tst_map_itemview.qml
@@ -20,6 +20,7 @@ Item {
&& mapForView.mapReady
&& mapForTestingListModel.mapReady
&& mapForTestingRouteModel.mapReady
+ && mapForTestingDelegateModelAccess.mapReady
MapItemView {
id: routeItemViewExtra
@@ -274,6 +275,100 @@ Item {
}
}
+ Map {
+ id: mapForTestingDelegateModelAccess
+
+ property int mapItemsLength: mapItems.length
+
+ plugin: testPlugin
+ center: mapDefaultCenter
+ anchors.fill: parent
+ zoomLevel: 2
+
+ MapItemView {
+ id: delegateModelAccessItemView
+ width: 100
+ height: 100
+
+ property Component typedDelegate: MapQuickItem {
+ implicitWidth: 10
+ implicitHeight: 10
+
+ required property QtObject model
+
+ required property real a
+
+ property real immediateX: a
+ property real modelX: model.a
+
+ function writeImmediate() {
+ a = 1;
+ }
+
+ function writeThroughModel() {
+ model.a = 3;
+ }
+ }
+
+ property Component untypedDelegate: MapQuickItem {
+ implicitWidth: 10
+ implicitHeight: 10
+
+ property real immediateX: a
+ property real modelX: model.a
+
+ function writeImmediate() {
+ a = 1;
+ }
+
+ function writeThroughModel() {
+ model.a = 3;
+ }
+ }
+
+ property Component singularModel: ListModel {
+ ListElement {
+ a: 11
+ }
+ }
+
+ property Component listModel: ListModel {
+ ListElement {
+ a: 11
+ y: 12
+ }
+ }
+
+ function array() { return [ {a: 11, y: 12} ] }
+
+ property Component object: QtObject {
+ property int a: 11
+ property int y: 12
+ }
+
+ property int modelIndex: Model.None
+ property int delegateIndex: Delegate.None
+
+ model: {
+ switch (modelIndex) {
+ case Model.Singular: return singularModel.createObject()
+ case Model.List: return listModel.createObject()
+ case Model.Array: return array()
+ case Model.Object: return object.createObject()
+ }
+ return undefined;
+ }
+
+ delegate: {
+ switch (delegateIndex) {
+ case Delegate.Untyped: return untypedDelegate
+ case Delegate.Typed: return typedDelegate
+ }
+ return null
+ }
+ }
+ }
+
TestCase {
name: "MapItem"
when: windowShown && allMapsReady
@@ -555,5 +650,134 @@ Item {
mapForTestingRouteModel.clearMapItems()
compare(mapForTestingRouteModel.mapItems.length, 0)
}
+
+ function test_delegateModelAccess_data() {
+ function modelKey(value) {
+ switch (value) {
+ case Model.None:
+ return "None"
+ case Model.Singular:
+ return "Singular"
+ case Model.List:
+ return "List"
+ case Model.Array:
+ return "Array"
+ case Model.Object:
+ return "Object"
+ default:
+ break
+ }
+
+ return ""
+ }
+
+ function delegateKey(value) {
+ switch (value) {
+ case Delegate.None:
+ return "None"
+ case Delegate.Untyped:
+ return "Untyped"
+ case Delegate.Typed:
+ return "Typed"
+ default:
+ break
+ }
+
+ return ""
+ }
+
+ function accessKey(value) {
+ switch (value) {
+ case DelegateModel.Qt5ReadWrite:
+ return "Qt5ReadWrite"
+ case DelegateModel.ReadOnly:
+ return "ReadOnly"
+ case DelegateModel.ReadWrite:
+ return "ReadWrite"
+ default:
+ break;
+ }
+
+ return "";
+ }
+
+ let data = [];
+ for (let access of [
+ DelegateModel.Qt5ReadWrite,
+ DelegateModel.ReadOnly,
+ DelegateModel.ReadWrite]) {
+ for (let model of [Model.Singular, Model.List, Model.Array, Model.Object]) {
+ for (let delegate of [Delegate.Untyped, Delegate.Typed]) {
+ data.push({
+ tag: `${accessKey(access)}-${modelKey(model)}-${delegateKey(delegate)}`,
+ access: access,
+ modelKind: model,
+ delegateKind: delegate
+ });
+ }
+ }
+ }
+ return data
+ }
+
+ function test_delegateModelAccess(data) {
+ delegateModelAccessItemView.delegateModelAccess = DelegateModel.Qt5ReadWrite
+ delegateModelAccessItemView.modelIndex = Model.None
+ delegateModelAccessItemView.delegateIndex = Delegate.None
+ tryCompare(mapForTestingDelegateModelAccess, "mapItemsLength", 0)
+
+ const access = data.access
+ const modelKind = data.modelKind
+ const delegateKind = data.delegateKind
+
+ if (delegateKind === Delegate.Untyped && modelKind === Model.Array)
+ skip("Properties of objects in arrays are not exposed as context properties")
+
+ delegateModelAccessItemView.delegateModelAccess = access
+ delegateModelAccessItemView.modelIndex = modelKind
+ delegateModelAccessItemView.delegateIndex = delegateKind
+
+ tryCompare(mapForTestingDelegateModelAccess, "mapItemsLength", 1)
+ const delegate = mapForTestingDelegateModelAccess.mapItems[0]
+ verify(delegate)
+
+ const modelWritable = (access !== DelegateModel.ReadOnly)
+ const immediateWritable = (delegateKind === Delegate.Untyped)
+ ? access !== DelegateModel.ReadOnly
+ : access === DelegateModel.ReadWrite
+
+ let expected = 11
+
+ compare(delegate.immediateX, expected)
+ compare(delegate.modelX, expected)
+
+ if (modelWritable)
+ expected = 3
+
+ try {
+ delegate.writeThroughModel()
+ } catch (e1) {
+ compare(e1.message, 'Cannot assign to read-only property "a"')
+ compare(access, DelegateModel.ReadOnly)
+ }
+
+ compare(delegate.immediateX, expected)
+ compare(delegate.modelX, expected)
+
+ if (immediateWritable)
+ expected = 1
+
+ try {
+ delegate.writeImmediate()
+ } catch (e2) {
+ compare(e2.message, 'Cannot assign to read-only property "a"')
+ compare(access, DelegateModel.ReadOnly)
+ compare(delegateKind, Delegate.Untyped)
+ }
+
+ // Writes to required properties always succeed, but might not be propagated to the model
+ compare(delegate.immediateX, delegateKind === Delegate.Untyped ? expected : 1)
+ compare(delegate.modelX, expected)
+ }
}
}