summaryrefslogtreecommitdiffstats
path: root/tests/auto/gui/kernel/qwindow
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/gui/kernel/qwindow')
-rw-r--r--tests/auto/gui/kernel/qwindow/BLACKLIST7
-rw-r--r--tests/auto/gui/kernel/qwindow/CMakeLists.txt16
-rw-r--r--tests/auto/gui/kernel/qwindow/tst_foreignwindow.cpp229
-rw-r--r--tests/auto/gui/kernel/qwindow/tst_qwindow.cpp355
4 files changed, 520 insertions, 87 deletions
diff --git a/tests/auto/gui/kernel/qwindow/BLACKLIST b/tests/auto/gui/kernel/qwindow/BLACKLIST
index 328d3f51b1..69df8883c8 100644
--- a/tests/auto/gui/kernel/qwindow/BLACKLIST
+++ b/tests/auto/gui/kernel/qwindow/BLACKLIST
@@ -11,8 +11,6 @@ windows
# QTBUG-69162
windows-10
android
-[testInputEvents]
-rhel-7.4
[modalWindowPosition]
# QTBUG-69161
android
@@ -23,3 +21,8 @@ android
android
[modalWindowModallity]
android
+[enterLeaveOnWindowShowHide]
+windows-10
+windows-11
+android
+rhel
diff --git a/tests/auto/gui/kernel/qwindow/CMakeLists.txt b/tests/auto/gui/kernel/qwindow/CMakeLists.txt
index 605761622f..5824989ac3 100644
--- a/tests/auto/gui/kernel/qwindow/CMakeLists.txt
+++ b/tests/auto/gui/kernel/qwindow/CMakeLists.txt
@@ -5,6 +5,12 @@
## tst_qwindow Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qwindow LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qwindow
SOURCES
tst_qwindow.cpp
@@ -14,8 +20,9 @@ qt_internal_add_test(tst_qwindow
Qt::GuiPrivate
)
-if(APPLE OR WIN32)
+if(APPLE OR WIN32 OR QT_FEATURE_xcb)
qt_internal_add_test(tst_foreignwindow
+ LOWDPI
SOURCES
tst_foreignwindow.cpp
LIBRARIES
@@ -25,9 +32,14 @@ if(APPLE OR WIN32)
)
if(APPLE)
- target_compile_options(tst_foreignwindow PRIVATE -x objective-c++)
+ enable_language(OBJCXX)
+ set_source_files_properties(tst_foreignwindow.cpp PROPERTIES LANGUAGE OBJCXX)
set_property(TARGET tst_foreignwindow PROPERTY PROPERTY MACOSX_BUNDLE TRUE)
endif()
+
+ if(QT_FEATURE_xcb)
+ target_link_libraries(tst_foreignwindow PRIVATE XCB::XCB)
+ endif()
endif()
## Scopes:
diff --git a/tests/auto/gui/kernel/qwindow/tst_foreignwindow.cpp b/tests/auto/gui/kernel/qwindow/tst_foreignwindow.cpp
index 256564f6bc..fdb1b333ef 100644
--- a/tests/auto/gui/kernel/qwindow/tst_foreignwindow.cpp
+++ b/tests/auto/gui/kernel/qwindow/tst_foreignwindow.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2023 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 <QTest>
@@ -7,114 +7,189 @@
#include <private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
-#if defined(Q_OS_MACOS)
-# include <AppKit/AppKit.h>
-#elif defined(Q_OS_WIN)
-# include <winuser.h>
-#endif
-
-Q_LOGGING_CATEGORY(lcTests, "qt.gui.tests")
+#include "../../../../shared/nativewindow.h"
-class NativeWindow
+class tst_ForeignWindow: public QObject
{
- Q_DISABLE_COPY(NativeWindow)
-public:
- NativeWindow();
- ~NativeWindow();
-
- operator WId() const { return reinterpret_cast<WId>(m_handle); }
-
-private:
-#if defined(Q_OS_MACOS)
- NSView *m_handle = nullptr;
-#elif defined(Q_OS_WIN)
- HWND m_handle = nullptr;
-#endif
-};
+ Q_OBJECT
-#if defined(Q_OS_MACOS)
+private slots:
+ void initTestCase()
+ {
+ auto *platformIntegration = QGuiApplicationPrivate::platformIntegration();
+ if (!platformIntegration->hasCapability(QPlatformIntegration::ForeignWindows))
+ QSKIP("This platform does not support foreign windows");
+ }
-@interface View : NSView
-@end
+ void fromWinId();
+ void initialState();
-@implementation View
-- (instancetype)init
-{
- if ((self = [super init])) {
- qCDebug(lcTests) << "Initialized" << self;
- }
- return self;
-}
+ void embedForeignWindow();
+ void embedInForeignWindow();
-- (void)dealloc
-{
- qCDebug(lcTests) << "Deallocating" << self;
- [super dealloc];
-}
-@end
+ void destroyExplicitly();
+ void destroyWhenParentIsDestroyed();
+};
-NativeWindow::NativeWindow()
- : m_handle([View new])
+void tst_ForeignWindow::fromWinId()
{
+ NativeWindow nativeWindow;
+ QVERIFY(nativeWindow);
+
+ std::unique_ptr<QWindow> foreignWindow(QWindow::fromWinId(nativeWindow));
+ QVERIFY(foreignWindow);
+ QVERIFY(foreignWindow->flags().testFlag(Qt::ForeignWindow));
+ QVERIFY(foreignWindow->handle());
+
+ // fromWinId does not take (exclusive) ownership of the native window,
+ // so deleting the foreign window should not be a problem/cause crashes.
+ foreignWindow.reset();
}
-NativeWindow::~NativeWindow()
+void tst_ForeignWindow::initialState()
{
- [m_handle release];
-}
+ NativeWindow nativeWindow;
+ QVERIFY(nativeWindow);
-#elif defined(Q_OS_WIN)
+ // A foreign window can be used to embed a Qt UI in a foreign window hierarchy,
+ // in which case the foreign window merely acts as a parent and should not be
+ // modified, or to embed a foreign window in a Qt UI, in which case the foreign
+ // window must to be able to re-parent, move, resize, show, etc, so that the
+ // containing Qt UI can treat it as any other window.
-NativeWindow::NativeWindow()
-{
- static const LPCWSTR className = []{
- WNDCLASS wc = {};
- wc.lpfnWndProc = DefWindowProc;
- wc.hInstance = GetModuleHandle(nullptr);
- wc.lpszClassName = L"Native Window";
- RegisterClass(&wc);
- return wc.lpszClassName;
- }();
- m_handle = CreateWindowEx(0, className, nullptr, WS_POPUP,
- CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
- nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
+ // At the point of creation though, we don't know what the foreign window
+ // will be used for, so the platform should not assume it can modify the
+ // window. Any properties set on the native window should persist past
+ // creation of the foreign window.
+
+ const QRect initialGeometry(123, 456, 321, 654);
+ nativeWindow.setGeometry(initialGeometry);
+ QTRY_COMPARE(nativeWindow.geometry(), initialGeometry);
+
+ std::unique_ptr<QWindow> foreignWindow(QWindow::fromWinId(nativeWindow));
+ QCOMPARE(nativeWindow.geometry(), initialGeometry);
+
+ // For extra bonus points, the foreign window should actually
+ // reflect the state of the native window.
+ QCOMPARE(foreignWindow->geometry(), initialGeometry);
}
-NativeWindow::~NativeWindow()
+void tst_ForeignWindow::embedForeignWindow()
{
- DestroyWindow(m_handle);
-}
+ // A foreign window embedded into a Qt UI requires that the rest of Qt
+ // is to be able to treat the foreign child window as any other window
+ // that it can show, hide, stack, and move around.
+
+ QWindow parentWindow;
+
+ NativeWindow nativeWindow;
+ QVERIFY(nativeWindow);
+
+ // As a prerequisite to that, we must be able to reparent the foreign window
+ std::unique_ptr<QWindow> foreignWindow(QWindow::fromWinId(nativeWindow));
+ foreignWindow->setParent(&parentWindow);
+ QTRY_COMPARE(nativeWindow.parentWinId(), parentWindow.winId());
+ // FIXME: This test is flakey on Linux. Figure out why
+#if !defined(Q_OS_LINUX)
+ foreignWindow->setParent(nullptr);
+ QTRY_VERIFY(nativeWindow.parentWinId() != parentWindow.winId());
#endif
+}
-class tst_ForeignWindow: public QObject
+void tst_ForeignWindow::embedInForeignWindow()
{
- Q_OBJECT
+ // When a foreign window is used as a container to embed a Qt UI
+ // in a foreign window hierarchy, the foreign window merely acts
+ // as a parent, and should not be modified.
-private slots:
- void initTestCase()
{
- auto *platformIntegration = QGuiApplicationPrivate::platformIntegration();
- if (!platformIntegration->hasCapability(QPlatformIntegration::ForeignWindows))
- QSKIP("This platform does not support foreign windows");
+ // At a minimum, we must be able to reparent into the window
+ NativeWindow nativeWindow;
+ QVERIFY(nativeWindow);
+
+ std::unique_ptr<QWindow> foreignWindow(QWindow::fromWinId(nativeWindow));
+
+ QWindow embeddedWindow;
+ embeddedWindow.setParent(foreignWindow.get());
+ QTRY_VERIFY(nativeWindow.isParentOf(embeddedWindow.winId()));
}
- void fromWinId();
-};
+ {
+ // The foreign window's native window should not be reparent as a
+ // result of creating the foreign window, adding and removing children,
+ // or destroying the foreign window.
-void tst_ForeignWindow::fromWinId()
+ NativeWindow topLevelNativeWindow;
+ NativeWindow childNativeWindow;
+ childNativeWindow.setParent(topLevelNativeWindow);
+ QVERIFY(topLevelNativeWindow.isParentOf(childNativeWindow));
+
+ std::unique_ptr<QWindow> foreignWindow(QWindow::fromWinId(childNativeWindow));
+ QVERIFY(topLevelNativeWindow.isParentOf(childNativeWindow));
+
+ QWindow embeddedWindow;
+ embeddedWindow.setParent(foreignWindow.get());
+ QTRY_VERIFY(childNativeWindow.isParentOf(embeddedWindow.winId()));
+ QVERIFY(topLevelNativeWindow.isParentOf(childNativeWindow));
+
+ embeddedWindow.setParent(nullptr);
+ QVERIFY(topLevelNativeWindow.isParentOf(childNativeWindow));
+
+ foreignWindow.reset();
+ QVERIFY(topLevelNativeWindow.isParentOf(childNativeWindow));
+ }
+}
+
+void tst_ForeignWindow::destroyExplicitly()
{
NativeWindow nativeWindow;
QVERIFY(nativeWindow);
std::unique_ptr<QWindow> foreignWindow(QWindow::fromWinId(nativeWindow));
- QVERIFY(foreignWindow);
- QVERIFY(foreignWindow->flags().testFlag(Qt::ForeignWindow));
QVERIFY(foreignWindow->handle());
- // fromWinId does not take (exclusive) ownership of the native window,
- // so deleting the foreign window should not be a problem/cause crashes.
- foreignWindow.reset();
+ // Explicitly destroying a foreign window is a no-op, as
+ // the documentation claims that it "releases the native
+ // platform resources associated with this window.", which
+ // is not technically true for foreign windows.
+ auto *windowHandleBeforeDestroy = foreignWindow->handle();
+ foreignWindow->destroy();
+ QCOMPARE(foreignWindow->handle(), windowHandleBeforeDestroy);
+}
+
+void tst_ForeignWindow::destroyWhenParentIsDestroyed()
+{
+ QWindow parentWindow;
+
+ NativeWindow nativeWindow;
+ QVERIFY(nativeWindow);
+
+ std::unique_ptr<QWindow> foreignWindow(QWindow::fromWinId(nativeWindow));
+ foreignWindow->setParent(&parentWindow);
+ QTRY_COMPARE(nativeWindow.parentWinId(), parentWindow.winId());
+
+ // Reparenting into a window will result in creating it
+ QVERIFY(parentWindow.handle());
+
+ parentWindow.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&parentWindow));
+
+ // Destroying the parent window of the foreign window results
+ // in destroying the foreign window as well, as the foreign
+ // window no longer has a parent it can be embedded in.
+ QVERIFY(foreignWindow->handle());
+ parentWindow.destroy();
+ QVERIFY(!foreignWindow->handle());
+
+ // But the foreign window can be recreated again, and will
+ // continue to be a native child of the parent window.
+ foreignWindow->create();
+ QVERIFY(foreignWindow->handle());
+ QTRY_COMPARE(nativeWindow.parentWinId(), parentWindow.winId());
+
+ parentWindow.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&parentWindow));
}
#include <tst_foreignwindow.moc>
diff --git a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp
index 1cac1820d9..227fd77e1a 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();
@@ -89,6 +97,13 @@ private slots:
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;
@@ -106,6 +121,10 @@ static bool isPlatformWayland()
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;
@@ -231,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;
@@ -429,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());
@@ -616,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);
@@ -1449,6 +1509,7 @@ void tst_QWindow::touchCancelWithTouchToMouse()
void tst_QWindow::touchInterruptedByPopup()
{
InputTestWindow window;
+ window.setObjectName("main");
window.setTitle(QLatin1String(QTest::currentTestFunction()));
window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
window.show();
@@ -1469,6 +1530,7 @@ void tst_QWindow::touchInterruptedByPopup()
// Launch a popup window
InputTestWindow popup;
+ window.setObjectName("popup");
popup.setFlags(Qt::Popup);
popup.setModality(Qt::WindowModal);
popup.resize(m_testWindowSize / 2);
@@ -1491,9 +1553,6 @@ void tst_QWindow::touchInterruptedByPopup()
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
QCoreApplication::processEvents();
QTRY_COMPARE(window.touchReleasedCount, 0);
-
- // Due to temporary fix for QTBUG-37371: the original window should receive a TouchCancel
- QTRY_COMPARE(window.touchEventType, QEvent::TouchCancel);
}
void tst_QWindow::orientation()
@@ -1576,6 +1635,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
@@ -2080,6 +2146,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);
}
@@ -2091,6 +2161,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);
}
}
@@ -2266,6 +2338,10 @@ 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);
}
@@ -2294,6 +2370,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;
@@ -2474,6 +2553,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);
@@ -2788,11 +2869,11 @@ void tst_QWindow::stateChangeSignal()
"On other operating systems, the signal may be emitted twice.");
#endif
QWindow w;
- Q_ASSUME(connect (&w, &QWindow::windowStateChanged, [](Qt::WindowState s){qCDebug(lcTests) << "State change to" << s;}));
+ 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_ASSUME(connect(&w, &QWindow::windowStateChanged, [&effectiveStates](Qt::WindowState state)
+ Q_ASSERT(connect(&w, &QWindow::windowStateChanged, [&effectiveStates](Qt::WindowState state)
{ effectiveStates.append(state); }));
// Part 1:
// => test signal emission on programmatic state changes
@@ -2876,6 +2957,268 @@ void tst_QWindow::stateChangeSignal()
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)