summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp46
-rw-r--r--tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp1
-rw-r--r--tests/auto/qml/ecmascripttests/TestExpectations2
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp287
-rw-r--r--tests/auto/qml/qjsvalue/tst_qjsvalue.cpp52
-rw-r--r--tests/auto/qml/qjsvalue/tst_qjsvalue.h1
-rw-r--r--tests/auto/qml/qml.pro5
-rw-r--r--tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp1
-rw-r--r--tests/auto/qml/qqmlchangeset/qqmlchangeset.pro2
-rw-r--r--tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp1
-rw-r--r--tests/auto/qml/qqmlecmascript/data/regularExpression.2.qml7
-rw-r--r--tests/auto/qml/qqmlecmascript/data/regularExpression.qml7
-rw-r--r--tests/auto/qml/qqmlecmascript/testtypes.h18
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp33
-rw-r--r--tests/auto/qml/qqmlinstantiator/qqmlinstantiator.pro2
-rw-r--r--tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp2
-rw-r--r--tests/auto/qml/qqmllanguage/data/bareQmlImport.errors.txt0
-rw-r--r--tests/auto/qml/qqmllanguage/data/bareQmlImport.qml6
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp14
-rw-r--r--tests/auto/qml/qqmllistcompositor/qqmllistcompositor.pro2
-rw-r--r--tests/auto/qml/qqmllistmodel/qqmllistmodel.pro2
-rw-r--r--tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp6
-rw-r--r--tests/auto/qml/qqmllistmodelworkerscript/qqmllistmodelworkerscript.pro2
-rw-r--r--tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp4
-rw-r--r--tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp11
-rw-r--r--tests/auto/qml/qqmlobjectmodel/qqmlobjectmodel.pro2
-rw-r--r--tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp4
-rw-r--r--tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp6
-rw-r--r--tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp1
-rw-r--r--tests/auto/qml/qqmltablemodel/data/TestModel.qml50
-rw-r--r--tests/auto/qml/qqmltablemodel/data/TestUtils.js45
-rw-r--r--tests/auto/qml/qqmltablemodel/data/common.qml149
-rw-r--r--tests/auto/qml/qqmltablemodel/data/complex.qml68
-rw-r--r--tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml55
-rw-r--r--tests/auto/qml/qqmltablemodel/data/empty.qml82
-rw-r--r--tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml47
-rw-r--r--tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml86
-rw-r--r--tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml84
-rw-r--r--tests/auto/qml/qqmltablemodel/qqmltablemodel.pro10
-rw-r--r--tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp980
-rw-r--r--tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp2
-rw-r--r--tests/auto/qml/qquickworkerscript/qquickworkerscript.pro2
-rw-r--r--tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp46
-rw-r--r--tests/auto/qml/qv4assembler/tst_qv4assembler.cpp12
-rw-r--r--tests/auto/qml/v4traced/tst_v4traced.cpp325
-rw-r--r--tests/auto/qml/v4traced/v4traced.pro5
-rw-r--r--tests/auto/quick/examples/tst_examples.cpp1
-rw-r--r--tests/auto/quick/pointerhandlers/pointerhandlers.pro1
-rw-r--r--tests/auto/quick/pointerhandlers/qquickwheelhandler/data/nested.qml55
-rw-r--r--tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml45
-rw-r--r--tests/auto/quick/pointerhandlers/qquickwheelhandler/qquickwheelhandler.pro14
-rw-r--r--tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp344
-rw-r--r--tests/auto/quick/propertyrequirements/tst_propertyrequirements.cpp1
-rw-r--r--tests/auto/quick/qquickanchors/tst_qquickanchors.cpp4
-rw-r--r--tests/auto/quick/qquickanimations/qquickanimations.pro2
-rw-r--r--tests/auto/quick/qquickanimations/tst_qquickanimations.cpp2
-rw-r--r--tests/auto/quick/qquickboundaryrule/data/dragHandler.qml23
-rw-r--r--tests/auto/quick/qquickboundaryrule/qquickboundaryrule.pro12
-rw-r--r--tests/auto/quick/qquickboundaryrule/tst_qquickboundaryrule.cpp99
-rw-r--r--tests/auto/quick/qquickgridview/qquickgridview.pro2
-rw-r--r--tests/auto/quick/qquickgridview/tst_qquickgridview.cpp2
-rw-r--r--tests/auto/quick/qquicklistview/qquicklistview.pro2
-rw-r--r--tests/auto/quick/qquicklistview/tst_qquicklistview.cpp6
-rw-r--r--tests/auto/quick/qquickpathview/qquickpathview.pro2
-rw-r--r--tests/auto/quick/qquickpathview/tst_qquickpathview.cpp2
-rw-r--r--tests/auto/quick/qquickrepeater/qquickrepeater.pro2
-rw-r--r--tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp14
-rw-r--r--tests/auto/quick/qquicktableview/data/syncviewsimple.qml122
-rw-r--r--tests/auto/quick/qquicktableview/qquicktableview.pro2
-rw-r--r--tests/auto/quick/qquicktableview/tst_qquicktableview.cpp536
-rw-r--r--tests/auto/quick/qquickvisualdatamodel/qquickvisualdatamodel.pro2
-rw-r--r--tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp2
-rw-r--r--tests/auto/quick/quick.pro1
-rw-r--r--tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp204
-rw-r--r--tests/libfuzzer/qml/jsapi/evaluate/evaluate.pro6
-rw-r--r--tests/libfuzzer/qml/jsapi/evaluate/main.cpp43
-rw-r--r--tests/manual/pointer/content/FakeFlickable.qml101
-rw-r--r--tests/manual/pointer/content/LeftDrawer.qml101
-rw-r--r--tests/manual/pointer/content/Slider.qml33
-rw-r--r--tests/manual/pointer/fakeFlickable.qml58
-rw-r--r--tests/manual/pointer/map.qml18
-rw-r--r--tests/manual/pointer/pinchAndWheel.qml160
-rw-r--r--tests/manual/tableview/tablemodel/form/RowForm.qml102
-rw-r--r--tests/manual/tableview/tablemodel/form/form.pro10
-rw-r--r--tests/manual/tableview/tablemodel/form/main.cpp52
-rw-r--r--tests/manual/tableview/tablemodel/form/main.qml290
-rw-r--r--tests/manual/tableview/tablemodel/json/JsonData.js186
-rw-r--r--tests/manual/tableview/tablemodel/json/json.pro12
-rw-r--r--tests/manual/tableview/tablemodel/json/main.cpp52
-rw-r--r--tests/manual/tableview/tablemodel/json/main.qml116
-rw-r--r--tests/manual/tableview/tablemodel/tablemodel.pro2
91 files changed, 4831 insertions, 590 deletions
diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp
index 99c90c142f..0ebf43eb6f 100644
--- a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp
+++ b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp
@@ -524,7 +524,7 @@ void tst_QQmlEngineDebugService::watch_property()
QCOMPARE(spy.count(), 1);
QCOMPARE(spy.at(0).at(0).value<QByteArray>(), prop.name.toUtf8());
- QCOMPARE(spy.at(0).at(1).value<QVariant>(), qVariantFromValue(origWidth*2));
+ QCOMPARE(spy.at(0).at(1).value<QVariant>(), QVariant::fromValue(origWidth*2));
}
void tst_QQmlEngineDebugService::watch_object()
@@ -772,11 +772,11 @@ void tst_QQmlEngineDebugService::queryObject()
}
// test specific property values
- QCOMPARE(findProperty(rect.properties, "width").value, qVariantFromValue(500));
- QCOMPARE(findProperty(rect.properties, "height").value, qVariantFromValue(600));
- QCOMPARE(findProperty(rect.properties, "color").value, qVariantFromValue(QColor("blue")));
+ QCOMPARE(findProperty(rect.properties, "width").value, QVariant::fromValue(500));
+ QCOMPARE(findProperty(rect.properties, "height").value, QVariant::fromValue(600));
+ QCOMPARE(findProperty(rect.properties, "color").value, QVariant::fromValue(QColor("blue")));
- QCOMPARE(findProperty(text.properties, "color").value, qVariantFromValue(QColor("blue")));
+ QCOMPARE(findProperty(text.properties, "color").value, QVariant::fromValue(QColor("blue")));
} else {
foreach (const QQmlEngineDebugObjectReference &child, obj.children) {
QVERIFY(!child.className.isEmpty());
@@ -851,11 +851,11 @@ void tst_QQmlEngineDebugService::queryObjectsForLocation()
}
// test specific property values
- QCOMPARE(findProperty(rect.properties, "width").value, qVariantFromValue(500));
- QCOMPARE(findProperty(rect.properties, "height").value, qVariantFromValue(600));
- QCOMPARE(findProperty(rect.properties, "color").value, qVariantFromValue(QColor("blue")));
+ QCOMPARE(findProperty(rect.properties, "width").value, QVariant::fromValue(500));
+ QCOMPARE(findProperty(rect.properties, "height").value, QVariant::fromValue(600));
+ QCOMPARE(findProperty(rect.properties, "color").value, QVariant::fromValue(QColor("blue")));
- QCOMPARE(findProperty(text.properties, "color").value, qVariantFromValue(QColor("blue")));
+ QCOMPARE(findProperty(text.properties, "color").value, QVariant::fromValue(QColor("blue")));
} else {
foreach (const QQmlEngineDebugObjectReference &child, obj.children) {
QVERIFY(!child.className.isEmpty());
@@ -1004,15 +1004,15 @@ void tst_QQmlEngineDebugService::queryExpressionResult_data()
QTest::addColumn<QString>("expr");
QTest::addColumn<QVariant>("result");
- QTest::newRow("width + 50") << "width + 50" << qVariantFromValue(60);
- QTest::newRow("blueRect.width") << "blueRect.width" << qVariantFromValue(500);
- QTest::newRow("bad expr") << "aeaef" << qVariantFromValue(QString("<undefined>"));
- QTest::newRow("QObject*") << "varObj" << qVariantFromValue(QString("<unnamed object>"));
- QTest::newRow("list of QObject*") << "varObjList" << qVariantFromValue(QVariantList() << QVariant(QString("<unnamed object>")));
+ QTest::newRow("width + 50") << "width + 50" << QVariant::fromValue(60);
+ QTest::newRow("blueRect.width") << "blueRect.width" << QVariant::fromValue(500);
+ QTest::newRow("bad expr") << "aeaef" << QVariant::fromValue(QString("<undefined>"));
+ QTest::newRow("QObject*") << "varObj" << QVariant::fromValue(QString("<unnamed object>"));
+ QTest::newRow("list of QObject*") << "varObjList" << QVariant::fromValue(QVariantList() << QVariant(QString("<unnamed object>")));
QVariantMap map;
map.insert(QLatin1String("rect"), QVariant(QLatin1String("<unnamed object>")));
- QTest::newRow("varObjMap") << "varObjMap" << qVariantFromValue(map);
- QTest::newRow("simpleVar") << "simpleVar" << qVariantFromValue(10.05);
+ QTest::newRow("varObjMap") << "varObjMap" << QVariant::fromValue(map);
+ QTest::newRow("simpleVar") << "simpleVar" << QVariant::fromValue(10.05);
}
void tst_QQmlEngineDebugService::queryExpressionResultInRootContext()
@@ -1052,15 +1052,15 @@ void tst_QQmlEngineDebugService::queryExpressionResultBC_data()
QTest::addColumn<QString>("expr");
QTest::addColumn<QVariant>("result");
- QTest::newRow("width + 50") << "width + 50" << qVariantFromValue(60);
- QTest::newRow("blueRect.width") << "blueRect.width" << qVariantFromValue(500);
- QTest::newRow("bad expr") << "aeaef" << qVariantFromValue(QString("<undefined>"));
- QTest::newRow("QObject*") << "varObj" << qVariantFromValue(QString("<unnamed object>"));
- QTest::newRow("list of QObject*") << "varObjList" << qVariantFromValue(QVariantList() << QVariant(QString("<unnamed object>")));
+ QTest::newRow("width + 50") << "width + 50" << QVariant::fromValue(60);
+ QTest::newRow("blueRect.width") << "blueRect.width" << QVariant::fromValue(500);
+ QTest::newRow("bad expr") << "aeaef" << QVariant::fromValue(QString("<undefined>"));
+ QTest::newRow("QObject*") << "varObj" << QVariant::fromValue(QString("<unnamed object>"));
+ QTest::newRow("list of QObject*") << "varObjList" << QVariant::fromValue(QVariantList() << QVariant(QString("<unnamed object>")));
QVariantMap map;
map.insert(QLatin1String("rect"), QVariant(QLatin1String("<unnamed object>")));
- QTest::newRow("varObjMap") << "varObjMap" << qVariantFromValue(map);
- QTest::newRow("simpleVar") << "simpleVar" << qVariantFromValue(10.05);
+ QTest::newRow("varObjMap") << "varObjMap" << QVariant::fromValue(map);
+ QTest::newRow("simpleVar") << "simpleVar" << QVariant::fromValue(10.05);
}
void tst_QQmlEngineDebugService::setBindingForObject()
diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
index 497c721f50..84f5eebd10 100644
--- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
+++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
@@ -36,7 +36,6 @@
#include <QQmlComponent>
#include <private/qv4engine_p.h>
#include <private/qv4debugging_p.h>
-#include <private/qv8engine_p.h>
#include <private/qv4objectiterator_p.h>
#include <private/qv4string_p.h>
#include <private/qqmlbuiltinfunctions_p.h>
diff --git a/tests/auto/qml/ecmascripttests/TestExpectations b/tests/auto/qml/ecmascripttests/TestExpectations
index cf7bf3d8ba..0e0d70845b 100644
--- a/tests/auto/qml/ecmascripttests/TestExpectations
+++ b/tests/auto/qml/ecmascripttests/TestExpectations
@@ -211,7 +211,6 @@ built-ins/Promise/prototype/then/ctor-throws.js fails
built-ins/Promise/race/ctx-ctor.js fails
built-ins/Proxy/ownKeys/return-duplicate-entries-throws.js fails
built-ins/Proxy/ownKeys/return-duplicate-symbol-entries-throws.js fails
-built-ins/RegExp/S15.10.2.12_A2_T1.js fails
built-ins/RegExp/prototype/Symbol.match/builtin-success-u-return-val-groups.js fails
built-ins/RegExp/prototype/Symbol.split/species-ctor.js fails
built-ins/RegExp/prototype/exec/S15.10.6.2_A5_T3.js fails
@@ -219,7 +218,6 @@ built-ins/RegExp/prototype/exec/failure-lastindex-access.js fails
built-ins/RegExp/prototype/exec/success-lastindex-access.js fails
built-ins/RegExp/prototype/source/value-line-terminator.js fails
built-ins/RegExp/prototype/test/S15.10.6.3_A1_T22.js fails
-built-ins/RegExp/u180e.js fails
built-ins/RegExp/unicode_restricted_brackets.js fails
built-ins/RegExp/unicode_restricted_character_class_escape.js fails
built-ins/RegExp/unicode_restricted_identity_escape.js fails
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index 75fa2216f7..6ca2663f30 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -66,6 +66,10 @@ private slots:
void newArray();
void newArray_HooliganTask218092();
void newArray_HooliganTask233836();
+ void toScriptValue_data();
+ void toScriptValue();
+ void toScriptValuenotroundtripped_data();
+ void toScriptValuenotroundtripped();
void newVariant();
void newVariant_valueOfToString();
void newVariant_valueOfEnum();
@@ -94,6 +98,7 @@ private slots:
void valueConversion_basic2();
void valueConversion_dateTime();
void valueConversion_regExp();
+ void valueConversion_RegularExpression();
void castWithMultipleInheritance();
void collectGarbage();
void gcWithNestedDataStructure();
@@ -135,6 +140,8 @@ private slots:
void qRegExpInport_data();
void qRegExpInport();
+ void qRegularExpressionImport_data();
+ void qRegularExpressionImport();
void dateRoundtripJSQtJS();
void dateRoundtripQtJSQt();
void dateConversionJSQt();
@@ -238,6 +245,9 @@ private slots:
void equality();
void aggressiveGc();
+ void interrupt_data();
+ void interrupt();
+
public:
Q_INVOKABLE QJSValue throwingCppMethod1();
Q_INVOKABLE void throwingCppMethod2();
@@ -481,17 +491,97 @@ void tst_QJSEngine::newArray_HooliganTask233836()
}
}
+void tst_QJSEngine::toScriptValue_data()
+{
+ QTest::addColumn<QVariant>("input");
+
+ QTest::newRow("UnknownType") << QVariant(int(QMetaType::UnknownType), nullptr);
+ QTest::newRow("Nullptr") << QVariant(int(QMetaType::Nullptr), nullptr);
+ QTest::newRow("true") << QVariant(true);
+ QTest::newRow("false") << QVariant(false);
+ QTest::newRow("int") << QVariant(int(42));
+ QTest::newRow("uint") << QVariant(uint(42));
+ QTest::newRow("longlong") << QVariant(qlonglong(4242));
+ QTest::newRow("ulonglong") << QVariant(qulonglong(4242));
+ QTest::newRow("double") << QVariant(double(42.42));
+ QTest::newRow("float") << QVariant(float(42.42));
+ QTest::newRow("qstring") << QVariant(QString::fromLatin1("hello"));
+ QTest::newRow("qbytearray") << QVariant(QByteArray("hello"));
+ QTest::newRow("short") << QVariant(short('r'));
+ QTest::newRow("ushort") << QVariant(short('b'));
+ QTest::newRow("char") << QVariant(char('r'));
+ QTest::newRow("uchar") << QVariant(uchar('b'));
+ QTest::newRow("qchar") << QVariant(QString::fromUtf8("å").at(0));
+ QTest::newRow("qdate") << QVariant(QDate(1925, 5, 8));
+ QTest::newRow("qtime") << QVariant(QTime(4, 5, 6));
+ QTest::newRow("qregularexpression") << QVariant(QRegularExpression(".*"));
+ QTest::newRow("qpointf") << QVariant(QPointF(42, 24));
+ QTest::newRow("qvariantlist") << QVariant(QVariantList() << 42.24 << 5 << "hello");
+ QTest::newRow("qvariantlist_point") << QVariant(QVariantList() << 42.24 << QPointF(42.24, 24.42) << QPointF(24.42, 42.24));
+ QVariantMap vm; vm.insert("test", 55); vm.insert("abc", 42.42);;
+ QTest::newRow("qvariantmap") << QVariant(vm);
+ vm.clear(); vm.insert("point1", QPointF(42.24, 24.42)); vm.insert("point2", QPointF(42.24, 24.42));
+ QTest::newRow("qvariantmap_point") << QVariant(vm);
+ QTest::newRow("qvariant") << QVariant(QVariant(42));
+ QTest::newRow("QList<int>") << QVariant::fromValue(QList<int>() << 1 << 2 << 3 << 4);
+ QTest::newRow("QVector<int>") << QVariant::fromValue(QVector<int>() << 1 << 2 << 3 << 4);
+ QTest::newRow("QList<QString>") << QVariant::fromValue(QVector<QString>() << "1" << "2" << "3" << "4");
+ QTest::newRow("QStringList") << QVariant::fromValue(QStringList() << "1" << "2" << "3" << "4");
+ QTest::newRow("QMap<QString, QString>") << QVariant::fromValue(QMap<QString, QString>{{ "1", "2" }, { "3", "4" }});
+ QTest::newRow("QHash<QString, QString>") << QVariant::fromValue(QHash<QString, QString>{{ "1", "2" }, { "3", "4" }});
+ QTest::newRow("QMap<QString, QPointF>") << QVariant::fromValue(QMap<QString, QPointF>{{ "1", { 42.24, 24.42 } }, { "3", { 24.42, 42.24 } }});
+ QTest::newRow("QHash<QString, QPointF>") << QVariant::fromValue(QHash<QString, QPointF>{{ "1", { 42.24, 24.42 } }, { "3", { 24.42, 42.24 } }});
+}
+
+void tst_QJSEngine::toScriptValue()
+{
+ QFETCH(QVariant, input);
+
+ QJSEngine engine;
+ QJSValue outputJS = engine.toScriptValue(input);
+ QVariant output = engine.fromScriptValue<QVariant>(outputJS);
+
+ QCOMPARE(input, output);
+}
+
+void tst_QJSEngine::toScriptValuenotroundtripped_data()
+{
+ QTest::addColumn<QVariant>("input");
+ QTest::addColumn<QVariant>("output");
+
+ QTest::newRow("QList<QObject*>") << QVariant::fromValue(QList<QObject*>() << this) << QVariant(QVariantList() << QVariant::fromValue(this));
+ QTest::newRow("QObjectList") << QVariant::fromValue(QObjectList() << this) << QVariant(QVariantList() << QVariant::fromValue(this));
+ QTest::newRow("QList<QPoint>") << QVariant::fromValue(QList<QPointF>() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42)) << QVariant(QVariantList() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42));
+ QTest::newRow("QVector<QPoint>") << QVariant::fromValue(QVector<QPointF>() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42)) << QVariant(QVariantList() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42));
+ QTest::newRow("VoidStar") << QVariant(int(QMetaType::VoidStar), nullptr) << QVariant(int(QMetaType::Nullptr), nullptr);
+ QTest::newRow("qregex") << QVariant(QRegExp(".*", Qt::CaseSensitive, QRegExp::RegExp2)) << QVariant(QRegularExpression(".*"));
+}
+
+// This is almost the same as toScriptValue, but the inputs don't roundtrip to
+// exactly the same value.
+void tst_QJSEngine::toScriptValuenotroundtripped()
+{
+ QFETCH(QVariant, input);
+ QFETCH(QVariant, output);
+
+ QJSEngine engine;
+ QJSValue outputJS = engine.toScriptValue(input);
+ QVariant actualOutput = engine.fromScriptValue<QVariant>(outputJS);
+
+ QCOMPARE(actualOutput, output);
+}
+
void tst_QJSEngine::newVariant()
{
QJSEngine eng;
{
QJSValue opaque = eng.toScriptValue(QVariant(QPoint(1, 2)));
QVERIFY(!opaque.isUndefined());
- QCOMPARE(opaque.isVariant(), true);
+ QCOMPARE(opaque.isVariant(), false);
QVERIFY(!opaque.isCallable());
QCOMPARE(opaque.isObject(), true);
QVERIFY(!opaque.prototype().isUndefined());
- QCOMPARE(opaque.prototype().isVariant(), true);
+ QCOMPARE(opaque.prototype().isVariant(), false);
QVERIFY(opaque.property("valueOf").callWithInstance(opaque).equals(opaque));
}
}
@@ -505,7 +595,7 @@ void tst_QJSEngine::newVariant_valueOfToString()
QJSValue value = object.property("valueOf").callWithInstance(object);
QVERIFY(value.isObject());
QVERIFY(value.strictlyEquals(object));
- QCOMPARE(object.toString(), QString::fromLatin1("QVariant(QPoint, QPoint(10,20))"));
+ QCOMPARE(object.toString(), QString::fromLatin1("QPoint(10, 20)"));
}
}
@@ -523,22 +613,27 @@ void tst_QJSEngine::newVariant_valueOfEnum()
void tst_QJSEngine::newRegExp()
{
QJSEngine eng;
- QJSValue rexp = eng.toScriptValue(QRegExp("foo"));
- QVERIFY(!rexp.isUndefined());
- QCOMPARE(rexp.isRegExp(), true);
- QCOMPARE(rexp.isObject(), true);
- QCOMPARE(rexp.isCallable(), false);
- // prototype should be RegExp.prototype
- QVERIFY(!rexp.prototype().isUndefined());
- QCOMPARE(rexp.prototype().isObject(), true);
- // Get [[Class]] internal property of RegExp Prototype Object.
- // See ECMA-262 Section 8.6.2, "Object Internal Properties and Methods".
- // See ECMA-262 Section 15.10.6, "Properties of the RegExp Prototype Object".
- QJSValue r = eng.evaluate("Object.prototype.toString.call(RegExp.prototype)");
- QCOMPARE(r.toString(), QString::fromLatin1("[object Object]"));
- QCOMPARE(rexp.prototype().strictlyEquals(eng.evaluate("RegExp.prototype")), true);
+ QJSValue rexps[] = {
+ eng.toScriptValue(QRegularExpression("foo")),
+ eng.toScriptValue(QRegExp("foo"))
+ };
+ for (const auto &rexp : rexps) {
+ QVERIFY(!rexp.isUndefined());
+ QCOMPARE(rexp.isRegExp(), true);
+ QCOMPARE(rexp.isObject(), true);
+ QCOMPARE(rexp.isCallable(), false);
+ // prototype should be RegExp.prototype
+ QVERIFY(!rexp.prototype().isUndefined());
+ QCOMPARE(rexp.prototype().isObject(), true);
+ // Get [[Class]] internal property of RegExp Prototype Object.
+ // See ECMA-262 Section 8.6.2, "Object Internal Properties and Methods".
+ // See ECMA-262 Section 15.10.6, "Properties of the RegExp Prototype Object".
+ QJSValue r = eng.evaluate("Object.prototype.toString.call(RegExp.prototype)");
+ QCOMPARE(r.toString(), QString::fromLatin1("[object Object]"));
+ QCOMPARE(rexp.prototype().strictlyEquals(eng.evaluate("RegExp.prototype")), true);
- QCOMPARE(qjsvalue_cast<QRegExp>(rexp).pattern(), QRegExp("foo").pattern());
+ QCOMPARE(qjsvalue_cast<QRegExp>(rexp).pattern(), QRegExp("foo").pattern());
+ }
}
void tst_QJSEngine::jsRegExp()
@@ -1490,15 +1585,15 @@ void tst_QJSEngine::valueConversion_QVariant()
QCOMPARE(val.toString(), str);
}
{
- QJSValue val = eng.toScriptValue(qVariantFromValue((QObject*)this));
+ QJSValue val = eng.toScriptValue(QVariant::fromValue((QObject*)this));
QVERIFY(!val.isVariant());
QVERIFY(val.isQObject());
QCOMPARE(val.toQObject(), (QObject*)this);
}
{
- QVariant var = qVariantFromValue(QPoint(123, 456));
+ QVariant var = QVariant::fromValue(QPoint(123, 456));
QJSValue val = eng.toScriptValue(var);
- QVERIFY(val.isVariant());
+ QVERIFY(!val.isVariant());
QCOMPARE(val.toVariant(), var);
}
@@ -1604,6 +1699,28 @@ void tst_QJSEngine::valueConversion_regExp()
}
}
+void tst_QJSEngine::valueConversion_RegularExpression()
+{
+ QJSEngine eng;
+ {
+ QRegularExpression in = QRegularExpression("foo");
+ QJSValue val = eng.toScriptValue(in);
+ QVERIFY(val.isRegExp());
+ QRegularExpression out = qjsvalue_cast<QRegularExpression>(val);
+ QCOMPARE(out.pattern(), in.pattern());
+ QCOMPARE(out.patternOptions(), in.patternOptions());
+ }
+ {
+ QRegularExpression in = QRegularExpression("foo",
+ QRegularExpression::CaseInsensitiveOption);
+ QJSValue val = eng.toScriptValue(in);
+ QVERIFY(val.isRegExp());
+ QCOMPARE(qjsvalue_cast<QRegularExpression>(val), in);
+ QRegularExpression out = qjsvalue_cast<QRegularExpression>(val);
+ QCOMPARE(out.patternOptions(), in.patternOptions());
+ }
+}
+
Q_DECLARE_METATYPE(QGradient)
Q_DECLARE_METATYPE(QGradient*)
Q_DECLARE_METATYPE(QLinearGradient)
@@ -2953,6 +3070,8 @@ void tst_QJSEngine::reentrancy_objectCreation()
QJSValue r2 = eng2.evaluate("new RegExp('foo', 'gim')");
QCOMPARE(qjsvalue_cast<QRegExp>(r1), qjsvalue_cast<QRegExp>(r2));
QCOMPARE(qjsvalue_cast<QRegExp>(r2), qjsvalue_cast<QRegExp>(r1));
+ QCOMPARE(qjsvalue_cast<QRegularExpression>(r1), qjsvalue_cast<QRegularExpression>(r2));
+ QCOMPARE(qjsvalue_cast<QRegularExpression>(r2), qjsvalue_cast<QRegularExpression>(r1));
}
{
QJSValue o1 = eng1.newQObject(temp);
@@ -3148,6 +3267,56 @@ void tst_QJSEngine::qRegExpInport()
}
}
+void tst_QJSEngine::qRegularExpressionImport_data()
+{
+ QTest::addColumn<QRegularExpression>("rx");
+ QTest::addColumn<QString>("string");
+ QTest::addColumn<QString>("matched");
+
+ QTest::newRow("normal") << QRegularExpression("(test|foo)") << "test _ foo _ test _ Foo";
+ QTest::newRow("normal2") << QRegularExpression("(Test|Foo)") << "test _ foo _ test _ Foo";
+ QTest::newRow("case insensitive") << QRegularExpression("(test|foo)", QRegularExpression::CaseInsensitiveOption) << "test _ foo _ test _ Foo";
+ QTest::newRow("case insensitive2") << QRegularExpression("(Test|Foo)", QRegularExpression::CaseInsensitiveOption) << "test _ foo _ test _ Foo";
+ QTest::newRow("b(a*)(b*)") << QRegularExpression("b(a*)(b*)", QRegularExpression::CaseInsensitiveOption) << "aaabbBbaAabaAaababaaabbaaab";
+ QTest::newRow("greedy") << QRegularExpression("a*(a*)", QRegularExpression::CaseInsensitiveOption) << "aaaabaaba";
+ QTest::newRow("wildcard") << QRegularExpression(".*\\.txt") << "file.txt";
+ QTest::newRow("wildcard 2") << QRegularExpression("a.b\\.txt") << "ab.txt abb.rtc acb.txt";
+ QTest::newRow("slash") << QRegularExpression("g/.*/s", QRegularExpression::CaseInsensitiveOption) << "string/string/string";
+ QTest::newRow("slash2") << QRegularExpression("g / .* / s", QRegularExpression::CaseInsensitiveOption) << "string / string / string";
+ QTest::newRow("fixed") << QRegularExpression("a\\*aa\\.a\\(ba\\)\\*a\\\\ba", QRegularExpression::CaseInsensitiveOption) << "aa*aa.a(ba)*a\\ba";
+ QTest::newRow("fixed insensitive") << QRegularExpression("A\\*A", QRegularExpression::CaseInsensitiveOption) << "a*A A*a A*A a*a";
+ QTest::newRow("fixed sensitive") << QRegularExpression("A\\*A") << "a*A A*a A*A a*a";
+ QTest::newRow("html") << QRegularExpression("<b>(.*)</b>") << "<b>bold</b><i>italic</i><b>bold</b>";
+ QTest::newRow("html minimal") << QRegularExpression("^<b>(.*)</b>$") << "<b>bold</b><i>italic</i><b>bold</b>";
+ QTest::newRow("aaa") << QRegularExpression("a{2,5}") << "aAaAaaaaaAa";
+ QTest::newRow("aaa minimal") << QRegularExpression("^a{2,5}$") << "aAaAaaaaaAa";
+ QTest::newRow("minimal") << QRegularExpression("^.*\\} [*8]$") << "}?} ?} *";
+ QTest::newRow(".? minimal") << QRegularExpression("^.?$") << ".?";
+ QTest::newRow(".+ minimal") << QRegularExpression("^.+$") << ".+";
+ QTest::newRow("[.?] minimal") << QRegularExpression("^[.?]$") << ".?";
+ QTest::newRow("[.+] minimal") << QRegularExpression("^[.+]$") << ".+";
+}
+
+void tst_QJSEngine::qRegularExpressionImport()
+{
+ QFETCH(QRegularExpression, rx);
+ QFETCH(QString, string);
+
+ QJSEngine eng;
+ QJSValue rexp;
+ rexp = eng.toScriptValue(rx);
+
+ QCOMPARE(rexp.isRegExp(), true);
+ QCOMPARE(rexp.isCallable(), false);
+
+ QJSValue func = eng.evaluate("(function(string, regexp) { return string.match(regexp); })");
+ QJSValue result = func.call(QJSValueList() << string << rexp);
+
+ const QRegularExpressionMatch match = rx.match(string);
+ for (int i = 0; i <= match.lastCapturedIndex(); i++)
+ QCOMPARE(result.property(i).toString(), match.captured(i));
+}
+
// QScriptValue::toDateTime() returns a local time, whereas JS dates
// are always stored as UTC. Qt Script must respect the current time
// zone, and correctly adjust for daylight saving time that may be in
@@ -4673,6 +4842,82 @@ void tst_QJSEngine::aggressiveGc()
qputenv("QV4_MM_AGGRESSIVE_GC", origAggressiveGc);
}
+void tst_QJSEngine::interrupt_data()
+{
+ QTest::addColumn<int>("jitThreshold");
+ QTest::addColumn<QString>("code");
+
+ const int big = (1 << 24);
+ for (int i = 0; i <= big; i += big) {
+ const char *mode = i ? "interpret" : "jit";
+ QTest::addRow("for with content / %s", mode) << i << "var a = 0; for (;;) { a += 2; }";
+ QTest::addRow("for empty / %s", mode) << i << "for (;;) {}";
+ QTest::addRow("for continue / %s", mode) << i << "for (;;) { continue; }";
+ QTest::addRow("while with content / %s", mode) << i << "var a = 0; while (true) { a += 2; }";
+ QTest::addRow("while empty / %s", mode) << i << "while (true) {}";
+ QTest::addRow("while continue / %s", mode) << i << "while (true) { continue; }";
+ QTest::addRow("do with content / %s", mode) << i << "var a = 0; do { a += 2; } while (true);";
+ QTest::addRow("do empty / %s", mode) << i << "do {} while (true);";
+ QTest::addRow("do continue / %s", mode) << i << "do { continue; } while (true);";
+ QTest::addRow("nested loops / %s", mode) << i << "while (true) { for (;;) {} }";
+ QTest::addRow("labeled continue / %s", mode) << i << "a: while (true) { for (;;) { continue a; } }";
+ QTest::addRow("labeled break / %s", mode) << i << "while (true) { a: for (;;) { break a; } }";
+ QTest::addRow("tail call / %s", mode) << i << "'use strict';\nfunction x() { return x(); }; x();";
+ }
+}
+
+class TemporaryJitThreshold
+{
+ Q_DISABLE_COPY_MOVE(TemporaryJitThreshold)
+public:
+ TemporaryJitThreshold(int threshold) {
+ m_wasSet = qEnvironmentVariableIsSet(m_envVar);
+ m_value = qgetenv(m_envVar);
+ qputenv(m_envVar, QByteArray::number(threshold));
+ }
+
+ ~TemporaryJitThreshold()
+ {
+ if (m_wasSet)
+ qputenv(m_envVar, m_value);
+ else
+ qunsetenv(m_envVar);
+ }
+
+private:
+ const char *m_envVar = "QV4_JIT_CALL_THRESHOLD";
+ bool m_wasSet = false;
+ QByteArray m_value;
+};
+
+void tst_QJSEngine::interrupt()
+{
+ QFETCH(int, jitThreshold);
+ QFETCH(QString, code);
+
+ TemporaryJitThreshold threshold(jitThreshold);
+ Q_UNUSED(threshold);
+
+ QJSEngine *engineInThread = nullptr;
+ QScopedPointer<QThread> worker(QThread::create([&engineInThread, &code, jitThreshold](){
+ QJSEngine jsEngine;
+ engineInThread = &jsEngine;
+ QJSValue result = jsEngine.evaluate(code);
+ QVERIFY(jsEngine.isInterrupted());
+ QVERIFY(result.isError());
+ QCOMPARE(result.toString(), QString::fromLatin1("Error: Interrupted"));
+ engineInThread = nullptr;
+ }));
+ worker->start();
+
+ QTRY_VERIFY(engineInThread);
+
+ engineInThread->setInterrupted(true);
+
+ QVERIFY(worker->wait());
+ QVERIFY(!engineInThread);
+}
+
QTEST_MAIN(tst_QJSEngine)
#include "tst_qjsengine.moc"
diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
index b58cd98d1e..a57cd3113c 100644
--- a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
+++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
@@ -404,8 +404,8 @@ void tst_QJSValue::toString()
// variant should use internal valueOf(), then fall back to QVariant::toString(),
// then fall back to "QVariant(typename)"
QJSValue variant = eng.toScriptValue(QPoint(10, 20));
- QVERIFY(variant.isVariant());
- QCOMPARE(variant.toString(), QString::fromLatin1("QVariant(QPoint, QPoint(10,20))"));
+ QVERIFY(!variant.isVariant());
+ QCOMPARE(variant.toString(), QString::fromLatin1("QPoint(10, 20)"));
variant = eng.toScriptValue(QUrl());
QVERIFY(variant.isVariant());
QVERIFY(variant.toString().isEmpty());
@@ -1027,6 +1027,20 @@ void tst_QJSValue::toVariant()
QJSValue rxObject = eng.toScriptValue(rx);
QVERIFY(rxObject.isRegExp());
QVariant var = rxObject.toVariant();
+
+ // We can't roundtrip a QRegExp this way, as toVariant() has no information on whether we
+ // want QRegExp or QRegularExpression. It will always create a QRegularExpression.
+ QCOMPARE(var.type(), QMetaType::QRegularExpression);
+ QRegularExpression result = var.toRegularExpression();
+ QCOMPARE(result.pattern(), rx.pattern());
+ QCOMPARE(result.patternOptions() & QRegularExpression::CaseInsensitiveOption, 0);
+ }
+
+ {
+ QRegularExpression rx = QRegularExpression("[0-9a-z]+");
+ QJSValue rxObject = eng.toScriptValue(rx);
+ QVERIFY(rxObject.isRegExp());
+ QVariant var = rxObject.toVariant();
QCOMPARE(var, QVariant(rx));
}
@@ -1107,7 +1121,7 @@ void tst_QJSValue::toQObject_nonQObject_data()
QTest::newRow("array") << engine->newArray();
QTest::newRow("date") << engine->evaluate("new Date(124)");
QTest::newRow("variant(12345)") << engine->toScriptValue(QVariant(12345));
- QTest::newRow("variant((QObject*)0)") << engine->toScriptValue(qVariantFromValue((QObject*)nullptr));
+ QTest::newRow("variant((QObject*)0)") << engine->toScriptValue(QVariant::fromValue((QObject*)nullptr));
QTest::newRow("newQObject(0)") << engine->newQObject(nullptr);
}
@@ -1194,6 +1208,32 @@ void tst_QJSValue::toRegExp()
QVERIFY(qjsvalue_cast<QRegExp>(eng.toScriptValue(QVariant())).isEmpty());
}
+void tst_QJSValue::toRegularExpression()
+{
+ QJSEngine eng;
+ {
+ QRegularExpression rx = qjsvalue_cast<QRegularExpression>(eng.evaluate("/foo/"));
+ QVERIFY(rx.isValid());
+ QCOMPARE(rx.pattern(), QString::fromLatin1("foo"));
+ QVERIFY(!(rx.patternOptions() & QRegularExpression::CaseInsensitiveOption));
+ }
+ {
+ QRegularExpression rx = qjsvalue_cast<QRegularExpression>(eng.evaluate("/bar/gi"));
+ QVERIFY(rx.isValid());
+ QCOMPARE(rx.pattern(), QString::fromLatin1("bar"));
+ QVERIFY(rx.patternOptions() & QRegularExpression::CaseInsensitiveOption);
+ }
+
+ QVERIFY(qjsvalue_cast<QRegularExpression>(eng.evaluate("[]")).pattern().isEmpty());
+ QVERIFY(qjsvalue_cast<QRegularExpression>(eng.evaluate("{}")).pattern().isEmpty());
+ QVERIFY(qjsvalue_cast<QRegularExpression>(eng.globalObject()).pattern().isEmpty());
+ QVERIFY(qjsvalue_cast<QRegularExpression>(QJSValue()).pattern().isEmpty());
+ QVERIFY(qjsvalue_cast<QRegularExpression>(QJSValue(123)).pattern().isEmpty());
+ QVERIFY(qjsvalue_cast<QRegularExpression>(QJSValue(false)).pattern().isEmpty());
+ QVERIFY(qjsvalue_cast<QRegularExpression>(eng.evaluate("null")).pattern().isEmpty());
+ QVERIFY(qjsvalue_cast<QRegularExpression>(eng.toScriptValue(QVariant())).pattern().isEmpty());
+}
+
void tst_QJSValue::isArray_data()
{
newEngine();
@@ -2248,8 +2288,8 @@ void tst_QJSValue::strictlyEquals()
{
QJSValue var1 = eng.toScriptValue(QVariant(QStringList() << "a"));
QJSValue var2 = eng.toScriptValue(QVariant(QStringList() << "a"));
- QVERIFY(var1.isArray());
- QVERIFY(var2.isArray());
+ QVERIFY(!var1.isArray());
+ QVERIFY(!var2.isArray());
QVERIFY(!var1.strictlyEquals(var2));
}
{
@@ -2287,7 +2327,7 @@ void tst_QJSValue::castToPointer()
QBrush *bp = qjsvalue_cast<QBrush*>(v);
QVERIFY(!bp);
- QJSValue v2 = eng.toScriptValue(qVariantFromValue(cp));
+ QJSValue v2 = eng.toScriptValue(QVariant::fromValue(cp));
QCOMPARE(qjsvalue_cast<QColor*>(v2), cp);
}
}
diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.h b/tests/auto/qml/qjsvalue/tst_qjsvalue.h
index b8b9f4403c..9532b1f10e 100644
--- a/tests/auto/qml/qjsvalue/tst_qjsvalue.h
+++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.h
@@ -76,6 +76,7 @@ private slots:
void toQObject();
void toDateTime();
void toRegExp();
+ void toRegularExpression();
void isArray_data();
void isArray();
void isDate();
diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro
index 5448088ee5..db9bb52010 100644
--- a/tests/auto/qml/qml.pro
+++ b/tests/auto/qml/qml.pro
@@ -70,6 +70,7 @@ PRIVATETESTS += \
qqmltranslation \
qqmlimport \
qqmlobjectmodel \
+ qqmltablemodel \
qv4assembler \
qv4mm \
qv4identifiertable \
@@ -104,7 +105,3 @@ qtConfig(private_tests): \
qtNomakeTools( \
qmlplugindump \
)
-
-QtConfig(qml_tracing) {
- PRIVATETESTS += v4traced
-}
diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
index 70a5a73e0f..e86b7bf5fe 100644
--- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
+++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
@@ -30,7 +30,6 @@
#include <private/qv4compileddata_p.h>
#include <private/qv4compiler_p.h>
-#include <private/qv8engine_p.h>
#include <private/qv4engine_p.h>
#include <private/qv4codegen_p.h>
#include <private/qqmlcomponent_p.h>
diff --git a/tests/auto/qml/qqmlchangeset/qqmlchangeset.pro b/tests/auto/qml/qqmlchangeset/qqmlchangeset.pro
index 9f854f1fa2..cdac5c0ff9 100644
--- a/tests/auto/qml/qqmlchangeset/qqmlchangeset.pro
+++ b/tests/auto/qml/qqmlchangeset/qqmlchangeset.pro
@@ -4,4 +4,4 @@ macx:CONFIG -= app_bundle
SOURCES += tst_qqmlchangeset.cpp
-QT += core-private gui-private qml-private testlib
+QT += core-private gui-private qml-private testlib qmlmodels-private
diff --git a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
index 7c7c7d3bd0..98ae86d248 100644
--- a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
+++ b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
@@ -35,7 +35,6 @@
#include <QtQuick>
#include <QtQuick/private/qquickrectangle_p.h>
#include <QtQuick/private/qquickmousearea_p.h>
-#include <private/qv8engine_p.h>
#include <private/qqmlcontext_p.h>
#include <private/qv4qmlcontext_p.h>
#include <private/qv4scopedvalue_p.h>
diff --git a/tests/auto/qml/qqmlecmascript/data/regularExpression.2.qml b/tests/auto/qml/qqmlecmascript/data/regularExpression.2.qml
new file mode 100644
index 0000000000..b22f8ab71e
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/regularExpression.2.qml
@@ -0,0 +1,7 @@
+import Qt.test 1.0
+
+MyQmlObject{
+ id: obj
+ objectName: "obj"
+ regularExpression: "[a-zA-z]"
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/regularExpression.qml b/tests/auto/qml/qqmlecmascript/data/regularExpression.qml
new file mode 100644
index 0000000000..6f31ffd305
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/regularExpression.qml
@@ -0,0 +1,7 @@
+import Qt.test 1.0
+
+MyQmlObject{
+ id: obj
+ objectName: "obj"
+ regularExpression: /[a-zA-z]/
+}
diff --git a/tests/auto/qml/qqmlecmascript/testtypes.h b/tests/auto/qml/qqmlecmascript/testtypes.h
index 4547a74470..730dc7cab8 100644
--- a/tests/auto/qml/qqmlecmascript/testtypes.h
+++ b/tests/auto/qml/qqmlecmascript/testtypes.h
@@ -33,6 +33,7 @@
#include <QtQml/qqmlexpression.h>
#include <QtCore/qpoint.h>
#include <QtCore/qsize.h>
+#include <QtCore/qregularexpression.h>
#include <QtQml/qqmllist.h>
#include <QtCore/qrect.h>
#include <QtGui/qmatrix.h>
@@ -49,7 +50,6 @@
#include <QtQml/qqmlcomponent.h>
#include <private/qqmlengine_p.h>
-#include <private/qv8engine_p.h>
#include <private/qv4qobjectwrapper_p.h>
class MyQmlAttachedObject : public QObject
@@ -101,6 +101,7 @@ class MyQmlObject : public QObject
Q_PROPERTY(QQmlListProperty<QObject> objectListProperty READ objectListProperty CONSTANT)
Q_PROPERTY(int resettableProperty READ resettableProperty WRITE setResettableProperty RESET resetProperty)
Q_PROPERTY(QRegExp regExp READ regExp WRITE setRegExp)
+ Q_PROPERTY(QRegularExpression regularExpression READ regularExpression WRITE setRegularExpression)
Q_PROPERTY(int nonscriptable READ nonscriptable WRITE setNonscriptable SCRIPTABLE false)
Q_PROPERTY(int intProperty READ intProperty WRITE setIntProperty NOTIFY intChanged)
Q_PROPERTY(QJSValue qjsvalue READ qjsvalue WRITE setQJSValue NOTIFY qjsvalueChanged)
@@ -170,6 +171,12 @@ public:
QRegExp regExp() { return m_regExp; }
void setRegExp(const QRegExp &regExp) { m_regExp = regExp; }
+ QRegularExpression regularExpression() { return m_regularExpression; }
+ void setRegularExpression(const QRegularExpression &regularExpression)
+ {
+ m_regularExpression = regularExpression;
+ }
+
int console() const { return 11; }
int nonscriptable() const { return 0; }
@@ -270,6 +277,7 @@ private:
int m_value;
int m_resetProperty;
QRegExp m_regExp;
+ QRegularExpression m_regularExpression;
QVariant m_variant;
QJSValue m_qjsvalue;
int m_intProperty;
@@ -788,11 +796,11 @@ public:
Q_INVOKABLE void method_real(qreal a) { invoke(10); m_actuals << a; }
Q_INVOKABLE void method_QString(QString a) { invoke(11); m_actuals << a; }
Q_INVOKABLE void method_QPointF(QPointF a) { invoke(12); m_actuals << a; }
- Q_INVOKABLE void method_QObject(QObject *a) { invoke(13); m_actuals << qVariantFromValue(a); }
- Q_INVOKABLE void method_QScriptValue(QJSValue a) { invoke(14); m_actuals << qVariantFromValue(a); }
- Q_INVOKABLE void method_intQScriptValue(int a, QJSValue b) { invoke(15); m_actuals << a << qVariantFromValue(b); }
+ Q_INVOKABLE void method_QObject(QObject *a) { invoke(13); m_actuals << QVariant::fromValue(a); }
+ Q_INVOKABLE void method_QScriptValue(QJSValue a) { invoke(14); m_actuals << QVariant::fromValue(a); }
+ Q_INVOKABLE void method_intQScriptValue(int a, QJSValue b) { invoke(15); m_actuals << a << QVariant::fromValue(b); }
Q_INVOKABLE void method_QByteArray(QByteArray value) { invoke(29); m_actuals << value; }
- Q_INVOKABLE QJSValue method_intQJSValue(int a, QJSValue b) { invoke(30); m_actuals << a << qVariantFromValue(b); return b.call(); }
+ Q_INVOKABLE QJSValue method_intQJSValue(int a, QJSValue b) { invoke(30); m_actuals << a << QVariant::fromValue(b); return b.call(); }
Q_INVOKABLE QJSValue method_intQJSValue(int a, int b) { m_actuals << a << b; return QJSValue();} // Should never be called.
Q_INVOKABLE void method_overload(int a) { invoke(16); m_actuals << a; }
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 09b271c5f1..72a524ece5 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -2423,6 +2423,13 @@ void tst_qqmlecmascript::regExpBug()
delete object;
}
+ {
+ QQmlComponent component(&engine, testFileUrl("regularExpression.qml"));
+ QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create()));
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->regularExpression().pattern(), QLatin1String("[a-zA-z]"));
+ }
+
//QTBUG-23068
{
QString err = QString(QLatin1String("%1:6 Invalid property assignment: regular expression expected; use /pattern/ syntax\n")).arg(testFileUrl("regExp.2.qml").toString());
@@ -2432,6 +2439,18 @@ void tst_qqmlecmascript::regExpBug()
QVERIFY(!object);
QCOMPARE(component.errorString(), err);
}
+
+ {
+ const QString err = QString::fromLatin1("%1:6 Invalid property assignment: "
+ "regular expression expected; "
+ "use /pattern/ syntax\n")
+ .arg(testFileUrl("regularExpression.2.qml").toString());
+ QQmlComponent component(&engine, testFileUrl("regularExpression.2.qml"));
+ QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
+ MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
+ QVERIFY(!object);
+ QCOMPARE(component.errorString(), err);
+ }
}
static inline bool evaluate_error(QV4::ExecutionEngine *v4, const QV4::Value &o, const char *source)
@@ -2486,7 +2505,7 @@ static inline bool evaluate_value(QV4::ExecutionEngine *v4, const QV4::Value &o,
scope.engine->catchException();
return false;
}
- return QV4::Runtime::method_strictEqual(value, result);
+ return QV4::Runtime::StrictEqual::call(value, result);
}
static inline QV4::ReturnedValue evaluate(QV4::ExecutionEngine *v4, const QV4::Value &o,
@@ -2816,35 +2835,35 @@ void tst_qqmlecmascript::callQtInvokables()
QCOMPARE(o->error(), false);
QCOMPARE(o->invoked(), 13);
QCOMPARE(o->actuals().count(), 1);
- QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)nullptr));
+ QCOMPARE(o->actuals().at(0), QVariant::fromValue((QObject *)nullptr));
o->reset();
QVERIFY(EVALUATE_VALUE("object.method_QObject(\"Hello world\")", QV4::Primitive::undefinedValue()));
QCOMPARE(o->error(), false);
QCOMPARE(o->invoked(), 13);
QCOMPARE(o->actuals().count(), 1);
- QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)nullptr));
+ QCOMPARE(o->actuals().at(0), QVariant::fromValue((QObject *)nullptr));
o->reset();
QVERIFY(EVALUATE_VALUE("object.method_QObject(null)", QV4::Primitive::undefinedValue()));
QCOMPARE(o->error(), false);
QCOMPARE(o->invoked(), 13);
QCOMPARE(o->actuals().count(), 1);
- QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)nullptr));
+ QCOMPARE(o->actuals().at(0), QVariant::fromValue((QObject *)nullptr));
o->reset();
QVERIFY(EVALUATE_VALUE("object.method_QObject(undefined)", QV4::Primitive::undefinedValue()));
QCOMPARE(o->error(), false);
QCOMPARE(o->invoked(), 13);
QCOMPARE(o->actuals().count(), 1);
- QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)nullptr));
+ QCOMPARE(o->actuals().at(0), QVariant::fromValue((QObject *)nullptr));
o->reset();
QVERIFY(EVALUATE_VALUE("object.method_QObject(object)", QV4::Primitive::undefinedValue()));
QCOMPARE(o->error(), false);
QCOMPARE(o->invoked(), 13);
QCOMPARE(o->actuals().count(), 1);
- QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)o));
+ QCOMPARE(o->actuals().at(0), QVariant::fromValue((QObject *)o));
o->reset();
QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(null)", QV4::Primitive::undefinedValue()));
@@ -3120,7 +3139,7 @@ void tst_qqmlecmascript::resolveClashingProperties()
QString key = name->toQStringNoThrow();
if (key == QLatin1String("clashes")) {
value = v;
- QV4::ScopedValue typeString(scope, QV4::Runtime::method_typeofValue(engine, value));
+ QV4::ScopedValue typeString(scope, QV4::Runtime::TypeofValue::call(engine, value));
QString type = typeString->toQStringNoThrow();
if (type == QLatin1String("boolean")) {
QVERIFY(!seenProperty);
diff --git a/tests/auto/qml/qqmlinstantiator/qqmlinstantiator.pro b/tests/auto/qml/qqmlinstantiator/qqmlinstantiator.pro
index 542ec44736..e8ed8e91b1 100644
--- a/tests/auto/qml/qqmlinstantiator/qqmlinstantiator.pro
+++ b/tests/auto/qml/qqmlinstantiator/qqmlinstantiator.pro
@@ -9,4 +9,4 @@ include (../../shared/util.pri)
TESTDATA = data/*
-QT += core-private gui-private qml-private testlib
+QT += core-private gui-private qml-private testlib qmlmodels-private
diff --git a/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp b/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp
index a66f13e6bb..9c5e09c77c 100644
--- a/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp
+++ b/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp
@@ -31,7 +31,7 @@
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
-#include <QtQml/private/qqmlinstantiator_p.h>
+#include <QtQmlModels/private/qqmlinstantiator_p.h>
#include <QtQml/qqmlcontext.h>
#include <QtQml/qqmlincubator.h>
#include "../../shared/util.h"
diff --git a/tests/auto/qml/qqmllanguage/data/bareQmlImport.errors.txt b/tests/auto/qml/qqmllanguage/data/bareQmlImport.errors.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/bareQmlImport.errors.txt
diff --git a/tests/auto/qml/qqmllanguage/data/bareQmlImport.qml b/tests/auto/qml/qqmllanguage/data/bareQmlImport.qml
new file mode 100644
index 0000000000..3567cdb5a3
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/bareQmlImport.qml
@@ -0,0 +1,6 @@
+import QML 1.0
+QtObject {
+ property Component component: Component {
+ QtObject {}
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index 63fc55ad0f..08dab99e0f 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -620,6 +620,8 @@ void tst_qqmllanguage::errors_data()
QTest::newRow("fuzzed.1") << "fuzzed.1.qml" << "fuzzed.1.errors.txt" << false;
QTest::newRow("fuzzed.2") << "fuzzed.2.qml" << "fuzzed.2.errors.txt" << false;
+
+ QTest::newRow("bareQmlImport") << "bareQmlImport.qml" << "bareQmlImport.errors.txt" << false;
}
@@ -1458,8 +1460,8 @@ void tst_qqmllanguage::dynamicObjectProperties()
QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
- QCOMPARE(object->property("objectProperty"), qVariantFromValue((QObject*)nullptr));
- QVERIFY(object->property("objectProperty2") != qVariantFromValue((QObject*)nullptr));
+ QCOMPARE(object->property("objectProperty"), QVariant::fromValue((QObject*)nullptr));
+ QVERIFY(object->property("objectProperty2") != QVariant::fromValue((QObject*)nullptr));
}
{
QQmlComponent component(&engine, testFileUrl("dynamicObjectProperties.2.qml"));
@@ -1467,7 +1469,7 @@ void tst_qqmllanguage::dynamicObjectProperties()
QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
- QVERIFY(object->property("objectProperty") != qVariantFromValue((QObject*)nullptr));
+ QVERIFY(object->property("objectProperty") != QVariant::fromValue((QObject*)nullptr));
}
}
@@ -1736,7 +1738,7 @@ void tst_qqmllanguage::aliasProperties()
// Write through alias
MyQmlObject *v2 = new MyQmlObject();
v2->setParent(object.data());
- object->setProperty("aliasObject", qVariantFromValue(v2));
+ object->setProperty("aliasObject", QVariant::fromValue(v2));
MyQmlObject *v3 =
qvariant_cast<MyQmlObject *>(object->property("aliasObject"));
QVERIFY(v3 != nullptr);
@@ -3812,7 +3814,7 @@ void tst_qqmllanguage::scopedEnumsWithNameClash()
{
auto typeId = qmlRegisterUncreatableType<ScopedEnumsWithNameClash>("ScopedEnumsWithNameClashTest", 1, 0, "ScopedEnum", "Dummy reason");
auto registryGuard = qScopeGuard([typeId]() {
- qmlUnregisterType(typeId);
+ QQmlMetaType::unregisterType(typeId);
});
QQmlEngine engine;
@@ -3831,7 +3833,7 @@ void tst_qqmllanguage::scopedEnumsWithResolvedNameClash()
{
auto typeId = qmlRegisterUncreatableType<ScopedEnumsWithResolvedNameClash>("ScopedEnumsWithResolvedNameClashTest", 1, 0, "ScopedEnum", "Dummy reason");
auto registryGuard = qScopeGuard([typeId]() {
- qmlUnregisterType(typeId);
+ QQmlMetaType::unregisterType(typeId);
});
QQmlEngine engine;
diff --git a/tests/auto/qml/qqmllistcompositor/qqmllistcompositor.pro b/tests/auto/qml/qqmllistcompositor/qqmllistcompositor.pro
index 4ada590a2a..62ad85547e 100644
--- a/tests/auto/qml/qqmllistcompositor/qqmllistcompositor.pro
+++ b/tests/auto/qml/qqmllistcompositor/qqmllistcompositor.pro
@@ -4,4 +4,4 @@ macx:CONFIG -= app_bundle
SOURCES += tst_qqmllistcompositor.cpp
-QT += core-private gui-private qml-private quick-private testlib
+QT += core-private gui-private qml-private quick-private testlib qmlmodels-private
diff --git a/tests/auto/qml/qqmllistmodel/qqmllistmodel.pro b/tests/auto/qml/qqmllistmodel/qqmllistmodel.pro
index 8e3aed0baf..4d44d6b22b 100644
--- a/tests/auto/qml/qqmllistmodel/qqmllistmodel.pro
+++ b/tests/auto/qml/qqmllistmodel/qqmllistmodel.pro
@@ -8,4 +8,4 @@ include (../../shared/util.pri)
TESTDATA = data/*
-QT += core-private gui-private qml-private quick-private testlib
+QT += core-private gui-private qml-private quick-private testlib qmlmodels-private
diff --git a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
index 771f3e5c4e..77ab0ecbc0 100644
--- a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
+++ b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
@@ -30,7 +30,7 @@
#include <QtQuick/private/qquicktext_p.h>
#include <QtQuick/private/qquickanimation_p.h>
#include <QtQml/private/qqmlengine_p.h>
-#include <QtQml/private/qqmllistmodel_p.h>
+#include <QtQmlModels/private/qqmllistmodel_p.h>
#include <QtQml/private/qqmlexpression_p.h>
#include <QQmlComponent>
@@ -760,11 +760,11 @@ void tst_qqmllistmodel::set()
RUNEXPR("model.set(0, {test:true})");
QCOMPARE(RUNEXPR("model.get(0).test").toBool(), true); // triggers creation of model cache
- QCOMPARE(model.data(0, 0), qVariantFromValue(true));
+ QCOMPARE(model.data(0, 0), QVariant::fromValue(true));
RUNEXPR("model.set(0, {test:false})");
QCOMPARE(RUNEXPR("model.get(0).test").toBool(), false); // tests model cache is updated
- QCOMPARE(model.data(0, 0), qVariantFromValue(false));
+ QCOMPARE(model.data(0, 0), QVariant::fromValue(false));
QString warning = QString::fromLatin1("<Unknown File>: Can't create role for unsupported data type");
if (isValidErrorMessage(warning, dynamicRoles))
diff --git a/tests/auto/qml/qqmllistmodelworkerscript/qqmllistmodelworkerscript.pro b/tests/auto/qml/qqmllistmodelworkerscript/qqmllistmodelworkerscript.pro
index 9e1cea9867..de58c0c075 100644
--- a/tests/auto/qml/qqmllistmodelworkerscript/qqmllistmodelworkerscript.pro
+++ b/tests/auto/qml/qqmllistmodelworkerscript/qqmllistmodelworkerscript.pro
@@ -8,4 +8,4 @@ include (../../shared/util.pri)
TESTDATA = data/*
-QT += core-private gui-private qml-private quick-private testlib
+QT += core-private gui-private qml-private quick-private testlib qmlmodels-private
diff --git a/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp b/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp
index 21b0508e4d..b5e8800d0e 100644
--- a/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp
+++ b/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp
@@ -29,7 +29,7 @@
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquicktext_p.h>
#include <QtQml/private/qqmlengine_p.h>
-#include <QtQml/private/qqmllistmodel_p.h>
+#include <QtQmlModels/private/qqmllistmodel_p.h>
#include <QtQml/private/qqmlexpression_p.h>
#include <QQmlComponent>
@@ -162,7 +162,7 @@ QQuickItem *tst_qqmllistmodelworkerscript::createWorkerTest(QQmlEngine *eng, QQm
QQuickItem *item = qobject_cast<QQuickItem*>(component->create());
QQmlEngine::setContextForObject(model, eng->rootContext());
if (item)
- item->setProperty("model", qVariantFromValue(model));
+ item->setProperty("model", QVariant::fromValue(model));
return item;
}
diff --git a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp
index a7805922a5..76185a97e0 100644
--- a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp
+++ b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp
@@ -36,7 +36,6 @@
#include <private/qqmlmetatype_p.h>
#include <private/qqmlpropertyvalueinterceptor_p.h>
#include <private/qqmlengine_p.h>
-#include <private/qhashedstring_p.h>
#include "../../shared/util.h"
class tst_qqmlmetatype : public QQmlDataTest
@@ -401,7 +400,7 @@ void tst_qqmlmetatype::unregisterCustomType()
QCOMPARE(enumVal.type(), QVariant::Int);
QCOMPARE(enumVal.toInt(), 1);
}
- qmlUnregisterType(controllerId);
+ QQmlMetaType::unregisterType(controllerId);
{
QQmlEngine engine;
QQmlType type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0);
@@ -424,7 +423,7 @@ void tst_qqmlmetatype::unregisterCustomType()
QCOMPARE(enumVal.type(), QVariant::Int);
QCOMPARE(enumVal.toInt(), 111);
}
- qmlUnregisterType(controllerId);
+ QQmlMetaType::unregisterType(controllerId);
{
QQmlEngine engine;
QQmlType type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0);
@@ -493,7 +492,7 @@ void tst_qqmlmetatype::unregisterCustomSingletonType()
QCOMPARE(stringVal.type(), QVariant::String);
QCOMPARE(stringVal.toString(), QStringLiteral("StaticProvider #1"));
}
- qmlUnregisterType(staticProviderId);
+ QQmlMetaType::unregisterType(staticProviderId);
{
QQmlEngine engine;
staticProviderId = qmlRegisterSingletonType<StaticProvider2>("mytypes", 1, 0, "StaticProvider", createStaticProvider2);
@@ -509,7 +508,7 @@ void tst_qqmlmetatype::unregisterCustomSingletonType()
QCOMPARE(stringVal.type(), QVariant::String);
QCOMPARE(stringVal.toString(), QStringLiteral("StaticProvider #2"));
}
- qmlUnregisterType(staticProviderId);
+ QQmlMetaType::unregisterType(staticProviderId);
{
QQmlEngine engine;
staticProviderId = qmlRegisterSingletonType<StaticProvider1>("mytypes", 1, 0, "StaticProvider", createStaticProvider1);
@@ -535,7 +534,7 @@ void tst_qqmlmetatype::normalizeUrls()
QVERIFY(QQmlMetaType::qmlType(url, /*includeNonFileImports=*/true).isValid());
QUrl normalizedURL("qrc:/tstqqmlmetatype/data/CompositeType.qml");
QVERIFY(QQmlMetaType::qmlType(normalizedURL, /*includeNonFileImports=*/true).isValid());
- qmlUnregisterType(registrationId);
+ QQmlMetaType::unregisterType(registrationId);
QVERIFY(!QQmlMetaType::qmlType(url, /*includeNonFileImports=*/true).isValid());
}
diff --git a/tests/auto/qml/qqmlobjectmodel/qqmlobjectmodel.pro b/tests/auto/qml/qqmlobjectmodel/qqmlobjectmodel.pro
index 88bb630e29..5746ff754a 100644
--- a/tests/auto/qml/qqmlobjectmodel/qqmlobjectmodel.pro
+++ b/tests/auto/qml/qqmlobjectmodel/qqmlobjectmodel.pro
@@ -5,4 +5,4 @@ osx:CONFIG -= app_bundle
SOURCES += tst_qqmlobjectmodel.cpp
QT += qml testlib
-QT += core-private qml-private
+QT += core-private qml-private qmlmodels-private
diff --git a/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp b/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp
index fb63d811a8..6691fa43a0 100644
--- a/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp
+++ b/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp
@@ -25,8 +25,8 @@
** $QT_END_LICENSE$
**
****************************************************************************/
-#include <QtQml/private/qqmlobjectmodel_p.h>
-#include <QtQml/private/qqmlchangeset_p.h>
+#include <QtQmlModels/private/qqmlobjectmodel_p.h>
+#include <QtQmlModels/private/qqmlchangeset_p.h>
#include <QtTest/qsignalspy.h>
#include <QtTest/qtest.h>
diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
index 27e06c6f67..04c61ec11b 100644
--- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
+++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
@@ -1624,7 +1624,7 @@ void tst_qqmlproperty::writeObjectToList()
MyQmlObject *object = new MyQmlObject;
QQmlProperty prop(container, "children");
- prop.write(qVariantFromValue(object));
+ prop.write(QVariant::fromValue(object));
QCOMPARE(list.count(), 1);
QCOMPARE(list.at(0), qobject_cast<QObject*>(object));
}
@@ -1641,13 +1641,13 @@ void tst_qqmlproperty::writeListToList()
QList<QObject*> objList;
objList << new MyQmlObject() << new MyQmlObject() << new MyQmlObject() << new MyQmlObject();
QQmlProperty prop(container, "children");
- prop.write(qVariantFromValue(objList));
+ prop.write(QVariant::fromValue(objList));
QCOMPARE(list.count(), 4);
//XXX need to try this with read/write prop (for read-only it correctly doesn't write)
/*QList<MyQmlObject*> typedObjList;
typedObjList << new MyQmlObject();
- prop.write(qVariantFromValue(&typedObjList));
+ prop.write(QVariant::fromValue(&typedObjList));
QCOMPARE(container->children()->size(), 1);*/
}
diff --git a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp
index 02b5302a45..9a1e4667dd 100644
--- a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp
+++ b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp
@@ -31,7 +31,6 @@
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcontext.h>
#include <QtQml/qqmlcomponent.h>
-#include <private/qv8engine_p.h>
#include <private/qmetaobjectbuilder_p.h>
#include <QCryptographicHash>
#include "../../shared/util.h"
diff --git a/tests/auto/qml/qqmltablemodel/data/TestModel.qml b/tests/auto/qml/qqmltablemodel/data/TestModel.qml
new file mode 100644
index 0000000000..00e1fa65a7
--- /dev/null
+++ b/tests/auto/qml/qqmltablemodel/data/TestModel.qml
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import Qt.labs.qmlmodels 1.0
+
+import "TestUtils.js" as TestUtils
+
+TableModel {
+ id: testModel
+ objectName: "testModel"
+
+ TableModelColumn { display: "name" }
+ TableModelColumn { display: "age" }
+
+ rows: [
+ {
+ name: "John",
+ age: 22
+ },
+ {
+ name: "Oliver",
+ age: 33
+ }
+ ]
+}
diff --git a/tests/auto/qml/qqmltablemodel/data/TestUtils.js b/tests/auto/qml/qqmltablemodel/data/TestUtils.js
new file mode 100644
index 0000000000..0b92a377bb
--- /dev/null
+++ b/tests/auto/qml/qqmltablemodel/data/TestUtils.js
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+function testModelRoleDataProvider(index, role, cellData) {
+ switch (role) {
+ case "display":
+ switch (index.column) {
+ case 0:
+ return cellData.name
+ case 1:
+ return cellData.age
+ }
+ break
+ case "name":
+ return cellData.name
+ case "age":
+ return cellData.age
+ }
+ return cellData
+}
diff --git a/tests/auto/qml/qqmltablemodel/data/common.qml b/tests/auto/qml/qqmltablemodel/data/common.qml
new file mode 100644
index 0000000000..2f8b0c072b
--- /dev/null
+++ b/tests/auto/qml/qqmltablemodel/data/common.qml
@@ -0,0 +1,149 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.13
+import Qt.labs.qmlmodels 1.0
+
+Item {
+ id: root
+ width: 200
+ height: 200
+
+ property alias testModel: testModel
+ property alias tableView: tableView
+
+ function appendRow(personName, personAge) {
+ testModel.appendRow({
+ name: personName,
+ age: personAge
+ })
+ }
+
+ function appendRowExtraData() {
+ testModel.appendRow({
+ name: "Foo",
+ age: 99,
+ nonExistentRole: 123
+ })
+ }
+
+ function appendRowInvalid1() {
+ testModel.appendRow(123)
+ }
+
+ function appendRowInvalid2() {
+ testModel.appendRow({
+ name: "Foo",
+ age: []
+ })
+ }
+
+ function appendRowInvalid3() {
+ testModel.appendRow([
+ { name: "Bar" },
+ { age: "111" }
+ ])
+ }
+
+ function insertRow(personName, personAge, rowIndex) {
+ testModel.insertRow(rowIndex, {
+ name: personName,
+ age: personAge
+ })
+ }
+
+ function insertRowExtraData() {
+ testModel.insertRow(0, {
+ name: "Foo",
+ age: 99,
+ nonExistentRole: 123
+ })
+ }
+
+ function insertRowInvalid1() {
+ testModel.insertRow(0, 123)
+ }
+
+ function insertRowInvalid2() {
+ testModel.insertRow(0, {
+ name: "Foo",
+ age: []
+ })
+ }
+
+ function insertRowInvalid3() {
+ testModel.insertRow(0, [
+ { name: "Bar" },
+ { age: "111" }
+ ])
+ }
+
+ function setRow(rowIndex, personName, personAge) {
+ testModel.setRow(rowIndex, {
+ name: personName,
+ age: personAge
+ })
+ }
+
+ function setRowExtraData() {
+ testModel.setRow(0, {
+ name: "Foo",
+ age: 99,
+ nonExistentRole: 123
+ })
+ }
+
+ function setRowInvalid1() {
+ testModel.setRow(0, 123)
+ }
+
+ function setRowInvalid2() {
+ testModel.setRow(0, {
+ name: "Foo",
+ age: []
+ })
+ }
+
+ function setRowInvalid3() {
+ testModel.setRow(0, [
+ { name: "Bar" },
+ { age: "111" }
+ ])
+ }
+
+ TableView {
+ id: tableView
+ anchors.fill: parent
+ model: TestModel {
+ id: testModel
+ }
+ delegate: Text {
+ text: model.display
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmltablemodel/data/complex.qml b/tests/auto/qml/qqmltablemodel/data/complex.qml
new file mode 100644
index 0000000000..dbf53bac7e
--- /dev/null
+++ b/tests/auto/qml/qqmltablemodel/data/complex.qml
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.13
+import Qt.labs.qmlmodels 1.0
+
+TableView {
+ width: 100
+ height: 100
+ delegate: Item {
+ implicitWidth: 50
+ implicitHeight: 50
+
+ Text {
+ text: model.display
+ anchors.centerIn: parent
+ }
+ }
+ model: TableModel {
+ id: testModel
+ objectName: "testModel"
+
+ TableModelColumn {
+ display: function(modelIndex) { return testModel.rows[modelIndex.row][0].name }
+ setDisplay: function(modelIndex, cellData) { testModel.rows[modelIndex.row][0].name = cellData }
+ }
+ TableModelColumn {
+ display: function(modelIndex) { return testModel.rows[modelIndex.row][1].age }
+ setDisplay: function(modelIndex, cellData) { testModel.rows[modelIndex.row][1].age = cellData }
+ }
+
+ rows: [
+ [
+ { name: "John" },
+ { age: 22 }
+ ],
+ [
+ { name: "Oliver" },
+ { age: 33 }
+ ]
+ ]
+ }
+}
diff --git a/tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml b/tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml
new file mode 100644
index 0000000000..d3f726bfa1
--- /dev/null
+++ b/tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import Qt.labs.qmlmodels 1.0
+
+TableView {
+ width: 200; height: 200
+ model: TestModel {
+ id: testModel
+
+ // This is silly: in real life, store the birthdate instead of the age,
+ // and let the delegate calculate the age, so it won't need updating
+ function happyBirthday(dude) {
+ var row = -1;
+ for (var r = 0; row < 0 && r < testModel.rowCount; ++r)
+ if (testModel.data(testModel.index(r, 0), "display") === dude)
+ row = r;
+ var index = testModel.index(row, 1)
+ testModel.setData(index, "display", testModel.data(index, "display") + 1)
+ }
+ }
+ delegate: Text {
+ id: textItem
+ text: model.display
+ TapHandler {
+ onTapped: testModel.happyBirthday(testModel.data(testModel.index(row, 0), "display"))
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmltablemodel/data/empty.qml b/tests/auto/qml/qqmltablemodel/data/empty.qml
new file mode 100644
index 0000000000..f5afbd1d27
--- /dev/null
+++ b/tests/auto/qml/qqmltablemodel/data/empty.qml
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import Qt.labs.qmlmodels 1.0
+
+Item {
+ id: root
+ width: 200
+ height: 200
+
+ property alias testModel: testModel
+ property alias tableView: tableView
+
+ function setRows() {
+ testModel.rows = [
+ {
+ name: "John",
+ age: 22
+ },
+ {
+ name: "Oliver",
+ age: 33
+ }
+ ]
+ }
+
+ function appendJohn() {
+ testModel.appendRow({
+ name: "John",
+ age: 22
+ })
+ }
+
+ function appendOliver() {
+ testModel.appendRow({
+ name: "Oliver",
+ age: 33
+ })
+ }
+
+ TableModel {
+ id: testModel
+ objectName: "testModel"
+
+ TableModelColumn { display: "name" }
+ TableModelColumn { display: "age" }
+ }
+ TableView {
+ id: tableView
+ anchors.fill: parent
+ model: testModel
+ delegate: Text {
+ text: model.display
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml b/tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml
new file mode 100644
index 0000000000..86bcb08fa2
--- /dev/null
+++ b/tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml
@@ -0,0 +1,47 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import Qt.labs.qmlmodels 1.0
+
+TableModel {
+ objectName: "testModel"
+
+ TableModelColumn { display: "name" }
+ TableModelColumn { display: "age" }
+
+ rows: [
+ {
+ name: "John",
+ age: 22
+ },
+ {
+ name: "Oliver",
+ age: 33
+ }
+ ]
+}
diff --git a/tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml b/tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml
new file mode 100644
index 0000000000..ebfe4ed930
--- /dev/null
+++ b/tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import Qt.labs.qmlmodels 1.0
+
+Item {
+ id: root
+ width: 200
+ height: 200
+
+ property alias testModel: testModel
+
+ signal shouldModify()
+ signal shouldModifyInvalidRole()
+ signal shouldModifyInvalidType()
+
+ function modify() {
+ shouldModify()
+ }
+
+ function modifyInvalidRole() {
+ shouldModifyInvalidRole()
+ }
+
+ function modifyInvalidType() {
+ shouldModifyInvalidType()
+ }
+
+ TableView {
+ anchors.fill: parent
+ model: TestModel {
+ id: testModel
+ }
+
+ delegate: Text {
+ id: textItem
+ text: model.display
+
+ Connections {
+ target: root
+ enabled: column === 1
+ onShouldModify: model.display = 18
+ }
+
+ Connections {
+ target: root
+ enabled: column === 0
+ // Invalid: should be "display".
+ onShouldModifyInvalidRole: model.age = 100
+ }
+
+ Connections {
+ target: root
+ enabled: column === 1
+ // Invalid: should be string.
+ onShouldModifyInvalidType: model.display = "Whoops"
+ }
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml b/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml
new file mode 100644
index 0000000000..01ec40270c
--- /dev/null
+++ b/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import Qt.labs.qmlmodels 1.0
+
+Item {
+ id: root
+ width: 200
+ height: 200
+
+ property alias testModel: testModel
+ property alias tableView: tableView
+
+ function setRowsValid() {
+ testModel.rows = [
+ {
+ name: "Max",
+ age: 20
+ },
+ {
+ name: "Imum",
+ age: 41
+ },
+ {
+ name: "Power",
+ age: 89
+ }
+ ]
+ }
+
+ function setRowsInvalid() {
+ testModel.rows = [
+ {
+ nope: "Nope",
+ age: 20
+ },
+ {
+ nope: "Nah",
+ age: 41
+ },
+ {
+ nope: "No",
+ age: 89
+ }
+ ]
+ }
+
+ TableView {
+ id: tableView
+ anchors.fill: parent
+ model: TestModel {
+ id: testModel
+ }
+ delegate: Text {
+ text: model.display
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmltablemodel/qqmltablemodel.pro b/tests/auto/qml/qqmltablemodel/qqmltablemodel.pro
new file mode 100644
index 0000000000..9d298dfdf2
--- /dev/null
+++ b/tests/auto/qml/qqmltablemodel/qqmltablemodel.pro
@@ -0,0 +1,10 @@
+CONFIG += testcase
+TARGET = tst_qqmltablemodel
+
+SOURCES += tst_qqmltablemodel.cpp
+
+include (../../shared/util.pri)
+
+TESTDATA = data/*
+
+QT += core gui qml-private qml quick-private quick testlib qmlmodels-private
diff --git a/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp b/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp
new file mode 100644
index 0000000000..d913bcdf9a
--- /dev/null
+++ b/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp
@@ -0,0 +1,980 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/qtest.h>
+#include <QtTest/qsignalspy.h>
+#include <QtCore/qregularexpression.h>
+#include <QtQml/private/qqmlengine_p.h>
+#include <QtQmlModels/private/qqmltablemodel_p.h>
+#include <QtQml/qqmlcomponent.h>
+#include <QtQuick/qquickitem.h>
+#include <QtQuick/qquickview.h>
+#include <QtQuick/private/qquicktableview_p.h>
+
+#include "../../shared/util.h"
+
+class tst_QQmlTableModel : public QQmlDataTest
+{
+ Q_OBJECT
+
+public:
+ tst_QQmlTableModel() {}
+
+private slots:
+ void appendRemoveRow();
+ void appendRowToEmptyModel();
+ void clear();
+ void getRow();
+ void insertRow();
+ void moveRow();
+ void setRow();
+ void setDataThroughDelegate();
+ void setRowsImperatively();
+ void setRowsMultipleTimes();
+ void dataAndEditing();
+ void omitTableModelColumnIndex();
+ void complexRow();
+};
+
+void tst_QQmlTableModel::appendRemoveRow()
+{
+ QQuickView view(testFileUrl("common.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>();
+ QVERIFY(model);
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+
+ QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged()));
+ QVERIFY(columnCountSpy.isValid());
+
+ QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged()));
+ QVERIFY(rowCountSpy.isValid());
+ int rowCountSignalEmissions = 0;
+
+ const QHash<int, QByteArray> roleNames = model->roleNames();
+ QCOMPARE(roleNames.size(), 1);
+ QVERIFY(roleNames.values().contains("display"));
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+
+ // Call remove() with a negative rowIndex.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*removeRow\\(\\): \"rowIndex\" cannot be negative"));
+ QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, -1)));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Call remove() with an rowIndex that is too large.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*removeRow\\(\\): \"rowIndex\" 2 is greater than or equal to rowCount\\(\\) of 2"));
+ QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, 2)));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Call remove() with a valid rowIndex but negative rows.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*removeRow\\(\\): \"rows\" is less than or equal to zero"));
+ QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, 0), Q_ARG(int, -1)));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Call remove() with a valid rowIndex but excessive rows.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*removeRow\\(\\): \"rows\" 3 exceeds available rowCount\\(\\) of 2 when removing from \"rowIndex\" 0"));
+ QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, 0), Q_ARG(int, 3)));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Call remove() without specifying the number of rows to remove; it should remove one row.
+ QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, 0)));
+ QCOMPARE(model->rowCount(), 1);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions);
+
+ // Call append() with a row that has an unexpected role; the row should be added and the extra data ignored.
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRowExtraData"));
+ // Nothing should change.
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 99);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions);
+
+ // Call append() with a row that is an int.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*appendRow\\(\\): expected \"row\" argument to be a QJSValue, but got int instead:\nQVariant\\(int, 123\\)"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRowInvalid1"));
+ // Nothing should change.
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 99);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Call append() with a row with a role of the wrong type.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*appendRow\\(\\): expected the property named \"age\" to be of type \"int\", but got \"QVariantList\" instead"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRowInvalid2"));
+ // Nothing should change.
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 99);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Call append() with a row that is an array instead of a simple object.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*appendRow\\(\\): row manipulation functions do not support complex rows \\(row index: -1\\)"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRowInvalid3"));
+ // Nothing should change.
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Call append() to insert one row.
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRow", Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40)));
+ QCOMPARE(model->rowCount(), 3);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 99);
+ QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max"));
+ QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 40);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions);
+
+ // Call remove() and specify rowIndex and rows, removing all remaining rows.
+ QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, 0), Q_ARG(int, 3)));
+ QCOMPARE(model->rowCount(), 0);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")), QVariant());
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")), QVariant());
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions);
+}
+
+void tst_QQmlTableModel::appendRowToEmptyModel()
+{
+ QQuickView view(testFileUrl("empty.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>();
+ QVERIFY(model);
+ QCOMPARE(model->rowCount(), 0);
+ QCOMPARE(model->columnCount(), 2);
+
+ QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged()));
+ QVERIFY(columnCountSpy.isValid());
+
+ QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged()));
+ QVERIFY(rowCountSpy.isValid());
+
+ QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>();
+ QVERIFY(tableView);
+ QCOMPARE(tableView->rows(), 0);
+ QCOMPARE(tableView->columns(), 2);
+
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendJohn"));
+ QCOMPARE(model->rowCount(), 1);
+ QCOMPARE(model->columnCount(), 2);
+ const QHash<int, QByteArray> roleNames = model->roleNames();
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), 1);
+ QTRY_COMPARE(tableView->rows(), 1);
+ QCOMPARE(tableView->columns(), 2);
+}
+
+void tst_QQmlTableModel::clear()
+{
+ QQuickView view(testFileUrl("common.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>();
+ QVERIFY(model);
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+
+ QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged()));
+ QVERIFY(columnCountSpy.isValid());
+
+ QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged()));
+ QVERIFY(rowCountSpy.isValid());
+
+ const QHash<int, QByteArray> roleNames = model->roleNames();
+ QVERIFY(roleNames.values().contains("display"));
+ QCOMPARE(roleNames.size(), 1);
+
+ QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>();
+ QVERIFY(tableView);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ QVERIFY(QMetaObject::invokeMethod(model, "clear"));
+ QCOMPARE(model->rowCount(), 0);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")), QVariant());
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")), QVariant());
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), 1);
+ // Wait until updatePolish() gets called, which is where the size is recalculated.
+ QTRY_COMPARE(tableView->rows(), 0);
+ QCOMPARE(tableView->columns(), 2);
+}
+
+void tst_QQmlTableModel::getRow()
+{
+ QQuickView view(testFileUrl("common.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>();
+ QVERIFY(model);
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+
+ // Call get() with a negative row index.
+ QVariant returnValue;
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*getRow\\(\\): \"rowIndex\" cannot be negative"));
+ QVERIFY(QMetaObject::invokeMethod(model, "getRow", Q_RETURN_ARG(QVariant, returnValue), Q_ARG(int, -1)));
+ QVERIFY(!returnValue.isValid());
+
+ // Call get() with a row index that is too large.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*getRow\\(\\): \"rowIndex\" 2 is greater than or equal to rowCount\\(\\) of 2"));
+ QVERIFY(QMetaObject::invokeMethod(model, "getRow", Q_RETURN_ARG(QVariant, returnValue), Q_ARG(int, 2)));
+ QVERIFY(!returnValue.isValid());
+
+ // Call get() with a valid row index.
+ QVERIFY(QMetaObject::invokeMethod(model, "getRow", Q_RETURN_ARG(QVariant, returnValue), Q_ARG(int, 0)));
+ const QVariantMap rowAsVariantMap = returnValue.toMap();
+ QCOMPARE(rowAsVariantMap.value(QLatin1String("name")).toString(), QLatin1String("John"));
+ QCOMPARE(rowAsVariantMap.value(QLatin1String("age")).toInt(), 22);
+}
+
+void tst_QQmlTableModel::insertRow()
+{
+ QQuickView view(testFileUrl("common.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>();
+ QVERIFY(model);
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+
+ QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged()));
+ QVERIFY(columnCountSpy.isValid());
+
+ QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged()));
+ QVERIFY(rowCountSpy.isValid());
+ int rowCountSignalEmissions = 0;
+
+ QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>();
+ QVERIFY(tableView);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Try to insert with a negative index.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*insertRow\\(\\): \"rowIndex\" cannot be negative"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRow",
+ Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40), Q_ARG(QVariant, -1)));
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->rowCount(), 2);
+ const QHash<int, QByteArray> roleNames = model->roleNames();
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Try to insert past the last allowed index.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*insertRow\\(\\): \"rowIndex\" 3 is greater than rowCount\\(\\) of 2"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRow",
+ Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40), Q_ARG(QVariant, 3)));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Call insert() with a row that is an int.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*insertRow\\(\\): expected \"row\" argument to be a QJSValue, but got int instead:\nQVariant\\(int, 123\\)"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRowInvalid1"));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Try to insert a row with a role of the wrong type.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*insertRow\\(\\): expected the property named \"age\" to be of type \"int\", but got \"QVariantList\" instead"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRowInvalid2"));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Try to insert a row that is an array instead of a simple object.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*insertRow\\(\\): row manipulation functions do not support complex rows \\(row index: 0\\)"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRowInvalid3"));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Try to insert a row has an unexpected role; the row should be added and the extra data ignored.
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRowExtraData"));
+ QCOMPARE(model->rowCount(), 3);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions);
+ QTRY_COMPARE(tableView->rows(), 3);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Insert a row at the bottom of the table.
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRow",
+ Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40), Q_ARG(QVariant, 3)));
+ QCOMPARE(model->rowCount(), 4);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max"));
+ QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("display")).toInt(), 40);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions);
+ QTRY_COMPARE(tableView->rows(), 4);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Insert a row in the middle of the table.
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRow",
+ Q_ARG(QVariant, QLatin1String("Daisy")), Q_ARG(QVariant, 30), Q_ARG(QVariant, 2)));
+ QCOMPARE(model->rowCount(), 5);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy"));
+ QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 30);
+ QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max"));
+ QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 40);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions);
+ QTRY_COMPARE(tableView->rows(), 5);
+ QCOMPARE(tableView->columns(), 2);
+}
+
+void tst_QQmlTableModel::moveRow()
+{
+ QQuickView view(testFileUrl("common.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>();
+ QVERIFY(model);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->rowCount(), 2);
+
+ QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged()));
+ QVERIFY(columnCountSpy.isValid());
+
+ QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged()));
+ QVERIFY(rowCountSpy.isValid());
+ int rowCountSignalEmissions = 0;
+
+ const QHash<int, QByteArray> roleNames = model->roleNames();
+
+ // Append some rows.
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRow", Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40)));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRow", Q_ARG(QVariant, QLatin1String("Daisy")), Q_ARG(QVariant, 30)));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRow", Q_ARG(QVariant, QLatin1String("Trev")), Q_ARG(QVariant, 48)));
+ QCOMPARE(model->rowCount(), 5);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max"));
+ QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 40);
+ QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy"));
+ QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("display")).toInt(), 30);
+ QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev"));
+ QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48);
+ rowCountSignalEmissions = 3;
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Try to move with a fromRowIndex that is negative.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*moveRow\\(\\): \"fromRowIndex\" cannot be negative"));
+ QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, -1), Q_ARG(int, 1)));
+ // Shouldn't have changed.
+ QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev"));
+ QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Try to move with a fromRowIndex that is too large.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*moveRow\\(\\): \"fromRowIndex\" 5 is greater than or equal to rowCount\\(\\)"));
+ QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 5), Q_ARG(int, 1)));
+ QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev"));
+ QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Try to move with a toRowIndex that is negative.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*moveRow\\(\\): \"toRowIndex\" cannot be negative"));
+ QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 0), Q_ARG(int, -1)));
+ // Shouldn't have changed.
+ QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev"));
+ QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Try to move with a toRowIndex that is too large.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*moveRow\\(\\): \"toRowIndex\" 5 is greater than or equal to rowCount\\(\\)"));
+ QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 0), Q_ARG(int, 5)));
+ QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev"));
+ QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Move the first row to the end.
+ QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 0), Q_ARG(int, 4)));
+ // The counts shouldn't have changed.
+ QCOMPARE(model->rowCount(), 5);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 40);
+ QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy"));
+ QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 30);
+ QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev"));
+ QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("display")).toInt(), 48);
+ QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Move it back again.
+ QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 4), Q_ARG(int, 0)));
+ QCOMPARE(model->rowCount(), 5);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max"));
+ QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 40);
+ QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy"));
+ QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("display")).toInt(), 30);
+ QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev"));
+ QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Move the first row down one by one row.
+ QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 0), Q_ARG(int, 1)));
+ QCOMPARE(model->rowCount(), 5);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max"));
+ QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 40);
+ QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy"));
+ QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("display")).toInt(), 30);
+ QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev"));
+ QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+}
+
+void tst_QQmlTableModel::setRow()
+{
+ QQuickView view(testFileUrl("common.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>();
+ QVERIFY(model);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->rowCount(), 2);
+
+ QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged()));
+ QVERIFY(columnCountSpy.isValid());
+
+ QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged()));
+ QVERIFY(rowCountSpy.isValid());
+ int rowCountSignalEmissions = 0;
+
+ QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>();
+ QVERIFY(tableView);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Try to set with a negative index.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*setRow\\(\\): \"rowIndex\" cannot be negative"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRow",
+ Q_ARG(QVariant, -1), Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40)));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ const QHash<int, QByteArray> roleNames = model->roleNames();
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Try to set at an index past the last allowed index.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*setRow\\(\\): \"rowIndex\" 3 is greater than rowCount\\(\\) of 2"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRow",
+ Q_ARG(QVariant, 3), Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40)));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Try to set a row that has an unexpected role; the row should be set and the extra data ignored.
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowExtraData"));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Try to insert a row that is not an array.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*setRow\\(\\): expected \"row\" argument to be a QJSValue, but got int instead:\nQVariant\\(int, 123\\)"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowInvalid1"));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Try to insert a row with a role that is of the wrong type.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*setRow\\(\\): expected the property named \"age\" to be of type \"int\", but got \"QVariantList\" instead"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowInvalid2"));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Try to insert a row that is an array instead of a simple object.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*setRow\\(\\): row manipulation functions do not support complex rows \\(row index: 0\\)"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowInvalid3"));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Set the first row.
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRow",
+ Q_ARG(QVariant, 0), Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40)));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 40);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Set the last row.
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRow",
+ Q_ARG(QVariant, 1), Q_ARG(QVariant, QLatin1String("Daisy")), Q_ARG(QVariant, 30)));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 40);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 30);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Append a row by passing an index that is equal to rowCount().
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRow",
+ Q_ARG(QVariant, 2), Q_ARG(QVariant, QLatin1String("Wot")), Q_ARG(QVariant, 99)));
+ QCOMPARE(model->rowCount(), 3);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 40);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 30);
+ QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Wot"));
+ QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 99);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions);
+ QTRY_COMPARE(tableView->rows(), 3);
+ QCOMPARE(tableView->columns(), 2);
+}
+
+void tst_QQmlTableModel::setDataThroughDelegate()
+{
+ QQuickView view(testFileUrl("setDataThroughDelegate.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>();
+ QVERIFY(model);
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+
+ QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged()));
+ QVERIFY(columnCountSpy.isValid());
+
+ QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged()));
+ QVERIFY(rowCountSpy.isValid());
+
+ const QHash<int, QByteArray> roleNames = model->roleNames();
+ QCOMPARE(roleNames.size(), 1);
+ QVERIFY(roleNames.values().contains("display"));
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), 0);
+
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "modify"));
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 18);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 18);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), 0);
+
+ // Test setting a role that doesn't exist for a certain column.
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "modifyInvalidRole"));
+ // Should be unchanged.
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 18);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 18);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), 0);
+
+ // Test setting a role with a value of the wrong type.
+ // There are two rows, so two delegates respond to the signal, which means we need to ignore two warnings.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*setData\\(\\): failed converting value QVariant\\(QString, \"Whoops\"\\) " \
+ "set at row 0 column 1 with role \"display\" to \"int\""));
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*setData\\(\\): failed converting value QVariant\\(QString, \"Whoops\"\\) " \
+ "set at row 1 column 1 with role \"display\" to \"int\""));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "modifyInvalidType"));
+ // Should be unchanged.
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 18);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 18);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), 0);
+}
+
+// Start off with empty rows and then set them to test rowCountChanged().
+void tst_QQmlTableModel::setRowsImperatively()
+{
+ QQuickView view(testFileUrl("empty.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>();
+ QVERIFY(model);
+ QCOMPARE(model->rowCount(), 0);
+ QCOMPARE(model->columnCount(), 2);
+
+ QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged()));
+ QVERIFY(columnCountSpy.isValid());
+
+ QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged()));
+ QVERIFY(rowCountSpy.isValid());
+
+ QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>();
+ QVERIFY(tableView);
+ QCOMPARE(tableView->rows(), 0);
+ QCOMPARE(tableView->columns(), 2);
+
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRows"));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ const QHash<int, QByteArray> roleNames = model->roleNames();
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), 1);
+ QTRY_COMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+}
+
+void tst_QQmlTableModel::setRowsMultipleTimes()
+{
+ QQuickView view(testFileUrl("setRowsMultipleTimes.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>();
+ QVERIFY(model);
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+
+ QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged()));
+ QVERIFY(columnCountSpy.isValid());
+
+ QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged()));
+ QVERIFY(rowCountSpy.isValid());
+
+ QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>();
+ QVERIFY(tableView);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Set valid rows after they've already been declared.
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowsValid"));
+ QCOMPARE(model->rowCount(), 3);
+ QCOMPARE(model->columnCount(), 2);
+ const QHash<int, QByteArray> roleNames = model->roleNames();
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 20);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Imum"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 41);
+ QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Power"));
+ QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 89);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), 1);
+ QTRY_COMPARE(tableView->rows(), 3);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Set invalid rows; we should get a warning and nothing should change.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*setRows\\(\\): expected a property named \"name\" in row at index 0, but couldn't find one"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowsInvalid"));
+ QCOMPARE(model->rowCount(), 3);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 20);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Imum"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 41);
+ QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Power"));
+ QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 89);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), 1);
+ QCOMPARE(tableView->rows(), 3);
+ QCOMPARE(tableView->columns(), 2);
+}
+
+void tst_QQmlTableModel::dataAndEditing()
+{
+ QQuickView view(testFileUrl("dataAndSetData.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QQmlTableModel *model = view.rootObject()->property("model").value<QQmlTableModel*>();
+ QVERIFY(model);
+
+ const QHash<int, QByteArray> roleNames = model->roleNames();
+ QVERIFY(roleNames.values().contains("display"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QVERIFY(QMetaObject::invokeMethod(model, "happyBirthday", Q_ARG(QVariant, QLatin1String("Oliver"))));
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 34);
+}
+
+void tst_QQmlTableModel::omitTableModelColumnIndex()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("omitTableModelColumnIndex.qml"));
+ QCOMPARE(component.status(), QQmlComponent::Ready);
+
+ QScopedPointer<QQmlTableModel> model(qobject_cast<QQmlTableModel*>(component.create()));
+ QVERIFY(model);
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+
+ const QHash<int, QByteArray> roleNames = model->roleNames();
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+}
+
+void tst_QQmlTableModel::complexRow()
+{
+ QQuickView view(testFileUrl("complex.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QQuickTableView *tableView = qobject_cast<QQuickTableView*>(view.rootObject());
+ QVERIFY(tableView);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ QQmlTableModel *model = tableView->model().value<QQmlTableModel*>();
+ QVERIFY(model);
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+
+ const QHash<int, QByteArray> roleNames = model->roleNames();
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+}
+
+QTEST_MAIN(tst_QQmlTableModel)
+
+#include "tst_qqmltablemodel.moc"
diff --git a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
index 15bd031ce3..83a37df797 100644
--- a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
+++ b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
@@ -1713,7 +1713,7 @@ void tst_qqmlvaluetypes::sequences()
QJSValue value = engine.toScriptValue(qcharVector);
QCOMPARE(value.property("length").toInt(), qcharVector.length());
for (int i = 0; i < qcharVector.length(); ++i)
- QCOMPARE(value.property(i).toInt(), qcharVector.at(i));
+ QCOMPARE(value.property(i).toString(), qcharVector.at(i));
}
{
MyTypeObject a, b, c;
diff --git a/tests/auto/qml/qquickworkerscript/qquickworkerscript.pro b/tests/auto/qml/qquickworkerscript/qquickworkerscript.pro
index be8b9089a2..f58fd4543f 100644
--- a/tests/auto/qml/qquickworkerscript/qquickworkerscript.pro
+++ b/tests/auto/qml/qquickworkerscript/qquickworkerscript.pro
@@ -8,4 +8,4 @@ include (../../shared/util.pri)
TESTDATA = data/*
-QT += core-private gui-private qml-private testlib
+QT += core-private gui-private qml-private testlib qmlworkerscript-private
diff --git a/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp b/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp
index 4ad58ba56c..bea9978f0b 100644
--- a/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp
+++ b/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp
@@ -30,6 +30,7 @@
#include <QtCore/qtimer.h>
#include <QtCore/qdir.h>
#include <QtCore/qfileinfo.h>
+#include <QtCore/qregularexpression.h>
#include <QtQml/qjsengine.h>
#include <QtQml/qqmlcomponent.h>
@@ -91,14 +92,14 @@ void tst_QQuickWorkerScript::source()
QCOMPARE(worker->source(), source);
QVERIFY(QMetaObject::invokeMethod(worker.data(), "testSend", Q_ARG(QVariant, value)));
waitForEchoMessage(worker.data());
- QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker.data()).value<QVariant>(), qVariantFromValue(QString("Hello_World")));
+ QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker.data()).value<QVariant>(), QVariant::fromValue(QString("Hello_World")));
source = testFileUrl("script_module.mjs");
worker->setSource(source);
QCOMPARE(worker->source(), source);
QVERIFY(QMetaObject::invokeMethod(worker.data(), "testSend", Q_ARG(QVariant, value)));
waitForEchoMessage(worker.data());
- QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker.data()).value<QVariant>(), qVariantFromValue(QString("Hello from the module")));
+ QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker.data()).value<QVariant>(), QVariant::fromValue(QString("Hello from the module")));
qApp->processEvents();
}
@@ -118,7 +119,18 @@ void tst_QQuickWorkerScript::messaging()
QVariant response = mo->property(mo->indexOfProperty("response")).read(worker).value<QVariant>();
if (response.userType() == qMetaTypeId<QJSValue>())
response = response.value<QJSValue>().toVariant();
- QCOMPARE(response, value);
+
+ if (value.type() == QMetaType::QRegExp && response.type() == QMetaType::QRegularExpression) {
+ // toVariant() doesn't know if we want QRegExp or QRegularExpression. It always creates
+ // a QRegularExpression from a JavaScript regular expression.
+ const QRegularExpression responseRegExp = response.toRegularExpression();
+ const QRegExp valueRegExp = value.toRegExp();
+ QCOMPARE(responseRegExp.pattern(), valueRegExp.pattern());
+ QCOMPARE(bool(responseRegExp.patternOptions() & QRegularExpression::CaseInsensitiveOption),
+ bool(valueRegExp.caseSensitivity() == Qt::CaseInsensitive));
+ } else {
+ QCOMPARE(response, value);
+ }
qApp->processEvents();
delete worker;
@@ -129,16 +141,16 @@ void tst_QQuickWorkerScript::messaging_data()
QTest::addColumn<QVariant>("value");
QTest::newRow("invalid") << QVariant();
- QTest::newRow("bool") << qVariantFromValue(true);
- QTest::newRow("int") << qVariantFromValue(1001);
- QTest::newRow("real") << qVariantFromValue(10334.375);
- QTest::newRow("string") << qVariantFromValue(QString("More cheeeese, Gromit!"));
- QTest::newRow("variant list") << qVariantFromValue((QVariantList() << "a" << "b" << "c"));
- QTest::newRow("date time") << qVariantFromValue(QDateTime::currentDateTime());
-#ifndef QT_NO_REGEXP
- // Qt Script's QScriptValue -> QRegExp uses RegExp2 pattern syntax
- QTest::newRow("regexp") << qVariantFromValue(QRegExp("^\\d\\d?$", Qt::CaseInsensitive, QRegExp::RegExp2));
-#endif
+ QTest::newRow("bool") << QVariant::fromValue(true);
+ QTest::newRow("int") << QVariant::fromValue(1001);
+ QTest::newRow("real") << QVariant::fromValue(10334.375);
+ QTest::newRow("string") << QVariant::fromValue(QString("More cheeeese, Gromit!"));
+ QTest::newRow("variant list") << QVariant::fromValue((QVariantList() << "a" << "b" << "c"));
+ QTest::newRow("date time") << QVariant::fromValue(QDateTime::currentDateTime());
+ QTest::newRow("regexp") << QVariant::fromValue(QRegExp("^\\d\\d?$", Qt::CaseInsensitive,
+ QRegExp::RegExp2));
+ QTest::newRow("regularexpression") << QVariant::fromValue(QRegularExpression(
+ "^\\d\\d?$", QRegularExpression::CaseInsensitiveOption));
}
void tst_QQuickWorkerScript::messaging_sendQObjectList()
@@ -153,9 +165,9 @@ void tst_QQuickWorkerScript::messaging_sendQObjectList()
QVariantList objects;
for (int i=0; i<3; i++)
- objects << qVariantFromValue(new QObject(this));
+ objects << QVariant::fromValue(new QObject(this));
- QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, qVariantFromValue(objects))));
+ QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, QVariant::fromValue(objects))));
waitForEchoMessage(worker);
const QMetaObject *mo = worker->metaObject();
@@ -181,10 +193,10 @@ void tst_QQuickWorkerScript::messaging_sendJsObject()
map.insert("name", "zyz");
map.insert("spell power", 3101);
- QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, qVariantFromValue(map))));
+ QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, QVariant::fromValue(map))));
waitForEchoMessage(worker);
- QVariant result = qVariantFromValue(false);
+ QVariant result = QVariant::fromValue(false);
QVERIFY(QMetaObject::invokeMethod(worker, "compareLiteralResponse", Qt::DirectConnection,
Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, jsObject)));
QVERIFY(result.toBool());
diff --git a/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp b/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp
index fd50ff5020..392ce16880 100644
--- a/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp
+++ b/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp
@@ -140,23 +140,17 @@ void tst_QV4Assembler::functionTable()
#endif
}
-#ifdef V4_ENABLE_JIT
-#define JIT_ENABLED 1
-#else
-#define JIT_ENABLED 0
-#endif
-
void tst_QV4Assembler::jitEnabled()
{
#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
/* JIT should be disabled on iOS and tvOS. */
- QCOMPARE(JIT_ENABLED, 0);
+ QVERIFY(!QT_CONFIG(qml_jit));
#elif defined(Q_OS_WIN) && defined(Q_PROCESSOR_ARM)
/* JIT should be disabled Windows on ARM/ARM64 for now. */
- QCOMPARE(JIT_ENABLED, 0);
+ QVERIFY(!QT_CONFIG(qml_jit));
#else
/* JIT should be enabled on all other architectures/OSes tested in CI. */
- QCOMPARE(JIT_ENABLED, 1);
+ QVERIFY(QT_CONFIG(qml_jit));
#endif
}
diff --git a/tests/auto/qml/v4traced/tst_v4traced.cpp b/tests/auto/qml/v4traced/tst_v4traced.cpp
deleted file mode 100644
index f82cc0ed5e..0000000000
--- a/tests/auto/qml/v4traced/tst_v4traced.cpp
+++ /dev/null
@@ -1,325 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
-#include <QJSEngine>
-#include <private/qjsvalue_p.h>
-#include <private/qv4scopedvalue_p.h>
-#include <private/qv4script_p.h>
-
-class EnvVarSaver
-{
-public:
- EnvVarSaver(const char *name, const QByteArray &newValue)
- : _name(name)
- {
- _wasSet = qEnvironmentVariableIsSet(name);
- if (_wasSet)
- _oldValue = qgetenv(name);
- qputenv(name, newValue);
- }
-
- ~EnvVarSaver()
- {
- if (_wasSet)
- qputenv(_name, _oldValue);
- else
- qunsetenv(_name);
- }
-
-private:
- const char *_name;
- bool _wasSet;
- QByteArray _oldValue;
-};
-
-class tst_v4traced : public QObject
-{
- Q_OBJECT
-
-private slots:
- void collectTraces_data();
- void collectTraces();
-
- void binopI32deopt_data();
- void binopI32deopt();
-
- void calls_data();
- void calls();
-
- void setLookup();
- void construct();
-};
-
-void tst_v4traced::collectTraces_data()
-{
- QTest::addColumn<QString>("code");
- QTest::addColumn<int>("tracePointCount");
- QTest::addColumn<int>("interestingTracePoint");
- QTest::addColumn<quint8>("expectedBits");
-
- QTest::newRow("int+") << "var a = 4; a + 2" << 2 << 1 << quint8(QV4::ObservedTraceValues::Integer);
- QTest::newRow("double+") << "var a = 4.1; a + 1.9" << 2 << 1 << quint8(QV4::ObservedTraceValues::Double);
- QTest::newRow("object+") << "var a = '4'; a + '2'" << 2 << 1 << quint8(QV4::ObservedTraceValues::Other);
-}
-
-void tst_v4traced::collectTraces()
-{
- QFETCH(QString, code);
- QFETCH(int, tracePointCount);
- QFETCH(int, interestingTracePoint);
- QFETCH(quint8, expectedBits);
-
- EnvVarSaver forceInterpreter("QV4_FORCE_INTERPRETER", "1");
- EnvVarSaver forceTracing("QV4_FORCE_TRACING", "1");
-
- QV4::ExecutionEngine vm;
- QV4::Scope scope(&vm);
- QV4::ScopedContext ctx(scope, vm.rootContext());
- QV4::ScopedValue result(scope);
- QScopedPointer<QV4::Script> script;
- script.reset(new QV4::Script(ctx, QV4::Compiler::ContextType::Global, code, "collectTraces"));
- script->parseAsBinding = false;
-
- QVERIFY(!scope.engine->hasException);
- script->parse();
- QVERIFY(!scope.engine->hasException);
-
- QVERIFY(script->function()->tracingEnabled());
- result = script->run();
- QVERIFY(!scope.engine->hasException);
-
- QCOMPARE(int(script->function()->compiledFunction->nTraceInfos), tracePointCount);
- QCOMPARE(*script->function()->traceInfo(interestingTracePoint), expectedBits);
-}
-
-void tst_v4traced::binopI32deopt_data()
-{
- QTest::addColumn<QString>("operand");
- QTest::addColumn<int>("int_arg1");
- QTest::addColumn<int>("int_arg2");
- QTest::addColumn<int>("int_result");
- QTest::addColumn<QString>("other_arg1");
- QTest::addColumn<QString>("other_arg2");
- QTest::addColumn<double>("other_result");
-
- QTest::newRow("+") << "+" << 1 << 2 << 3 << "1.1" << "1.9" << 3.0;
- QTest::newRow("-") << "-" << 3 << 2 << 1 << "3.1" << "2.1" << 1.0;
- QTest::newRow("*") << "*" << 2 << 3 << 6 << "2.1" << "1.9" << 3.99;
- QTest::newRow("/") << "/" << 6 << 3 << 2 << "6.6" << "3.3" << 2.0;
-
- QTest::newRow("&") << "&" << 6 << 3 << 2 << "'6'" << "'3'" << 2.0;
- QTest::newRow("|") << "|" << 6 << 3 << 7 << "'6'" << "'3'" << 7.0;
- QTest::newRow("^") << "^" << 6 << 3 << 5 << "'6'" << "'3'" << 5.0;
-
- QTest::newRow("<<") << "<<" << 5 << 1 << 10 << "'5'" << "'1'" << 10.0;
- QTest::newRow(">>") << ">>" << -1 << 1 << -1 << "'-1'" << "'1'" << -1.0;
- QTest::newRow(">>>") << ">>>" << -1 << 1 << 0x7FFFFFFF << "'-1'" << "'1'" << 2147483647.0;
-
- QTest::newRow("==") << "==" << 2 << 1 << 0 << "'2'" << "'1'" << 0.0;
- QTest::newRow("!=") << "!=" << 2 << 1 << 1 << "'2'" << "'1'" << 1.0;
- QTest::newRow("<" ) << "<" << 2 << 1 << 0 << "'2'" << "'1'" << 0.0;
- QTest::newRow("<=") << "<=" << 2 << 1 << 0 << "'2'" << "'1'" << 0.0;
- QTest::newRow(">" ) << ">" << 2 << 1 << 1 << "'2'" << "'1'" << 1.0;
- QTest::newRow(">=") << ">=" << 2 << 1 << 1 << "'2'" << "'1'" << 1.0;
-}
-
-void tst_v4traced::binopI32deopt()
-{
- QFETCH(QString, operand);
- QFETCH(int, int_arg1);
- QFETCH(int, int_arg2);
- QFETCH(int, int_result);
- QFETCH(QString, other_arg1);
- QFETCH(QString, other_arg2);
- QFETCH(double, other_result);
-
- QString func = QStringLiteral("function binopI32(a, b) { return a %1 b }").arg(operand);
- QString intCall = QStringLiteral("binopI32(%1, %2)").arg(int_arg1).arg(int_arg2);
- QString otherCall = QStringLiteral("binopI32(%1, %2)").arg(other_arg1).arg(other_arg2);
-
- QJSEngine engine;
- engine.evaluate(func);
-
- QCOMPARE(engine.evaluate(intCall).toInt(), int_result); // interpret + trace
- QCOMPARE(engine.evaluate(intCall).toInt(), int_result); // jit
- QCOMPARE(engine.evaluate(otherCall).toNumber(), other_result); // deopt
- QCOMPARE(engine.evaluate(otherCall).toNumber(), other_result); // retrace
- QCOMPARE(engine.evaluate(otherCall).toNumber(), other_result); // rejit
-}
-
-void tst_v4traced::calls_data()
-{
- QTest::addColumn<QString>("call");
-
- QTest::newRow("callGlobalLookup") << "globalLookup";
- QTest::newRow("callPropertyLookup") << "obj.propertyLookup";
-}
-
-class Calls
-{
-public:
- static int callCount;
-
- static QV4::ReturnedValue doSomething(const QV4::FunctionObject */*o*/,
- const QV4::Value */*thiz*/,
- const QV4::Value *argv, int argc)
- {
- ++callCount;
-
- if (argc == 0)
- return QV4::Encode(42);
-
- int prod = 1;
- for (int i = 0; i < argc; ++i) {
- Q_ASSERT(argv[i].isInteger());
- prod *= argv[i].int_32();
- }
- return QV4::Encode(prod);
- }
-};
-
-int Calls::callCount = 0;
-
-void tst_v4traced::calls()
-{
- QFETCH(QString, call);
-
- EnvVarSaver forceTracing("QV4_FORCE_TRACING", "1");
- EnvVarSaver jitCallThreshold("QV4_JIT_CALL_THRESHOLD", "1");
-
- QV4::ExecutionEngine vm;
- QV4::Scope scope(&vm);
- QV4::ScopedContext ctx(scope, vm.rootContext());
- vm.globalObject->defineDefaultProperty(QLatin1String("globalLookup"),
- Calls::doSomething);
- QV4::ScopedObject obj(scope, vm.newObject());
- vm.globalObject->defineDefaultProperty(QLatin1String("obj"), obj);
- obj->defineDefaultProperty("propertyLookup", Calls::doSomething);
-
- QString code = QStringLiteral(
- "function doCalls() {\n"
- " if (%1() != 42) return false\n"
- " if (%1(21, 2) != 42) return false\n"
- " if (%1(2, 3, 7) != 42) return false\n"
- " return true\n"
- "}\n"
- "var result = true\n"
- "for (var i = 0; i < 10; ++i) {\n"
- " if (!doCalls()) { result = false; break }"
- "}\n"
- "result\n").arg(call);
- QScopedPointer<QV4::Script> script;
- script.reset(new QV4::Script(ctx, QV4::Compiler::ContextType::Global, code, "call"));
- script->parseAsBinding = false;
-
- QVERIFY(!scope.engine->hasException);
- script->parse();
- QVERIFY(!scope.engine->hasException);
-
- Calls::callCount = 0;
- QV4::ScopedValue result(scope, script->run());
- QVERIFY(!scope.engine->hasException);
-
- QVERIFY(result->isBoolean());
- QVERIFY(result->booleanValue());
- QCOMPARE(Calls::callCount, 30);
-}
-
-void tst_v4traced::setLookup()
-{
- EnvVarSaver forceTracing("QV4_FORCE_TRACING", "1");
- EnvVarSaver jitCallThreshold("QV4_JIT_CALL_THRESHOLD", "1");
-
- QV4::ExecutionEngine vm;
- QV4::Scope scope(&vm);
- QV4::ScopedContext ctx(scope, vm.rootContext());
- QV4::ScopedObject obj(scope, vm.newObject());
- vm.globalObject->defineDefaultProperty(QLatin1String("oracle"), obj);
- obj->defineDefaultProperty("answer", QV4::Primitive::fromInt32(32));
-
- QString code = QStringLiteral(
- "function doit() {\n"
- " ++oracle.answer\n"
- "}\n"
- "for (var i = 0; i < 10; ++i) doit()\n"
- "oracle.answer\n");
- QScopedPointer<QV4::Script> script;
- script.reset(new QV4::Script(ctx, QV4::Compiler::ContextType::Global, code, "setLookup"));
- script->parseAsBinding = false;
-
- QVERIFY(!scope.engine->hasException);
- script->parse();
- QVERIFY(!scope.engine->hasException);
-
- QV4::ScopedValue result(scope, script->run());
- QVERIFY(!scope.engine->hasException);
-
- QVERIFY(result->isInteger());
- QCOMPARE(result->int_32(), 42);
-}
-
-void tst_v4traced::construct()
-{
- EnvVarSaver forceTracing("QV4_FORCE_TRACING", "1");
- EnvVarSaver jitCallThreshold("QV4_JIT_CALL_THRESHOLD", "1");
-
- QV4::ExecutionEngine vm;
- QV4::Scope scope(&vm);
- QV4::ScopedContext ctx(scope, vm.rootContext());
- QV4::ScopedObject obj(scope, vm.newObject());
- vm.globalObject->defineDefaultProperty(QLatin1String("oracle"), obj);
- obj->defineDefaultProperty("answer", QV4::Primitive::fromInt32(32));
-
- QString code = QStringLiteral(
- "function doit() {\n"
- " this.arr = new Array()\n"
- " this.arr[0] = 0\n"
- " this.arr[1] = 1\n"
- " this.arr[2] = 2\n"
- "}\n"
- "var o\n"
- "for (var i = 0; i < 10; ++i) o = new doit()\n"
- "o.arr\n");
- QScopedPointer<QV4::Script> script;
- script.reset(new QV4::Script(ctx, QV4::Compiler::ContextType::Global, code, "setLookup"));
- script->parseAsBinding = false;
-
- QVERIFY(!scope.engine->hasException);
- script->parse();
- QVERIFY(!scope.engine->hasException);
-
- QV4::ScopedValue result(scope, script->run());
- QVERIFY(!scope.engine->hasException);
-
- QVERIFY(result->as<QV4::ArrayObject>());
-}
-
-QTEST_MAIN(tst_v4traced)
-
-#include "tst_v4traced.moc"
diff --git a/tests/auto/qml/v4traced/v4traced.pro b/tests/auto/qml/v4traced/v4traced.pro
deleted file mode 100644
index cbc8f38046..0000000000
--- a/tests/auto/qml/v4traced/v4traced.pro
+++ /dev/null
@@ -1,5 +0,0 @@
-CONFIG += testcase
-TARGET = tst_v4traced
-macos:CONFIG -= app_bundle
-QT += core-private qml-private testlib
-SOURCES += tst_v4traced.cpp
diff --git a/tests/auto/quick/examples/tst_examples.cpp b/tests/auto/quick/examples/tst_examples.cpp
index 9b3fa8fd2c..fdefa855e4 100644
--- a/tests/auto/quick/examples/tst_examples.cpp
+++ b/tests/auto/quick/examples/tst_examples.cpp
@@ -74,6 +74,7 @@ tst_examples::tst_examples()
{
// Add files to exclude here
excludedFiles << "snippets/qml/listmodel/listmodel.qml"; //Just a ListModel, no root QQuickItem
+ excludedFiles << "snippets/qml/tablemodel/fruit-example-delegatechooser.qml"; // Requires QtQuick.Controls import.
// Add directories you want excluded here
excludedDirs << "shared"; //Not an example
diff --git a/tests/auto/quick/pointerhandlers/pointerhandlers.pro b/tests/auto/quick/pointerhandlers/pointerhandlers.pro
index 950d6835eb..4d6311bdb2 100644
--- a/tests/auto/quick/pointerhandlers/pointerhandlers.pro
+++ b/tests/auto/quick/pointerhandlers/pointerhandlers.pro
@@ -10,4 +10,5 @@ qtConfig(private_tests) {
qquickpointerhandler \
qquickpointhandler \
qquicktaphandler \
+ qquickwheelhandler \
}
diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/nested.qml b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/nested.qml
new file mode 100644
index 0000000000..49e44f2b1f
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/nested.qml
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.14
+
+Rectangle {
+ width: 320; height: 240
+ color: "lightsteelblue"; antialiasing: true
+ border.color: outerWheelHandler.active ? "red" : "white"
+
+ WheelHandler {
+ id: outerWheelHandler
+ objectName: "outerWheelHandler"
+ property: "x"
+ }
+
+ Rectangle {
+ width: 120; height: 120; x: 100; y: 60
+ color: "beige"; antialiasing: true
+ border.color: innerWheelHandler.active ? "red" : "white"
+
+ WheelHandler {
+ id: innerWheelHandler
+ objectName: "innerWheelHandler"
+ // TODO should ideally deactivate because events go to the outer handler, not because of timeout
+ activeTimeout: 0.5
+ property: "x"
+ }
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml
new file mode 100644
index 0000000000..d4875d5313
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.14
+
+Rectangle {
+ width: 320; height: 240
+ color: "green"; antialiasing: true
+
+ Rectangle {
+ width: 100; height: 2; anchors.centerIn: parent
+ Rectangle {
+ width: 2; height: 100; anchors.centerIn: parent
+ }
+ }
+
+ WheelHandler {
+ activeTimeout: 0.5
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/qquickwheelhandler.pro b/tests/auto/quick/pointerhandlers/qquickwheelhandler/qquickwheelhandler.pro
new file mode 100644
index 0000000000..7509e38dd3
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/qquickwheelhandler.pro
@@ -0,0 +1,14 @@
+CONFIG += testcase
+TARGET = tst_qquickwheelhandler
+macos:CONFIG -= app_bundle
+
+SOURCES += tst_qquickwheelhandler.cpp
+OTHER_FILES = \
+ data/rectWheel.qml \
+
+include (../../../shared/util.pri)
+include (../../shared/util.pri)
+
+TESTDATA = data/*
+
+QT += core-private gui-private qml-private quick-private testlib
diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp b/tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp
new file mode 100644
index 0000000000..2abf2ea8c3
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp
@@ -0,0 +1,344 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QtTest/QSignalSpy>
+#include <QtGui/QStyleHints>
+#include <qpa/qwindowsysteminterface.h>
+#include <private/qquickwheelhandler_p.h>
+#include <QtQuick/private/qquickrectangle_p.h>
+#include <QtQuick/qquickview.h>
+#include <QtQml/qqmlcontext.h>
+#include "../../../shared/util.h"
+#include "../../shared/viewtestutil.h"
+
+Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests")
+
+class tst_QQuickWheelHandler: public QQmlDataTest
+{
+ Q_OBJECT
+public:
+ tst_QQuickWheelHandler() { }
+
+private slots:
+ void singleHandler_data();
+ void singleHandler();
+ void nestedHandler_data();
+ void nestedHandler();
+
+private:
+ void sendWheelEvent(QQuickView &window, QPoint pos, QPoint angleDelta,
+ QPoint pixelDelta = QPoint(), Qt::KeyboardModifiers modifiers = Qt::NoModifier,
+ Qt::ScrollPhase phase = Qt::NoScrollPhase, bool inverted = false);
+};
+
+void tst_QQuickWheelHandler::sendWheelEvent(QQuickView &window, QPoint pos, QPoint angleDelta,
+ QPoint pixelDelta, Qt::KeyboardModifiers modifiers, Qt::ScrollPhase phase, bool inverted)
+{
+ QWheelEvent wheelEvent(pos, window.mapToGlobal(pos), pixelDelta, angleDelta,
+ Qt::NoButton, modifiers, phase, inverted);
+ QGuiApplication::sendEvent(&window, &wheelEvent);
+ qApp->processEvents();
+}
+
+void tst_QQuickWheelHandler::singleHandler_data()
+{
+ // handler properties
+ QTest::addColumn<Qt::Orientation>("orientation");
+ QTest::addColumn<bool>("invertible");
+ QTest::addColumn<int>("rotationScale");
+ QTest::addColumn<QString>("property");
+ QTest::addColumn<qreal>("targetScaleMultiplier");
+ QTest::addColumn<bool>("targetTransformAroundCursor");
+ // event
+ QTest::addColumn<QPoint>("eventPos");
+ QTest::addColumn<QPoint>("eventAngleDelta");
+ QTest::addColumn<QPoint>("eventPixelDelta");
+ QTest::addColumn<Qt::KeyboardModifiers>("eventModifiers");
+ QTest::addColumn<bool>("eventPhases");
+ QTest::addColumn<bool>("eventInverted");
+ // result
+ QTest::addColumn<QPoint>("expectedPosition");
+ QTest::addColumn<qreal>("expectedScale");
+ QTest::addColumn<int>("expectedRotation");
+
+ // move the item
+ QTest::newRow("vertical wheel angle delta to adjust x")
+ << Qt::Vertical << false << 1 << "x" << 1.5 << true
+ << QPoint(160, 120) << QPoint(-360, 120) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false
+ << QPoint(15, 0) << 1.0 << 0;
+ QTest::newRow("horizontal wheel angle delta to adjust y")
+ << Qt::Horizontal << false << 1 << "y" << 1.5 << false
+ << QPoint(160, 120) << QPoint(-360, 120) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false
+ << QPoint(0, -45) << 1.0 << 0;
+ QTest::newRow("vertical wheel angle delta to adjust y, amplified and inverted")
+ << Qt::Vertical << true << 4 << "y" << 1.5 << true
+ << QPoint(160, 120) << QPoint(60, 60) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << true
+ << QPoint(0, 30) << 1.0 << 0;
+ QTest::newRow("horizontal wheel angle delta to adjust x, amplified and reversed")
+ << Qt::Horizontal << false << -4 << "x" << 1.5 << false
+ << QPoint(160, 120) << QPoint(60, 60) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false
+ << QPoint(-30, 0) << 1.0 << 0;
+ QTest::newRow("vertical wheel pixel delta to adjust x")
+ << Qt::Vertical << false << 1 << "x" << 1.5 << true
+ << QPoint(160, 120) << QPoint(-360, 120) << QPoint(20, 20) << Qt::KeyboardModifiers(Qt::NoModifier) << true << false
+ << QPoint(20, 0) << 1.0 << 0;
+ QTest::newRow("horizontal wheel pixel delta to adjust y")
+ << Qt::Horizontal << false << 1 << "y" << 1.5 << false
+ << QPoint(160, 120) << QPoint(-360, 120) << QPoint(20, 20) << Qt::KeyboardModifiers(Qt::NoModifier) << true << false
+ << QPoint(0, 20) << 1.0 << 0;
+ QTest::newRow("vertical wheel pixel delta to adjust y, amplified and inverted")
+ << Qt::Vertical << true << 4 << "y" << 1.5 << true
+ << QPoint(160, 120) << QPoint(60, 60) << QPoint(20, 20) << Qt::KeyboardModifiers(Qt::NoModifier) << true << true
+ << QPoint(0, 80) << 1.0 << 0;
+ QTest::newRow("horizontal wheel pixel delta to adjust x, amplified and reversed")
+ << Qt::Horizontal << false << -4 << "x" << 1.5 << false
+ << QPoint(160, 120) << QPoint(60, 60) << QPoint(20, 20) << Qt::KeyboardModifiers(Qt::NoModifier) << true << false
+ << QPoint(-80, 0) << 1.0 << 0;
+
+ // scale the item
+ QTest::newRow("vertical wheel angle delta to adjust scale")
+ << Qt::Vertical << false << 1 << "scale" << 1.5 << true
+ << QPoint(50, 32) << QPoint(360, 120) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false
+ << QPoint(55, 44) << 1.5 << 0;
+ QTest::newRow("horizontal wheel angle delta to adjust scale, amplified and reversed, don't adjust position")
+ << Qt::Horizontal << false << -2 << "scale" << 1.5 << false
+ << QPoint(50, 32) << QPoint(-240, 360) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false
+ << QPoint(0, 0) << 5.0625 << 0;
+
+ // rotate the item
+ QTest::newRow("vertical wheel angle delta to adjust rotation")
+ << Qt::Vertical << false << 1 << "rotation" << 1.5 << true
+ << QPoint(50, 32) << QPoint(360, -120) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false
+ << QPoint(19, -31) << 1.0 << -15;
+ QTest::newRow("horizontal wheel angle delta to adjust rotation, amplified and reversed, don't adjust position")
+ << Qt::Horizontal << false << -2 << "rotation" << 1.5 << false
+ << QPoint(80, 80) << QPoint(240, 360) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false
+ << QPoint(0, 0) << 1.0 << -60;
+}
+
+void tst_QQuickWheelHandler::singleHandler()
+{
+ // handler properties
+ QFETCH(Qt::Orientation, orientation);
+ QFETCH(bool, invertible);
+ QFETCH(int, rotationScale);
+ QFETCH(QString, property);
+ QFETCH(qreal, targetScaleMultiplier);
+ QFETCH(bool, targetTransformAroundCursor);
+ // event
+ QFETCH(QPoint, eventPos);
+ QFETCH(QPoint, eventAngleDelta);
+ QFETCH(QPoint, eventPixelDelta);
+ QFETCH(Qt::KeyboardModifiers, eventModifiers);
+ QFETCH(bool, eventPhases);
+ QFETCH(bool, eventInverted);
+ // result
+ QFETCH(QPoint, expectedPosition);
+ QFETCH(qreal, expectedScale);
+ QFETCH(int, expectedRotation);
+
+ QQuickView window;
+ QByteArray errorMessage;
+ QVERIFY2(QQuickTest::initView(window, testFileUrl("rectWheel.qml"), true, &errorMessage), errorMessage.constData());
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+ QQuickItem *rect = window.rootObject();
+ QVERIFY(rect != nullptr);
+ QQuickWheelHandler *handler = rect->findChild<QQuickWheelHandler*>();
+ QVERIFY(handler != nullptr);
+ handler->setOrientation(orientation);
+ handler->setInvertible(invertible);
+ handler->setRotationScale(rotationScale);
+ handler->setProperty(property);
+ handler->setTargetScaleMultiplier(targetScaleMultiplier);
+ handler->setTargetTransformAroundCursor(targetTransformAroundCursor);
+ QSignalSpy activeChangedSpy(handler, SIGNAL(activeChanged()));
+
+ if (eventPhases) {
+ sendWheelEvent(window, eventPos, QPoint(), QPoint(), eventModifiers, Qt::ScrollBegin, eventInverted);
+ sendWheelEvent(window, eventPos, eventAngleDelta, eventPixelDelta, eventModifiers, Qt::ScrollUpdate, eventInverted);
+ } else {
+ sendWheelEvent(window, eventPos, eventAngleDelta, eventPixelDelta, eventModifiers, Qt::NoScrollPhase, eventInverted);
+ }
+ QCOMPARE(rect->position().toPoint(), expectedPosition);
+ QCOMPARE(activeChangedSpy.count(), 1);
+ QCOMPARE(handler->active(), true);
+ QCOMPARE(rect->scale(), expectedScale);
+ QCOMPARE(rect->rotation(), expectedRotation);
+ if (!eventPhases) {
+ QTRY_COMPARE(handler->active(), false);
+ QCOMPARE(activeChangedSpy.count(), 2);
+ }
+
+ // restore by rotating backwards
+ if (eventPhases) {
+ sendWheelEvent(window, eventPos, eventAngleDelta * -1, eventPixelDelta * -1, eventModifiers, Qt::ScrollUpdate, eventInverted);
+ sendWheelEvent(window, eventPos, QPoint(), QPoint(), eventModifiers, Qt::ScrollEnd, eventInverted);
+ } else {
+ sendWheelEvent(window, eventPos, eventAngleDelta * -1, eventPixelDelta * -1, eventModifiers, Qt::NoScrollPhase, eventInverted);
+ }
+ QCOMPARE(activeChangedSpy.count(), eventPhases ? 2 : 3);
+ QCOMPARE(handler->active(), !eventPhases);
+ QCOMPARE(rect->position().toPoint(), QPoint(0, 0));
+ QCOMPARE(rect->scale(), 1);
+ QCOMPARE(rect->rotation(), 0);
+}
+
+void tst_QQuickWheelHandler::nestedHandler_data()
+{
+ // handler properties
+ QTest::addColumn<Qt::Orientation>("orientation");
+ QTest::addColumn<bool>("invertible");
+ QTest::addColumn<int>("rotationScale");
+ QTest::addColumn<QString>("property");
+ QTest::addColumn<qreal>("targetScaleMultiplier");
+ QTest::addColumn<bool>("targetTransformAroundCursor");
+ // event
+ QTest::addColumn<QPoint>("eventPos");
+ QTest::addColumn<QPoint>("eventAngleDelta");
+ QTest::addColumn<QPoint>("eventPixelDelta");
+ QTest::addColumn<Qt::KeyboardModifiers>("eventModifiers");
+ QTest::addColumn<bool>("eventPhases");
+ QTest::addColumn<bool>("eventInverted");
+ QTest::addColumn<int>("eventCount");
+ // result: inner handler
+ QTest::addColumn<QPoint>("innerPosition");
+ QTest::addColumn<qreal>("innerScale");
+ QTest::addColumn<int>("innerRotation");
+ // result: outer handler
+ QTest::addColumn<QPoint>("outerPosition");
+ QTest::addColumn<qreal>("outerScale");
+ QTest::addColumn<int>("outerRotation");
+
+ // move the item
+ QTest::newRow("vertical wheel angle delta to adjust x")
+ << Qt::Vertical << false << 1 << "x" << 1.5 << true
+ << QPoint(160, 120) << QPoint(120, 120) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false << 10
+ << QPoint(175,60) << 1.0 << 0
+ << QPoint(75, 0) << 1.0 << 0;
+ QTest::newRow("horizontal wheel pixel delta to adjust y")
+ << Qt::Horizontal << false << 1 << "y" << 1.5 << false
+ << QPoint(160, 120) << QPoint(120, 120) << QPoint(50, 50) << Qt::KeyboardModifiers(Qt::NoModifier) << true << false << 4
+ << QPoint(100, 160) << 1.0 << 0
+ << QPoint(0, 100) << 1.0 << 0;
+}
+
+void tst_QQuickWheelHandler::nestedHandler()
+{
+ // handler properties
+ QFETCH(Qt::Orientation, orientation);
+ QFETCH(bool, invertible);
+ QFETCH(int, rotationScale);
+ QFETCH(QString, property);
+ QFETCH(qreal, targetScaleMultiplier);
+ QFETCH(bool, targetTransformAroundCursor);
+ // event
+ QFETCH(QPoint, eventPos);
+ QFETCH(QPoint, eventAngleDelta);
+ QFETCH(QPoint, eventPixelDelta);
+ QFETCH(Qt::KeyboardModifiers, eventModifiers);
+ QFETCH(bool, eventPhases);
+ QFETCH(bool, eventInverted);
+ QFETCH(int, eventCount);
+ // result: inner handler
+ QFETCH(QPoint, innerPosition);
+ QFETCH(qreal, innerScale);
+ QFETCH(int, innerRotation);
+ // result: outer handler
+ QFETCH(QPoint, outerPosition);
+ QFETCH(qreal, outerScale);
+ QFETCH(int, outerRotation);
+
+ QQuickView window;
+ QByteArray errorMessage;
+ QVERIFY2(QQuickTest::initView(window, testFileUrl("nested.qml"), true, &errorMessage), errorMessage.constData());
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+ QQuickItem *outerRect = window.rootObject();
+ QVERIFY(outerRect != nullptr);
+ QQuickWheelHandler *outerHandler = outerRect->findChild<QQuickWheelHandler*>("outerWheelHandler");
+ QVERIFY(outerHandler != nullptr);
+ QQuickWheelHandler *innerHandler = outerRect->findChild<QQuickWheelHandler*>("innerWheelHandler");
+ QVERIFY(innerHandler != nullptr);
+ QQuickItem *innerRect = innerHandler->parentItem();
+ QVERIFY(innerRect != nullptr);
+ innerHandler->setOrientation(orientation);
+ innerHandler->setInvertible(invertible);
+ innerHandler->setRotationScale(rotationScale);
+ innerHandler->setProperty(property);
+ innerHandler->setTargetScaleMultiplier(targetScaleMultiplier);
+ innerHandler->setTargetTransformAroundCursor(targetTransformAroundCursor);
+ outerHandler->setOrientation(orientation);
+ outerHandler->setInvertible(invertible);
+ outerHandler->setRotationScale(rotationScale);
+ outerHandler->setProperty(property);
+ outerHandler->setTargetScaleMultiplier(targetScaleMultiplier);
+ outerHandler->setTargetTransformAroundCursor(targetTransformAroundCursor);
+ QSignalSpy innerActiveChangedSpy(innerHandler, SIGNAL(activeChanged()));
+ QSignalSpy outerActiveChangedSpy(outerHandler, SIGNAL(activeChanged()));
+
+ if (eventPhases)
+ sendWheelEvent(window, eventPos, QPoint(), QPoint(), eventModifiers, Qt::ScrollBegin, eventInverted);
+ for (int i = 0; i < eventCount; ++i)
+ sendWheelEvent(window, eventPos, eventAngleDelta, eventPixelDelta, eventModifiers,
+ (eventPhases ? Qt::ScrollUpdate : Qt::NoScrollPhase), eventInverted);
+ QCOMPARE(innerRect->position().toPoint(), innerPosition);
+
+ /*
+ If outer is activated, maybe inner should be deactivated? But the event
+ doesn't get delivered to inner anymore, so it doesn't find out that
+ it's no longer getting events. It will get deactivated after the
+ timeout, just as if the user stopped scrolling.
+
+ This situation is similar to QTBUG-50199, but it's questionable whether
+ that was really so important. So far in Qt Quick, if you move the mouse
+ while wheel momentum continues, or if the item moves out from under the
+ mouse, a different item starts getting the events immediately. In
+ non-Qt applications on most OSes, that's quite normal.
+ */
+ // QCOMPARE(innerActiveChangedSpy.count(), 2);
+ // QCOMPARE(innerHandler->active(), false);
+ QCOMPARE(innerRect->scale(), innerScale);
+ QCOMPARE(innerRect->rotation(), innerRotation);
+ QCOMPARE(outerRect->position().toPoint(), outerPosition);
+ QCOMPARE(outerActiveChangedSpy.count(), 1);
+ QCOMPARE(outerHandler->active(), true);
+ QCOMPARE(outerRect->scale(), outerScale);
+ QCOMPARE(outerRect->rotation(), outerRotation);
+ if (!eventPhases) {
+ QTRY_COMPARE(outerHandler->active(), false);
+ QCOMPARE(outerActiveChangedSpy.count(), 2);
+ }
+}
+
+QTEST_MAIN(tst_QQuickWheelHandler)
+
+#include "tst_qquickwheelhandler.moc"
diff --git a/tests/auto/quick/propertyrequirements/tst_propertyrequirements.cpp b/tests/auto/quick/propertyrequirements/tst_propertyrequirements.cpp
index 34be4d98b4..5781a007b6 100644
--- a/tests/auto/quick/propertyrequirements/tst_propertyrequirements.cpp
+++ b/tests/auto/quick/propertyrequirements/tst_propertyrequirements.cpp
@@ -28,7 +28,6 @@
#include <qtest.h>
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlengine.h>
-#include <QtQml/private/qhashedstring_p.h>
#include <QtQml/private/qqmlmetatype_p.h>
#include <QtCore/QDebug>
#include <QtCore/QHash>
diff --git a/tests/auto/quick/qquickanchors/tst_qquickanchors.cpp b/tests/auto/quick/qquickanchors/tst_qquickanchors.cpp
index 77fa1292c4..128a154492 100644
--- a/tests/auto/quick/qquickanchors/tst_qquickanchors.cpp
+++ b/tests/auto/quick/qquickanchors/tst_qquickanchors.cpp
@@ -364,7 +364,7 @@ void tst_qquickanchors::reset()
const QMetaObject *meta = itemPrivate->anchors()->metaObject();
QMetaProperty p = meta->property(meta->indexOfProperty(side.toUtf8().constData()));
- QVERIFY(p.write(itemPrivate->anchors(), qVariantFromValue(anchorLine)));
+ QVERIFY(p.write(itemPrivate->anchors(), QVariant::fromValue(anchorLine)));
QCOMPARE(itemPrivate->anchors()->usedAnchors().testFlag(anchor), true);
QVERIFY(p.reset(itemPrivate->anchors()));
@@ -423,7 +423,7 @@ void tst_qquickanchors::nullItem()
QMetaProperty p = meta->property(meta->indexOfProperty(side.toUtf8().constData()));
QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML Item: Cannot anchor to a null item.");
- QVERIFY(p.write(itemPrivate->anchors(), qVariantFromValue(anchor)));
+ QVERIFY(p.write(itemPrivate->anchors(), QVariant::fromValue(anchor)));
delete item;
}
diff --git a/tests/auto/quick/qquickanimations/qquickanimations.pro b/tests/auto/quick/qquickanimations/qquickanimations.pro
index 8bb1f47af5..cd0a0fbb15 100644
--- a/tests/auto/quick/qquickanimations/qquickanimations.pro
+++ b/tests/auto/quick/qquickanimations/qquickanimations.pro
@@ -8,7 +8,7 @@ macx:CONFIG -= app_bundle
TESTDATA = data/*
-QT += core-private gui-private qml-private quick-private testlib
+QT += core-private gui-private qml-private quick-private testlib qmlmodels-private
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
OTHER_FILES += \
diff --git a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
index 961506372a..b75dc8bc6b 100644
--- a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
+++ b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
@@ -30,7 +30,7 @@
#include <QtQml/qqmlcomponent.h>
#include <QtQuick/qquickview.h>
#include <QtQml/private/qqmltimer_p.h>
-#include <QtQml/private/qqmllistmodel_p.h>
+#include <QtQmlModels/private/qqmllistmodel_p.h>
#include <QtQml/private/qanimationgroupjob_p.h>
#include <QtQuick/private/qquickrectangle_p.h>
#include <QtQuick/private/qquickitemanimation_p.h>
diff --git a/tests/auto/quick/qquickboundaryrule/data/dragHandler.qml b/tests/auto/quick/qquickboundaryrule/data/dragHandler.qml
new file mode 100644
index 0000000000..c66fd76ff1
--- /dev/null
+++ b/tests/auto/quick/qquickboundaryrule/data/dragHandler.qml
@@ -0,0 +1,23 @@
+import QtQuick 2.14
+import Qt.labs.animation 1.0
+
+Rectangle {
+ id: root
+ width: 240; height: 120
+ color: "green"
+
+ DragHandler {
+ id: dragHandler
+ yAxis.minimum: -1000
+ xAxis.minimum: -1000
+ onActiveChanged: if (!active) xbr.returnToBounds();
+ }
+
+ BoundaryRule on x {
+ id: xbr
+ minimum: -50
+ maximum: 100
+ minimumOvershoot: 40
+ maximumOvershoot: 40
+ }
+}
diff --git a/tests/auto/quick/qquickboundaryrule/qquickboundaryrule.pro b/tests/auto/quick/qquickboundaryrule/qquickboundaryrule.pro
new file mode 100644
index 0000000000..ef43f4526a
--- /dev/null
+++ b/tests/auto/quick/qquickboundaryrule/qquickboundaryrule.pro
@@ -0,0 +1,12 @@
+CONFIG += testcase
+TARGET = tst_qquickboundaryrule
+macx:CONFIG -= app_bundle
+
+SOURCES += tst_qquickboundaryrule.cpp
+
+include (../../shared/util.pri)
+include (../shared/util.pri)
+
+TESTDATA = data/*
+
+QT += core-private gui-private qml-private quick-private testlib
diff --git a/tests/auto/quick/qquickboundaryrule/tst_qquickboundaryrule.cpp b/tests/auto/quick/qquickboundaryrule/tst_qquickboundaryrule.cpp
new file mode 100644
index 0000000000..44f1c9a2f9
--- /dev/null
+++ b/tests/auto/quick/qquickboundaryrule/tst_qquickboundaryrule.cpp
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QtTest/QtTest>
+#include <qsignalspy.h>
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlcomponent.h>
+#include <QtQuick/qquickview.h>
+#include <QtQuick/private/qquickboundaryrule_p.h>
+#include <QtQuick/private/qquickdraghandler_p.h>
+#include "../../shared/util.h"
+#include "../shared/viewtestutil.h"
+
+class tst_qquickboundaryrule : public QQmlDataTest
+{
+ Q_OBJECT
+public:
+ tst_qquickboundaryrule() {}
+
+private slots:
+ void init() { qApp->processEvents(); } //work around animation timer bug (QTBUG-22865)
+ void dragHandler();
+};
+
+void tst_qquickboundaryrule::dragHandler()
+{
+ QQuickView window;
+ QByteArray errorMessage;
+ QVERIFY2(QQuickTest::initView(window, testFileUrl("dragHandler.qml"), true, &errorMessage), errorMessage.constData());
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+ QQuickItem *target = window.rootObject();
+ QVERIFY(target);
+ QQuickDragHandler *dragHandler = target->findChild<QQuickDragHandler*>();
+ QVERIFY(dragHandler);
+ QQuickBoundaryRule *boundaryRule = target->findChild<QQuickBoundaryRule*>();
+ QVERIFY(boundaryRule);
+ QSignalSpy overshootChangedSpy(boundaryRule, SIGNAL(currentOvershootChanged()));
+
+ QPoint p1(10, 10);
+ QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, p1);
+ // unrestricted drag
+ p1 += QPoint(100, 0);
+ QTest::mouseMove(&window, p1);
+ QTRY_VERIFY(dragHandler->active());
+ QCOMPARE(target->position().x(), 100);
+ QCOMPARE(boundaryRule->currentOvershoot(), 0);
+ QCOMPARE(boundaryRule->peakOvershoot(), 0);
+ QCOMPARE(overshootChangedSpy.count(), 0);
+ // restricted drag: halfway into overshoot
+ p1 += QPoint(20, 0);
+ QTest::mouseMove(&window, p1);
+ QCOMPARE(target->position().x(), 117.5);
+ QCOMPARE(boundaryRule->currentOvershoot(), 20);
+ QCOMPARE(boundaryRule->peakOvershoot(), 20);
+ QCOMPARE(overshootChangedSpy.count(), 1);
+ // restricted drag: maximum overshoot
+ p1 += QPoint(80, 0);
+ QTest::mouseMove(&window, p1);
+ QCOMPARE(target->position().x(), 140);
+ QCOMPARE(boundaryRule->currentOvershoot(), 100);
+ QCOMPARE(boundaryRule->peakOvershoot(), 100);
+ QCOMPARE(overshootChangedSpy.count(), 2);
+ // release and let it return to bounds
+ QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, p1);
+ QTRY_COMPARE(dragHandler->active(), false);
+ QTRY_COMPARE(overshootChangedSpy.count(), 3);
+ QCOMPARE(boundaryRule->currentOvershoot(), 0);
+ QCOMPARE(boundaryRule->peakOvershoot(), 0);
+ QCOMPARE(target->position().x(), 100);
+}
+
+QTEST_MAIN(tst_qquickboundaryrule)
+
+#include "tst_qquickboundaryrule.moc"
diff --git a/tests/auto/quick/qquickgridview/qquickgridview.pro b/tests/auto/quick/qquickgridview/qquickgridview.pro
index 5051f8bc62..0390637058 100644
--- a/tests/auto/quick/qquickgridview/qquickgridview.pro
+++ b/tests/auto/quick/qquickgridview/qquickgridview.pro
@@ -10,5 +10,5 @@ include (../shared/util.pri)
TESTDATA = data/*
-QT += core-private gui-private qml-private quick-private testlib qmltest
+QT += core-private gui-private qml-private quick-private testlib qmltest qmlmodels-private
diff --git a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
index 448096720c..b65b8770b1 100644
--- a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
+++ b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
@@ -40,7 +40,7 @@
#include <QtQuick/private/qquickitemview_p_p.h>
#include <QtQuick/private/qquickgridview_p.h>
#include <QtQuick/private/qquicktext_p.h>
-#include <QtQml/private/qqmllistmodel_p.h>
+#include <QtQmlModels/private/qqmllistmodel_p.h>
#include "../../shared/util.h"
#include "../shared/viewtestutil.h"
#include "../shared/visualtestutil.h"
diff --git a/tests/auto/quick/qquicklistview/qquicklistview.pro b/tests/auto/quick/qquicklistview/qquicklistview.pro
index fd96c269a2..b08fca2b1d 100644
--- a/tests/auto/quick/qquicklistview/qquicklistview.pro
+++ b/tests/auto/quick/qquicklistview/qquicklistview.pro
@@ -18,5 +18,5 @@ include (../shared/util.pri)
TESTDATA = data/*
DISTFILES += data/*
-QT += core-private gui-private qml-private quick-private testlib qmltest
+QT += core-private gui-private qml-private quick-private testlib qmltest qmlmodels-private
diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
index 2ea8a477a8..894a5ee16e 100644
--- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
+++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
@@ -40,9 +40,9 @@
#include <QtQuick/private/qquicklistview_p.h>
#include <QtQuick/private/qquickmousearea_p.h>
#include <QtQuick/private/qquicktext_p.h>
-#include <QtQml/private/qqmlobjectmodel_p.h>
-#include <QtQml/private/qqmllistmodel_p.h>
-#include <QtQml/private/qqmldelegatemodel_p.h>
+#include <QtQmlModels/private/qqmlobjectmodel_p.h>
+#include <QtQmlModels/private/qqmllistmodel_p.h>
+#include <QtQmlModels/private/qqmldelegatemodel_p.h>
#include "../../shared/util.h"
#include "../shared/viewtestutil.h"
#include "../shared/visualtestutil.h"
diff --git a/tests/auto/quick/qquickpathview/qquickpathview.pro b/tests/auto/quick/qquickpathview/qquickpathview.pro
index f21fb64fa4..5eb24b89bd 100644
--- a/tests/auto/quick/qquickpathview/qquickpathview.pro
+++ b/tests/auto/quick/qquickpathview/qquickpathview.pro
@@ -9,5 +9,5 @@ include (../shared/util.pri)
TESTDATA = data/*
-QT += core-private gui-private qml-private quick-private testlib qmltest
+QT += core-private gui-private qml-private quick-private testlib qmltest qmlmodels-private
qtHaveModule(widgets): QT += widgets
diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
index 1a5ce39318..a1b2b64ae2 100644
--- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
+++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
@@ -39,7 +39,7 @@
#include <QtQuick/private/qquicktext_p.h>
#include <QtQuick/private/qquickrectangle_p.h>
#include <QtQuickTest/QtQuickTest>
-#include <QtQml/private/qqmllistmodel_p.h>
+#include <QtQmlModels/private/qqmllistmodel_p.h>
#include <QtQml/private/qqmlvaluetype_p.h>
#include <QtGui/qstandarditemmodel.h>
#include <QStringListModel>
diff --git a/tests/auto/quick/qquickrepeater/qquickrepeater.pro b/tests/auto/quick/qquickrepeater/qquickrepeater.pro
index 5554342943..aed5702266 100644
--- a/tests/auto/quick/qquickrepeater/qquickrepeater.pro
+++ b/tests/auto/quick/qquickrepeater/qquickrepeater.pro
@@ -9,4 +9,4 @@ include (../shared/util.pri)
TESTDATA = data/*
-QT += core-private gui-private qml-private quick-private testlib
+QT += core-private gui-private qml-private quick-private testlib qmlmodels-private
diff --git a/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp b/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp
index e4b427f6ec..65e7d29595 100644
--- a/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp
+++ b/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp
@@ -35,8 +35,8 @@
#include <QtQml/qqmlincubator.h>
#include <private/qquickrepeater_p.h>
#include <QtQuick/private/qquicktext_p.h>
-#include <QtQml/private/qqmllistmodel_p.h>
-#include <QtQml/private/qqmlobjectmodel_p.h>
+#include <QtQmlModels/private/qqmllistmodel_p.h>
+#include <QtQmlModels/private/qqmlobjectmodel_p.h>
#include <QtGui/qstandarditemmodel.h>
#include "../../shared/util.h"
@@ -899,15 +899,15 @@ void tst_QQuickRepeater::destroyCount()
QQuickRepeater *repeater = findItem<QQuickRepeater>(rootObject, "repeater");
QVERIFY(repeater);
- repeater->setProperty("model", qVariantFromValue<int>(3));
+ repeater->setProperty("model", QVariant::fromValue<int>(3));
QCOMPARE(repeater->property("componentCount").toInt(), 3);
- repeater->setProperty("model", qVariantFromValue<int>(0));
+ repeater->setProperty("model", QVariant::fromValue<int>(0));
QCOMPARE(repeater->property("componentCount").toInt(), 0);
- repeater->setProperty("model", qVariantFromValue<int>(4));
+ repeater->setProperty("model", QVariant::fromValue<int>(4));
QCOMPARE(repeater->property("componentCount").toInt(), 4);
QStringListModel model;
- repeater->setProperty("model", qVariantFromValue<QStringListModel *>(&model));
+ repeater->setProperty("model", QVariant::fromValue<QStringListModel *>(&model));
QCOMPARE(repeater->property("componentCount").toInt(), 0);
QStringList list;
list << "1" << "2" << "3" << "4";
@@ -915,7 +915,7 @@ void tst_QQuickRepeater::destroyCount()
QCOMPARE(repeater->property("componentCount").toInt(), 4);
model.insertRows(2,1);
QModelIndex index = model.index(2);
- model.setData(index, qVariantFromValue<QString>(QStringLiteral("foobar")));
+ model.setData(index, QVariant::fromValue<QString>(QStringLiteral("foobar")));
QCOMPARE(repeater->property("componentCount").toInt(), 5);
model.removeRows(2,1);
diff --git a/tests/auto/quick/qquicktableview/data/syncviewsimple.qml b/tests/auto/quick/qquicktableview/data/syncviewsimple.qml
new file mode 100644
index 0000000000..012cceaa9d
--- /dev/null
+++ b/tests/auto/quick/qquicktableview/data/syncviewsimple.qml
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.14
+import QtQuick.Window 2.3
+
+Item {
+ width: 640
+ height: 450
+
+ property alias tableView: mainTableView
+ property alias tableViewH: tableViewH
+ property alias tableViewV: tableViewV
+ property alias tableViewHV: tableViewHV
+
+ Column {
+ spacing: 10
+ TableView {
+ id: tableViewH
+ objectName: "Hor"
+ width: 600
+ height: 100
+ anchors.margins: 1
+ clip: true
+ delegate: tableViewDelegate
+ syncView: mainTableView
+ syncDirection: Qt.Horizontal
+ }
+
+ TableView {
+ id: tableViewV
+ objectName: "Ver"
+ width: 600
+ height: 100
+ anchors.margins: 1
+ clip: true
+ delegate: tableViewDelegate
+ syncView: mainTableView
+ syncDirection: Qt.Vertical
+ }
+
+ TableView {
+ id: tableViewHV
+ objectName: "HorVer"
+ width: 600
+ height: 100
+ anchors.margins: 1
+ clip: true
+ delegate: tableViewDelegate
+ syncView: mainTableView
+ }
+
+ TableView {
+ id: mainTableView
+ objectName: "root"
+ width: 600
+ height: 100
+ anchors.margins: 1
+ clip: true
+ delegate: tableViewDelegate
+ columnSpacing: 1
+ rowSpacing: 1
+
+ columnWidthProvider: function(c) { return 50 + c }
+ rowHeightProvider: function(r) { return 25 + r }
+ }
+ }
+
+ Component {
+ id: tableViewDelegate
+ Rectangle {
+ objectName: "tableViewDelegate"
+ color: "lightgray"
+ border.width: 1
+ implicitWidth: 30
+ implicitHeight: 60
+
+ Text {
+ anchors.centerIn: parent
+ font.pixelSize: 10
+ text: parent.TableView.view.objectName + "\n" + column + ", " + row
+ }
+ }
+ }
+
+}
diff --git a/tests/auto/quick/qquicktableview/qquicktableview.pro b/tests/auto/quick/qquicktableview/qquicktableview.pro
index cf831ed5b5..da0c0b01d0 100644
--- a/tests/auto/quick/qquicktableview/qquicktableview.pro
+++ b/tests/auto/quick/qquicktableview/qquicktableview.pro
@@ -11,5 +11,5 @@ include (../shared/util.pri)
TESTDATA = data/*
-QT += core-private gui-private qml-private quick-private testlib
+QT += core-private gui-private qml-private quick-private testlib qmlmodels-private
diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
index 60d48bb59f..d03f08ec6a 100644
--- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
+++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
@@ -38,8 +38,8 @@
#include <QtQml/qqmlcontext.h>
#include <QtQml/qqmlexpression.h>
#include <QtQml/qqmlincubator.h>
-#include <QtQml/private/qqmlobjectmodel_p.h>
-#include <QtQml/private/qqmllistmodel_p.h>
+#include <QtQmlModels/private/qqmlobjectmodel_p.h>
+#include <QtQmlModels/private/qqmllistmodel_p.h>
#include "testmodel.h"
@@ -57,17 +57,17 @@ static const char *kModelDataBindingProp = "modelDataBinding";
Q_DECLARE_METATYPE(QMarginsF);
-#define DECLARE_TABLEVIEW_VARIABLES \
- auto tableView = view->rootObject()->property(kTableViewPropName).value<QQuickTableView *>(); \
- QVERIFY(tableView); \
- auto tableViewPrivate = QQuickTableViewPrivate::get(tableView); \
- Q_UNUSED(tableViewPrivate)
+#define GET_QML_TABLEVIEW(PROPNAME) \
+ auto PROPNAME = view->rootObject()->property(#PROPNAME).value<QQuickTableView *>(); \
+ QVERIFY(PROPNAME); \
+ auto PROPNAME ## Private = QQuickTableViewPrivate::get(PROPNAME); \
+ Q_UNUSED(PROPNAME ## Private)
#define LOAD_TABLEVIEW(fileName) \
view->setSource(testFileUrl(fileName)); \
view->show(); \
QVERIFY(QTest::qWaitForWindowActive(view)); \
- DECLARE_TABLEVIEW_VARIABLES
+ GET_QML_TABLEVIEW(tableView)
#define LOAD_TABLEVIEW_ASYNC(fileName) \
view->setSource(testFileUrl("asyncloader.qml")); \
@@ -77,11 +77,12 @@ Q_DECLARE_METATYPE(QMarginsF);
loader->setSource(QUrl::fromLocalFile("data/" fileName)); \
QTRY_VERIFY(loader->item()); \
QCOMPARE(loader->status(), QQuickLoader::Status::Ready); \
- DECLARE_TABLEVIEW_VARIABLES
+ GET_QML_TABLEVIEW(tableView)
-#define WAIT_UNTIL_POLISHED \
- QVERIFY(QQuickTest::qIsPolishScheduled(tableView)); \
- QVERIFY(QQuickTest::qWaitForItemPolished(tableView))
+#define WAIT_UNTIL_POLISHED_ARG(item) \
+ QVERIFY(QQuickTest::qIsPolishScheduled(item)); \
+ QVERIFY(QQuickTest::qWaitForItemPolished(item))
+#define WAIT_UNTIL_POLISHED WAIT_UNTIL_POLISHED_ARG(tableView)
class tst_QQuickTableView : public QQmlDataTest
{
@@ -122,6 +123,9 @@ private slots:
void checkContentWidthAndHeight();
void checkPageFlicking();
void checkExplicitContentWidthAndHeight();
+ void checkExtents_origin();
+ void checkExtents_endExtent();
+ void checkExtents_moveTableToEdge();
void checkContentXY();
void noDelegate();
void changeDelegateDuringUpdate();
@@ -162,6 +166,13 @@ private slots:
void hideRowsAndColumns_data();
void hideRowsAndColumns();
void checkThatRevisionedPropertiesCannotBeUsedInOldImports();
+ void checkSyncView_rootView_data();
+ void checkSyncView_rootView();
+ void checkSyncView_childViews_data();
+ void checkSyncView_childViews();
+ void checkSyncView_differentSizedModels();
+ void checkSyncView_connect_late_data();
+ void checkSyncView_connect_late();
};
tst_QQuickTableView::tst_QQuickTableView()
@@ -601,8 +612,7 @@ void tst_QQuickTableView::checkContentWidthAndHeight()
// Since we move the viewport more than a page, tableview
// will jump to the new position and do a rebuild.
- QVERIFY(tableViewPrivate->polishScheduled);
- QVERIFY(tableViewPrivate->rebuildScheduled);
+ QVERIFY(tableViewPrivate->scheduledRebuildOptions);
WAIT_UNTIL_POLISHED;
// Check that the average cell size is now matching the
@@ -654,7 +664,7 @@ void tst_QQuickTableView::checkContentWidthAndHeight()
// Since we move the viewport more than a page, tableview
// will jump to the new position and do a rebuild.
QVERIFY(tableViewPrivate->polishScheduled);
- QVERIFY(tableViewPrivate->rebuildScheduled);
+ QVERIFY(tableViewPrivate->scheduledRebuildOptions);
WAIT_UNTIL_POLISHED;
// We should now have the same content width/height as when we started
@@ -689,7 +699,6 @@ void tst_QQuickTableView::checkPageFlicking()
QCOMPARE(tableViewPrivate->averageEdgeSize.width(), cellWidth);
QCOMPARE(tableViewPrivate->averageEdgeSize.height(), cellHeight);
- QVERIFY(!tableViewPrivate->rebuildScheduled);
QCOMPARE(tableViewPrivate->scheduledRebuildOptions, QQuickTableViewPrivate::RebuildOption::None);
// Flick 5000 columns to the right, and check that this triggers a
@@ -699,7 +708,6 @@ void tst_QQuickTableView::checkPageFlicking()
const qreal flickToColumnInPixels = ((cellWidth + columnSpacing) * flickToColumn) - columnSpacing;
tableView->setContentX(flickToColumnInPixels);
- QVERIFY(tableViewPrivate->rebuildScheduled);
QVERIFY(tableViewPrivate->scheduledRebuildOptions & QQuickTableViewPrivate::RebuildOption::ViewportOnly);
QVERIFY(tableViewPrivate->scheduledRebuildOptions & QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftColumn);
QVERIFY(!(tableViewPrivate->scheduledRebuildOptions & QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftRow));
@@ -721,7 +729,6 @@ void tst_QQuickTableView::checkPageFlicking()
const qreal flickToRowInPixels = ((cellHeight + rowSpacing) * flickToRow) - rowSpacing;
tableView->setContentY(flickToRowInPixels);
- QVERIFY(tableViewPrivate->rebuildScheduled);
QVERIFY(tableViewPrivate->scheduledRebuildOptions & QQuickTableViewPrivate::RebuildOption::ViewportOnly);
QVERIFY(!(tableViewPrivate->scheduledRebuildOptions & QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftColumn));
QVERIFY(tableViewPrivate->scheduledRebuildOptions & QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftRow);
@@ -756,6 +763,164 @@ void tst_QQuickTableView::checkExplicitContentWidthAndHeight()
QCOMPARE(tableView->contentHeight(), 1000);
}
+void tst_QQuickTableView::checkExtents_origin()
+{
+ // Check that if the beginning of the content view doesn't match the
+ // actual size of the table, origin will be adjusted to make it fit.
+ LOAD_TABLEVIEW("contentwidthheight.qml");
+
+ const int rows = 10;
+ const int columns = rows;
+ const qreal columnWidth = 100;
+ const qreal rowHeight = 100;
+ const qreal actualTableSize = columns * columnWidth;
+
+ // Set a content size that is far too large
+ // compared to the size of the table.
+ tableView->setContentWidth(actualTableSize * 2);
+ tableView->setContentHeight(actualTableSize * 2);
+ tableView->setRowSpacing(0);
+ tableView->setColumnSpacing(0);
+ tableView->setLeftMargin(0);
+ tableView->setRightMargin(0);
+ tableView->setTopMargin(0);
+ tableView->setBottomMargin(0);
+
+ auto model = TestModelAsVariant(rows, columns);
+ tableView->setModel(model);
+
+ WAIT_UNTIL_POLISHED;
+
+ // Flick slowly to column 5 (to avoid rebuilds). Flick two columns at a
+ // time to ensure that we create a gap before TableView gets a chance to
+ // adjust endExtent first. This gap on the right side will make TableView
+ // move the table to move to the edge. Because of this, the table will not
+ // be aligned at the start of the content view when we next flick back again.
+ // And this will cause origin to move.
+ for (int x = 0; x <= 6; x += 2) {
+ tableView->setContentX(x * columnWidth);
+ tableView->setContentY(x * rowHeight);
+ }
+
+ // Check that the table has now been moved one column to the right
+ // (One column because that's how far outside the table we ended up flicking above).
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect.right(), actualTableSize + columnWidth);
+
+ // Flick back one column at a time so that TableView detects that the first
+ // column is not at the origin before the "table move" logic kicks in. This
+ // will make TableView adjust the origin.
+ for (int x = 6; x >= 0; x -= 1) {
+ tableView->setContentX(x * columnWidth);
+ tableView->setContentY(x * rowHeight);
+ }
+
+ // The origin will be moved with the same offset that the table was
+ // moved on the right side earlier, which is one column length.
+ QCOMPARE(tableViewPrivate->origin.x(), columnWidth);
+ QCOMPARE(tableViewPrivate->origin.y(), rowHeight);
+}
+
+void tst_QQuickTableView::checkExtents_endExtent()
+{
+ // Check that if we the content view size doesn't match the actual size
+ // of the table, endExtent will be adjusted to make it fit (so that
+ // e.g the the flicking will bounce to a stop at the edge of the table).
+ LOAD_TABLEVIEW("contentwidthheight.qml");
+
+ const int rows = 10;
+ const int columns = rows;
+ const qreal columnWidth = 100;
+ const qreal rowHeight = 100;
+ const qreal actualTableSize = columns * columnWidth;
+
+ // Set a content size that is far too large
+ // compared to the size of the table.
+ tableView->setContentWidth(actualTableSize * 2);
+ tableView->setContentHeight(actualTableSize * 2);
+ tableView->setRowSpacing(0);
+ tableView->setColumnSpacing(0);
+ tableView->setLeftMargin(0);
+ tableView->setRightMargin(0);
+ tableView->setTopMargin(0);
+ tableView->setBottomMargin(0);
+
+ auto model = TestModelAsVariant(rows, columns);
+ tableView->setModel(model);
+
+ WAIT_UNTIL_POLISHED;
+
+ // Flick slowly to column 5 (to avoid rebuilds). This will flick the table to
+ // the last column in the model. But since there still is a lot space left in
+ // the content view, endExtent will be set accordingly to compensate.
+ for (int x = 1; x <= 5; x++)
+ tableView->setContentX(x * columnWidth);
+ QCOMPARE(tableViewPrivate->rightColumn(), columns - 1);
+ qreal expectedEndExtentWidth = actualTableSize - tableView->contentWidth();
+ QCOMPARE(tableViewPrivate->endExtent.width(), expectedEndExtentWidth);
+
+ for (int y = 1; y <= 5; y++)
+ tableView->setContentY(y * rowHeight);
+ QCOMPARE(tableViewPrivate->bottomRow(), rows - 1);
+ qreal expectedEndExtentHeight = actualTableSize - tableView->contentHeight();
+ QCOMPARE(tableViewPrivate->endExtent.height(), expectedEndExtentHeight);
+}
+
+void tst_QQuickTableView::checkExtents_moveTableToEdge()
+{
+ // Check that if we the content view size doesn't match the actual
+ // size of the table, and we fast-flick the viewport to outside
+ // the table, we end up moving the table back into the viewport to
+ // avoid any visual glitches.
+ LOAD_TABLEVIEW("contentwidthheight.qml");
+
+ const int rows = 10;
+ const int columns = rows;
+ const qreal columnWidth = 100;
+ const qreal rowHeight = 100;
+ const qreal actualTableSize = columns * columnWidth;
+
+ // Set a content size that is far to large
+ // compared to the size of the table.
+ tableView->setContentWidth(actualTableSize * 2);
+ tableView->setContentHeight(actualTableSize * 2);
+ tableView->setRowSpacing(0);
+ tableView->setColumnSpacing(0);
+ tableView->setLeftMargin(0);
+ tableView->setRightMargin(0);
+ tableView->setTopMargin(0);
+ tableView->setBottomMargin(0);
+
+ auto model = TestModelAsVariant(rows, columns);
+ tableView->setModel(model);
+
+ WAIT_UNTIL_POLISHED;
+
+ // Flick slowly to column 5 (to avoid rebuilds). Flick two columns at a
+ // time to ensure that we create a gap before TableView gets a chance to
+ // adjust endExtent first. This gap on the right side will make TableView
+ // move the table to the edge (in addition to adjusting the extents, but that
+ // will happen in a subsequent polish, and is not for this test verify).
+ for (int x = 0; x <= 6; x += 2)
+ tableView->setContentX(x * columnWidth);
+ QCOMPARE(tableViewPrivate->rightColumn(), columns - 1);
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect, tableViewPrivate->viewportRect);
+
+ for (int y = 0; y <= 6; y += 2)
+ tableView->setContentY(y * rowHeight);
+ QCOMPARE(tableViewPrivate->bottomRow(), rows - 1);
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect, tableViewPrivate->viewportRect);
+
+ for (int x = 6; x >= 0; x -= 2)
+ tableView->setContentX(x * columnWidth);
+ QCOMPARE(tableViewPrivate->leftColumn(), 0);
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect, tableViewPrivate->viewportRect);
+
+ for (int y = 6; y >= 0; y -= 2)
+ tableView->setContentY(y * rowHeight);
+ QCOMPARE(tableViewPrivate->topRow(), 0);
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect, tableViewPrivate->viewportRect);
+}
+
void tst_QQuickTableView::checkContentXY()
{
// Check that you can bind contentX and contentY to
@@ -1942,7 +2107,7 @@ void tst_QQuickTableView::checkChangingModelFromDelegate()
// And since the QML code tried to add another row as well, we
// expect rebuildScheduled to be true, and a polish event to be pending.
- QCOMPARE(tableViewPrivate->rebuildScheduled, true);
+ QVERIFY(tableViewPrivate->scheduledRebuildOptions);
QCOMPARE(tableViewPrivate->polishScheduled, true);
WAIT_UNTIL_POLISHED;
@@ -2025,7 +2190,7 @@ void tst_QQuickTableView::checkTableviewInsideAsyncLoader()
QCOMPARE(loader->status(), QQuickLoader::Ready);
// Check that TableView has finished building
- QCOMPARE(tableViewPrivate->rebuildScheduled, false);
+ QVERIFY(!tableViewPrivate->scheduledRebuildOptions);
QCOMPARE(tableViewPrivate->rebuildState, QQuickTableViewPrivate::RebuildState::Done);
// Check that all expected delegate items have been loaded
@@ -2152,6 +2317,337 @@ void tst_QQuickTableView::checkThatRevisionedPropertiesCannotBeUsedInOldImports(
QCOMPARE(resolvedColumn, 42);
}
+void tst_QQuickTableView::checkSyncView_rootView_data()
+{
+ QTest::addColumn<qreal>("flickToPos");
+
+ QTest::newRow("pos:110") << 110.;
+ QTest::newRow("pos:2010") << 2010.;
+}
+
+void tst_QQuickTableView::checkSyncView_rootView()
+{
+ // Check that if you flick on the root tableview (the view that has
+ // no other view as syncView), all the other tableviews will sync
+ // their content view position according to their syncDirection flag.
+ QFETCH(qreal, flickToPos);
+ LOAD_TABLEVIEW("syncviewsimple.qml");
+ GET_QML_TABLEVIEW(tableViewH);
+ GET_QML_TABLEVIEW(tableViewV);
+ GET_QML_TABLEVIEW(tableViewHV);
+ QQuickTableView *views[] = {tableViewH, tableViewV, tableViewHV};
+
+ auto model = TestModelAsVariant(100, 100);
+
+ tableView->setModel(model);
+ for (auto view : views)
+ view->setModel(model);
+
+ tableView->setContentX(flickToPos);
+ tableView->setContentY(flickToPos);
+
+ WAIT_UNTIL_POLISHED;
+
+ // Check that geometry properties are mirrored
+ QCOMPARE(tableViewH->columnSpacing(), tableView->columnSpacing());
+ QCOMPARE(tableViewH->rowSpacing(), 0);
+ QCOMPARE(tableViewH->contentWidth(), tableView->contentWidth());
+ QCOMPARE(tableViewV->columnSpacing(), 0);
+ QCOMPARE(tableViewV->rowSpacing(), tableView->rowSpacing());
+ QCOMPARE(tableViewV->contentHeight(), tableView->contentHeight());
+
+ // Check that viewport is in sync after the flick
+ QCOMPARE(tableView->contentX(), flickToPos);
+ QCOMPARE(tableView->contentY(), flickToPos);
+ QCOMPARE(tableViewH->contentX(), tableView->contentX());
+ QCOMPARE(tableViewH->contentY(), 0);
+ QCOMPARE(tableViewV->contentX(), 0);
+ QCOMPARE(tableViewV->contentY(), tableView->contentY());
+ QCOMPARE(tableViewHV->contentX(), tableView->contentX());
+ QCOMPARE(tableViewHV->contentY(), tableView->contentY());
+
+ // Check that topLeft cell is in sync after the flick
+ QCOMPARE(tableViewHPrivate->leftColumn(), tableViewPrivate->leftColumn());
+ QCOMPARE(tableViewHPrivate->rightColumn(), tableViewPrivate->rightColumn());
+ QCOMPARE(tableViewHPrivate->topRow(), 0);
+ QCOMPARE(tableViewVPrivate->leftColumn(), 0);
+ QCOMPARE(tableViewVPrivate->topRow(), tableViewPrivate->topRow());
+ QCOMPARE(tableViewHVPrivate->leftColumn(), tableViewPrivate->leftColumn());
+ QCOMPARE(tableViewHVPrivate->topRow(), tableViewPrivate->topRow());
+
+ // Check that the geometry of the tables are in sync after the flick
+ QCOMPARE(tableViewHPrivate->loadedTableOuterRect.left(), tableViewPrivate->loadedTableOuterRect.left());
+ QCOMPARE(tableViewHPrivate->loadedTableOuterRect.right(), tableViewPrivate->loadedTableOuterRect.right());
+ QCOMPARE(tableViewHPrivate->loadedTableOuterRect.top(), 0);
+
+ QCOMPARE(tableViewVPrivate->loadedTableOuterRect.top(), tableViewPrivate->loadedTableOuterRect.top());
+ QCOMPARE(tableViewVPrivate->loadedTableOuterRect.bottom(), tableViewPrivate->loadedTableOuterRect.bottom());
+ QCOMPARE(tableViewVPrivate->loadedTableOuterRect.left(), 0);
+
+ QCOMPARE(tableViewHVPrivate->loadedTableOuterRect, tableViewPrivate->loadedTableOuterRect);
+}
+
+void tst_QQuickTableView::checkSyncView_childViews_data()
+{
+ QTest::addColumn<int>("viewIndexToFlick");
+ QTest::addColumn<qreal>("flickToPos");
+
+ QTest::newRow("tableViewH, pos:100") << 0 << 100.;
+ QTest::newRow("tableViewV, pos:100") << 1 << 100.;
+ QTest::newRow("tableViewHV, pos:100") << 2 << 100.;
+ QTest::newRow("tableViewH, pos:2000") << 0 << 2000.;
+ QTest::newRow("tableViewV, pos:2000") << 1 << 2000.;
+ QTest::newRow("tableViewHV, pos:2000") << 2 << 2000.;
+}
+
+void tst_QQuickTableView::checkSyncView_childViews()
+{
+ // Check that if you flick on a tableview that has a syncView, the
+ // syncView will move to the new position as well, which will also
+ // recursivly move all other connected child views of the syncView.
+ QFETCH(int, viewIndexToFlick);
+ QFETCH(qreal, flickToPos);
+ LOAD_TABLEVIEW("syncviewsimple.qml");
+ GET_QML_TABLEVIEW(tableViewH);
+ GET_QML_TABLEVIEW(tableViewV);
+ GET_QML_TABLEVIEW(tableViewHV);
+ QQuickTableView *views[] = {tableViewH, tableViewV, tableViewHV};
+ QQuickTableView *viewToFlick = views[viewIndexToFlick];
+ QQuickTableViewPrivate *viewToFlickPrivate = QQuickTableViewPrivate::get(viewToFlick);
+
+ auto model = TestModelAsVariant(100, 100);
+
+ tableView->setModel(model);
+ for (auto view : views)
+ view->setModel(model);
+
+ viewToFlick->setContentX(flickToPos);
+ viewToFlick->setContentY(flickToPos);
+
+ WAIT_UNTIL_POLISHED;
+
+ // The view the user flicks on can always be flicked in both directions
+ // (unless is has a flickingDirection set, which is not the case here).
+ QCOMPARE(viewToFlick->contentX(), flickToPos);
+ QCOMPARE(viewToFlick->contentY(), flickToPos);
+
+ // The root view (tableView) will move in sync according
+ // to the syncDirection of the view being flicked.
+ if (viewToFlick->syncDirection() & Qt::Horizontal) {
+ QCOMPARE(tableView->contentX(), flickToPos);
+ QCOMPARE(tableViewPrivate->leftColumn(), viewToFlickPrivate->leftColumn());
+ QCOMPARE(tableViewPrivate->rightColumn(), viewToFlickPrivate->rightColumn());
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect.left(), viewToFlickPrivate->loadedTableOuterRect.left());
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect.right(), viewToFlickPrivate->loadedTableOuterRect.right());
+ } else {
+ QCOMPARE(tableView->contentX(), 0);
+ QCOMPARE(tableViewPrivate->leftColumn(), 0);
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect.left(), 0);
+ }
+
+ if (viewToFlick->syncDirection() & Qt::Vertical) {
+ QCOMPARE(tableView->contentY(), flickToPos);
+ QCOMPARE(tableViewPrivate->topRow(), viewToFlickPrivate->topRow());
+ QCOMPARE(tableViewPrivate->bottomRow(), viewToFlickPrivate->bottomRow());
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect.top(), viewToFlickPrivate->loadedTableOuterRect.top());
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect.bottom(), viewToFlickPrivate->loadedTableOuterRect.bottom());
+ } else {
+ QCOMPARE(tableView->contentY(), 0);
+ QCOMPARE(tableViewPrivate->topRow(), 0);
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect.top(), 0);
+ }
+
+ // The other views should continue to stay in sync with
+ // the root view, unless it was the view being flicked.
+ if (viewToFlick != tableViewH) {
+ QCOMPARE(tableViewH->contentX(), tableView->contentX());
+ QCOMPARE(tableViewH->contentY(), 0);
+ QCOMPARE(tableViewHPrivate->leftColumn(), tableViewPrivate->leftColumn());
+ QCOMPARE(tableViewHPrivate->rightColumn(), tableViewPrivate->rightColumn());
+ QCOMPARE(tableViewHPrivate->loadedTableOuterRect.left(), tableViewPrivate->loadedTableOuterRect.left());
+ QCOMPARE(tableViewHPrivate->loadedTableOuterRect.right(), tableViewPrivate->loadedTableOuterRect.right());
+ QCOMPARE(tableViewHPrivate->topRow(), 0);
+ QCOMPARE(tableViewHPrivate->loadedTableOuterRect.top(), 0);
+ }
+
+ if (viewToFlick != tableViewV) {
+ QCOMPARE(tableViewV->contentX(), 0);
+ QCOMPARE(tableViewV->contentY(), tableView->contentY());
+ QCOMPARE(tableViewVPrivate->topRow(), tableViewPrivate->topRow());
+ QCOMPARE(tableViewVPrivate->bottomRow(), tableViewPrivate->bottomRow());
+ QCOMPARE(tableViewVPrivate->loadedTableOuterRect.top(), tableViewPrivate->loadedTableOuterRect.top());
+ QCOMPARE(tableViewVPrivate->loadedTableOuterRect.bottom(), tableViewPrivate->loadedTableOuterRect.bottom());
+ QCOMPARE(tableViewVPrivate->leftColumn(), 0);
+ QCOMPARE(tableViewVPrivate->loadedTableOuterRect.left(), 0);
+ }
+
+ if (viewToFlick != tableViewHV) {
+ QCOMPARE(tableViewHV->contentX(), tableView->contentX());
+ QCOMPARE(tableViewHV->contentY(), tableView->contentY());
+ QCOMPARE(tableViewHVPrivate->leftColumn(), tableViewPrivate->leftColumn());
+ QCOMPARE(tableViewHVPrivate->rightColumn(), tableViewPrivate->rightColumn());
+ QCOMPARE(tableViewHVPrivate->topRow(), tableViewPrivate->topRow());
+ QCOMPARE(tableViewHVPrivate->bottomRow(), tableViewPrivate->bottomRow());
+ QCOMPARE(tableViewHVPrivate->loadedTableOuterRect, tableViewPrivate->loadedTableOuterRect);
+ }
+}
+
+void tst_QQuickTableView::checkSyncView_differentSizedModels()
+{
+ // Check that you can have two tables in a syncView relation, where
+ // the sync "child" has fewer rows/columns than the syncView. In that
+ // case, it will be possible to flick the syncView further out than
+ // the child have rows/columns to follow. This causes some extra
+ // challenges for TableView to ensure that they are still kept in
+ // sync once you later flick the syncView back to a point where both
+ // tables ends up visible. This test will check this sitiation.
+ LOAD_TABLEVIEW("syncviewsimple.qml");
+ GET_QML_TABLEVIEW(tableViewH);
+ GET_QML_TABLEVIEW(tableViewV);
+ GET_QML_TABLEVIEW(tableViewHV);
+
+ auto tableViewModel = TestModelAsVariant(100, 100);
+ auto tableViewHModel = TestModelAsVariant(100, 50);
+ auto tableViewVModel = TestModelAsVariant(50, 100);
+ auto tableViewHVModel = TestModelAsVariant(5, 5);
+
+ tableView->setModel(tableViewModel);
+ tableViewH->setModel(tableViewHModel);
+ tableViewV->setModel(tableViewVModel);
+ tableViewHV->setModel(tableViewHVModel);
+
+ WAIT_UNTIL_POLISHED;
+
+ // Flick far out, beyond the smaller tables, which will
+ // also force a rebuild (and as such, cause layout properties
+ // like average cell width to be temporarily out of sync).
+ tableView->setContentX(5000);
+ QVERIFY(tableViewPrivate->scheduledRebuildOptions);
+
+ WAIT_UNTIL_POLISHED;
+
+ // Check that the smaller tables are now flicked out of view
+ qreal leftEdge = tableViewPrivate->loadedTableOuterRect.left();
+ QVERIFY(tableViewHPrivate->loadedTableOuterRect.right() < leftEdge);
+ QVERIFY(tableViewHVPrivate->loadedTableOuterRect.right() < leftEdge);
+
+ // Flick slowly back so that we don't trigger a rebuild (since
+ // we want to check that we stay in sync also when not rebuilding).
+ while (tableView->contentX() > 200) {
+ tableView->setContentX(tableView->contentX() - 200);
+ QVERIFY(!tableViewPrivate->rebuildOptions);
+ QVERIFY(!tableViewPrivate->polishScheduled);
+ }
+
+ leftEdge = tableViewPrivate->loadedTableOuterRect.left();
+ const int leftColumn = tableViewPrivate->leftColumn();
+ QCOMPARE(tableViewHPrivate->loadedTableOuterRect.left(), leftEdge);
+ QCOMPARE(tableViewHPrivate->leftColumn(), leftColumn);
+
+ // Because the tableView was fast flicked and then slowly flicked back, the
+ // left column is now 49, which is actually far too high, since we're almost
+ // at the beginning of the content view. But this "miscalculation" is expected
+ // when the column widths are increasing for each column, like they do in this
+ // test. In that case, the algorithm that predicts where each column should end
+ // up gets slightly confused. Anyway, check that tableViewHV, that has only
+ // 5 columns, is not showing any columns, since it should always stay in sync with
+ // syncView regardless of the content view position.
+ QVERIFY(tableViewHVPrivate->loadedColumns.isEmpty());
+}
+
+void tst_QQuickTableView::checkSyncView_connect_late_data()
+{
+ QTest::addColumn<qreal>("flickToPos");
+
+ QTest::newRow("pos:110") << 110.;
+ QTest::newRow("pos:2010") << 2010.;
+}
+
+void tst_QQuickTableView::checkSyncView_connect_late()
+{
+ // Check that if you assign a syncView to a TableView late, and
+ // after the views have been flicked around, they will still end up in sync.
+ QFETCH(qreal, flickToPos);
+ LOAD_TABLEVIEW("syncviewsimple.qml");
+ GET_QML_TABLEVIEW(tableViewH);
+ GET_QML_TABLEVIEW(tableViewV);
+ GET_QML_TABLEVIEW(tableViewHV);
+ QQuickTableView *views[] = {tableViewH, tableViewV, tableViewHV};
+
+ auto model = TestModelAsVariant(100, 100);
+
+ tableView->setModel(model);
+
+ // Start with no syncView connections
+ for (auto view : views) {
+ view->setSyncView(nullptr);
+ view->setModel(model);
+ }
+
+ WAIT_UNTIL_POLISHED;
+
+ tableView->setContentX(flickToPos);
+ tableView->setContentY(flickToPos);
+
+ WAIT_UNTIL_POLISHED;
+
+ // Check that viewport is not in sync after the flick
+ QCOMPARE(tableView->contentX(), flickToPos);
+ QCOMPARE(tableView->contentY(), flickToPos);
+ QCOMPARE(tableViewH->contentX(), 0);
+ QCOMPARE(tableViewH->contentY(), 0);
+ QCOMPARE(tableViewV->contentX(), 0);
+ QCOMPARE(tableViewV->contentY(), 0);
+ QCOMPARE(tableViewHV->contentX(), 0);
+ QCOMPARE(tableViewHV->contentY(), 0);
+
+ // Assign the syncView late. This should make
+ // all the views end up in sync.
+ for (auto view : views) {
+ view->setSyncView(tableView);
+ WAIT_UNTIL_POLISHED_ARG(view);
+ }
+
+ // Check that geometry properties are mirrored
+ QCOMPARE(tableViewH->columnSpacing(), tableView->columnSpacing());
+ QCOMPARE(tableViewH->rowSpacing(), 0);
+ QCOMPARE(tableViewH->contentWidth(), tableView->contentWidth());
+ QCOMPARE(tableViewV->columnSpacing(), 0);
+ QCOMPARE(tableViewV->rowSpacing(), tableView->rowSpacing());
+ QCOMPARE(tableViewV->contentHeight(), tableView->contentHeight());
+
+ // Check that viewport is in sync after the flick
+ QCOMPARE(tableView->contentX(), flickToPos);
+ QCOMPARE(tableView->contentY(), flickToPos);
+ QCOMPARE(tableViewH->contentX(), tableView->contentX());
+ QCOMPARE(tableViewH->contentY(), 0);
+ QCOMPARE(tableViewV->contentX(), 0);
+ QCOMPARE(tableViewV->contentY(), tableView->contentY());
+ QCOMPARE(tableViewHV->contentX(), tableView->contentX());
+ QCOMPARE(tableViewHV->contentY(), tableView->contentY());
+
+ // Check that topLeft cell is in sync after the flick
+ QCOMPARE(tableViewHPrivate->leftColumn(), tableViewPrivate->leftColumn());
+ QCOMPARE(tableViewHPrivate->rightColumn(), tableViewPrivate->rightColumn());
+ QCOMPARE(tableViewHPrivate->topRow(), 0);
+ QCOMPARE(tableViewVPrivate->leftColumn(), 0);
+ QCOMPARE(tableViewVPrivate->topRow(), tableViewPrivate->topRow());
+ QCOMPARE(tableViewHVPrivate->leftColumn(), tableViewPrivate->leftColumn());
+ QCOMPARE(tableViewHVPrivate->topRow(), tableViewPrivate->topRow());
+
+ // Check that the geometry of the tables are in sync after the flick
+ QCOMPARE(tableViewHPrivate->loadedTableOuterRect.left(), tableViewPrivate->loadedTableOuterRect.left());
+ QCOMPARE(tableViewHPrivate->loadedTableOuterRect.right(), tableViewPrivate->loadedTableOuterRect.right());
+ QCOMPARE(tableViewHPrivate->loadedTableOuterRect.top(), 0);
+
+ QCOMPARE(tableViewVPrivate->loadedTableOuterRect.top(), tableViewPrivate->loadedTableOuterRect.top());
+ QCOMPARE(tableViewVPrivate->loadedTableOuterRect.bottom(), tableViewPrivate->loadedTableOuterRect.bottom());
+ QCOMPARE(tableViewVPrivate->loadedTableOuterRect.left(), 0);
+
+ QCOMPARE(tableViewHVPrivate->loadedTableOuterRect, tableViewPrivate->loadedTableOuterRect);
+
+}
+
QTEST_MAIN(tst_QQuickTableView)
#include "tst_qquicktableview.moc"
diff --git a/tests/auto/quick/qquickvisualdatamodel/qquickvisualdatamodel.pro b/tests/auto/quick/qquickvisualdatamodel/qquickvisualdatamodel.pro
index 9222e39477..5445f6768d 100644
--- a/tests/auto/quick/qquickvisualdatamodel/qquickvisualdatamodel.pro
+++ b/tests/auto/quick/qquickvisualdatamodel/qquickvisualdatamodel.pro
@@ -9,5 +9,5 @@ include (../shared/util.pri)
TESTDATA = data/*
-QT += core-private gui-private qml-private quick-private testlib
+QT += core-private gui-private qml-private quick-private testlib qmlmodels-private
qtHaveModule(widgets): QT += widgets
diff --git a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp
index fac8283e2c..32008f675a 100644
--- a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp
+++ b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp
@@ -39,7 +39,7 @@
#include <QtQuick/qquickview.h>
#include <private/qquicklistview_p.h>
#include <QtQuick/private/qquicktext_p.h>
-#include <QtQml/private/qqmldelegatemodel_p.h>
+#include <QtQmlModels/private/qqmldelegatemodel_p.h>
#include <private/qqmlvaluetype_p.h>
#include <private/qqmlchangeset_p.h>
#include <private/qqmlengine_p.h>
diff --git a/tests/auto/quick/quick.pro b/tests/auto/quick/quick.pro
index 7257a99d2a..b6ffdc0f7e 100644
--- a/tests/auto/quick/quick.pro
+++ b/tests/auto/quick/quick.pro
@@ -29,6 +29,7 @@ PRIVATETESTS += \
qquickanimations \
qquickapplication \
qquickbehaviors \
+ qquickboundaryrule \
qquickfontloader \
qquickfontloader_static \
qquickfontmetrics \
diff --git a/tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp b/tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp
index d7c54703ad..7db01180be 100644
--- a/tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp
+++ b/tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp
@@ -35,6 +35,16 @@
// This benchmark produces performance statistics
// for the standard set of elements, properties and expressions which
// are provided in the QtDeclarative library (QtQml and QtQuick).
+//
+// Note that we have hand-rolled our own benchmark harness, rather
+// than directly using QBENCHMARK, in order to avoid contaminating
+// the benchmark results with unwanted initialization or side-effects
+// and allow us to more completely isolate the required specific area
+// (compilation, instantiation, or cached type-data instantiation)
+// that we wish to benchmark.
+
+#define AVERAGE_OVER_N 10
+#define IGNORE_N_OUTLIERS 2
class ModuleApi : public QObject
{
@@ -197,6 +207,18 @@ void tst_librarymetrics_performance::metrics_data()
QTest::newRow("062) positioning - binding (with grid) positioning") << testFileUrl("data/bindingwithgridpositioning.qml");
}
+// This method is intended to benchmark the amount of time / cpu cycles
+// required to compile the given QML input.
+// Note that this deliberately does NOT include the time taken to
+// construct the QML engine.
+// Also note that between each iteration, we attempt to clean the
+// engine state (destroying and reconstructing the engine, clearing
+// the QML type registrations, etc) to simulate a cold-start environment.
+//
+// The benchmark result is expected to be dominated by QML parsing,
+// compilation, and optimization paths, and since the compiled component
+// is never instantiated, no construction or binding evaluation should
+// occur.
void tst_librarymetrics_performance::compilation()
{
QFETCH(QUrl, qmlfile);
@@ -211,37 +233,148 @@ void tst_librarymetrics_performance::compilation()
}
}
- QBENCHMARK {
+ QList<qint64> nResults;
+
+ // generate AVERAGE_OVER_N results
+ for (int i = 0; i < AVERAGE_OVER_N; ++i) {
cleanState(&e);
- QQmlComponent c(e, this);
- c.loadUrl(qmlfile); // just compile.
+ {
+ QElapsedTimer et;
+ et.start();
+ // BEGIN benchmarked code block
+ QQmlComponent c(e, this);
+ c.loadUrl(qmlfile);
+ // END benchmarked code block
+ qint64 etime = et.nsecsElapsed();
+ nResults.append(etime);
+ }
}
+
+ // sort the list
+ qSort(nResults);
+
+ // remove IGNORE_N_OUTLIERS*2 from ONLY the worst end (remove gc interference)
+ for (int i = 0; i < IGNORE_N_OUTLIERS; ++i) {
+ if (!nResults.isEmpty()) nResults.removeLast();
+ if (!nResults.isEmpty()) nResults.removeLast();
+ }
+
+ // now generate an average
+ qint64 totaltime = 0;
+ if (nResults.size() == 0) nResults.append(9999);
+ for (int i = 0; i < nResults.size(); ++i)
+ totaltime += nResults.at(i);
+ double average = ((double)totaltime) / nResults.count();
+
+ // and return it as the result
+ QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds);
+ QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds); // twice to workaround bug in QTestLib
}
+// This method is intended to benchmark the amount of time / cpu cycles
+// required to compile and instantiate the given QML input,
+// where the QML state has NOT been cleared in between each iteration.
+// Thus, cached type data should be used when instantiating the object.
+//
+// The benchmark result is expected to be dominated by QObject
+// hierarchy construction and first-time binding evaluation.
void tst_librarymetrics_performance::instantiation_cached()
{
QFETCH(QUrl, qmlfile);
+
cleanState(&e);
+ QList<qint64> nResults;
- QBENCHMARK {
+ // generate AVERAGE_OVER_N results
+ for (int i = 0; i < AVERAGE_OVER_N; ++i) {
+ QElapsedTimer et;
+ et.start();
+ // BEGIN benchmarked code block
QQmlComponent c(e, this);
- c.loadUrl(qmlfile); // just compile.
+ c.loadUrl(qmlfile);
QObject *o = c.create();
+ // END benchmarked code block
+ qint64 etime = et.nsecsElapsed();
+ nResults.append(etime);
delete o;
}
+
+ // sort the list
+ qSort(nResults);
+
+ // remove IGNORE_N_OUTLIERS*2 from ONLY the worst end (remove gc interference)
+ for (int i = 0; i < IGNORE_N_OUTLIERS; ++i) {
+ if (!nResults.isEmpty()) nResults.removeLast();
+ if (!nResults.isEmpty()) nResults.removeLast();
+ }
+
+ // now generate an average
+ qint64 totaltime = 0;
+ if (nResults.size() == 0) nResults.append(9999);
+ for (int i = 0; i < nResults.size(); ++i)
+ totaltime += nResults.at(i);
+ double average = ((double)totaltime) / nResults.count();
+
+ // and return it as the result
+ QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds);
+ QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds); // twice to workaround bug in QTestLib
}
+// This method is intended to benchmark the amount of time / cpu cycles
+// required to compile and instantiate the given QML input,
+// where the QML state has been cleared in between each iteration.
+// This will cause the engine to parse, compile and optimize the
+// input QML in each iteration. After compilation, the component
+// will be instantiated, and so QObject hierarchy construction and
+// first time binding evaluation will contribute to the result.
+//
+// The compilation phase is expected to dominate the result, however
+// in some cases (complex positioning via expensive bindings, or
+// other pathological cases) instantiation may dominate. Those
+// cases are prime candidates for further investigation...
void tst_librarymetrics_performance::instantiation()
{
QFETCH(QUrl, qmlfile);
- QBENCHMARK {
+ cleanState(&e);
+ QList<qint64> nResults;
+
+ // generate AVERAGE_OVER_N results
+ for (int i = 0; i < AVERAGE_OVER_N; ++i) {
cleanState(&e);
- QQmlComponent c(e, this);
- c.loadUrl(qmlfile); // just compile.
- QObject *o = c.create();
- delete o;
+ {
+ QElapsedTimer et;
+ et.start();
+ // BEGIN benchmarked code block
+ QQmlComponent c(e, this);
+ c.loadUrl(qmlfile);
+ QObject *o = c.create();
+ // END benchmarked code block
+ qint64 etime = et.nsecsElapsed();
+ nResults.append(etime);
+ delete o;
+ }
+ }
+
+ // sort the list
+ qSort(nResults);
+
+ // remove IGNORE_N_OUTLIERS*2 from ONLY the worst end (remove gc interference)
+ for (int i = 0; i < IGNORE_N_OUTLIERS; ++i) {
+ if (!nResults.isEmpty()) nResults.removeLast();
+ if (!nResults.isEmpty()) nResults.removeLast();
}
+
+ // now generate an average
+ qint64 totaltime = 0;
+ if (nResults.size() == 0) nResults.append(9999);
+ for (int i = 0; i < nResults.size(); ++i)
+ totaltime += nResults.at(i);
+ double average = ((double)totaltime) / nResults.count();
+
+ // and return it as the result
+ QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds);
+ QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds); // twice to workaround bug in QTestLib
}
void tst_librarymetrics_performance::positioners_data()
@@ -256,19 +389,58 @@ void tst_librarymetrics_performance::positioners_data()
QTest::newRow("07) positioning - binding (with grid) positioning") << testFileUrl("data/bindingwithgridpositioning.2.qml");
}
-// this test triggers repositioning a large number of times,
+// This method triggers repositioning a large number of times,
// so we can track the cost of different repositioning methods.
+// Note that the engine state is cleared before every iteration,
+// so the benchmark result will include the cost of compilation
+// as well as instantiation and first-time binding evaluation.
+//
+// The repositioning triggered within the QML is expected
+// to dominate the benchmark time, especially if slow-path
+// binding evaluation occurs.
void tst_librarymetrics_performance::positioners()
{
QFETCH(QUrl, qmlfile);
- QBENCHMARK {
+ cleanState(&e);
+ QList<qint64> nResults;
+
+ // generate AVERAGE_OVER_N results
+ for (int i = 0; i < AVERAGE_OVER_N; ++i) {
cleanState(&e);
- QQmlComponent c(e, this);
- c.loadUrl(qmlfile); // just compile.
- QObject *o = c.create();
- delete o;
+ {
+ QElapsedTimer et;
+ et.start();
+ // BEGIN benchmarked code block
+ QQmlComponent c(e, this);
+ c.loadUrl(qmlfile);
+ QObject *o = c.create();
+ // END benchmarked code block
+ qint64 etime = et.nsecsElapsed();
+ nResults.append(etime);
+ delete o;
+ }
}
+
+ // sort the list
+ qSort(nResults);
+
+ // remove IGNORE_N_OUTLIERS*2 from ONLY the worst end (remove gc interference)
+ for (int i = 0; i < IGNORE_N_OUTLIERS; ++i) {
+ if (!nResults.isEmpty()) nResults.removeLast();
+ if (!nResults.isEmpty()) nResults.removeLast();
+ }
+
+ // now generate an average
+ qint64 totaltime = 0;
+ if (nResults.size() == 0) nResults.append(9999);
+ for (int i = 0; i < nResults.size(); ++i)
+ totaltime += nResults.at(i);
+ double average = ((double)totaltime) / nResults.count();
+
+ // and return it as the result
+ QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds);
+ QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds); // twice to workaround bug in QTestLib
}
QTEST_MAIN(tst_librarymetrics_performance)
diff --git a/tests/libfuzzer/qml/jsapi/evaluate/evaluate.pro b/tests/libfuzzer/qml/jsapi/evaluate/evaluate.pro
new file mode 100644
index 0000000000..301b4f606a
--- /dev/null
+++ b/tests/libfuzzer/qml/jsapi/evaluate/evaluate.pro
@@ -0,0 +1,6 @@
+QT -= gui
+QT += qml
+CONFIG += console
+CONFIG -= app_bundle
+SOURCES += main.cpp
+LIBS += -fsanitize=fuzzer
diff --git a/tests/libfuzzer/qml/jsapi/evaluate/main.cpp b/tests/libfuzzer/qml/jsapi/evaluate/main.cpp
new file mode 100644
index 0000000000..9e90ba7cbd
--- /dev/null
+++ b/tests/libfuzzer/qml/jsapi/evaluate/main.cpp
@@ -0,0 +1,43 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QCoreApplication>
+#include <QJSEngine>
+
+// libfuzzer test for QJSEngine::evaluate()
+
+extern "C" int LLVMFuzzerTestOneInput(const char *Data, size_t Size) {
+ const QByteArray ba(Data, Size);
+ // avoid potential endless loops
+ if (ba.contains("for") || ba.contains("while"))
+ return 1;
+ int c = 0;
+ QCoreApplication a(c, nullptr);
+ QJSEngine().evaluate(ba);
+ return 0;
+}
diff --git a/tests/manual/pointer/content/FakeFlickable.qml b/tests/manual/pointer/content/FakeFlickable.qml
index ffb5c4e914..636399ba2c 100644
--- a/tests/manual/pointer/content/FakeFlickable.qml
+++ b/tests/manual/pointer/content/FakeFlickable.qml
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the manual tests of the Qt Toolkit.
@@ -26,10 +26,12 @@
**
****************************************************************************/
-import QtQuick 2.12
+import QtQuick 2.14
+import Qt.labs.animation 1.0
Item {
id: root
+ objectName: "viewport"
default property alias data: __contentItem.data
property alias velocity: anim.velocity
property alias contentX: __contentItem.x // sign is reversed compared to Flickable.contentX
@@ -45,52 +47,81 @@ Item {
width: childrenRect.width
height: childrenRect.height
- property real xlimit: root.width - __contentItem.width
- property real ylimit: root.height - __contentItem.height
+ BoundaryRule on x {
+ id: xbr
+ minimum: root.width - __contentItem.width
+ maximum: 0
+ minimumOvershoot: 100
+ maximumOvershoot: 100
+ overshootFilter: BoundaryRule.Peak
+ }
- function returnToBounds() {
- if (x > 0) {
- returnXAnim.to = 0
- returnXAnim.start()
- } else if (x < xlimit) {
- returnXAnim.to = xlimit
- returnXAnim.start()
- }
- if (y > 0) {
- returnYAnim.to = 0
- returnYAnim.start()
- } else if (y < ylimit) {
- returnYAnim.to = ylimit
- returnYAnim.start()
- }
+ BoundaryRule on y {
+ id: ybr
+ minimum: root.height - __contentItem.height
+ maximum: 0
+ minimumOvershoot: 100
+ maximumOvershoot: 100
+ overshootFilter: BoundaryRule.Peak
}
DragHandler {
id: dragHandler
- onActiveChanged: if (!active) anim.restart(centroid.velocity)
+ onActiveChanged:
+ if (active) {
+ anim.stop()
+ root.flickStarted()
+ } else {
+ var vel = centroid.velocity
+ if (xbr.returnToBounds())
+ vel.x = 0
+ if (ybr.returnToBounds())
+ vel.y = 0
+ if (vel.x !== 0 || vel.y !== 0)
+ anim.restart(vel)
+ else
+ root.flickEnded()
+ }
+ }
+ WheelHandler {
+ rotationScale: 15
+ property: "x"
+ orientation: Qt.Horizontal
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
+ onActiveChanged:
+ // emitting signals in both instances is redundant but hard to avoid
+ // when the touchpad is flicking along both axes
+ if (active) {
+ anim.stop()
+ root.flickStarted()
+ } else {
+ xbr.returnToBounds()
+ root.flickEnded()
+ }
+ }
+ WheelHandler {
+ rotationScale: 15
+ property: "y"
+ orientation: Qt.Vertical
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
+ onActiveChanged:
+ if (active) {
+ anim.stop()
+ root.flickStarted()
+ } else {
+ ybr.returnToBounds()
+ root.flickEnded()
+ }
}
MomentumAnimation {
id: anim
target: __contentItem
onStarted: root.flickStarted()
onStopped: {
- __contentItem.returnToBounds()
+ xbr.returnToBounds()
+ ybr.returnToBounds()
root.flickEnded()
}
}
- NumberAnimation {
- id: returnXAnim
- target: __contentItem
- property: "x"
- duration: 200
- easing.type: Easing.OutQuad
- }
- NumberAnimation {
- id: returnYAnim
- target: __contentItem
- property: "y"
- duration: 200
- easing.type: Easing.OutQuad
- }
}
}
diff --git a/tests/manual/pointer/content/LeftDrawer.qml b/tests/manual/pointer/content/LeftDrawer.qml
new file mode 100644
index 0000000000..08f2f67b5c
--- /dev/null
+++ b/tests/manual/pointer/content/LeftDrawer.qml
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the manual tests of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+import QtQuick 2.14
+import Qt.labs.animation 1.0
+import QtGraphicalEffects 1.14
+
+Item {
+ id: root
+ objectName: "LeftHandlerDrawer"
+ width: 100
+ height: 400
+ property real stickout: 4
+ property real xOpen: rect.radius * -2
+ property real xClosed: stickout - width
+ x: xClosed
+ y: 10
+
+ function close() {
+ openCloseAnimation.to = xClosed
+ openCloseAnimation.start()
+ }
+ function open() {
+ openCloseAnimation.to = xOpen
+ openCloseAnimation.start()
+ }
+
+ DragHandler {
+ id: dragHandler
+ yAxis.enabled: false
+ xAxis.minimum: -1000
+ margin: 20
+ onActiveChanged:
+ if (!active) {
+ if (xbr.returnToBounds())
+ return;
+ if (translation.x > 0)
+ open()
+ if (translation.x < 0)
+ close()
+ }
+ }
+
+ BoundaryRule on x {
+ id: xbr
+ minimum: xClosed
+ maximum: xOpen
+ minimumOvershoot: rect.radius
+ maximumOvershoot: rect.radius
+ }
+
+ NumberAnimation on x {
+ id: openCloseAnimation
+ duration: 300
+ easing { type: Easing.OutBounce; overshoot: 5 }
+ }
+
+ RectangularGlow {
+ id: effect
+ anchors.fill: parent
+ glowRadius: dragHandler.margin
+ spread: 0.2
+ color: "cyan"
+ cornerRadius: rect.radius + glowRadius
+ }
+
+ Rectangle {
+ id: rect
+ anchors.fill: parent
+ anchors.margins: 3
+ color: "#333"
+ border.color: "cyan"
+ border.width: 2
+ radius: 10
+ antialiasing: true
+ }
+}
diff --git a/tests/manual/pointer/content/Slider.qml b/tests/manual/pointer/content/Slider.qml
index c381d97c7c..beb84b176b 100644
--- a/tests/manual/pointer/content/Slider.qml
+++ b/tests/manual/pointer/content/Slider.qml
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the manual tests of the Qt Toolkit.
@@ -26,7 +26,8 @@
**
****************************************************************************/
-import QtQuick 2.12
+import QtQuick 2.14
+import Qt.labs.animation 1.0
Item {
id: root
@@ -42,8 +43,16 @@ Item {
objectName: label.text + " DragHandler"
target: knob
xAxis.enabled: false
- yAxis.minimum: slot.y
- yAxis.maximum: slot.height + slot.y - knob.height
+ }
+
+ WheelHandler {
+ id: wheelHandler
+ objectName: label.text + " WheelHandler"
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
+ invertible: false // Don't let the system "natural scrolling" setting affect this
+ rotationScale: -0.5 // But make it go consistently in the same direction as the fingers or wheel, a bit slow
+ target: knob
+ property: "y"
}
Rectangle {
@@ -51,8 +60,8 @@ Item {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.margins: 10
- anchors.topMargin: 30
- anchors.bottomMargin: 30
+ anchors.topMargin: label.height + 6
+ anchors.bottomMargin: valueLabel.height + 4
anchors.horizontalCenter: parent.horizontalCenter
width: 10
color: "black"
@@ -82,10 +91,10 @@ Item {
height: root.width / 2
width: implicitWidth / implicitHeight * height
property bool programmatic: false
- property real multiplier: root.maximumValue / (dragHandler.yAxis.maximum - dragHandler.yAxis.minimum)
- onYChanged: if (!programmatic) root.value = root.maximumValue - (knob.y - dragHandler.yAxis.minimum) * multiplier
+ property real multiplier: root.maximumValue / (ybr.maximum - ybr.minimum)
+ onYChanged: if (!programmatic) root.value = root.maximumValue - (knob.y - ybr.minimum) * multiplier
transformOrigin: Item.Center
- function setValue(value) { knob.y = dragHandler.yAxis.maximum - value / knob.multiplier }
+ function setValue(value) { knob.y = ybr.maximum - value / knob.multiplier }
TapHandler {
id: tap
objectName: label.text + " TapHandler"
@@ -95,9 +104,15 @@ Item {
root.tapped
}
}
+ BoundaryRule on y {
+ id: ybr
+ minimum: slot.y
+ maximum: slot.height + slot.y - knob.height
+ }
}
Text {
+ id: valueLabel
font.pointSize: 16
color: "red"
anchors.bottom: parent.bottom
diff --git a/tests/manual/pointer/fakeFlickable.qml b/tests/manual/pointer/fakeFlickable.qml
index 284e0d1f34..be52e4dbaa 100644
--- a/tests/manual/pointer/fakeFlickable.qml
+++ b/tests/manual/pointer/fakeFlickable.qml
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the manual tests of the Qt Toolkit.
@@ -30,39 +30,34 @@ import QtQuick 2.12
import "content"
Rectangle {
+ id: root
color: "#444"
width: 480
- height: 480
+ height: 640
FakeFlickable {
id: ff
anchors.fill: parent
+ anchors.leftMargin: 20
anchors.rightMargin: rightSB.width
- Row {
- Item {
- width: 100
- height: 400
- Slider {
- id: slider
- label: "font size"
- anchors.fill: parent
- maximumValue: 36
- value: 14
- }
- }
- Text {
- id: text
- color: "beige"
- font.family: "mono"
- font.pointSize: slider.value
- onTextChanged: console.log("text geom " + width + "x" + height +
- ", parent " + parent + " geom " + parent.width + "x" + parent.height)
- }
- }
+ Text {
+ id: text
+ color: "beige"
+ font.family: "mono"
+ font.pointSize: slider.value
+ onTextChanged: console.log("text geom " + width + "x" + height +
+ ", parent " + parent + " geom " + parent.width + "x" + parent.height)
+ }
- onFlickStarted: console.log("flick started with velocity " + velocity)
- onFlickEnded: console.log("flick ended with velocity " + velocity)
+ onFlickStarted: {
+ root.border.color = "green"
+ console.log("flick started with velocity " + velocity)
+ }
+ onFlickEnded: {
+ root.border.color = "transparent"
+ console.log("flick ended with velocity " + velocity)
+ }
Component.onCompleted: {
var request = new XMLHttpRequest()
@@ -101,4 +96,17 @@ Rectangle {
bottom: parent.bottom
}
}
+
+ LeftDrawer {
+ width: 100
+ anchors.verticalCenter: parent.verticalCenter
+ Slider {
+ id: slider
+ label: "font\nsize"
+ anchors.fill: parent
+ anchors.margins: 10
+ maximumValue: 36
+ value: 14
+ }
+ }
}
diff --git a/tests/manual/pointer/map.qml b/tests/manual/pointer/map.qml
index c400874d58..0e815ccd9c 100644
--- a/tests/manual/pointer/map.qml
+++ b/tests/manual/pointer/map.qml
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the manual tests of the Qt Toolkit.
@@ -26,7 +26,7 @@
**
****************************************************************************/
-import QtQuick 2.12
+import QtQuick 2.14
Item {
width: 640
@@ -41,6 +41,20 @@ Item {
height: image.height
transform: Rotation { id: tilt; origin.x: width / 2; origin.y: height / 2; axis { x: 1; y: 0; z: 0 } }
+ WheelHandler {
+ id: wheelHandler
+ objectName: "vertical mouse wheel for scaling"
+ property: "scale"
+ onWheel: console.log("rotation " + event.angleDelta + " scaled " + rotation + " @ " + point.position + " => " + map.scale)
+ }
+
+ WheelHandler {
+ id: horizontalWheelHandler
+ objectName: "horizontal mouse wheel for side-scrolling"
+ property: "x"
+ orientation: Qt.Horizontal
+ }
+
Image {
id: image
anchors.centerIn: parent
diff --git a/tests/manual/pointer/pinchAndWheel.qml b/tests/manual/pointer/pinchAndWheel.qml
new file mode 100644
index 0000000000..0944717bb7
--- /dev/null
+++ b/tests/manual/pointer/pinchAndWheel.qml
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the manual tests of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.14
+import Qt.labs.animation 1.0
+import "content"
+
+Rectangle {
+ id: root
+ width: 1024; height: 600
+ color: "#eee"
+
+ CheckBox {
+ id: cbTouchpadEnabled
+ x: 10; y: 6
+ label: "Touchpad wheel emulation enabled"
+ }
+
+ CheckBox {
+ id: cbHorzWheelEnabled
+ x: cbTouchpadEnabled.width + 20; y: 6
+ label: "Horizontal wheel rotation"
+ }
+
+ function getTransformationDetails(item, pinchhandler) {
+ return "\n\npinch.scale:" + pinchhandler.scale.toFixed(2)
+ + "\npinch.rotation:" + pinchhandler.rotation.toFixed(2)
+ + "°\npinch.translation:" + "(" + pinchhandler.translation.x.toFixed(2) + "," + pinchhandler.translation.y.toFixed(2) + ")"
+ + "\nscale wheel.rotation:" + scaleWheelHandler.rotation.toFixed(2)
+ + "°\nhorizontal wheel.rotation:" + horizontalRotationWheelHandler.rotation.toFixed(2)
+ + "°\ncontrol-rotation wheel.rotation:" + controlRotationWheelHandler.rotation.toFixed(2)
+ + "°\nrect.scale: " + item.scale.toFixed(2)
+ + "\nrect.rotation: " + item.rotation.toFixed(2)
+ + "°\nrect.position: " + "(" + item.x.toFixed(2) + "," + item.y.toFixed(2) + ")"
+ }
+
+ Rectangle {
+ id: transformable
+ width: parent.width - 100; height: parent.height - 100; x: 50; y: 50
+ color: "#ffe0e0e0"
+ antialiasing: true
+
+ PinchHandler {
+ id: parentPinch
+ objectName: "parent pinch"
+ minimumScale: 0.5
+ maximumScale: 3
+ }
+
+ WheelHandler {
+ id: scaleWheelHandler
+ objectName: "mouse wheel for scaling"
+ acceptedDevices: cbTouchpadEnabled.checked ? PointerDevice.Mouse | PointerDevice.TouchPad : PointerDevice.Mouse
+ acceptedModifiers: Qt.NoModifier
+ property: "scale"
+ onActiveChanged: if (!active) sbr.returnToBounds();
+ onWheel: console.log(objectName + ": rotation " + event.angleDelta.y + " scaled " + rotation + " @ " + point.position + " => " + parent.scale)
+ }
+
+ BoundaryRule on scale {
+ id: sbr
+ minimum: 0.1
+ maximum: 2
+ minimumOvershoot: 0.05
+ maximumOvershoot: 0.05
+ }
+
+ BoundaryRule on rotation {
+ id: rbr
+ minimum: -90
+ maximum: 360
+ minimumOvershoot: 10
+ maximumOvershoot: 10
+ }
+
+ WheelHandler {
+ id: horizontalRotationWheelHandler
+ enabled: cbHorzWheelEnabled.checked
+ objectName: "horizontal mouse wheel for rotation"
+ acceptedDevices: cbTouchpadEnabled.checked ? PointerDevice.Mouse | PointerDevice.TouchPad : PointerDevice.Mouse
+ acceptedModifiers: Qt.NoModifier
+ property: "rotation"
+ orientation: Qt.Horizontal
+ onActiveChanged: if (!active) rbr.returnToBounds()
+ onWheel: console.log(objectName + ": rotation " + event.angleDelta.y + " scaled " + rotation + " @ " + point.position + " => " + parent.rotation)
+ }
+
+ WheelHandler {
+ id: controlRotationWheelHandler
+ objectName: "control-mouse wheel for rotation"
+ acceptedDevices: cbTouchpadEnabled.checked ? PointerDevice.Mouse | PointerDevice.TouchPad : PointerDevice.Mouse
+ acceptedModifiers: Qt.ControlModifier
+ property: "rotation"
+ orientation: Qt.Vertical // already the default
+ // TODO returnToBounds() causes trouble because position isn't being adjusted when this happens
+ onActiveChanged: if (!active) rbr.returnToBounds()
+ onWheel: console.log(objectName + ": rotation " + event.angleDelta.y + " scaled " + rotation + " @ " + point.position + " => " + parent.rotation)
+ }
+
+ HoverHandler {
+ id: hover
+ acceptedDevices: PointerDevice.AllDevices
+ property var scenePoint: transformable.mapToItem(root, point.position.x, point.position.y)
+ }
+
+ Text {
+ text: "Pinch with 2 fingers to scale, rotate and translate\nMouse wheel to scale, Ctrl+mouse wheel or horizontal wheel to rotate"
+ + getTransformationDetails(parent, parentPinch)
+ }
+ }
+
+ Rectangle {
+ width: 1; height: parent.height
+ color: "blue"
+ x: hover.scenePoint.x
+ Text {
+ x: implicitWidth / -2; style: Text.Outline; styleColor: "white"
+ y: 30
+ color: "blue"
+ text: "outer " + parent.x.toFixed(2) + " inner " + hover.point.position.x.toFixed(2)
+ }
+ }
+
+ Rectangle {
+ width: parent.width; height: 1
+ color: "blue"
+ y: hover.scenePoint.y
+ Text {
+ x: 45
+ y: implicitHeight / -2; style: Text.Outline; styleColor: "white"
+ color: "blue"
+ text: "outer " + parent.y.toFixed(2) + " inner " + hover.point.position.y.toFixed(2)
+ }
+ }
+}
diff --git a/tests/manual/tableview/tablemodel/form/RowForm.qml b/tests/manual/tableview/tablemodel/form/RowForm.qml
new file mode 100644
index 0000000000..bb03e685c0
--- /dev/null
+++ b/tests/manual/tableview/tablemodel/form/RowForm.qml
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import QtQuick.Controls 2.3
+import QtQuick.Layouts 1.11
+
+ScrollView {
+ clip: true
+
+ function inputAsRow() {
+ return {
+ checked: checkedCheckBox.checked,
+ amount: amountSpinBox.value,
+ fruitType: fruitTypeTextField.text,
+ fruitName: fruitNameTextField.text,
+ fruitPrice: parseFloat(fruitPriceTextField.text)
+ }
+ }
+
+ default property alias content: gridLayout.children
+
+ GridLayout {
+ id: gridLayout
+ columns: 2
+
+ Label {
+ text: "checked"
+ }
+ CheckBox {
+ id: checkedCheckBox
+ }
+
+ Label {
+ text: "amount"
+ }
+ SpinBox {
+ id: amountSpinBox
+ value: 1
+ }
+
+ Label {
+ text: "fruitType"
+ }
+ TextField {
+ id: fruitTypeTextField
+ text: "Pear"
+ }
+
+ Label {
+ text: "fruitName"
+ }
+ TextField {
+ id: fruitNameTextField
+ text: "Williams"
+ }
+
+ Label {
+ text: "fruitPrice"
+ }
+ TextField {
+ id: fruitPriceTextField
+ text: "1.50"
+ }
+ }
+}
diff --git a/tests/manual/tableview/tablemodel/form/form.pro b/tests/manual/tableview/tablemodel/form/form.pro
new file mode 100644
index 0000000000..ba6f7a91b1
--- /dev/null
+++ b/tests/manual/tableview/tablemodel/form/form.pro
@@ -0,0 +1,10 @@
+TEMPLATE = app
+TARGET = form
+QT += qml quick
+SOURCES += main.cpp
+RESOURCES += main.qml RowForm.qml
+
+# Default rules for deployment.
+qnx: target.path = /tmp/$${TARGET}/bin
+else: unix:!android: target.path = /opt/$${TARGET}/bin
+!isEmpty(target.path): INSTALLS += target
diff --git a/tests/manual/tableview/tablemodel/form/main.cpp b/tests/manual/tableview/tablemodel/form/main.cpp
new file mode 100644
index 0000000000..2a3b90d392
--- /dev/null
+++ b/tests/manual/tableview/tablemodel/form/main.cpp
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
+
+ return app.exec();
+}
diff --git a/tests/manual/tableview/tablemodel/form/main.qml b/tests/manual/tableview/tablemodel/form/main.qml
new file mode 100644
index 0000000000..6c6874fb4b
--- /dev/null
+++ b/tests/manual/tableview/tablemodel/form/main.qml
@@ -0,0 +1,290 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import QtQuick.Controls 2.5
+import QtQuick.Layouts 1.12
+import QtQuick.Window 2.12
+import Qt.labs.qmlmodels 1.0
+
+ApplicationWindow {
+ id: window
+ width: 800
+ height: 800
+ visible: true
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ TableView {
+ id: tableView
+ boundsBehavior: Flickable.StopAtBounds
+
+ ScrollBar.horizontal: ScrollBar {}
+ ScrollBar.vertical: ScrollBar {}
+
+ Layout.minimumHeight: window.height / 2
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ model: TableModel {
+ TableModelColumn { display: "checked" }
+ TableModelColumn { display: "amount" }
+ TableModelColumn { display: "fruitType" }
+ TableModelColumn { display: "fruitName" }
+ TableModelColumn { display: "fruitPrice" }
+
+ // One row = one type of fruit that can be ordered
+ rows: [
+ {
+ // Each object (line) is one column,
+ // and each property in that object represents a role.
+ checked: false,
+ amount: 1,
+ fruitType: "Apple",
+ fruitName: "Granny Smith",
+ fruitPrice: 1.50
+ },
+ {
+ checked: true,
+ amount: 4,
+ fruitType: "Orange",
+ fruitName: "Navel",
+ fruitPrice: 2.50
+ },
+ {
+ checked: false,
+ amount: 1,
+ fruitType: "Banana",
+ fruitName: "Cavendish",
+ fruitPrice: 3.50
+ }
+ ]
+ }
+
+ delegate: DelegateChooser {
+ DelegateChoice {
+ column: 0
+ delegate: CheckBox {
+ objectName: "tableViewCheckBoxDelegate"
+ checked: model.display
+ onToggled: model.display = display
+ }
+ }
+ DelegateChoice {
+ column: 1
+ delegate: SpinBox {
+ objectName: "tableViewSpinBoxDelegate"
+ value: model.display
+ onValueModified: model.display = value
+ }
+ }
+ DelegateChoice {
+ delegate: TextField {
+ objectName: "tableViewTextFieldDelegate"
+ text: model.display
+ selectByMouse: true
+ implicitWidth: 140
+ onAccepted: model.display = text
+ }
+ }
+ }
+ }
+
+ TabBar {
+ id: operationTabBar
+
+ Layout.fillWidth: true
+ Layout.preferredHeight: 40
+
+ TabButton {
+ text: "Append"
+ }
+ TabButton {
+ text: "Clear"
+ }
+ TabButton {
+ text: "Insert"
+ }
+ TabButton {
+ text: "Move"
+ }
+ TabButton {
+ text: "Remove"
+ }
+ TabButton {
+ text: "Set"
+ }
+ }
+
+ StackLayout {
+ currentIndex: operationTabBar.currentIndex
+
+ ColumnLayout {
+ RowForm {
+ id: appendRowForm
+
+ Layout.fillHeight: true
+ }
+
+ Button {
+ text: "Append"
+
+ Layout.alignment: Qt.AlignRight
+
+ onClicked: tableView.model.appendRow(appendRowForm.inputAsRow())
+ }
+ }
+ ColumnLayout {
+ Button {
+ text: "Clear"
+ enabled: tableView.rows > 0
+
+ onClicked: tableView.model.clear()
+ }
+ }
+ ColumnLayout {
+ RowForm {
+ id: insertRowForm
+
+ Layout.fillHeight: true
+
+ Label {
+ text: "Insert index"
+ }
+ SpinBox {
+ id: insertIndexSpinBox
+ from: 0
+ to: tableView.rows
+ }
+ }
+
+ Button {
+ text: "Insert"
+
+ Layout.alignment: Qt.AlignRight
+
+ onClicked: tableView.model.insertRow(insertIndexSpinBox.value, insertRowForm.inputAsRow())
+ }
+ }
+ GridLayout {
+ columns: 2
+
+ Label {
+ text: "Move from index"
+ }
+ SpinBox {
+ id: moveFromIndexSpinBox
+ from: 0
+ to: tableView.rows > 0 ? tableView.rows - 1 : 0
+ }
+
+ Label {
+ text: "Move to index"
+ }
+ SpinBox {
+ id: moveToIndexSpinBox
+ from: 0
+ to: tableView.rows > 0 ? tableView.rows - 1 : 0
+ }
+
+ Label {
+ text: "Rows to move"
+ }
+ SpinBox {
+ id: rowsToMoveSpinBox
+ from: 1
+ to: tableView.rows
+ }
+
+ Button {
+ text: "Move"
+ enabled: tableView.rows > 0
+
+ Layout.alignment: Qt.AlignRight
+ Layout.columnSpan: 2
+
+ onClicked: tableView.model.moveRow(moveFromIndexSpinBox.value, moveToIndexSpinBox.value, rowsToMoveSpinBox.value)
+ }
+ }
+ GridLayout {
+ Label {
+ text: "Remove index"
+ }
+ SpinBox {
+ id: removeIndexSpinBox
+ from: 0
+ to: tableView.rows > 0 ? tableView.rows - 1 : 0
+ }
+
+ Button {
+ text: "Remove"
+ enabled: tableView.rows > 0
+
+ Layout.alignment: Qt.AlignRight
+ Layout.columnSpan: 2
+
+ onClicked: tableView.model.removeRow(removeIndexSpinBox.value)
+ }
+ }
+ ColumnLayout {
+ RowForm {
+ id: setRowForm
+
+ Layout.fillHeight: true
+
+ Label {
+ text: "Set index"
+ }
+ SpinBox {
+ id: setIndexSpinBox
+ from: 0
+ to: tableView.rows > 0 ? tableView.rows - 1 : 0
+ }
+ }
+
+ Button {
+ text: "Set"
+
+ onClicked: tableView.model.setRow(setIndexSpinBox.value, setRowForm.inputAsRow());
+ }
+ }
+ }
+ }
+}
diff --git a/tests/manual/tableview/tablemodel/json/JsonData.js b/tests/manual/tableview/tablemodel/json/JsonData.js
new file mode 100644
index 0000000000..a37233c539
--- /dev/null
+++ b/tests/manual/tableview/tablemodel/json/JsonData.js
@@ -0,0 +1,186 @@
+let drivers = [
+ {
+ "driverId":"fisichella",
+ "code":"FIS",
+ "url":"http://en.wikipedia.org/wiki/Giancarlo_Fisichella",
+ "givenName":"Giancarlo",
+ "familyName":"Fisichella",
+ "dateOfBirth":"1973-01-14",
+ "nationality":"Italian"
+ },
+ {
+ "driverId":"barrichello",
+ "code":"BAR",
+ "url":"http://en.wikipedia.org/wiki/Rubens_Barrichello",
+ "givenName":"Rubens",
+ "familyName":"Barrichello",
+ "dateOfBirth":"1972-05-23",
+ "nationality":"Brazilian"
+ },
+ {
+ "driverId":"alonso",
+ "permanentNumber":"14",
+ "code":"ALO",
+ "url":"http://en.wikipedia.org/wiki/Fernando_Alonso",
+ "givenName":"Fernando",
+ "familyName":"Alonso",
+ "dateOfBirth":"1981-07-29",
+ "nationality":"Spanish"
+ },
+ {
+ "driverId":"coulthard",
+ "code":"COU",
+ "url":"http://en.wikipedia.org/wiki/David_Coulthard",
+ "givenName":"David",
+ "familyName":"Coulthard",
+ "dateOfBirth":"1971-03-27",
+ "nationality":"British"
+ },
+ {
+ "driverId":"webber",
+ "code":"WEB",
+ "url":"http://en.wikipedia.org/wiki/Mark_Webber",
+ "givenName":"Mark",
+ "familyName":"Webber",
+ "dateOfBirth":"1976-08-27",
+ "nationality":"Australian"
+ },
+ {
+ "driverId":"montoya",
+ "code":"MON",
+ "url":"http://en.wikipedia.org/wiki/Juan_Pablo_Montoya",
+ "givenName":"Juan",
+ "familyName":"Pablo Montoya",
+ "dateOfBirth":"1975-09-20",
+ "nationality":"Colombian"
+ },
+ {
+ "driverId":"klien",
+ "code":"KLI",
+ "url":"http://en.wikipedia.org/wiki/Christian_Klien",
+ "givenName":"Christian",
+ "familyName":"Klien",
+ "dateOfBirth":"1983-02-07",
+ "nationality":"Austrian"
+ },
+ {
+ "driverId":"raikkonen",
+ "permanentNumber":"7",
+ "code":"RAI",
+ "url":"http://en.wikipedia.org/wiki/Kimi_R%C3%A4ikk%C3%B6nen",
+ "givenName":"Kimi",
+ "familyName":"Räikkönen",
+ "dateOfBirth":"1979-10-17",
+ "nationality":"Finnish"
+ },
+ {
+ "driverId":"trulli",
+ "code":"TRU",
+ "url":"http://en.wikipedia.org/wiki/Jarno_Trulli",
+ "givenName":"Jarno",
+ "familyName":"Trulli",
+ "dateOfBirth":"1974-07-13",
+ "nationality":"Italian"
+ },
+ {
+ "driverId":"massa",
+ "permanentNumber":"19",
+ "code":"MAS",
+ "url":"http://en.wikipedia.org/wiki/Felipe_Massa",
+ "givenName":"Felipe",
+ "familyName":"Massa",
+ "dateOfBirth":"1981-04-25",
+ "nationality":"Brazilian"
+ },
+ {
+ "driverId":"button",
+ "permanentNumber":"22",
+ "code":"BUT",
+ "url":"http://en.wikipedia.org/wiki/Jenson_Button",
+ "givenName":"Jenson",
+ "familyName":"Button",
+ "dateOfBirth":"1980-01-19",
+ "nationality":"British"
+ },
+ {
+ "driverId":"ralf_schumacher",
+ "code":"SCH",
+ "url":"http://en.wikipedia.org/wiki/Ralf_Schumacher",
+ "givenName":"Ralf",
+ "familyName":"Schumacher",
+ "dateOfBirth":"1975-06-30",
+ "nationality":"German"
+ },
+ {
+ "driverId":"villeneuve",
+ "code":"VIL",
+ "url":"http://en.wikipedia.org/wiki/Jacques_Villeneuve",
+ "givenName":"Jacques",
+ "familyName":"Villeneuve",
+ "dateOfBirth":"1971-04-09",
+ "nationality":"Canadian"
+ },
+ {
+ "driverId":"sato",
+ "code":"SAT",
+ "url":"http://en.wikipedia.org/wiki/Takuma_Sato",
+ "givenName":"Takuma",
+ "familyName":"Sato",
+ "dateOfBirth":"1977-01-28",
+ "nationality":"Japanese"
+ },
+ {
+ "driverId":"karthikeyan",
+ "code":"KAR",
+ "url":"http://en.wikipedia.org/wiki/Narain_Karthikeyan",
+ "givenName":"Narain",
+ "familyName":"Karthikeyan",
+ "dateOfBirth":"1977-01-14",
+ "nationality":"Indian"
+ },
+ {
+ "driverId":"monteiro",
+ "code":"TMO",
+ "url":"http://en.wikipedia.org/wiki/Tiago_Monteiro",
+ "givenName":"Tiago",
+ "familyName":"Monteiro",
+ "dateOfBirth":"1976-07-24",
+ "nationality":"Portuguese"
+ },
+ {
+ "driverId":"friesacher",
+ "code":"FRI",
+ "url":"http://en.wikipedia.org/wiki/Patrick_Friesacher",
+ "givenName":"Patrick",
+ "familyName":"Friesacher",
+ "dateOfBirth":"1980-09-26",
+ "nationality":"Austrian"
+ },
+ {
+ "driverId":"michael_schumacher",
+ "code":"MSC",
+ "url":"http://en.wikipedia.org/wiki/Michael_Schumacher",
+ "givenName":"Michael",
+ "familyName":"Schumacher",
+ "dateOfBirth":"1969-01-03",
+ "nationality":"German"
+ },
+ {
+ "driverId":"heidfeld",
+ "code":"HEI",
+ "url":"http://en.wikipedia.org/wiki/Nick_Heidfeld",
+ "givenName":"Nick",
+ "familyName":"Heidfeld",
+ "dateOfBirth":"1977-05-10",
+ "nationality":"German"
+ },
+ {
+ "driverId":"albers",
+ "code":"ALB",
+ "url":"http://en.wikipedia.org/wiki/Christijan_Albers",
+ "givenName":"Christijan",
+ "familyName":"Albers",
+ "dateOfBirth":"1979-04-16",
+ "nationality":"Dutch"
+ }
+]
diff --git a/tests/manual/tableview/tablemodel/json/json.pro b/tests/manual/tableview/tablemodel/json/json.pro
new file mode 100644
index 0000000000..2339e488aa
--- /dev/null
+++ b/tests/manual/tableview/tablemodel/json/json.pro
@@ -0,0 +1,12 @@
+TEMPLATE = app
+TARGET = json
+QT += qml quick
+SOURCES += main.cpp
+RESOURCES += \
+ main.qml \
+ JsonData.js
+
+# Default rules for deployment.
+qnx: target.path = /tmp/$${TARGET}/bin
+else: unix:!android: target.path = /opt/$${TARGET}/bin
+!isEmpty(target.path): INSTALLS += target
diff --git a/tests/manual/tableview/tablemodel/json/main.cpp b/tests/manual/tableview/tablemodel/json/main.cpp
new file mode 100644
index 0000000000..176b532d49
--- /dev/null
+++ b/tests/manual/tableview/tablemodel/json/main.cpp
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
+
+ return app.exec();
+}
diff --git a/tests/manual/tableview/tablemodel/json/main.qml b/tests/manual/tableview/tablemodel/json/main.qml
new file mode 100644
index 0000000000..2a835459c0
--- /dev/null
+++ b/tests/manual/tableview/tablemodel/json/main.qml
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import QtQuick.Controls 2.5
+import QtQuick.Layouts 1.12
+import QtQuick.Window 2.12
+import Qt.labs.qmlmodels 1.0
+
+import "JsonData.js" as CachedJsonData
+
+ApplicationWindow {
+ id: window
+ width: 800
+ height: 300
+ visible: true
+
+ function requestJson() {
+ let doc = new XMLHttpRequest()
+ doc.onreadystatechange = function() {
+ if (doc.readyState === XMLHttpRequest.DONE) {
+ var root = JSON.parse(doc.responseText)
+ var race = root.MRData.RaceTable.Races[0]
+ var raceResults = race.Results
+ var drivers = []
+ for (let i = 0; i < raceResults.length; ++i) {
+ drivers.push(raceResults[i].Driver)
+ }
+ tableView.model.rows = drivers
+ print(JSON.stringify(drivers))
+ }
+ }
+
+ doc.open("GET", "http://ergast.com/api/f1/2005/1/results.json")
+ doc.send()
+ }
+
+ Component.onCompleted: requestJson()
+ // Same as the data we get from ergast. Use it while developing
+ // to avoid flooding the server with requests.
+// Component.onCompleted: tableView.model.rows = CachedJsonData.drivers
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ TableView {
+ id: tableView
+ boundsBehavior: Flickable.StopAtBounds
+
+ ScrollBar.horizontal: ScrollBar {
+ policy: ScrollBar.AlwaysOn
+ }
+ ScrollBar.vertical: ScrollBar {
+ policy: ScrollBar.AlwaysOn
+ }
+
+ Layout.minimumHeight: window.height / 2
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ model: TableModel {
+ TableModelColumn { display: "driverId" }
+ TableModelColumn { display: "code" }
+ TableModelColumn { display: "url" }
+ TableModelColumn { display: "givenName" }
+ TableModelColumn { display: "familyName" }
+ TableModelColumn { display: "dateOfBirth" }
+ TableModelColumn { display: "nationality" }
+ }
+
+ delegate: TextField {
+ objectName: "tableViewTextFieldDelegate"
+ text: model.display
+ selectByMouse: true
+ implicitWidth: 140
+ onAccepted: model.display = text
+ }
+ }
+ }
+}
diff --git a/tests/manual/tableview/tablemodel/tablemodel.pro b/tests/manual/tableview/tablemodel/tablemodel.pro
new file mode 100644
index 0000000000..4e4eba7653
--- /dev/null
+++ b/tests/manual/tableview/tablemodel/tablemodel.pro
@@ -0,0 +1,2 @@
+TEMPLATE = subdirs
+SUBDIRS += form