summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Ottens <kevin.ottens.qnx@kdab.com>2012-07-17 17:18:25 +0200
committerQt by Nokia <qt-info@nokia.com>2012-07-24 10:46:24 +0200
commit7808ec795c5831d56dae9c4f9f7e1306489864aa (patch)
tree1e1aaf184be8e83423ac168704818cc811aa687c
parent349f16b03cb75c5284022f957c122446ebfc0b79 (diff)
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>
-rw-r--r--src/widgets/kernel/qapplication.cpp90
-rw-r--r--src/widgets/kernel/qapplication_p.h1
-rw-r--r--tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp26
-rw-r--r--tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp160
4 files changed, 264 insertions, 13 deletions
diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp
index 6634a2eb69..08cd47037b 100644
--- a/src/widgets/kernel/qapplication.cpp
+++ b/src/widgets/kernel/qapplication.cpp
@@ -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,
diff --git a/src/widgets/kernel/qapplication_p.h b/src/widgets/kernel/qapplication_p.h
index 463cccb4f9..e53896f82c 100644
--- a/src/widgets/kernel/qapplication_p.h
+++ b/src/widgets/kernel/qapplication_p.h
@@ -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,
diff --git a/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp b/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp
index aebe416ed6..610c0bcba2 100644
--- a/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp
+++ b/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp
@@ -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);
}
}
diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp
index c670c5bf72..c9d8b3162e 100644
--- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp
+++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp
@@ -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"