Fix MouseButtonDblClick synthesis from touch double-tap

When QGuiApplicationPrivate::processTouchEvent() sees that the
touch event was not handled, and calls processMouseEvent(), the latter
uses the QEventPoint with pointId 0 regardless of the original
touchpoint ID. Now it updates the persistent QEventPoint from the
original touchpoint so that a double-click event will not be ruled out
because of the timestamp delta or position delta (movement since press)
being too large.

Fixes: QTBUG-125993
Pick-to: 6.7 6.5
Change-Id: I8e9b007818107ac2329454e0ccfb2ac9e506b617
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
(cherry picked from commit 2a0b907f11b9c0ad46322ba06482861423246d93)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Shawn Rutledge 2024-06-06 22:37:46 -07:00 committed by Qt Cherry-pick Bot
parent c410163173
commit b1481a1663
4 changed files with 79 additions and 6 deletions

View File

@ -2271,7 +2271,7 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo
QWindowSystemInterfacePrivate::MouseEvent moveEvent(window, e->timestamp,
e->localPos, e->globalPos, e->buttons ^ button, e->modifiers, Qt::NoButton,
e->nonClientArea ? QEvent::NonClientAreaMouseMove : QEvent::MouseMove,
e->source, e->nonClientArea);
e->source, e->nonClientArea, device, e->eventPointId);
if (e->synthetic())
moveEvent.flags |= QWindowSystemInterfacePrivate::WindowSystemEvent::Synthetic;
processMouseEvent(&moveEvent); // mouse move excluding state change
@ -2291,6 +2291,9 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo
bool doubleClick = false;
auto persistentEPD = devPriv->pointById(0);
if (e->synthetic(); auto *originalDeviceEPD = devPriv->queryPointById(e->eventPointId))
QMutableEventPoint::update(originalDeviceEPD->eventPoint, persistentEPD->eventPoint);
if (mouseMove) {
QGuiApplicationPrivate::lastCursorPosition = globalPoint;
const auto doubleClickDistance = (e->device && e->device->type() == QInputDevice::DeviceType::Mouse ?
@ -3207,7 +3210,8 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To
}
// All touch events that are not accepted by the application will be translated to
// left mouse button events instead (see AA_SynthesizeMouseForUnhandledTouchEvents docs).
// TODO why go through QPA? Why not just send a QMouseEvent right from here?
// Sending a QPA event (rather than simply sending a QMouseEvent) takes care of
// side-effects such as double-click synthesis.
QWindowSystemInterfacePrivate::MouseEvent fake(window, e->timestamp,
window->mapFromGlobal(touchPoint->globalPosition().toPoint()),
touchPoint->globalPosition(),
@ -3217,7 +3221,8 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To
mouseEventType,
Qt::MouseEventSynthesizedByQt,
false,
device);
device,
touchPoint->id());
fake.flags |= QWindowSystemInterfacePrivate::WindowSystemEvent::Synthetic;
processMouseEvent(&fake);
}

View File

@ -224,9 +224,11 @@ public:
Qt::MouseButtons state, Qt::KeyboardModifiers mods,
Qt::MouseButton b, QEvent::Type type,
Qt::MouseEventSource src = Qt::MouseEventNotSynthesized, bool frame = false,
const QPointingDevice *device = QPointingDevice::primaryPointingDevice())
const QPointingDevice *device = QPointingDevice::primaryPointingDevice(),
int evPtId = -1)
: PointerEvent(w, time, Mouse, mods, device), localPos(local), globalPos(global),
buttons(state), source(src), nonClientArea(frame), button(b), buttonType(type) { }
buttons(state), source(src), nonClientArea(frame), button(b), buttonType(type),
eventPointId(evPtId) { }
QPointF localPos;
QPointF globalPos;
@ -235,6 +237,7 @@ public:
bool nonClientArea;
Qt::MouseButton button;
QEvent::Type buttonType;
int eventPointId; // from the original device if synth-mouse, otherwise -1
};
class WheelEvent : public PointerEvent {

View File

@ -3872,6 +3872,7 @@ void tst_QGraphicsProxyWidget::touchEventPropagation()
break;
}
case QEvent::MouseButtonPress:
case QEvent::MouseButtonDblClick:
mousePressReceiver = qobject_cast<QWidget*>(receiver);
break;
default:

View File

@ -432,6 +432,7 @@ private slots:
void touchUpdateOnNewTouch();
void touchCancel();
void touchEventsForGesturePendingWidgets();
void synthMouseDoubleClick();
void styleSheetPropagation();
@ -12257,7 +12258,24 @@ protected:
case QEvent::MouseMove:
case QEvent::MouseButtonRelease:
++m_mouseEventCount;
m_lastMouseEventPos = static_cast<QMouseEvent *>(e)->position();
{
QMouseEvent *me = static_cast<QMouseEvent *>(e);
m_lastMouseEventPos = me->position();
m_lastMouseTimestamp = me->timestamp();
}
if (m_acceptMouse)
e->accept();
else
e->ignore();
return true;
case QEvent::MouseButtonDblClick:
++m_mouseEventCount;
{
QMouseEvent *me = static_cast<QMouseEvent *>(e);
m_lastMouseEventPos = me->position();
m_doubleClickTimestamp = me->timestamp();
}
if (m_acceptMouse)
e->accept();
else
@ -12283,6 +12301,8 @@ public:
int m_mouseEventCount = 0;
bool m_acceptMouse = true;
QPointF m_lastMouseEventPos;
quint64 m_lastMouseTimestamp = 0;
quint64 m_doubleClickTimestamp = 0;
};
void tst_QWidget::touchEventSynthesizedMouseEvent()
@ -12524,6 +12544,50 @@ void tst_QWidget::touchEventsForGesturePendingWidgets()
QVERIFY(parent.m_gestureEventCount > 0);
}
void tst_QWidget::synthMouseDoubleClick()
{
TouchMouseWidget widget;
widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
widget.show();
QWindow* window = widget.windowHandle();
QVERIFY(QTest::qWaitForWindowExposed(window));
// tap once; move slightly from press to release
QPoint p(20, 20);
int expectedMouseEventCount = 0;
QTest::touchEvent(window, m_touchScreen).press(1, p, window);
QCOMPARE(widget.m_touchBeginCount, 0);
QCOMPARE(widget.m_mouseEventCount, ++expectedMouseEventCount);
QCOMPARE(widget.m_lastMouseEventPos.toPoint(), p);
quint64 mouseTimestamp = widget.m_lastMouseTimestamp;
p += {1, 0};
QTest::touchEvent(window, m_touchScreen).move(1, p, window);
QCOMPARE(widget.m_mouseEventCount, ++expectedMouseEventCount);
QCOMPARE(widget.m_lastMouseEventPos.toPoint(), p);
QCOMPARE_GT(widget.m_lastMouseTimestamp, mouseTimestamp);
mouseTimestamp = widget.m_lastMouseTimestamp;
QTest::touchEvent(window, m_touchScreen).release(1, p, window);
QCOMPARE(widget.m_mouseEventCount, ++expectedMouseEventCount);
QCOMPARE(widget.m_lastMouseEventPos.toPoint(), p);
QCOMPARE_GT(widget.m_lastMouseTimestamp, mouseTimestamp);
mouseTimestamp = widget.m_lastMouseTimestamp;
// tap again nearby: a double-click event should be synthesized
p += {0, 1};
QTest::touchEvent(window, m_touchScreen).press(2, p, window);
QCOMPARE(widget.m_touchBeginCount, 0);
QCOMPARE(widget.m_mouseEventCount, ++expectedMouseEventCount);
QCOMPARE(widget.m_lastMouseEventPos.toPoint(), p);
QCOMPARE_GT(widget.m_doubleClickTimestamp, mouseTimestamp);
mouseTimestamp = widget.m_doubleClickTimestamp;
QTest::touchEvent(window, m_touchScreen).release(2, p, window);
QCOMPARE(widget.m_mouseEventCount, ++expectedMouseEventCount);
QCOMPARE(widget.m_lastMouseEventPos.toPoint(), p);
QCOMPARE_GT(widget.m_lastMouseTimestamp, mouseTimestamp);
mouseTimestamp = widget.m_lastMouseTimestamp;
}
void tst_QWidget::styleSheetPropagation()
{
QTableView tw;