Fix widget gesture grabbing

After 7369c31ca1f446e81eda1c93ba13767ffcd83efe, unaccepted touch points
were not grabbed anymore in calls to activateImplicitTouchGrab. However,
gesture recognition relies that widgets for which a gesture has been
partially recognized grab also unaccepted touch points.

Add a parameter to allow the implicit grabbing to take place also for
unaccepted event points.

Add test case that replays touch events similar to what Squish is doing,
and fails without this fix.

Pick-to: 6.2
Change-Id: Idb0b20301b1827be57a03013a59043d97c2ee7b6
Reviewed-by: Stefan Gehn <stefan.gehn@qt.io>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
bb10
Volker Hilsheimer 2021-12-07 23:02:03 +01:00
parent 4229de1685
commit 9da1f6b608
3 changed files with 31 additions and 6 deletions

View File

@ -3159,8 +3159,9 @@ bool QApplication::notify(QObject *receiver, QEvent *e)
#ifndef QT_NO_GESTURES
if (!eventAccepted && !gesturePendingWidget.isNull()) {
// the first widget subscribed to a gesture gets an implicit grab
d->activateImplicitTouchGrab(gesturePendingWidget, touchEvent);
// the first widget subscribed to a gesture gets an implicit grab for all
// points, also for events and event points that have not been accepted.
d->activateImplicitTouchGrab(gesturePendingWidget, touchEvent, QApplicationPrivate::GrabAllPoints);
}
#endif
@ -3851,16 +3852,18 @@ QWidget *QApplicationPrivate::findClosestTouchPointTarget(const QPointingDevice
return static_cast<QWidget *>(closestTarget);
}
void QApplicationPrivate::activateImplicitTouchGrab(QWidget *widget, QTouchEvent *touchEvent)
void QApplicationPrivate::activateImplicitTouchGrab(QWidget *widget, QTouchEvent *touchEvent,
ImplicitTouchGrabMode grabMode)
{
if (touchEvent->type() != QEvent::TouchBegin)
return;
// If the widget dispatched the event further (see QGraphicsProxyWidget), then
// there might already be an implicit grabber. Don't override that.
// there might already be an implicit grabber. Don't override that. A widget that
// has partially recognized a gesture needs to grab all points.
for (int i = 0; i < touchEvent->pointCount(); ++i) {
auto &mep = QMutableEventPoint::from(touchEvent->point(i));
if (!mep.target() && mep.isAccepted())
if (!mep.target() && (mep.isAccepted() || grabMode == GrabAllPoints))
mep.setTarget(widget);
}
// TODO setExclusiveGrabber() to be consistent with Qt Quick?

View File

@ -248,7 +248,9 @@ public:
QWidget *findClosestTouchPointTarget(const QPointingDevice *device, const QEventPoint &touchPoint);
void appendTouchPoint(const QEventPoint &touchPoint);
void removeTouchPoint(int touchPointId);
void activateImplicitTouchGrab(QWidget *widget, QTouchEvent *touchBeginEvent);
enum ImplicitTouchGrabMode { GrabAcceptedPoints, GrabAllPoints };
void activateImplicitTouchGrab(QWidget *widget, QTouchEvent *touchBeginEvent,
ImplicitTouchGrabMode grabMode = GrabAcceptedPoints);
static bool translateRawTouchEvent(QWidget *widget, const QTouchEvent *touchEvent);
static void translateTouchCancel(const QPointingDevice *device, ulong timestamp);

View File

@ -53,6 +53,7 @@ private Q_SLOTS:
void pinchGesture();
void swipeGesture_data();
void swipeGesture();
void touchReplay();
#endif // !QT_NO_GESTURES
private:
@ -326,6 +327,25 @@ void tst_QGestureRecognizer::swipeGesture()
}
}
void tst_QGestureRecognizer::touchReplay()
{
const Qt::GestureType gestureType = Qt::TapGesture;
QWidget parent;
TestWidget widget(GestureTypeVector(1, gestureType));
widget.setParent(&parent);
widget.setGeometry(0, 0, 100, 100);
parent.adjustSize();
parent.show();
QVERIFY(QTest::qWaitForWindowActive(&parent));
QWindow* windowHandle = parent.window()->windowHandle();
const QPoint globalPos = QPoint(42, 16);
QTest::touchEvent(windowHandle, m_touchDevice).press(1, globalPos);
QTest::touchEvent(windowHandle, m_touchDevice).release(1, globalPos);
QVERIFY(widget.gestureReceived(gestureType));
}
#endif // !QT_NO_GESTURES
QTEST_MAIN(tst_QGestureRecognizer)