TapHandler: ignore scroll events and native gestures

During a 2-finger press (to emulate right click on a trackpad), the
OS may also generate a QWheelEvent with ScrollBegin phase just in case
scrolling starts.  This must not prematurely deactivate the TapHandler.

Also if a gesture or wheel event begins as the very first event after
an application starts, ensure that subsequent mouse events are not
mis-delivered as wheel or gesture events.

Fixes: QTBUG-71955
Change-Id: Ic12e116483ab9ad37c4ac3b1d10ccb62e1349e0a
Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
This commit is contained in:
Shawn Rutledge 2018-12-04 12:27:23 +01:00
parent ecec3d45ec
commit f8f0f0835a
4 changed files with 89 additions and 4 deletions

View File

@ -107,6 +107,10 @@ static bool dragOverThreshold(const QQuickEventPoint *point)
bool QQuickTapHandler::wantsEventPoint(QQuickEventPoint *point)
{
if (!point->pointerEvent()->asPointerMouseEvent() &&
!point->pointerEvent()->asPointerTouchEvent() &&
!point->pointerEvent()->asPointerTabletEvent() )
return false;
// If the user has not violated any constraint, it could be a tap.
// Otherwise we want to give up the grab so that a competing handler
// (e.g. DragHandler) gets a chance to take over.

View File

@ -2246,13 +2246,14 @@ QQuickPointerEvent *QQuickWindowPrivate::queryPointerEventInstance(QQuickPointer
{
// Search for a matching reusable event object.
for (QQuickPointerEvent *e : pointerEventInstances) {
// If device can generate native gestures (e.g. a trackpad), there might be two QQuickPointerEvents:
// QQuickPointerNativeGestureEvent and QQuickPointerTouchEvent. Use eventType to disambiguate.
// If device can generate native gestures (e.g. a trackpad), there might be multiple QQuickPointerEvents:
// QQuickPointerNativeGestureEvent, QQuickPointerScrollEvent, and QQuickPointerTouchEvent.
// Use eventType to disambiguate.
#if QT_CONFIG(gestures)
if (eventType == QEvent::NativeGesture && !qobject_cast<QQuickPointerNativeGestureEvent*>(e))
if ((eventType == QEvent::NativeGesture) != bool(e->asPointerNativeGestureEvent()))
continue;
#endif
if (eventType == QEvent::Wheel && !qobject_cast<QQuickPointerScrollEvent*>(e))
if ((eventType == QEvent::Wheel) != bool(e->asPointerScrollEvent()))
continue;
// Otherwise we assume there's only one event type per device.
// More disambiguation tests might need to be added above if that changes later.

View File

@ -0,0 +1,41 @@
/****************************************************************************
**
** Copyright (C) 2018 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
Rectangle {
width: 320
height: 240
color: rightTap.pressed ? "tomato" : "beige"
TapHandler {
id: rightTap
objectName: "right button TapHandler"
longPressThreshold: 0.5
acceptedButtons: Qt.RightButton
}
}

View File

@ -69,6 +69,7 @@ private slots:
void mouseLongPress();
void buttonsMultiTouch();
void componentUserBehavioralOverride();
void rightLongPressIgnoreWheel();
private:
void createView(QScopedPointer<QQuickView> &window, const char *fileName);
@ -622,6 +623,44 @@ void tst_TapHandler::componentUserBehavioralOverride()
QCOMPARE(userGrabChangedSpy.count(), 2);
}
void tst_TapHandler::rightLongPressIgnoreWheel()
{
QScopedPointer<QQuickView> windowPtr;
createView(windowPtr, "rightTapHandler.qml");
QQuickView * window = windowPtr.data();
QQuickTapHandler *tap = window->rootObject()->findChild<QQuickTapHandler*>();
QVERIFY(tap);
QSignalSpy tappedSpy(tap, SIGNAL(tapped(QQuickEventPoint *)));
QSignalSpy longPressedSpy(tap, SIGNAL(longPressed()));
QPoint p1(100, 100);
// Mouse wheel with ScrollBegin phase (because as soon as two fingers are touching
// the trackpad, it will send such an event: QTBUG-71955)
{
QWheelEvent wheelEvent(p1, p1, QPoint(0, 0), QPoint(0, 0),
Qt::NoButton, Qt::NoModifier, Qt::ScrollBegin, false, Qt::MouseEventNotSynthesized);
QGuiApplication::sendEvent(window, &wheelEvent);
}
// Press
QTest::mousePress(window, Qt::RightButton, Qt::NoModifier, p1);
QTRY_COMPARE(tap->isPressed(), true);
// Mouse wheel ScrollEnd phase
QWheelEvent wheelEvent(p1, p1, QPoint(0, 0), QPoint(0, 0),
Qt::NoButton, Qt::NoModifier, Qt::ScrollEnd, false, Qt::MouseEventNotSynthesized);
QGuiApplication::sendEvent(window, &wheelEvent);
QTRY_COMPARE(longPressedSpy.count(), 1);
QCOMPARE(tap->isPressed(), true);
QCOMPARE(tappedSpy.count(), 0);
// Release
QTest::mouseRelease(window, Qt::RightButton, Qt::NoModifier, p1, 500);
QTRY_COMPARE(tap->isPressed(), false);
QCOMPARE(tappedSpy.count(), 0);
}
QTEST_MAIN(tst_TapHandler)
#include "tst_qquicktaphandler.moc"