diff options
-rw-r--r-- | src/quick/items/qquicktableview.cpp | 37 | ||||
-rw-r--r-- | src/quick/items/qquicktableview_p.h | 8 | ||||
-rw-r--r-- | src/quick/items/qquicktableview_p_p.h | 4 | ||||
-rw-r--r-- | tests/auto/quick/qquicktableview/data/delegateModelAccess.qml | 92 | ||||
-rw-r--r-- | tests/auto/quick/qquicktableview/tst_qquicktableview.cpp | 120 |
5 files changed, 260 insertions, 1 deletions
diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index 3e83cb1d09..d9ac4328fd 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -4508,6 +4508,7 @@ void QQuickTableViewPrivate::syncWithPendingChanges() syncViewportRect(); syncModel(); syncDelegate(); + syncDelegateModelAccess(); syncSyncView(); syncPositionView(); @@ -4556,6 +4557,18 @@ void QQuickTableViewPrivate::syncDelegate() tableModel->setDelegate(assignedDelegate); } +void QQuickTableViewPrivate::syncDelegateModelAccess() +{ + if (!tableModel) { + // Only the tableModel uses the delegateModelAccess assigned to a + // TableView. DelegateModel has its own delegateModelAccess, and + // ObjectModel doesn't use one. + return; + } + + tableModel->setDelegateModelAccess(assignedDelegateModelAccess); +} + QVariant QQuickTableViewPrivate::modelImpl() const { return assignedModel; @@ -5808,6 +5821,30 @@ void QQuickTableView::setEditTriggers(QQuickTableView::EditTriggers editTriggers emit editTriggersChanged(); } +/*! + \qmlproperty enumeration QtQuick::TableView::delegateModelAccess + + \include delegatemodelaccess.qdocinc +*/ +QQmlDelegateModel::DelegateModelAccess QQuickTableView::delegateModelAccess() const +{ + Q_D(const QQuickTableView); + return d->assignedDelegateModelAccess; +} + +void QQuickTableView::setDelegateModelAccess( + QQmlDelegateModel::DelegateModelAccess delegateModelAccess) +{ + Q_D(QQuickTableView); + if (delegateModelAccess == d->assignedDelegateModelAccess) + return; + + d->assignedDelegateModelAccess = delegateModelAccess; + d->scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::All); + + emit delegateModelAccessChanged(); +} + bool QQuickTableView::reuseItems() const { return bool(d_func()->reusableFlag == QQmlTableInstanceModel::Reusable); diff --git a/src/quick/items/qquicktableview_p.h b/src/quick/items/qquicktableview_p.h index cfcc9eae8a..d437f29724 100644 --- a/src/quick/items/qquicktableview_p.h +++ b/src/quick/items/qquicktableview_p.h @@ -27,6 +27,7 @@ QT_REQUIRE_CONFIG(quick_tableview); #include <QtQml/private/qqmlnullablevalue_p.h> #include <QtQml/private/qqmlfinalizer_p.h> #include <QtQml/private/qqmlguard_p.h> +#include <QtQmlModels/private/qqmldelegatemodel_p.h> QT_BEGIN_NAMESPACE @@ -68,6 +69,8 @@ class Q_QUICK_EXPORT QQuickTableView : public QQuickFlickable, public QQmlFinali Q_PROPERTY(bool resizableRows READ resizableRows WRITE setResizableRows NOTIFY resizableRowsChanged REVISION(6, 5) FINAL) Q_PROPERTY(EditTriggers editTriggers READ editTriggers WRITE setEditTriggers NOTIFY editTriggersChanged REVISION(6, 5) FINAL) Q_PROPERTY(SelectionMode selectionMode READ selectionMode WRITE setSelectionMode NOTIFY selectionModeChanged REVISION(6, 6) FINAL) + Q_PROPERTY(QQmlDelegateModel::DelegateModelAccess delegateModelAccess READ delegateModelAccess + WRITE setDelegateModelAccess NOTIFY delegateModelAccessChanged REVISION(6, 10) FINAL) QML_NAMED_ELEMENT(TableView) QML_ADDED_IN_VERSION(2, 12) @@ -185,6 +188,9 @@ public: EditTriggers editTriggers() const; void setEditTriggers(EditTriggers editTriggers); + QQmlDelegateModel::DelegateModelAccess delegateModelAccess() const; + void setDelegateModelAccess(QQmlDelegateModel::DelegateModelAccess delegateModelAccess); + Q_INVOKABLE void forceLayout(); Q_INVOKABLE void positionViewAtCell(const QPoint &cell, PositionMode mode, const QPointF &offset = QPointF(), const QRectF &subRect = QRectF()); Q_INVOKABLE void positionViewAtIndex(const QModelIndex &index, PositionMode mode, const QPointF &offset = QPointF(), const QRectF &subRect = QRectF()); @@ -274,7 +280,7 @@ Q_SIGNALS: Q_REVISION(6, 6) void selectionModeChanged(); Q_REVISION(6, 8) void rowMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex); Q_REVISION(6, 8) void columnMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex); - + Q_REVISION(6, 10) void delegateModelAccessChanged(); protected: void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override; diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h index c25772eaef..ee25d2cc44 100644 --- a/src/quick/items/qquicktableview_p_p.h +++ b/src/quick/items/qquicktableview_p_p.h @@ -393,6 +393,9 @@ public: bool warnNoSelectionModel = true; + QQmlDelegateModel::DelegateModelAccess assignedDelegateModelAccess + = QQmlDelegateModel::Qt5ReadWrite; + QJSValue rowHeightProvider; QJSValue columnWidthProvider; @@ -594,6 +597,7 @@ public: virtual void syncWithPendingChanges(); virtual void syncDelegate(); + virtual void syncDelegateModelAccess(); virtual QVariant modelImpl() const; virtual void setModelImpl(const QVariant &newModel); virtual void syncModel(); diff --git a/tests/auto/quick/qquicktableview/data/delegateModelAccess.qml b/tests/auto/quick/qquicktableview/data/delegateModelAccess.qml new file mode 100644 index 0000000000..21e67bb5d3 --- /dev/null +++ b/tests/auto/quick/qquicktableview/data/delegateModelAccess.qml @@ -0,0 +1,92 @@ +import QtQuick +import Test + +Item { + width: 100 + height: 100 + + property alias tableView: root + + TableView { + id: root + width: 100 + height: 100 + + property Component typedDelegate: Item { + 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: Item { + implicitWidth: 10 + implicitHeight: 10 + + property real immediateX: a + property real modelX: model.a + + function writeImmediate() { + a = 1; + } + + function writeThroughModel() { + model.a = 3; + } + } + + property ListModel singularModel: ListModel { + ListElement { + a: 11 + } + } + + property ListModel listModel: ListModel { + ListElement { + a: 11 + y: 12 + } + } + + property var array: [ {a: 11, y: 12} ] + + property QtObject 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 + case Model.List: return listModel + case Model.Array: return array + case Model.Object: return object + } + return undefined; + } + + delegate: { + switch (delegateIndex) { + case Delegate.Untyped: return untypedDelegate + case Delegate.Typed: return typedDelegate + } + return null + } + } +} diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp index 59a8e76a80..190181c523 100644 --- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp +++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp @@ -304,6 +304,9 @@ private slots: void typedModelData(); void requiredModelData(); + void delegateModelAccess_data(); + void delegateModelAccess(); + // Row and column reordering void checkVisualRowColumnAfterReorder(); void checkColumnRowSizeAfterReorder(); @@ -8192,6 +8195,123 @@ void tst_QQuickTableView::requiredModelData() } } +namespace Model { +Q_NAMESPACE +QML_ELEMENT +enum Kind : qint8 +{ + None = -1, + Singular, + List, + Array, + Object +}; +Q_ENUM_NS(Kind) +} + +namespace Delegate { +Q_NAMESPACE +QML_ELEMENT +enum Kind : qint8 +{ + None = -1, + Untyped, + Typed +}; +Q_ENUM_NS(Kind) +} + +template<typename Enum> +const char *enumKey(Enum value) { + const QMetaObject *mo = qt_getEnumMetaObject(value); + const QMetaEnum metaEnum = mo->enumerator(mo->indexOfEnumerator(qt_getEnumName(value))); + return metaEnum.valueToKey(value); +} + +void tst_QQuickTableView::delegateModelAccess_data() +{ + QTest::addColumn<QQmlDelegateModel::DelegateModelAccess>("access"); + QTest::addColumn<Model::Kind>("modelKind"); + QTest::addColumn<Delegate::Kind>("delegateKind"); + + using Access = QQmlDelegateModel::DelegateModelAccess; + for (auto access : { Access::Qt5ReadWrite, Access::ReadOnly, Access::ReadWrite }) { + for (auto model : { Model::Singular, Model::List, Model::Array, Model::Object }) { + for (auto delegate : { Delegate::Untyped, Delegate::Typed }) { + QTest::addRow("%s-%s-%s", enumKey(access), enumKey(model), enumKey(delegate)) + << access << model << delegate; + } + } + } +} + +void tst_QQuickTableView::delegateModelAccess() +{ + static const bool initialized = []() { + qmlRegisterNamespaceAndRevisions(&Model::staticMetaObject, "Test", 1); + qmlRegisterNamespaceAndRevisions(&Delegate::staticMetaObject, "Test", 1); + return true; + }(); + QVERIFY(initialized); + + QFETCH(QQmlDelegateModel::DelegateModelAccess, access); + QFETCH(Model::Kind, modelKind); + QFETCH(Delegate::Kind, delegateKind); + + const QUrl url = testFileUrl("delegateModelAccess.qml"); + LOAD_TABLEVIEW("delegateModelAccess.qml"); + + if (delegateKind == Delegate::Untyped && modelKind == Model::Array) + QSKIP("Properties of objects in arrays are not exposed as context properties"); + + if (access == QQmlDelegateModel::ReadOnly) { + const QRegularExpression message( + url.toString() + ":[0-9]+: TypeError: Cannot assign to read-only property \"a\""); + + QTest::ignoreMessage(QtWarningMsg, message); + if (delegateKind == Delegate::Untyped) + QTest::ignoreMessage(QtWarningMsg, message); + } + + tableView->setProperty("delegateModelAccess", access); + tableView->setProperty("modelIndex", modelKind); + tableView->setProperty("delegateIndex", delegateKind); + + WAIT_UNTIL_POLISHED; + + QCOMPARE(QQuickTableViewPrivate::get(tableView)->loadedItems.size(), 1); + QObject *delegate = QQuickTableViewPrivate::get(tableView)->loadedItems.begin().value()->item; + QVERIFY(delegate); + + const bool modelWritable = access != QQmlDelegateModel::ReadOnly; + const bool immediateWritable = (delegateKind == Delegate::Untyped) + ? access != QQmlDelegateModel::ReadOnly + : access == QQmlDelegateModel::ReadWrite; + + double expected = 11; + + QCOMPARE(delegate->property("immediateX").toDouble(), expected); + QCOMPARE(delegate->property("modelX").toDouble(), expected); + + if (modelWritable) + expected = 3; + + QMetaObject::invokeMethod(delegate, "writeThroughModel"); + QCOMPARE(delegate->property("immediateX").toDouble(), expected); + QCOMPARE(delegate->property("modelX").toDouble(), expected); + + if (immediateWritable) + expected = 1; + + QMetaObject::invokeMethod(delegate, "writeImmediate"); + + // Writes to required properties always succeed, but might not be propagated to the model + QCOMPARE(delegate->property("immediateX").toDouble(), + delegateKind == Delegate::Untyped ? expected : 1); + + QCOMPARE(delegate->property("modelX").toDouble(), expected); +} + void tst_QQuickTableView::checkVisualRowColumnAfterReorder() { LOAD_TABLEVIEW("reordertableview.qml"); // gives us 'tableView' variable |