diff options
Diffstat (limited to 'tests/auto/quick/qquickflickable/tst_qquickflickable.cpp')
-rw-r--r-- | tests/auto/quick/qquickflickable/tst_qquickflickable.cpp | 391 |
1 files changed, 350 insertions, 41 deletions
diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index ab21415235..b003511356 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> #include <QtTest/QSignalSpy> @@ -8,6 +8,7 @@ #include <QtGui/QStyleHints> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlcomponent.h> +#include <private/qguiapplication_p.h> #include <private/qquickflickable_p.h> #include <private/qquickflickable_p_p.h> #include <private/qquickmousearea_p.h> @@ -15,6 +16,7 @@ #include <private/qqmlvaluetype_p.h> #include <private/qquicktaphandler_p.h> #include <math.h> +#include <QtGui/qpa/qplatformintegration.h> #include <QtQuickTestUtils/private/qmlutils_p.h> #include <QtQuickTestUtils/private/geometrytestutils_p.h> #include <QtQuickTestUtils/private/viewtestutils_p.h> @@ -165,6 +167,7 @@ private slots: void rebound(); void maximumFlickVelocity(); void flickDeceleration(); + void pressDelay_data(); void pressDelay(); void nestedPressDelay(); void filterReplayedPress(); @@ -174,6 +177,7 @@ private slots: void returnToBounds(); void returnToBounds_data(); void wheel(); + void wheelBackwards(); void trackpad(); void nestedTrackpad(); void movingAndFlicking(); @@ -217,14 +221,24 @@ private slots: void receiveTapOutsideContentItem(); void flickWhenRotated_data(); void flickWhenRotated(); + void flickAndReleaseOutsideBounds(); void scrollingWithFractionalExtentSize_data(); void scrollingWithFractionalExtentSize(); void setContentPositionWhileDragging_data(); void setContentPositionWhileDragging(); + void coalescedMove(); + void onlyOneMove(); + void proportionalWheelScrolling(); + void touchCancel(); + void pixelAlignedEndPoints(); private: void flickWithTouch(QQuickWindow *window, const QPoint &from, const QPoint &to); QPointingDevice *touchDevice = QTest::createTouchDevice(); + const QPointingDevice *mouseDevice = new QPointingDevice( + "test mouse", 1000, QInputDevice::DeviceType::Mouse, QPointingDevice::PointerType::Generic, + QInputDevice::Capability::Position | QInputDevice::Capability::Hover | QInputDevice::Capability::Scroll, + 1, 5, QString(), QPointingDeviceUniqueId(), this); }; void tst_qquickflickable::initTestCase() @@ -234,6 +248,8 @@ void tst_qquickflickable::initTestCase() #endif QQmlDataTest::initTestCase(); qmlRegisterType<TouchDragArea>("Test",1,0,"TouchDragArea"); + touchDevice->setParent(this); // avoid leak + QWindowSystemInterface::registerInputDevice(mouseDevice); } void tst_qquickflickable::cleanup() @@ -519,45 +535,54 @@ void tst_qquickflickable::flickDeceleration() delete flickable; } +void tst_qquickflickable::pressDelay_data() +{ + QTest::addColumn<const QPointingDevice *>("device"); + const QPointingDevice *constTouchDevice = touchDevice; + + QTest::newRow("mouse") << mouseDevice; + QTest::newRow("touch") << constTouchDevice; +} + void tst_qquickflickable::pressDelay() { - QScopedPointer<QQuickView> window(new QQuickView); - window->setSource(testFileUrl("pressDelay.qml")); - QTRY_COMPARE(window->status(), QQuickView::Ready); - QQuickViewTestUtils::centerOnScreen(window.data()); - QQuickViewTestUtils::moveMouseAway(window.data()); - window->show(); - QVERIFY(QTest::qWaitForWindowActive(window.data())); - QVERIFY(window->rootObject() != nullptr); + QFETCH(const QPointingDevice *, device); - QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject()); - QSignalSpy spy(flickable, SIGNAL(pressDelayChanged())); + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("pressDelay.qml"))); + QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window.rootObject()); QVERIFY(flickable); - QCOMPARE(flickable->pressDelay(), 100); + QQuickMouseArea *mouseArea = flickable->findChild<QQuickMouseArea*>(); + QSignalSpy clickedSpy(mouseArea, &QQuickMouseArea::clicked); + // Test the pressDelay property itself + QSignalSpy pressDelayChangedSpy(flickable, &QQuickFlickable::pressDelayChanged); + QCOMPARE(flickable->pressDelay(), 100); flickable->setPressDelay(200); QCOMPARE(flickable->pressDelay(), 200); - QCOMPARE(spy.size(),1); + QCOMPARE(pressDelayChangedSpy.size(), 1); flickable->setPressDelay(200); - QCOMPARE(spy.size(),1); + QCOMPARE(pressDelayChangedSpy.size(), 1); - QQuickItem *mouseArea = window->rootObject()->findChild<QQuickItem*>("mouseArea"); - QSignalSpy clickedSpy(mouseArea, SIGNAL(clicked(QQuickMouseEvent*))); - moveAndPress(window.data(), QPoint(150, 150)); + // Test the press delay + QPoint p(150, 150); + if (device->type() == QInputDevice::DeviceType::Mouse) + QQuickTest::pointerMove(device, &window, 0, p); + QQuickTest::pointerPress(device, &window, 0, p); // The press should not occur immediately - QVERIFY(!mouseArea->property("pressed").toBool()); + QCOMPARE(mouseArea->isPressed(), false); // But, it should occur eventually - QTRY_VERIFY(mouseArea->property("pressed").toBool()); + QTRY_VERIFY(mouseArea->isPressed()); - QCOMPARE(clickedSpy.size(),0); + QCOMPARE(clickedSpy.size(), 0); // On release the clicked signal should be emitted - QTest::mouseRelease(window.data(), Qt::LeftButton, Qt::NoModifier, QPoint(150, 150)); - QCOMPARE(clickedSpy.size(),1); + QQuickTest::pointerRelease(device, &window, 0, p); + QCOMPARE(clickedSpy.size(), 1); // Press and release position should match QCOMPARE(flickable->property("pressX").toReal(), flickable->property("releaseX").toReal()); @@ -565,38 +590,44 @@ void tst_qquickflickable::pressDelay() // Test a quick tap within the pressDelay timeout + p = QPoint(180, 180); clickedSpy.clear(); - moveAndPress(window.data(), QPoint(180, 180)); + if (device->type() == QInputDevice::DeviceType::Mouse) + QQuickTest::pointerMove(device, &window, 0, p); + QQuickTest::pointerPress(device, &window, 0, p); // The press should not occur immediately - QVERIFY(!mouseArea->property("pressed").toBool()); + QCOMPARE(mouseArea->isPressed(), false); + QCOMPARE(clickedSpy.size(), 0); - QCOMPARE(clickedSpy.size(),0); - - // On release the press, release and clicked signal should be emitted - QTest::mouseRelease(window.data(), Qt::LeftButton, Qt::NoModifier, QPoint(180, 180)); - QCOMPARE(clickedSpy.size(),1); + // On release, the press, release and clicked signal should be emitted + QQuickTest::pointerRelease(device, &window, 0, p); + QCOMPARE(clickedSpy.size(), 1); // Press and release position should match QCOMPARE(flickable->property("pressX").toReal(), flickable->property("releaseX").toReal()); QCOMPARE(flickable->property("pressY").toReal(), flickable->property("releaseY").toReal()); - // QTBUG-31168 - moveAndPress(window.data(), QPoint(150, 110)); + // Test flick after press (QTBUG-31168) + QPoint startPosition(150, 110); + p = QPoint(150, 190); + clickedSpy.clear(); + if (device->type() == QInputDevice::DeviceType::Mouse) + QQuickTest::pointerMove(device, &window, 0, startPosition); + QQuickTest::pointerPress(device, &window, 0, startPosition); // The press should not occur immediately - QVERIFY(!mouseArea->property("pressed").toBool()); - - QTest::mouseMove(window.data(), QPoint(150, 190)); + QCOMPARE(mouseArea->isPressed(), false); + QQuickTest::pointerMove(device, &window, 0, p); - // As we moved pass the drag threshold, we should never receive the press - QVERIFY(!mouseArea->property("pressed").toBool()); - QTRY_VERIFY(!mouseArea->property("pressed").toBool()); + // Since we moved past the drag threshold, we should never receive the press + QCOMPARE(mouseArea->isPressed(), false); + QTRY_COMPARE(mouseArea->isPressed(), false); - // On release the clicked signal should *not* be emitted - QTest::mouseRelease(window.data(), Qt::LeftButton, Qt::NoModifier, QPoint(150, 190)); - QCOMPARE(clickedSpy.size(),1); + // On release, the clicked signal should *not* be emitted + QQuickTest::pointerRelease(device, &window, 0, p); + QCOMPARE(clickedSpy.size(), 0); } // QTBUG-17361 @@ -935,6 +966,47 @@ void tst_qquickflickable::wheel() QCOMPARE(flick->property("movementsAfterEnd").value<int>(), 0); // QTBUG-55886 } +void tst_qquickflickable::wheelBackwards() // (QTBUG-121349) +{ + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("wheel.qml"))); + QQuickFlickable *flick = window.rootObject()->findChild<QQuickFlickable*>("flick"); + QVERIFY(flick); + QSignalSpy moveEndSpy(flick, SIGNAL(movementEnded())); + quint64 timestamp = 10; + const QPoint pos(200, 200); + + // attempting to scroll vertically "backwards" beyond extents does not initiate overshoot + { + QWheelEvent event(pos, window.mapToGlobal(pos), QPoint(), QPoint(0, 120), + Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false); + event.setAccepted(false); + event.setTimestamp(timestamp++); + QGuiApplication::sendEvent(&window, &event); + } + QCOMPARE(flick->contentY(), qreal(0)); + QCOMPARE(flick->isMoving(), false); + QCOMPARE(moveEndSpy.size(), 0); + + // get ready to test horizontal wheel + flick->setContentY(0); // which triggers movementEnded again + flick->setProperty("movementsAfterEnd", 0); + flick->setProperty("ended", false); + QCOMPARE(flick->contentY(), qreal(0)); + + // attempting to scroll horizontally "backwards" beyond extents does not initiate overshoot + { + QWheelEvent event(pos, window.mapToGlobal(pos), QPoint(), QPoint(120, 0), + Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false); + event.setAccepted(false); + event.setTimestamp(timestamp); + QGuiApplication::sendEvent(&window, &event); + } + QCOMPARE(flick->contentX(), qreal(0)); + QCOMPARE(flick->isMoving(), false); + QCOMPARE(moveEndSpy.size(), 0); +} + void tst_qquickflickable::trackpad() { QScopedPointer<QQuickView> window(new QQuickView); @@ -2872,7 +2944,7 @@ void tst_qquickflickable::receiveTapOutsideContentItem() QVERIFY(QTest::qWaitForWindowActive(&window)); QQuickTapHandler tapHandler(&flickable); - QSignalSpy clickedSpy(&tapHandler, SIGNAL(tapped(QEventPoint, Qt::MouseButton))); + QSignalSpy clickedSpy(&tapHandler, SIGNAL(tapped(QEventPoint,Qt::MouseButton))); // Tap outside the content item in the top-left corner QTest::mouseClick(&window, Qt::LeftButton, {}, QPoint(5, 5)); @@ -2935,6 +3007,42 @@ void tst_qquickflickable::flickWhenRotated() // QTBUG-99639 QVERIFY(!flickable->isAtYBeginning()); } +void tst_qquickflickable::flickAndReleaseOutsideBounds() // QTBUG-104987 +{ + // Check that flicking works when the mouse release happens + // outside the bounds of the flickable (and the flick started on top + // of a TapHandler that has a passive grab). + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("flickableWithTapHandler.qml"))); + QQuickItem *rootItem = window.rootObject(); + QVERIFY(rootItem); + QQuickFlickable *flickable = rootItem->findChild<QQuickFlickable*>(); + QVERIFY(flickable); + QQuickItem *childItem = flickable->findChild<QQuickItem*>("childItem"); + QVERIFY(childItem); + + QVERIFY(flickable->isAtYBeginning()); + + // Startpoint is on top of the tapHandler, while the endpoint is outside the flickable + const QPointF startPos = childItem->mapToGlobal(QPoint(10, 10)); + const QPointF endPos = flickable->mapToGlobal(QPoint(10, -10)); + const QPoint globalStartPos = window.mapFromGlobal(startPos).toPoint(); + const QPoint globalEndPos = window.mapFromGlobal(endPos).toPoint(); + const qreal dragDistance = 20; + + // Note: here we need to initiate a flick using raw events, rather than + // flickable.flick(), since we're testing if the mouse events takes the + // correct path to starts a flick (among passive and exclusive grabbers, combined + // with childMouseEventFilter()). + QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, globalStartPos); + QTest::mouseMove(&window, globalStartPos - QPoint(0, dragDistance / 2)); + QTest::mouseMove(&window, globalStartPos - QPoint(0, dragDistance)); + QTest::mouseMove(&window, globalEndPos); + QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, globalEndPos); + + // Ensure that the content item ends up being moved more than what we dragged + QTRY_VERIFY(flickable->contentY() > dragDistance * 2); +} void tst_qquickflickable::scrollingWithFractionalExtentSize_data() { @@ -3123,6 +3231,207 @@ void tst_qquickflickable::setContentPositionWhileDragging() // QTBUG-104966 QVERIFY(!flickable->isDragging()); } +void tst_qquickflickable::coalescedMove() +{ + QQuickView *window = new QQuickView; + QScopedPointer<QQuickView> windowPtr(window); + windowPtr->setSource(testFileUrl("flickable03.qml")); + QTRY_COMPARE(window->status(), QQuickView::Ready); + QQuickVisualTestUtils::centerOnScreen(window); + QQuickVisualTestUtils::moveMouseAway(window); + window->show(); + QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(window->rootObject() != nullptr); + + QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject()); + QVERIFY(flickable != nullptr); + + QSignalSpy movementStartedSpy(flickable, SIGNAL(movementStarted())); + QSignalSpy movementEndedSpy(flickable, SIGNAL(movementEnded())); + QSignalSpy flickStartedSpy(flickable, SIGNAL(flickStarted())); + QSignalSpy flickEndedSpy(flickable, SIGNAL(flickEnded())); + + QTest::touchEvent(window, touchDevice).press(0, {10, 10}).commit(); + + QTest::touchEvent(window, touchDevice).move(0, {10, 40}).commit(); + + QTest::touchEvent(window, touchDevice).move(0, {10, 100}).commit(); + + QTest::touchEvent(window, touchDevice).release(0, {10, 150}).commit(); + QQuickTouchUtils::flush(window); + + QTRY_VERIFY(!flickable->isMoving()); + + QCOMPARE(movementStartedSpy.size(), 1); + QCOMPARE(flickStartedSpy.size(), 1); + QCOMPARE(movementEndedSpy.size(), 1); + QCOMPARE(flickEndedSpy.size(), 1); +} + +void tst_qquickflickable::onlyOneMove() +{ + QQuickView *window = new QQuickView; + QScopedPointer<QQuickView> windowPtr(window); + windowPtr->setSource(testFileUrl("flickable03.qml")); + QTRY_COMPARE(window->status(), QQuickView::Ready); + QQuickVisualTestUtils::centerOnScreen(window); + QQuickVisualTestUtils::moveMouseAway(window); + window->show(); + QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(window->rootObject() != nullptr); + + QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject()); + QVERIFY(flickable != nullptr); + + QSignalSpy movementStartedSpy(flickable, SIGNAL(movementStarted())); + QSignalSpy movementEndedSpy(flickable, SIGNAL(movementEnded())); + QSignalSpy flickStartedSpy(flickable, SIGNAL(flickStarted())); + QSignalSpy flickEndedSpy(flickable, SIGNAL(flickEnded())); + + QTest::touchEvent(window, touchDevice).press(0, {10, 10}).commit(); + QQuickTouchUtils::flush(window); + + QTest::touchEvent(window, touchDevice).move(0, {10, 100}).commit(); + QQuickTouchUtils::flush(window); + + QTest::touchEvent(window, touchDevice).release(0, {10, 200}).commit(); + QQuickTouchUtils::flush(window); + + QTRY_VERIFY(!flickable->isMoving()); + + QCOMPARE(movementStartedSpy.size(), 1); + QCOMPARE(flickStartedSpy.size(), 1); + QCOMPARE(movementEndedSpy.size(), 1); + QCOMPARE(flickEndedSpy.size(), 1); +} + +void tst_qquickflickable::proportionalWheelScrolling() // QTBUG-106338 etc. +{ + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("wheel.qml"))); + QQuickViewTestUtils::centerOnScreen(&window); + QVERIFY(window.isVisible()); + QQuickItem *rootItem = window.rootObject(); + QVERIFY(rootItem); + QQuickFlickable *flickable = rootItem->findChild<QQuickFlickable *>(); + QVERIFY(flickable); + + QVERIFY(!flickable->property("ended").value<bool>()); + + QPointF pos(flickable->x() + flickable->width() / 2, flickable->y() + flickable->height() / 2); + QPoint angleDelta(0, -120); + QWheelEvent wheelEvent(pos, window.mapToGlobal(pos), QPoint(), angleDelta, + Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false); + + QGuiApplication::sendEvent(&window, &wheelEvent); + qApp->processEvents(); + + // Verify that scrolling is proportional to the wheel delta + QVERIFY(flickable->isMovingVertically()); + QTRY_VERIFY(!flickable->isMovingVertically()); + + // The current movement formula being used is: delta / 120 * wheelScrollLines * 24 + const int defaultWheelDecel = 15000; + bool wheelDecelerationEnvSet = false; + const int wheelDecelerationEnv = qEnvironmentVariableIntValue("QT_QUICK_FLICKABLE_WHEEL_DECELERATION", &wheelDecelerationEnvSet); + const qreal wheelDecel = wheelDecelerationEnvSet ? wheelDecelerationEnv : defaultWheelDecel; + const bool proportionalWheel = wheelDecel >= 15000; + qCDebug(lcTests) << "platform wheel decel" << defaultWheelDecel + << "env wheel decel" << wheelDecelerationEnv + << "expect proportional scrolling?" << proportionalWheel; + const qreal expectedMovementFromWheelClick = qAbs(angleDelta.y()) / 120 * qApp->styleHints()->wheelScrollLines() * 24; + + if (proportionalWheel) + QCOMPARE(flickable->contentY(), expectedMovementFromWheelClick); + + QVERIFY(flickable->property("ended").value<bool>()); + QCOMPARE(flickable->property("movementsAfterEnd").value<int>(), 0); + + flickable->setProperty("ended", QVariant::fromValue(false)); + flickable->setContentY(0); + + // Verify that multiple wheel events in a row won't accumulate the scroll distance, before the timeline completes + wheelEvent.setTimestamp(wheelEvent.timestamp() + 2000); + QGuiApplication::sendEvent(&window, &wheelEvent); + + wheelEvent.setTimestamp(wheelEvent.timestamp() + 10); + QGuiApplication::sendEvent(&window, &wheelEvent); + + wheelEvent.setTimestamp(wheelEvent.timestamp() + 10); + QGuiApplication::sendEvent(&window, &wheelEvent); + + qApp->processEvents(); + + QVERIFY(flickable->isMovingVertically()); + QTRY_VERIFY(!flickable->isMovingVertically()); + + if (proportionalWheel) { + QVERIFY2(flickable->contentY() >= expectedMovementFromWheelClick, "The contentItem moved shorter than expected from a wheelEvent"); + QCOMPARE_LT(flickable->contentY(), expectedMovementFromWheelClick * 3); + } + + QVERIFY(flickable->property("ended").value<bool>()); + QCOMPARE(flickable->property("movementsAfterEnd").value<int>(), 0); +} + +void tst_qquickflickable::touchCancel() +{ + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("flickable03.qml"))); + QQuickViewTestUtils::centerOnScreen(&window); + QVERIFY(window.isVisible()); + + QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window.rootObject()); + QVERIFY(flickable != nullptr); + + QSignalSpy movementStartedSpy(flickable, SIGNAL(movementStarted())); + QSignalSpy movementEndedSpy(flickable, SIGNAL(movementEnded())); + + int touchPosY = 10; + QTest::touchEvent(&window, touchDevice).press(0, {10, touchPosY}).commit(); + QQuickTouchUtils::flush(&window); + + for (int i = 0; i < 3; ++i) { + touchPosY += qApp->styleHints()->startDragDistance(); + QTest::touchEvent(&window, touchDevice).move(0, {10, touchPosY}).commit(); + QQuickTouchUtils::flush(&window); + } + + QTRY_COMPARE(movementStartedSpy.size(), 1); + QWindowSystemInterface::handleTouchCancelEvent(nullptr, touchDevice); + QTRY_COMPARE(movementEndedSpy.size(), 1); +} + +void tst_qquickflickable::pixelAlignedEndPoints() +{ + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("endpoints.qml"))); + QQuickViewTestUtils::centerOnScreen(&window); + QVERIFY(window.isVisible()); + QQuickItem *rootItem = window.rootObject(); + QVERIFY(rootItem); + QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(rootItem); + QVERIFY(flickable); + flickable->setPixelAligned(true); + QVERIFY(flickable->isAtYBeginning()); + + QSignalSpy isAtEndSpy(flickable, &QQuickFlickable::atYEndChanged); + QSignalSpy isAtBeginningSpy(flickable, &QQuickFlickable::atYBeginningChanged); + + flickable->setContentY(199.99); + QCOMPARE(flickable->contentY(), 200); + QVERIFY(!flickable->isAtYBeginning()); + QVERIFY(flickable->isAtYEnd()); + QCOMPARE(isAtEndSpy.count(), 1); + QCOMPARE(isAtBeginningSpy.count(), 1); + + flickable->setContentY(0.01); + QCOMPARE(flickable->contentY(), 0); + QVERIFY(flickable->isAtYBeginning()); + QVERIFY(!flickable->isAtYEnd()); + QCOMPARE(isAtEndSpy.count(), 2); + QCOMPARE(isAtBeginningSpy.count(), 2);} + QTEST_MAIN(tst_qquickflickable) #include "tst_qquickflickable.moc" |