diff options
Diffstat (limited to 'tests/auto/quick')
48 files changed, 1950 insertions, 236 deletions
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/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/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..ca348eea03 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> @@ -121,7 +120,7 @@ void tst_PropertyRequirements::constantOrNotifyableFull() } static const QString messagePattern("\nProperty %1 neither CONSTANT nor NOTIFYable. Affected types:\n\t%2"); - QStringList occurrencesList = occurrences.toList(); + QStringList occurrencesList = occurrences.values(); occurrencesList.sort(); messages.append(messagePattern.arg(it.key(), occurrencesList.join("\n\t"))); @@ -159,7 +158,8 @@ void tst_PropertyRequirements::testAllQmlTypes(TestDepth testDepth, FailuresByPr testQmlType(testDepth, qmlType, failuresByProperty); } } - seenTypes.unite(QSet<QString>::fromList(QQmlMetaType::qmlTypeNames())); + const auto &typeNameList = QQmlMetaType::qmlTypeNames(); + seenTypes.unite(QSet<QString>(typeNameList.cbegin(), typeNameList.cend())); } } 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 1c5494a24a..94f694181d 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 17dfa4a3d7..48f779a490 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/qquickbehaviors/data/delete.qml b/tests/auto/quick/qquickbehaviors/data/delete.qml new file mode 100644 index 0000000000..1bf0267b84 --- /dev/null +++ b/tests/auto/quick/qquickbehaviors/data/delete.qml @@ -0,0 +1,37 @@ +import QtQuick 2.12 + +Item { + visible: true + width: 640 + height: 480 + + Component.onCompleted: { + myLoader.active = false + } + + Loader { + id: myLoader + + active: true + sourceComponent: Item { + width: 100 + height: 100 + id: myPopup + + NumberAnimation { + id: anim + } + + Rectangle { + color: "black" + Component.onCompleted: { + opacity = 20 + } + + Behavior on opacity { + animation: anim + } + } + } + } +} diff --git a/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp b/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp index 6367f327da..64e32dcdfd 100644 --- a/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp +++ b/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp @@ -74,6 +74,7 @@ private slots: void aliasedProperty(); void innerBehaviorOverwritten(); void oneWay(); + void safeToDelete(); }; void tst_qquickbehaviors::simpleBehavior() @@ -647,6 +648,16 @@ void tst_qquickbehaviors::oneWay() QCOMPARE(myAnimation->isRunning(), false); } +// QTBUG-76749 +void tst_qquickbehaviors::safeToDelete() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("delete.qml")); + QVERIFY(c.create()); +} + + + QTEST_MAIN(tst_qquickbehaviors) #include "tst_qquickbehaviors.moc" 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..46e3457d6e 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" @@ -6433,11 +6433,13 @@ QList<int> tst_QQuickGridView::toIntList(const QVariantList &list) void tst_QQuickGridView::matchIndexLists(const QVariantList &indexLists, const QList<int> &expectedIndexes) { + const QSet<int> expectedIndexSet(expectedIndexes.cbegin(), expectedIndexes.cend()); for (int i=0; i<indexLists.count(); i++) { - QSet<int> current = indexLists[i].value<QList<int> >().toSet(); - if (current != expectedIndexes.toSet()) + const auto ¤tList = indexLists[i].value<QList<int> >(); + const QSet<int> current(currentList.cbegin(), currentList.cend()); + if (current != expectedIndexSet) qDebug() << "Cannot match actual targets" << current << "with expected" << expectedIndexes; - QCOMPARE(current, expectedIndexes.toSet()); + QCOMPARE(current, expectedIndexSet); } } diff --git a/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp b/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp index 4b75a7e008..c79e665d94 100644 --- a/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp +++ b/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp @@ -33,6 +33,7 @@ #include <QImageReader> #include <QWaitCondition> #include <QThreadPool> +#include <private/qqmlengine_p.h> Q_DECLARE_METATYPE(QQuickImageProvider*); @@ -67,6 +68,8 @@ private slots: void asyncTextureTest(); void instantAsyncTextureTest(); + void asyncImageThreadSafety(); + private: QString newImageFileName() const; void fillRequestTestsData(const QString &id); @@ -448,21 +451,56 @@ void tst_qquickimageprovider::threadTest() foreach (QQuickImage *img, images) { QCOMPARE(img->status(), QQuickImage::Loading); } - provider->ok = true; - provider->cond.wakeAll(); + { + QMutexLocker lock(&provider->mutex); + provider->ok = true; + provider->cond.wakeAll(); + } QTest::qWait(250); foreach (QQuickImage *img, images) { QTRY_COMPARE(img->status(), QQuickImage::Ready); } } -class TestImageResponse : public QQuickImageResponse, public QRunnable +class TestImageResponseRunner : public QObject, public QRunnable { + + Q_OBJECT + +public: + Q_SIGNAL void finished(QQuickTextureFactory *texture); + TestImageResponseRunner(QMutex *lock, QWaitCondition *condition, bool *ok, const QString &id, const QSize &requestedSize) + : m_lock(lock), m_condition(condition), m_ok(ok), m_id(id), m_requestedSize(requestedSize) {} + void run() + { + m_lock->lock(); + if (!(*m_ok)) { + m_condition->wait(m_lock); + } + m_lock->unlock(); + QImage image(50, 50, QImage::Format_RGB32); + image.fill(QColor(m_id).rgb()); + if (m_requestedSize.isValid()) + image = image.scaled(m_requestedSize); + emit finished(QQuickTextureFactory::textureFactoryForImage(image)); + } + +private: + QMutex *m_lock; + QWaitCondition *m_condition; + bool *m_ok; + QString m_id; + QSize m_requestedSize; +}; + +class TestImageResponse : public QQuickImageResponse { public: - TestImageResponse(QMutex *lock, QWaitCondition *condition, bool *ok, const QString &id, const QSize &requestedSize) + TestImageResponse(QMutex *lock, QWaitCondition *condition, bool *ok, const QString &id, const QSize &requestedSize, QThreadPool *pool) : m_lock(lock), m_condition(condition), m_ok(ok), m_id(id), m_requestedSize(requestedSize), m_texture(nullptr) { - setAutoDelete(false); + auto runnable = new TestImageResponseRunner(m_lock, m_condition, m_ok, m_id, m_requestedSize); + QObject::connect(runnable, &TestImageResponseRunner::finished, this, &TestImageResponse::handleResponse); + pool->start(runnable); } QQuickTextureFactory *textureFactory() const @@ -470,18 +508,8 @@ class TestImageResponse : public QQuickImageResponse, public QRunnable return m_texture; } - void run() - { - m_lock->lock(); - if (!(*m_ok)) { - m_condition->wait(m_lock); - } - m_lock->unlock(); - QImage image(50, 50, QImage::Format_RGB32); - image.fill(QColor(m_id).rgb()); - if (m_requestedSize.isValid()) - image = image.scaled(m_requestedSize); - m_texture = QQuickTextureFactory::textureFactoryForImage(image); + void handleResponse(QQuickTextureFactory *factory) { + this->m_texture = factory; emit finished(); } @@ -505,8 +533,7 @@ class TestAsyncProvider : public QQuickAsyncImageProvider QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) { - TestImageResponse *response = new TestImageResponse(&lock, &condition, &ok, id, requestedSize); - pool.start(response); + TestImageResponse *response = new TestImageResponse(&lock, &condition, &ok, id, requestedSize, &pool); return response; } @@ -544,8 +571,11 @@ void tst_qquickimageprovider::asyncTextureTest() foreach (QQuickImage *img, images) { QTRY_COMPARE(img->status(), QQuickImage::Loading); } - provider->ok = true; - provider->condition.wakeAll(); + { + QMutexLocker lock(&provider->lock); + provider->ok = true; + provider->condition.wakeAll(); + } foreach (QQuickImage *img, images) { QTRY_COMPARE(img->status(), QQuickImage::Ready); } @@ -616,6 +646,115 @@ void tst_qquickimageprovider::instantAsyncTextureTest() } +class WaitingAsyncImageResponse : public QQuickImageResponse, public QRunnable +{ +public: + WaitingAsyncImageResponse(QMutex *providerRemovedMutex, QWaitCondition *providerRemovedCond, bool *providerRemoved, QMutex *imageRequestedMutex, QWaitCondition *imageRequestedCond, bool *imageRequested) + : m_providerRemovedMutex(providerRemovedMutex), m_providerRemovedCond(providerRemovedCond), m_providerRemoved(providerRemoved), + m_imageRequestedMutex(imageRequestedMutex), m_imageRequestedCondition(imageRequestedCond), m_imageRequested(imageRequested) + { + setAutoDelete(false); + } + + void run() override + { + m_imageRequestedMutex->lock(); + *m_imageRequested = true; + m_imageRequestedCondition->wakeAll(); + m_imageRequestedMutex->unlock(); + m_providerRemovedMutex->lock(); + while (!*m_providerRemoved) + m_providerRemovedCond->wait(m_providerRemovedMutex); + m_providerRemovedMutex->unlock(); + emit finished(); + } + + QQuickTextureFactory *textureFactory() const + { + QImage image(50, 50, QImage::Format_RGB32); + auto texture = QQuickTextureFactory::textureFactoryForImage(image); + return texture; + } + + QMutex *m_providerRemovedMutex; + QWaitCondition *m_providerRemovedCond; + bool *m_providerRemoved; + QMutex *m_imageRequestedMutex; + QWaitCondition *m_imageRequestedCondition; + bool *m_imageRequested; + +}; + +class WaitingAsyncProvider : public QQuickAsyncImageProvider +{ +public: + WaitingAsyncProvider(QMutex *providerRemovedMutex, QWaitCondition *providerRemovedCond, bool *providerRemoved, QMutex *imageRequestedMutex, QWaitCondition *imageRequestedCond, bool *imageRequested) + : m_providerRemovedMutex(providerRemovedMutex), m_providerRemovedCond(providerRemovedCond), m_providerRemoved(providerRemoved), + m_imageRequestedMutex(imageRequestedMutex), m_imageRequestedCondition(imageRequestedCond), m_imageRequested(imageRequested) + { + } + + ~WaitingAsyncProvider() {} + + QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) + { + auto response = new WaitingAsyncImageResponse(m_providerRemovedMutex, m_providerRemovedCond, m_providerRemoved, m_imageRequestedMutex, m_imageRequestedCondition, m_imageRequested); + pool.start(response); + return response; + } + + QMutex *m_providerRemovedMutex; + QWaitCondition *m_providerRemovedCond; + bool *m_providerRemoved; + QMutex *m_imageRequestedMutex; + QWaitCondition *m_imageRequestedCondition; + bool *m_imageRequested; + QThreadPool pool; +}; + + +// QTBUG-76527 +void tst_qquickimageprovider::asyncImageThreadSafety() +{ + QQmlEngine engine; + QMutex providerRemovedMutex; + bool providerRemoved = false; + QWaitCondition providerRemovedCond; + QMutex imageRequestedMutex; + bool imageRequested = false; + QWaitCondition imageRequestedCond; + auto imageProvider = new WaitingAsyncProvider(&providerRemovedMutex, &providerRemovedCond, &providerRemoved, &imageRequestedMutex, &imageRequestedCond, &imageRequested); + engine.addImageProvider("test_waiting", imageProvider); + QVERIFY(engine.imageProvider("test_waiting") != nullptr); + auto privateEngine = QQmlEnginePrivate::get(&engine); + + QString componentStr = "import QtQuick 2.0\nItem { \n" + "Image { source: \"image://test_waiting/blue\"; }\n" + " }"; + QQmlComponent component(&engine); + component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QWeakPointer<QQmlImageProviderBase> observer = privateEngine->imageProvider("test_waiting").toWeakRef(); + QVERIFY(!observer.isNull()); // engine still own the object + imageRequestedMutex.lock(); + while (!imageRequested) + imageRequestedCond.wait(&imageRequestedMutex); + imageRequestedMutex.unlock(); + engine.removeImageProvider("test_waiting"); + + QVERIFY(engine.imageProvider("test_waiting") == nullptr); + QVERIFY(!observer.isNull()); // lifetime has been extended + + providerRemovedMutex.lock(); + providerRemoved = true; + providerRemovedCond.wakeAll(); + providerRemovedMutex.unlock(); + + QTRY_VERIFY(observer.isNull()); // once the reply has finished, the imageprovider gets deleted +} + + QTEST_MAIN(tst_qquickimageprovider) #include "tst_qquickimageprovider.moc" diff --git a/tests/auto/quick/qquicklistview/BLACKLIST b/tests/auto/quick/qquicklistview/BLACKLIST index 42658dca7a..1f3736328a 100644 --- a/tests/auto/quick/qquicklistview/BLACKLIST +++ b/tests/auto/quick/qquicklistview/BLACKLIST @@ -1,15 +1,11 @@ [enforceRange_withoutHighlight] -osx opensuse-42.3 opensuse-leap #QTBUG-53863 [populateTransitions] opensuse-42.1 -#QTBUG-65964 - [contentHeightWithDelayRemove] osx-10.12 - #QTBUG-75960 #QTBUG-76652 [currentIndex] 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 39df10b9d9..f14a6e75f6 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" @@ -7281,11 +7281,13 @@ QList<int> tst_QQuickListView::toIntList(const QVariantList &list) void tst_QQuickListView::matchIndexLists(const QVariantList &indexLists, const QList<int> &expectedIndexes) { + const QSet<int> expectedIndexSet(expectedIndexes.cbegin(), expectedIndexes.cend()); for (int i=0; i<indexLists.count(); i++) { - QSet<int> current = indexLists[i].value<QList<int> >().toSet(); - if (current != expectedIndexes.toSet()) + const auto ¤tList = indexLists[i].value<QList<int> >(); + const QSet<int> current(currentList.cbegin(), currentList.cend()); + if (current != expectedIndexSet) qDebug() << "Cannot match actual targets" << current << "with expected" << expectedIndexes; - QCOMPARE(current, expectedIndexes.toSet()); + QCOMPARE(current, expectedIndexSet); } } diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 52d1458a53..4215017db3 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -393,10 +393,20 @@ void tst_QQuickMouseArea::dragging() QTRY_COMPARE(blackRect->x(), 61.0); QCOMPARE(blackRect->y(), 61.0); + qreal relativeX = mouseRegion->mouseX(); + qreal relativeY = mouseRegion->mouseY(); + for (int i = 0; i < 20; i++) { + p += QPoint(1, 1); + QTest::mouseMove(&window, p); + } + QTRY_VERIFY(drag->active()); + QTRY_COMPARE(mouseRegion->mouseX(), relativeX); + QCOMPARE(mouseRegion->mouseY(), relativeY); + QTest::mouseRelease(&window, button, Qt::NoModifier, p); QTRY_VERIFY(!drag->active()); - QTRY_COMPARE(blackRect->x(), 61.0); - QCOMPARE(blackRect->y(), 61.0); + QTRY_COMPARE(blackRect->x(), 81.0); + QCOMPARE(blackRect->y(), 81.0); } void tst_QQuickMouseArea::dragSmoothed() diff --git a/tests/auto/quick/qquickpath/qquickpath.pro b/tests/auto/quick/qquickpath/qquickpath.pro index 492f82f53d..ef110a8331 100644 --- a/tests/auto/quick/qquickpath/qquickpath.pro +++ b/tests/auto/quick/qquickpath/qquickpath.pro @@ -7,5 +7,6 @@ SOURCES += tst_qquickpath.cpp include (../../shared/util.pri) TESTDATA = data/* +DISTFILES = data/* QT += core-private gui-private qml-private quick-private testlib diff --git a/tests/auto/quick/qquickpath/tst_qquickpath.cpp b/tests/auto/quick/qquickpath/tst_qquickpath.cpp index 12a8c673b0..c89ce730a8 100644 --- a/tests/auto/quick/qquickpath/tst_qquickpath.cpp +++ b/tests/auto/quick/qquickpath/tst_qquickpath.cpp @@ -42,18 +42,44 @@ public: private slots: void arc(); void angleArc(); - void catmullromCurve(); - void closedCatmullromCurve(); + void catmullRomCurve(); + void closedCatmullRomCurve(); void svg(); void line(); + +private: + void arc(QSizeF scale); + void angleArc(QSizeF scale); + void catmullRomCurve(QSizeF scale, const QVector<QPointF> &points); + void closedCatmullRomCurve(QSizeF scale, const QVector<QPointF> &points); + void svg(QSizeF scale); + void line(QSizeF scale); }; -void tst_QuickPath::arc() +static void compare(const QPointF &point, const QSizeF &scale, int line, double x, double y) +{ + QVERIFY2(qFuzzyCompare(float(point.x()), float(x * scale.width())), + (QStringLiteral("Actual: ") + QString::number(point.x(),'g',14) + + QStringLiteral(" Expected: ") + QString::number(x * scale.width(),'g',14) + + QStringLiteral(" At: ") + QString::number(line)).toLatin1().data()); + QVERIFY2(qFuzzyCompare(float(point.y()), float(y * scale.height())), + (QStringLiteral("Actual: ") + QString::number(point.y(),'g',14) + + QStringLiteral(" Expected: ") + QString::number(y * scale.height(),'g',14) + + QStringLiteral(" At: ") + QString::number(line)).toLatin1().data()); +} +static void compare(const QPointF &point, int line, const QPointF &pt) +{ + return compare(point, QSizeF(1,1), line, pt.x(), pt.y()); +} + +void tst_QuickPath::arc(QSizeF scale) { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("arc.qml")); QQuickPath *obj = qobject_cast<QQuickPath*>(c.create()); QVERIFY(obj != nullptr); + if (scale != QSizeF(1,1)) + obj->setProperty("scale", scale); QCOMPARE(obj->startX(), 0.); QCOMPARE(obj->startY(), 0.); @@ -73,22 +99,30 @@ void tst_QuickPath::arc() QPainterPath path = obj->path(); QVERIFY(path != QPainterPath()); - QPointF pos = obj->pointAt(0); + QPointF pos = obj->pointAtPercent(0); QCOMPARE(pos, QPointF(0,0)); - pos = obj->pointAt(.25); - QCOMPARE(pos.toPoint(), QPoint(39,8)); //fuzzy compare - pos = obj->pointAt(.75); - QCOMPARE(pos.toPoint(), QPoint(92,61)); //fuzzy compare - pos = obj->pointAt(1); - QCOMPARE(pos, QPointF(100,100)); + pos = obj->pointAtPercent(.25); + compare(pos, scale, __LINE__, 38.9244897744, 7.85853964341); + pos = obj->pointAtPercent(.75); + compare(pos, scale, __LINE__, 92.141460356592, 61.07551022559); + pos = obj->pointAtPercent(1); + QCOMPARE(pos, QPointF(100 * scale.width(), 100 * scale.height())); } -void tst_QuickPath::angleArc() +void tst_QuickPath::arc() +{ + arc(QSizeF(1,1)); + arc(QSizeF(2.2,3.4)); +} + +void tst_QuickPath::angleArc(QSizeF scale) { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("anglearc.qml")); QQuickPath *obj = qobject_cast<QQuickPath*>(c.create()); QVERIFY(obj != nullptr); + if (scale != QSizeF(1,1)) + obj->setProperty("scale", scale); QQmlListReference list(obj, "pathElements"); QCOMPARE(list.count(), 1); @@ -106,28 +140,35 @@ void tst_QuickPath::angleArc() QPainterPath path = obj->path(); QVERIFY(path != QPainterPath()); - // using QPoint to do fuzzy compare - QPointF pos = obj->pointAt(0); - QCOMPARE(pos.toPoint(), QPoint(135,135)); - pos = obj->pointAt(.25); - QCOMPARE(pos.toPoint(), QPoint(119,146)); - pos = obj->pointAt(.75); - QCOMPARE(pos.toPoint(), QPoint(81,146)); - pos = obj->pointAt(1); - QCOMPARE(pos.toPoint(), QPoint(65,135)); + QPointF pos = obj->pointAtPercent(0); + compare(pos, scale, __LINE__, 135.35533905867, 135.35533905867); + pos = obj->pointAtPercent(.25); + compare(pos, scale, __LINE__, 119.46222180396, 146.07068621369); + pos = obj->pointAtPercent(.75); + compare(pos, scale, __LINE__, 80.537778196007, 146.07068621366); + pos = obj->pointAtPercent(1); + compare(pos, scale, __LINE__, 64.644660941173, 135.35533905867); // if moveToStart is false, we should have a line starting from startX/Y arc->setMoveToStart(false); - pos = obj->pointAt(0); + pos = obj->pointAtPercent(0); QCOMPARE(pos, QPointF(0,0)); } -void tst_QuickPath::catmullromCurve() +void tst_QuickPath::angleArc() +{ + angleArc(QSizeF(1,1)); + angleArc(QSizeF(2.7,0.92)); +} + +void tst_QuickPath::catmullRomCurve(QSizeF scale, const QVector<QPointF> &points) { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("curve.qml")); QQuickPath *obj = qobject_cast<QQuickPath*>(c.create()); QVERIFY(obj != nullptr); + if (scale != QSizeF(1,1)) + obj->setProperty("scale", scale); QCOMPARE(obj->startX(), 0.); QCOMPARE(obj->startY(), 0.); @@ -148,22 +189,36 @@ void tst_QuickPath::catmullromCurve() QPainterPath path = obj->path(); QVERIFY(path != QPainterPath()); - QPointF pos = obj->pointAt(0); - QCOMPARE(pos, QPointF(0,0)); - pos = obj->pointAt(.25); - QCOMPARE(pos.toPoint(), QPoint(63,26)); //fuzzy compare - pos = obj->pointAt(.75); - QCOMPARE(pos.toPoint(), QPoint(51,105)); //fuzzy compare - pos = obj->pointAt(1); - QCOMPARE(pos.toPoint(), QPoint(100,150)); + QPointF pos = path.pointAtPercent(0); + QCOMPARE(pos, points.at(0)); + pos = path.pointAtPercent(.25); + compare(pos, __LINE__, points.at(1)); + pos = path.pointAtPercent(.75); + compare(pos, __LINE__, points.at(2)); + pos = path.pointAtPercent(1); + compare(pos, __LINE__, points.at(3)); +} + +void tst_QuickPath::catmullRomCurve() +{ + catmullRomCurve(QSizeF(1,1), { QPointF(0,0), + QPointF(62.917022919131, 26.175485291549), + QPointF(51.194527196674 , 105.27985623074), + QPointF(100, 150) }); + catmullRomCurve(QSizeF(2,5.3), { QPointF(0,0), + QPointF(150.80562419914, 170.34065984615), + QPointF(109.08400252853 , 588.35165918579), + QPointF(200, 795) }); } -void tst_QuickPath::closedCatmullromCurve() +void tst_QuickPath::closedCatmullRomCurve(QSizeF scale, const QVector<QPointF> &points) { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("closedcurve.qml")); QQuickPath *obj = qobject_cast<QQuickPath*>(c.create()); QVERIFY(obj != nullptr); + if (scale != QSizeF(1,1)) + obj->setProperty("scale", scale); QCOMPARE(obj->startX(), 50.); QCOMPARE(obj->startY(), 50.); @@ -181,22 +236,36 @@ void tst_QuickPath::closedCatmullromCurve() QPainterPath path = obj->path(); QVERIFY(path != QPainterPath()); - QPointF pos = obj->pointAt(0); - QCOMPARE(pos, QPointF(50,50)); - pos = obj->pointAt(.1); - QCOMPARE(pos.toPoint(), QPoint(67,56)); //fuzzy compare - pos = obj->pointAt(.75); - QCOMPARE(pos.toPoint(), QPoint(44,116)); //fuzzy compare - pos = obj->pointAt(1); - QCOMPARE(pos, QPointF(50,50)); + QPointF pos = path.pointAtPercent(0); + QCOMPARE(pos, points.at(0)); + pos = path.pointAtPercent(.1); + compare(pos, __LINE__, points.at(1)); + pos = path.pointAtPercent(.75); + compare(pos, __LINE__, points.at(2)); + pos = path.pointAtPercent(1); + compare(pos, __LINE__, points.at(3)); } -void tst_QuickPath::svg() +void tst_QuickPath::closedCatmullRomCurve() +{ + closedCatmullRomCurve(QSizeF(1,1), { QPointF(50,50), + QPointF(66.776225481812, 55.617435304145), + QPointF(44.10269379731 , 116.33512508175), + QPointF(50, 50) }); + closedCatmullRomCurve(QSizeF(2,3), { QPointF(100,150), + QPointF(136.49725836178, 170.25466686363), + QPointF(87.713232151943 , 328.29232737977), + QPointF(100, 150) }); +} + +void tst_QuickPath::svg(QSizeF scale) { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("svg.qml")); QQuickPath *obj = qobject_cast<QQuickPath*>(c.create()); QVERIFY(obj != nullptr); + if (scale != QSizeF(1,1)) + obj->setProperty("scale", scale); QCOMPARE(obj->startX(), 0.); QCOMPARE(obj->startY(), 0.); @@ -211,17 +280,23 @@ void tst_QuickPath::svg() QPainterPath path = obj->path(); QVERIFY(path != QPainterPath()); - QPointF pos = obj->pointAt(0); - QCOMPARE(pos, QPointF(200,300)); - pos = obj->pointAt(.25); - QCOMPARE(pos.toPoint(), QPoint(400,175)); //fuzzy compare - pos = obj->pointAt(.75); - QCOMPARE(pos.toPoint(), QPoint(800,425)); //fuzzy compare - pos = obj->pointAt(1); - QCOMPARE(pos, QPointF(1000,300)); + QPointF pos = obj->pointAtPercent(0); + QCOMPARE(pos, QPointF(200 * scale.width(),300 * scale.height())); + pos = obj->pointAtPercent(.25); + QCOMPARE(pos.toPoint(), QPoint(400 * scale.width(),175 * scale.height())); //fuzzy compare + pos = obj->pointAtPercent(.75); + QCOMPARE(pos.toPoint(), QPoint(800 * scale.width(),425 * scale.height())); //fuzzy compare + pos = obj->pointAtPercent(1); + QCOMPARE(pos, QPointF(1000 * scale.width(),300 * scale.height())); } -void tst_QuickPath::line() +void tst_QuickPath::svg() +{ + svg(QSizeF(1,1)); + svg(QSizeF(5,3)); +} + +void tst_QuickPath::line(QSizeF scale) { QQmlEngine engine; QQmlComponent c1(&engine); @@ -234,6 +309,8 @@ void tst_QuickPath::line() QScopedPointer<QObject> o1(c1.create()); QQuickPath *path1 = qobject_cast<QQuickPath *>(o1.data()); QVERIFY(path1); + if (scale != QSizeF(1,1)) + path1->setProperty("scale", scale); QQmlComponent c2(&engine); c2.setData( @@ -246,18 +323,25 @@ void tst_QuickPath::line() QScopedPointer<QObject> o2(c2.create()); QQuickPath *path2 = qobject_cast<QQuickPath *>(o2.data()); QVERIFY(path2); + if (scale != QSizeF(1,1)) + path2->setProperty("scale", scale); for (int i = 0; i < 167; ++i) { qreal t = i / 167.0; - QPointF p1 = path1->pointAt(t); + QPointF p1 = path1->pointAtPercent(t); QCOMPARE(p1.x(), p1.y()); - QPointF p2 = path2->pointAt(t); + QPointF p2 = path2->pointAtPercent(t); QCOMPARE(p1.toPoint(), p2.toPoint()); } } +void tst_QuickPath::line() +{ + line(QSizeF(1,1)); + line(QSizeF(7.23,7.23)); +} QTEST_MAIN(tst_QuickPath) 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 e3dc0434b1..8b963117ed 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> @@ -241,7 +241,7 @@ void tst_QQuickPathView::items() QVERIFY(path); QVERIFY(pathview->highlightItem()); - QPointF start = path->pointAt(0.0); + QPointF start = path->pointAtPercent(0.0); QPointF offset; offset.setX(pathview->highlightItem()->width()/2); offset.setY(pathview->highlightItem()->height()/2); @@ -919,7 +919,7 @@ void tst_QQuickPathView::pathMoved() QVERIFY(firstItem); QQuickPath *path = qobject_cast<QQuickPath*>(pathview->path()); QVERIFY(path); - QPointF start = path->pointAt(0.0); + QPointF start = path->pointAtPercent(0.0); QPointF offset;//Center of item is at point, but pos is from corner offset.setX(firstItem->width()/2); offset.setY(firstItem->height()/2); @@ -928,7 +928,7 @@ void tst_QQuickPathView::pathMoved() for (int i=0; i<model.count(); i++) { QQuickRectangle *curItem = findItem<QQuickRectangle>(pathview, "wrapper", i); - QPointF itemPos(path->pointAt(0.25 + i*0.25)); + QPointF itemPos(path->pointAtPercent(0.25 + i*0.25)); QCOMPARE(curItem->position() + offset, QPointF(itemPos.x(), itemPos.y())); } @@ -1007,7 +1007,7 @@ void tst_QQuickPathView::setCurrentIndex() QVERIFY(firstItem); QQuickPath *path = qobject_cast<QQuickPath*>(pathview->path()); QVERIFY(path); - QPointF start = path->pointAt(0.0); + QPointF start = path->pointAtPercent(0.0); QPointF offset;//Center of item is at point, but pos is from corner offset.setX(firstItem->width()/2); offset.setY(firstItem->height()/2); @@ -1648,7 +1648,7 @@ void tst_QQuickPathView::changePreferredHighlight() QVERIFY(firstItem); QQuickPath *path = qobject_cast<QQuickPath*>(pathview->path()); QVERIFY(path); - QPointF start = path->pointAt(0.5); + QPointF start = path->pointAtPercent(0.5); QPointF offset;//Center of item is at point, but pos is from corner offset.setX(firstItem->width()/2); offset.setY(firstItem->height()/2); @@ -1656,7 +1656,7 @@ void tst_QQuickPathView::changePreferredHighlight() pathview->setPreferredHighlightBegin(0.8); pathview->setPreferredHighlightEnd(0.8); - start = path->pointAt(0.8); + start = path->pointAtPercent(0.8); QTRY_COMPARE(firstItem->position() + offset, start); QCOMPARE(pathview->currentIndex(), 0); @@ -1709,7 +1709,7 @@ void tst_QQuickPathView::currentOffsetOnInsertion() QQuickPath *path = qobject_cast<QQuickPath*>(pathview->path()); QVERIFY(path); - QPointF start = path->pointAt(0.5); + QPointF start = path->pointAtPercent(0.5); QPointF offset;//Center of item is at point, but pos is from corner offset.setX(item->width()/2); offset.setY(item->height()/2); @@ -1798,7 +1798,7 @@ void tst_QQuickPathView::asynchronous() QVERIFY(firstItem); QQuickPath *path = qobject_cast<QQuickPath*>(pathview->path()); QVERIFY(path); - QPointF start = path->pointAt(0.0); + QPointF start = path->pointAtPercent(0.0); QPointF offset;//Center of item is at point, but pos is from corner offset.setX(firstItem->width()/2); offset.setY(firstItem->height()/2); @@ -1807,7 +1807,7 @@ void tst_QQuickPathView::asynchronous() for (int i=0; i<5; i++) { QQuickItem *curItem = findItem<QQuickItem>(pathview, "wrapper", i); - QPointF itemPos(path->pointAt(0.2 + i*0.2)); + QPointF itemPos(path->pointAtPercent(0.2 + i*0.2)); QCOMPARE(curItem->position() + offset, itemPos); } diff --git a/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp b/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp index 80be25d1b0..d3c0f345b9 100644 --- a/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp +++ b/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp @@ -4029,11 +4029,13 @@ QQuickView *tst_qquickpositioners::createView(const QString &filename, bool wait void tst_qquickpositioners::matchIndexLists(const QVariantList &indexLists, const QList<int> &expectedIndexes) { + const QSet<int> expectedIndexSet(expectedIndexes.cbegin(), expectedIndexes.cend()); for (int i=0; i<indexLists.count(); i++) { - QSet<int> current = indexLists[i].value<QList<int> >().toSet(); - if (current != expectedIndexes.toSet()) + const auto ¤tList = indexLists[i].value<QList<int> >(); + const QSet<int> current(currentList.cbegin(), currentList.cend()); + if (current != expectedIndexSet) qDebug() << "Cannot match actual targets" << current << "with expected" << expectedIndexes; - QCOMPARE(current, expectedIndexes.toSet()); + QCOMPARE(current, expectedIndexSet); } } diff --git a/tests/auto/quick/qquickrectangle/data/gradient-preset.qml b/tests/auto/quick/qquickrectangle/data/gradient-preset.qml index b740bdd610..c046dc4c05 100644 --- a/tests/auto/quick/qquickrectangle/data/gradient-preset.qml +++ b/tests/auto/quick/qquickrectangle/data/gradient-preset.qml @@ -10,7 +10,23 @@ Item { gradient: "NightFade" } Rectangle { - objectName: "invalid" + objectName: "invalid1" gradient: -1 } + Rectangle { + objectName: "invalid2" + gradient: 123456789 + } + Rectangle { + objectName: "invalid3" + gradient: "NOT_EXISTING" + } + Rectangle { + objectName: "invalid4" + gradient: "NumPresets" + } + Rectangle { + objectName: "invalid5" + gradient: Gradient.NumPresets + } } diff --git a/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp b/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp index 710caaa734..e4d790f466 100644 --- a/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp +++ b/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp @@ -189,9 +189,11 @@ void tst_qquickrectangle::gradient_preset() QVERIFY(stringRect->gradient().isString()); QCOMPARE(stringRect->gradient().toString(), QLatin1String("NightFade")); - QQuickRectangle *invalidRect = view.rootObject()->findChild<QQuickRectangle *>("invalid"); - QVERIFY(invalidRect); - QVERIFY(invalidRect->gradient().isUndefined()); + for (int i = 1; i <= 5; ++i) { + QQuickRectangle *invalidRect = view.rootObject()->findChild<QQuickRectangle *>(qPrintable(QString("invalid%1").arg(i))); + QVERIFY(invalidRect); + QVERIFY(invalidRect->gradient().isUndefined()); + } } void tst_qquickrectangle::antialiasing() 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/qquickshape/BLACKLIST b/tests/auto/quick/qquickshape/BLACKLIST deleted file mode 100644 index d0ebc2f505..0000000000 --- a/tests/auto/quick/qquickshape/BLACKLIST +++ /dev/null @@ -1,8 +0,0 @@ -[render] -osx ci -[renderWithMultipleSp] -osx ci -[radialGrad] -osx ci -[conicalGrad] -osx ci diff --git a/tests/auto/quick/qquickshape/data/pathitem7.qml b/tests/auto/quick/qquickshape/data/pathitem7.qml new file mode 100644 index 0000000000..b3ef47a4dd --- /dev/null +++ b/tests/auto/quick/qquickshape/data/pathitem7.qml @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** 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 +import tst_qquickpathitem 1.0 + +Item { + id: item + width: 200 + height: 150 + + Shape { + vendorExtensionsEnabled: false + objectName: "shape" + id: shape + anchors.fill: parent + + ShapePath { + strokeWidth: 4 + strokeColor: "red" + scale: Qt.size(shape.width - 1, shape.height - 1) + fillGradient: LinearGradient { + x1: 20; y1: 20 + x2: 180; y2: 130 + GradientStop { position: 0; color: "blue" } + GradientStop { position: 0.2; color: "green" } + GradientStop { position: 0.4; color: "red" } + GradientStop { position: 0.6; color: "yellow" } + GradientStop { position: 1; color: "cyan" } + } + strokeStyle: ShapePath.DashLine + dashPattern: [ 1, 4 ] + startX: 20; startY: 20 // unnecessary, PathPolyline moves to the first vertex. + PathPolyline { + path: [ Qt.point(20.0 / (item.width - 1.0), 20.0 / (item.height - 1.0)), + Qt.point(180.0 / (item.width - 1.0), 130.0 / (item.height - 1.0)), + Qt.point(20.0 / (item.width - 1.0), 130.0 / (item.height - 1.0)), + Qt.point(20.0 / (item.width - 1.0), 20.0 / (item.height - 1.0)) ] + } + } + } +} diff --git a/tests/auto/quick/qquickshape/qquickshape.pro b/tests/auto/quick/qquickshape/qquickshape.pro index a0e5c002e0..3cf79426c5 100644 --- a/tests/auto/quick/qquickshape/qquickshape.pro +++ b/tests/auto/quick/qquickshape/qquickshape.pro @@ -8,6 +8,7 @@ include (../../shared/util.pri) include (../shared/util.pri) TESTDATA = data/* +DISTFILES = data/* QT += core-private gui-private qml-private quick-private testlib quickshapes-private qtHaveModule(widgets): QT += widgets diff --git a/tests/auto/quick/qquickshape/tst_qquickshape.cpp b/tests/auto/quick/qquickshape/tst_qquickshape.cpp index 61fb260612..174ada65a5 100644 --- a/tests/auto/quick/qquickshape/tst_qquickshape.cpp +++ b/tests/auto/quick/qquickshape/tst_qquickshape.cpp @@ -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 test suite of the Qt Toolkit. @@ -34,6 +34,7 @@ #include <QtQml/qqmlexpression.h> #include <QtQml/qqmlincubator.h> #include <QtQuickShapes/private/qquickshape_p.h> +#include <QStandardPaths> #include "../../shared/util.h" #include "../shared/viewtestutil.h" @@ -57,6 +58,7 @@ private slots: void renderWithMultipleSp(); void radialGrad(); void conicalGrad(); + void renderPolyline(); }; tst_QQuickShape::tst_QQuickShape() @@ -66,11 +68,12 @@ tst_QQuickShape::tst_QQuickShape() const char *uri = "tst_qquickpathitem"; qmlRegisterType<QQuickShape>(uri, 1, 0, "Shape"); - qmlRegisterType<QQuickShapePath>(uri, 1, 0, "ShapePath"); + qmlRegisterType<QQuickShapePath, 14>(uri, 1, 0, "ShapePath"); qmlRegisterUncreatableType<QQuickShapeGradient>(uri, 1, 0, "ShapeGradient", QQuickShapeGradient::tr("ShapeGradient is an abstract base class")); qmlRegisterType<QQuickShapeLinearGradient>(uri, 1, 0, "LinearGradient"); qmlRegisterType<QQuickShapeRadialGradient>(uri, 1, 0, "RadialGradient"); qmlRegisterType<QQuickShapeConicalGradient>(uri, 1, 0, "ConicalGradient"); + qmlRegisterType<QQuickPathPolyline>(uri, 1, 0, "PathPolyline"); } void tst_QQuickShape::initValues() @@ -311,6 +314,35 @@ void tst_QQuickShape::conicalGrad() qPrintable(errorMessage)); } +void tst_QQuickShape::renderPolyline() +{ + QScopedPointer<QQuickView> window(createView()); + + window->setSource(testFileUrl("pathitem7.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + if ((QGuiApplication::platformName() == QLatin1String("offscreen")) + || (QGuiApplication::platformName() == QLatin1String("minimal"))) + QEXPECT_FAIL("", "Failure due to grabWindow not functional on offscreen/minimimal platforms", Abort); + + QImage img = window->grabWindow(); + QVERIFY(!img.isNull()); + + QImage refImg(testFileUrl("pathitem3.png").toLocalFile()); // It's a recreation of pathitem3 using PathPolyline + QVERIFY(!refImg.isNull()); + + QString errorMessage; + const QImage actualImg = img.convertToFormat(refImg.format()); + const bool res = QQuickVisualTestUtil::compareImages(actualImg, refImg, &errorMessage); + if (!res) { // For visual inspection purposes. + QTest::qWait(5000); + const QString &tempLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation); + actualImg.save(tempLocation + QLatin1String("/pathitem7.png")); + } + QVERIFY2(res, qPrintable(errorMessage)); +} + QTEST_MAIN(tst_QQuickShape) #include "tst_qquickshape.moc" 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..fbe56abda5 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() @@ -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,72 +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->polishScheduled); - QVERIFY(tableViewPrivate->rebuildScheduled); - 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); @@ -654,10 +601,10 @@ 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 + // We should still have the same content width/height as when we started QCOMPARE(tableView->contentWidth(), expectedSizeInit); QCOMPARE(tableView->contentHeight(), expectedSizeInit); } @@ -689,7 +636,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 +645,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 +666,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 +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 @@ -1138,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); @@ -1149,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); @@ -1942,7 +2052,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 +2135,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 +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/qquicktext/BLACKLIST b/tests/auto/quick/qquicktext/BLACKLIST index 531d981159..6c3c3af154 100644 --- a/tests/auto/quick/qquicktext/BLACKLIST +++ b/tests/auto/quick/qquicktext/BLACKLIST @@ -1,6 +1,6 @@ [dependentImplicitSizes] -* -[lineLaidOutRelayout] -msvc-2015 +b2qt +qemu +osx-10.12 [fontSizeMode] opensuse-42.1 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/qquickwindow/BLACKLIST b/tests/auto/quick/qquickwindow/BLACKLIST index 1282a9d5ec..b4b7d2d761 100644 --- a/tests/auto/quick/qquickwindow/BLACKLIST +++ b/tests/auto/quick/qquickwindow/BLACKLIST @@ -1,6 +1,3 @@ [openglContextCreatedSignal] opensuse-42.3 opensuse-leap -# QTBUG-62177 -[attachedProperty] -osx 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/auto/quick/scenegraph/tst_scenegraph.cpp b/tests/auto/quick/scenegraph/tst_scenegraph.cpp index 063358c795..b4abdd6fe2 100644 --- a/tests/auto/quick/scenegraph/tst_scenegraph.cpp +++ b/tests/auto/quick/scenegraph/tst_scenegraph.cpp @@ -564,7 +564,10 @@ bool tst_SceneGraph::isRunningOnOpenGL() bool retval = false; QQuickView dummy; dummy.show(); - QTest::qWaitForWindowExposed(&dummy); + if (!QTest::qWaitForWindowExposed(&dummy)) { + [](){ QFAIL("Could not show a QQuickView"); }(); + return false; + } if (dummy.rendererInterface()->graphicsApi() == QSGRendererInterface::OpenGL) retval = true; dummy.hide(); diff --git a/tests/auto/quick/shared/viewtestutil.cpp b/tests/auto/quick/shared/viewtestutil.cpp index 5631ffe047..1680e850f7 100644 --- a/tests/auto/quick/shared/viewtestutil.cpp +++ b/tests/auto/quick/shared/viewtestutil.cpp @@ -328,7 +328,8 @@ QQuickViewTestUtil::ListRange QQuickViewTestUtil::ListRange::operator+(const Lis bool QQuickViewTestUtil::ListRange::operator==(const ListRange &other) const { - return indexes.toSet() == other.indexes.toSet(); + return QSet<int>(indexes.cbegin(), indexes.cend()) + == QSet<int>(other.indexes.cbegin(), other.indexes.cend()); } bool QQuickViewTestUtil::ListRange::operator!=(const ListRange &other) const |