summaryrefslogtreecommitdiffstats
path: root/tests/auto/widgets/kernel/qwidget
diff options
context:
space:
mode:
authorVolker Hilsheimer <volker.hilsheimer@qt.io>2021-09-17 15:22:28 +0200
committerVolker Hilsheimer <volker.hilsheimer@qt.io>2021-09-24 19:19:42 +0200
commita5e5943d8a7d2a1345dc94dad0a97cf2966f6e7b (patch)
tree759bf3a2fa1a4f002163c170f26517920bed62d9 /tests/auto/widgets/kernel/qwidget
parent8da42e1af631ffefc29fb2ef0e289dd11da376c5 (diff)
macOS: send enter/leave when a window opens/closes
Since macOS doesn't give us any event when a modal window opens, we need to do so ourselves explicitly so that the current mouse window gets a leave event when e.g. a popup opens, and an enter event when the popup closes again. The case for modal dialogs is partially handled by QGuiApplication already. Note: We cannot rely on the transientParent of the opening/closing window, as it's nullptr for QMenu windows even if the QMenu has a widget parent. Add a test for enter/leave events when a secondary window opens, covering both the dialog and the popup case. For the dialog case, we sometimes get two Enter events when the dailog closes, which we have to tolerate for now. To make the test pass on b2qt platforms, fix the offscreen plugin to explicitly send enter/leave events in the same way as Cocoa now does. Fixes: QTBUG-78970 Pick-to: 6.2 Change-Id: If45e43e625e8362c3502c740154f6a6a8962b9e9 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Diffstat (limited to 'tests/auto/widgets/kernel/qwidget')
-rw-r--r--tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp127
1 files changed, 124 insertions, 3 deletions
diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp
index 4123c79141..5cd88f9ab8 100644
--- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp
+++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp
@@ -358,6 +358,8 @@ private slots:
void maskedUpdate();
#ifndef QT_NO_CURSOR
void syntheticEnterLeave();
+ void enterLeaveOnWindowShowHide_data();
+ void enterLeaveOnWindowShowHide();
void taskQTBUG_4055_sendSyntheticEnterLeave();
void underMouse();
void taskQTBUG_27643_enterEvents();
@@ -9839,6 +9841,124 @@ void tst_QWidget::syntheticEnterLeave()
#endif
#ifndef QT_NO_CURSOR
+void tst_QWidget::enterLeaveOnWindowShowHide_data()
+{
+ QTest::addColumn<Qt::WindowType>("windowType");
+ QTest::addRow("dialog") << Qt::Dialog;
+ QTest::addRow("popup") << Qt::Popup;
+}
+
+
+/*!
+ Verify that a window that has the mouse gets a leave event
+ when a dialog or popup opens (even if that dialog or popup is
+ not under the mouse), and an enter event when the secondary window
+ closes again (while the mouse is still over the original widget.
+
+ Since mouse grabbing might cause some event interaction, simulate
+ the opening of the secondary window from a mouse press, like we would with
+ a button or context menu. See QTBUG-78970.
+*/
+void tst_QWidget::enterLeaveOnWindowShowHide()
+{
+ QFETCH(Qt::WindowType, windowType);
+ class Widget : public QWidget
+ {
+ public:
+ int numEnterEvents = 0;
+ int numLeaveEvents = 0;
+ QPoint enterPosition;
+ Qt::WindowType secondaryWindowType = {};
+ protected:
+ void enterEvent(QEnterEvent *e) override
+ {
+ enterPosition = e->position().toPoint();
+ ++numEnterEvents;
+ }
+ void leaveEvent(QEvent *) override
+ {
+ enterPosition = {};
+ ++numLeaveEvents;
+ }
+ void mousePressEvent(QMouseEvent *e) override
+ {
+ QWidget *secondary = nullptr;
+ switch (secondaryWindowType) {
+ case Qt::Dialog: {
+ QDialog *dialog = new QDialog(this);
+ dialog->setModal(true);
+ dialog->setWindowModality(Qt::ApplicationModal);
+ secondary = dialog;
+ break;
+ }
+ case Qt::Popup: {
+ QMenu *menu = new QMenu(this);
+ menu->addAction("Action 1");
+ menu->addAction("Action 2");
+ secondary = menu;
+ break;
+ }
+ default:
+ QVERIFY2(false, "Test case not implemented for window type");
+ break;
+ }
+
+ QPoint secondaryPos = e->globalPosition().toPoint();
+ if (e->button() == Qt::LeftButton)
+ secondaryPos += QPoint(10, 10); // cursor outside secondary
+ else
+ secondaryPos -= QPoint(10, 10); // cursor inside secondary
+ secondary->move(secondaryPos);
+ secondary->show();
+ if (!QTest::qWaitForWindowExposed(secondary))
+ QEXPECT_FAIL("", "Secondary window failed to show, test will fail", Abort);
+ }
+ };
+
+ int expectedEnter = 0;
+ int expectedLeave = 0;
+
+ Widget widget;
+ widget.secondaryWindowType = windowType;
+ const QRect screenGeometry = widget.screen()->availableGeometry();
+ const QPoint cursorPos = screenGeometry.topLeft() + QPoint(50, 50);
+ widget.setGeometry(QRect(cursorPos - QPoint(50, 50), screenGeometry.size() / 4));
+ QCursor::setPos(cursorPos);
+
+ if (!QTest::qWaitFor([&]{ return widget.geometry().contains(QCursor::pos()); }))
+ QSKIP("We can't move the cursor");
+ widget.show();
+ QApplication::setActiveWindow(&widget);
+ QVERIFY(QTest::qWaitForWindowActive(&widget));
+
+ ++expectedEnter;
+ QTRY_COMPARE_WITH_TIMEOUT(widget.numEnterEvents, expectedEnter, 250);
+ QCOMPARE(widget.enterPosition, widget.mapFromGlobal(cursorPos));
+ QVERIFY(widget.underMouse());
+
+ QTest::mouseClick(&widget, Qt::LeftButton, {}, widget.mapFromGlobal(cursorPos));
+ ++expectedLeave;
+ QTRY_COMPARE_WITH_TIMEOUT(widget.numLeaveEvents, expectedLeave, 500);
+ QVERIFY(!widget.underMouse());
+ if (QApplication::activeModalWidget())
+ QApplication::activeModalWidget()->close();
+ else if (QApplication::activePopupWidget())
+ QApplication::activePopupWidget()->close();
+ ++expectedEnter;
+ // Use default timeout, the test is flaky on Windows otherwise.
+ QVERIFY(QTest::qWaitFor([&]{ return widget.numEnterEvents >= expectedEnter; }));
+ // When a modal dialog closes we might get more than one enter event on macOS.
+ // This seems to depend on timing, so we tolerate that flakiness for now.
+ if (widget.numEnterEvents > expectedEnter && QGuiApplication::platformName() == "cocoa")
+ QEXPECT_FAIL("dialog", "On macOS, we might get more than one Enter event", Continue);
+
+ QCOMPARE(widget.numEnterEvents, expectedEnter);
+ QCOMPARE(widget.enterPosition, widget.mapFromGlobal(cursorPos));
+ QVERIFY(widget.underMouse());
+}
+#endif
+
+#ifndef QT_NO_CURSOR
void tst_QWidget::taskQTBUG_4055_sendSyntheticEnterLeave()
{
if (m_platform == QStringLiteral("wayland"))
@@ -11281,9 +11401,10 @@ void tst_QWidget::underMouse()
QCOMPARE(QApplication::activePopupWidget(), &popupWidget);
// Send an artificial leave event for window, as it won't get generated automatically
- // due to cursor not actually being over the window.
- QWindowSystemInterface::handleLeaveEvent(window);
- QApplication::processEvents();
+ // due to cursor not actually being over the window. The Cocoa and offscreen plugins
+ // do this for us.
+ if (QGuiApplication::platformName() != "cocoa" && QGuiApplication::platformName() != "offscreen")
+ QWindowSystemInterface::handleLeaveEvent<QWindowSystemInterface::SynchronousDelivery>(window);
// If there is an active popup, undermouse should not be reported (QTBUG-27478),
// but opening a popup causes leave for widgets under mouse.