Propagate synthesized mouse events in parallel (lock-step) with touch
This patch implement the equivalent of 468626e99a90d6ac21cb311cde05c658ccb3b781 in qtdeclarative but for QtWidgets. If a widget doesn't accept a touch event, then QApplication gives it another try by synthesizing a corresponding mouse event. This way QtQuick and QtWidget behave in a similar way, removing the need for platform backends to try to emulate a mouse event from a touch event unconditionally. Also add relevant unit tests and adjust old QApplication ones. Change-Id: Iddbf6d756c4b52931a9d1c314b50d7a31dbcdee9 Reviewed-by: Marc Mutz <marc.mutz@kdab.com> Reviewed-by: Sean Harmer <sean.harmer@kdab.com> Reviewed-by: Kevin Krammer <kevin.krammer@kdab.com> Reviewed-by: Friedemann Kleint <Friedemann.Kleint@nokia.com>
This commit is contained in:
parent
349f16b03c
commit
7808ec795c
@ -3412,6 +3412,25 @@ bool QApplication::notify(QObject *receiver, QEvent *e)
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case QEvent::TouchUpdate:
|
||||
case QEvent::TouchEnd:
|
||||
{
|
||||
QWidget *widget = static_cast<QWidget *>(receiver);
|
||||
QTouchEvent *touchEvent = static_cast<QTouchEvent *>(e);
|
||||
const bool acceptTouchEvents = widget->testAttribute(Qt::WA_AcceptTouchEvents);
|
||||
|
||||
touchEvent->setTarget(widget);
|
||||
touchEvent->setAccepted(acceptTouchEvents);
|
||||
|
||||
res = acceptTouchEvents && d->notify_helper(widget, touchEvent);
|
||||
|
||||
// If the touch event wasn't accepted, synthesize a mouse event and see if the widget wants it.
|
||||
if (!touchEvent->isAccepted())
|
||||
res = d->translateTouchToMouse(widget, touchEvent);
|
||||
break;
|
||||
}
|
||||
|
||||
case QEvent::TouchBegin:
|
||||
// Note: TouchUpdate and TouchEnd events are never propagated
|
||||
{
|
||||
@ -3432,6 +3451,15 @@ bool QApplication::notify(QObject *receiver, QEvent *e)
|
||||
touchEvent->setAccepted(acceptTouchEvents);
|
||||
QPointer<QWidget> p = widget;
|
||||
res = acceptTouchEvents && d->notify_helper(widget, touchEvent);
|
||||
|
||||
// If the touch event wasn't accepted, synthesize a mouse event and see if the widget wants it.
|
||||
if (!touchEvent->isAccepted()) {
|
||||
res = d->translateTouchToMouse(widget, touchEvent);
|
||||
eventAccepted = touchEvent->isAccepted();
|
||||
if (eventAccepted)
|
||||
break;
|
||||
}
|
||||
|
||||
eventAccepted = touchEvent->isAccepted();
|
||||
if (p.isNull()) {
|
||||
// widget was deleted
|
||||
@ -4357,6 +4385,68 @@ QWidget *QApplicationPrivate::findClosestTouchPointTarget(QTouchDevice *device,
|
||||
return static_cast<QWidget *>(closestTarget);
|
||||
}
|
||||
|
||||
class WidgetAttributeSaver
|
||||
{
|
||||
public:
|
||||
explicit WidgetAttributeSaver(QWidget *widget, Qt::WidgetAttribute attribute, bool forcedValue)
|
||||
: m_widget(widget),
|
||||
m_attribute(attribute),
|
||||
m_savedValue(widget->testAttribute(attribute))
|
||||
{
|
||||
widget->setAttribute(attribute, forcedValue);
|
||||
}
|
||||
|
||||
~WidgetAttributeSaver()
|
||||
{
|
||||
m_widget->setAttribute(m_attribute, m_savedValue);
|
||||
}
|
||||
|
||||
private:
|
||||
QWidget * const m_widget;
|
||||
const Qt::WidgetAttribute m_attribute;
|
||||
const bool m_savedValue;
|
||||
};
|
||||
|
||||
bool QApplicationPrivate::translateTouchToMouse(QWidget *widget, QTouchEvent *event)
|
||||
{
|
||||
Q_Q(QApplication);
|
||||
|
||||
Q_FOREACH (const QTouchEvent::TouchPoint &p, event->touchPoints()) {
|
||||
const QEvent::Type eventType = (p.state() & Qt::TouchPointPressed) ? QEvent::MouseButtonPress
|
||||
: (p.state() & Qt::TouchPointReleased) ? QEvent::MouseButtonRelease
|
||||
: (p.state() & Qt::TouchPointMoved) ? QEvent::MouseMove
|
||||
: QEvent::None;
|
||||
|
||||
if (eventType == QEvent::None)
|
||||
continue;
|
||||
|
||||
const QPoint pos = widget->mapFromGlobal(p.scenePos().toPoint());
|
||||
|
||||
QMouseEvent mouseEvent(eventType, pos,
|
||||
Qt::LeftButton, Qt::LeftButton,
|
||||
event->modifiers());
|
||||
mouseEvent.setAccepted(true);
|
||||
mouseEvent.setTimestamp(event->timestamp());
|
||||
|
||||
// Make sure our synthesized mouse event doesn't propagate
|
||||
// we want to control the propagation ourself to get a chance to
|
||||
// deliver a proper touch event higher up in the hierarchy if that
|
||||
// widget doesn't pick up the mouse event either.
|
||||
WidgetAttributeSaver saver(widget, Qt::WA_NoMousePropagation, true);
|
||||
|
||||
// Note it has to be a spontaneous event if we want the focus management
|
||||
// and input method support to behave properly. Quite some of the code
|
||||
// related to those aspect check for the spontaneous flag.
|
||||
const bool res = q->sendSpontaneousEvent(widget, &mouseEvent);
|
||||
event->setAccepted(mouseEvent.isAccepted());
|
||||
|
||||
if (mouseEvent.isAccepted())
|
||||
return res;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void QApplicationPrivate::translateRawTouchEvent(QWidget *window,
|
||||
QTouchDevice *device,
|
||||
const QList<QTouchEvent::TouchPoint> &touchPoints,
|
||||
|
@ -290,6 +290,7 @@ public:
|
||||
QWidget *findClosestTouchPointTarget(QTouchDevice *device, const QPointF &screenPos);
|
||||
void appendTouchPoint(const QTouchEvent::TouchPoint &touchPoint);
|
||||
void removeTouchPoint(int touchPointId);
|
||||
bool translateTouchToMouse(QWidget *widget, QTouchEvent *event);
|
||||
static void translateRawTouchEvent(QWidget *widget,
|
||||
QTouchDevice *device,
|
||||
const QList<QTouchEvent::TouchPoint> &touchPoints,
|
||||
|
@ -1933,7 +1933,7 @@ void tst_QApplication::touchEventPropagation()
|
||||
QTest::QTouchEventSequence::touchPointList(releasedTouchPoints));
|
||||
QCoreApplication::processEvents();
|
||||
QVERIFY(!window.seenTouchEvent);
|
||||
QVERIFY(!window.seenMouseEvent);
|
||||
QVERIFY(window.seenMouseEvent); // Since QApplication transforms ignored touch events in mouse events
|
||||
|
||||
window.reset();
|
||||
window.setAttribute(Qt::WA_AcceptTouchEvents);
|
||||
@ -1947,7 +1947,7 @@ void tst_QApplication::touchEventPropagation()
|
||||
QTest::QTouchEventSequence::touchPointList(releasedTouchPoints));
|
||||
QCoreApplication::processEvents();
|
||||
QVERIFY(window.seenTouchEvent);
|
||||
QVERIFY(!window.seenMouseEvent);
|
||||
QVERIFY(window.seenMouseEvent);
|
||||
|
||||
window.reset();
|
||||
window.acceptTouchEvent = true;
|
||||
@ -1985,9 +1985,9 @@ void tst_QApplication::touchEventPropagation()
|
||||
QTest::QTouchEventSequence::touchPointList(releasedTouchPoints));
|
||||
QCoreApplication::processEvents();
|
||||
QVERIFY(!widget.seenTouchEvent);
|
||||
QVERIFY(!widget.seenMouseEvent);
|
||||
QVERIFY(widget.seenMouseEvent);
|
||||
QVERIFY(!window.seenTouchEvent);
|
||||
QVERIFY(!window.seenMouseEvent);
|
||||
QVERIFY(window.seenMouseEvent);
|
||||
|
||||
window.reset();
|
||||
widget.reset();
|
||||
@ -2002,9 +2002,9 @@ void tst_QApplication::touchEventPropagation()
|
||||
QTest::QTouchEventSequence::touchPointList(releasedTouchPoints));
|
||||
QCoreApplication::processEvents();
|
||||
QVERIFY(widget.seenTouchEvent);
|
||||
QVERIFY(!widget.seenMouseEvent);
|
||||
QVERIFY(widget.seenMouseEvent);
|
||||
QVERIFY(!window.seenTouchEvent);
|
||||
QVERIFY(!window.seenMouseEvent);
|
||||
QVERIFY(window.seenMouseEvent);
|
||||
|
||||
window.reset();
|
||||
widget.reset();
|
||||
@ -2019,7 +2019,7 @@ void tst_QApplication::touchEventPropagation()
|
||||
QTest::QTouchEventSequence::touchPointList(releasedTouchPoints));
|
||||
QCoreApplication::processEvents();
|
||||
QVERIFY(widget.seenTouchEvent);
|
||||
QVERIFY(!widget.seenMouseEvent);
|
||||
QVERIFY(widget.seenMouseEvent);
|
||||
QVERIFY(!window.seenTouchEvent);
|
||||
QVERIFY(!window.seenMouseEvent);
|
||||
|
||||
@ -2054,9 +2054,9 @@ void tst_QApplication::touchEventPropagation()
|
||||
QTest::QTouchEventSequence::touchPointList(releasedTouchPoints));
|
||||
QCoreApplication::processEvents();
|
||||
QVERIFY(!widget.seenTouchEvent);
|
||||
QVERIFY(!widget.seenMouseEvent);
|
||||
QVERIFY(widget.seenMouseEvent);
|
||||
QVERIFY(window.seenTouchEvent);
|
||||
QVERIFY(!window.seenMouseEvent);
|
||||
QVERIFY(window.seenMouseEvent);
|
||||
|
||||
window.reset();
|
||||
widget.reset();
|
||||
@ -2071,13 +2071,13 @@ void tst_QApplication::touchEventPropagation()
|
||||
QTest::QTouchEventSequence::touchPointList(releasedTouchPoints));
|
||||
QCoreApplication::processEvents();
|
||||
QVERIFY(!widget.seenTouchEvent);
|
||||
QVERIFY(!widget.seenMouseEvent);
|
||||
QVERIFY(widget.seenMouseEvent);
|
||||
QVERIFY(window.seenTouchEvent);
|
||||
QVERIFY(!window.seenMouseEvent);
|
||||
|
||||
window.reset();
|
||||
widget.reset();
|
||||
widget.acceptMouseEvent = true; // doesn't matter, touch events are propagated first
|
||||
widget.acceptMouseEvent = true; // it matters, touch events are propagated in parallel to synthesized mouse events
|
||||
window.acceptTouchEvent = true;
|
||||
QWindowSystemInterface::handleTouchEvent(window.windowHandle(),
|
||||
0,
|
||||
@ -2089,8 +2089,8 @@ void tst_QApplication::touchEventPropagation()
|
||||
QTest::QTouchEventSequence::touchPointList(releasedTouchPoints));
|
||||
QCoreApplication::processEvents();
|
||||
QVERIFY(!widget.seenTouchEvent);
|
||||
QVERIFY(!widget.seenMouseEvent);
|
||||
QVERIFY(window.seenTouchEvent);
|
||||
QVERIFY(widget.seenMouseEvent);
|
||||
QVERIFY(!window.seenTouchEvent);
|
||||
QVERIFY(!window.seenMouseEvent);
|
||||
}
|
||||
}
|
||||
|
@ -385,6 +385,8 @@ private slots:
|
||||
void nativeChildFocus();
|
||||
void grab();
|
||||
|
||||
void touchEventSynthesizedMouseEvent();
|
||||
|
||||
private:
|
||||
bool ensureScreenSize(int width, int height);
|
||||
QWidget *testWidget;
|
||||
@ -9284,5 +9286,163 @@ void tst_QWidget::grab()
|
||||
}
|
||||
}
|
||||
|
||||
class TouchMouseWidget : public QWidget {
|
||||
public:
|
||||
explicit TouchMouseWidget(QWidget *parent = 0)
|
||||
: QWidget(parent),
|
||||
m_touchEventCount(0),
|
||||
m_acceptTouch(false),
|
||||
m_mouseEventCount(0),
|
||||
m_acceptMouse(true)
|
||||
{
|
||||
resize(200, 200);
|
||||
}
|
||||
|
||||
void setAcceptTouch(bool accept)
|
||||
{
|
||||
m_acceptTouch = accept;
|
||||
setAttribute(Qt::WA_AcceptTouchEvents, accept);
|
||||
}
|
||||
|
||||
void setAcceptMouse(bool accept)
|
||||
{
|
||||
m_acceptMouse = accept;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool event(QEvent *e)
|
||||
{
|
||||
switch (e->type()) {
|
||||
case QEvent::TouchBegin:
|
||||
case QEvent::TouchUpdate:
|
||||
case QEvent::TouchEnd:
|
||||
++m_touchEventCount;
|
||||
if (m_acceptTouch)
|
||||
e->accept();
|
||||
else
|
||||
e->ignore();
|
||||
return true;
|
||||
|
||||
case QEvent::MouseButtonPress:
|
||||
case QEvent::MouseMove:
|
||||
case QEvent::MouseButtonRelease:
|
||||
++m_mouseEventCount;
|
||||
if (m_acceptMouse)
|
||||
e->accept();
|
||||
else
|
||||
e->ignore();
|
||||
return true;
|
||||
|
||||
default:
|
||||
return QWidget::event(e);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
int m_touchEventCount;
|
||||
bool m_acceptTouch;
|
||||
int m_mouseEventCount;
|
||||
bool m_acceptMouse;
|
||||
};
|
||||
|
||||
void tst_QWidget::touchEventSynthesizedMouseEvent()
|
||||
{
|
||||
{
|
||||
// Simple case, we ignore the touch events, we get mouse events instead
|
||||
QTouchDevice *device = new QTouchDevice;
|
||||
device->setType(QTouchDevice::TouchScreen);
|
||||
QWindowSystemInterface::registerTouchDevice(device);
|
||||
|
||||
TouchMouseWidget widget;
|
||||
widget.show();
|
||||
QVERIFY(QTest::qWaitForWindowExposed(widget.windowHandle()));
|
||||
QCOMPARE(widget.m_touchEventCount, 0);
|
||||
QCOMPARE(widget.m_mouseEventCount, 0);
|
||||
|
||||
QTest::touchEvent(&widget, device).press(0, QPoint(10, 10), &widget);
|
||||
QCOMPARE(widget.m_touchEventCount, 0);
|
||||
QCOMPARE(widget.m_mouseEventCount, 1);
|
||||
QTest::touchEvent(&widget, device).move(0, QPoint(15, 15), &widget);
|
||||
QCOMPARE(widget.m_touchEventCount, 0);
|
||||
QCOMPARE(widget.m_mouseEventCount, 2);
|
||||
QTest::touchEvent(&widget, device).release(0, QPoint(20, 20), &widget);
|
||||
QCOMPARE(widget.m_touchEventCount, 0);
|
||||
QCOMPARE(widget.m_mouseEventCount, 3);
|
||||
}
|
||||
|
||||
{
|
||||
// We accept the touch events, no mouse event is generated
|
||||
QTouchDevice *device = new QTouchDevice;
|
||||
device->setType(QTouchDevice::TouchScreen);
|
||||
QWindowSystemInterface::registerTouchDevice(device);
|
||||
|
||||
TouchMouseWidget widget;
|
||||
widget.setAcceptTouch(true);
|
||||
widget.show();
|
||||
QVERIFY(QTest::qWaitForWindowExposed(widget.windowHandle()));
|
||||
QCOMPARE(widget.m_touchEventCount, 0);
|
||||
QCOMPARE(widget.m_mouseEventCount, 0);
|
||||
|
||||
QTest::touchEvent(&widget, device).press(0, QPoint(10, 10), &widget);
|
||||
QCOMPARE(widget.m_touchEventCount, 1);
|
||||
QCOMPARE(widget.m_mouseEventCount, 0);
|
||||
QTest::touchEvent(&widget, device).move(0, QPoint(15, 15), &widget);
|
||||
QCOMPARE(widget.m_touchEventCount, 2);
|
||||
QCOMPARE(widget.m_mouseEventCount, 0);
|
||||
QTest::touchEvent(&widget, device).release(0, QPoint(20, 20), &widget);
|
||||
QCOMPARE(widget.m_touchEventCount, 3);
|
||||
QCOMPARE(widget.m_mouseEventCount, 0);
|
||||
}
|
||||
|
||||
{
|
||||
// Parent accepts touch events, child ignore both mouse and touch
|
||||
// We should see propagation of the TouchBegin
|
||||
QTouchDevice *device = new QTouchDevice;
|
||||
device->setType(QTouchDevice::TouchScreen);
|
||||
QWindowSystemInterface::registerTouchDevice(device);
|
||||
|
||||
TouchMouseWidget parent;
|
||||
parent.setAcceptTouch(true);
|
||||
TouchMouseWidget child(&parent);
|
||||
child.setAcceptMouse(false);
|
||||
parent.show();
|
||||
QVERIFY(QTest::qWaitForWindowExposed(parent.windowHandle()));
|
||||
QCOMPARE(parent.m_touchEventCount, 0);
|
||||
QCOMPARE(parent.m_mouseEventCount, 0);
|
||||
QCOMPARE(child.m_touchEventCount, 0);
|
||||
QCOMPARE(child.m_mouseEventCount, 0);
|
||||
|
||||
QTest::touchEvent(parent.window(), device).press(0, QPoint(10, 10), &child);
|
||||
QCOMPARE(parent.m_touchEventCount, 1);
|
||||
QCOMPARE(parent.m_mouseEventCount, 0);
|
||||
QCOMPARE(child.m_touchEventCount, 0);
|
||||
QCOMPARE(child.m_mouseEventCount, 1); // Attempt at mouse event before propagation
|
||||
}
|
||||
|
||||
{
|
||||
// Parent accepts mouse events, child ignore both mouse and touch
|
||||
// We should see propagation of the TouchBegin into a MouseButtonPress
|
||||
QTouchDevice *device = new QTouchDevice;
|
||||
device->setType(QTouchDevice::TouchScreen);
|
||||
QWindowSystemInterface::registerTouchDevice(device);
|
||||
|
||||
TouchMouseWidget parent;
|
||||
TouchMouseWidget child(&parent);
|
||||
child.setAcceptMouse(false);
|
||||
parent.show();
|
||||
QVERIFY(QTest::qWaitForWindowExposed(parent.windowHandle()));
|
||||
QCOMPARE(parent.m_touchEventCount, 0);
|
||||
QCOMPARE(parent.m_mouseEventCount, 0);
|
||||
QCOMPARE(child.m_touchEventCount, 0);
|
||||
QCOMPARE(child.m_mouseEventCount, 0);
|
||||
|
||||
QTest::touchEvent(parent.window(), device).press(0, QPoint(10, 10), &child);
|
||||
QCOMPARE(parent.m_touchEventCount, 0);
|
||||
QCOMPARE(parent.m_mouseEventCount, 1);
|
||||
QCOMPARE(child.m_touchEventCount, 0);
|
||||
QCOMPARE(child.m_mouseEventCount, 1); // Attempt at mouse event before propagation
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QWidget)
|
||||
#include "tst_qwidget.moc"
|
||||
|
Loading…
Reference in New Issue
Block a user