From 536e1cccf15963b586f3112a0654ddc0d101fa69 Mon Sep 17 00:00:00 2001
From: Daniel d'Andrada <daniel.dandrada@canonical.com>
Date: Tue, 12 Nov 2013 11:47:32 -0200
Subject: [PATCH] Fix delivery of QEvents to QQuickItems

Make them go through QObject::event() and comply to filtering
from objects passsed to QObject::installEventFilter()

Task-number: QTBUG-32004
Change-Id: Ib6972e7f5e588bee986ae5f2d69aa6fccb58af95
Reviewed-by: J-P Nurmi <jpnurmi@digia.com>
Reviewed-by: Alan Alpert <aalpert@blackberry.com>
Reviewed-by: Frederik Gladhorn <frederik.gladhorn@digia.com>
---
 src/quick/items/qquickitem.cpp                | 176 +++++++-----------
 src/quick/items/qquickitem_p.h                |  10 -
 src/quick/items/qquickwindow.cpp              |  46 ++---
 .../quick/qquickwindow/tst_qquickwindow.cpp   |  97 ++++++++++
 4 files changed, 188 insertions(+), 141 deletions(-)

diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index c11bf904be..65322f6de4 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -4472,96 +4472,6 @@ void QQuickItemPrivate::deliverInputMethodEvent(QInputMethodEvent *e)
 }
 #endif // QT_NO_IM
 
-void QQuickItemPrivate::deliverFocusEvent(QFocusEvent *e)
-{
-    Q_Q(QQuickItem);
-
-    if (e->type() == QEvent::FocusIn) {
-        q->focusInEvent(e);
-    } else {
-        q->focusOutEvent(e);
-    }
-}
-
-void QQuickItemPrivate::deliverMouseEvent(QMouseEvent *e)
-{
-    Q_Q(QQuickItem);
-
-    Q_ASSERT(e->isAccepted());
-
-    switch (e->type()) {
-    default:
-        Q_ASSERT(!"Unknown event type");
-    case QEvent::MouseMove:
-        q->mouseMoveEvent(e);
-        break;
-    case QEvent::MouseButtonPress:
-        q->mousePressEvent(e);
-        break;
-    case QEvent::MouseButtonRelease:
-        q->mouseReleaseEvent(e);
-        break;
-    case QEvent::MouseButtonDblClick:
-        q->mouseDoubleClickEvent(e);
-        break;
-    }
-}
-
-#ifndef QT_NO_WHEELEVENT
-void QQuickItemPrivate::deliverWheelEvent(QWheelEvent *e)
-{
-    Q_Q(QQuickItem);
-    q->wheelEvent(e);
-}
-#endif
-
-void QQuickItemPrivate::deliverTouchEvent(QTouchEvent *e)
-{
-    Q_Q(QQuickItem);
-    q->touchEvent(e);
-}
-
-void QQuickItemPrivate::deliverHoverEvent(QHoverEvent *e)
-{
-    Q_Q(QQuickItem);
-    switch (e->type()) {
-    default:
-        Q_ASSERT(!"Unknown event type");
-    case QEvent::HoverEnter:
-        q->hoverEnterEvent(e);
-        break;
-    case QEvent::HoverLeave:
-        q->hoverLeaveEvent(e);
-        break;
-    case QEvent::HoverMove:
-        q->hoverMoveEvent(e);
-        break;
-    }
-}
-
-#ifndef QT_NO_DRAGANDDROP
-void QQuickItemPrivate::deliverDragEvent(QEvent *e)
-{
-    Q_Q(QQuickItem);
-    switch (e->type()) {
-    default:
-        Q_ASSERT(!"Unknown event type");
-    case QEvent::DragEnter:
-        q->dragEnterEvent(static_cast<QDragEnterEvent *>(e));
-        break;
-    case QEvent::DragLeave:
-        q->dragLeaveEvent(static_cast<QDragLeaveEvent *>(e));
-        break;
-    case QEvent::DragMove:
-        q->dragMoveEvent(static_cast<QDragMoveEvent *>(e));
-        break;
-    case QEvent::Drop:
-        q->dropEvent(static_cast<QDropEvent *>(e));
-        break;
-    }
-}
-#endif // QT_NO_DRAGANDDROP
-
 /*!
     Called when \a change occurs for this item.
 
@@ -6983,18 +6893,17 @@ QRectF QQuickItem::mapRectFromScene(const QRectF &rect) const
   */
 bool QQuickItem::event(QEvent *ev)
 {
+    Q_D(QQuickItem);
+
+    switch (ev->type()) {
 #if 0
-    if (ev->type() == QEvent::PolishRequest) {
-        Q_D(QQuickItem);
+    case QEvent::PolishRequest:
         d->polishScheduled = false;
         updatePolish();
-        return true;
-    } else {
-        return QObject::event(ev);
-    }
+        break;
 #endif
 #ifndef QT_NO_IM
-    if (ev->type() == QEvent::InputMethodQuery) {
+    case QEvent::InputMethodQuery: {
         QInputMethodQueryEvent *query = static_cast<QInputMethodQueryEvent *>(ev);
         Qt::InputMethodQueries queries = query->queries();
         for (uint i = 0; i < 32; ++i) {
@@ -7005,17 +6914,76 @@ bool QQuickItem::event(QEvent *ev)
             }
         }
         query->accept();
-        return true;
-    } else if (ev->type() == QEvent::InputMethod) {
+        break;
+    }
+    case QEvent::InputMethod:
         inputMethodEvent(static_cast<QInputMethodEvent *>(ev));
-        return true;
-    } else
+        break;
 #endif // QT_NO_IM
-    if (ev->type() == QEvent::StyleAnimationUpdate) {
+    case QEvent::TouchBegin:
+    case QEvent::TouchUpdate:
+    case QEvent::TouchEnd:
+    case QEvent::TouchCancel:
+        touchEvent(static_cast<QTouchEvent*>(ev));
+        break;
+    case QEvent::StyleAnimationUpdate:
         update();
-        return true;
+        break;
+    case QEvent::HoverEnter:
+        hoverEnterEvent(static_cast<QHoverEvent*>(ev));
+        break;
+    case QEvent::HoverLeave:
+        hoverLeaveEvent(static_cast<QHoverEvent*>(ev));
+        break;
+    case QEvent::HoverMove:
+        hoverMoveEvent(static_cast<QHoverEvent*>(ev));
+        break;
+    case QEvent::KeyPress:
+    case QEvent::KeyRelease:
+        d->deliverKeyEvent(static_cast<QKeyEvent*>(ev));
+        break;
+    case QEvent::FocusIn:
+        focusInEvent(static_cast<QFocusEvent*>(ev));
+        break;
+    case QEvent::FocusOut:
+        focusOutEvent(static_cast<QFocusEvent*>(ev));
+        break;
+    case QEvent::MouseMove:
+        mouseMoveEvent(static_cast<QMouseEvent*>(ev));
+        break;
+    case QEvent::MouseButtonPress:
+        mousePressEvent(static_cast<QMouseEvent*>(ev));
+        break;
+    case QEvent::MouseButtonRelease:
+        mouseReleaseEvent(static_cast<QMouseEvent*>(ev));
+        break;
+    case QEvent::MouseButtonDblClick:
+        mouseDoubleClickEvent(static_cast<QMouseEvent*>(ev));
+        break;
+#ifndef QT_NO_WHEELEVENT
+    case QEvent::Wheel:
+        wheelEvent(static_cast<QWheelEvent*>(ev));
+        break;
+#endif
+#ifndef QT_NO_DRAGANDDROP
+    case QEvent::DragEnter:
+        dragEnterEvent(static_cast<QDragEnterEvent*>(ev));
+        break;
+    case QEvent::DragLeave:
+        dragLeaveEvent(static_cast<QDragLeaveEvent*>(ev));
+        break;
+    case QEvent::DragMove:
+        dragMoveEvent(static_cast<QDragMoveEvent*>(ev));
+        break;
+    case QEvent::Drop:
+        dropEvent(static_cast<QDropEvent*>(ev));
+        break;
+#endif // QT_NO_DRAGANDDROP
+    default:
+        return QObject::event(ev);
     }
-    return QObject::event(ev);
+
+    return true;
 }
 
 #ifndef QT_NO_DEBUG_STREAM
diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h
index 7faf39f8e5..ccf949ee8b 100644
--- a/src/quick/items/qquickitem_p.h
+++ b/src/quick/items/qquickitem_p.h
@@ -540,16 +540,6 @@ public:
 #ifndef QT_NO_IM
     void deliverInputMethodEvent(QInputMethodEvent *);
 #endif
-    void deliverFocusEvent(QFocusEvent *);
-    void deliverMouseEvent(QMouseEvent *);
-#ifndef QT_NO_WHEELEVENT
-    void deliverWheelEvent(QWheelEvent *);
-#endif
-    void deliverTouchEvent(QTouchEvent *);
-    void deliverHoverEvent(QHoverEvent *);
-#ifndef QT_NO_DRAGANDDROP
-    void deliverDragEvent(QEvent *);
-#endif
 
     bool calcEffectiveVisible() const;
     bool setEffectiveVisibleRecur(bool);
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index a993889c45..10906dd64c 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -524,7 +524,7 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e
                 item->grabMouse();
             item->grabTouchPoints(QVector<int>() << touchMouseId);
 
-            QQuickItemPrivate::get(item)->deliverMouseEvent(mousePress.data());
+            QCoreApplication::sendEvent(item, mousePress.data());
             event->setAccepted(mousePress->isAccepted());
             if (!mousePress->isAccepted()) {
                 touchMouseId = -1;
@@ -537,7 +537,7 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e
 
             if (mousePress->isAccepted() && checkIfDoubleClicked(event->timestamp())) {
                 QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item));
-                QQuickItemPrivate::get(item)->deliverMouseEvent(mouseDoubleClick.data());
+                QCoreApplication::sendEvent(item, mouseDoubleClick.data());
                 event->setAccepted(mouseDoubleClick->isAccepted());
                 if (mouseDoubleClick->isAccepted()) {
                     return true;
@@ -558,7 +558,7 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e
             if (p.state() & Qt::TouchPointMoved) {
                 if (mouseGrabberItem) {
                     QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, mouseGrabberItem));
-                    QQuickItemPrivate::get(item)->deliverMouseEvent(me.data());
+                    QCoreApplication::sendEvent(item, me.data());
                     event->setAccepted(me->isAccepted());
                     if (me->isAccepted()) {
                         itemForTouchPointId[p.id()] = mouseGrabberItem; // N.B. the mouseGrabberItem may be different after returning from sendEvent()
@@ -588,7 +588,7 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e
                 touchMouseId = -1;
                 if (mouseGrabberItem) {
                     QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseButtonRelease, p, event, mouseGrabberItem));
-                    QQuickItemPrivate::get(item)->deliverMouseEvent(me.data());
+                    QCoreApplication::sendEvent(item, me.data());
                     if (mouseGrabberItem) // might have ungrabbed due to event
                         mouseGrabberItem->ungrabMouse();
                     return me->isAccepted();
@@ -1807,11 +1807,11 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QTouchEv
         itemForTouchPointId[id] = item;
 
     // Deliver the touch event to the given item
-    QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
-    itemPrivate->deliverTouchEvent(touchEvent.data());
+    QCoreApplication::sendEvent(item, touchEvent.data());
     touchEventAccepted = touchEvent->isAccepted();
 
     // If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it.
+    QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
     if (!touchEventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) {
         //  send mouse event
         event->setAccepted(translateTouchToMouse(item, event));
@@ -2229,16 +2229,12 @@ bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e)
     case QEvent::KeyPress:
     case QEvent::KeyRelease:
         e->accept();
-        QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
+        QCoreApplication::sendEvent(item, e);
         while (!e->isAccepted() && (item = item->parentItem())) {
             e->accept();
-            QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
+            QCoreApplication::sendEvent(item, e);
         }
         break;
-    case QEvent::FocusIn:
-    case QEvent::FocusOut:
-        QQuickItemPrivate::get(item)->deliverFocusEvent(static_cast<QFocusEvent *>(e));
-        break;
     case QEvent::MouseButtonPress:
     case QEvent::MouseButtonRelease:
     case QEvent::MouseButtonDblClick:
@@ -2247,7 +2243,7 @@ bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e)
         if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
             // accept because qml items by default accept and have to explicitly opt out of accepting
             e->accept();
-            QQuickItemPrivate::get(item)->deliverMouseEvent(static_cast<QMouseEvent *>(e));
+            QCoreApplication::sendEvent(item, e);
         }
         break;
     case QEvent::UngrabMouse:
@@ -2258,30 +2254,26 @@ bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e)
         break;
 #ifndef QT_NO_WHEELEVENT
     case QEvent::Wheel:
-        QQuickItemPrivate::get(item)->deliverWheelEvent(static_cast<QWheelEvent *>(e));
-        break;
 #endif
+#ifndef QT_NO_DRAGANDDROP
+    case QEvent::DragEnter:
+    case QEvent::DragMove:
+    case QEvent::DragLeave:
+    case QEvent::Drop:
+#endif
+    case QEvent::FocusIn:
+    case QEvent::FocusOut:
     case QEvent::HoverEnter:
     case QEvent::HoverLeave:
     case QEvent::HoverMove:
-        QQuickItemPrivate::get(item)->deliverHoverEvent(static_cast<QHoverEvent *>(e));
+    case QEvent::TouchCancel:
+        QCoreApplication::sendEvent(item, e);
         break;
     case QEvent::TouchBegin:
     case QEvent::TouchUpdate:
     case QEvent::TouchEnd:
         d->sendFilteredTouchEvent(item->parentItem(), item, static_cast<QTouchEvent *>(e));
         break;
-    case QEvent::TouchCancel:
-        QQuickItemPrivate::get(item)->deliverTouchEvent(static_cast<QTouchEvent *>(e));
-        break;
-#ifndef QT_NO_DRAGANDDROP
-    case QEvent::DragEnter:
-    case QEvent::DragMove:
-    case QEvent::DragLeave:
-    case QEvent::Drop:
-        QQuickItemPrivate::get(item)->deliverDragEvent(e);
-        break;
-#endif
     default:
         break;
     }
diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
index 984881c8da..1d9beedbab 100644
--- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
+++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
@@ -250,6 +250,18 @@ int TestTouchItem::mousePressNum = 0;
 int TestTouchItem::mouseMoveNum = 0;
 int TestTouchItem::mouseReleaseNum = 0;
 
+class EventFilter : public QObject
+{
+public:
+    bool eventFilter(QObject *watched, QEvent *event) {
+        Q_UNUSED(watched);
+        events.append(event->type());
+        return false;
+    }
+
+    QList<int> events;
+};
+
 class ConstantUpdateItem : public QQuickItem
 {
 Q_OBJECT
@@ -330,6 +342,10 @@ private slots:
 
     void crashWhenHoverItemDeleted();
 
+    void qobjectEventFilter_touch();
+    void qobjectEventFilter_key();
+    void qobjectEventFilter_mouse();
+
 #ifndef QT_NO_CURSOR
     void cursor();
 #endif
@@ -922,6 +938,9 @@ void tst_qquickwindow::mouseFiltering()
     QTRY_COMPARE(middleItem->mousePressId, 1);
     QTRY_COMPARE(bottomItem->mousePressId, 2);
     QTRY_COMPARE(topItem->mousePressId, 3);
+
+    // clean up mouse press state for the next tests
+    QTest::mouseRelease(window, Qt::LeftButton, 0, pos);
 }
 
 void tst_qquickwindow::qmlCreation()
@@ -1525,6 +1544,84 @@ void tst_qquickwindow::crashWhenHoverItemDeleted()
     }
 }
 
+// QTBUG-32004
+void tst_qquickwindow::qobjectEventFilter_touch()
+{
+    QQuickWindow window;
+
+    window.resize(250, 250);
+    window.setPosition(100, 100);
+    window.show();
+    QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+    TestTouchItem *item = new TestTouchItem(window.contentItem());
+    item->setSize(QSizeF(150, 150));
+
+    EventFilter eventFilter;
+    item->installEventFilter(&eventFilter);
+
+    QPointF pos(10, 10);
+
+    // press single point
+    QTest::touchEvent(&window, touchDevice).press(0, item->mapToScene(pos).toPoint(), &window);
+
+    QCOMPARE(eventFilter.events.count(), 1);
+    QCOMPARE(eventFilter.events.first(), (int)QEvent::TouchBegin);
+}
+
+// QTBUG-32004
+void tst_qquickwindow::qobjectEventFilter_key()
+{
+    QQuickWindow window;
+
+    window.resize(250, 250);
+    window.setPosition(100, 100);
+    window.show();
+    QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+    TestTouchItem *item = new TestTouchItem(window.contentItem());
+    item->setSize(QSizeF(150, 150));
+    item->setFocus(true);
+
+    EventFilter eventFilter;
+    item->installEventFilter(&eventFilter);
+
+    QTest::keyPress(&window, Qt::Key_A);
+
+    // NB: It may also receive some QKeyEvent(ShortcutOverride) which we're not interested in
+    QVERIFY(eventFilter.events.contains((int)QEvent::KeyPress));
+    eventFilter.events.clear();
+
+    QTest::keyRelease(&window, Qt::Key_A);
+
+    QVERIFY(eventFilter.events.contains((int)QEvent::KeyRelease));
+}
+
+// QTBUG-32004
+void tst_qquickwindow::qobjectEventFilter_mouse()
+{
+    QQuickWindow window;
+
+    window.resize(250, 250);
+    window.setPosition(100, 100);
+    window.show();
+    QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+    TestTouchItem *item = new TestTouchItem(window.contentItem());
+    item->setSize(QSizeF(150, 150));
+
+    EventFilter eventFilter;
+    item->installEventFilter(&eventFilter);
+
+    QPoint point = item->mapToScene(QPointF(10, 10)).toPoint();
+    QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, point);
+
+    QVERIFY(eventFilter.events.contains((int)QEvent::MouseButtonPress));
+
+    // clean up mouse press state for the next tests
+    QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, point);
+}
+
 QTEST_MAIN(tst_qquickwindow)
 
 #include "tst_qquickwindow.moc"
-- 
GitLab