summaryrefslogtreecommitdiffstats
path: root/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/gui/kernel/qwindow/tst_qwindow.cpp')
-rw-r--r--tests/auto/gui/kernel/qwindow/tst_qwindow.cpp651
1 files changed, 545 insertions, 106 deletions
diff --git a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp
index 342e6489ed..a9e2c5f882 100644
--- a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp
+++ b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qrasterwindow.h>
#include <qpa/qwindowsysteminterface.h>
@@ -7,6 +7,7 @@
#include <qpa/qplatformwindow.h>
#include <private/qguiapplication_p.h>
#include <private/qhighdpiscaling_p.h>
+#include <private/qwindow_p.h>
#include <QtGui/QPainter>
#include <QTest>
@@ -22,6 +23,12 @@
Q_LOGGING_CATEGORY(lcTests, "qt.gui.tests")
+static bool isPlatformEglFS()
+{
+ static const bool isEglFS = !QGuiApplication::platformName().compare(QLatin1String("eglfs"), Qt::CaseInsensitive);
+ return isEglFS;
+}
+
class tst_QWindow: public QObject
{
Q_OBJECT
@@ -30,6 +37,7 @@ private slots:
void create();
void setParent();
void setVisible();
+ void setVisibleThenCreate();
void setVisibleFalseDoesNotCreateWindow();
void eventOrderOnShow();
void paintEvent();
@@ -70,6 +78,7 @@ private slots:
void modalWithChildWindow();
void modalWindowModallity();
void modalWindowPosition();
+ void modalCloseWhileBlocked();
#ifndef QT_NO_CURSOR
void modalWindowEnterEventOnHide_QTBUG35109();
void spuriousMouseMove();
@@ -86,6 +95,15 @@ private slots:
void keepPendingUpdateRequests();
void activateDeactivateEvent();
void qobject_castOnDestruction();
+ void touchToMouseTranslationByPopup();
+ void stateChangeSignal();
+#ifndef QT_NO_CURSOR
+ void enterLeaveOnWindowShowHide_data();
+ void enterLeaveOnWindowShowHide();
+#endif
+ void windowExposedAfterReparent();
+ void childEvents();
+ void parentEvents();
private:
QPoint m_availableTopLeft;
@@ -96,8 +114,17 @@ private:
QInputDevice::Capability::Position | QInputDevice::Capability::MouseEmulation);
};
+static bool isPlatformWayland()
+{
+ return QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive);
+}
+
void tst_QWindow::initTestCase()
{
+#ifdef Q_OS_ANDROID
+ if (QNativeInterface::QAndroidApplication::sdkVersion() == 33)
+ QSKIP("Is flaky on Android 13 / RHEL 8.6 and 8.8 (QTQAINFRA-5606)");
+#endif
// Size of reference window, 200 for < 2000, scale up for larger screens
// to avoid Windows warnings about minimum size for decorated windows.
int width = 200;
@@ -107,6 +134,10 @@ void tst_QWindow::initTestCase()
if (screenWidth > 2000)
width = 100 * ((screenWidth + 500) / 1000);
m_testWindowSize = QSize(width, width);
+
+ // Make sure test runs consistently on all compositors by force-disabling window decorations
+ if (isPlatformWayland())
+ qputenv("QT_WAYLAND_DISABLE_WINDOWDECORATION", "1");
}
void tst_QWindow::cleanup()
@@ -219,6 +250,40 @@ void tst_QWindow::setVisible()
QVERIFY(QTest::qWaitForWindowExposed(&i));
}
+class SurfaceCreatedWindow : public QWindow
+{
+ Q_OBJECT
+public:
+ using QWindow::QWindow;
+
+ bool eventFilter(QObject *o, QEvent *e) override
+ {
+ if (e->type() == QEvent::PlatformSurface) {
+ auto type = static_cast<QPlatformSurfaceEvent*>(e)->surfaceEventType();
+ if (type == QPlatformSurfaceEvent::SurfaceCreated)
+ ++surfaceCreatedEvents;
+ }
+ return QWindow::eventFilter(o, e);
+ }
+
+ int surfaceCreatedEvents = 0;
+};
+
+void tst_QWindow::setVisibleThenCreate()
+{
+ QWindow parent;
+ parent.setObjectName("Parent");
+ SurfaceCreatedWindow child(&parent);
+ child.installEventFilter(&child);
+ child.setObjectName("Child");
+ child.setVisible(true);
+ child.create();
+ QCOMPARE(child.surfaceCreatedEvents, 1);
+ parent.setVisible(true);
+ QCOMPARE(child.surfaceCreatedEvents, 1);
+ QVERIFY(QTest::qWaitForWindowExposed(&child));
+}
+
void tst_QWindow::setVisibleFalseDoesNotCreateWindow()
{
QWindow w;
@@ -286,13 +351,6 @@ public:
m_received[event->type()]++;
m_order << event->type();
switch (event->type()) {
- case QEvent::Expose:
-QT_WARNING_PUSH
-QT_WARNING_DISABLE_DEPRECATED
- m_exposeRegion = static_cast<QExposeEvent *>(event)->region();
-QT_WARNING_POP
- break;
-
case QEvent::PlatformSurface:
m_surfaceventType = static_cast<QPlatformSurfaceEvent *>(event)->surfaceEventType();
break;
@@ -322,11 +380,6 @@ QT_WARNING_POP
return m_order.indexOf(type);
}
- QRegion exposeRegion() const
- {
- return m_exposeRegion;
- }
-
QPlatformSurfaceEvent::SurfaceEventType surfaceEventType() const
{
return m_surfaceventType;
@@ -338,7 +391,6 @@ QT_WARNING_POP
private:
QHash<QEvent::Type, int> m_received;
QList<QEvent::Type> m_order;
- QRegion m_exposeRegion;
QPlatformSurfaceEvent::SurfaceEventType m_surfaceventType;
};
@@ -430,11 +482,16 @@ void tst_QWindow::resizeEventAfterResize()
// Make sure we get a resizeEvent after calling resize
window.resize(m_testWindowSize);
+ if (isPlatformEglFS())
+ QEXPECT_FAIL("", "eglfs windows are fullscreen by default.", Continue);
+
QTRY_COMPARE(window.received(QEvent::Resize), 2);
}
void tst_QWindow::exposeEventOnShrink_QTBUG54040()
{
+ if (isPlatformEglFS())
+ QSKIP("", "eglfs windows are fullscreen by default.", Continue);
Window window;
window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
window.setTitle(QTest::currentTestFunction());
@@ -498,13 +555,11 @@ static QString msgRectMismatch(const QRect &r1, const QRect &r2)
return result;
}
-static bool isPlatformWayland()
-{
- return QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive);
-}
-
void tst_QWindow::positioning()
{
+#ifdef Q_OS_ANDROID
+ QSKIP("Fails on Android. QTBUG-105201");
+#endif
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(
QPlatformIntegration::NonFullScreenWindows)) {
QSKIP("This platform does not support non-fullscreen windows");
@@ -526,9 +581,8 @@ void tst_QWindow::positioning()
QCOMPARE(window.geometry(), geometry);
// explicitly use non-fullscreen show. show() can be fullscreen on some platforms
window.showNormal();
- QCoreApplication::processEvents();
- QVERIFY(QTest::qWaitForWindowExposed(&window));
+ QVERIFY(QTest::qWaitForWindowActive(&window));
QMargins originalMargins = window.frameMargins();
@@ -620,6 +674,8 @@ void tst_QWindow::childWindowPositioning_data()
void tst_QWindow::childWindowPositioning()
{
+ if (isPlatformEglFS())
+ QSKIP("eglfs does not support child windows.");
const QPoint topLeftOrigin(0, 0);
ColoredWindow topLevelWindowFirst(Qt::green);
@@ -722,7 +778,7 @@ void tst_QWindow::stateChange()
// explicitly use non-fullscreen show. show() can be fullscreen on some platforms
window.showNormal();
QVERIFY(QTest::qWaitForWindowExposed(&window));
- for (Qt::WindowState state : qAsConst(stateSequence)) {
+ for (Qt::WindowState state : std::as_const(stateSequence)) {
window.setWindowState(state);
QCoreApplication::processEvents();
}
@@ -796,16 +852,6 @@ void tst_QWindow::isExposed()
QTRY_VERIFY(window.received(QEvent::Expose) > 0);
QTRY_VERIFY(window.isExposed());
-#ifndef Q_OS_WIN
- // This is a top-level window so assuming it is completely exposed, the
- // expose region must be (0, 0), (width, height). If this is not the case,
- // the platform plugin is sending expose events with a region in an
- // incorrect coordinate system.
- QRect r = window.exposeRegion().boundingRect();
- r = QRect(window.mapToGlobal(r.topLeft()), r.size());
- QCOMPARE(r, window.geometry());
-#endif
-
window.hide();
QCoreApplication::processEvents();
@@ -950,6 +996,9 @@ public:
if (spinLoopWhenPressed)
QCoreApplication::processEvents();
}
+ if (closeOnTap)
+ this->close();
+
}
void mouseReleaseEvent(QMouseEvent *event) override
{
@@ -1006,7 +1055,7 @@ public:
}
touchEventType = event->type();
QList<QTouchEvent::TouchPoint> points = event->points();
- for (int i = 0; i < points.count(); ++i) {
+ for (int i = 0; i < points.size(); ++i) {
const auto &point = points.at(i);
switch (point.state()) {
case QEventPoint::State::Pressed:
@@ -1017,6 +1066,8 @@ public:
touchPressLocalPos = point.position();
touchPressGlobalPos = point.globalPosition();
}
+ if (closeOnTap)
+ this->close();
break;
case QEventPoint::State::Released:
++touchReleasedCount;
@@ -1073,6 +1124,8 @@ public:
const QPointingDevice *mouseDevice = nullptr;
const QPointingDevice *touchDevice = nullptr;
+
+ bool closeOnTap = false;
};
static void simulateMouseClick(QWindow *target, const QPointF &local, const QPointF &global)
@@ -1157,18 +1210,14 @@ void tst_QWindow::touchToMouseTranslation()
QVERIFY(QTest::qWaitForWindowExposed(&window));
QList<QWindowSystemInterface::TouchPoint> points;
- QWindowSystemInterface::TouchPoint tp1, tp2, tp3;
+ QWindowSystemInterface::TouchPoint tp1, tp2;
const QRectF pressArea(101, 102, 4, 4);
- const QRectF pressArea1(107, 110, 4, 4);
const QRectF moveArea(105, 108, 4, 4);
tp1.id = 1;
tp1.state = QEventPoint::State::Pressed;
tp1.area = QHighDpi::toNativePixels(pressArea, &window);
tp2.id = 2;
tp2.state = QEventPoint::State::Pressed;
- tp3.id = 3;
- tp3.state = QEventPoint::State::Pressed;
- tp3.area = QHighDpi::toNativePixels(pressArea1, &window);
points << tp1 << tp2;
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
// Now an update but with changed list order. The mouse event should still
@@ -1250,40 +1299,6 @@ void tst_QWindow::touchToMouseTranslation()
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
QCoreApplication::processEvents();
QTRY_COMPARE(window.mouseReleaseButton, 1);
-
- points.clear();
- points.append(tp1);
- points[0].state = QEventPoint::State::Pressed;
- QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
- QCoreApplication::processEvents();
- points.clear();
- points.append(tp2);
- points[0].state = QEventPoint::State::Pressed;
- QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
- QCoreApplication::processEvents();
- points.clear();
- points.append(tp3);
- points[0].state = QEventPoint::State::Pressed;
- QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
- QCoreApplication::processEvents();
- QTRY_COMPARE(window.mousePressButton, 1);
-
- points.clear();
- points.append(tp2);
- points[0].state = QEventPoint::State::Released;
- QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
- QCoreApplication::processEvents();
- points.clear();
- points.append(tp3);
- points[0].state = QEventPoint::State::Released;
- QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
- QCoreApplication::processEvents();
- points.clear();
- points.append(tp1);
- points[0].state = QEventPoint::State::Released;
- QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
- QCoreApplication::processEvents();
- QTRY_COMPARE(window.mouseReleaseButton, 1);
}
void tst_QWindow::touchToMouseTranslationForDevices()
@@ -1557,7 +1572,7 @@ void tst_QWindow::orientation()
QSignalSpy spy(&window, SIGNAL(contentOrientationChanged(Qt::ScreenOrientation)));
window.reportContentOrientationChange(Qt::LandscapeOrientation);
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
}
void tst_QWindow::sizes()
@@ -1576,40 +1591,40 @@ void tst_QWindow::sizes()
QCOMPARE(window.minimumHeight(), 0);
QCOMPARE(window.minimumSize(), QSize(10, 0));
QCOMPARE(window.maximumSize(), oldMaximum);
- QCOMPARE(minimumWidthSpy.count(), 1);
- QCOMPARE(minimumHeightSpy.count(), 0);
- QCOMPARE(maximumWidthSpy.count(), 0);
- QCOMPARE(maximumHeightSpy.count(), 0);
+ QCOMPARE(minimumWidthSpy.size(), 1);
+ QCOMPARE(minimumHeightSpy.size(), 0);
+ QCOMPARE(maximumWidthSpy.size(), 0);
+ QCOMPARE(maximumHeightSpy.size(), 0);
window.setMinimumHeight(10);
QCOMPARE(window.minimumWidth(), 10);
QCOMPARE(window.minimumHeight(), 10);
QCOMPARE(window.minimumSize(), QSize(10, 10));
QCOMPARE(window.maximumSize(), oldMaximum);
- QCOMPARE(minimumWidthSpy.count(), 1);
- QCOMPARE(minimumHeightSpy.count(), 1);
- QCOMPARE(maximumWidthSpy.count(), 0);
- QCOMPARE(maximumHeightSpy.count(), 0);
+ QCOMPARE(minimumWidthSpy.size(), 1);
+ QCOMPARE(minimumHeightSpy.size(), 1);
+ QCOMPARE(maximumWidthSpy.size(), 0);
+ QCOMPARE(maximumHeightSpy.size(), 0);
window.setMaximumWidth(100);
QCOMPARE(window.maximumWidth(), 100);
QCOMPARE(window.maximumHeight(), oldMaximum.height());
QCOMPARE(window.minimumSize(), QSize(10, 10));
QCOMPARE(window.maximumSize(), QSize(100, oldMaximum.height()));
- QCOMPARE(minimumWidthSpy.count(), 1);
- QCOMPARE(minimumHeightSpy.count(), 1);
- QCOMPARE(maximumWidthSpy.count(), 1);
- QCOMPARE(maximumHeightSpy.count(), 0);
+ QCOMPARE(minimumWidthSpy.size(), 1);
+ QCOMPARE(minimumHeightSpy.size(), 1);
+ QCOMPARE(maximumWidthSpy.size(), 1);
+ QCOMPARE(maximumHeightSpy.size(), 0);
window.setMaximumHeight(100);
QCOMPARE(window.maximumWidth(), 100);
QCOMPARE(window.maximumHeight(), 100);
QCOMPARE(window.minimumSize(), QSize(10, 10));
QCOMPARE(window.maximumSize(), QSize(100, 100));
- QCOMPARE(minimumWidthSpy.count(), 1);
- QCOMPARE(minimumHeightSpy.count(), 1);
- QCOMPARE(maximumWidthSpy.count(), 1);
- QCOMPARE(maximumHeightSpy.count(), 1);
+ QCOMPARE(minimumWidthSpy.size(), 1);
+ QCOMPARE(minimumHeightSpy.size(), 1);
+ QCOMPARE(maximumWidthSpy.size(), 1);
+ QCOMPARE(maximumHeightSpy.size(), 1);
// test if min and max limits will change the size
QVERIFY(window.minimumWidth() < 50 && window.maximumWidth() > 80);
@@ -1621,6 +1636,13 @@ void tst_QWindow::sizes()
window.resize(80, 80);
window.setMaximumSize(QSize(70, 70));
QCOMPARE(window.size(), QSize(70, 70));
+
+ // QTBUG-113233
+ // test for an invalid min/max pair
+ window.setMinimumSize(QSize(80, 80)); // current maximumSize = QSize(70, 70)
+ QCOMPARE(window.size(), QSize(70, 70));
+ window.setMaximumSize(QSize(90, 90));
+ QCOMPARE(window.size(), QSize(80, 80));
}
class CloseOnCloseEventWindow : public QWindow
@@ -1852,25 +1874,25 @@ void tst_QWindow::windowModality()
QCOMPARE(window.modality(), Qt::NonModal);
window.setModality(Qt::NonModal);
QCOMPARE(window.modality(), Qt::NonModal);
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
window.setModality(Qt::WindowModal);
QCOMPARE(window.modality(), Qt::WindowModal);
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
window.setModality(Qt::WindowModal);
QCOMPARE(window.modality(), Qt::WindowModal);
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
window.setModality(Qt::ApplicationModal);
QCOMPARE(window.modality(), Qt::ApplicationModal);
- QCOMPARE(spy.count(), 2);
+ QCOMPARE(spy.size(), 2);
window.setModality(Qt::ApplicationModal);
QCOMPARE(window.modality(), Qt::ApplicationModal);
- QCOMPARE(spy.count(), 2);
+ QCOMPARE(spy.size(), 2);
window.setModality(Qt::NonModal);
QCOMPARE(window.modality(), Qt::NonModal);
- QCOMPARE(spy.count(), 3);
+ QCOMPARE(spy.size(), 3);
}
void tst_QWindow::inputReentrancy()
@@ -2053,32 +2075,32 @@ void tst_QWindow::visibility()
QVERIFY(window.isVisible());
QVERIFY(window.visibility() != QWindow::Hidden);
QVERIFY(window.visibility() != QWindow::AutomaticVisibility);
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
spy.clear();
window.setVisibility(QWindow::Hidden);
QVERIFY(!window.isVisible());
QCOMPARE(window.visibility(), QWindow::Hidden);
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
spy.clear();
window.setVisibility(QWindow::FullScreen);
QVERIFY(window.isVisible());
QCOMPARE(window.windowState(), Qt::WindowFullScreen);
QCOMPARE(window.visibility(), QWindow::FullScreen);
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QTRY_COMPARE(window.lastReceivedWindowState, Qt::WindowFullScreen);
spy.clear();
window.setWindowState(Qt::WindowNoState);
QCOMPARE(window.visibility(), QWindow::Windowed);
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QTRY_COMPARE(window.lastReceivedWindowState, Qt::WindowNoState);
spy.clear();
window.setVisible(false);
QCOMPARE(window.visibility(), QWindow::Hidden);
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
spy.clear();
}
@@ -2125,8 +2147,10 @@ void tst_QWindow::initialSize()
w.setTitle(QLatin1String(QTest::currentTestFunction()));
w.setWidth(m_testWindowSize.width());
w.showNormal();
- if (isPlatformWayland())
- QEXPECT_FAIL("", "Wayland: This fails. See QTBUG-66818.", Abort);
+
+ if (isPlatformEglFS())
+ QEXPECT_FAIL("", "eglfs windows are fullscreen by default.", Continue);
+
QTRY_COMPARE(w.width(), m_testWindowSize.width());
QTRY_VERIFY(w.height() > 0);
}
@@ -2138,6 +2162,8 @@ void tst_QWindow::initialSize()
w.showNormal();
const QSize expectedSize = testSize;
+ if (isPlatformEglFS())
+ QEXPECT_FAIL("", "eglfs windows are fullscreen by default.", Continue);
QTRY_COMPARE(w.size(), expectedSize);
}
}
@@ -2302,6 +2328,9 @@ void tst_QWindow::modalWindowModallity()
void tst_QWindow::modalWindowPosition()
{
+ if (isPlatformWayland())
+ QSKIP("Window position not queryable on Wayland");
+
QWindow window;
window.setTitle(QLatin1String(QTest::currentTestFunction()));
window.setGeometry(QRect(m_availableTopLeft + QPoint(100, 100), m_testWindowSize));
@@ -2310,11 +2339,29 @@ void tst_QWindow::modalWindowPosition()
window.setModality(Qt::WindowModal);
window.show();
QVERIFY(QTest::qWaitForWindowExposed(&window));
- if (isPlatformWayland())
- QEXPECT_FAIL("", "Wayland: This fails. See QTBUG-100888.", Abort);
+
+ if (isPlatformEglFS())
+ QEXPECT_FAIL("", "eglfs windows are fullscreen by default.", Continue);
+
QCOMPARE(window.geometry(), origGeo);
}
+void tst_QWindow::modalCloseWhileBlocked()
+{
+ QWindow first;
+ first.setModality(Qt::ApplicationModal);
+ first.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&first));
+
+ QWindow second;
+ second.setModality(Qt::ApplicationModal);
+ second.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&first));
+
+ first.close();
+ QTRY_VERIFY(!first.isVisible());
+}
+
#ifndef QT_NO_CURSOR
void tst_QWindow::modalWindowEnterEventOnHide_QTBUG35109()
{
@@ -2324,6 +2371,9 @@ void tst_QWindow::modalWindowEnterEventOnHide_QTBUG35109()
if (isPlatformOffscreenOrMinimal())
QSKIP("Can't test window focusing on offscreen/minimal");
+ if (isPlatformEglFS())
+ QSKIP("QCursor::setPos() is not supported on this platform");
+
const QPoint center = QGuiApplication::primaryScreen()->availableGeometry().center();
const int childOffset = 16;
@@ -2504,6 +2554,8 @@ void tst_QWindow::spuriousMouseMove()
QSKIP("No enter events sent");
if (platformName == QLatin1String("wayland"))
QSKIP("Setting mouse cursor position is not possible on Wayland");
+ if (isPlatformEglFS())
+ QSKIP("QCursor::setPos() is not supported on this platform");
const QRect screenGeometry = QGuiApplication::primaryScreen()->geometry();
const QPoint center = screenGeometry.center();
QCursor::setPos(center);
@@ -2590,8 +2642,6 @@ void tst_QWindow::requestUpdate()
QCoreApplication::processEvents();
QTRY_VERIFY(window.isExposed());
- if (isPlatformWayland())
- QEXPECT_FAIL("", "Wayland: This fails. See QTBUG-100889.", Abort);
QCOMPARE(window.received(QEvent::UpdateRequest), 0);
window.requestUpdate();
@@ -2781,6 +2831,395 @@ void tst_QWindow::qobject_castOnDestruction()
});
}
+void tst_QWindow::touchToMouseTranslationByPopup()
+{
+ InputTestWindow window;
+ window.setTitle(QLatin1String(QTest::currentTestFunction()));
+ window.ignoreTouch = true;
+ window.setGeometry(QRect(m_availableTopLeft, m_testWindowSize));
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+ InputTestWindow popupWindow;
+ popupWindow.setGeometry(QRect(m_availableTopLeft + QPoint(20, 20),
+ QSize(m_testWindowSize.width(), m_testWindowSize.height() / 2)));
+ popupWindow.setFlag(Qt::Popup);
+ popupWindow.setTransientParent(&window);
+ popupWindow.ignoreTouch = true;
+ popupWindow.closeOnTap = true;
+ popupWindow.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&popupWindow));
+
+ QTest::touchEvent(&popupWindow, touchDevice).press(0, {1, 1}, &window);
+ QVERIFY(!popupWindow.isVisible());
+
+ // Omit touchpoint 0: because the popup was closed, touchpoint0.release is not sent.
+ const QPoint tp1(50, 1);
+ QTest::touchEvent(&window, touchDevice).press(1, tp1, &window);
+ QTRY_COMPARE(window.mousePressButton, int(Qt::LeftButton));
+ QTest::touchEvent(&window, touchDevice).release(1, tp1, &window);
+ QTRY_COMPARE(window.mouseReleaseButton, int(Qt::LeftButton));
+}
+
+// Test that windowStateChanged is not emitted on noop change (QTBUG-102478)
+void tst_QWindow::stateChangeSignal()
+{
+ // Test only for Windows, Linux and macOS
+#if !defined(Q_OS_LINUX) && !defined(Q_OS_WINDOWS) && !defined(Q_OS_DARWIN)
+ QSKIP("Singular windowStateChanged signal emission is guaranteed for Linux, Windows and macOS only.\n"
+ "On other operating systems, the signal may be emitted twice.");
+#endif
+ QWindow w;
+ Q_ASSERT(connect (&w, &QWindow::windowStateChanged, [](Qt::WindowState s){qCDebug(lcTests) << "State change to" << s;}));
+ QSignalSpy spy(&w, SIGNAL(windowStateChanged(Qt::WindowState)));
+ unsigned short signalCount = 0;
+ QList<Qt::WindowState> effectiveStates;
+ Q_ASSERT(connect(&w, &QWindow::windowStateChanged, [&effectiveStates](Qt::WindowState state)
+ { effectiveStates.append(state); }));
+ // Part 1:
+ // => test signal emission on programmatic state changes
+ QCOMPARE(w.windowState(), Qt::WindowNoState);
+ // - wait for target state to be set
+ // - wait for signal spy to have reached target count
+ // - extract state from signal and compare to target
+#define CHECK_STATE(State)\
+ QTRY_VERIFY(QTest::qWaitFor([&w](){return (w.windowState() == State); }));\
+ CHECK_SIGNAL(State)
+#define CHECK_SIGNAL(State)\
+ QTRY_COMPARE(spy.count(), signalCount);\
+ if (signalCount > 0) {\
+ QVariantList list = spy.at(signalCount - 1).toList();\
+ QCOMPARE(list.count(), 1);\
+ bool ok;\
+ const int stateInt = list.at(0).toInt(&ok);\
+ QVERIFY(ok);\
+ const Qt::WindowState newState = static_cast<Qt::WindowState>(stateInt);\
+ QCOMPARE(newState, State);\
+ }
+ // Check initialization
+ CHECK_STATE(Qt::WindowNoState);
+ // showMaximized after init
+ // expected behavior: signal emitted once with state == WindowMaximized
+ ++signalCount;
+ w.showMaximized();
+ CHECK_STATE(Qt::WindowMaximized);
+ // setWindowState to normal
+ // expected behavior: signal emitted once with state == WindowNoState
+ ++signalCount;
+ w.setWindowState(Qt::WindowNoState);
+ CHECK_STATE(Qt::WindowNoState);
+ // redundant setWindowState to normal - except windows, where the no-op is counted
+ // expected behavior: No emits.
+ // On Windows, a no-op state change causes a no-op resize and repaint, leading to a
+ // no-op state change and singal emission.
+#ifdef Q_OS_WINDOWS
+ ++signalCount;
+ ++signalCount;
+#endif
+ w.setWindowState(Qt::WindowNoState);
+ CHECK_STATE(Qt::WindowNoState);
+ // setWindowState to minimized
+ // expected behavior: signal emitted once with state == WindowMinimized
+ ++signalCount;
+ w.showMinimized();
+ CHECK_STATE(Qt::WindowMinimized);
+ // setWindowState to Normal
+ // expected behavior: signal emitted once with state == WindowNoState
+ ++signalCount;
+ w.showNormal();
+ CHECK_STATE(Qt::WindowNoState);
+ /*
+ - Testcase showFullScreen is omitted: Depending on window manager,
+ WindowFullScreen can be mapped to WindowMaximized
+ - Transition from WindowMinimized to WindowMaximized is omitted:
+ WindowNoState to WindowMaximized
+ */
+ // Part 2:
+ // => test signal emission on simulated user interaction
+ // To test the code path, inject state change events into the QPA event queue.
+ // Test the signal emission only, not the window's actual visible state.
+
+ // Flush pending events and clear
+ QCoreApplication::processEvents();
+ spy.clear();
+ effectiveStates.clear();
+ signalCount = 0;
+ // Maximize window
+ QWindowSystemInterface::handleWindowStateChanged(&w, Qt::WindowMaximized, w.windowState());
+ ++signalCount;
+ CHECK_SIGNAL(Qt::WindowMaximized);
+ // Normalize window
+ QWindowSystemInterface::handleWindowStateChanged(&w, Qt::WindowNoState, w.windowState());
+ ++signalCount;
+ CHECK_SIGNAL(Qt::WindowNoState);
+ // Minimize window
+ QWindowSystemInterface::handleWindowStateChanged(&w, Qt::WindowMinimized, w.windowState());
+ ++signalCount;
+ CHECK_SIGNAL(Qt::WindowMinimized);
+}
+
+#ifndef QT_NO_CURSOR
+void tst_QWindow::enterLeaveOnWindowShowHide_data()
+{
+ QTest::addColumn<Qt::WindowType>("windowType");
+ QTest::addRow("dialog") << Qt::Dialog;
+ QTest::addRow("popup") << Qt::Popup;
+}
+
+/*!
+ Verify that we get enter and leave events if the window under the mouse
+ opens and closes a modal dialog or popup. QWindow might get multiple
+ events in a row, as the various QPA plugins need to use different techniques
+ to synthesize events if the native platform doesn't provide them for us.
+*/
+void tst_QWindow::enterLeaveOnWindowShowHide()
+{
+ if (isPlatformWayland())
+ QSKIP("Can't set cursor position and qWaitForWindowActive on Wayland");
+
+ if (isPlatformEglFS())
+ QSKIP("QCursor::setPos() is not supported on this platform");
+
+ QFETCH(Qt::WindowType, windowType);
+
+ class Window : public QWindow
+ {
+ public:
+ int numEnterEvents = 0;
+ int numLeaveEvents = 0;
+ QPoint enterPosition;
+ protected:
+ bool event(QEvent *e) override
+ {
+ switch (e->type()) {
+ case QEvent::Enter:
+ ++numEnterEvents;
+ enterPosition = static_cast<QEnterEvent*>(e)->position().toPoint();
+ break;
+ case QEvent::Leave:
+ ++numLeaveEvents;
+ break;
+ default:
+ break;
+ }
+ return QWindow::event(e);
+ }
+ };
+
+ int expectedEnter = 0;
+ int expectedLeave = 0;
+
+ Window window;
+ const QRect screenGeometry = window.screen()->availableGeometry();
+ const QPoint cursorPos = screenGeometry.topLeft() + QPoint(50, 50);
+ window.setGeometry(QRect(cursorPos - QPoint(50, 50), screenGeometry.size() / 4));
+ QCursor::setPos(cursorPos);
+
+ if (!QTest::qWaitFor([&]{ return window.geometry().contains(QCursor::pos()); }))
+ QSKIP("We can't move the cursor");
+
+ window.show();
+ window.requestActivate();
+ QVERIFY(QTest::qWaitForWindowActive(&window));
+
+ ++expectedEnter;
+ QTRY_COMPARE_WITH_TIMEOUT(window.numEnterEvents, expectedEnter, 250);
+ QCOMPARE(window.enterPosition, window.mapFromGlobal(QCursor::pos()));
+
+ QWindow secondary;
+ secondary.setFlag(windowType);
+ secondary.setModality(Qt::WindowModal);
+ secondary.setTransientParent(&window);
+ secondary.setPosition(cursorPos + QPoint(50, 50));
+ secondary.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&secondary));
+ ++expectedLeave;
+ QTRY_VERIFY(window.numLeaveEvents >= expectedLeave);
+ secondary.close();
+ ++expectedEnter;
+ QTRY_VERIFY(window.numEnterEvents >= expectedEnter);
+ QCOMPARE(window.enterPosition, window.mapFromGlobal(QCursor::pos()));
+}
+#endif
+
+void tst_QWindow::windowExposedAfterReparent()
+{
+ QWindow parent;
+ QWindow child(&parent);
+ child.show();
+ parent.show();
+
+ QVERIFY(QTest::qWaitForWindowExposed(&parent));
+ QVERIFY(QTest::qWaitForWindowExposed(&child));
+
+ child.setParent(nullptr);
+ QCoreApplication::processEvents();
+ QVERIFY(QTest::qWaitForWindowExposed(&child));
+
+ child.setParent(&parent);
+ QCoreApplication::processEvents();
+ QVERIFY(QTest::qWaitForWindowExposed(&child));
+}
+
+struct ParentWindow : public QWindow
+{
+ bool event(QEvent *event) override
+ {
+ [&]() -> void {
+ if (event->type() == QEvent::ChildWindowAdded
+ || event->type() == QEvent::ChildWindowRemoved) {
+ // We should not receive child events after the window has been destructed
+ QVERIFY(this->isWindowType());
+
+ auto *parentWindow = this;
+ auto *childEvent = static_cast<QChildWindowEvent*>(event);
+ auto *childWindow = childEvent->child();
+
+ if (event->type() == QEvent::ChildWindowAdded) {
+ QVERIFY(childWindow->parent());
+ QVERIFY(parentWindow->isAncestorOf(childWindow));
+ if (childWindow->handle())
+ QVERIFY(childWindow->handle()->parent() == parentWindow->handle());
+
+ } else {
+ QVERIFY(!childWindow->parent());
+ QVERIFY(!parentWindow->isAncestorOf(childWindow));
+ if (childWindow->handle())
+ QVERIFY(childWindow->handle()->parent() != parentWindow->handle());
+ }
+ }
+ }();
+
+ return QWindow::event(event);
+ }
+};
+
+void tst_QWindow::childEvents()
+{
+ ParentWindow parent;
+
+ {
+ // ChildAdded via constructor
+ QWindow constructorChild(&parent);
+ if (QTest::currentTestFailed()) return;
+ // ChildRemoved via destructor
+ }
+
+ if (QTest::currentTestFailed()) return;
+
+ // ChildAdded and ChildRemoved via setParent
+ QWindow child;
+ child.setParent(&parent);
+ if (QTest::currentTestFailed()) return;
+ child.setParent(nullptr);
+ if (QTest::currentTestFailed()) return;
+
+ parent.create();
+ child.create();
+
+ // ChildAdded and ChildRemoved after creation
+ child.setParent(&parent);
+ if (QTest::currentTestFailed()) return;
+ child.setParent(nullptr);
+ if (QTest::currentTestFailed()) return;
+}
+
+struct ChildWindowPrivate;
+struct ChildWindow : public QWindow
+{
+ ChildWindow(QWindow *parent = nullptr);
+};
+
+struct ChildWindowPrivate : public QWindowPrivate
+{
+ ChildWindowPrivate() : QWindowPrivate()
+ {
+ receiveParentEvents = true;
+ }
+};
+
+ChildWindow::ChildWindow(QWindow *parent)
+ : QWindow(*new ChildWindowPrivate, parent)
+{}
+
+struct ParentEventTester : public QObject
+{
+ bool eventFilter(QObject *object, QEvent *event) override
+ {
+ [&]() -> void {
+ if (event->type() == QEvent::ParentWindowAboutToChange
+ || event->type() == QEvent::ParentWindowChange) {
+ // We should not receive parent events after the window has been destructed
+ QVERIFY(object->isWindowType());
+ auto *window = static_cast<QWindow*>(object);
+
+ if (event->type() == QEvent::ParentWindowAboutToChange) {
+ QVERIFY(window->parent() != nextExpectedParent);
+ if (window->handle()) {
+ QVERIFY(window->handle()->parent() !=
+ (nextExpectedParent ? nextExpectedParent->handle() : nullptr));
+ }
+ } else {
+ QVERIFY(window->parent() == nextExpectedParent);
+ if (window->handle()) {
+ QVERIFY(window->handle()->parent() ==
+ (nextExpectedParent ? nextExpectedParent->handle() : nullptr));
+ }
+ }
+ }
+ }();
+
+ return QObject::eventFilter(object, event);
+ }
+
+ QWindow *nextExpectedParent = nullptr;
+};
+
+
+
+void tst_QWindow::parentEvents()
+{
+ QWindow parent;
+
+ {
+ ParentEventTester tester;
+
+ {
+ // We can't hook in early enough to get the parent change during
+ // QObject construction.
+ ChildWindow child(&parent);
+
+ // But we can observe the one during destruction
+ child.installEventFilter(&tester);
+ tester.nextExpectedParent = nullptr;
+ }
+ }
+ if (QTest::currentTestFailed()) return;
+
+ ParentEventTester tester;
+ ChildWindow child;
+ child.installEventFilter(&tester);
+
+ tester.nextExpectedParent = &parent;
+ child.setParent(&parent);
+ if (QTest::currentTestFailed()) return;
+
+ tester.nextExpectedParent = nullptr;
+ child.setParent(nullptr);
+ if (QTest::currentTestFailed()) return;
+
+ parent.create();
+ child.create();
+
+ tester.nextExpectedParent = &parent;
+ child.setParent(&parent);
+ if (QTest::currentTestFailed()) return;
+
+ tester.nextExpectedParent = nullptr;
+ child.setParent(nullptr);
+ if (QTest::currentTestFailed()) return;
+}
+
#include <tst_qwindow.moc>
QTEST_MAIN(tst_QWindow)