diff options
Diffstat (limited to 'tests/auto')
27 files changed, 1370 insertions, 129 deletions
diff --git a/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp b/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp index cbe3be2e70..33efcb4da4 100644 --- a/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp +++ b/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp @@ -509,7 +509,7 @@ static bool executeTest(const QByteArray &data, bool runAsModule = false, const QVector<QUrl> modulesToLoad = { rootModuleUrl }; while (!modulesToLoad.isEmpty()) { QUrl url = modulesToLoad.takeFirst(); - QQmlRefPointer<QV4::CompiledData::CompilationUnit> module; + QQmlRefPointer<QV4::ExecutableCompilationUnit> module; QFile f(url.toLocalFile()); if (f.open(QIODevice::ReadOnly)) { diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp index e86b7bf5fe..c215e9620d 100644 --- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -33,6 +33,7 @@ #include <private/qv4engine_p.h> #include <private/qv4codegen_p.h> #include <private/qqmlcomponent_p.h> +#include <private/qv4executablecompilationunit_p.h> #include <QQmlComponent> #include <QQmlEngine> #include <QQmlFileSelector> @@ -118,7 +119,8 @@ struct TestCompiler { closeMapping(); testFilePath = baseDirectory + QStringLiteral("/test.qml"); - cacheFilePath = QV4::CompiledData::CompilationUnit::localCacheFilePath(QUrl::fromLocalFile(testFilePath)); + cacheFilePath = QV4::ExecutableCompilationUnit::localCacheFilePath( + QUrl::fromLocalFile(testFilePath)); mappedFile.setFileName(cacheFilePath); } @@ -185,8 +187,10 @@ struct TestCompiler bool verify() { - QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading(); - return unit->loadFromDisk(QUrl::fromLocalFile(testFilePath), QFileInfo(testFilePath).lastModified(), &lastErrorString); + QQmlRefPointer<QV4::ExecutableCompilationUnit> unit + = QV4::ExecutableCompilationUnit::create(); + return unit->loadFromDisk(QUrl::fromLocalFile(testFilePath), + QFileInfo(testFilePath).lastModified(), &lastErrorString); } void closeMapping() @@ -263,8 +267,9 @@ void tst_qmldiskcache::loadLocalAsFallback() f.write(reinterpret_cast<const char *>(&unit), sizeof(unit)); } - QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading(); - bool loaded = unit->loadFromDisk(QUrl::fromLocalFile(testCompiler.testFilePath), QFileInfo(testCompiler.testFilePath).lastModified(), + QQmlRefPointer<QV4::ExecutableCompilationUnit> unit = QV4::ExecutableCompilationUnit::create(); + bool loaded = unit->loadFromDisk(QUrl::fromLocalFile(testCompiler.testFilePath), + QFileInfo(testCompiler.testFilePath).lastModified(), &testCompiler.lastErrorString); QVERIFY2(loaded, qPrintable(testCompiler.lastErrorString)); QCOMPARE(unit->objectCount(), 1); @@ -323,7 +328,10 @@ void tst_qmldiskcache::regenerateAfterChange() const QV4::CompiledData::Object *obj = qmlUnit->objectAt(0); QCOMPARE(quint32(obj->nBindings), quint32(2)); QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Number)); - QCOMPARE(obj->bindingTable()->valueAsNumber(reinterpret_cast<const QV4::Value *>(testUnit->constants())), double(42)); + + QCOMPARE(reinterpret_cast<const QV4::Value *>(testUnit->constants()) + [obj->bindingTable()->value.constantValueIndex].doubleValue(), + double(42)); QCOMPARE(quint32(testUnit->functionTableSize), quint32(1)); @@ -572,7 +580,8 @@ void tst_qmldiskcache::fileSelectors() QVERIFY(!obj.isNull()); QCOMPARE(obj->property("value").toInt(), 42); - QFile cacheFile(QV4::CompiledData::CompilationUnit::localCacheFilePath(QUrl::fromLocalFile(testFilePath))); + QFile cacheFile(QV4::ExecutableCompilationUnit::localCacheFilePath( + QUrl::fromLocalFile(testFilePath))); QVERIFY2(cacheFile.exists(), qPrintable(cacheFile.fileName())); } @@ -587,7 +596,8 @@ void tst_qmldiskcache::fileSelectors() QVERIFY(!obj.isNull()); QCOMPARE(obj->property("value").toInt(), 100); - QFile cacheFile(QV4::CompiledData::CompilationUnit::localCacheFilePath(QUrl::fromLocalFile(selectedTestFilePath))); + QFile cacheFile(QV4::ExecutableCompilationUnit::localCacheFilePath( + QUrl::fromLocalFile(selectedTestFilePath))); QVERIFY2(cacheFile.exists(), qPrintable(cacheFile.fileName())); } } @@ -737,7 +747,8 @@ void tst_qmldiskcache::stableOrderOfDependentCompositeTypes() QVERIFY2(firstDependentTypeClassName.contains("QMLTYPE"), firstDependentTypeClassName.constData()); QVERIFY2(secondDependentTypeClassName.contains("QMLTYPE"), secondDependentTypeClassName.constData()); - const QString testFileCachePath = QV4::CompiledData::CompilationUnit::localCacheFilePath(QUrl::fromLocalFile(testFilePath)); + const QString testFileCachePath = QV4::ExecutableCompilationUnit::localCacheFilePath( + QUrl::fromLocalFile(testFilePath)); QVERIFY(QFile::exists(testFileCachePath)); QDateTime initialCacheTimeStamp = QFileInfo(testFileCachePath).lastModified(); @@ -815,7 +826,8 @@ void tst_qmldiskcache::singletonDependency() QCOMPARE(obj->property("value").toInt(), 42); } - const QString testFileCachePath = QV4::CompiledData::CompilationUnit::localCacheFilePath(QUrl::fromLocalFile(testFilePath)); + const QString testFileCachePath = QV4::ExecutableCompilationUnit::localCacheFilePath( + QUrl::fromLocalFile(testFilePath)); QVERIFY(QFile::exists(testFileCachePath)); QDateTime initialCacheTimeStamp = QFileInfo(testFileCachePath).lastModified(); @@ -872,7 +884,8 @@ void tst_qmldiskcache::cppRegisteredSingletonDependency() QCOMPARE(value.toInt(), 42); } - const QString testFileCachePath = QV4::CompiledData::CompilationUnit::localCacheFilePath(QUrl::fromLocalFile(testFilePath)); + const QString testFileCachePath = QV4::ExecutableCompilationUnit::localCacheFilePath( + QUrl::fromLocalFile(testFilePath)); QVERIFY(QFile::exists(testFileCachePath)); QDateTime initialCacheTimeStamp = QFileInfo(testFileCachePath).lastModified(); diff --git a/tests/auto/qml/qqmlbinding/data/restoreBinding2.qml b/tests/auto/qml/qqmlbinding/data/restoreBinding2.qml new file mode 100644 index 0000000000..b42f975fe0 --- /dev/null +++ b/tests/auto/qml/qqmlbinding/data/restoreBinding2.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.0 +import QtQml 2.14 + +Rectangle { + height: 400 + width: 400 + + Rectangle { + property bool when: true + + id: myItem + objectName: "myItem" + height: 300 + width: 200 + } + + property var boundValue: 100 + + Binding { + restoreMode: Binding.RestoreValue + objectName: "theBinding" + target: myItem + property: "height" + when: myItem.when + value: boundValue + } +} diff --git a/tests/auto/qml/qqmlbinding/data/restoreBinding3.qml b/tests/auto/qml/qqmlbinding/data/restoreBinding3.qml new file mode 100644 index 0000000000..1f63457ab6 --- /dev/null +++ b/tests/auto/qml/qqmlbinding/data/restoreBinding3.qml @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** 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.0 +import QtQml 2.14 + +Rectangle { + height: 400 + width: 400 + + Rectangle { + property bool when: true + + id: myItem + objectName: "myItem" + height: 300 + width: 200 + property var foo: 42 + } + + property var boundValue: 13 + + Binding { + restoreMode: Binding.RestoreBindingOrValue + objectName: "theBinding" + target: myItem + property: "foo" + when: myItem.when + value: boundValue + } +} diff --git a/tests/auto/qml/qqmlbinding/data/restoreBinding4.qml b/tests/auto/qml/qqmlbinding/data/restoreBinding4.qml new file mode 100644 index 0000000000..f34c3b40cf --- /dev/null +++ b/tests/auto/qml/qqmlbinding/data/restoreBinding4.qml @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** 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.0 +import QtQml 2.14 + +Rectangle { + height: 400 + width: 400 + + Rectangle { + property bool when: true + + id: myItem + objectName: "myItem" + height: 300 + width: 200 + property var foo: original + property bool fooCheck: foo === original && foo.bar() === 42 + } + + property var original: ({ bar: function() { return 42 } }) + + property var boundValue: 13 + + Binding { + restoreMode: Binding.RestoreBinding + objectName: "theBinding" + target: myItem + property: "foo" + when: myItem.when + value: boundValue + } +} diff --git a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp index 34cf21024d..717fd5dbd1 100644 --- a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp +++ b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp @@ -42,6 +42,9 @@ private slots: void binding(); void whenAfterValue(); void restoreBinding(); + void restoreBindingValue(); + void restoreBindingVarValue(); + void restoreBindingJSValue(); void restoreBindingWithLoop(); void restoreBindingWithoutCrash(); void deletedObject(); @@ -134,6 +137,78 @@ void tst_qqmlbinding::restoreBinding() delete rect; } +void tst_qqmlbinding::restoreBindingValue() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("restoreBinding2.qml")); + QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(c.create())); + QVERIFY(!rect.isNull()); + + auto myItem = qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("myItem")); + QVERIFY(myItem != nullptr); + + QCOMPARE(myItem->height(), 100); + myItem->setProperty("when", QVariant(false)); + QCOMPARE(myItem->height(), 300); // make sure the original value was restored + + myItem->setProperty("when", QVariant(true)); + QCOMPARE(myItem->height(), 100); // make sure the value specified in Binding is set + rect->setProperty("boundValue", 200); + QCOMPARE(myItem->height(), 200); // make sure the changed binding value is set + myItem->setProperty("when", QVariant(false)); + // make sure that the original value is back, not e.g. the value from before the + // change (i.e. 100) + QCOMPARE(myItem->height(), 300); +} + +void tst_qqmlbinding::restoreBindingVarValue() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("restoreBinding3.qml")); + QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(c.create())); + QVERIFY(!rect.isNull()); + + auto myItem = qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("myItem")); + QVERIFY(myItem != nullptr); + + QCOMPARE(myItem->property("foo"), 13); + myItem->setProperty("when", QVariant(false)); + QCOMPARE(myItem->property("foo"), 42); // make sure the original value was restored + + myItem->setProperty("when", QVariant(true)); + QCOMPARE(myItem->property("foo"), 13); // make sure the value specified in Binding is set + rect->setProperty("boundValue", 31337); + QCOMPARE(myItem->property("foo"), 31337); // make sure the changed binding value is set + myItem->setProperty("when", QVariant(false)); + // make sure that the original value is back, not e.g. the value from before the + // change (i.e. 100) + QCOMPARE(myItem->property("foo"), 42); +} + +void tst_qqmlbinding::restoreBindingJSValue() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("restoreBinding4.qml")); + QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(c.create())); + QVERIFY(!rect.isNull()); + + auto myItem = qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("myItem")); + QVERIFY(myItem != nullptr); + + QCOMPARE(myItem->property("fooCheck"), false); + myItem->setProperty("when", QVariant(false)); + QCOMPARE(myItem->property("fooCheck"), true); // make sure the original value was restored + + myItem->setProperty("when", QVariant(true)); + QCOMPARE(myItem->property("fooCheck"), false); // make sure the value specified in Binding is set + rect->setProperty("boundValue", 31337); + QCOMPARE(myItem->property("fooCheck"), false); // make sure the changed binding value is set + myItem->setProperty("when", QVariant(false)); + // make sure that the original value is back, not e.g. the value from before the change + QCOMPARE(myItem->property("fooCheck"), true); + +} + void tst_qqmlbinding::restoreBindingWithLoop() { QQmlEngine engine; diff --git a/tests/auto/qml/qqmlengine/data/CachedGetterLookup.qml b/tests/auto/qml/qqmlengine/data/CachedGetterLookup.qml new file mode 100644 index 0000000000..1a2a7ff341 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/CachedGetterLookup.qml @@ -0,0 +1,20 @@ +import QtQuick 2.12 + +QtObject { + Component.onCompleted: { + // create getter + var getFoo = function(o) { return o.foo; } + + // create two diffrent shapes for x,y + var x = { foo:1 , bar:2 } + var y = { bar:2 , foo:1 } + + // initialize inline cache with getFoo + getFoo(x); + getFoo(y); + + // hit getter lookup with string "crash" + getFoo('crash'); + } +} + diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp index b9cb6b70d2..0cb6753020 100644 --- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp +++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp @@ -81,6 +81,7 @@ private slots: void cppSignalAndEval(); void singletonInstance(); void aggressiveGc(); + void cachedGetterLookup_qtbug_75335(); public slots: QObject *createAQObjectForOwnershipTest () @@ -1056,6 +1057,16 @@ void tst_qqmlengine::aggressiveGc() qputenv("QV4_MM_AGGRESSIVE_GC", origAggressiveGc); } +void tst_qqmlengine::cachedGetterLookup_qtbug_75335() +{ + QQmlEngine engine; + const QUrl testUrl = testFileUrl("CachedGetterLookup.qml"); + QQmlComponent component(&engine, testUrl); + QVERIFY(component.isReady()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); +} + QTEST_MAIN(tst_qqmlengine) #include "tst_qqmlengine.moc" 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/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp index f9a6ee8e5a..d6215307bf 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.cpp +++ b/tests/auto/qml/qqmllanguage/testtypes.cpp @@ -125,7 +125,7 @@ QVariant myCustomVariantTypeConverter(const QString &data) } -void CustomBindingParser::applyBindings(QObject *object, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) +void CustomBindingParser::applyBindings(QObject *object, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) { CustomBinding *customBinding = qobject_cast<CustomBinding*>(object); Q_ASSERT(customBinding); @@ -154,7 +154,7 @@ void CustomBinding::componentComplete() } } -void EnumSupportingCustomParser::verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) +void EnumSupportingCustomParser::verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) { if (bindings.count() != 1) { error(bindings.first(), QStringLiteral("Custom parser invoked incorrectly for unit test")); @@ -184,7 +184,7 @@ void EnumSupportingCustomParser::verifyBindings(const QQmlRefPointer<QV4::Compil } } -void SimpleObjectCustomParser::applyBindings(QObject *object, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &bindings) +void SimpleObjectCustomParser::applyBindings(QObject *object, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &bindings) { SimpleObjectWithCustomParser *o = qobject_cast<SimpleObjectWithCustomParser*>(object); Q_ASSERT(o); diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h index bb6e9582c2..0618d2b20f 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.h +++ b/tests/auto/qml/qqmllanguage/testtypes.h @@ -781,15 +781,15 @@ class MyCustomParserType : public QObject class MyCustomParserTypeParser : public QQmlCustomParser { public: - virtual void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {} - virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {} + virtual void verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {} + virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {} }; class EnumSupportingCustomParser : public QQmlCustomParser { public: - virtual void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &); - virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {} + virtual void verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &); + virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {} }; class MyParserStatus : public QObject, public QQmlParserStatus @@ -1275,15 +1275,15 @@ public: void setTarget(QObject *newTarget) { m_target = newTarget; } QPointer<QObject> m_target; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; QList<const QV4::CompiledData::Binding*> bindings; QByteArray m_bindingData; }; class CustomBindingParser : public QQmlCustomParser { - virtual void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {} - virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &); + virtual void verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {} + virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &); }; class SimpleObjectWithCustomParser : public QObject @@ -1328,8 +1328,8 @@ private: class SimpleObjectCustomParser : public QQmlCustomParser { - virtual void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {} - virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &); + virtual void verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {} + virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &); }; class RootObjectInCreationTester : public QObject diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index b97c75e2ce..8cbb39974e 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; } @@ -2223,7 +2225,7 @@ void tst_qqmllanguage::scriptStringWithoutSourceCode() QV4::CompiledData::Unit *qmlUnit = reinterpret_cast<QV4::CompiledData::Unit *>(malloc(readOnlyQmlUnit->unitSize)); memcpy(qmlUnit, readOnlyQmlUnit, readOnlyQmlUnit->unitSize); qmlUnit->flags &= ~QV4::CompiledData::Unit::StaticData; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = td->compilationUnit(); + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit = td->compilationUnit(); compilationUnit->setUnitData(qmlUnit); const QV4::CompiledData::Object *rootObject = compilationUnit->objectAt(/*root object*/0); @@ -2233,9 +2235,9 @@ void tst_qqmllanguage::scriptStringWithoutSourceCode() const QV4::CompiledData::Binding *binding = rootObject->bindingTable() + i; if (compilationUnit->stringAt(binding->propertyNameIndex) != QString("scriptProperty")) continue; - QCOMPARE(binding->valueAsScriptString(compilationUnit.data()), QString("intProperty")); + QCOMPARE(compilationUnit->bindingValueAsScriptString(binding), QString("intProperty")); const_cast<QV4::CompiledData::Binding*>(binding)->stringIndex = 0; // empty string index - QVERIFY(binding->valueAsScriptString(compilationUnit.data()).isEmpty()); + QVERIFY(compilationUnit->bindingValueAsScriptString(binding).isEmpty()); break; } QVERIFY(i < rootObject->nBindings); diff --git a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp index 809a9bd9db..3b17df9872 100644 --- a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp +++ b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp @@ -85,7 +85,8 @@ void tst_qqmltranslation::translation() << QStringLiteral("disambiguation") << QStringLiteral("singular") << QStringLiteral("plural"); - const QV4::CompiledData::Object *rootObject = compilationUnit->objectAt(/*root object*/0); + const QV4::CompiledData::Object *rootObject + = compilationUnit->qmlData->objectAt(/*root object*/0); const QV4::CompiledData::Binding *binding = rootObject->bindingTable(); for (quint32 i = 0; i < rootObject->nBindings; ++i, ++binding) { const QString propertyName = compilationUnit->stringAt(binding->propertyNameIndex); @@ -139,7 +140,8 @@ void tst_qqmltranslation::idTranslation() QV4::CompiledData::CompilationUnit *compilationUnit = typeData->compilationUnit(); QVERIFY(compilationUnit); - const QV4::CompiledData::Object *rootObject = compilationUnit->objectAt(/*root object*/0); + const QV4::CompiledData::Object *rootObject + = compilationUnit->qmlData->objectAt(/*root object*/0); const QV4::CompiledData::Binding *binding = rootObject->bindingTable(); for (quint32 i = 0; i < rootObject->nBindings; ++i, ++binding) { const QString propertyName = compilationUnit->stringAt(binding->propertyNameIndex); 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/qmltest/statemachine/tst_parallelmachine.qml b/tests/auto/qmltest/statemachine/tst_parallelmachine.qml index be7d73fbe5..eb996c7718 100644 --- a/tests/auto/qmltest/statemachine/tst_parallelmachine.qml +++ b/tests/auto/qmltest/statemachine/tst_parallelmachine.qml @@ -32,25 +32,29 @@ import QtQml.StateMachine 1.0 TestCase { StateMachine { id: myStateMachine - childMode: State.ParallelStates + initialState: rootState State { - id: childState1 + id: rootState childMode: State.ParallelStates State { - id: childState11 + id: childState1 + childMode: State.ParallelStates + State { + id: childState11 + } + State { + id: childState12 + } } State { - id: childState12 - } - } - State { - id: childState2 - initialState: childState21 - State { - id: childState21 - } - State { - id: childState22 + id: childState2 + initialState: childState21 + State { + id: childState21 + } + State { + id: childState22 + } } } } diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.qml new file mode 100644 index 0000000000..d6eb791700 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.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 + +Item { + id: root + objectName: "snapMode" + width: 640 + height: 480 + + Rectangle { + id: rect1 + objectName: "rect1" + width: 90 + height: 100 + x: 100 + y: 100 + color: "teal" + + Rectangle { + width: parent.width/2 + height: parent.width/2 + x: width/2 + y: -x + color: dragHandler1.active ? "red" : "salmon" + + DragHandler { + id: dragHandler1 + objectName: "dragHandler1" + target: rect1 + } + } + } + + + Rectangle { + id: rect2 + objectName: "rect2" + width: 90 + height: 100 + x: 200 + y: 100 + color: "teal" + + DragHandler { + id: dragHandler2 + objectName: "dragHandler2" + target: rect2b + } + + Rectangle { + id: rect2b + width: parent.width/2 + height: parent.width/2 + anchors.horizontalCenter: parent.horizontalCenter + y: -width/2 + color: dragHandler2.active ? "red" : "salmon" + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/qquickdraghandler.pro b/tests/auto/quick/pointerhandlers/qquickdraghandler/qquickdraghandler.pro index 42c4e46c4f..6258fb9392 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/qquickdraghandler.pro +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/qquickdraghandler.pro @@ -19,3 +19,4 @@ OTHER_FILES += data/DragAnywhereSlider.qml \ data/grabberstate.qml \ data/multipleSliders.qml \ data/reparenting.qml \ + data/snapMode.qml \ diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp index cc8c567e5c..7636bb38f1 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp @@ -56,6 +56,8 @@ private slots: void touchDrag(); void mouseDrag(); void dragFromMargin(); + void snapMode_data(); + void snapMode(); void touchDragMulti(); void touchDragMultiSliders_data(); void touchDragMultiSliders(); @@ -284,6 +286,78 @@ void tst_DragHandler::dragFromMargin() // QTBUG-74966 QCOMPARE(dragHandler->centroid().pressedButtons(), Qt::NoButton); } +void tst_DragHandler::snapMode_data() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QTest::addColumn<QString>("subTree"); + QTest::addColumn<int>("snapMode"); + QTest::addColumn<QPoint>("startDragPos"); + QTest::addColumn<QPoint>("dragMovement"); + QTest::addColumn<QPoint>("expectedMovement"); + + struct TestEntry { + const char *desc; + const char *subTree; + QQuickDragHandler::SnapMode mode; + QPoint startDragPos; + QPoint dragMovement; + QPoint expectedMovement; + }; + + TestEntry testdata[] = { + {"outside the target", "rect1", QQuickDragHandler::SnapAuto, QPoint(45, -10), QPoint(dragThreshold*2, 0), QPoint(dragThreshold*2, 0)}, + {"inside the target", "rect1", QQuickDragHandler::SnapAuto, QPoint(45, 10), QPoint(dragThreshold*2, 0), QPoint(dragThreshold*2, 0)}, + {"outside the target", "rect1", QQuickDragHandler::SnapAlways, QPoint(45, -10), QPoint(dragThreshold*2, 0), QPoint(dragThreshold*2, -50-10)}, + {"outside the target", "rect1", QQuickDragHandler::NoSnap, QPoint(45, -10), QPoint(dragThreshold*2, 0), QPoint(dragThreshold*2, 0)}, + {"outside the target", "rect1", QQuickDragHandler::SnapIfPressedOutsideTarget, QPoint(45, -10), QPoint(dragThreshold*2, 0), QPoint(dragThreshold*2, -50-10)}, + {"inside the target", "rect1", QQuickDragHandler::SnapIfPressedOutsideTarget, QPoint(45, 10), QPoint(dragThreshold*2, 0), QPoint(dragThreshold*2, 0)}, + //targets y pos moves from -25 to (25 + dragThreshold*2) because of snapping to center: + {"outside target, should snap", "rect2", QQuickDragHandler::SnapAuto, QPoint(45, 50), QPoint(0, dragThreshold*2), QPoint(0, 25 + 25 + dragThreshold*2)}, + {"inside target, shouldn't snap", "rect2", QQuickDragHandler::SnapAuto, QPoint(45, 10), QPoint(0, dragThreshold*2), QPoint(0, dragThreshold*2)} + }; + + for (const TestEntry& e : testdata) { + const QMetaEnum menum = QMetaEnum::fromType<QQuickDragHandler::SnapMode>(); + const QString dataTag = QString::fromLatin1("%1, %2, %3").arg(e.subTree).arg(menum.valueToKey(e.mode)).arg(e.desc); + QTest::newRow(dataTag.toUtf8().constData()) << e.subTree << (int)e.mode + << e.startDragPos << e.dragMovement << e.expectedMovement; + } +} + +void tst_DragHandler::snapMode() +{ + QFETCH(QString, subTree); + QFETCH(QPoint, startDragPos); + QFETCH(QPoint, dragMovement); + QFETCH(int, snapMode); + QFETCH(QPoint, expectedMovement); + + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "snapMode.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *rect1 = window->rootObject()->findChild<QQuickItem*>(subTree); + QVERIFY(rect1); + QQuickItem *rect1b = rect1->childItems().first(); + QVERIFY(rect1b); + QQuickDragHandler *dragHandler1 = rect1->findChild<QQuickDragHandler*>(); + QVERIFY(dragHandler1); + dragHandler1->setSnapMode((QQuickDragHandler::SnapMode)snapMode); + QQuickItem *dragTarget = dragHandler1->target(); + QPointF oldTargetPos = dragTarget->position(); + + QPoint p1 = rect1->mapToScene(QPointF(startDragPos)).toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QVERIFY(!dragHandler1->active()); + p1 += dragMovement; + QTest::mouseMove(window, p1); + QTRY_VERIFY(dragHandler1->active()); + QCOMPARE(dragTarget->position(), oldTargetPos + expectedMovement); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(!dragHandler1->active()); + QCOMPARE(dragHandler1->centroid().pressedButtons(), Qt::NoButton); +} + void tst_DragHandler::touchDragMulti() { const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); diff --git a/tests/auto/quick/qquickaccessible/data/statictext.qml b/tests/auto/quick/qquickaccessible/data/text.qml index 1092f33daf..6daeacfd81 100644 --- a/tests/auto/quick/qquickaccessible/data/statictext.qml +++ b/tests/auto/quick/qquickaccessible/data/text.qml @@ -28,4 +28,33 @@ Item { Accessible.name: "The Hello 2 accessible text" Accessible.description: "A text description" } + + TextInput { + x: 100 + y: 80 + width: 200 + height: 40 + text: "A text input" + Accessible.role: Accessible.EditableText + } + + TextEdit { + x: 100 + y: 120 + width: 200 + height: 100 + text: "A multi-line text edit\nTesting Accessibility." + Accessible.role: Accessible.EditableText + } + + Text { + x: 100 + y: 160 + width: 100 + height: 40 + text : "Hello 3" + Accessible.name: text + Accessible.description: "description" + } + } diff --git a/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp b/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp index 1bfeb94161..c5fdb6c1b9 100644 --- a/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp +++ b/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp @@ -130,7 +130,7 @@ void tst_QQuickAccessible::commonTests_data() { QTest::addColumn<QString>("accessibleRoleFileName"); - QTest::newRow("StaticText") << "statictext.qml"; + QTest::newRow("Text") << "text.qml"; QTest::newRow("PushButton") << "pushbutton.qml"; } @@ -302,7 +302,7 @@ void tst_QQuickAccessible::basicPropertiesTest() QCOMPARE(app->childCount(), 0); QQuickView *window = new QQuickView(); - window->setSource(testFileUrl("statictext.qml")); + window->setSource(testFileUrl("text.qml")); window->show(); QCOMPARE(app->childCount(), 1); @@ -312,7 +312,7 @@ void tst_QQuickAccessible::basicPropertiesTest() QAccessibleInterface *item = iface->child(0); QVERIFY(item); - QCOMPARE(item->childCount(), 2); + QCOMPARE(item->childCount(), 5); QCOMPARE(item->rect().size(), QSize(400, 400)); QCOMPARE(item->role(), QAccessible::Client); QCOMPARE(iface->indexOfChild(item), 0); @@ -338,10 +338,74 @@ void tst_QQuickAccessible::basicPropertiesTest() QCOMPARE(text2->rect().y(), item->rect().y() + 40); QCOMPARE(text2->role(), QAccessible::StaticText); QCOMPARE(item->indexOfChild(text2), 1); + QCOMPARE(text2->state().editable, 0); + QCOMPARE(text2->state().readOnly, 1); QCOMPARE(iface->indexOfChild(text2), -1); QCOMPARE(text2->indexOfChild(item), -1); + // TextInput + QAccessibleInterface *textInput = item->child(2); + QVERIFY(textInput); + QCOMPARE(textInput->childCount(), 0); + QCOMPARE(textInput->role(), QAccessible::EditableText); + QCOMPARE(textInput->state().editable, 1); + QCOMPARE(textInput->state().readOnly, 0); + QCOMPARE(textInput->state().multiLine, 0); + QCOMPARE(textInput->state().focusable, 1); + QCOMPARE(textInput->text(QAccessible::Value), "A text input"); + auto textInterface = textInput->textInterface(); + QVERIFY(textInterface); + auto editableTextInterface = textInput->editableTextInterface(); + QEXPECT_FAIL("", "EditableTextInterface is not implemented", Continue); + QVERIFY(editableTextInterface); + auto newText = QString("a new text"); + textInput->setText(QAccessible::Value, newText); + QCOMPARE(textInput->text(QAccessible::Value), newText); + + // TextEdit + QAccessibleInterface *textEdit = item->child(3); + QVERIFY(textEdit); + QCOMPARE(textEdit->childCount(), 0); + QCOMPARE(textEdit->role(), QAccessible::EditableText); + QCOMPARE(textEdit->state().editable, 1); + QCOMPARE(textEdit->state().readOnly, 0); + QCOMPARE(textEdit->state().focusable, 1); + QCOMPARE(textEdit->text(QAccessible::Value), "A multi-line text edit\nTesting Accessibility."); + auto textEditTextInterface = textEdit->textInterface(); + QVERIFY(textEditTextInterface); + auto textEditEditableTextInterface = textEdit->editableTextInterface(); + QEXPECT_FAIL("", "EditableTextInterface is not implemented", Continue); + QVERIFY(textEditEditableTextInterface); + textEdit->setText(QAccessible::Value, newText); + QCOMPARE(textEdit->text(QAccessible::Value), newText); + QEXPECT_FAIL("", "multi line is not implemented", Continue); + QCOMPARE(textInput->state().multiLine, 1); + + // Text "Hello 3" + QAccessibleInterface *text3 = item->child(4); + QVERIFY(text3); + QCOMPARE(text3->childCount(), 0); + QCOMPARE(text3->text(QAccessible::Name), QLatin1String("Hello 3")); + QCOMPARE(text3->role(), QAccessible::StaticText); + QCOMPARE(item->indexOfChild(text3), 4); + QCOMPARE(text3->state().editable, 0); + QCOMPARE(text3->state().readOnly, 0); + // test implicit state values due to role change + QQuickAccessibleAttached *attached = QQuickAccessibleAttached::attachedProperties(text3->object()); + attached->setRole(QAccessible::StaticText); + QCOMPARE(text3->role(), QAccessible::StaticText); + QCOMPARE(text3->state().readOnly, 1); + + // see if implicit changes back + attached->setRole(QAccessible::EditableText); + QEXPECT_FAIL("", "EditableText does not implicitly set readOnly to false", Continue); + QCOMPARE(text3->state().readOnly, 0); + // explicitly set state + attached->set_readOnly(false); + attached->setRole(QAccessible::StaticText); + QCOMPARE(text3->state().readOnly, 0); + delete window; QTestAccessibility::clearEvents(); } diff --git a/tests/auto/quick/qquicklistview/BLACKLIST b/tests/auto/quick/qquicklistview/BLACKLIST index 893f95dcea..6dd7302661 100644 --- a/tests/auto/quick/qquicklistview/BLACKLIST +++ b/tests/auto/quick/qquicklistview/BLACKLIST @@ -9,3 +9,7 @@ opensuse-42.1 [contentHeightWithDelayRemove] osx-10.12 + +#QTBUG-75960 +[currentIndex] +osx-10.12 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/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp index 6c339c16d1..fbe56abda5 100644 --- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp +++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp @@ -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() @@ -552,7 +563,7 @@ void tst_QQuickTableView::checkForceLayoutFunction() void tst_QQuickTableView::checkContentWidthAndHeight() { - // Check that contentWidth/Height reports the correct size of the the + // Check that contentWidth/Height reports the correct size of the // table, based on knowledge of the rows and columns that has been loaded. LOAD_TABLEVIEW("contentwidthheight.qml"); @@ -563,11 +574,7 @@ void tst_QQuickTableView::checkContentWidthAndHeight() const int tableSize = 100; const int cellSizeSmall = 100; - const int cellSizeLarge = 200; const int spacing = 1; - const int smallCellCount = 20; - const int largeCellCount = tableSize - smallCellCount; - const qreal accumulatedSpacing = ((tableSize - 1) * spacing); auto model = TestModelAsVariant(tableSize, tableSize); tableView->setModel(model); @@ -580,71 +587,12 @@ void tst_QQuickTableView::checkContentWidthAndHeight() QCOMPARE(tableViewPrivate->averageEdgeSize.width(), cellSizeSmall); QCOMPARE(tableViewPrivate->averageEdgeSize.height(), cellSizeSmall); - // Flick in 5 more rows and columns, but not so far that we start loading in - // the ones that are bigger. Loading in more rows and columns of the same - // size as the initial ones should not change the first prediction. - qreal flickTo = ((cellSizeSmall + spacing) * 5); - tableView->setContentX(flickTo); - tableView->setContentY(flickTo); + // Flick to the end, and check that content width/height stays unchanged + tableView->setContentX(tableView->contentWidth() - tableView->width()); + tableView->setContentY(tableView->contentHeight() - tableView->height()); QCOMPARE(tableView->contentWidth(), expectedSizeInit); QCOMPARE(tableView->contentHeight(), expectedSizeInit); - QCOMPARE(tableViewPrivate->averageEdgeSize.width(), cellSizeSmall); - QCOMPARE(tableViewPrivate->averageEdgeSize.height(), cellSizeSmall); - - // Flick to row and column 20 (smallCellCount), since there the row and - // column sizes increases with 100. Check that TableView then adjusts - // contentWidth and contentHeight accordingly. - flickTo = ((cellSizeSmall + spacing) * smallCellCount) - spacing; - tableView->setContentX(flickTo); - tableView->setContentY(flickTo); - - // Since we move the viewport more than a page, tableview - // will jump to the new position and do a rebuild. - QVERIFY(tableViewPrivate->scheduledRebuildOptions); - WAIT_UNTIL_POLISHED; - - // Check that the average cell size is now matching the - // large cells since they fill up the whole view. - QCOMPARE(tableViewPrivate->averageEdgeSize.width(), cellSizeLarge); - QCOMPARE(tableViewPrivate->averageEdgeSize.height(), cellSizeLarge); - - const int largeSizeCellCountInView = qCeil(tableView->width() / cellSizeLarge); - const int columnCount = smallCellCount + largeSizeCellCountInView; - QCOMPARE(tableViewPrivate->leftColumn(), smallCellCount); - QCOMPARE(tableViewPrivate->rightColumn(), columnCount - 1); - - const qreal firstHalfLength = smallCellCount * cellSizeSmall; - const qreal secondHalfOneScreenLength = largeSizeCellCountInView * cellSizeLarge; - const qreal lengthAfterFlick = firstHalfLength + secondHalfOneScreenLength; - - // Check that loadedTableOuterRect has been calculated correct thus far - const qreal spacingAfterFlick = (smallCellCount + largeSizeCellCountInView - 1) * spacing; - QCOMPARE(tableViewPrivate->loadedTableOuterRect.left(), flickTo + spacing); - QCOMPARE(tableViewPrivate->loadedTableOuterRect.right(), lengthAfterFlick + spacingAfterFlick); - QCOMPARE(tableViewPrivate->loadedTableOuterRect.top(), flickTo + spacing); - QCOMPARE(tableViewPrivate->loadedTableOuterRect.bottom(), lengthAfterFlick + spacingAfterFlick); - - // At this point, we should have the exact content width/height set, because - // TableView knows where the large cells start in the viewport, and how many - // columns that remain in the model. It will assume that the rest of the the - // columns have the same average size as the ones currently inside the viewport. - const qreal expectedContentSize = (smallCellCount * cellSizeSmall) + (largeCellCount * cellSizeLarge) + accumulatedSpacing; - QCOMPARE(tableView->contentWidth(), expectedContentSize); - QCOMPARE(tableView->contentHeight(), expectedContentSize); - - // Flick to the end (row/column 100, and overshoot a bit), and - // check that we then end up with the exact content width/height. - const qreal secondHalfLength = largeCellCount * cellSizeLarge; - const qreal expectedFullSize = (firstHalfLength + secondHalfLength) + accumulatedSpacing; - const qreal overshoot = 100; - const qreal endPosX = expectedFullSize - tableView->width() + overshoot; - const qreal endPosY = expectedFullSize - tableView->height() + overshoot; - tableView->setContentX(endPosX); - tableView->setContentY(endPosY); - - QCOMPARE(tableView->contentWidth(), expectedFullSize); - QCOMPARE(tableView->contentHeight(), expectedFullSize); // Flick back to start tableView->setContentX(0); @@ -656,7 +604,7 @@ void tst_QQuickTableView::checkContentWidthAndHeight() QVERIFY(tableViewPrivate->scheduledRebuildOptions); WAIT_UNTIL_POLISHED; - // We should now have the same content width/height as when we started + // We should still have the same content width/height as when we started QCOMPARE(tableView->contentWidth(), expectedSizeInit); QCOMPARE(tableView->contentHeight(), expectedSizeInit); } @@ -752,6 +700,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 @@ -1134,8 +1240,11 @@ void tst_QQuickTableView::checkSpacingValues() tableView->polish(); WAIT_UNTIL_POLISHED; - QCOMPARE(tableView->contentWidth(), columnCount * (delegateWidth + tableView->columnSpacing()) - tableView->columnSpacing()); - QCOMPARE(tableView->contentHeight(), rowCount * (delegateHeight + tableView->rowSpacing()) - tableView->rowSpacing()); + + const qreal expectedInitialContentWidth = columnCount * (delegateWidth + tableView->columnSpacing()) - tableView->columnSpacing(); + const qreal expectedInitialContentHeight = rowCount * (delegateHeight + tableView->rowSpacing()) - tableView->rowSpacing(); + QCOMPARE(tableView->contentWidth(), expectedInitialContentWidth); + QCOMPARE(tableView->contentHeight(), expectedInitialContentHeight); // Valid spacing assignment tableView->setRowSpacing(42); @@ -1145,8 +1254,13 @@ void tst_QQuickTableView::checkSpacingValues() tableView->polish(); WAIT_UNTIL_POLISHED; - QCOMPARE(tableView->contentWidth(), columnCount * (delegateWidth + tableView->columnSpacing()) - tableView->columnSpacing()); - QCOMPARE(tableView->contentHeight(), rowCount * (delegateHeight + tableView->rowSpacing()) - tableView->rowSpacing()); + + // Even after changing spacing, TableView will keep the initial guesstimated content size. The + // reason is that deciding the content size based on the currently visible row/columns/spacing + // will almost always be at a little bit wrong at best. So instead of pretending that TableView + // knows what the size of the full table is, it sticks with the first guesstimate. + QCOMPARE(tableView->contentWidth(), expectedInitialContentWidth); + QCOMPARE(tableView->contentHeight(), expectedInitialContentHeight); // Invalid assignments (should ignore) tableView->setRowSpacing(-1); @@ -2148,6 +2262,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/qquicktextedit/qquicktextedit.pro b/tests/auto/quick/qquicktextedit/qquicktextedit.pro index ea6e8bc60d..576e25d092 100644 --- a/tests/auto/quick/qquicktextedit/qquicktextedit.pro +++ b/tests/auto/quick/qquicktextedit/qquicktextedit.pro @@ -8,6 +8,7 @@ SOURCES += tst_qquicktextedit.cpp \ HEADERS += ../../shared/testhttpserver.h include (../../shared/util.pri) +include (../shared/util.pri) TESTDATA = data/* diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp index ce2a8eb257..33a6b829bc 100644 --- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp +++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp @@ -49,6 +49,7 @@ #include <QMimeData> #include <private/qquicktextcontrol_p.h> #include "../../shared/util.h" +#include "../shared/viewtestutil.h" #include "../../shared/platformquirks.h" #include "../../shared/platforminputcontext.h" #include <private/qinputmethod_p.h> @@ -63,6 +64,11 @@ Q_DECLARE_METATYPE(QQuickTextEdit::SelectionMode) Q_DECLARE_METATYPE(Qt::Key) DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD) +static bool isPlatformWayland() +{ + return !QGuiApplication::platformName().compare(QLatin1String("wayland"), Qt::CaseInsensitive); +} + QString createExpectedFileIfNotFound(const QString& filebasename, const QImage& actual) { // XXX This will be replaced by some clever persistent platform image store. @@ -133,6 +139,7 @@ private slots: void positionAt_data(); void positionAt(); + void linkHover(); void linkInteraction(); void cursorDelegate_data(); @@ -2536,10 +2543,59 @@ void tst_qquicktextedit::positionAt() QVERIFY(texteditObject->positionAt(x0 / 2, y1) > 0); } +#if QT_CONFIG(cursor) +void tst_qquicktextedit::linkHover() +{ + if (isPlatformWayland()) + QSKIP("Wayland: QCursor::setPos() doesn't work."); + + QQuickView window(testFileUrl("linkInteraction.qml")); + window.setFlag(Qt::FramelessWindowHint); + QVERIFY(window.rootObject() != nullptr); + QQuickViewTestUtil::centerOnScreen(&window); + QQuickViewTestUtil::moveMouseAway(&window); + window.show(); + window.requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(&window)); + QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit *>(window.rootObject()); + QVERIFY(texteditObject != nullptr); + + QSignalSpy hover(texteditObject, SIGNAL(linkHovered(QString))); + + const QString link("http://example.com/"); + const QPoint linkPos = window.mapToGlobal(texteditObject->positionToRectangle(7).center().toPoint()); + const QPoint textPos = window.mapToGlobal(texteditObject->positionToRectangle(2).center().toPoint()); + + QCursor::setPos(linkPos); + QTRY_COMPARE(hover.count(), 1); + QCOMPARE(window.cursor().shape(), Qt::PointingHandCursor); + QCOMPARE(hover.last()[0].toString(), link); + + QCursor::setPos(textPos); + QTRY_COMPARE(hover.count(), 2); + QCOMPARE(window.cursor().shape(), Qt::IBeamCursor); + QCOMPARE(hover.last()[0].toString(), QString()); + + texteditObject->setCursor(Qt::OpenHandCursor); + + QCursor::setPos(linkPos); + QTRY_COMPARE(hover.count(), 3); + QCOMPARE(window.cursor().shape(), Qt::PointingHandCursor); + QCOMPARE(hover.last()[0].toString(), link); + + QCursor::setPos(textPos); + QTRY_COMPARE(hover.count(), 4); + QCOMPARE(window.cursor().shape(), Qt::OpenHandCursor); + QCOMPARE(hover.last()[0].toString(), QString()); +} +#endif + void tst_qquicktextedit::linkInteraction() { QQuickView window(testFileUrl("linkInteraction.qml")); QVERIFY(window.rootObject() != nullptr); + QQuickViewTestUtil::centerOnScreen(&window); + QQuickViewTestUtil::moveMouseAway(&window); window.show(); window.requestActivate(); QVERIFY(QTest::qWaitForWindowActive(&window)); diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index 4cf7fa7119..f08b9207d1 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -353,6 +353,11 @@ protected: m_mouseEvents << *event; } + void mouseDoubleClickEvent(QMouseEvent *event) override { + qCDebug(lcTests) << event; + m_mouseEvents << *event; + } + public: QList<QMouseEvent> m_mouseEvents; QList<QTouchEvent> m_touchEvents; @@ -401,6 +406,8 @@ private slots: void mouseFromTouch_basic(); void synthMouseFromTouch_data(); void synthMouseFromTouch(); + void synthMouseDoubleClickFromTouch_data(); + void synthMouseDoubleClickFromTouch(); void clearWindow(); @@ -1241,6 +1248,55 @@ void tst_qquickwindow::synthMouseFromTouch() QCOMPARE(ev.source(), Qt::MouseEventSynthesizedByQt); } +void tst_qquickwindow::synthMouseDoubleClickFromTouch_data() +{ + QTest::addColumn<QPoint>("movement"); + QTest::addColumn<QPoint>("distanceBetweenPresses"); + QTest::addColumn<bool>("expectedSynthesizedDoubleClickEvent"); + + QTest::newRow("normal") << QPoint(0, 0) << QPoint(0, 0) << true; + QTest::newRow("with 1 pixel wiggle") << QPoint(1, 1) << QPoint(1, 1) << true; + QTest::newRow("too much distance to second tap") << QPoint(0, 0) << QPoint(50, 0) << false; + QTest::newRow("too much drag") << QPoint(50, 0) << QPoint(0, 0) << false; + QTest::newRow("too much drag and too much distance to second tap") << QPoint(50, 0) << QPoint(50, 0) << false; +} + +void tst_qquickwindow::synthMouseDoubleClickFromTouch() +{ + QFETCH(QPoint, movement); + QFETCH(QPoint, distanceBetweenPresses); + QFETCH(bool, expectedSynthesizedDoubleClickEvent); + + QCoreApplication::setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, true); + QScopedPointer<MouseRecordingWindow> window(new MouseRecordingWindow); + QScopedPointer<MouseRecordingItem> item(new MouseRecordingItem(false, nullptr)); + item->setParentItem(window->contentItem()); + window->resize(250, 250); + window->setPosition(100, 100); + window->setTitle(QTest::currentTestFunction()); + window->show(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QTest::qWait(100); + + QPoint p1 = item->mapToScene(item->clipRect().center()).toPoint(); + QTest::touchEvent(window.data(), touchDevice).press(0, p1, window.data()); + QTest::touchEvent(window.data(), touchDevice).move(0, p1 + movement, window.data()); + QTest::touchEvent(window.data(), touchDevice).release(0, p1 + movement, window.data()); + + QPoint p2 = p1 + distanceBetweenPresses; + QTest::touchEvent(window.data(), touchDevice).press(1, p2, window.data()); + QTest::touchEvent(window.data(), touchDevice).move(1, p2 + movement, window.data()); + QTest::touchEvent(window.data(), touchDevice).release(1, p2 + movement, window.data()); + + const int eventCount = item->m_mouseEvents.count(); + QVERIFY(eventCount >= 2); + + const int nDoubleClicks = std::count_if(item->m_mouseEvents.constBegin(), item->m_mouseEvents.constEnd(), [](const QMouseEvent &ev) { return (ev.type() == QEvent::MouseButtonDblClick); } ); + const bool foundDoubleClick = (nDoubleClicks == 1); + QCOMPARE(foundDoubleClick, expectedSynthesizedDoubleClickEvent); + +} + void tst_qquickwindow::clearWindow() { QQuickWindow *window = new QQuickWindow; |