diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index 1630103afe0..9ef61ef4fa8 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -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); } diff --git a/src/gui/kernel/qwindowsysteminterface_p.h b/src/gui/kernel/qwindowsysteminterface_p.h index 51ab58fc99d..677a5ed6281 100644 --- a/src/gui/kernel/qwindowsysteminterface_p.h +++ b/src/gui/kernel/qwindowsysteminterface_p.h @@ -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 { diff --git a/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp index ee1a8c530aa..c3f77957f82 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp @@ -3872,6 +3872,7 @@ void tst_QGraphicsProxyWidget::touchEventPropagation() break; } case QEvent::MouseButtonPress: + case QEvent::MouseButtonDblClick: mousePressReceiver = qobject_cast(receiver); break; default: diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp index 666cb70da99..cac968c5dd7 100644 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp +++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp @@ -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(e)->position(); + { + QMouseEvent *me = static_cast(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(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;