1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtTest/QtTest>
31
32#include <qapplication.h>
33#include <qwindow.h>
34#include <qwidget.h>
35
36#include <qdockwidget.h>
37#include <qmainwindow.h>
38#include <qscreen.h>
39#include <qscopedpointer.h>
40#include <qevent.h>
41
42
43class Window : public QWindow
44{
45public:
46 Window()
47 : numberOfExposes(0)
48 , numberOfObscures(0)
49 {
50 }
51
52 void exposeEvent(QExposeEvent *) {
53 if (isExposed())
54 ++numberOfExposes;
55 else
56 ++numberOfObscures;
57 }
58
59 int numberOfExposes;
60 int numberOfObscures;
61};
62
63class tst_QWindowContainer: public QObject
64{
65 Q_OBJECT
66
67public:
68 tst_QWindowContainer() : m_availableGeometry(QGuiApplication::primaryScreen()->availableGeometry()) {}
69
70private slots:
71 void testShow();
72 void testPositionAndSize();
73 void testExposeObscure();
74 void testOwnership();
75 void testBehindTheScenesDeletion();
76 void testUnparenting();
77 void testUnparentReparent();
78 void testActivation();
79 void testAncestorChange();
80 void testDockWidget();
81 void testNativeContainerParent();
82 void testPlatformSurfaceEvent();
83 void cleanup();
84
85private:
86 const QRect m_availableGeometry;
87};
88
89void tst_QWindowContainer::cleanup()
90{
91 QVERIFY(QGuiApplication::topLevelWindows().isEmpty());
92}
93
94void tst_QWindowContainer::testShow()
95{
96 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
97 QSKIP("Wayland: This fails. Figure out why.");
98
99 QWidget root;
100 root.setWindowTitle(QTest::currentTestFunction());
101 root.setGeometry(ax: m_availableGeometry.x() + 100, ay: m_availableGeometry.y() + 100, aw: 400, ah: 400);
102
103 Window *window = new Window();
104 QWidget *container = QWidget::createWindowContainer(window, parent: &root);
105
106 container->setGeometry(ax: 50, ay: 50, aw: 200, ah: 200);
107
108 root.show();
109
110#ifdef Q_OS_WINRT
111 QEXPECT_FAIL("", "Fails on WinRT - QTBUG-68297", Abort);
112#endif
113 QVERIFY(QTest::qWaitForWindowExposed(window));
114}
115
116
117
118void tst_QWindowContainer::testPositionAndSize()
119{
120 QWindow *window = new QWindow();
121 window->setGeometry(posx: m_availableGeometry.x() + 300, posy: m_availableGeometry.y() + 400, w: 500, h: 600);
122
123 QScopedPointer<QWidget> container(QWidget::createWindowContainer(window));
124 container->setWindowTitle(QTest::currentTestFunction());
125 container->setGeometry(ax: 50, ay: 50, aw: 200, ah: 200);
126
127
128 container->show();
129 QVERIFY(QTest::qWaitForWindowExposed(container.data()));
130
131 QCOMPARE(window->x(), 0);
132 QCOMPARE(window->y(), 0);
133 QCOMPARE(window->width(), container->width());
134 QCOMPARE(window->height(), container->height());
135}
136
137
138
139void tst_QWindowContainer::testExposeObscure()
140{
141 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
142 QSKIP("Wayland: This fails. Figure out why.");
143
144 Window *window = new Window();
145
146 QScopedPointer<QWidget> container(QWidget::createWindowContainer(window));
147 container->setWindowTitle(QTest::currentTestFunction());
148 container->setGeometry(ax: m_availableGeometry.x() + 50, ay: m_availableGeometry.y() + 50, aw: 200, ah: 200);
149
150 container->show();
151 QVERIFY(QTest::qWaitForWindowExposed(container.data()));
152#ifdef Q_OS_WINRT
153 QEXPECT_FAIL("", "Fails on WinRT - QTBUG-68297", Abort);
154#endif
155 QVERIFY(QTest::qWaitForWindowExposed(window));
156
157 QVERIFY(window->numberOfExposes > 0);
158
159 container->hide();
160
161 QTRY_VERIFY(window->numberOfObscures > 0);
162}
163
164
165
166void tst_QWindowContainer::testOwnership()
167{
168 QPointer<QWindow> window(new QWindow());
169 QWidget *container = QWidget::createWindowContainer(window);
170
171 delete container;
172
173 QCOMPARE(window.data(), nullptr);
174}
175
176
177
178void tst_QWindowContainer::testBehindTheScenesDeletion()
179{
180 QWindow *window = new QWindow();
181 QWidget *container = QWidget::createWindowContainer(window);
182
183 delete window;
184
185 // The child got removed, showing not should not have any side effects,
186 // such as for instance, crashing...
187 container->show();
188 QVERIFY(QTest::qWaitForWindowExposed(container));
189 delete container;
190}
191
192
193
194void tst_QWindowContainer::testActivation()
195{
196 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
197 QSKIP("Wayland: This fails. Figure out why.");
198
199 QWidget root;
200 root.setWindowTitle(QTest::currentTestFunction());
201
202 QWindow *window = new QWindow();
203 QWidget *container = QWidget::createWindowContainer(window, parent: &root);
204
205 container->setGeometry(ax: 100, ay: 100, aw: 200, ah: 100);
206 root.setGeometry(ax: m_availableGeometry.x() + 100, ay: m_availableGeometry.y() + 100, aw: 400, ah: 300);
207
208 root.show();
209 root.activateWindow();
210 QVERIFY(QTest::qWaitForWindowExposed(&root));
211
212 QVERIFY(QTest::qWaitForWindowActive(root.windowHandle()));
213 QCOMPARE(QGuiApplication::focusWindow(), root.windowHandle());
214
215 // Verify that all states in the root widget indicate it is active
216 QVERIFY(root.windowHandle()->isActive());
217 QVERIFY(root.isActiveWindow());
218 QCOMPARE(root.palette().currentColorGroup(), QPalette::Active);
219
220 // Under KDE (ubuntu 12.10), we experience that doing two activateWindow in a row
221 // does not work. The second gets ignored by the window manager, even though the
222 // timestamp in the xcb connection is unique for both.
223 if (!QGuiApplication::platformName().compare(other: QLatin1String("xcb"), cs: Qt::CaseInsensitive))
224 QTest::qWait(ms: 100);
225
226 window->requestActivate();
227 QTRY_COMPARE(QGuiApplication::focusWindow(), window);
228
229 // Verify that all states in the root widget still indicate it is active
230 QVERIFY(root.windowHandle()->isActive());
231 QVERIFY(root.isActiveWindow());
232 QCOMPARE(root.palette().currentColorGroup(), QPalette::Active);
233}
234
235
236
237void tst_QWindowContainer::testUnparenting()
238{
239 QWindow *window = new QWindow();
240 QScopedPointer<QWidget> container(QWidget::createWindowContainer(window));
241 container->setWindowTitle(QTest::currentTestFunction());
242 container->setGeometry(ax: m_availableGeometry.x() + 100, ay: m_availableGeometry.y() + 100, aw: 200, ah: 100);
243
244 window->setParent(0);
245
246 container->show();
247
248 QVERIFY(QTest::qWaitForWindowExposed(container.data()));
249
250 // Window should not be made visible by container..
251 QVERIFY(!window->isVisible());
252}
253
254void tst_QWindowContainer::testUnparentReparent()
255{
256 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
257 QSKIP("Wayland: This fails. Figure out why.");
258
259 QWidget root;
260
261 QWindow *window = new QWindow();
262 QScopedPointer<QWidget> container(QWidget::createWindowContainer(window, parent: &root));
263 container->setWindowTitle(QTest::currentTestFunction());
264 container->setGeometry(ax: m_availableGeometry.x() + 100, ay: m_availableGeometry.y() + 100, aw: 200, ah: 100);
265
266 root.show();
267
268 QVERIFY(QTest::qWaitForWindowExposed(&root));
269
270 QTRY_VERIFY(window->isVisible());
271
272 container->setParent(nullptr);
273 QTRY_VERIFY(!window->isVisible());
274
275 container->show();
276#ifdef Q_OS_WINRT
277 QEXPECT_FAIL("", "Fails on WinRT - QTBUG-68297", Abort);
278#endif
279 QVERIFY(QTest::qWaitForWindowExposed(window));
280 QTRY_VERIFY(window->isVisible());
281
282 container->setParent(&root); // This should not crash (QTBUG-63168)
283}
284
285void tst_QWindowContainer::testAncestorChange()
286{
287 QWidget root;
288 root.setWindowTitle(QStringLiteral("Root ") + QTest::currentTestFunction());
289 QWidget *left = new QWidget(&root);
290 QWidget *right = new QWidget(&root);
291
292
293 root.setGeometry(ax: m_availableGeometry.x() + 50, ay: m_availableGeometry.y() + 50, aw: 200, ah: 100);
294 left->setGeometry(ax: 0, ay: 0, aw: 100, ah: 100);
295 right->setGeometry(ax: 100, ay: 0, aw: 100, ah: 100);
296
297 QWindow *window = new QWindow();
298 QWidget *container = QWidget::createWindowContainer(window, parent: left);
299 container->setGeometry(ax: 0, ay: 0, aw: 100, ah: 100);
300
301 // Root
302 // + left
303 // | + container
304 // | + window
305 // + right
306 root.show();
307 QVERIFY(QTest::qWaitForWindowExposed(&root));
308 QCOMPARE(window->geometry(), QRect(0, 0, 100, 100));
309
310 container->setParent(right);
311 // Root
312 // + left
313 // + right
314 // + container
315 // + window
316 QCOMPARE(window->geometry(), QRect(100, 0, 100, 100));
317
318 QWidget *newRoot = new QWidget(&root);
319 newRoot->setWindowTitle(QStringLiteral("newRoot ") + QTest::currentTestFunction());
320 newRoot->setGeometry(ax: 50, ay: 50, aw: 200, ah: 200);
321 right->setParent(newRoot);
322 // Root
323 // + left
324 // + newRoot
325 // + right
326 // + container
327 // + window
328 QCOMPARE(window->geometry(), QRect(150, 50, 100, 100));
329 newRoot->move(ax: 0, ay: 0);
330 QCOMPARE(window->geometry(), QRect(100, 0, 100, 100));
331
332 newRoot->setParent(0);
333 QScopedPointer<QWidget> newRootGuard(newRoot);
334 newRoot->setGeometry(ax: m_availableGeometry.x() + 100, ay: m_availableGeometry.y() + 100, aw: 200, ah: 200);
335 newRoot->show();
336 QVERIFY(QTest::qWaitForWindowExposed(newRoot));
337 QCOMPARE(newRoot->windowHandle(), window->parent());
338 // newRoot
339 // + right
340 // + container
341 // + window
342 QCOMPARE(window->geometry(), QRect(100, 0, 100, 100));
343}
344
345
346void tst_QWindowContainer::testDockWidget()
347{
348 QMainWindow mainWindow;
349 mainWindow.setWindowTitle(QTest::currentTestFunction());
350 mainWindow.resize(w: 200, h: 200);
351 mainWindow.move(m_availableGeometry.center() - QPoint(100, 100));
352
353 QDockWidget *dock = new QDockWidget(QStringLiteral("Dock ") + QTest::currentTestFunction());
354 QWindow *window = new QWindow();
355 QWidget *container = QWidget::createWindowContainer(window);
356 dock->setWidget(container);
357 mainWindow.addDockWidget(area: Qt::RightDockWidgetArea, dockwidget: dock);
358
359 mainWindow.show();
360 QVERIFY(QTest::qWaitForWindowExposed(&mainWindow));
361 QCOMPARE(window->parent(), mainWindow.window()->windowHandle());
362
363 dock->setFloating(true);
364 QTRY_VERIFY(window->parent() != mainWindow.window()->windowHandle());
365
366 dock->setFloating(false);
367 QTRY_COMPARE(window->parent(), mainWindow.window()->windowHandle());
368}
369
370void tst_QWindowContainer::testNativeContainerParent()
371{
372 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
373 QSKIP("Wayland: This fails. Figure out why.");
374
375 QWidget root;
376 root.setWindowTitle(QTest::currentTestFunction());
377 root.setGeometry(ax: m_availableGeometry.x() + 50, ay: m_availableGeometry.y() + 50, aw: 200, ah: 200);
378
379 Window *window = new Window();
380 QWidget *container = QWidget::createWindowContainer(window, parent: &root);
381 container->setAttribute(Qt::WA_NativeWindow);
382 container->setGeometry(ax: 50, ay: 50, aw: 150, ah: 150);
383
384 root.show();
385
386#ifdef Q_OS_WINRT
387 QEXPECT_FAIL("", "Fails on WinRT - QTBUG-68297", Abort);
388#endif
389 QVERIFY(QTest::qWaitForWindowExposed(window));
390 QTRY_COMPARE(window->parent(), container->windowHandle());
391}
392
393class EventWindow : public QWindow
394{
395public:
396 EventWindow(bool *surfaceDestroyFlag) : m_surfaceDestroyFlag(surfaceDestroyFlag) { }
397 bool event(QEvent *e) override;
398
399private:
400 bool *m_surfaceDestroyFlag;
401};
402
403bool EventWindow::event(QEvent *e)
404{
405 if (e->type() == QEvent::PlatformSurface) {
406 if (static_cast<QPlatformSurfaceEvent *>(e)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed)
407 *m_surfaceDestroyFlag = true;
408 }
409 return QWindow::event(e);
410}
411
412void tst_QWindowContainer::testPlatformSurfaceEvent()
413{
414 // Verify that SurfaceAboutToBeDestroyed is delivered and the
415 // window subclass still gets a chance to process it.
416
417 bool ok = false;
418 QPointer<EventWindow> window(new EventWindow(&ok));
419 window->create();
420 QWidget *container = QWidget::createWindowContainer(window);
421
422 delete container;
423
424 QCOMPARE(window.data(), nullptr);
425 QVERIFY(ok);
426}
427
428QTEST_MAIN(tst_QWindowContainer)
429
430#include "tst_qwindowcontainer.moc"
431

source code of qtbase/tests/auto/widgets/kernel/qwindowcontainer/tst_qwindowcontainer.cpp