diff options
Diffstat (limited to 'tests/auto/quick')
40 files changed, 1792 insertions, 171 deletions
diff --git a/tests/auto/quick/nodes/tst_nodestest.cpp b/tests/auto/quick/nodes/tst_nodestest.cpp index bd5e6c6383..249ecd5aa5 100644 --- a/tests/auto/quick/nodes/tst_nodestest.cpp +++ b/tests/auto/quick/nodes/tst_nodestest.cpp @@ -121,9 +121,6 @@ class DummyRenderer : public QSGBatchRenderer::Renderer public: DummyRenderer(QSGRootNode *root, QSGDefaultRenderContext *renderContext) : QSGBatchRenderer::Renderer(renderContext) - , changedNode(nullptr) - , changedState(nullptr) - , renderCount(0) { setRootNode(root); } @@ -139,11 +136,11 @@ public: QSGBatchRenderer::Renderer::nodeChanged(node, state); } - QSGNode *changedNode; + QSGNode *changedNode = nullptr; QSGNode::DirtyState changedState; - int renderCount; - int renderingOrder; + int renderCount = 0; + int renderingOrder = 0; static int globalRendereringOrder; }; diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST b/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST index 20f989fc50..92903955ac 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST +++ b/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST @@ -1,5 +1,6 @@ [touchAndDragHandlerOnFlickable] windows gcc +opensuse-leap [touchDragFlickableBehindSlider] windows gcc [touchDragFlickableBehindButton] diff --git a/tests/auto/quick/pointerhandlers/mousearea_interop/data/dragTakeOverFromSibling.qml b/tests/auto/quick/pointerhandlers/mousearea_interop/data/dragTakeOverFromSibling.qml new file mode 100644 index 0000000000..48b1dc86f0 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/mousearea_interop/data/dragTakeOverFromSibling.qml @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** 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 { + width: 640 + height: 480 + + Rectangle { + width: 200 + height: 200 + color: mouseArea.pressed ? "red" : "blue" + opacity: 0.6 + + MouseArea { + id: mouseArea + anchors.fill: parent + } + } + Rectangle { + y: 100 + z: -1 + width: 200 + height: 200 + color: dragHandler.active ? "orange" : "green" + opacity: 0.6 + + DragHandler { + id: dragHandler + } + } +} diff --git a/tests/auto/quick/pointerhandlers/mousearea_interop/mousearea_interop.pro b/tests/auto/quick/pointerhandlers/mousearea_interop/mousearea_interop.pro new file mode 100644 index 0000000000..0bf0ec86a9 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/mousearea_interop/mousearea_interop.pro @@ -0,0 +1,15 @@ +CONFIG += testcase + +TARGET = tst_mousearea_interop +QT += core-private gui-private qml-private quick-private testlib + +macos:CONFIG -= app_bundle + +SOURCES += tst_mousearea_interop.cpp + +include (../../../shared/util.pri) +include (../../shared/util.pri) + +TESTDATA = data/* + +OTHER_FILES += data/dragTakeOverFromSibling.qml diff --git a/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp b/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp new file mode 100644 index 0000000000..794562fea0 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp @@ -0,0 +1,178 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module 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 <QtQml/qqmlengine.h> +#include <QtQml/qqmlproperty.h> +#include <QtQuick/private/qquickdraghandler_p.h> +#include <QtQuick/private/qquickmousearea_p.h> +#include <QtQuick/qquickitem.h> +#include <QtQuick/qquickview.h> + +#include "../../../shared/util.h" +#include "../../shared/viewtestutil.h" + +Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests") + +class tst_MouseAreaInterop : public QQmlDataTest +{ + Q_OBJECT +public: + tst_MouseAreaInterop() + : touchDevice(QTest::createTouchDevice()) + , touchPointerDevice(QQuickPointerDevice::touchDevice(touchDevice)) + {} + +private slots: + void dragHandlerInSiblingStealingGrabFromMouseAreaViaMouse(); + void dragHandlerInSiblingStealingGrabFromMouseAreaViaTouch_data(); + void dragHandlerInSiblingStealingGrabFromMouseAreaViaTouch(); + +private: + void createView(QScopedPointer<QQuickView> &window, const char *fileName); + QTouchDevice *touchDevice; + QQuickPointerDevice *touchPointerDevice; +}; + +void tst_MouseAreaInterop::createView(QScopedPointer<QQuickView> &window, const char *fileName) +{ + window.reset(new QQuickView); + window->setSource(testFileUrl(fileName)); + QTRY_COMPARE(window->status(), QQuickView::Ready); + QQuickViewTestUtil::centerOnScreen(window.data()); + QQuickViewTestUtil::moveMouseAway(window.data()); + + window->show(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QVERIFY(window->rootObject() != nullptr); +} + +void tst_MouseAreaInterop::dragHandlerInSiblingStealingGrabFromMouseAreaViaMouse() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "dragTakeOverFromSibling.qml"); + QQuickView * window = windowPtr.data(); + auto pointerEvent = QQuickWindowPrivate::get(window)->pointerEventInstance(QQuickPointerDevice::genericMouseDevice()); + + QPointer<QQuickPointerHandler> handler = window->rootObject()->findChild<QQuickPointerHandler*>(); + QVERIFY(handler); + QQuickMouseArea *ma = window->rootObject()->findChild<QQuickMouseArea*>(); + QVERIFY(ma); + + QPoint p1(150, 150); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QCOMPARE(window->mouseGrabberItem(), ma); + QCOMPARE(ma->pressed(), true); + + // Start dragging + // DragHandler keeps monitoring, due to its passive grab, + // and eventually steals the exclusive grab from MA + int dragStoleGrab = 0; + for (int i = 0; i < 4; ++i) { + p1 += QPoint(dragThreshold / 2, 0); + QTest::mouseMove(window, p1); + if (!dragStoleGrab && pointerEvent->point(0)->exclusiveGrabber() == handler) + dragStoleGrab = i; + } + if (dragStoleGrab) + qCDebug(lcPointerTests, "DragHandler stole the grab after %d events", dragStoleGrab); + QVERIFY(dragStoleGrab > 1); + QCOMPARE(handler->active(), true); + QCOMPARE(ma->pressed(), false); + + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QCOMPARE(handler->active(), false); +} + +void tst_MouseAreaInterop::dragHandlerInSiblingStealingGrabFromMouseAreaViaTouch_data() +{ + QTest::addColumn<bool>("preventStealing"); + + QTest::newRow("allow stealing") << false; + QTest::newRow("prevent stealing") << true; +} + +void tst_MouseAreaInterop::dragHandlerInSiblingStealingGrabFromMouseAreaViaTouch() // QTBUG-77624 and QTBUG-79163 +{ + QFETCH(bool, preventStealing); + + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "dragTakeOverFromSibling.qml"); + QQuickView * window = windowPtr.data(); + auto pointerEvent = QQuickWindowPrivate::get(window)->pointerEventInstance(touchPointerDevice); + + QPointer<QQuickPointerHandler> handler = window->rootObject()->findChild<QQuickPointerHandler*>(); + QVERIFY(handler); + QQuickMouseArea *ma = window->rootObject()->findChild<QQuickMouseArea*>(); + QVERIFY(ma); + ma->setPreventStealing(preventStealing); + + QPoint p1(150, 150); + QTest::QTouchEventSequence touch = QTest::touchEvent(window, touchDevice); + + touch.press(1, p1).commit(); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(pointerEvent->point(0)->passiveGrabbers().contains(handler)); + QCOMPARE(pointerEvent->point(0)->grabberItem(), ma); + QCOMPARE(window->mouseGrabberItem(), ma); + QCOMPARE(ma->pressed(), true); + + // Start dragging + // DragHandler keeps monitoring, due to its passive grab, + // and eventually steals the exclusive grab from MA if MA allows it + int dragStoleGrab = 0; + for (int i = 0; i < 4; ++i) { + p1 += QPoint(dragThreshold / 2, 0); + touch.move(1, p1).commit(); + QQuickTouchUtils::flush(window); + if (!dragStoleGrab && pointerEvent->point(0)->exclusiveGrabber() == handler) + dragStoleGrab = i; + } + if (dragStoleGrab) + qCDebug(lcPointerTests, "DragHandler stole the grab after %d events", dragStoleGrab); + if (preventStealing) { + QCOMPARE(dragStoleGrab, 0); + QCOMPARE(handler->active(), false); + QCOMPARE(ma->pressed(), true); + } else { + QVERIFY(dragStoleGrab > 1); + QCOMPARE(handler->active(), true); + QCOMPARE(ma->pressed(), false); + } + + touch.release(1, p1).commit(); + QQuickTouchUtils::flush(window); + QCOMPARE(handler->active(), false); +} + +QTEST_MAIN(tst_MouseAreaInterop) + +#include "tst_mousearea_interop.moc" diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp index 35fed99e8b..4a7a132be2 100644 --- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp +++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp @@ -295,10 +295,10 @@ void tst_MptaInterop::unloadHandlerWithPassiveGrab() QVERIFY(mpta); QPoint point(90, 90); - QTest::mousePress(window, Qt::LeftButton, 0, point); + QTest::mousePress(window, Qt::LeftButton, {}, point); QCOMPARE(window->mouseGrabberItem(), mpta); QTRY_VERIFY(handler.isNull()); // it got unloaded - QTest::mouseRelease(window, Qt::LeftButton, 0, point); // QTBUG-73819: don't crash + QTest::mouseRelease(window, Qt::LeftButton, {}, point); // QTBUG-73819: don't crash } void tst_MptaInterop::dragHandlerInParentStealingGrabFromItem() // QTBUG-75025 diff --git a/tests/auto/quick/pointerhandlers/pointerhandlers.pro b/tests/auto/quick/pointerhandlers/pointerhandlers.pro index 4d6311bdb2..7db28b6583 100644 --- a/tests/auto/quick/pointerhandlers/pointerhandlers.pro +++ b/tests/auto/quick/pointerhandlers/pointerhandlers.pro @@ -3,6 +3,7 @@ TEMPLATE = subdirs qtConfig(private_tests) { SUBDIRS += \ flickableinterop \ + mousearea_interop \ multipointtoucharea_interop \ qquickdraghandler \ qquickhoverhandler \ diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml index 042b730799..800c25c77d 100644 --- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml @@ -31,6 +31,7 @@ import QtQuick 2.12 Rectangle { id: root property alias label: label.text + property alias active: tap.active property alias pressed: tap.pressed property bool checked: false property alias gesturePolicy: tap.gesturePolicy diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp index e77ea97518..419afed3ac 100644 --- a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp @@ -600,10 +600,32 @@ void tst_TapHandler::buttonsMultiTouch() touchSeq.stationary(1).press(2, p2, window).commit(); QQuickTouchUtils::flush(window); QTRY_VERIFY(buttonWithinBounds->property("pressed").toBool()); + QVERIFY(buttonWithinBounds->property("active").toBool()); QPoint p3 = buttonReleaseWithinBounds->mapToScene(QPointF(20, 20)).toPoint(); touchSeq.stationary(1).stationary(2).press(3, p3, window).commit(); QQuickTouchUtils::flush(window); QTRY_VERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + QVERIFY(buttonReleaseWithinBounds->property("active").toBool()); + QVERIFY(buttonWithinBounds->property("pressed").toBool()); + QVERIFY(buttonWithinBounds->property("active").toBool()); + QVERIFY(buttonDragThreshold->property("pressed").toBool()); + + // combinations of small touchpoint movements and stationary points should not cause state changes + p1 += QPoint(2, 0); + p2 += QPoint(3, 0); + touchSeq.move(1, p1).move(2, p2).stationary(3).commit(); + QVERIFY(buttonDragThreshold->property("pressed").toBool()); + QVERIFY(buttonWithinBounds->property("pressed").toBool()); + QVERIFY(buttonWithinBounds->property("active").toBool()); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + QVERIFY(buttonReleaseWithinBounds->property("active").toBool()); + p3 += QPoint(4, 0); + touchSeq.stationary(1).stationary(2).move(3, p3).commit(); + QVERIFY(buttonDragThreshold->property("pressed").toBool()); + QVERIFY(buttonWithinBounds->property("pressed").toBool()); + QVERIFY(buttonWithinBounds->property("active").toBool()); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + QVERIFY(buttonReleaseWithinBounds->property("active").toBool()); // can release top button and press again: others stay pressed the whole time touchSeq.stationary(2).stationary(3).release(1, p1, window).commit(); diff --git a/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp b/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp index d1f6d67aa1..061d5f8a1a 100644 --- a/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp +++ b/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp @@ -185,7 +185,8 @@ void tst_QQuickAccessible::quickAttachedProperties() QObject *object = component.create(); QVERIFY(object != nullptr); - QObject *attachedObject = QQuickAccessibleAttached::attachedProperties(object); + const auto attachedObject = qobject_cast<QQuickAccessibleAttached*>( + QQuickAccessibleAttached::attachedProperties(object)); QVERIFY(attachedObject); if (attachedObject) { QVariant p = attachedObject->property("role"); @@ -195,6 +196,7 @@ void tst_QQuickAccessible::quickAttachedProperties() QCOMPARE(p.isNull(), true); p = attachedObject->property("description"); QCOMPARE(p.isNull(), true); + QCOMPARE(attachedObject->wasNameExplicitlySet(), false); } delete object; } @@ -211,7 +213,8 @@ void tst_QQuickAccessible::quickAttachedProperties() QObject *object = component.create(); QVERIFY(object != nullptr); - QObject *attachedObject = QQuickAccessibleAttached::attachedProperties(object); + const auto attachedObject = qobject_cast<QQuickAccessibleAttached*>( + QQuickAccessibleAttached::attachedProperties(object)); QVERIFY(attachedObject); if (attachedObject) { QVariant p = attachedObject->property("role"); @@ -223,6 +226,7 @@ void tst_QQuickAccessible::quickAttachedProperties() p = attachedObject->property("description"); QCOMPARE(p.isNull(), false); QCOMPARE(p.toString(), QLatin1String("Duck")); + QCOMPARE(attachedObject->wasNameExplicitlySet(), true); } delete object; } @@ -292,6 +296,32 @@ void tst_QQuickAccessible::quickAttachedProperties() } delete object; } + // Check that a name can be implicitly set. + { + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData(R"( + import QtQuick 2.0 + Text { + Accessible.role: Accessible.Button + Accessible.description: "Text Button" + })", QUrl()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object); + + const auto attachedObject = qobject_cast<QQuickAccessibleAttached*>( + QQuickAccessibleAttached::attachedProperties(object.data())); + QVERIFY(attachedObject); + QVERIFY(!attachedObject->wasNameExplicitlySet()); + + attachedObject->setNameImplicitly(QLatin1String("Implicit")); + QCOMPARE(attachedObject->name(), QLatin1String("Implicit")); + QVERIFY(!attachedObject->wasNameExplicitlySet()); + + attachedObject->setName(QLatin1String("Explicit")); + QCOMPARE(attachedObject->name(), QLatin1String("Explicit")); + QVERIFY(attachedObject->wasNameExplicitlySet()); + } QTestAccessibility::clearEvents(); } diff --git a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp index 48f779a490..55957fa71a 100644 --- a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp +++ b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp @@ -265,7 +265,8 @@ void tst_qquickanimations::simplePath() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("pathAnimation.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickRectangle *redRect = rect->findChild<QQuickRectangle*>(); @@ -299,14 +300,13 @@ void tst_qquickanimations::simplePath() pathAnim->start(); QTRY_VERIFY(redRect->rotation() != 0); pathAnim->stop(); - - delete rect; } { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("pathAnimation2.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickRectangle *redRect = rect->findChild<QQuickRectangle*>(); @@ -334,8 +334,6 @@ void tst_qquickanimations::simplePath() QCOMPARE(redRect->x(), qreal(300)); QCOMPARE(redRect->y(), qreal(300)); QCOMPARE(redRect->rotation(), qreal(0)); - - delete rect; } } @@ -343,7 +341,8 @@ void tst_qquickanimations::simpleAnchor() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("reanchor.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickRectangle *greenRect = rect->findChild<QQuickRectangle*>(); @@ -411,15 +410,14 @@ void tst_qquickanimations::simpleAnchor() QCOMPARE(greenRect->y(), qreal(50)); QCOMPARE(greenRect->width(), qreal(150)); QCOMPARE(greenRect->height(), qreal(125)); - - delete rect; } void tst_qquickanimations::reparent() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("reparent.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickRectangle *target = rect->findChild<QQuickRectangle*>("target"); @@ -460,15 +458,14 @@ void tst_qquickanimations::reparent() QCOMPARE(target->height(), qreal(50)); QCOMPARE(target->rotation(), qreal(0)); QCOMPARE(target->scale(), qreal(1)); - - delete rect; } void tst_qquickanimations::pathInterpolator() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("pathInterpolator.qml")); - QQuickPathInterpolator *interpolator = qobject_cast<QQuickPathInterpolator*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *interpolator = qobject_cast<QQuickPathInterpolator*>(obj.data()); QVERIFY(interpolator); QCOMPARE(interpolator->progress(), qreal(0)); @@ -504,7 +501,8 @@ void tst_qquickanimations::pathInterpolatorBackwardJump() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("pathInterpolatorBack.qml")); - QQuickPathInterpolator *interpolator = qobject_cast<QQuickPathInterpolator*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *interpolator = qobject_cast<QQuickPathInterpolator*>(obj.data()); QVERIFY(interpolator); QCOMPARE(interpolator->progress(), qreal(0)); @@ -535,7 +533,8 @@ void tst_qquickanimations::pathInterpolatorBackwardJump() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("pathInterpolatorBack2.qml")); - QQuickPathInterpolator *interpolator = qobject_cast<QQuickPathInterpolator*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *interpolator = qobject_cast<QQuickPathInterpolator*>(obj.data()); QVERIFY(interpolator); QCOMPARE(interpolator->progress(), qreal(0)); @@ -562,7 +561,8 @@ void tst_qquickanimations::pathWithNoStart() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("pathAnimationNoStart.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickRectangle *redRect = rect->findChild<QQuickRectangle*>(); @@ -728,12 +728,9 @@ void tst_qquickanimations::badTypes() { //don't crash { - QQuickView *view = new QQuickView; + QScopedPointer<QQuickView> view(new QQuickView); view->setSource(testFileUrl("badtype1.qml")); - qApp->processEvents(); - - delete view; } //make sure we get a compiler error @@ -741,7 +738,8 @@ void tst_qquickanimations::badTypes() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("badtype2.qml")); QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); - c.create(); + QScopedPointer<QObject> obj(c.create()); + QVERIFY(obj.isNull()); QCOMPARE(c.errors().count(), 1); QCOMPARE(c.errors().at(0).description(), QLatin1String("Invalid property assignment: number expected")); @@ -752,7 +750,8 @@ void tst_qquickanimations::badTypes() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("badtype3.qml")); QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); - c.create(); + QScopedPointer<QObject> obj(c.create()); + QVERIFY(obj.isNull()); QCOMPARE(c.errors().count(), 1); QCOMPARE(c.errors().at(0).description(), QLatin1String("Invalid property assignment: color expected")); @@ -762,7 +761,8 @@ void tst_qquickanimations::badTypes() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("badtype4.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickItemPrivate::get(rect)->setState("state1"); @@ -782,13 +782,15 @@ void tst_qquickanimations::badProperties() QQmlComponent c1(&engine, testFileUrl("badproperty1.qml")); QByteArray message = testFileUrl("badproperty1.qml").toString().toUtf8() + ":18:9: QML ColorAnimation: Cannot animate non-existent property \"border.colr\""; QTest::ignoreMessage(QtWarningMsg, message); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c1.create()); + QScopedPointer<QObject> obj(c1.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQmlComponent c2(&engine, testFileUrl("badproperty2.qml")); message = testFileUrl("badproperty2.qml").toString().toUtf8() + ":18:9: QML ColorAnimation: Cannot animate read-only property \"border\""; QTest::ignoreMessage(QtWarningMsg, message); - rect = qobject_cast<QQuickRectangle*>(c2.create()); + QScopedPointer<QObject> obj2(c2.create()); + rect = qobject_cast<QQuickRectangle*>(obj2.data()); QVERIFY(rect); //### should we warn here are well? @@ -804,7 +806,8 @@ void tst_qquickanimations::mixedTypes() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("mixedtype1.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickItemPrivate::get(rect)->setState("state1"); @@ -820,7 +823,8 @@ void tst_qquickanimations::mixedTypes() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("mixedtype2.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickItemPrivate::get(rect)->setState("state1"); @@ -840,7 +844,8 @@ void tst_qquickanimations::properties() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("properties.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>("TheRect"); @@ -852,7 +857,8 @@ void tst_qquickanimations::properties() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("properties2.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>("TheRect"); @@ -864,7 +870,8 @@ void tst_qquickanimations::properties() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("properties3.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>("TheRect"); @@ -876,7 +883,8 @@ void tst_qquickanimations::properties() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("properties4.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>("TheRect"); @@ -889,7 +897,8 @@ void tst_qquickanimations::properties() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("properties5.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>("TheRect"); @@ -906,7 +915,8 @@ void tst_qquickanimations::propertiesTransition() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("propertiesTransition.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickItemPrivate::get(rect)->setState("moved"); @@ -919,7 +929,8 @@ void tst_qquickanimations::propertiesTransition() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("propertiesTransition2.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>("TheRect"); @@ -934,7 +945,8 @@ void tst_qquickanimations::propertiesTransition() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("propertiesTransition3.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>("TheRect"); @@ -947,7 +959,8 @@ void tst_qquickanimations::propertiesTransition() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("propertiesTransition4.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>("TheRect"); @@ -961,7 +974,8 @@ void tst_qquickanimations::propertiesTransition() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("propertiesTransition5.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>("TheRect"); @@ -975,7 +989,8 @@ void tst_qquickanimations::propertiesTransition() /*{ QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("propertiesTransition6.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>("TheRect"); @@ -989,7 +1004,8 @@ void tst_qquickanimations::propertiesTransition() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("propertiesTransition7.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickItemPrivate::get(rect)->setState("moved"); @@ -1005,7 +1021,8 @@ void tst_qquickanimations::pathTransition() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("pathTransition.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>("redRect"); @@ -1025,7 +1042,8 @@ void tst_qquickanimations::disabledTransition() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("disabledTransition.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>("TheRect"); @@ -1053,12 +1071,12 @@ void tst_qquickanimations::disabledTransition() void tst_qquickanimations::invalidDuration() { - QQuickPropertyAnimation *animation = new QQuickPropertyAnimation; + QScopedPointer<QQuickPropertyAnimation> animation(new QQuickPropertyAnimation); QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML PropertyAnimation: Cannot set a duration of < 0"); animation->setDuration(-1); QCOMPARE(animation->duration(), 250); - QQuickPauseAnimation *pauseAnimation = new QQuickPauseAnimation; + QScopedPointer<QQuickPauseAnimation> pauseAnimation(new QQuickPauseAnimation); QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML PauseAnimation: Cannot set a duration of < 0"); pauseAnimation->setDuration(-1); QCOMPARE(pauseAnimation->duration(), 250); @@ -1071,7 +1089,8 @@ void tst_qquickanimations::attached() QQmlComponent c(&engine, testFileUrl("attached.qml")); QTest::ignoreMessage(QtDebugMsg, "off"); QTest::ignoreMessage(QtDebugMsg, "on"); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); } @@ -1082,7 +1101,8 @@ void tst_qquickanimations::propertyValueSourceDefaultStart() QQmlComponent c(&engine, testFileUrl("valuesource.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickAbstractAnimation *myAnim = rect->findChild<QQuickAbstractAnimation*>("MyAnim"); @@ -1095,7 +1115,8 @@ void tst_qquickanimations::propertyValueSourceDefaultStart() QQmlComponent c(&engine, testFileUrl("valuesource2.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickAbstractAnimation *myAnim = rect->findChild<QQuickAbstractAnimation*>("MyAnim"); @@ -1108,7 +1129,8 @@ void tst_qquickanimations::propertyValueSourceDefaultStart() QQmlComponent c(&engine, testFileUrl("dontAutoStart.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickAbstractAnimation *myAnim = rect->findChild<QQuickAbstractAnimation*>("MyAnim"); @@ -1127,7 +1149,8 @@ void tst_qquickanimations::dontStart() QString warning = c.url().toString() + ":14:13: QML NumberAnimation: setRunning() cannot be used on non-root animation nodes."; QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickAbstractAnimation *myAnim = rect->findChild<QQuickAbstractAnimation*>("MyAnim"); @@ -1142,7 +1165,8 @@ void tst_qquickanimations::dontStart() QString warning = c.url().toString() + ":15:17: QML NumberAnimation: setRunning() cannot be used on non-root animation nodes."; QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickAbstractAnimation *myAnim = rect->findChild<QQuickAbstractAnimation*>("MyAnim"); @@ -1158,7 +1182,8 @@ void tst_qquickanimations::easingProperties() QString componentStr = "import QtQuick 2.0\nNumberAnimation { easing.type: \"InOutQuad\" }"; QQmlComponent animationComponent(&engine); animationComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); - QQuickPropertyAnimation *animObject = qobject_cast<QQuickPropertyAnimation*>(animationComponent.create()); + QScopedPointer<QObject> obj(animationComponent.create()); + auto *animObject = qobject_cast<QQuickPropertyAnimation *>(obj.data()); QVERIFY(animObject != nullptr); QCOMPARE(animObject->easing().type(), QEasingCurve::InOutQuad); @@ -1169,7 +1194,8 @@ void tst_qquickanimations::easingProperties() QString componentStr = "import QtQuick 2.0\nPropertyAnimation { easing.type: \"OutBounce\"; easing.amplitude: 5.0 }"; QQmlComponent animationComponent(&engine); animationComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); - QQuickPropertyAnimation *animObject = qobject_cast<QQuickPropertyAnimation*>(animationComponent.create()); + QScopedPointer<QObject> obj(animationComponent.create()); + auto *animObject = qobject_cast<QQuickPropertyAnimation *>(obj.data()); QVERIFY(animObject != nullptr); QCOMPARE(animObject->easing().type(), QEasingCurve::OutBounce); @@ -1181,7 +1207,8 @@ void tst_qquickanimations::easingProperties() QString componentStr = "import QtQuick 2.0\nPropertyAnimation { easing.type: \"OutElastic\"; easing.amplitude: 5.0; easing.period: 3.0}"; QQmlComponent animationComponent(&engine); animationComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); - QQuickPropertyAnimation *animObject = qobject_cast<QQuickPropertyAnimation*>(animationComponent.create()); + QScopedPointer<QObject> obj(animationComponent.create()); + auto *animObject = qobject_cast<QQuickPropertyAnimation *>(obj.data()); QVERIFY(animObject != nullptr); QCOMPARE(animObject->easing().type(), QEasingCurve::OutElastic); @@ -1194,7 +1221,8 @@ void tst_qquickanimations::easingProperties() QString componentStr = "import QtQuick 2.0\nPropertyAnimation { easing.type: \"InOutBack\"; easing.overshoot: 2 }"; QQmlComponent animationComponent(&engine); animationComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); - QQuickPropertyAnimation *animObject = qobject_cast<QQuickPropertyAnimation*>(animationComponent.create()); + QScopedPointer<QObject> obj(animationComponent.create()); + auto *animObject = qobject_cast<QQuickPropertyAnimation *>(obj.data()); QVERIFY(animObject != nullptr); QCOMPARE(animObject->easing().type(), QEasingCurve::InOutBack); @@ -1206,7 +1234,8 @@ void tst_qquickanimations::easingProperties() QString componentStr = "import QtQuick 2.0\nPropertyAnimation { easing.type: \"Bezier\"; easing.bezierCurve: [0.5, 0.2, 0.13, 0.65, 1.0, 1.0] }"; QQmlComponent animationComponent(&engine); animationComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); - QQuickPropertyAnimation *animObject = qobject_cast<QQuickPropertyAnimation*>(animationComponent.create()); + QScopedPointer<QObject> obj(animationComponent.create()); + auto *animObject = qobject_cast<QQuickPropertyAnimation *>(obj.data()); QVERIFY(animObject != nullptr); QCOMPARE(animObject->easing().type(), QEasingCurve::BezierSpline); @@ -1222,7 +1251,8 @@ void tst_qquickanimations::rotation() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("rotation.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickRectangle *rr = rect->findChild<QQuickRectangle*>("rr"); @@ -1252,7 +1282,8 @@ void tst_qquickanimations::startStopSignals() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("signals.qml")); - QQuickItem *root = qobject_cast<QQuickItem*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *root = qobject_cast<QQuickItem *>(obj.data()); QVERIFY(root); QCOMPARE(root->property("startedCount").toInt(), 1); //autostart @@ -1297,7 +1328,8 @@ void tst_qquickanimations::runningTrueBug() //ensure we start correctly when "running: true" is explicitly set QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("runningTrueBug.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickRectangle *cloud = rect->findChild<QQuickRectangle*>("cloud"); @@ -1312,7 +1344,8 @@ void tst_qquickanimations::pathAnimationInOutBackBug() //ensure we don't pass bad progress value (out of [0,1]) to QQuickPath::backwardsPointAt() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("pathAnimationInOutBackCrash.qml")); - QQuickItem *item = qobject_cast<QQuickItem*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *item = qobject_cast<QQuickItem *>(obj.data()); QVERIFY(item); QQuickRectangle *rect = item->findChild<QQuickRectangle *>("rect"); @@ -1330,7 +1363,8 @@ void tst_qquickanimations::nonTransitionBug() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("nonTransitionBug.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect != nullptr); QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(rect); QQuickRectangle *mover = rect->findChild<QQuickRectangle*>("mover"); @@ -1356,7 +1390,8 @@ void tst_qquickanimations::registrationBug() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("registrationBug.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect != nullptr); QTRY_COMPARE(rect->property("value"), QVariant(int(100))); } @@ -1366,7 +1401,8 @@ void tst_qquickanimations::doubleRegistrationBug() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("doubleRegistrationBug.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect != nullptr); QQuickAbstractAnimation *anim = rect->findChild<QQuickAbstractAnimation*>("animation"); @@ -1407,7 +1443,8 @@ void tst_qquickanimations::transitionAssignmentBug() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("transitionAssignmentBug.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect != nullptr); QCOMPARE(rect->property("nullObject").toBool(), false); @@ -1419,12 +1456,11 @@ void tst_qquickanimations::pauseBindingBug() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("pauseBindingBug.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect != nullptr); QQuickAbstractAnimation *anim = rect->findChild<QQuickAbstractAnimation*>("animation"); QCOMPARE(anim->qtAnimation()->state(), QAbstractAnimationJob::Paused); - - delete rect; } //QTBUG-13598 @@ -1433,13 +1469,12 @@ void tst_qquickanimations::pauseBug() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("pauseBug.qml")); - QQuickAbstractAnimation *anim = qobject_cast<QQuickAbstractAnimation*>(c.create()); + QScopedPointer<QObject> obj(c.create()); + auto *anim = qobject_cast<QQuickAbstractAnimation*>(obj.data()); QVERIFY(anim != nullptr); QCOMPARE(anim->qtAnimation()->state(), QAbstractAnimationJob::Paused); QCOMPARE(anim->isPaused(), true); QCOMPARE(anim->isRunning(), true); - - delete anim; } //QTBUG-23092 @@ -1448,7 +1483,7 @@ void tst_qquickanimations::loopingBug() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("looping.qml")); - QObject *obj = c.create(); + QScopedPointer<QObject> obj(c.create()); QQuickAbstractAnimation *anim = obj->findChild<QQuickAbstractAnimation*>(); QVERIFY(anim != nullptr); @@ -1460,8 +1495,6 @@ void tst_qquickanimations::loopingBug() QQuickRectangle *rect = obj->findChild<QQuickRectangle*>(); QVERIFY(rect != nullptr); QCOMPARE(rect->rotation(), qreal(90)); - - delete obj; } //QTBUG-24532 @@ -1482,7 +1515,7 @@ void tst_qquickanimations::scriptActionBug() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("scriptActionBug.qml")); - QObject *obj = c.create(); + QScopedPointer<QObject> obj(c.create()); //Both the ScriptAction and StateChangeScript should be triggered QCOMPARE(obj->property("actionTriggered").toBool(), true); @@ -1496,20 +1529,16 @@ void tst_qquickanimations::groupAnimationNullChildBug() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("sequentialAnimationNullChildBug.qml")); - QQuickItem *root = qobject_cast<QQuickItem*>(c.create()); + QScopedPointer<QObject> root(c.create()); QVERIFY(root); - - delete root; } { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("parallelAnimationNullChildBug.qml")); - QQuickItem *root = qobject_cast<QQuickItem*>(c.create()); + QScopedPointer<QObject> root(c.create()); QVERIFY(root); - - delete root; } } @@ -1518,12 +1547,10 @@ void tst_qquickanimations::scriptActionCrash() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("scriptActionCrash.qml")); - QObject *obj = c.create(); + QScopedPointer<QObject> obj(c.create()); //just testing that we don't crash QTest::qWait(1000); //5x transition duration - - delete obj; } // QTBUG-49364 @@ -1534,12 +1561,10 @@ void tst_qquickanimations::animatorInvalidTargetCrash() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("animatorInvalidTargetCrash.qml")); - QObject *obj = c.create(); + QScopedPointer<QObject> obj(c.create()); //just testing that we don't crash QTest::qWait(5000); //animator duration - - delete obj; } Q_DECLARE_METATYPE(QList<QQmlError>) @@ -1555,7 +1580,8 @@ void tst_qquickanimations::defaultPropertyWarning() QVERIFY(warnings.isValid()); QQmlComponent component(&engine, testFileUrl("defaultRotationAnimation.qml")); - QScopedPointer<QQuickItem> root(qobject_cast<QQuickItem*>(component.create())); + QScopedPointer<QObject> obj(component.create()); + auto *root = qobject_cast<QQuickItem *>(obj.data()); QVERIFY(root); QVERIFY(warnings.isEmpty()); @@ -1566,7 +1592,8 @@ void tst_qquickanimations::pathSvgAnimation() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("pathSvgAnimation.qml")); - QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(component.create())); + QScopedPointer<QObject> obj(component.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickRectangle *redRect = rect->findChild<QQuickRectangle*>(); @@ -1587,7 +1614,8 @@ void tst_qquickanimations::pathLineUnspecifiedXYBug() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("pathLineUnspecifiedXYBug.qml")); - QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(component.create())); + QScopedPointer<QObject> obj(component.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); QVERIFY(rect); QQuickRectangle *redRect = rect->findChild<QQuickRectangle*>(); @@ -1623,7 +1651,8 @@ void tst_qquickanimations::finished() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("finished.qml")); - QScopedPointer<QObject> root(component.create()); + QScopedPointer<QObject> obj(component.create()); + auto *root = qobject_cast<QQuickItem *>(obj.data()); QVERIFY(root); // Test that finished() is emitted for a simple top-level animation. @@ -1696,7 +1725,8 @@ void tst_qquickanimations::replacingTransitions() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("replacingTransitions.qml")); - QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(c.create())); + QScopedPointer<QObject> obj(c.create()); + auto *rect = qobject_cast<QQuickRectangle*>(obj.data()); if (!c.errors().isEmpty()) qDebug() << c.errorString(); QVERIFY(rect); @@ -1732,13 +1762,14 @@ void tst_qquickanimations::animationJobSelfDestruction() QQmlEngine engine; engine.clearComponentCache(); QQmlComponent c(&engine, testFileUrl("animationJobSelfDestructionBug.qml")); - QScopedPointer<QQuickWindow> win(qobject_cast<QQuickWindow*>(c.create())); + QScopedPointer<QObject> obj(c.create()); + auto *win = qobject_cast<QQuickWindow *>(obj.data()); if (!c.errors().isEmpty()) qDebug() << c.errorString(); QVERIFY(win); win->setTitle(QTest::currentTestFunction()); win->show(); - QVERIFY(QTest::qWaitForWindowExposed(win.data())); + QVERIFY(QTest::qWaitForWindowExposed(win)); QQmlTimer *timer = win->property("timer").value<QQmlTimer*>(); QVERIFY(timer); QCOMPARE(timer->isRunning(), false); @@ -1752,13 +1783,14 @@ void tst_qquickanimations::fastFlickingBug() QQmlEngine engine; engine.clearComponentCache(); QQmlComponent c(&engine, testFileUrl("fastFlickingBug.qml")); - QScopedPointer<QQuickWindow> win(qobject_cast<QQuickWindow*>(c.create())); + QScopedPointer<QObject> obj(c.create()); + auto *win = qobject_cast<QQuickWindow *>(obj.data()); if (!c.errors().isEmpty()) qDebug() << c.errorString(); QVERIFY(win); win->setTitle(QTest::currentTestFunction()); win->show(); - QVERIFY(QTest::qWaitForWindowExposed(win.data())); + QVERIFY(QTest::qWaitForWindowExposed(win)); auto timer = win->property("timer").value<QQmlTimer*>(); QVERIFY(timer); QCOMPARE(timer->isRunning(), false); diff --git a/tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml b/tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml index e8b3960486..be94fca8d4 100644 --- a/tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml +++ b/tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml @@ -1073,5 +1073,38 @@ Item { layout[propName] = data.value compare(layout.spy.count, 1) } + + Component { + id: layout_columnIsOutsideGrid_Component + GridLayout { + columns: 2 + Item { + Layout.row: 0 + Layout.column: 1 + } + Item { + implicitWidth: 10 + implicitHeight: 10 + Layout.row: 0 + Layout.column: 2 + } + Item { + Layout.columnSpan: 2 + Layout.fillWidth: true + Layout.fillHeight: true + } + } + } + + function test_columnIsOutsideGrid() + { + ignoreWarning(/QML Item: Layout: column \(2\) should be less than the number of columns \(2\)/); + var layout = layout_columnIsOutsideGrid_Component.createObject(container); + layout.width = layout.implicitWidth + layout.height = layout.implicitHeight + waitForRendering(layout); + layout.destroy() + } + } } diff --git a/tests/auto/quick/qquicklistview/data/listview-initCurrent.qml b/tests/auto/quick/qquicklistview/data/listview-initCurrent.qml index 8aff649a67..41d5bd3491 100644 --- a/tests/auto/quick/qquicklistview/data/listview-initCurrent.qml +++ b/tests/auto/quick/qquicklistview/data/listview-initCurrent.qml @@ -7,6 +7,7 @@ Rectangle { property bool showHeader: false property bool showFooter: false property int currentItemChangedCount: 0 + property string s: ""+list.currentIndex width: 240 height: 320 diff --git a/tests/auto/quick/qquicklistview/data/moveObjectModelItemToAnotherObjectModel.qml b/tests/auto/quick/qquicklistview/data/moveObjectModelItemToAnotherObjectModel.qml new file mode 100644 index 0000000000..ebdebeb449 --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/moveObjectModelItemToAnotherObjectModel.qml @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** 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:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.14 +import QtQml.Models 2.14 + +Item { + id: root + width: 400 + height: 400 + + readonly property int rectCount: 3 + property var rectColors: ["red", "green", "blue"] + + property alias listView1: listView1 + property alias listView2: listView2 + + function moveRedRectToModel2() { + var appItem = objectModel1.get(0) + objectModel1.remove(0, 1) + objectModel2.insert(0, appItem) + } + + function moveRedRectToModel1() { + var appItem = objectModel2.get(0) + objectModel2.remove(0, 1) + objectModel1.insert(0, appItem) + } + + ObjectModel { + id: objectModel1 + objectName: "objectModel1" + + Component.onCompleted: { + for (var i = 0; i < root.rectCount; i++) { + var outerRect = rectComponent.createObject(null, { + "objectName": root.rectColors[i] + "Rect", + "color": root.rectColors[i] + }) + objectModel1.append(outerRect) + } + } + } + + ObjectModel { + id: objectModel2 + objectName: "objectModel2" + } + + ListView { + id: listView1 + objectName: "listView1" + anchors.left: parent.left + anchors.top: parent.top + height: 100 + width: 100 + anchors.margins: 20 + clip: true + cacheBuffer: 0 + model: objectModel1 + orientation: ListView.Horizontal + spacing: 20 + + Component.onCompleted: contentItem.objectName = "listView1ContentItem" + } + + ListView { + id: listView2 + objectName: "listView2" + anchors.right: parent.right + anchors.top: parent.top + height: 100 + width: 100 + anchors.margins: 20 + clip: true + cacheBuffer: 0 + model: objectModel2 + orientation: ListView.Horizontal + spacing: 20 + + Component.onCompleted: contentItem.objectName = "listView2ContentItem" + } + + Component { + id: rectComponent + + Rectangle { + height: 100 + width: 100 + opacity: 0.2 + } + } +} diff --git a/tests/auto/quick/qquicklistview/data/reusedelegateitems.qml b/tests/auto/quick/qquicklistview/data/reusedelegateitems.qml new file mode 100644 index 0000000000..773fb50f81 --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/reusedelegateitems.qml @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** 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:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.15 + +Rectangle { + id: root + width: 640 + height: 480 + + property int rows: 500 + property int columns: 20 + property real delegateHeight: 30 + property real delegateWidth: 50 + + ListView { + id: list + anchors.fill: parent + anchors.margins: 10 + objectName: "list" + + model: reuseModel + reuseItems: true + + cacheBuffer: 0 + contentWidth: columns * delegateWidth + contentHeight: rows * delegateHeight + clip: true + + property int delegatesCreatedCount: 0 + + delegate: Item { + objectName: "delegate" + width: list.contentWidth + height: delegateHeight + + property int modelIndex: index + property int reusedCount: 0 + property int pooledCount: 0 + property string displayBinding: display + + ListView.onPooled: pooledCount++ + ListView.onReused: reusedCount++ + Component.onCompleted: list.delegatesCreatedCount++ + + Text { + id: text1 + text: display + " (Model index: " + modelIndex + ", Reused count: " + reusedCount + ")" + } + } + } +} diff --git a/tests/auto/quick/qquicklistview/qquicklistview.pro b/tests/auto/quick/qquicklistview/qquicklistview.pro index b08fca2b1d..10edb06549 100644 --- a/tests/auto/quick/qquicklistview/qquicklistview.pro +++ b/tests/auto/quick/qquicklistview/qquicklistview.pro @@ -5,7 +5,8 @@ macx:CONFIG -= app_bundle HEADERS += incrementalmodel.h \ proxytestinnermodel.h \ - randomsortmodel.h + randomsortmodel.h \ + reusemodel.h SOURCES += tst_qquicklistview.cpp \ incrementalmodel.cpp \ proxytestinnermodel.cpp \ diff --git a/tests/auto/quick/qquicklistview/reusemodel.h b/tests/auto/quick/qquicklistview/reusemodel.h new file mode 100644 index 0000000000..21e6739384 --- /dev/null +++ b/tests/auto/quick/qquicklistview/reusemodel.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef REUSEMODEL_H +#define REUSEMODEL_H + +#include <QAbstractListModel> +#include <QList> +#include <QStringList> + +class ReuseModel : public QAbstractListModel +{ + Q_OBJECT + +public: + ReuseModel(int rowCount, QObject *parent = nullptr) + : QAbstractListModel(parent) + , m_rowCount(rowCount) + {} + + int rowCount(const QModelIndex & = QModelIndex()) const override + { + return m_rowCount; + } + + QVariant data(const QModelIndex &index, int role) const override + { + if (!index.isValid()) + return QVariant(); + + switch (role) { + case Qt::DisplayRole: + return displayStringForRow(index.row()); + default: + break; + } + + return QVariant(); + } + + QString displayStringForRow(int row) const + { + return row % 2 == 0 ? + QStringLiteral("Even%1").arg(row) : + QStringLiteral("Odd%1").arg(row); + } + + QHash<int, QByteArray> roleNames() const override + { + return { + {Qt::DisplayRole, "display"}, + }; + } + +private: + int m_rowCount; +}; + +#endif diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index fb5ae168e8..e527853148 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -49,6 +49,7 @@ #include "incrementalmodel.h" #include "proxytestinnermodel.h" #include "randomsortmodel.h" +#include "reusemodel.h" #include <math.h> Q_DECLARE_METATYPE(Qt::LayoutDirection) @@ -268,6 +269,7 @@ private slots: void QTBUG_61269_appendDuringScrollDown_data(); void QTBUG_50097_stickyHeader_positionViewAtIndex(); void QTBUG_63974_stickyHeader_positionViewAtIndex_Contain(); + void QTBUG_66163_setModelViewPortSizeChange(); void itemFiltered(); void releaseItems(); @@ -280,9 +282,14 @@ private slots: void setPositionOnLayout(); void touchCancel(); void resizeAfterComponentComplete(); + void dragOverFloatingHeaderOrFooter(); void delegateWithRequiredProperties(); + void reuse_reuseIsOffByDefault(); + void reuse_checkThatItemsAreReused(); + void moveObjectModelItemToAnotherObjectModel(); + private: template <class T> void items(const QUrl &source); template <class T> void changed(const QUrl &source); @@ -2901,7 +2908,11 @@ void tst_QQuickListView::currentIndex() // empty model should reset currentIndex to -1 QaimModel emptyModel; + window->rootObject()->setProperty("currentItemChangedCount", QVariant(0)); + QVERIFY(QQmlProperty(window->rootObject(), "s").read().toString() != QLatin1String("-1")); ctxt->setContextProperty("testModel", &emptyModel); + QCOMPARE(QQmlProperty(window->rootObject(), "s").read().toString(), "-1"); + QCOMPARE(window->rootObject()->property("currentItemChangedCount").toInt(), 1); QCOMPARE(listview->currentIndex(), -1); delete window; @@ -8855,6 +8866,62 @@ void tst_QQuickListView::QTBUG_63974_stickyHeader_positionViewAtIndex_Contain() QTRY_COMPARE(listview->contentY(), -headerSize); } +void tst_QQuickListView::QTBUG_66163_setModelViewPortSizeChange() +{ + QScopedPointer<QQuickView> window(createView()); + QQmlComponent comp(window->engine()); + comp.setData(R"( + import QtQuick 2.0 + + Item { + id: root + width: 400 + height: 400 + + ListView { + id: view + objectName: "view" + anchors.fill: parent + + model: 4 + highlightRangeMode: ListView.StrictlyEnforceRange + + delegate: Rectangle { + color: index % 2 ? "green" : "orange" + width: parent.width + height: 50 + } + + populate: Transition { + SequentialAnimation { + NumberAnimation { property: "y"; from: 100; duration: 1000 } + } + } + } + } + )", QUrl("testData")); + auto root {qobject_cast<QQuickItem*>(comp.create())}; + QVERIFY(root); + window->setContent(QUrl(), &comp, root); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + auto view = root->findChild<QQuickListView *>("view"); + QVERIFY(view); + QVERIFY(QQuickTest::qWaitForItemPolished(view)); + QSignalSpy spy(view, &QQuickListView::contentYChanged); + auto transition = view->property("populate").value<QQuickTransition*>(); + QVERIFY(transition); + QQmlProperty model(view, "model"); + QVERIFY(model.isValid()); + model.write(5); + // Animations inside a Transition do not emit a finished signal + // so we cannot wait for them in that way + QTest::qWait(1100); // animation takes 1000ms, + 10% extra delay + /* the viewport should not have changed, thus there should not have + been any contentYChanged signal*/ + QCOMPARE(spy.count(), 0); +} + void tst_QQuickListView::itemFiltered() { QStringListModel model(QStringList() << "one" << "two" << "three" << "four" << "five" << "six"); @@ -9158,6 +9225,196 @@ void tst_QQuickListView::delegateWithRequiredProperties() } } +void tst_QQuickListView::reuse_reuseIsOffByDefault() +{ + // Check that delegate recycling is off by default. The reason is that + // ListView needs to be backwards compatible with legacy applications. And + // when using delegate recycling, there are certain differences, like that + // a delegates Component.onCompleted will just be called the first time the + // item is created, and not when it's reused. + QScopedPointer<QQuickView> window(createView()); + window->setSource(testFileUrl("listviewtest.qml")); + window->resize(640, 480); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QQuickListView *listView = findItem<QQuickListView>(window->rootObject(), "list"); + QVERIFY(listView != nullptr); + QVERIFY(!listView->reuseItems()); +} + +void tst_QQuickListView::reuse_checkThatItemsAreReused() +{ + // Flick up and down one page of items. Check that this results in the + // delegate items being reused once. + // Note that this is slightly different from tableview, which will reuse the items + // twice during a similar down-then-up flick. The reason is that listview fills up + // free space in the view with items _before_ it release old items that have been + // flicked out. But changing this will break other auto tests (and perhaps legacy + // apps), so we have chosen to stick with this behavior for now. + QScopedPointer<QQuickView> window(createView()); + + ReuseModel model(100); + QQmlContext *ctxt = window->rootContext(); + ctxt->setContextProperty("reuseModel", &model); + + window->setSource(testFileUrl("reusedelegateitems.qml")); + window->resize(640, 480); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QQuickListView *listView = findItem<QQuickListView>(window->rootObject(), "list"); + QTRY_VERIFY(listView != nullptr); + const auto itemView_d = QQuickItemViewPrivate::get(listView); + + QVERIFY(listView->reuseItems()); + + auto items = findItems<QQuickItem>(listView, "delegate"); + const int initialItemCount = items.count(); + QVERIFY(initialItemCount > 0); + + // Sanity check that the size of the initial list of items match the count we tracked from QML + QCOMPARE(listView->property("delegatesCreatedCount").toInt(), initialItemCount); + + // Go through all the initial items and check that they have not been reused yet + for (const auto item : qAsConst(items)) + QCOMPARE(item->property("reusedCount").toInt(), 0); + + // Flick one page down and count how many items we have created thus + // far. We expect this number to be twice as high as the initial count + // since we flicked one whole page. + const qreal delegateHeight = items.at(0)->height(); + const qreal flickDistance = (initialItemCount * delegateHeight) + 1; + listView->setContentY(flickDistance); + QVERIFY(QQuickTest::qWaitForItemPolished(listView)); + const int countAfterDownFlick = listView->property("delegatesCreatedCount").toInt(); + QCOMPARE(countAfterDownFlick, initialItemCount * 2); + + // Check that the reuse pool is now populated. We expect all initial items to be pooled, + // except model index 0, which was never reused or released, since it's ListView.currentItem. + const int poolSizeAfterDownFlick = itemView_d->model->poolSize(); + QCOMPARE(poolSizeAfterDownFlick, initialItemCount - 1); + + // Go through all items and check that all model data inside the delegate + // have values updated according to their model index. Since model roles + // like 'display' are injected into the context in a special way by the + // QML model classes, we need to catch it through a binding instead (which is + // OK, since then we can also check that bindings are updated when reused). + items = findItems<QQuickItem>(listView, "delegate"); + for (const auto item : qAsConst(items)) { + const QString display = item->property("displayBinding").toString(); + const int modelIndex = item->property("modelIndex").toInt(); + QVERIFY(modelIndex >= initialItemCount); + QCOMPARE(display, model.displayStringForRow(modelIndex)); + } + + // Flick one page up. This time there shouldn't be any new items created, so + // delegatesCreatedCount should remain unchanged. But while we reuse all the items + // in the pool during the flick, we also fill it up again with all the items that + // were inside the page that was flicked out. + listView->setContentY(0); + QVERIFY(QQuickTest::qWaitForItemPolished(listView)); + const int countAfterUpFlick = listView->property("delegatesCreatedCount").toInt(); + const int poolSizeAfterUpFlick = itemView_d->model->poolSize(); + QCOMPARE(countAfterUpFlick, countAfterDownFlick); + QCOMPARE(poolSizeAfterUpFlick, initialItemCount); + + // Go through all items and check that they have been reused exactly once + // (except for ListView.currentItem, which was never released). + const auto listViewCurrentItem = listView->currentItem(); + items = findItems<QQuickItem>(listView, "delegate"); + for (const auto item : qAsConst(items)) { + const int reusedCount = item->property("reusedCount").toInt(); + if (item == listViewCurrentItem) + QCOMPARE(reusedCount, 0); + else + QCOMPARE(reusedCount, 1); + } + + // Go through all items again and check that all model data inside the delegate + // have correct values now that they have been reused. + items = findItems<QQuickItem>(listView, "delegate"); + for (const auto item : qAsConst(items)) { + const QString display = item->property("displayBinding").toString(); + const int modelIndex = item->property("modelIndex").toInt(); + QVERIFY(modelIndex < initialItemCount); + QCOMPARE(display, model.displayStringForRow(modelIndex)); + } +} + +void tst_QQuickListView::dragOverFloatingHeaderOrFooter() // QTBUG-74046 +{ + QQuickView *window = getView(); + QQuickViewTestUtil::moveMouseAway(window); + window->setSource(testFileUrl("qtbug63974.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + QQuickListView *listview = qmlobject_cast<QQuickListView *>(window->rootObject()); + QVERIFY(listview); + QCOMPARE(listview->contentY(), -20); + + // Drag downwards from the header: the list shouldn't move + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(10,10)); + for (int i = 0; i < 10; ++i) + QTest::mouseMove(window, QPoint(10, 10 + i * 10)); + QCOMPARE(listview->isMoving(), false); + QCOMPARE(listview->contentY(), -20); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier); + + // Drag upwards from the footer: the list shouldn't move + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(10,190)); + for (int i = 0; i < 10; ++i) + QTest::mouseMove(window, QPoint(10, 190 - i * 10)); + QCOMPARE(listview->isMoving(), false); + QCOMPARE(listview->contentY(), -20); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier); + + // Drag upwards from the middle: the list should move + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(10,100)); + for (int i = 0; i < 10 && listview->contentY() == -20; ++i) + QTest::mouseMove(window, QPoint(10, 100 - i * 10)); + QVERIFY(listview->isMoving()); + QVERIFY(listview->contentY() > -20); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier); + + releaseView(window); +} + +void tst_QQuickListView::moveObjectModelItemToAnotherObjectModel() +{ + QScopedPointer<QQuickView> window(createView()); + window->setSource(testFileUrl("moveObjectModelItemToAnotherObjectModel.qml")); + QCOMPARE(window->status(), QQuickView::Ready); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QObject *root = window->rootObject(); + QVERIFY(root); + + const QQuickListView *listView1 = root->property("listView1").value<QQuickListView*>(); + QVERIFY(listView1); + + const QQuickListView *listView2 = root->property("listView2").value<QQuickListView*>(); + QVERIFY(listView2); + + const QQuickItem *redRect = listView1->itemAtIndex(0); + QVERIFY(redRect); + QCOMPARE(redRect->objectName(), QString::fromLatin1("redRect")); + + QVERIFY(QMetaObject::invokeMethod(root, "moveRedRectToModel2")); + QVERIFY(QQuickTest::qIsPolishScheduled(listView2)); + QVERIFY(QQuickTest::qWaitForItemPolished(listView2)); + QVERIFY(redRect->isVisible()); + QVERIFY(!QQuickItemPrivate::get(redRect)->culled); + + QVERIFY(QMetaObject::invokeMethod(root, "moveRedRectToModel1")); + QVERIFY(QQuickTest::qIsPolishScheduled(listView1)); + QVERIFY(QQuickTest::qWaitForItemPolished(listView1)); + QVERIFY(redRect->isVisible()); + QVERIFY(!QQuickItemPrivate::get(redRect)->culled); +} + QTEST_MAIN(tst_QQuickListView) #include "tst_qquicklistview.moc" diff --git a/tests/auto/quick/qquickmousearea/BLACKLIST b/tests/auto/quick/qquickmousearea/BLACKLIST deleted file mode 100644 index f2cb00225b..0000000000 --- a/tests/auto/quick/qquickmousearea/BLACKLIST +++ /dev/null @@ -1,4 +0,0 @@ -# QTBUG-78153 -[nestedStopAtBounds] -opensuse-leap - diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 54a29dbc7f..6efa2e6b3a 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -717,7 +717,7 @@ void tst_QQuickMouseArea::updateMouseAreaPosOnClick() QCOMPARE(mouseRegion->mouseX(), rect->x()); QCOMPARE(mouseRegion->mouseY(), rect->y()); - QMouseEvent event(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, nullptr); + QMouseEvent event(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {}); QGuiApplication::sendEvent(&window, &event); QCOMPARE(mouseRegion->mouseX(), 100.0); @@ -745,7 +745,7 @@ void tst_QQuickMouseArea::updateMouseAreaPosOnResize() QCOMPARE(mouseRegion->mouseX(), 0.0); QCOMPARE(mouseRegion->mouseY(), 0.0); - QMouseEvent event(QEvent::MouseButtonPress, rect->position().toPoint(), Qt::LeftButton, Qt::LeftButton, nullptr); + QMouseEvent event(QEvent::MouseButtonPress, rect->position().toPoint(), Qt::LeftButton, Qt::LeftButton, {}); QGuiApplication::sendEvent(&window, &event); QVERIFY(!mouseRegion->property("emitPositionChanged").toBool()); @@ -774,7 +774,7 @@ void tst_QQuickMouseArea::noOnClickedWithPressAndHold() QQuickMouseArea *mouseArea = qobject_cast<QQuickMouseArea*>(window.rootObject()->children().first()); QVERIFY(mouseArea); - QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, nullptr); + QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {}); QGuiApplication::sendEvent(&window, &pressEvent); QCOMPARE(mouseArea->pressedButtons(), Qt::LeftButton); @@ -788,7 +788,7 @@ void tst_QQuickMouseArea::noOnClickedWithPressAndHold() QVERIFY(!window.rootObject()->property("clicked").toBool()); QVERIFY(window.rootObject()->property("held").toBool()); - QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, nullptr); + QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {}); QGuiApplication::sendEvent(&window, &releaseEvent); QTRY_VERIFY(window.rootObject()->property("held").toBool()); @@ -804,14 +804,14 @@ void tst_QQuickMouseArea::noOnClickedWithPressAndHold() QVERIFY(QTest::qWaitForWindowExposed(&window)); QVERIFY(window.rootObject() != nullptr); - QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, nullptr); + QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {}); QGuiApplication::sendEvent(&window, &pressEvent); QVERIFY(!window.rootObject()->property("clicked").toBool()); QTest::qWait(1000); - QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, nullptr); + QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {}); QGuiApplication::sendEvent(&window, &releaseEvent); QVERIFY(window.rootObject()->property("clicked").toBool()); @@ -835,7 +835,7 @@ void tst_QQuickMouseArea::onMousePressRejected() QVERIFY(!window.rootObject()->property("mr2_released").toBool()); QVERIFY(!window.rootObject()->property("mr2_canceled").toBool()); - QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, nullptr); + QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {}); QGuiApplication::sendEvent(&window, &pressEvent); QVERIFY(window.rootObject()->property("mr1_pressed").toBool()); @@ -847,7 +847,7 @@ void tst_QQuickMouseArea::onMousePressRejected() QTest::qWait(200); - QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, nullptr); + QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {}); QGuiApplication::sendEvent(&window, &releaseEvent); QVERIFY(window.rootObject()->property("mr1_released").toBool()); @@ -882,8 +882,8 @@ void tst_QQuickMouseArea::pressedCanceledOnWindowDeactivate() QCOMPARE(window.rootObject()->property("clicked").toInt(), expectedClicks); - QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, nullptr); - QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, nullptr); + QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {}); + QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {}); QGuiApplication::sendEvent(&window, &pressEvent); @@ -900,7 +900,7 @@ void tst_QQuickMouseArea::pressedCanceledOnWindowDeactivate() QCOMPARE(window.rootObject()->property("clicked").toInt(), ++expectedClicks); QGuiApplication::sendEvent(&window, &pressEvent); - QMouseEvent pressEvent2(QEvent::MouseButtonDblClick, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, nullptr); + QMouseEvent pressEvent2(QEvent::MouseButtonDblClick, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {}); QGuiApplication::sendEvent(&window, &pressEvent2); QTRY_VERIFY(window.rootObject()->property("pressed").toBool()); @@ -953,16 +953,16 @@ void tst_QQuickMouseArea::doubleClick() // The sequence for a double click is: // press, release, (click), press, double click, release - QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), button, button, nullptr); + QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), button, button, {}); QGuiApplication::sendEvent(&window, &pressEvent); - QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), button, button, nullptr); + QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), button, button, {}); QGuiApplication::sendEvent(&window, &releaseEvent); QCOMPARE(window.rootObject()->property("released").toInt(), 1); QGuiApplication::sendEvent(&window, &pressEvent); - pressEvent = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100), button, button, nullptr); + pressEvent = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100), button, button, {}); QGuiApplication::sendEvent(&window, &pressEvent); QGuiApplication::sendEvent(&window, &releaseEvent); @@ -988,10 +988,10 @@ void tst_QQuickMouseArea::clickTwice() QVERIFY(mouseArea); mouseArea->setAcceptedButtons(acceptedButtons); - QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), button, button, nullptr); + QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), button, button, {}); QGuiApplication::sendEvent(&window, &pressEvent); - QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), button, button, nullptr); + QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), button, button, {}); QGuiApplication::sendEvent(&window, &releaseEvent); QCOMPARE(window.rootObject()->property("pressed").toInt(), 1); @@ -999,7 +999,7 @@ void tst_QQuickMouseArea::clickTwice() QCOMPARE(window.rootObject()->property("clicked").toInt(), 1); QGuiApplication::sendEvent(&window, &pressEvent); - pressEvent = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100), button, button, nullptr); + pressEvent = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100), button, button, {}); QGuiApplication::sendEvent(&window, &pressEvent); QGuiApplication::sendEvent(&window, &releaseEvent); @@ -1026,16 +1026,16 @@ void tst_QQuickMouseArea::invalidClick() // The sequence for a double click is: // press, release, (click), press, double click, release - QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), button, button, nullptr); + QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), button, button, {}); QGuiApplication::sendEvent(&window, &pressEvent); - QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), button, button, nullptr); + QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), button, button, {}); QGuiApplication::sendEvent(&window, &releaseEvent); QCOMPARE(window.rootObject()->property("released").toInt(), 0); QGuiApplication::sendEvent(&window, &pressEvent); - pressEvent = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100), button, button, nullptr); + pressEvent = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100), button, button, {}); QGuiApplication::sendEvent(&window, &pressEvent); QGuiApplication::sendEvent(&window, &releaseEvent); @@ -1055,12 +1055,12 @@ void tst_QQuickMouseArea::pressedOrdering() QCOMPARE(window.rootObject()->property("value").toString(), QLatin1String("base")); - QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, nullptr); + QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {}); QGuiApplication::sendEvent(&window, &pressEvent); QCOMPARE(window.rootObject()->property("value").toString(), QLatin1String("pressed")); - QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, nullptr); + QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {}); QGuiApplication::sendEvent(&window, &releaseEvent); QCOMPARE(window.rootObject()->property("value").toString(), QLatin1String("toggled")); @@ -1319,13 +1319,13 @@ void tst_QQuickMouseArea::hoverPropagation() QCOMPARE(root->property("point1").toBool(), false); QCOMPARE(root->property("point2").toBool(), false); - QMouseEvent moveEvent(QEvent::MouseMove, QPoint(32, 32), Qt::NoButton, Qt::NoButton, nullptr); + QMouseEvent moveEvent(QEvent::MouseMove, QPoint(32, 32), Qt::NoButton, Qt::NoButton, {}); QGuiApplication::sendEvent(&window, &moveEvent); QCOMPARE(root->property("point1").toBool(), true); QCOMPARE(root->property("point2").toBool(), false); - QMouseEvent moveEvent2(QEvent::MouseMove, QPoint(232, 32), Qt::NoButton, Qt::NoButton, nullptr); + QMouseEvent moveEvent2(QEvent::MouseMove, QPoint(232, 32), Qt::NoButton, Qt::NoButton, {}); QGuiApplication::sendEvent(&window, &moveEvent2); QCOMPARE(root->property("point1").toBool(), false); QCOMPARE(root->property("point2").toBool(), true); @@ -1887,7 +1887,7 @@ void tst_QQuickMouseArea::nestedStopAtBounds() QTest::mouseMove(&view, position); axis += invert ? threshold : -threshold; QTest::mouseMove(&view, position); - QCOMPARE(outer->drag()->active(), true); + QTRY_COMPARE(outer->drag()->active(), true); QCOMPARE(inner->drag()->active(), false); QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position); diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp index e96b892b54..c18a220996 100644 --- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp +++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp @@ -31,12 +31,15 @@ #include <private/qquickmultipointtoucharea_p.h> #include <private/qquickflickable_p.h> #include <private/qquickmousearea_p.h> +#include <private/qquickwindow_p.h> #include <qpa/qwindowsysteminterface.h> #include <QtQuick/qquickview.h> #include <QtGui/QScreen> #include "../../shared/util.h" #include "../shared/viewtestutil.h" +Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests") + class tst_QQuickMultiPointTouchArea : public QQmlDataTest { Q_OBJECT @@ -62,6 +65,7 @@ private slots: void nested(); void inFlickable(); void inFlickable2(); + void inFlickableWithPressDelay(); void inMouseArea(); void mouseAsTouchpoint(); void invisible(); @@ -812,6 +816,68 @@ void tst_QQuickMultiPointTouchArea::inFlickable2() QTRY_VERIFY(!flickable->isMoving()); } +void tst_QQuickMultiPointTouchArea::inFlickableWithPressDelay() // QTBUG-78818 +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> window(createAndShowView("inFlickable.qml")); + QVERIFY(window->rootObject() != nullptr); + QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window.data()); + + QQuickFlickable *flickable = qobject_cast<QQuickFlickable *>(window->rootObject()); + QVERIFY(flickable != nullptr); + flickable->setPressDelay(50); + + QQuickMultiPointTouchArea *mpta = window->rootObject()->findChild<QQuickMultiPointTouchArea*>(); + QVERIFY(mpta != nullptr); + mpta->setMinimumTouchPoints(1); + QQuickTouchPoint *point11 = window->rootObject()->findChild<QQuickTouchPoint*>("point1"); + QPoint p1(20,100); + + // press: Flickable prevents delivery of TouchBegin, but sends mouse press instead, after the delay. + // MPTA handles the mouse press, and its first declared touchpoint is pressed. + QTest::touchEvent(window.data(), device).press(0, p1); + QQuickTouchUtils::flush(window.data()); + QTRY_COMPARE(point11->pressed(), true); + auto pointerEvent = windowPriv->pointerEventInstance(QQuickPointerDevice::touchDevices().at(0)); + QCOMPARE(pointerEvent->point(0)->exclusiveGrabber(), mpta); + + // release: MPTA receives TouchEnd (which is asymmetric with mouse press); does NOT emit canceled. + QTest::touchEvent(window.data(), device).release(0, p1); + QQuickTouchUtils::flush(window.data()); + QCOMPARE(flickable->property("cancelCount").toInt(), 0); + + // press again + QTest::touchEvent(window.data(), device).press(0, p1); + QQuickTouchUtils::flush(window.data()); + QTRY_COMPARE(point11->pressed(), true); // wait until pressDelay exceeded + QCOMPARE(pointerEvent->point(0)->exclusiveGrabber(), mpta); + + // drag past the threshold: Flickable takes over the grab, MPTA gets touchUngrab and is no longer pressed + int i = 0; + for (; i < 10 && window->mouseGrabberItem() != flickable; ++i) { + p1 += QPoint(0,dragThreshold); + QTest::touchEvent(window.data(), device).move(0, p1); + QQuickTouchUtils::flush(window.data()); + } + QCOMPARE(window->mouseGrabberItem(), flickable); + qCDebug(lcTests, "Flickable stole grab from MPTA after %d moves", i); + QCOMPARE(pointerEvent->point(0)->exclusiveGrabber(), flickable); + QCOMPARE(point11->pressed(), false); + QVERIFY(flickable->property("cancelCount").toInt() > 0); // actually 2 because 2 touchPoints are declared... but only one was really cancelled + + // drag a little more and the Flickable moves + p1 += QPoint(0,1); + QTest::touchEvent(window.data(), device).move(0, p1); + QQuickTouchUtils::flush(window.data()); + QVERIFY(flickable->contentY() < 0); + QVERIFY(flickable->isMoving()); + + QTest::touchEvent(window.data(), device).release(0, p1); + QQuickTouchUtils::flush(window.data()); + + QTRY_VERIFY(!flickable->isMoving()); +} + // QTBUG-31047 void tst_QQuickMultiPointTouchArea::inMouseArea() { diff --git a/tests/auto/quick/qquickpathview/data/ungrabNestedinFlickable.qml b/tests/auto/quick/qquickpathview/data/ungrabNestedinFlickable.qml new file mode 100644 index 0000000000..4258d8fef4 --- /dev/null +++ b/tests/auto/quick/qquickpathview/data/ungrabNestedinFlickable.qml @@ -0,0 +1,75 @@ +import QtQuick 2.9 + +Flickable { + width: 480 + height: 480 + contentX: 0 + contentWidth: width + contentHeight: height + leftMargin: 408 + rightMargin: 36 + maximumFlickVelocity: 0 + boundsBehavior: Flickable.StopAtBounds + flickableDirection: Flickable.HorizontalFlick + + PathView { + id:pathView + objectName: "pathView" + + property int countclick: 0 + + readonly property int contentsWidth: 348 + readonly property int contentsHeight: 480 + + width: contentsWidth + height: contentsHeight + + interactive: true + + cacheItemCount: 10 + currentIndex: 2 + pathItemCount: 4 + highlightMoveDuration: 300 + highlightRangeMode : PathView.StrictlyEnforceRange + preferredHighlightBegin: 0.5 + preferredHighlightEnd: 0.5 + snapMode : PathView.SnapOneItem + + path: Path { + startX: pathView.contentsWidth / 2 - 800 + startY: pathView.contentsHeight / 2 - 800 + + PathArc { + x: pathView.contentsWidth / 2 - 800 + y: pathView.contentsHeight / 2 + 800 + radiusX: 800 + radiusY: 800 + direction: PathArc.Clockwise + } + } + + model: ListModel { + ListElement { objectName:"aqua"; name: "aqua" ;mycolor:"aqua"} + ListElement { objectName:"blue"; name: "blue" ;mycolor:"blue"} + ListElement { objectName:"blueviolet"; name: "blueviolet" ;mycolor:"blueviolet"} + ListElement { objectName:"brown"; name: "brown" ;mycolor:"brown"} + ListElement { objectName:"chartreuse"; name: "chartreuse" ;mycolor:"chartreuse"} + } + + delegate: Item { + id: revolveritem + objectName: model.objectName + + width: pathView.contentsWidth + height: pathView.contentsHeight + + Rectangle + { + id:myRectangle + color: mycolor + width: pathView.contentsWidth -20 + height: pathView.contentsHeight -20 + } + } + } +} diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp index a8e847a5c7..8ba09fa509 100644 --- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp +++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp @@ -140,6 +140,7 @@ private slots: void cacheItemCount(); void changePathDuringRefill(); void nestedinFlickable(); + void ungrabNestedinFlickable(); void flickableDelegate(); void jsArrayChange(); void qtbug37815(); @@ -2413,6 +2414,40 @@ void tst_QQuickPathView::nestedinFlickable() } +void tst_QQuickPathView::ungrabNestedinFlickable() +{ + QScopedPointer<QQuickView> window(createView()); + QQuickViewTestUtil::moveMouseAway(window.data()); + window->setSource(testFileUrl("ungrabNestedinFlickable.qml")); + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QCOMPARE(window.data(), qGuiApp->focusWindow()); + + QQuickPathView *pathview = findItem<QQuickPathView>(window->rootObject(), "pathView"); + QVERIFY(pathview != nullptr); + + double pathviewOffsetBefore = pathview->offset(); + + // Drag slowly upwards so that it does not flick, release, and let it start snapping back + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(200, 350)); + for (int i = 0; i < 4; ++i) + QTest::mouseMove(window.data(), QPoint(200, 325 - i * 25), 500); + QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(200, 250)); + QCOMPARE(pathview->isMoving(), true); + + // Press again to stop moving + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(200, 350)); + QTRY_COMPARE(pathview->isMoving(), false); + + // Cancel the grab, wait for movement to stop, and expect it to snap to + // the nearest delegate, which should be at the same offset as where we started + pathview->ungrabMouse(); + QTRY_COMPARE(pathview->offset(), pathviewOffsetBefore); + QCOMPARE(pathview->isMoving(), false); + QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(200, 350)); +} + void tst_QQuickPathView::flickableDelegate() { QScopedPointer<QQuickView> window(createView()); diff --git a/tests/auto/quick/qquickpixmapcache/data/asynchronousNoCache.qml b/tests/auto/quick/qquickpixmapcache/data/asynchronousNoCache.qml new file mode 100644 index 0000000000..5331be5a15 --- /dev/null +++ b/tests/auto/quick/qquickpixmapcache/data/asynchronousNoCache.qml @@ -0,0 +1,17 @@ +import QtQuick 2.12 + +Item { + visible: true + width: 640 + height: 480 + + Image{ + asynchronous: true + anchors.fill: parent + fillMode: Image.Stretch + source: "exists1.png" + cache: false + sourceSize.width: width/2 + sourceSize.height: height/2 + } +} diff --git a/tests/auto/quick/qquickpixmapcache/tst_qquickpixmapcache.cpp b/tests/auto/quick/qquickpixmapcache/tst_qquickpixmapcache.cpp index bffaaf7c6e..88cf6ece96 100644 --- a/tests/auto/quick/qquickpixmapcache/tst_qquickpixmapcache.cpp +++ b/tests/auto/quick/qquickpixmapcache/tst_qquickpixmapcache.cpp @@ -30,6 +30,7 @@ #include <QtQuick/private/qquickpixmapcache_p.h> #include <QtQml/qqmlengine.h> #include <QtQuick/qquickimageprovider.h> +#include <QtQml/QQmlComponent> #include <QNetworkReply> #include "../../shared/util.h" #include "testhttpserver.h" @@ -62,6 +63,7 @@ private slots: #endif void lockingCrash(); void uncached(); + void asynchronousNoCache(); #if PIXMAP_DATA_LEAK_TEST void dataLeak(); #endif @@ -431,7 +433,7 @@ void tst_qquickpixmapcache::uncached() QUrl url("image://mypixmaps/mypix"); { QQuickPixmap p; - p.load(&engine, url, nullptr); + p.load(&engine, url, QQuickPixmap::Options{}); QImage img = p.image(); QCOMPARE(img.pixel(0,0), qRgb(255, 0, 0)); } @@ -440,7 +442,7 @@ void tst_qquickpixmapcache::uncached() MyPixmapProvider::fillColor = qRgb(0, 255, 0); { QQuickPixmap p; - p.load(&engine, url, nullptr); + p.load(&engine, url, QQuickPixmap::Options{}); QImage img = p.image(); QCOMPARE(img.pixel(0,0), qRgb(0, 255, 0)); } @@ -458,7 +460,7 @@ void tst_qquickpixmapcache::uncached() MyPixmapProvider::fillColor = qRgb(255, 0, 255); { QQuickPixmap p; - p.load(&engine, url, nullptr); + p.load(&engine, url, QQuickPixmap::Options{}); QImage img = p.image(); QCOMPARE(img.pixel(0,0), qRgb(255, 0, 255)); } @@ -473,6 +475,13 @@ void tst_qquickpixmapcache::uncached() } } +void tst_qquickpixmapcache::asynchronousNoCache() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("asynchronousNoCache.qml")); + QScopedPointer<QObject> root {component.create()}; // should not crash +} + #if PIXMAP_DATA_LEAK_TEST // This test should not be enabled by default as it diff --git a/tests/auto/quick/qquickrepeater/data/contextProperty.qml b/tests/auto/quick/qquickrepeater/data/contextProperty.qml new file mode 100644 index 0000000000..44e76f474f --- /dev/null +++ b/tests/auto/quick/qquickrepeater/data/contextProperty.qml @@ -0,0 +1,13 @@ +import QtQuick 2.14 + +Item { + Column { + Repeater { + model: ["apples", "oranges", "pears"] + Text { + id: txt + text: modelData + index + } + } + } +} diff --git a/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp b/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp index ccfef63902..33b8742170 100644 --- a/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp +++ b/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp @@ -81,6 +81,7 @@ private slots: void package(); void ownership(); void requiredProperties(); + void contextProperties(); }; class TestObject : public QObject @@ -1131,6 +1132,29 @@ void tst_QQuickRepeater::requiredProperties() QVERIFY(o); } +void tst_QQuickRepeater::contextProperties() +{ + QQmlEngine engine; + + QQmlComponent component(&engine, testFileUrl("contextProperty.qml")); + QScopedPointer<QObject> o {component.create()}; + QVERIFY(o); + + auto *root = qobject_cast<QQuickItem *>(o.get()); + QVERIFY(root); + + QQueue<QQuickItem *> items; + items.append(root); + + while (!items.isEmpty()) { + QQuickItem *item = items.dequeue(); + QQmlContextData *data = QQmlContextData::get(qmlContext(item)); + QVERIFY(!data->hasExtraObject); + for (QQuickItem *child : item->childItems()) + items.enqueue(child); + } +} + QTEST_MAIN(tst_QQuickRepeater) #include "tst_qquickrepeater.moc" diff --git a/tests/auto/quick/qquicktableview/data/checkalwaysemit.qml b/tests/auto/quick/qquicktableview/data/checkalwaysemit.qml new file mode 100644 index 0000000000..33db6f6d02 --- /dev/null +++ b/tests/auto/quick/qquicktableview/data/checkalwaysemit.qml @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Window 2.3 + +Item { + width: 640 + height: 450 + + property alias tableView: tableView + property real delegateWidth: 100 + property real delegateHeight: 50 + property Component delegate: tableViewDelegate + property bool delegateParentSetBeforeCompleted: false + + TableView { + id: tableView + width: 600 + height: 400 + anchors.margins: 1 + clip: true + delegate: tableViewDelegate + columnSpacing: 1 + rowSpacing: 1 + } + + Component { + id: tableViewDelegate + Rectangle { + objectName: "tableViewDelegate" + implicitWidth: delegateWidth + implicitHeight: delegateHeight + color: "lightgray" + border.width: 1 + + property string modelDataFromIndex: tableView.model.dataFromSerializedIndex(index) + property string modelDataBinding: modelData + + Text { + anchors.centerIn: parent + text: modelData + } + + Component.onCompleted: { + delegateParentSetBeforeCompleted = parent != null; + } + } + } + +} diff --git a/tests/auto/quick/qquicktableview/data/replaceModelTableView.qml b/tests/auto/quick/qquicktableview/data/replaceModelTableView.qml new file mode 100644 index 0000000000..cc109bb469 --- /dev/null +++ b/tests/auto/quick/qquicktableview/data/replaceModelTableView.qml @@ -0,0 +1,56 @@ +import QtQuick 2.14 +import QtQml.Models 2.14 + +Item { + id: root + visible: true + width: 640 + height: 480 + + property alias tableView: tv + property alias objectModel: om + property alias listModel: lm + property alias delegateModel: dm + + ObjectModel { + id: om + Rectangle { implicitHeight: 30; implicitWidth: 80; color: "red" } + Rectangle { implicitHeight: 30; implicitWidth: 80; color: "green" } + Rectangle { implicitHeight: 30; implicitWidth: 80; color: "blue" } + } + + ListModel { + id: lm + ListElement { name: "1" } + ListElement { name: "44"} + } + + DelegateModel { + id: dm + model: ListModel { + ListElement { name: "Apple" } + ListElement { name: "Orange" } + } + delegate: Rectangle { + implicitHeight: 25 + implicitWidth: 100 + Text { text: "Name: " + name} + } + } + TableView { + id: tv + visible: true + anchors.fill: parent + + delegate: Rectangle { + id: dlg + implicitWidth: 40 + implicitHeight: 20 + color: "red" + Text { + text: qsTr("name: " + name) + } + border.color: "green" + } + } +} diff --git a/tests/auto/quick/qquicktableview/qquicktableview.pro b/tests/auto/quick/qquicktableview/qquicktableview.pro index da0c0b01d0..735c728fc6 100644 --- a/tests/auto/quick/qquicktableview/qquicktableview.pro +++ b/tests/auto/quick/qquicktableview/qquicktableview.pro @@ -13,3 +13,5 @@ TESTDATA = data/* QT += core-private gui-private qml-private quick-private testlib qmlmodels-private +DISTFILES += + diff --git a/tests/auto/quick/qquicktableview/testmodel.h b/tests/auto/quick/qquicktableview/testmodel.h index 50f434019e..2697b1e801 100644 --- a/tests/auto/quick/qquicktableview/testmodel.h +++ b/tests/auto/quick/qquicktableview/testmodel.h @@ -46,6 +46,13 @@ public: , m_columns(columns) {} + TestModel(int rows, int columns, bool dataCanBeFetched, QObject *parent = nullptr) + : QAbstractTableModel(parent) + , m_rows(rows) + , m_columns(columns) + , m_dataCanBeFetched(dataCanBeFetched) + {} + int rowCount(const QModelIndex & = QModelIndex()) const override { return m_rows; } void setRowCount(int count) { beginResetModel(); m_rows = count; emit rowCountChanged(); endResetModel(); } @@ -63,6 +70,13 @@ public: return QStringLiteral("%1").arg(index.row()); } + Q_INVOKABLE QVariant dataFromSerializedIndex(int index) const + { + if (modelData.contains(index)) + return modelData.value(index); + return QString(); + } + QHash<int, QByteArray> roleNames() const override { return { {Qt::DisplayRole, "display"} }; @@ -102,6 +116,12 @@ public: beginRemoveRows(parent, row, row + count - 1); m_rows -= count; + for (int c = 0; c < m_columns; ++c) { + for (int r = 0; r < count; ++r) { + const int serializedIndex = (row + r) + (c * m_rows); + modelData.remove(serializedIndex); + } + } endRemoveRows(); return true; } @@ -128,6 +148,12 @@ public: return true; } + bool canFetchMore(const QModelIndex &parent) const override + { + Q_UNUSED(parent) + return m_dataCanBeFetched; + } + void swapRows(int row1, int row2) { layoutAboutToBeChanged(); @@ -139,6 +165,12 @@ public: layoutChanged(); } + void fetchMore(const QModelIndex &parent) override + { + Q_UNUSED(parent) + addRow(m_rows - 1); + } + void clear() { beginResetModel(); m_rows = 0; @@ -159,6 +191,7 @@ signals: private: int m_rows = 0; int m_columns = 0; + bool m_dataCanBeFetched = false; QHash<int, QString> modelData; }; diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp index 230dcc9446..ef39a91a65 100644 --- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp +++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp @@ -60,7 +60,7 @@ Q_DECLARE_METATYPE(QMarginsF); auto PROPNAME = view->rootObject()->property(#PROPNAME).value<QQuickTableView *>(); \ QVERIFY(PROPNAME); \ auto PROPNAME ## Private = QQuickTableViewPrivate::get(PROPNAME); \ - Q_UNUSED(PROPNAME ## Private) + Q_UNUSED(PROPNAME ## Private) void() #define LOAD_TABLEVIEW(fileName) \ view->setSource(testFileUrl(fileName)); \ @@ -120,6 +120,7 @@ private slots: void checkRowHeightProviderNotCallable(); void checkForceLayoutFunction(); void checkForceLayoutEndUpDoingALayout(); + void checkForceLayoutDuringModelChange(); void checkContentWidthAndHeight(); void checkPageFlicking(); void checkExplicitContentWidthAndHeight(); @@ -159,6 +160,7 @@ private slots: void checkContextPropertiesQQmlListProperyModel_data(); void checkContextPropertiesQQmlListProperyModel(); void checkRowAndColumnChangedButNotIndex(); + void checkThatWeAlwaysEmitChangedUponItemReused(); void checkChangingModelFromDelegate(); void checkRebuildViewportOnly(); void useDelegateChooserWithoutDefault(); @@ -174,6 +176,8 @@ private slots: void checkSyncView_connect_late_data(); void checkSyncView_connect_late(); void delegateWithRequiredProperties(); + void checkThatFetchMoreIsCalledWhenScrolledToTheEndOfTable(); + void replaceModel(); }; tst_QQuickTableView::tst_QQuickTableView() @@ -390,7 +394,7 @@ void tst_QQuickTableView::checkColumnWidthProviderInvalidReturnValues() tableView->setModel(model); - QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*implicitHeight.*zero")); + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*implicit.*zero")); WAIT_UNTIL_POLISHED; @@ -487,7 +491,7 @@ void tst_QQuickTableView::checkRowHeightProviderInvalidReturnValues() tableView->setModel(model); - QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*implicitHeight.*zero")); + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*implicit.*zero")); WAIT_UNTIL_POLISHED; @@ -588,6 +592,29 @@ void tst_QQuickTableView::checkForceLayoutEndUpDoingALayout() QCOMPARE(fxItem->item->height(), newDelegateSize); } +void tst_QQuickTableView::checkForceLayoutDuringModelChange() +{ + // Check that TableView doesn't assert if we call + // forceLayout() in the middle of a model change. + LOAD_TABLEVIEW("plaintableview.qml"); + + const int initialRowCount = 10; + TestModel model(initialRowCount, 10); + tableView->setModel(QVariant::fromValue(&model)); + + connect(&model, &QAbstractItemModel::rowsInserted, [=](){ + QCOMPARE(tableView->rows(), initialRowCount); + tableView->forceLayout(); + QCOMPARE(tableView->rows(), initialRowCount + 1); + }); + + WAIT_UNTIL_POLISHED; + + QCOMPARE(tableView->rows(), initialRowCount); + model.addRow(0); + QCOMPARE(tableView->rows(), initialRowCount + 1); +} + void tst_QQuickTableView::checkContentWidthAndHeight() { // Check that contentWidth/Height reports the correct size of the @@ -2060,6 +2087,41 @@ void tst_QQuickTableView::checkRowAndColumnChangedButNotIndex() QCOMPARE(contextColumn, 1); } +void tst_QQuickTableView::checkThatWeAlwaysEmitChangedUponItemReused() +{ + // Check that we always emit changes to index when we reuse an item, even + // if it doesn't change. This is needed since the model can have changed + // row or column count while the item was in the pool, which means that + // any data referred to by the index property inside the delegate + // will change too. So we need to refresh any bindings to index. + // QTBUG-79209 + LOAD_TABLEVIEW("checkalwaysemit.qml"); + + TestModel model(1, 1); + tableView->setModel(QVariant::fromValue(&model)); + model.setModelData(QPoint(0, 0), QSize(1, 1), "old value"); + + WAIT_UNTIL_POLISHED; + + const auto reuseItem = tableViewPrivate->loadedTableItem(QPoint(0, 0))->item; + const auto context = qmlContext(reuseItem.data()); + + // Remove the cell/row that has "old value" as model data, and + // add a new one right after. The new cell will have the same + // index, but with no model data assigned. + // This change will not be detected by items in the pool. But since + // we emit indexChanged when the item is reused, it will be updated then. + model.removeRow(0); + model.insertRow(0); + + WAIT_UNTIL_POLISHED; + + QCOMPARE(context->contextProperty("index").toInt(), 0); + QCOMPARE(context->contextProperty("row").toInt(), 0); + QCOMPARE(context->contextProperty("column").toInt(), 0); + QCOMPARE(context->contextProperty("modelDataFromIndex").toString(), ""); +} + void tst_QQuickTableView::checkChangingModelFromDelegate() { // Check that we don't restart a rebuild of the table @@ -2617,7 +2679,27 @@ void tst_QQuickTableView::checkSyncView_connect_late() QCOMPARE(tableViewVPrivate->loadedTableOuterRect.left(), 0); QCOMPARE(tableViewHVPrivate->loadedTableOuterRect, tableViewPrivate->loadedTableOuterRect); +} + +void tst_QQuickTableView::checkThatFetchMoreIsCalledWhenScrolledToTheEndOfTable() +{ + LOAD_TABLEVIEW("plaintableview.qml"); + + auto model = TestModelAsVariant(5, 5, true); + tableView->setModel(model); + WAIT_UNTIL_POLISHED; + + QCOMPARE(tableView->rows(), 5); + QCOMPARE(tableView->columns(), 5); + // Flick table out of view on top + tableView->setContentX(0); + tableView->setContentY(-tableView->height() - 10); + tableView->polish(); + WAIT_UNTIL_POLISHED; + + QCOMPARE(tableView->rows(), 6); + QCOMPARE(tableView->columns(), 5); } void tst_QQuickTableView::delegateWithRequiredProperties() @@ -2648,7 +2730,7 @@ void tst_QQuickTableView::delegateWithRequiredProperties() auto model = QVariant::fromValue(QSharedPointer<MyTable>(new MyTable)); { QTest::ignoreMessage(QtMsgType::QtInfoMsg, "success"); - LOAD_TABLEVIEW("delegateWithRequired.qml") + LOAD_TABLEVIEW("delegateWithRequired.qml"); QVERIFY(tableView); tableView->setModel(model); WAIT_UNTIL_POLISHED; @@ -2656,7 +2738,7 @@ void tst_QQuickTableView::delegateWithRequiredProperties() } { QTest::ignoreMessage(QtMsgType::QtWarningMsg, QRegularExpression(R"|(TableView: failed loading index: \d)|")); - LOAD_TABLEVIEW("delegatewithRequiredUnset.qml") + LOAD_TABLEVIEW("delegatewithRequiredUnset.qml"); QVERIFY(tableView); tableView->setModel(model); WAIT_UNTIL_POLISHED; @@ -2664,6 +2746,28 @@ void tst_QQuickTableView::delegateWithRequiredProperties() } } +void tst_QQuickTableView::replaceModel() +{ + LOAD_TABLEVIEW("replaceModelTableView.qml"); + + const auto objectModel = view->rootObject()->property("objectModel"); + const auto listModel = view->rootObject()->property("listModel"); + const auto delegateModel = view->rootObject()->property("delegateModel"); + + tableView->setModel(listModel); + QTRY_COMPARE(tableView->rows(), 2); + tableView->setModel(objectModel); + QTRY_COMPARE(tableView->rows(), 3); + tableView->setModel(delegateModel); + QTRY_COMPARE(tableView->rows(), 2); + tableView->setModel(listModel); + QTRY_COMPARE(tableView->rows(), 2); + tableView->setModel(QVariant()); + QTRY_COMPARE(tableView->rows(), 0); + QCOMPARE(tableView->contentWidth(), 0); + QCOMPARE(tableView->contentHeight(), 0); +} + QTEST_MAIN(tst_QQuickTableView) #include "tst_qquicktableview.moc" diff --git a/tests/auto/quick/qquicktextedit/BLACKLIST b/tests/auto/quick/qquicktextedit/BLACKLIST index 9df9c7d75a..36c7f0042f 100644 --- a/tests/auto/quick/qquicktextedit/BLACKLIST +++ b/tests/auto/quick/qquicktextedit/BLACKLIST @@ -1,2 +1,6 @@ [mouseSelection] opensuse-leap + +# QTBUG-78846 +[mouseSelectionMode] +opensuse-leap diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp index d03441e052..85342d4c1e 100644 --- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp +++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp @@ -226,7 +226,7 @@ private: void simulateKeys(QWindow *window, const QKeySequence &sequence); #endif - void simulateKey(QWindow *, int key, Qt::KeyboardModifiers modifiers = nullptr); + void simulateKey(QWindow *, int key, Qt::KeyboardModifiers modifiers = {}); QStringList standard; QStringList richText; @@ -1389,7 +1389,7 @@ void tst_qquicktextedit::focusOnPress() QCOMPARE(textEditObject->hasActiveFocus(), false); QPoint centerPoint(window.width()/2, window.height()/2); - Qt::KeyboardModifiers noModifiers = nullptr; + Qt::KeyboardModifiers noModifiers; QTest::mousePress(&window, Qt::LeftButton, noModifiers, centerPoint); QGuiApplication::processEvents(); QCOMPARE(textEditObject->hasFocus(), true); diff --git a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp index 1d12121a6f..2e64c80b85 100644 --- a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp +++ b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp @@ -2073,11 +2073,15 @@ void tst_qquicktextinput::validators() QTest::keyPress(&window, Qt::Key_Comma); QTest::keyRelease(&window, Qt::Key_Comma, Qt::NoModifier); QTRY_COMPARE(dblInput->text(), QLatin1String("12,")); - QCOMPARE(dblInput->hasAcceptableInput(), true); - QTest::keyPress(&window, Qt::Key_1); - QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier); - QTRY_COMPARE(dblInput->text(), QLatin1String("12,")); - QCOMPARE(dblInput->hasAcceptableInput(), true); + int extraSignals = 2; + if (dblInput->hasAcceptableInput()) { + // TODO: old behavior of QDoubleValidator - remove when merged from qtbase + QTest::keyPress(&window, Qt::Key_1); + QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier); + QTRY_COMPARE(dblInput->text(), QLatin1String("12,")); + QCOMPARE(dblInput->hasAcceptableInput(), true); + extraSignals = 0; + } dblValidator->setLocaleName(deLocale.name()); QCOMPARE(dblInput->hasAcceptableInput(), true); QTest::keyPress(&window, Qt::Key_1); @@ -2106,84 +2110,84 @@ void tst_qquicktextinput::validators() QTRY_COMPARE(dblInput->text(), QLatin1String("12.")); QCOMPARE(dblInput->hasAcceptableInput(), true); QCOMPARE(dblInput->property("acceptable").toBool(), true); - QCOMPARE(dblSpy.count(), 1); + QCOMPARE(dblSpy.count(), 1 + extraSignals); QTest::keyPress(&window, Qt::Key_1); QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier); QTRY_COMPARE(dblInput->text(), QLatin1String("12.1")); QCOMPARE(dblInput->hasAcceptableInput(), true); QCOMPARE(dblInput->property("acceptable").toBool(), true); - QCOMPARE(dblSpy.count(), 1); + QCOMPARE(dblSpy.count(), 1 + extraSignals); QTest::keyPress(&window, Qt::Key_1); QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier); QTRY_COMPARE(dblInput->text(), QLatin1String("12.11")); QCOMPARE(dblInput->hasAcceptableInput(), true); QCOMPARE(dblInput->property("acceptable").toBool(), true); - QCOMPARE(dblSpy.count(), 1); + QCOMPARE(dblSpy.count(), 1 + extraSignals); QTest::keyPress(&window, Qt::Key_1); QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier); QTRY_COMPARE(dblInput->text(), QLatin1String("12.11")); QCOMPARE(dblInput->hasAcceptableInput(), true); QCOMPARE(dblInput->property("acceptable").toBool(), true); - QCOMPARE(dblSpy.count(), 1); + QCOMPARE(dblSpy.count(), 1 + extraSignals); // Ensure the validator doesn't prevent characters being removed. dblInput->setValidator(intInput->validator()); QCOMPARE(dblInput->text(), QLatin1String("12.11")); QCOMPARE(dblInput->hasAcceptableInput(), false); QCOMPARE(dblInput->property("acceptable").toBool(), false); - QCOMPARE(dblSpy.count(), 2); + QCOMPARE(dblSpy.count(), 2 + extraSignals); QTest::keyPress(&window, Qt::Key_Backspace); QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier); QTRY_COMPARE(dblInput->text(), QLatin1String("12.1")); QCOMPARE(dblInput->hasAcceptableInput(), false); QCOMPARE(dblInput->property("acceptable").toBool(), false); - QCOMPARE(dblSpy.count(), 2); + QCOMPARE(dblSpy.count(), 2 + extraSignals); // Once unacceptable input is in anything goes until it reaches an acceptable state again. QTest::keyPress(&window, Qt::Key_1); QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier); QTRY_COMPARE(dblInput->text(), QLatin1String("12.11")); QCOMPARE(dblInput->hasAcceptableInput(), false); - QCOMPARE(dblSpy.count(), 2); + QCOMPARE(dblSpy.count(), 2 + extraSignals); QTest::keyPress(&window, Qt::Key_Backspace); QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier); QTRY_COMPARE(dblInput->text(), QLatin1String("12.1")); QCOMPARE(dblInput->hasAcceptableInput(), false); QCOMPARE(dblInput->property("acceptable").toBool(), false); - QCOMPARE(dblSpy.count(), 2); + QCOMPARE(dblSpy.count(), 2 + extraSignals); QTest::keyPress(&window, Qt::Key_Backspace); QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier); QTRY_COMPARE(dblInput->text(), QLatin1String("12.")); QCOMPARE(dblInput->hasAcceptableInput(), false); QCOMPARE(dblInput->property("acceptable").toBool(), false); - QCOMPARE(dblSpy.count(), 2); + QCOMPARE(dblSpy.count(), 2 + extraSignals); QTest::keyPress(&window, Qt::Key_Backspace); QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier); QTRY_COMPARE(dblInput->text(), QLatin1String("12")); QCOMPARE(dblInput->hasAcceptableInput(), false); QCOMPARE(dblInput->property("acceptable").toBool(), false); - QCOMPARE(dblSpy.count(), 2); + QCOMPARE(dblSpy.count(), 2 + extraSignals); QTest::keyPress(&window, Qt::Key_Backspace); QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier); QTRY_COMPARE(dblInput->text(), QLatin1String("1")); QCOMPARE(dblInput->hasAcceptableInput(), false); QCOMPARE(dblInput->property("acceptable").toBool(), false); - QCOMPARE(dblSpy.count(), 2); + QCOMPARE(dblSpy.count(), 2 + extraSignals); QTest::keyPress(&window, Qt::Key_1); QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier); QCOMPARE(dblInput->text(), QLatin1String("11")); QCOMPARE(dblInput->property("acceptable").toBool(), true); QCOMPARE(dblInput->hasAcceptableInput(), true); - QCOMPARE(dblSpy.count(), 3); + QCOMPARE(dblSpy.count(), 3 + extraSignals); // Changing the validator properties will re-evaluate whether the input is acceptable. intValidator->setTop(10); QCOMPARE(dblInput->property("acceptable").toBool(), false); QCOMPARE(dblInput->hasAcceptableInput(), false); - QCOMPARE(dblSpy.count(), 4); + QCOMPARE(dblSpy.count(), 4 + extraSignals); intValidator->setTop(12); QCOMPARE(dblInput->property("acceptable").toBool(), true); QCOMPARE(dblInput->hasAcceptableInput(), true); - QCOMPARE(dblSpy.count(), 5); + QCOMPARE(dblSpy.count(), 5 + extraSignals); QQuickTextInput *strInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("strInput"))); QVERIFY(strInput); @@ -6408,8 +6412,20 @@ void tst_qquicktextinput::setInputMask_data() QTest::newRow(QString(insert_mode + "blank=input").toLatin1()) << QString("9999;0") << QString("2004") + << QString("24") << QString("2004") - << QString("2004") + << bool(insert_text); + QTest::newRow(QString(insert_mode + "any_opt").toLatin1()) + << QString("@xxx@") + << QString("@A C@") + << QString("@AC@") + << QString("@A C@") + << bool(insert_text); + QTest::newRow(QString(insert_mode + "any_req").toLatin1()) + << QString("@XXX@") + << QString("@A C@") + << QString("@AC@@") + << QString("@AC@@") << bool(insert_text); } } @@ -6428,6 +6444,14 @@ void tst_qquicktextinput::setInputMask() QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create()); QVERIFY(textInput != nullptr); + // [QTBUG-80190] check if setting the same property value again doesn't emit an + // inputMaskChanged signal + QString unescapedMask = mask; // mask is escaped, because '\' is also escape in a JS string + unescapedMask.replace(QLatin1String("\\\\"), QLatin1String("\\")); // simple unescape + QSignalSpy spy(textInput, SIGNAL(inputMaskChanged(const QString &))); + textInput->setInputMask(unescapedMask); + QCOMPARE(spy.count(), 0); + // then either insert using insert() or keyboard if (insert_text) { textInput->insert(0, input); @@ -6444,9 +6468,6 @@ void tst_qquicktextinput::setInputMask() QTest::keyClick(&window, input.at(i).toLatin1()); } - QEXPECT_FAIL( "keys blank=input", "To eat blanks or not? Known issue. Task 43172", Abort); - QEXPECT_FAIL( "insert blank=input", "To eat blanks or not? Known issue. Task 43172", Abort); - QCOMPARE(textInput->text(), expectedText); QCOMPARE(textInput->displayText(), expectedDisplay); } diff --git a/tests/auto/quick/qquickvisualdatamodel/data/filterGroupForDelegate.qml b/tests/auto/quick/qquickvisualdatamodel/data/filterGroupForDelegate.qml new file mode 100644 index 0000000000..d72ca51d7f --- /dev/null +++ b/tests/auto/quick/qquickvisualdatamodel/data/filterGroupForDelegate.qml @@ -0,0 +1,77 @@ +import QtQml.Models 2.12 +import Qt.labs.qmlmodels 1.0 +import QtQuick 2.12 + +Item { + id: root + width: 200 + height: 320 + + property int numChanges: 0 + property bool ok: true + + DelegateModel { + id: theModel + + model: ListModel { + ListElement { role: "section" } + ListElement { role: "item" } + ListElement { role: "section" } + ListElement { role: "item" } + ListElement { role: "section" } + ListElement { role: "item" } + ListElement { role: "item" } + ListElement { role: "item" } + } + + filterOnGroup: "expanded" + groups: DelegateModelGroup { + name: "expanded" + } + + delegate: DelegateChooser { + role: "role" + + DelegateChoice { + roleValue: "section" + Text { + text: "+ Section " + index + + Timer { + interval: (index + 10) + repeat: true + running: true + onTriggered: { + ++ root.numChanges; + if (model.role !== "section") { + root.ok = false; + console.warn("wrong!", root.numChanges); + } + let i = parent.DelegateModel.itemsIndex + 1; + for (; i < theModel.items.count; ++i) { + let item = theModel.items.get(i); + if (item.model.role === "section") + break; + item.inExpanded = !item.inExpanded; + } + } + } + } + } + + DelegateChoice { + roleValue: "item" + Text { + text: "Item " + index + } + } + } + + Component.onCompleted: items.addGroups(0, items.count, ["expanded"]) + } + + ListView { + anchors.fill: parent + model: theModel + } +} diff --git a/tests/auto/quick/qquickvisualdatamodel/data/readFromProxyObject.qml b/tests/auto/quick/qquickvisualdatamodel/data/readFromProxyObject.qml new file mode 100644 index 0000000000..3983c707a1 --- /dev/null +++ b/tests/auto/quick/qquickvisualdatamodel/data/readFromProxyObject.qml @@ -0,0 +1,23 @@ +import QtQuick 2.15 +import QtQuick.Window 2.15 + +Window { + id: window + width: 200 + height: 200 + color: "red" + visible: true + Repeater { + model: Qt.application.screens + Text { + required property string name + required property int virtualX + required property int virtualY + + text: name + virtualX + ", " + virtualY + Component.onCompleted: window.name = name + } + } + + property string name: "wrong" +} diff --git a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp index fe56cad018..66069c48cb 100644 --- a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp +++ b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp @@ -433,6 +433,8 @@ private slots: void invalidContext(); void externalManagedModel(); void delegateModelChangeDelegate(); + void checkFilterGroupForDelegate(); + void readFromProxyObject(); private: template <int N> void groups_verify( @@ -4342,6 +4344,34 @@ void tst_qquickvisualdatamodel::delegateModelChangeDelegate() QCOMPARE(visualModel->count(), 3); } +void tst_qquickvisualdatamodel::checkFilterGroupForDelegate() +{ + QQuickView view; + view.setSource(testFileUrl("filterGroupForDelegate.qml")); + view.show(); + + QQuickItem *obj = view.rootObject(); + QVERIFY(obj); + + QTRY_VERIFY(obj->property("numChanges").toInt() > 100); + QVERIFY(obj->property("ok").toBool()); +} + +void tst_qquickvisualdatamodel::readFromProxyObject() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("readFromProxyObject.qml")); + + QScopedPointer<QObject> obj(component.create()); + QVERIFY(obj); + + auto *window = qobject_cast<QQuickWindow *>(obj.get()); + QVERIFY(window); + + QCOMPARE(window->property("name").type(), QMetaType::QString); + QTRY_VERIFY(window->property("name").toString() != QLatin1String("wrong")); +} + QTEST_MAIN(tst_qquickvisualdatamodel) #include "tst_qquickvisualdatamodel.moc" diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index 7faa621e86..5b6b11c746 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -73,7 +73,7 @@ static QTouchEvent::TouchPoint makeTouchPoint(QQuickItem *item, const QPointF &p return tp; } -static TouchEventData makeTouchData(QEvent::Type type, QWindow *w, Qt::TouchPointStates states = nullptr, +static TouchEventData makeTouchData(QEvent::Type type, QWindow *w, Qt::TouchPointStates states = {}, const QList<QTouchEvent::TouchPoint>& touchPoints = QList<QTouchEvent::TouchPoint>()) { TouchEventData d = { type, nullptr, w, states, touchPoints }; @@ -157,7 +157,7 @@ public: setEnabled(true); setVisible(true); - lastEvent = makeTouchData(QEvent::None, window(), nullptr, QList<QTouchEvent::TouchPoint>());//CHECK_VALID + lastEvent = makeTouchData(QEvent::None, window(), {}, QList<QTouchEvent::TouchPoint>());//CHECK_VALID lastVelocity = lastVelocityFromMouseMove = QVector2D(); lastMousePos = QPointF(); @@ -2082,7 +2082,7 @@ void tst_qquickwindow::requestActivate() QString warning = QString::fromLatin1("Mouse event MousePress not accepted by receiving window"); QWARN(warning.toLatin1().data()); } - me = QMouseEvent(QEvent::MouseButtonPress, pos, window1->mapToGlobal(pos), Qt::LeftButton, nullptr, Qt::NoModifier); + me = QMouseEvent(QEvent::MouseButtonPress, pos, window1->mapToGlobal(pos), Qt::LeftButton, {}, Qt::NoModifier); QSpontaneKeyEvent::setSpontaneous(&me); if (!qApp->notify(window1.data(), &me)) { QString warning = QString::fromLatin1("Mouse event MouseRelease not accepted by receiving window"); diff --git a/tests/auto/quick/shared/viewtestutil.cpp b/tests/auto/quick/shared/viewtestutil.cpp index 1680e850f7..4089e5ddae 100644 --- a/tests/auto/quick/shared/viewtestutil.cpp +++ b/tests/auto/quick/shared/viewtestutil.cpp @@ -73,13 +73,13 @@ void QQuickViewTestUtil::moveMouseAway(QQuickView *window) void QQuickViewTestUtil::moveAndRelease(QQuickView *window, const QPoint &position) { QTest::mouseMove(window, position); - QTest::mouseRelease(window, Qt::LeftButton, 0, position); + QTest::mouseRelease(window, Qt::LeftButton, {}, position); } void QQuickViewTestUtil::moveAndPress(QQuickView *window, const QPoint &position) { QTest::mouseMove(window, position); - QTest::mousePress(window, Qt::LeftButton, 0, position); + QTest::mousePress(window, Qt::LeftButton, {}, position); } void QQuickViewTestUtil::flick(QQuickView *window, const QPoint &from, const QPoint &to, int duration) |