From dde3f6a934ae26c8e92b0132b1bb85c0aa841ac1 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 30 Oct 2012 15:54:43 +0200 Subject: [PATCH] Fix excess enter events when showing modal dialog on button press MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Oliver Wolff Reviewed-by: Samuel Rødal --- src/widgets/kernel/qapplication.cpp | 6 ++ tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp | 94 +++++++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index efa5245..4a5fe0e 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -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 diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp index 0340677..0d63682 100644 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp +++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp @@ -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) -- 2.7.4