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.cpp795
1 files changed, 684 insertions, 111 deletions
diff --git a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp
index 10e0d450b9..a9e2c5f882 100644
--- a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp
+++ b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qrasterwindow.h>
#include <qpa/qwindowsysteminterface.h>
@@ -32,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>
@@ -47,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
@@ -55,6 +37,7 @@ private slots:
void create();
void setParent();
void setVisible();
+ void setVisibleThenCreate();
void setVisibleFalseDoesNotCreateWindow();
void eventOrderOnShow();
void paintEvent();
@@ -95,6 +78,7 @@ private slots:
void modalWithChildWindow();
void modalWindowModallity();
void modalWindowPosition();
+ void modalCloseWhileBlocked();
#ifndef QT_NO_CURSOR
void modalWindowEnterEventOnHide_QTBUG35109();
void spuriousMouseMove();
@@ -110,6 +94,16 @@ private slots:
void generatedMouseMove();
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;
@@ -120,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;
@@ -131,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()
@@ -243,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;
@@ -293,7 +334,7 @@ public:
#if !defined(Q_OS_MACOS)
// FIXME: All platforms should send window-state change events, regardless
// of the sync/async nature of the the underlying platform, but they don't.
- connect(this, &QWindow::windowStateChanged, [=]() {
+ connect(this, &QWindow::windowStateChanged, [this]() {
lastReceivedWindowState = windowState();
});
#endif
@@ -310,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;
@@ -346,11 +380,6 @@ QT_WARNING_POP
return m_order.indexOf(type);
}
- QRegion exposeRegion() const
- {
- return m_exposeRegion;
- }
-
QPlatformSurfaceEvent::SurfaceEventType surfaceEventType() const
{
return m_surfaceventType;
@@ -362,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;
};
@@ -454,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());
@@ -522,13 +555,11 @@ static QString msgRectMismatch(const QRect &r1, const QRect &r2)
return result;
}
-static bool isPlatformWayland()
-{
- return !QGuiApplication::platformName().compare(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");
@@ -550,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();
@@ -644,9 +674,8 @@ void tst_QWindow::childWindowPositioning_data()
void tst_QWindow::childWindowPositioning()
{
- if (isPlatformWayland())
- QSKIP("Wayland: This is flaky (protocol errors for xdg-shell v6). See QTBUG-67648.");
-
+ if (isPlatformEglFS())
+ QSKIP("eglfs does not support child windows.");
const QPoint topLeftOrigin(0, 0);
ColoredWindow topLevelWindowFirst(Qt::green);
@@ -749,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();
}
@@ -823,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();
@@ -977,6 +996,9 @@ public:
if (spinLoopWhenPressed)
QCoreApplication::processEvents();
}
+ if (closeOnTap)
+ this->close();
+
}
void mouseReleaseEvent(QMouseEvent *event) override
{
@@ -1033,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:
@@ -1044,6 +1066,8 @@ public:
touchPressLocalPos = point.position();
touchPressGlobalPos = point.globalPosition();
}
+ if (closeOnTap)
+ this->close();
break;
case QEventPoint::State::Released:
++touchReleasedCount;
@@ -1100,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)
@@ -1323,6 +1349,8 @@ void tst_QWindow::mouseToTouchTranslation()
QTRY_COMPARE(window.touchPressedCount, 1);
QTRY_COMPARE(window.touchReleasedCount, 1);
QCOMPARE(window.mouseDevice, window.touchDevice);
+ if (isPlatformWayland())
+ QEXPECT_FAIL("", "Wayland: This fails. See QTBUG-100887.", Abort);
QCOMPARE(window.touchDevice->type(), QInputDevice::DeviceType::Mouse);
QCOMPARE(window.touchPressLocalPos.toPoint(), localPos);
QCOMPARE(window.touchPressGlobalPos.toPoint(), window.mapToGlobal(localPos));
@@ -1480,9 +1508,6 @@ void tst_QWindow::touchCancelWithTouchToMouse()
void tst_QWindow::touchInterruptedByPopup()
{
- if (isPlatformWayland())
- QSKIP("Wayland: This test crashes with xdg-shell unstable v6");
-
InputTestWindow window;
window.setTitle(QLatin1String(QTest::currentTestFunction()));
window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
@@ -1547,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()
@@ -1566,56 +1591,168 @@ 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);
+ QVERIFY(window.minimumHeight() < 50 && window.maximumHeight() > 80);
+ window.resize(50, 50);
+ QCOMPARE(window.size(), QSize(50, 50));
+ window.setMinimumSize(QSize(60, 60));
+ QCOMPARE(window.size(), QSize(60, 60));
+ 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
+{
+public:
+ inline static int closeEvents;
+ CloseOnCloseEventWindow() { closeEvents = 0; }
+
+protected:
+ void closeEvent(QCloseEvent *e) override
+ {
+ if (++closeEvents > 1)
+ return;
+
+ close();
+ e->accept();
+ }
+};
void tst_QWindow::close()
{
- QWindow a;
- a.setTitle(QLatin1String(QTest::currentTestFunction()));
- QWindow b;
- QWindow c(&a);
+ {
+ QWindow a;
+ QWindow b;
+ QWindow c(&a);
- a.show();
- b.show();
+ a.show();
+ b.show();
- // we can not close a non top level window
- QVERIFY(!c.close());
- QVERIFY(a.close());
- QVERIFY(b.close());
+ // we can not close a non top level window
+ QVERIFY(!c.close());
+ QVERIFY(a.close());
+ QVERIFY(b.close());
+ }
+
+ // Verify that closing a QWindow deletes its platform window,
+ // independent of API used to close the window.
+ {
+ // Close with QWindow::close
+ {
+ QWindow w;
+ w.create();
+ QVERIFY(w.handle());
+ w.close();
+ QVERIFY(!w.handle());
+ }
+
+ // Close with QWindowSystemInterface::handleCloseEvent();
+ {
+ QWindow w;
+ w.create();
+ QVERIFY(w.handle());
+ QWindowSystemInterface::handleCloseEvent(&w);
+ QCoreApplication::processEvents();
+ QVERIFY(!w.handle());
+ }
+ }
+
+ // Verify that closing a QWindow deletes the platform window for
+ // child windows
+ {
+ QWindow w;
+ QWindow c(&w);
+ w.create();
+ c.create();
+ QVERIFY(w.handle());
+ QVERIFY(c.handle());
+ w.close();
+ QVERIFY(!w.handle());
+ QVERIFY(!c.handle());
+ }
+
+ // Verify that re-creating closed windows is possble.
+ {
+ // Re-create top-level window
+ {
+ QWindow w;
+ w.create();
+ QVERIFY(w.handle());
+ w.close();
+ QVERIFY(!w.handle());
+ w.create();
+ QVERIFY(w.handle());
+ }
+
+ // Re-create top-level window with child window
+ {
+ QWindow w;
+ QWindow c(&w);
+ c.create();
+ QVERIFY(w.handle());
+ QVERIFY(c.handle());
+ w.close();
+ QVERIFY(!w.handle());
+ QVERIFY(!c.handle());
+ c.create();
+ QVERIFY(w.handle());
+ QVERIFY(c.handle());
+ }
+ }
+
+ {
+ // A QWidget will call close() from the destructor, and
+ // we allow widgets deleting itself in the closeEvent,
+ // so we need to guard against close being called recursively.
+ CloseOnCloseEventWindow w;
+ w.create();
+ w.close();
+ QCOMPARE(CloseOnCloseEventWindow::closeEvents, 1);
+ }
}
void tst_QWindow::activateAndClose()
@@ -1737,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()
@@ -1938,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();
}
@@ -1996,9 +2133,6 @@ void tst_QWindow::mask()
void tst_QWindow::initialSize()
{
- if (isPlatformWayland())
- QSKIP("Wayland: This fails. See QTBUG-66818.");
-
QSize defaultSize(0,0);
{
Window w;
@@ -2013,6 +2147,10 @@ void tst_QWindow::initialSize()
w.setTitle(QLatin1String(QTest::currentTestFunction()));
w.setWidth(m_testWindowSize.width());
w.showNormal();
+
+ if (isPlatformEglFS())
+ QEXPECT_FAIL("", "eglfs windows are fullscreen by default.", Continue);
+
QTRY_COMPARE(w.width(), m_testWindowSize.width());
QTRY_VERIFY(w.height() > 0);
}
@@ -2024,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);
}
}
@@ -2063,7 +2203,7 @@ void tst_QWindow::modalDialog()
QGuiApplication::processEvents();
if (isPlatformOffscreenOrMinimal()) {
- QWARN("Focus stays in normalWindow on offscreen/minimal platforms");
+ qWarning("Focus stays in normalWindow on offscreen/minimal platforms");
QTRY_COMPARE(QGuiApplication::focusWindow(), &normalWindow);
return;
}
@@ -2109,7 +2249,7 @@ void tst_QWindow::modalDialogClosingOneOfTwoModal()
QGuiApplication::processEvents();
if (isPlatformOffscreenOrMinimal()) {
- QWARN("Focus is lost when closing modal dialog on offscreen/minimal platforms");
+ qWarning("Focus is lost when closing modal dialog on offscreen/minimal platforms");
QTRY_COMPARE(QGuiApplication::focusWindow(), nullptr);
return;
}
@@ -2188,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));
@@ -2196,9 +2339,29 @@ void tst_QWindow::modalWindowPosition()
window.setModality(Qt::WindowModal);
window.show();
QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+ 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()
{
@@ -2208,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;
@@ -2388,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);
@@ -2607,6 +2775,9 @@ void tst_QWindow::keepPendingUpdateRequests()
void tst_QWindow::activateDeactivateEvent()
{
+ if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
+ QSKIP("QWindow::requestActivate() is not supported.");
+
class Window : public QWindow
{
public:
@@ -2615,7 +2786,7 @@ void tst_QWindow::activateDeactivateEvent()
int activateCount = 0;
int deactivateCount = 0;
protected:
- bool event(QEvent *e)
+ bool event(QEvent *e) override
{
switch (e->type()) {
case QEvent::WindowActivate:
@@ -2647,6 +2818,408 @@ void tst_QWindow::activateDeactivateEvent()
QCOMPARE(w2.activateCount, 1);
}
+// Test that in a slot connected to destroyed() the emitter is
+// is no longer a QWindow.
+void tst_QWindow::qobject_castOnDestruction()
+{
+ QWindow window;
+ QObject::connect(&window, &QObject::destroyed, [](QObject *object)
+ {
+ QVERIFY(!qobject_cast<QWindow *>(object));
+ QVERIFY(!dynamic_cast<QWindow *>(object));
+ QVERIFY(!object->isWindowType());
+ });
+}
+
+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)