| /**************************************************************************** |
| ** |
| ** 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$ |
| ** |
| ****************************************************************************/ |
| |
| |
| #include <QtTest/QtTest> |
| |
| #include <qapplication.h> |
| #include <qwindow.h> |
| #include <qwidget.h> |
| |
| #include <qdockwidget.h> |
| #include <qmainwindow.h> |
| #include <qscreen.h> |
| #include <qscopedpointer.h> |
| #include <qevent.h> |
| |
| |
| class Window : public QWindow |
| { |
| public: |
| Window() |
| : numberOfExposes(0) |
| , numberOfObscures(0) |
| { |
| } |
| |
| void exposeEvent(QExposeEvent *) { |
| if (isExposed()) |
| ++numberOfExposes; |
| else |
| ++numberOfObscures; |
| } |
| |
| int numberOfExposes; |
| int numberOfObscures; |
| }; |
| |
| class tst_QWindowContainer: public QObject |
| { |
| Q_OBJECT |
| |
| public: |
| tst_QWindowContainer() : m_availableGeometry(QGuiApplication::primaryScreen()->availableGeometry()) {} |
| |
| private slots: |
| void testShow(); |
| void testPositionAndSize(); |
| void testExposeObscure(); |
| void testOwnership(); |
| void testBehindTheScenesDeletion(); |
| void testUnparenting(); |
| void testUnparentReparent(); |
| void testActivation(); |
| void testAncestorChange(); |
| void testDockWidget(); |
| void testNativeContainerParent(); |
| void testPlatformSurfaceEvent(); |
| void cleanup(); |
| |
| private: |
| const QRect m_availableGeometry; |
| }; |
| |
| void tst_QWindowContainer::cleanup() |
| { |
| QVERIFY(QGuiApplication::topLevelWindows().isEmpty()); |
| } |
| |
| void tst_QWindowContainer::testShow() |
| { |
| if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) |
| QSKIP("Wayland: This fails. Figure out why."); |
| |
| QWidget root; |
| root.setWindowTitle(QTest::currentTestFunction()); |
| root.setGeometry(m_availableGeometry.x() + 100, m_availableGeometry.y() + 100, 400, 400); |
| |
| Window *window = new Window(); |
| QWidget *container = QWidget::createWindowContainer(window, &root); |
| |
| container->setGeometry(50, 50, 200, 200); |
| |
| root.show(); |
| |
| #ifdef Q_OS_WINRT |
| QEXPECT_FAIL("", "Fails on WinRT - QTBUG-68297", Abort); |
| #endif |
| QVERIFY(QTest::qWaitForWindowExposed(window)); |
| } |
| |
| |
| |
| void tst_QWindowContainer::testPositionAndSize() |
| { |
| QWindow *window = new QWindow(); |
| window->setGeometry(m_availableGeometry.x() + 300, m_availableGeometry.y() + 400, 500, 600); |
| |
| QScopedPointer<QWidget> container(QWidget::createWindowContainer(window)); |
| container->setWindowTitle(QTest::currentTestFunction()); |
| container->setGeometry(50, 50, 200, 200); |
| |
| |
| container->show(); |
| QVERIFY(QTest::qWaitForWindowExposed(container.data())); |
| |
| QCOMPARE(window->x(), 0); |
| QCOMPARE(window->y(), 0); |
| QCOMPARE(window->width(), container->width()); |
| QCOMPARE(window->height(), container->height()); |
| } |
| |
| |
| |
| void tst_QWindowContainer::testExposeObscure() |
| { |
| if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) |
| QSKIP("Wayland: This fails. Figure out why."); |
| |
| Window *window = new Window(); |
| |
| QScopedPointer<QWidget> container(QWidget::createWindowContainer(window)); |
| container->setWindowTitle(QTest::currentTestFunction()); |
| container->setGeometry(m_availableGeometry.x() + 50, m_availableGeometry.y() + 50, 200, 200); |
| |
| container->show(); |
| QVERIFY(QTest::qWaitForWindowExposed(container.data())); |
| #ifdef Q_OS_WINRT |
| QEXPECT_FAIL("", "Fails on WinRT - QTBUG-68297", Abort); |
| #endif |
| QVERIFY(QTest::qWaitForWindowExposed(window)); |
| |
| QVERIFY(window->numberOfExposes > 0); |
| |
| container->hide(); |
| |
| QTRY_VERIFY(window->numberOfObscures > 0); |
| } |
| |
| |
| |
| void tst_QWindowContainer::testOwnership() |
| { |
| QPointer<QWindow> window(new QWindow()); |
| QWidget *container = QWidget::createWindowContainer(window); |
| |
| delete container; |
| |
| QCOMPARE(window.data(), nullptr); |
| } |
| |
| |
| |
| void tst_QWindowContainer::testBehindTheScenesDeletion() |
| { |
| QWindow *window = new QWindow(); |
| QWidget *container = QWidget::createWindowContainer(window); |
| |
| delete window; |
| |
| // The child got removed, showing not should not have any side effects, |
| // such as for instance, crashing... |
| container->show(); |
| QVERIFY(QTest::qWaitForWindowExposed(container)); |
| delete container; |
| } |
| |
| |
| |
| void tst_QWindowContainer::testActivation() |
| { |
| if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) |
| QSKIP("Wayland: This fails. Figure out why."); |
| |
| QWidget root; |
| root.setWindowTitle(QTest::currentTestFunction()); |
| |
| QWindow *window = new QWindow(); |
| QWidget *container = QWidget::createWindowContainer(window, &root); |
| |
| container->setGeometry(100, 100, 200, 100); |
| root.setGeometry(m_availableGeometry.x() + 100, m_availableGeometry.y() + 100, 400, 300); |
| |
| root.show(); |
| root.activateWindow(); |
| QVERIFY(QTest::qWaitForWindowExposed(&root)); |
| |
| QVERIFY(QTest::qWaitForWindowActive(root.windowHandle())); |
| QCOMPARE(QGuiApplication::focusWindow(), root.windowHandle()); |
| |
| // Verify that all states in the root widget indicate it is active |
| QVERIFY(root.windowHandle()->isActive()); |
| QVERIFY(root.isActiveWindow()); |
| QCOMPARE(root.palette().currentColorGroup(), QPalette::Active); |
| |
| // Under KDE (ubuntu 12.10), we experience that doing two activateWindow in a row |
| // does not work. The second gets ignored by the window manager, even though the |
| // timestamp in the xcb connection is unique for both. |
| if (!QGuiApplication::platformName().compare(QLatin1String("xcb"), Qt::CaseInsensitive)) |
| QTest::qWait(100); |
| |
| window->requestActivate(); |
| QTRY_COMPARE(QGuiApplication::focusWindow(), window); |
| |
| // Verify that all states in the root widget still indicate it is active |
| QVERIFY(root.windowHandle()->isActive()); |
| QVERIFY(root.isActiveWindow()); |
| QCOMPARE(root.palette().currentColorGroup(), QPalette::Active); |
| } |
| |
| |
| |
| void tst_QWindowContainer::testUnparenting() |
| { |
| QWindow *window = new QWindow(); |
| QScopedPointer<QWidget> container(QWidget::createWindowContainer(window)); |
| container->setWindowTitle(QTest::currentTestFunction()); |
| container->setGeometry(m_availableGeometry.x() + 100, m_availableGeometry.y() + 100, 200, 100); |
| |
| window->setParent(0); |
| |
| container->show(); |
| |
| QVERIFY(QTest::qWaitForWindowExposed(container.data())); |
| |
| // Window should not be made visible by container.. |
| QVERIFY(!window->isVisible()); |
| } |
| |
| void tst_QWindowContainer::testUnparentReparent() |
| { |
| if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) |
| QSKIP("Wayland: This fails. Figure out why."); |
| |
| QWidget root; |
| |
| QWindow *window = new QWindow(); |
| QScopedPointer<QWidget> container(QWidget::createWindowContainer(window, &root)); |
| container->setWindowTitle(QTest::currentTestFunction()); |
| container->setGeometry(m_availableGeometry.x() + 100, m_availableGeometry.y() + 100, 200, 100); |
| |
| root.show(); |
| |
| QVERIFY(QTest::qWaitForWindowExposed(&root)); |
| |
| QTRY_VERIFY(window->isVisible()); |
| |
| container->setParent(nullptr); |
| QTRY_VERIFY(!window->isVisible()); |
| |
| container->show(); |
| #ifdef Q_OS_WINRT |
| QEXPECT_FAIL("", "Fails on WinRT - QTBUG-68297", Abort); |
| #endif |
| QVERIFY(QTest::qWaitForWindowExposed(window)); |
| QTRY_VERIFY(window->isVisible()); |
| |
| container->setParent(&root); // This should not crash (QTBUG-63168) |
| } |
| |
| void tst_QWindowContainer::testAncestorChange() |
| { |
| QWidget root; |
| root.setWindowTitle(QStringLiteral("Root ") + QTest::currentTestFunction()); |
| QWidget *left = new QWidget(&root); |
| QWidget *right = new QWidget(&root); |
| |
| |
| root.setGeometry(m_availableGeometry.x() + 50, m_availableGeometry.y() + 50, 200, 100); |
| left->setGeometry(0, 0, 100, 100); |
| right->setGeometry(100, 0, 100, 100); |
| |
| QWindow *window = new QWindow(); |
| QWidget *container = QWidget::createWindowContainer(window, left); |
| container->setGeometry(0, 0, 100, 100); |
| |
| // Root |
| // + left |
| // | + container |
| // | + window |
| // + right |
| root.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&root)); |
| QCOMPARE(window->geometry(), QRect(0, 0, 100, 100)); |
| |
| container->setParent(right); |
| // Root |
| // + left |
| // + right |
| // + container |
| // + window |
| QCOMPARE(window->geometry(), QRect(100, 0, 100, 100)); |
| |
| QWidget *newRoot = new QWidget(&root); |
| newRoot->setWindowTitle(QStringLiteral("newRoot ") + QTest::currentTestFunction()); |
| newRoot->setGeometry(50, 50, 200, 200); |
| right->setParent(newRoot); |
| // Root |
| // + left |
| // + newRoot |
| // + right |
| // + container |
| // + window |
| QCOMPARE(window->geometry(), QRect(150, 50, 100, 100)); |
| newRoot->move(0, 0); |
| QCOMPARE(window->geometry(), QRect(100, 0, 100, 100)); |
| |
| newRoot->setParent(0); |
| QScopedPointer<QWidget> newRootGuard(newRoot); |
| newRoot->setGeometry(m_availableGeometry.x() + 100, m_availableGeometry.y() + 100, 200, 200); |
| newRoot->show(); |
| QVERIFY(QTest::qWaitForWindowExposed(newRoot)); |
| QCOMPARE(newRoot->windowHandle(), window->parent()); |
| // newRoot |
| // + right |
| // + container |
| // + window |
| QCOMPARE(window->geometry(), QRect(100, 0, 100, 100)); |
| } |
| |
| |
| void tst_QWindowContainer::testDockWidget() |
| { |
| QMainWindow mainWindow; |
| mainWindow.setWindowTitle(QTest::currentTestFunction()); |
| mainWindow.resize(200, 200); |
| mainWindow.move(m_availableGeometry.center() - QPoint(100, 100)); |
| |
| QDockWidget *dock = new QDockWidget(QStringLiteral("Dock ") + QTest::currentTestFunction()); |
| QWindow *window = new QWindow(); |
| QWidget *container = QWidget::createWindowContainer(window); |
| dock->setWidget(container); |
| mainWindow.addDockWidget(Qt::RightDockWidgetArea, dock); |
| |
| mainWindow.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&mainWindow)); |
| QCOMPARE(window->parent(), mainWindow.window()->windowHandle()); |
| |
| dock->setFloating(true); |
| QTRY_VERIFY(window->parent() != mainWindow.window()->windowHandle()); |
| |
| dock->setFloating(false); |
| QTRY_COMPARE(window->parent(), mainWindow.window()->windowHandle()); |
| } |
| |
| void tst_QWindowContainer::testNativeContainerParent() |
| { |
| if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) |
| QSKIP("Wayland: This fails. Figure out why."); |
| |
| QWidget root; |
| root.setWindowTitle(QTest::currentTestFunction()); |
| root.setGeometry(m_availableGeometry.x() + 50, m_availableGeometry.y() + 50, 200, 200); |
| |
| Window *window = new Window(); |
| QWidget *container = QWidget::createWindowContainer(window, &root); |
| container->setAttribute(Qt::WA_NativeWindow); |
| container->setGeometry(50, 50, 150, 150); |
| |
| root.show(); |
| |
| #ifdef Q_OS_WINRT |
| QEXPECT_FAIL("", "Fails on WinRT - QTBUG-68297", Abort); |
| #endif |
| QVERIFY(QTest::qWaitForWindowExposed(window)); |
| QTRY_COMPARE(window->parent(), container->windowHandle()); |
| } |
| |
| class EventWindow : public QWindow |
| { |
| public: |
| EventWindow(bool *surfaceDestroyFlag) : m_surfaceDestroyFlag(surfaceDestroyFlag) { } |
| bool event(QEvent *e) override; |
| |
| private: |
| bool *m_surfaceDestroyFlag; |
| }; |
| |
| bool EventWindow::event(QEvent *e) |
| { |
| if (e->type() == QEvent::PlatformSurface) { |
| if (static_cast<QPlatformSurfaceEvent *>(e)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) |
| *m_surfaceDestroyFlag = true; |
| } |
| return QWindow::event(e); |
| } |
| |
| void tst_QWindowContainer::testPlatformSurfaceEvent() |
| { |
| // Verify that SurfaceAboutToBeDestroyed is delivered and the |
| // window subclass still gets a chance to process it. |
| |
| bool ok = false; |
| QPointer<EventWindow> window(new EventWindow(&ok)); |
| window->create(); |
| QWidget *container = QWidget::createWindowContainer(window); |
| |
| delete container; |
| |
| QCOMPARE(window.data(), nullptr); |
| QVERIFY(ok); |
| } |
| |
| QTEST_MAIN(tst_QWindowContainer) |
| |
| #include "tst_qwindowcontainer.moc" |