Fix excess enter events when showing modal dialog on button press
authorMiikka Heikkinen <miikka.heikkinen@digia.com>
Tue, 30 Oct 2012 13:54:43 +0000 (15:54 +0200)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Wed, 7 Nov 2012 07:44:38 +0000 (08:44 +0100)
QApplicationPrivate::leaveAfterRelease was not properly cleared when
mouse event handling was interrupted by a modal dialog, which caused
every mouse move over the modal dialog to trigger enter event to the
widget under cursor.

Fixed by clearing QApplicationPrivate::leaveAfterRelease if mouse event
without any buttons pressed is handled.

Task-number: QTBUG-27643
Change-Id: I4f31daa656bc643c88e5338282a671ae2077e255
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
Reviewed-by: Oliver Wolff <oliver.wolff@digia.com>
Reviewed-by: Samuel Rødal <samuel.rodal@digia.com>
src/widgets/kernel/qapplication.cpp
tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp

index efa5245..4a5fe0e 100644 (file)
@@ -2482,6 +2482,12 @@ bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event,
 
     bool widgetUnderMouse = QRectF(receiver->rect()).contains(event->localPos());
 
+    // Clear the obsolete leaveAfterRelease value, if mouse button has been released but
+    // leaveAfterRelease has not been updated.
+    // This happens e.g. when modal dialog or popup is shown as a response to button click.
+    if (leaveAfterRelease && !*buttonDown && !event->buttons())
+        leaveAfterRelease = 0;
+
     if (*buttonDown) {
         if (!graphicsWidget) {
             // Register the widget that shall receive a leave event
index 0340677..0d63682 100644 (file)
@@ -354,6 +354,7 @@ private slots:
     void syntheticEnterLeave();
     void taskQTBUG_4055_sendSyntheticEnterLeave();
     void underMouse();
+    void taskQTBUG_27643_enterEvents();
 #endif
     void windowFlags();
     void initialPosForDontShowOnScreenWidgets();
@@ -9854,6 +9855,99 @@ void tst_QWidget::underMouse()
     QCOMPARE(childWidget2.enters, 0);
     QCOMPARE(childWidget2.leaves, 0);
 }
+
+class EnterTestModalDialog : public QDialog
+{
+    Q_OBJECT
+public:
+    EnterTestModalDialog() : QDialog(), button(0)
+    {
+        setGeometry(100, 300, 150, 100);
+        button = new QPushButton(this);
+        button->setGeometry(10, 10, 50, 30);
+    }
+
+    QPushButton *button;
+};
+
+class EnterTestMainDialog : public QDialog
+{
+    Q_OBJECT
+public:
+    EnterTestMainDialog() : QDialog(), modal(0), enters(0) {}
+
+public slots:
+    void buttonPressed()
+    {
+        qApp->installEventFilter(this);
+        modal = new EnterTestModalDialog();
+        QTimer::singleShot(2000, modal, SLOT(close())); // Failsafe
+        QTimer::singleShot(100, this, SLOT(doMouseMoves()));
+        modal->exec();
+        delete modal;
+    }
+
+    void doMouseMoves()
+    {
+        QPoint point1(15, 15);
+        QPoint point2(15, 20);
+        QPoint point3(20, 20);
+        QWindow *window = modal->windowHandle();
+        QWindowSystemInterface::handleEnterEvent(window);
+        QTest::mouseMove(window, point1);
+        QTest::mouseMove(window, point2);
+        QTest::mouseMove(window, point3);
+        modal->close();
+    }
+
+    bool eventFilter(QObject *o, QEvent *e)
+    {
+        if (modal && modal->button && o == modal->button) {
+            switch (e->type()) {
+            case QEvent::Enter:
+                enters++;
+                break;
+            default:
+                break;
+            }
+        }
+        return QDialog::eventFilter(o, e);
+    }
+
+public:
+    EnterTestModalDialog *modal;
+    int enters;
+};
+
+// A modal dialog launched by clicking a button should not trigger excess enter events
+// when mousing over it.
+void tst_QWidget::taskQTBUG_27643_enterEvents()
+{
+    // Move the mouse cursor to a safe location so it won't interfere
+    QCursor::setPos(0,0);
+
+    EnterTestMainDialog dialog;
+    QPushButton button(&dialog);
+
+    connect(&button, SIGNAL(clicked()), &dialog, SLOT(buttonPressed()));
+
+    dialog.setGeometry(100, 100, 150, 100);
+    button.setGeometry(10, 10, 100, 50);
+    dialog.show();
+    QVERIFY(QTest::qWaitForWindowExposed(&dialog));
+
+    QWindow *window = dialog.windowHandle();
+    QPoint overButton(25, 25);
+
+    QWindowSystemInterface::handleEnterEvent(window);
+    QTest::mouseMove(window, overButton);
+    QTest::mouseClick(window, Qt::LeftButton, 0, overButton, 0);
+
+    // Modal dialog opened in EnterTestMainDialog::buttonPressed()...
+
+    // Must only register only single enter on modal dialog's button after all said and done
+    QCOMPARE(dialog.enters, 1);
+}
 #endif // QTEST_NO_CURSOR
 
 QTEST_MAIN(tst_QWidget)