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 | |
43 | class Window : public QWindow |
44 | { |
45 | public: |
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 | |
63 | class tst_QWindowContainer: public QObject |
64 | { |
65 | Q_OBJECT |
66 | |
67 | public: |
68 | tst_QWindowContainer() : m_availableGeometry(QGuiApplication::primaryScreen()->availableGeometry()) {} |
69 | |
70 | private 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 | |
85 | private: |
86 | const QRect m_availableGeometry; |
87 | }; |
88 | |
89 | void tst_QWindowContainer::cleanup() |
90 | { |
91 | QVERIFY(QGuiApplication::topLevelWindows().isEmpty()); |
92 | } |
93 | |
94 | void 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 | |
118 | void 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 | |
139 | void 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 | |
166 | void 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 | |
178 | void 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 | |
194 | void 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 | |
237 | void 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 | |
254 | void 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 | |
285 | void 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 | |
346 | void 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 | |
370 | void 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 | |
393 | class EventWindow : public QWindow |
394 | { |
395 | public: |
396 | EventWindow(bool *surfaceDestroyFlag) : m_surfaceDestroyFlag(surfaceDestroyFlag) { } |
397 | bool event(QEvent *e) override; |
398 | |
399 | private: |
400 | bool *m_surfaceDestroyFlag; |
401 | }; |
402 | |
403 | bool 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 | |
412 | void 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 | |
428 | QTEST_MAIN(tst_QWindowContainer) |
429 | |
430 | #include "tst_qwindowcontainer.moc" |
431 | |