diff options
Diffstat (limited to 'tests')
10 files changed, 730 insertions, 47 deletions
diff --git a/tests/auto/quick/pointerhandlers/pointerhandlers.pro b/tests/auto/quick/pointerhandlers/pointerhandlers.pro index 950d6835eb..4d6311bdb2 100644 --- a/tests/auto/quick/pointerhandlers/pointerhandlers.pro +++ b/tests/auto/quick/pointerhandlers/pointerhandlers.pro @@ -10,4 +10,5 @@ qtConfig(private_tests) { qquickpointerhandler \ qquickpointhandler \ qquicktaphandler \ + qquickwheelhandler \ } diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/nested.qml b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/nested.qml new file mode 100644 index 0000000000..3243515180 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/nested.qml @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.14 + +Rectangle { + width: 320; height: 240 + color: "lightsteelblue"; antialiasing: true + border.color: outerWheelHandler.active ? "red" : "white" + + WheelHandler { + id: outerWheelHandler + objectName: "outerWheelHandler" + property: "x" + } + + Rectangle { + width: 120; height: 120; x: 100; y: 60 + color: "beige"; antialiasing: true + border.color: innerWheelHandler.active ? "red" : "white" + + WheelHandler { + id: innerWheelHandler + objectName: "innerWheelHandler" + // should deactivate because events go to the outer handler, not because of timeout + activeTimeout: 1 + property: "x" + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml new file mode 100644 index 0000000000..b2272a57c2 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.14 + +Rectangle { + width: 320; height: 240 + color: "green"; antialiasing: true + + Rectangle { + width: 100; height: 2; anchors.centerIn: parent + Rectangle { + width: 2; height: 100; anchors.centerIn: parent + } + } + + WheelHandler { } +} diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/qquickwheelhandler.pro b/tests/auto/quick/pointerhandlers/qquickwheelhandler/qquickwheelhandler.pro new file mode 100644 index 0000000000..7509e38dd3 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/qquickwheelhandler.pro @@ -0,0 +1,14 @@ +CONFIG += testcase +TARGET = tst_qquickwheelhandler +macos:CONFIG -= app_bundle + +SOURCES += tst_qquickwheelhandler.cpp +OTHER_FILES = \ + data/rectWheel.qml \ + +include (../../../shared/util.pri) +include (../../shared/util.pri) + +TESTDATA = data/* + +QT += core-private gui-private qml-private quick-private testlib diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp b/tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp new file mode 100644 index 0000000000..2abf2ea8c3 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp @@ -0,0 +1,344 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtTest/QSignalSpy> +#include <QtGui/QStyleHints> +#include <qpa/qwindowsysteminterface.h> +#include <private/qquickwheelhandler_p.h> +#include <QtQuick/private/qquickrectangle_p.h> +#include <QtQuick/qquickview.h> +#include <QtQml/qqmlcontext.h> +#include "../../../shared/util.h" +#include "../../shared/viewtestutil.h" + +Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests") + +class tst_QQuickWheelHandler: public QQmlDataTest +{ + Q_OBJECT +public: + tst_QQuickWheelHandler() { } + +private slots: + void singleHandler_data(); + void singleHandler(); + void nestedHandler_data(); + void nestedHandler(); + +private: + void sendWheelEvent(QQuickView &window, QPoint pos, QPoint angleDelta, + QPoint pixelDelta = QPoint(), Qt::KeyboardModifiers modifiers = Qt::NoModifier, + Qt::ScrollPhase phase = Qt::NoScrollPhase, bool inverted = false); +}; + +void tst_QQuickWheelHandler::sendWheelEvent(QQuickView &window, QPoint pos, QPoint angleDelta, + QPoint pixelDelta, Qt::KeyboardModifiers modifiers, Qt::ScrollPhase phase, bool inverted) +{ + QWheelEvent wheelEvent(pos, window.mapToGlobal(pos), pixelDelta, angleDelta, + Qt::NoButton, modifiers, phase, inverted); + QGuiApplication::sendEvent(&window, &wheelEvent); + qApp->processEvents(); +} + +void tst_QQuickWheelHandler::singleHandler_data() +{ + // handler properties + QTest::addColumn<Qt::Orientation>("orientation"); + QTest::addColumn<bool>("invertible"); + QTest::addColumn<int>("rotationScale"); + QTest::addColumn<QString>("property"); + QTest::addColumn<qreal>("targetScaleMultiplier"); + QTest::addColumn<bool>("targetTransformAroundCursor"); + // event + QTest::addColumn<QPoint>("eventPos"); + QTest::addColumn<QPoint>("eventAngleDelta"); + QTest::addColumn<QPoint>("eventPixelDelta"); + QTest::addColumn<Qt::KeyboardModifiers>("eventModifiers"); + QTest::addColumn<bool>("eventPhases"); + QTest::addColumn<bool>("eventInverted"); + // result + QTest::addColumn<QPoint>("expectedPosition"); + QTest::addColumn<qreal>("expectedScale"); + QTest::addColumn<int>("expectedRotation"); + + // move the item + QTest::newRow("vertical wheel angle delta to adjust x") + << Qt::Vertical << false << 1 << "x" << 1.5 << true + << QPoint(160, 120) << QPoint(-360, 120) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false + << QPoint(15, 0) << 1.0 << 0; + QTest::newRow("horizontal wheel angle delta to adjust y") + << Qt::Horizontal << false << 1 << "y" << 1.5 << false + << QPoint(160, 120) << QPoint(-360, 120) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false + << QPoint(0, -45) << 1.0 << 0; + QTest::newRow("vertical wheel angle delta to adjust y, amplified and inverted") + << Qt::Vertical << true << 4 << "y" << 1.5 << true + << QPoint(160, 120) << QPoint(60, 60) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << true + << QPoint(0, 30) << 1.0 << 0; + QTest::newRow("horizontal wheel angle delta to adjust x, amplified and reversed") + << Qt::Horizontal << false << -4 << "x" << 1.5 << false + << QPoint(160, 120) << QPoint(60, 60) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false + << QPoint(-30, 0) << 1.0 << 0; + QTest::newRow("vertical wheel pixel delta to adjust x") + << Qt::Vertical << false << 1 << "x" << 1.5 << true + << QPoint(160, 120) << QPoint(-360, 120) << QPoint(20, 20) << Qt::KeyboardModifiers(Qt::NoModifier) << true << false + << QPoint(20, 0) << 1.0 << 0; + QTest::newRow("horizontal wheel pixel delta to adjust y") + << Qt::Horizontal << false << 1 << "y" << 1.5 << false + << QPoint(160, 120) << QPoint(-360, 120) << QPoint(20, 20) << Qt::KeyboardModifiers(Qt::NoModifier) << true << false + << QPoint(0, 20) << 1.0 << 0; + QTest::newRow("vertical wheel pixel delta to adjust y, amplified and inverted") + << Qt::Vertical << true << 4 << "y" << 1.5 << true + << QPoint(160, 120) << QPoint(60, 60) << QPoint(20, 20) << Qt::KeyboardModifiers(Qt::NoModifier) << true << true + << QPoint(0, 80) << 1.0 << 0; + QTest::newRow("horizontal wheel pixel delta to adjust x, amplified and reversed") + << Qt::Horizontal << false << -4 << "x" << 1.5 << false + << QPoint(160, 120) << QPoint(60, 60) << QPoint(20, 20) << Qt::KeyboardModifiers(Qt::NoModifier) << true << false + << QPoint(-80, 0) << 1.0 << 0; + + // scale the item + QTest::newRow("vertical wheel angle delta to adjust scale") + << Qt::Vertical << false << 1 << "scale" << 1.5 << true + << QPoint(50, 32) << QPoint(360, 120) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false + << QPoint(55, 44) << 1.5 << 0; + QTest::newRow("horizontal wheel angle delta to adjust scale, amplified and reversed, don't adjust position") + << Qt::Horizontal << false << -2 << "scale" << 1.5 << false + << QPoint(50, 32) << QPoint(-240, 360) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false + << QPoint(0, 0) << 5.0625 << 0; + + // rotate the item + QTest::newRow("vertical wheel angle delta to adjust rotation") + << Qt::Vertical << false << 1 << "rotation" << 1.5 << true + << QPoint(50, 32) << QPoint(360, -120) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false + << QPoint(19, -31) << 1.0 << -15; + QTest::newRow("horizontal wheel angle delta to adjust rotation, amplified and reversed, don't adjust position") + << Qt::Horizontal << false << -2 << "rotation" << 1.5 << false + << QPoint(80, 80) << QPoint(240, 360) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false + << QPoint(0, 0) << 1.0 << -60; +} + +void tst_QQuickWheelHandler::singleHandler() +{ + // handler properties + QFETCH(Qt::Orientation, orientation); + QFETCH(bool, invertible); + QFETCH(int, rotationScale); + QFETCH(QString, property); + QFETCH(qreal, targetScaleMultiplier); + QFETCH(bool, targetTransformAroundCursor); + // event + QFETCH(QPoint, eventPos); + QFETCH(QPoint, eventAngleDelta); + QFETCH(QPoint, eventPixelDelta); + QFETCH(Qt::KeyboardModifiers, eventModifiers); + QFETCH(bool, eventPhases); + QFETCH(bool, eventInverted); + // result + QFETCH(QPoint, expectedPosition); + QFETCH(qreal, expectedScale); + QFETCH(int, expectedRotation); + + QQuickView window; + QByteArray errorMessage; + QVERIFY2(QQuickTest::initView(window, testFileUrl("rectWheel.qml"), true, &errorMessage), errorMessage.constData()); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + QQuickItem *rect = window.rootObject(); + QVERIFY(rect != nullptr); + QQuickWheelHandler *handler = rect->findChild<QQuickWheelHandler*>(); + QVERIFY(handler != nullptr); + handler->setOrientation(orientation); + handler->setInvertible(invertible); + handler->setRotationScale(rotationScale); + handler->setProperty(property); + handler->setTargetScaleMultiplier(targetScaleMultiplier); + handler->setTargetTransformAroundCursor(targetTransformAroundCursor); + QSignalSpy activeChangedSpy(handler, SIGNAL(activeChanged())); + + if (eventPhases) { + sendWheelEvent(window, eventPos, QPoint(), QPoint(), eventModifiers, Qt::ScrollBegin, eventInverted); + sendWheelEvent(window, eventPos, eventAngleDelta, eventPixelDelta, eventModifiers, Qt::ScrollUpdate, eventInverted); + } else { + sendWheelEvent(window, eventPos, eventAngleDelta, eventPixelDelta, eventModifiers, Qt::NoScrollPhase, eventInverted); + } + QCOMPARE(rect->position().toPoint(), expectedPosition); + QCOMPARE(activeChangedSpy.count(), 1); + QCOMPARE(handler->active(), true); + QCOMPARE(rect->scale(), expectedScale); + QCOMPARE(rect->rotation(), expectedRotation); + if (!eventPhases) { + QTRY_COMPARE(handler->active(), false); + QCOMPARE(activeChangedSpy.count(), 2); + } + + // restore by rotating backwards + if (eventPhases) { + sendWheelEvent(window, eventPos, eventAngleDelta * -1, eventPixelDelta * -1, eventModifiers, Qt::ScrollUpdate, eventInverted); + sendWheelEvent(window, eventPos, QPoint(), QPoint(), eventModifiers, Qt::ScrollEnd, eventInverted); + } else { + sendWheelEvent(window, eventPos, eventAngleDelta * -1, eventPixelDelta * -1, eventModifiers, Qt::NoScrollPhase, eventInverted); + } + QCOMPARE(activeChangedSpy.count(), eventPhases ? 2 : 3); + QCOMPARE(handler->active(), !eventPhases); + QCOMPARE(rect->position().toPoint(), QPoint(0, 0)); + QCOMPARE(rect->scale(), 1); + QCOMPARE(rect->rotation(), 0); +} + +void tst_QQuickWheelHandler::nestedHandler_data() +{ + // handler properties + QTest::addColumn<Qt::Orientation>("orientation"); + QTest::addColumn<bool>("invertible"); + QTest::addColumn<int>("rotationScale"); + QTest::addColumn<QString>("property"); + QTest::addColumn<qreal>("targetScaleMultiplier"); + QTest::addColumn<bool>("targetTransformAroundCursor"); + // event + QTest::addColumn<QPoint>("eventPos"); + QTest::addColumn<QPoint>("eventAngleDelta"); + QTest::addColumn<QPoint>("eventPixelDelta"); + QTest::addColumn<Qt::KeyboardModifiers>("eventModifiers"); + QTest::addColumn<bool>("eventPhases"); + QTest::addColumn<bool>("eventInverted"); + QTest::addColumn<int>("eventCount"); + // result: inner handler + QTest::addColumn<QPoint>("innerPosition"); + QTest::addColumn<qreal>("innerScale"); + QTest::addColumn<int>("innerRotation"); + // result: outer handler + QTest::addColumn<QPoint>("outerPosition"); + QTest::addColumn<qreal>("outerScale"); + QTest::addColumn<int>("outerRotation"); + + // move the item + QTest::newRow("vertical wheel angle delta to adjust x") + << Qt::Vertical << false << 1 << "x" << 1.5 << true + << QPoint(160, 120) << QPoint(120, 120) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false << 10 + << QPoint(175,60) << 1.0 << 0 + << QPoint(75, 0) << 1.0 << 0; + QTest::newRow("horizontal wheel pixel delta to adjust y") + << Qt::Horizontal << false << 1 << "y" << 1.5 << false + << QPoint(160, 120) << QPoint(120, 120) << QPoint(50, 50) << Qt::KeyboardModifiers(Qt::NoModifier) << true << false << 4 + << QPoint(100, 160) << 1.0 << 0 + << QPoint(0, 100) << 1.0 << 0; +} + +void tst_QQuickWheelHandler::nestedHandler() +{ + // handler properties + QFETCH(Qt::Orientation, orientation); + QFETCH(bool, invertible); + QFETCH(int, rotationScale); + QFETCH(QString, property); + QFETCH(qreal, targetScaleMultiplier); + QFETCH(bool, targetTransformAroundCursor); + // event + QFETCH(QPoint, eventPos); + QFETCH(QPoint, eventAngleDelta); + QFETCH(QPoint, eventPixelDelta); + QFETCH(Qt::KeyboardModifiers, eventModifiers); + QFETCH(bool, eventPhases); + QFETCH(bool, eventInverted); + QFETCH(int, eventCount); + // result: inner handler + QFETCH(QPoint, innerPosition); + QFETCH(qreal, innerScale); + QFETCH(int, innerRotation); + // result: outer handler + QFETCH(QPoint, outerPosition); + QFETCH(qreal, outerScale); + QFETCH(int, outerRotation); + + QQuickView window; + QByteArray errorMessage; + QVERIFY2(QQuickTest::initView(window, testFileUrl("nested.qml"), true, &errorMessage), errorMessage.constData()); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + QQuickItem *outerRect = window.rootObject(); + QVERIFY(outerRect != nullptr); + QQuickWheelHandler *outerHandler = outerRect->findChild<QQuickWheelHandler*>("outerWheelHandler"); + QVERIFY(outerHandler != nullptr); + QQuickWheelHandler *innerHandler = outerRect->findChild<QQuickWheelHandler*>("innerWheelHandler"); + QVERIFY(innerHandler != nullptr); + QQuickItem *innerRect = innerHandler->parentItem(); + QVERIFY(innerRect != nullptr); + innerHandler->setOrientation(orientation); + innerHandler->setInvertible(invertible); + innerHandler->setRotationScale(rotationScale); + innerHandler->setProperty(property); + innerHandler->setTargetScaleMultiplier(targetScaleMultiplier); + innerHandler->setTargetTransformAroundCursor(targetTransformAroundCursor); + outerHandler->setOrientation(orientation); + outerHandler->setInvertible(invertible); + outerHandler->setRotationScale(rotationScale); + outerHandler->setProperty(property); + outerHandler->setTargetScaleMultiplier(targetScaleMultiplier); + outerHandler->setTargetTransformAroundCursor(targetTransformAroundCursor); + QSignalSpy innerActiveChangedSpy(innerHandler, SIGNAL(activeChanged())); + QSignalSpy outerActiveChangedSpy(outerHandler, SIGNAL(activeChanged())); + + if (eventPhases) + sendWheelEvent(window, eventPos, QPoint(), QPoint(), eventModifiers, Qt::ScrollBegin, eventInverted); + for (int i = 0; i < eventCount; ++i) + sendWheelEvent(window, eventPos, eventAngleDelta, eventPixelDelta, eventModifiers, + (eventPhases ? Qt::ScrollUpdate : Qt::NoScrollPhase), eventInverted); + QCOMPARE(innerRect->position().toPoint(), innerPosition); + + /* + If outer is activated, maybe inner should be deactivated? But the event + doesn't get delivered to inner anymore, so it doesn't find out that + it's no longer getting events. It will get deactivated after the + timeout, just as if the user stopped scrolling. + + This situation is similar to QTBUG-50199, but it's questionable whether + that was really so important. So far in Qt Quick, if you move the mouse + while wheel momentum continues, or if the item moves out from under the + mouse, a different item starts getting the events immediately. In + non-Qt applications on most OSes, that's quite normal. + */ + // QCOMPARE(innerActiveChangedSpy.count(), 2); + // QCOMPARE(innerHandler->active(), false); + QCOMPARE(innerRect->scale(), innerScale); + QCOMPARE(innerRect->rotation(), innerRotation); + QCOMPARE(outerRect->position().toPoint(), outerPosition); + QCOMPARE(outerActiveChangedSpy.count(), 1); + QCOMPARE(outerHandler->active(), true); + QCOMPARE(outerRect->scale(), outerScale); + QCOMPARE(outerRect->rotation(), outerRotation); + if (!eventPhases) { + QTRY_COMPARE(outerHandler->active(), false); + QCOMPARE(outerActiveChangedSpy.count(), 2); + } +} + +QTEST_MAIN(tst_QQuickWheelHandler) + +#include "tst_qquickwheelhandler.moc" diff --git a/tests/manual/pointer/content/FakeFlickable.qml b/tests/manual/pointer/content/FakeFlickable.qml index ffb5c4e914..636399ba2c 100644 --- a/tests/manual/pointer/content/FakeFlickable.qml +++ b/tests/manual/pointer/content/FakeFlickable.qml @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the manual tests of the Qt Toolkit. @@ -26,10 +26,12 @@ ** ****************************************************************************/ -import QtQuick 2.12 +import QtQuick 2.14 +import Qt.labs.animation 1.0 Item { id: root + objectName: "viewport" default property alias data: __contentItem.data property alias velocity: anim.velocity property alias contentX: __contentItem.x // sign is reversed compared to Flickable.contentX @@ -45,52 +47,81 @@ Item { width: childrenRect.width height: childrenRect.height - property real xlimit: root.width - __contentItem.width - property real ylimit: root.height - __contentItem.height + BoundaryRule on x { + id: xbr + minimum: root.width - __contentItem.width + maximum: 0 + minimumOvershoot: 100 + maximumOvershoot: 100 + overshootFilter: BoundaryRule.Peak + } - function returnToBounds() { - if (x > 0) { - returnXAnim.to = 0 - returnXAnim.start() - } else if (x < xlimit) { - returnXAnim.to = xlimit - returnXAnim.start() - } - if (y > 0) { - returnYAnim.to = 0 - returnYAnim.start() - } else if (y < ylimit) { - returnYAnim.to = ylimit - returnYAnim.start() - } + BoundaryRule on y { + id: ybr + minimum: root.height - __contentItem.height + maximum: 0 + minimumOvershoot: 100 + maximumOvershoot: 100 + overshootFilter: BoundaryRule.Peak } DragHandler { id: dragHandler - onActiveChanged: if (!active) anim.restart(centroid.velocity) + onActiveChanged: + if (active) { + anim.stop() + root.flickStarted() + } else { + var vel = centroid.velocity + if (xbr.returnToBounds()) + vel.x = 0 + if (ybr.returnToBounds()) + vel.y = 0 + if (vel.x !== 0 || vel.y !== 0) + anim.restart(vel) + else + root.flickEnded() + } + } + WheelHandler { + rotationScale: 15 + property: "x" + orientation: Qt.Horizontal + acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad + onActiveChanged: + // emitting signals in both instances is redundant but hard to avoid + // when the touchpad is flicking along both axes + if (active) { + anim.stop() + root.flickStarted() + } else { + xbr.returnToBounds() + root.flickEnded() + } + } + WheelHandler { + rotationScale: 15 + property: "y" + orientation: Qt.Vertical + acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad + onActiveChanged: + if (active) { + anim.stop() + root.flickStarted() + } else { + ybr.returnToBounds() + root.flickEnded() + } } MomentumAnimation { id: anim target: __contentItem onStarted: root.flickStarted() onStopped: { - __contentItem.returnToBounds() + xbr.returnToBounds() + ybr.returnToBounds() root.flickEnded() } } - NumberAnimation { - id: returnXAnim - target: __contentItem - property: "x" - duration: 200 - easing.type: Easing.OutQuad - } - NumberAnimation { - id: returnYAnim - target: __contentItem - property: "y" - duration: 200 - easing.type: Easing.OutQuad - } } } diff --git a/tests/manual/pointer/content/Slider.qml b/tests/manual/pointer/content/Slider.qml index e3e02b0a2f..beb84b176b 100644 --- a/tests/manual/pointer/content/Slider.qml +++ b/tests/manual/pointer/content/Slider.qml @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the manual tests of the Qt Toolkit. @@ -26,7 +26,8 @@ ** ****************************************************************************/ -import QtQuick 2.12 +import QtQuick 2.14 +import Qt.labs.animation 1.0 Item { id: root @@ -42,8 +43,16 @@ Item { objectName: label.text + " DragHandler" target: knob xAxis.enabled: false - yAxis.minimum: slot.y - yAxis.maximum: slot.height + slot.y - knob.height + } + + WheelHandler { + id: wheelHandler + objectName: label.text + " WheelHandler" + acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad + invertible: false // Don't let the system "natural scrolling" setting affect this + rotationScale: -0.5 // But make it go consistently in the same direction as the fingers or wheel, a bit slow + target: knob + property: "y" } Rectangle { @@ -82,10 +91,10 @@ Item { height: root.width / 2 width: implicitWidth / implicitHeight * height property bool programmatic: false - property real multiplier: root.maximumValue / (dragHandler.yAxis.maximum - dragHandler.yAxis.minimum) - onYChanged: if (!programmatic) root.value = root.maximumValue - (knob.y - dragHandler.yAxis.minimum) * multiplier + property real multiplier: root.maximumValue / (ybr.maximum - ybr.minimum) + onYChanged: if (!programmatic) root.value = root.maximumValue - (knob.y - ybr.minimum) * multiplier transformOrigin: Item.Center - function setValue(value) { knob.y = dragHandler.yAxis.maximum - value / knob.multiplier } + function setValue(value) { knob.y = ybr.maximum - value / knob.multiplier } TapHandler { id: tap objectName: label.text + " TapHandler" @@ -95,6 +104,11 @@ Item { root.tapped } } + BoundaryRule on y { + id: ybr + minimum: slot.y + maximum: slot.height + slot.y - knob.height + } } Text { diff --git a/tests/manual/pointer/fakeFlickable.qml b/tests/manual/pointer/fakeFlickable.qml index 3007848c9f..be52e4dbaa 100644 --- a/tests/manual/pointer/fakeFlickable.qml +++ b/tests/manual/pointer/fakeFlickable.qml @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the manual tests of the Qt Toolkit. @@ -30,6 +30,7 @@ import QtQuick 2.12 import "content" Rectangle { + id: root color: "#444" width: 480 height: 640 @@ -49,8 +50,14 @@ Rectangle { ", parent " + parent + " geom " + parent.width + "x" + parent.height) } - onFlickStarted: console.log("flick started with velocity " + velocity) - onFlickEnded: console.log("flick ended with velocity " + velocity) + onFlickStarted: { + root.border.color = "green" + console.log("flick started with velocity " + velocity) + } + onFlickEnded: { + root.border.color = "transparent" + console.log("flick ended with velocity " + velocity) + } Component.onCompleted: { var request = new XMLHttpRequest() diff --git a/tests/manual/pointer/map.qml b/tests/manual/pointer/map.qml index c400874d58..0e815ccd9c 100644 --- a/tests/manual/pointer/map.qml +++ b/tests/manual/pointer/map.qml @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2018 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the manual tests of the Qt Toolkit. @@ -26,7 +26,7 @@ ** ****************************************************************************/ -import QtQuick 2.12 +import QtQuick 2.14 Item { width: 640 @@ -41,6 +41,20 @@ Item { height: image.height transform: Rotation { id: tilt; origin.x: width / 2; origin.y: height / 2; axis { x: 1; y: 0; z: 0 } } + WheelHandler { + id: wheelHandler + objectName: "vertical mouse wheel for scaling" + property: "scale" + onWheel: console.log("rotation " + event.angleDelta + " scaled " + rotation + " @ " + point.position + " => " + map.scale) + } + + WheelHandler { + id: horizontalWheelHandler + objectName: "horizontal mouse wheel for side-scrolling" + property: "x" + orientation: Qt.Horizontal + } + Image { id: image anchors.centerIn: parent diff --git a/tests/manual/pointer/pinchAndWheel.qml b/tests/manual/pointer/pinchAndWheel.qml new file mode 100644 index 0000000000..0944717bb7 --- /dev/null +++ b/tests/manual/pointer/pinchAndWheel.qml @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.14 +import Qt.labs.animation 1.0 +import "content" + +Rectangle { + id: root + width: 1024; height: 600 + color: "#eee" + + CheckBox { + id: cbTouchpadEnabled + x: 10; y: 6 + label: "Touchpad wheel emulation enabled" + } + + CheckBox { + id: cbHorzWheelEnabled + x: cbTouchpadEnabled.width + 20; y: 6 + label: "Horizontal wheel rotation" + } + + function getTransformationDetails(item, pinchhandler) { + return "\n\npinch.scale:" + pinchhandler.scale.toFixed(2) + + "\npinch.rotation:" + pinchhandler.rotation.toFixed(2) + + "°\npinch.translation:" + "(" + pinchhandler.translation.x.toFixed(2) + "," + pinchhandler.translation.y.toFixed(2) + ")" + + "\nscale wheel.rotation:" + scaleWheelHandler.rotation.toFixed(2) + + "°\nhorizontal wheel.rotation:" + horizontalRotationWheelHandler.rotation.toFixed(2) + + "°\ncontrol-rotation wheel.rotation:" + controlRotationWheelHandler.rotation.toFixed(2) + + "°\nrect.scale: " + item.scale.toFixed(2) + + "\nrect.rotation: " + item.rotation.toFixed(2) + + "°\nrect.position: " + "(" + item.x.toFixed(2) + "," + item.y.toFixed(2) + ")" + } + + Rectangle { + id: transformable + width: parent.width - 100; height: parent.height - 100; x: 50; y: 50 + color: "#ffe0e0e0" + antialiasing: true + + PinchHandler { + id: parentPinch + objectName: "parent pinch" + minimumScale: 0.5 + maximumScale: 3 + } + + WheelHandler { + id: scaleWheelHandler + objectName: "mouse wheel for scaling" + acceptedDevices: cbTouchpadEnabled.checked ? PointerDevice.Mouse | PointerDevice.TouchPad : PointerDevice.Mouse + acceptedModifiers: Qt.NoModifier + property: "scale" + onActiveChanged: if (!active) sbr.returnToBounds(); + onWheel: console.log(objectName + ": rotation " + event.angleDelta.y + " scaled " + rotation + " @ " + point.position + " => " + parent.scale) + } + + BoundaryRule on scale { + id: sbr + minimum: 0.1 + maximum: 2 + minimumOvershoot: 0.05 + maximumOvershoot: 0.05 + } + + BoundaryRule on rotation { + id: rbr + minimum: -90 + maximum: 360 + minimumOvershoot: 10 + maximumOvershoot: 10 + } + + WheelHandler { + id: horizontalRotationWheelHandler + enabled: cbHorzWheelEnabled.checked + objectName: "horizontal mouse wheel for rotation" + acceptedDevices: cbTouchpadEnabled.checked ? PointerDevice.Mouse | PointerDevice.TouchPad : PointerDevice.Mouse + acceptedModifiers: Qt.NoModifier + property: "rotation" + orientation: Qt.Horizontal + onActiveChanged: if (!active) rbr.returnToBounds() + onWheel: console.log(objectName + ": rotation " + event.angleDelta.y + " scaled " + rotation + " @ " + point.position + " => " + parent.rotation) + } + + WheelHandler { + id: controlRotationWheelHandler + objectName: "control-mouse wheel for rotation" + acceptedDevices: cbTouchpadEnabled.checked ? PointerDevice.Mouse | PointerDevice.TouchPad : PointerDevice.Mouse + acceptedModifiers: Qt.ControlModifier + property: "rotation" + orientation: Qt.Vertical // already the default + // TODO returnToBounds() causes trouble because position isn't being adjusted when this happens + onActiveChanged: if (!active) rbr.returnToBounds() + onWheel: console.log(objectName + ": rotation " + event.angleDelta.y + " scaled " + rotation + " @ " + point.position + " => " + parent.rotation) + } + + HoverHandler { + id: hover + acceptedDevices: PointerDevice.AllDevices + property var scenePoint: transformable.mapToItem(root, point.position.x, point.position.y) + } + + Text { + text: "Pinch with 2 fingers to scale, rotate and translate\nMouse wheel to scale, Ctrl+mouse wheel or horizontal wheel to rotate" + + getTransformationDetails(parent, parentPinch) + } + } + + Rectangle { + width: 1; height: parent.height + color: "blue" + x: hover.scenePoint.x + Text { + x: implicitWidth / -2; style: Text.Outline; styleColor: "white" + y: 30 + color: "blue" + text: "outer " + parent.x.toFixed(2) + " inner " + hover.point.position.x.toFixed(2) + } + } + + Rectangle { + width: parent.width; height: 1 + color: "blue" + y: hover.scenePoint.y + Text { + x: 45 + y: implicitHeight / -2; style: Text.Outline; styleColor: "white" + color: "blue" + text: "outer " + parent.y.toFixed(2) + " inner " + hover.point.position.y.toFixed(2) + } + } +} |