1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2019 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 "qmdisubwindow.h" |
33 | #include "private/qmdisubwindow_p.h" |
34 | #include "qmdiarea.h" |
35 | |
36 | #include <QLayout> |
37 | #include <QLineEdit> |
38 | #include <QMainWindow> |
39 | #include <QMenuBar> |
40 | #include <QMenu> |
41 | #include <QGroupBox> |
42 | #include <QTextEdit> |
43 | #include <QLayout> |
44 | #include <QHBoxLayout> |
45 | #include <QByteArray> |
46 | #include <QStyle> |
47 | #include <QStyleOptionTitleBar> |
48 | #include <QPushButton> |
49 | #include <QScreen> |
50 | #include <QSizeGrip> |
51 | |
52 | #include <QVector> |
53 | |
54 | QT_BEGIN_NAMESPACE |
55 | extern bool qt_tab_all_widgets(); |
56 | QT_END_NAMESPACE |
57 | |
58 | static inline bool tabAllWidgets() |
59 | { |
60 | #if !defined(Q_OS_WIN) |
61 | if (QApplication::style()->inherits(classname: "QMacStyle" )) |
62 | return qt_tab_all_widgets(); |
63 | #endif |
64 | return true; |
65 | } |
66 | |
67 | static inline void triggerSignal(QMdiSubWindow *window, QMdiArea *workspace, |
68 | const QByteArray &signal) |
69 | { |
70 | if (signal == SIGNAL(windowMaximized())) { |
71 | window->showMaximized(); |
72 | QCoreApplication::processEvents(); |
73 | if (window->parent()) |
74 | QVERIFY(window->isMaximized()); |
75 | } else if (signal == SIGNAL(windowMinimized())) { |
76 | window->showMinimized(); |
77 | QCoreApplication::processEvents(); |
78 | if (window->parent()) |
79 | QVERIFY(window->isMinimized()); |
80 | } else if (signal == SIGNAL(windowRestored())) { |
81 | window->showMaximized(); |
82 | QCoreApplication::processEvents(); |
83 | window->showNormal(); |
84 | QTRY_VERIFY(!window->isMinimized()); |
85 | QTRY_VERIFY(!window->isMaximized()); |
86 | QTRY_VERIFY(!window->isShaded()); |
87 | } else if (signal == SIGNAL(aboutToActivate())) { |
88 | if (window->parent()) { |
89 | workspace->setActiveSubWindow(window); |
90 | QCoreApplication::processEvents(); |
91 | } |
92 | } else if (signal == SIGNAL(windowActivated())) { |
93 | if (window->parent()) { |
94 | workspace->setActiveSubWindow(window); |
95 | QCoreApplication::processEvents(); |
96 | } |
97 | } else if (signal == SIGNAL(windowDeactivated())) { |
98 | if (!window->parent()) |
99 | return; |
100 | workspace->setActiveSubWindow(window); |
101 | QCoreApplication::processEvents(); |
102 | workspace->setActiveSubWindow(nullptr); |
103 | QCoreApplication::processEvents(); |
104 | } |
105 | } |
106 | |
107 | // --- from tst_qgraphicsview.cpp --- |
108 | static void sendMousePress(QWidget *widget, const QPoint &point, Qt::MouseButton button = Qt::LeftButton) |
109 | { |
110 | QMouseEvent event(QEvent::MouseButtonPress, point, widget->mapToGlobal(point), button, {}, {}); |
111 | QApplication::sendEvent(receiver: widget, event: &event); |
112 | } |
113 | |
114 | static void sendMouseMove(QWidget *widget, const QPoint &point, Qt::MouseButton button = Qt::LeftButton) |
115 | { |
116 | QMouseEvent event(QEvent::MouseMove, point, widget->mapToGlobal(point), button, button, {}); |
117 | QApplication::sendEvent(receiver: widget, event: &event); |
118 | } |
119 | |
120 | static void sendMouseRelease(QWidget *widget, const QPoint &point, Qt::MouseButton button = Qt::LeftButton) |
121 | { |
122 | QMouseEvent event(QEvent::MouseButtonRelease, point, widget->mapToGlobal(point), button, {}, {}); |
123 | QApplication::sendEvent(receiver: widget, event: &event); |
124 | } |
125 | // --- |
126 | |
127 | static void sendMouseDoubleClick(QWidget *widget, const QPoint &point, Qt::MouseButton button = Qt::LeftButton) |
128 | { |
129 | sendMousePress(widget, point, button); |
130 | sendMouseRelease(widget, point, button); |
131 | QMouseEvent event(QEvent::MouseButtonDblClick, point, widget->mapToGlobal(point), button, {}, {}); |
132 | QApplication::sendEvent(receiver: widget, event: &event); |
133 | } |
134 | |
135 | static const Qt::WindowFlags StandardWindowFlags |
136 | = Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint; |
137 | static const Qt::WindowFlags DialogWindowFlags |
138 | = Qt::WindowTitleHint | Qt::WindowSystemMenuHint; |
139 | |
140 | class LayoutDirectionGuard |
141 | { |
142 | public: |
143 | Q_DISABLE_COPY(LayoutDirectionGuard); |
144 | |
145 | LayoutDirectionGuard() = default; |
146 | ~LayoutDirectionGuard() { QApplication::setLayoutDirection(Qt::LeftToRight); } |
147 | }; |
148 | |
149 | Q_DECLARE_METATYPE(Qt::WindowState); |
150 | Q_DECLARE_METATYPE(Qt::WindowStates); |
151 | Q_DECLARE_METATYPE(Qt::WindowType); |
152 | Q_DECLARE_METATYPE(Qt::WindowFlags); |
153 | Q_DECLARE_METATYPE(QMdiSubWindow*); |
154 | |
155 | class TestPushButton : public QPushButton |
156 | { |
157 | public: |
158 | TestPushButton(const QString &title, QWidget *parent = nullptr) |
159 | : QPushButton(title, parent) |
160 | { |
161 | } |
162 | |
163 | protected: |
164 | // don't rely on style-specific button behavior in test |
165 | bool hitButton(const QPoint &point) const override |
166 | { |
167 | return rect().contains(p: point); |
168 | } |
169 | }; |
170 | |
171 | class tst_QMdiSubWindow : public QObject |
172 | { |
173 | Q_OBJECT |
174 | private slots: |
175 | void initTestCase(); |
176 | void cleanup(); |
177 | void sizeHint(); |
178 | void minimumSizeHint(); |
179 | void minimumSize(); |
180 | void setWidget(); |
181 | void setWindowState_data(); |
182 | void setWindowState(); |
183 | void mainWindowSupport(); |
184 | void emittingOfSignals_data(); |
185 | void emittingOfSignals(); |
186 | void showShaded(); |
187 | void showNormal_data(); |
188 | void showNormal(); |
189 | #ifndef QT_NO_CURSOR |
190 | void setOpaqueResizeAndMove_data(); |
191 | void setOpaqueResizeAndMove(); |
192 | #endif |
193 | void setWindowFlags_data(); |
194 | void setWindowFlags(); |
195 | void mouseDoubleClick(); |
196 | void setSystemMenu(); |
197 | void restoreFocus(); |
198 | void restoreFocusOverCreation(); |
199 | void changeFocusWithTab(); |
200 | void closeEvent(); |
201 | void setWindowTitle(); |
202 | void resizeEvents_data(); |
203 | void resizeEvents(); |
204 | #if defined(Q_OS_MAC) |
205 | void defaultSizeGrip(); |
206 | #endif |
207 | void hideAndShow(); |
208 | void keepWindowMaximizedState(); |
209 | void explicitlyHiddenWidget(); |
210 | void resizeTimer(); |
211 | void fixedMinMaxSize(); |
212 | #if !defined (Q_OS_DARWIN) |
213 | void replaceMenuBarWhileMaximized(); |
214 | void closeOnDoubleClick_data(); |
215 | void closeOnDoubleClick(); |
216 | #endif |
217 | void setFont(); |
218 | void task_188849(); |
219 | void mdiArea(); |
220 | void task_182852(); |
221 | void task_233197(); |
222 | void task_226929(); |
223 | void styleChange(); |
224 | void testFullScreenState(); |
225 | void testRemoveBaseWidget(); |
226 | }; |
227 | |
228 | void tst_QMdiSubWindow::initTestCase() |
229 | { |
230 | if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland" ), cs: Qt::CaseInsensitive)) |
231 | QSKIP("Wayland: Almost all of these fail. Figure out why." ); |
232 | |
233 | qRegisterMetaType<Qt::WindowStates>(typeName: "Qt::WindowStates" ); |
234 | // Avoid unnecessary waits for empty top level widget lists when |
235 | // testing menus. |
236 | QApplication::setEffectEnabled(Qt::UI_AnimateMenu, enable: false); |
237 | } |
238 | |
239 | void tst_QMdiSubWindow::cleanup() |
240 | { |
241 | QVERIFY(QApplication::topLevelWidgets().isEmpty()); |
242 | } |
243 | |
244 | void tst_QMdiSubWindow::sizeHint() |
245 | { |
246 | QMdiSubWindow *window = new QMdiSubWindow; |
247 | QCOMPARE(window->sizeHint(), window->minimumSizeHint()); |
248 | window->show(); |
249 | QCOMPARE(window->sizeHint(), window->minimumSizeHint()); |
250 | QMdiArea workspace; |
251 | workspace.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
252 | workspace.addSubWindow(widget: window); |
253 | QCOMPARE(window->sizeHint(), window->minimumSizeHint()); |
254 | } |
255 | |
256 | void tst_QMdiSubWindow::minimumSizeHint() |
257 | { |
258 | const auto globalStrut = QApplication::globalStrut(); |
259 | QMdiSubWindow window; |
260 | window.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
261 | window.show(); |
262 | |
263 | QCOMPARE(window.minimumSizeHint(), globalStrut); |
264 | |
265 | window.setWidget(new QWidget); |
266 | QCOMPARE(window.minimumSizeHint(), window.layout()->minimumSize() |
267 | .expandedTo(globalStrut)); |
268 | |
269 | delete window.widget(); |
270 | delete window.layout(); |
271 | window.setWidget(new QWidget); |
272 | QCOMPARE(window.minimumSizeHint(), globalStrut); |
273 | |
274 | window.widget()->show(); |
275 | QCOMPARE(window.minimumSizeHint(), window.widget()->minimumSizeHint() |
276 | .expandedTo(globalStrut)); |
277 | } |
278 | |
279 | void tst_QMdiSubWindow::minimumSize() |
280 | { |
281 | QMdiArea mdiArea; |
282 | mdiArea.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
283 | mdiArea.resize(w: 200, h: 200); |
284 | |
285 | // Check that we respect the minimum size set on the sub-window itself. |
286 | QMdiSubWindow *subWindow1 = mdiArea.addSubWindow(widget: new QWidget); |
287 | subWindow1->setMinimumSize(minw: 1000, minh: 1000); |
288 | mdiArea.show(); |
289 | QVERIFY(QTest::qWaitForWindowExposed(&mdiArea)); |
290 | QCOMPARE(subWindow1->size(), QSize(1000, 1000)); |
291 | |
292 | // Check that we respect the minimum size set on the internal widget. |
293 | QWidget *widget = new QWidget; |
294 | widget->setMinimumSize(minw: 1000, minh: 1000); |
295 | QMdiSubWindow *subWindow2 = mdiArea.addSubWindow(widget); |
296 | QVERIFY(subWindow2->size() != mdiArea.viewport()->size()); |
297 | QCOMPARE(subWindow2->size(), subWindow2->minimumSizeHint()); |
298 | } |
299 | |
300 | void tst_QMdiSubWindow::setWidget() |
301 | { |
302 | QMdiSubWindow window; |
303 | window.show(); |
304 | QVERIFY(window.layout()); |
305 | QVERIFY(!window.widget()); |
306 | |
307 | // QPointer so we can check if the widget is deleted |
308 | QPointer<QWidget> widget = new QWidget; |
309 | widget->setWindowTitle(QString::fromLatin1(str: "DummyTitle" )); |
310 | QCOMPARE(widget->windowTitle(), QString::fromLatin1("DummyTitle" )); |
311 | window.setWidget(widget); |
312 | QCOMPARE(window.windowTitle(), window.widget()->windowTitle()); |
313 | QCOMPARE(widget->parentWidget(), static_cast<QWidget *>(&window)); |
314 | QVERIFY(!widget->isVisible()); |
315 | QCOMPARE(window.layout()->count(), 1); |
316 | |
317 | QTest::ignoreMessage(type: QtWarningMsg,message: "QMdiSubWindow::setWidget: widget is already set" ); |
318 | window.setWidget(widget); |
319 | QCOMPARE(window.widget(), static_cast<QWidget *>(widget)); |
320 | QCOMPARE(widget->parentWidget(), static_cast<QWidget *>(&window)); |
321 | |
322 | window.setWidget(nullptr); |
323 | QVERIFY(widget); |
324 | QVERIFY(!widget->parent()); |
325 | QVERIFY(!window.widget()); |
326 | QCOMPARE(window.layout()->count(), 0); |
327 | |
328 | window.setWidget(widget); |
329 | delete window.layout(); |
330 | QVERIFY(!window.layout()); |
331 | QVERIFY(window.widget()); |
332 | QCOMPARE(window.widget()->parentWidget(), static_cast<QWidget *>(&window)); |
333 | |
334 | delete window.widget(); |
335 | QVERIFY(!widget); |
336 | QVERIFY(!window.widget()); |
337 | } |
338 | |
339 | void tst_QMdiSubWindow::setWindowState_data() |
340 | { |
341 | QTest::addColumn<Qt::WindowState>(name: "windowState" ); |
342 | |
343 | QTest::newRow(dataTag: "maximized" ) << Qt::WindowMaximized; |
344 | QTest::newRow(dataTag: "minimized" ) << Qt::WindowMinimized; |
345 | QTest::newRow(dataTag: "normalized" ) << Qt::WindowNoState; |
346 | } |
347 | |
348 | void tst_QMdiSubWindow::setWindowState() |
349 | { |
350 | QFETCH(Qt::WindowState, windowState); |
351 | QMdiArea workspace; |
352 | workspace.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
353 | QMdiSubWindow *window = qobject_cast<QMdiSubWindow *>(object: workspace.addSubWindow(widget: new QLineEdit)); |
354 | window->show(); |
355 | workspace.show(); |
356 | QVERIFY(QTest::qWaitForWindowExposed(&workspace)); |
357 | |
358 | QWidget *testWidget = nullptr; |
359 | for (int iteration = 0; iteration < 2; ++iteration) { |
360 | if (iteration == 0) |
361 | testWidget = window; |
362 | else |
363 | testWidget = window->widget(); |
364 | |
365 | testWidget->setWindowState(windowState); |
366 | |
367 | Qt::WindowStates windowStateWindow = window->windowState(); |
368 | windowStateWindow &= ~Qt::WindowActive; |
369 | Qt::WindowStates windowStateWidget = window->widget()->windowState(); |
370 | windowStateWidget &= ~Qt::WindowActive; |
371 | QCOMPARE(windowStateWindow, windowStateWidget); |
372 | |
373 | switch (windowState) { |
374 | case Qt::WindowNoState: |
375 | QVERIFY(!window->widget()->isMinimized()); |
376 | QVERIFY(!window->widget()->isMaximized()); |
377 | QVERIFY(!window->isMinimized()); |
378 | QVERIFY(!window->isMaximized()); |
379 | break; |
380 | case Qt::WindowMinimized: |
381 | QVERIFY(window->widget()->isMinimized()); |
382 | QVERIFY(window->isMinimized()); |
383 | break; |
384 | case Qt::WindowMaximized: |
385 | QVERIFY(window->widget()->isMaximized()); |
386 | QVERIFY(window->isMaximized()); |
387 | break; |
388 | default: |
389 | break; |
390 | } |
391 | } |
392 | } |
393 | |
394 | void tst_QMdiSubWindow::mainWindowSupport() |
395 | { |
396 | QVector<QMdiSubWindow *> windows; |
397 | QMdiArea *workspace = new QMdiArea; |
398 | QMainWindow mainWindow; |
399 | mainWindow.setCentralWidget(workspace); |
400 | mainWindow.show(); |
401 | mainWindow.menuBar()->setVisible(true); |
402 | QApplication::setActiveWindow(&mainWindow); |
403 | bool = mainWindow.menuBar()->isNativeMenuBar(); |
404 | |
405 | // QMainWindow's window title is empty, so on a platform which does NOT have a native menubar, |
406 | // the maximized subwindow's title is imposed onto the main window's titlebar. |
407 | if (!nativeMenuBar) { |
408 | QCOMPARE(mainWindow.windowTitle(), QString()); |
409 | QMdiSubWindow *window = workspace->addSubWindow(widget: new TestPushButton(QLatin1String("Test" ))); |
410 | QString expectedTitle = QLatin1String("MainWindow's title is empty" ); |
411 | window->setWindowTitle(expectedTitle); |
412 | QCOMPARE(window->windowTitle(), expectedTitle); |
413 | window->showMaximized(); |
414 | QVERIFY(window->isMaximized()); |
415 | QCOMPARE(window->windowTitle(), expectedTitle); |
416 | QCOMPARE(mainWindow.windowTitle(), expectedTitle); |
417 | window->showNormal(); |
418 | QCOMPARE(window->windowTitle(), expectedTitle); |
419 | QCOMPARE(mainWindow.windowTitle(), QString()); |
420 | window->close(); |
421 | } |
422 | |
423 | QString originalWindowTitle = QString::fromLatin1(str: "MainWindow" ); |
424 | mainWindow.setWindowTitle(originalWindowTitle); |
425 | |
426 | for (int i = 0; i < 5; ++i) { |
427 | mainWindow.menuBar()->setVisible(false); |
428 | |
429 | QMdiSubWindow *window = new QMdiSubWindow; |
430 | windows.append(t: window); |
431 | QVERIFY(!window->maximizedButtonsWidget()); |
432 | QVERIFY(!window->maximizedSystemMenuIconWidget()); |
433 | |
434 | QMdiArea *nestedWorkspace = new QMdiArea; // :-) |
435 | window->setWidget(nestedWorkspace); |
436 | window->widget()->setWindowTitle(QLatin1String("Window " ) + QString::number(i)); |
437 | |
438 | workspace->addSubWindow(widget: window); |
439 | QVERIFY(!window->maximizedButtonsWidget()); |
440 | QVERIFY(!window->maximizedSystemMenuIconWidget()); |
441 | window->show(); |
442 | |
443 | // mainWindow.menuBar() is not visible |
444 | window->showMaximized(); |
445 | QCoreApplication::processEvents(); |
446 | QVERIFY(window->isMaximized()); |
447 | QVERIFY(!window->maximizedButtonsWidget()); |
448 | QVERIFY(!window->maximizedSystemMenuIconWidget()); |
449 | window->showNormal(); |
450 | |
451 | // Now it is |
452 | mainWindow.menuBar()->setVisible(true); |
453 | |
454 | window->showMaximized(); |
455 | QCoreApplication::processEvents(); |
456 | QVERIFY(window->isMaximized()); |
457 | if (!nativeMenuBar) { |
458 | QVERIFY(window->maximizedButtonsWidget()); |
459 | QCOMPARE(window->maximizedButtonsWidget(), mainWindow.menuBar()->cornerWidget(Qt::TopRightCorner)); |
460 | QVERIFY(window->maximizedSystemMenuIconWidget()); |
461 | QCOMPARE(window->maximizedSystemMenuIconWidget(), |
462 | qobject_cast<QWidget *>(mainWindow.menuBar()->cornerWidget(Qt::TopLeftCorner))); |
463 | const QString expectedTitle = originalWindowTitle + QLatin1String(" - [" ) |
464 | + window->widget()->windowTitle() + QLatin1Char(']'); |
465 | QCOMPARE(mainWindow.windowTitle(), expectedTitle); |
466 | } |
467 | |
468 | // Check that nested child windows don't set window title |
469 | nestedWorkspace->show(); |
470 | QMdiSubWindow *nestedWindow = new QMdiSubWindow; |
471 | nestedWindow->setWidget(new QWidget); |
472 | nestedWorkspace->addSubWindow(widget: nestedWindow); |
473 | nestedWindow->widget()->setWindowTitle(QLatin1String("NestedWindow " ) + QString::number(i)); |
474 | nestedWindow->showMaximized(); |
475 | QCoreApplication::processEvents(); |
476 | QVERIFY(nestedWindow->isMaximized()); |
477 | QVERIFY(!nestedWindow->maximizedButtonsWidget()); |
478 | QVERIFY(!nestedWindow->maximizedSystemMenuIconWidget()); |
479 | |
480 | if (!nativeMenuBar) { |
481 | QCOMPARE(mainWindow.windowTitle(), QString::fromLatin1("%1 - [%2]" ) |
482 | .arg(originalWindowTitle, window->widget()->windowTitle())); |
483 | } |
484 | } |
485 | |
486 | if (nativeMenuBar) |
487 | return; |
488 | |
489 | workspace->activateNextSubWindow(); |
490 | QCoreApplication::processEvents(); |
491 | for (QMdiSubWindow *window : qAsConst(t&: windows)) { |
492 | QCOMPARE(workspace->activeSubWindow(), window); |
493 | QVERIFY(window->isMaximized()); |
494 | QVERIFY(window->maximizedButtonsWidget()); |
495 | QCOMPARE(window->maximizedButtonsWidget(), mainWindow.menuBar()->cornerWidget(Qt::TopRightCorner)); |
496 | QVERIFY(window->maximizedSystemMenuIconWidget()); |
497 | QCOMPARE(window->maximizedSystemMenuIconWidget(), qobject_cast<QWidget *>(mainWindow.menuBar() |
498 | ->cornerWidget(Qt::TopLeftCorner))); |
499 | QCOMPARE(mainWindow.windowTitle(), QString::fromLatin1("%1 - [%2]" ) |
500 | .arg(originalWindowTitle, window->widget()->windowTitle())); |
501 | workspace->activateNextSubWindow(); |
502 | QCoreApplication::processEvents(); |
503 | } |
504 | } |
505 | |
506 | // This test was written when QMdiSubWindow emitted separate signals |
507 | void tst_QMdiSubWindow::emittingOfSignals_data() |
508 | { |
509 | QTest::addColumn<QByteArray>(name: "signal" ); |
510 | QTest::addColumn<Qt::WindowState>(name: "watchedState" ); |
511 | |
512 | QTest::newRow(dataTag: "windowMaximized" ) << QByteArray(SIGNAL(windowMaximized())) << Qt::WindowMaximized; |
513 | QTest::newRow(dataTag: "windowMinimized" ) << QByteArray(SIGNAL(windowMinimized())) << Qt::WindowMinimized; |
514 | QTest::newRow(dataTag: "windowRestored" ) << QByteArray(SIGNAL(windowRestored())) << Qt::WindowNoState; |
515 | QTest::newRow(dataTag: "aboutToActivate" ) << QByteArray(SIGNAL(aboutToActivate())) << Qt::WindowNoState; |
516 | QTest::newRow(dataTag: "windowActivated" ) << QByteArray(SIGNAL(windowActivated())) << Qt::WindowActive; |
517 | QTest::newRow(dataTag: "windowDeactivated" ) << QByteArray(SIGNAL(windowDeactivated())) << Qt::WindowActive; |
518 | } |
519 | |
520 | void tst_QMdiSubWindow::emittingOfSignals() |
521 | { |
522 | QFETCH(QByteArray, signal); |
523 | QFETCH(Qt::WindowState, watchedState); |
524 | QMdiArea workspace; |
525 | workspace.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
526 | workspace.show(); |
527 | QCoreApplication::processEvents(); |
528 | QApplication::setActiveWindow(&workspace); |
529 | QMdiSubWindow *window = qobject_cast<QMdiSubWindow *>(object: workspace.addSubWindow(widget: new QWidget)); |
530 | QCoreApplication::processEvents(); |
531 | window->show(); |
532 | if (signal != SIGNAL(windowRestored())) |
533 | workspace.setActiveSubWindow(nullptr); |
534 | QCoreApplication::processEvents(); |
535 | |
536 | QSignalSpy spy(window, signal == SIGNAL(aboutToActivate()) |
537 | ? signal.data() |
538 | : SIGNAL(windowStateChanged(Qt::WindowStates,Qt::WindowStates))); |
539 | QVERIFY(spy.isEmpty()); |
540 | triggerSignal(window, workspace: &workspace, signal); |
541 | // Unless the signal is windowRestored or windowDeactivated, |
542 | // we're already in correct state and nothing should happen. |
543 | if (signal != SIGNAL(windowRestored()) && signal != SIGNAL(windowDeactivated())) |
544 | triggerSignal(window, workspace: &workspace, signal); |
545 | |
546 | int count = 0; |
547 | if (signal == SIGNAL(aboutToActivate())) { |
548 | count += spy.count(); |
549 | } else { |
550 | for (int i = 0; i < spy.count(); ++i) { |
551 | Qt::WindowStates oldState = qvariant_cast<Qt::WindowStates>(v: spy.at(i).at(i: 0)); |
552 | Qt::WindowStates newState = qvariant_cast<Qt::WindowStates>(v: spy.at(i).at(i: 1)); |
553 | if (watchedState != Qt::WindowNoState) { |
554 | if (!(oldState & watchedState) && (newState & watchedState)) |
555 | ++count; |
556 | } else { |
557 | if ((oldState & (Qt::WindowMinimized | Qt::WindowMaximized)) |
558 | && (newState & (watchedState | Qt::WindowActive))) { |
559 | ++count; |
560 | } |
561 | } |
562 | } |
563 | } |
564 | #ifdef Q_OS_WINRT |
565 | QEXPECT_FAIL("windowMaximized" , "Broken on WinRT - QTBUG-68297" , Abort); |
566 | #endif |
567 | QCOMPARE(count, 1); |
568 | |
569 | window->setParent(nullptr); |
570 | window->showNormal(); |
571 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
572 | QCoreApplication::processEvents(); |
573 | |
574 | spy.clear(); |
575 | triggerSignal(window, workspace: &workspace, signal); |
576 | QCOMPARE(spy.count(), 0); |
577 | |
578 | delete window; |
579 | window = nullptr; |
580 | } |
581 | |
582 | void tst_QMdiSubWindow::showShaded() |
583 | { |
584 | QMdiArea workspace; |
585 | workspace.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
586 | QMdiSubWindow *window = workspace.addSubWindow(widget: new QLineEdit); |
587 | window->resize(w: 300, h: 300); |
588 | QCoreApplication::processEvents(); |
589 | workspace.show(); |
590 | QVERIFY(QTest::qWaitForWindowExposed(&workspace)); |
591 | |
592 | QVERIFY(!window->isShaded()); |
593 | #ifdef Q_OS_WINRT |
594 | QEXPECT_FAIL("" , "Windows are maximized per default on WinRt " , Abort); |
595 | #endif |
596 | QVERIFY(!window->isMaximized()); |
597 | |
598 | QCOMPARE(window->size(), QSize(300, 300)); |
599 | QRect restoreGeometry = window->geometry(); |
600 | window->showShaded(); |
601 | QVERIFY(window->isShaded()); |
602 | QVERIFY(window->isMinimized()); |
603 | |
604 | window->showNormal(); |
605 | QVERIFY(!window->isShaded()); |
606 | QVERIFY(!window->isMinimized()); |
607 | QCOMPARE(window->geometry(), restoreGeometry); |
608 | window->showShaded(); |
609 | |
610 | window->showNormal(); |
611 | QVERIFY(!window->isShaded()); |
612 | QVERIFY(!window->isMinimized()); |
613 | QCOMPARE(window->geometry(), restoreGeometry); |
614 | window->showMinimized(); |
615 | window->showMaximized(); |
616 | window->showShaded(); |
617 | QCOMPARE(window->width(), workspace.contentsRect().width()); |
618 | window->showNormal(); |
619 | QCOMPARE(window->geometry(), workspace.contentsRect()); |
620 | |
621 | window->resize(w: 300, h: 300); |
622 | QCOMPARE(window->size(), QSize(300, 300)); |
623 | window->showShaded(); |
624 | window->showNormal(); |
625 | QTest::qWait(ms: 250); |
626 | |
627 | const QSize minimumSizeHint = window->minimumSizeHint(); |
628 | QVERIFY(minimumSizeHint.height() < 300); |
629 | const int maxHeightDiff = 300 - minimumSizeHint.height(); |
630 | |
631 | // Calculate mouse position for bottom right corner and simulate a |
632 | // vertical resize with the mouse. |
633 | int offset = window->style()->pixelMetric(metric: QStyle::PM_MdiSubWindowFrameWidth) / 2; |
634 | QPoint mousePosition(window->width() - qMax(a: offset, b: 2), window->height() - qMax(a: offset, b: 2)); |
635 | QWidget *mouseReceiver = nullptr; |
636 | #ifdef Q_OS_MAC |
637 | if (window->style()->inherits("QMacStyle" )) |
638 | mouseReceiver = window->findChild<QSizeGrip *>(); |
639 | else |
640 | #endif |
641 | mouseReceiver = window; |
642 | QVERIFY(mouseReceiver); |
643 | sendMouseMove(widget: mouseReceiver, point: mousePosition, button: Qt::NoButton); |
644 | sendMousePress(widget: mouseReceiver, point: mousePosition); |
645 | |
646 | for (int i = 0; i < maxHeightDiff + 20; ++i) { |
647 | --mousePosition.ry(); |
648 | sendMouseMove(widget: mouseReceiver, point: mousePosition); |
649 | } |
650 | |
651 | sendMouseRelease(widget: mouseReceiver, point: mousePosition); |
652 | // Make sure we respect the minimumSizeHint! |
653 | QCOMPARE(window->height(), minimumSizeHint.height()); |
654 | |
655 | window->showShaded(); |
656 | window->setParent(nullptr); |
657 | window->show(); |
658 | QVERIFY(!window->isShaded()); |
659 | |
660 | delete window; |
661 | } |
662 | |
663 | void tst_QMdiSubWindow::showNormal_data() |
664 | { |
665 | QTest::addColumn<QByteArray>(name: "slot" ); |
666 | |
667 | QTest::newRow(dataTag: "showMinimized" ) << QByteArray("showMinimized" ); |
668 | QTest::newRow(dataTag: "showMaximized" ) << QByteArray("showMaximized" ); |
669 | QTest::newRow(dataTag: "showShaded" ) << QByteArray("showShaded" ); |
670 | } |
671 | |
672 | void tst_QMdiSubWindow::showNormal() |
673 | { |
674 | QFETCH(QByteArray, slot); |
675 | |
676 | QMdiArea workspace; |
677 | workspace.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
678 | QWidget *window = workspace.addSubWindow(widget: new QWidget); |
679 | QCoreApplication::processEvents(); |
680 | workspace.show(); |
681 | window->show(); |
682 | QVERIFY(QTest::qWaitForWindowExposed(&workspace)); |
683 | |
684 | QRect originalGeometry = window->geometry(); |
685 | QVERIFY(QMetaObject::invokeMethod(window, slot.data())); |
686 | QCoreApplication::processEvents(); |
687 | window->showNormal(); |
688 | QCoreApplication::processEvents(); |
689 | #ifdef Q_OS_WINRT |
690 | QEXPECT_FAIL("showMinimized" , "Windows are maximized per default on WinRt " , Abort); |
691 | QEXPECT_FAIL("showMaximized" , "Windows are maximized per default on WinRt " , Abort); |
692 | #endif |
693 | QCOMPARE(window->geometry(), originalGeometry); |
694 | } |
695 | |
696 | class EventSpy : public QObject |
697 | { |
698 | public: |
699 | explicit EventSpy(QObject *object, QEvent::Type event) |
700 | : eventToSpy(event) |
701 | { |
702 | if (object) |
703 | object->installEventFilter(filterObj: this); |
704 | } |
705 | |
706 | int count() const { return _count; } |
707 | void clear() { _count = 0; } |
708 | |
709 | protected: |
710 | bool eventFilter(QObject *object, QEvent *event) override |
711 | { |
712 | if (event->type() == eventToSpy) |
713 | ++_count; |
714 | return QObject::eventFilter(watched: object, event); |
715 | } |
716 | |
717 | private: |
718 | const QEvent::Type eventToSpy; |
719 | int _count = 0; |
720 | }; |
721 | |
722 | #ifndef QT_NO_CURSOR |
723 | void tst_QMdiSubWindow::setOpaqueResizeAndMove_data() |
724 | { |
725 | QTest::addColumn<bool>(name: "opaqueMode" ); |
726 | QTest::addColumn<int>(name: "geometryCount" ); |
727 | QTest::addColumn<int>(name: "expectedGeometryCount" ); |
728 | QTest::addColumn<QSize>(name: "workspaceSize" ); |
729 | QTest::addColumn<QSize>(name: "windowSize" ); |
730 | |
731 | QTest::newRow(dataTag: "normal mode" ) << true<< 20 << 20 << QSize(400, 400) << QSize(240, 200); |
732 | QTest::newRow(dataTag: "rubberband mode" ) << false << 20 << 1 << QSize(400, 400) << QSize(240, 200); |
733 | } |
734 | |
735 | void tst_QMdiSubWindow::setOpaqueResizeAndMove() |
736 | { |
737 | QFETCH(bool, opaqueMode); |
738 | QFETCH(int, geometryCount); |
739 | QFETCH(int, expectedGeometryCount); |
740 | QFETCH(QSize, workspaceSize); |
741 | QFETCH(QSize, windowSize); |
742 | |
743 | QMdiArea workspace; |
744 | workspace.setWindowTitle(QLatin1String(QTest::currentTestFunction()) |
745 | + QLatin1String("::" ) + QLatin1String(QTest::currentDataTag())); |
746 | QMdiSubWindow *window = qobject_cast<QMdiSubWindow *>(object: workspace.addSubWindow(widget: new QWidget)); |
747 | QCoreApplication::processEvents(); |
748 | workspace.resize(workspaceSize); |
749 | workspace.show(); |
750 | QVERIFY(QTest::qWaitForWindowExposed(&workspace)); |
751 | |
752 | QWidget *mouseReceiver = nullptr; |
753 | if (window->style()->inherits(classname: "QMacStyle" )) |
754 | mouseReceiver = window->findChild<QSizeGrip *>(); |
755 | else |
756 | mouseReceiver = window; |
757 | QVERIFY(mouseReceiver); |
758 | |
759 | // ----------------------------- resize ----------------------------- |
760 | { |
761 | // setOpaqueResize |
762 | window->setOption(option: QMdiSubWindow::RubberBandResize, on: !opaqueMode); |
763 | QCOMPARE(window->testOption(QMdiSubWindow::RubberBandResize), !opaqueMode); |
764 | |
765 | // Check that the event spy actually works |
766 | EventSpy resizeSpy(window, QEvent::Resize); |
767 | QCOMPARE(resizeSpy.count(), 0); |
768 | window->resize(windowSize); |
769 | QCOMPARE(window->size(), windowSize); |
770 | QCOMPARE(resizeSpy.count(), 1); |
771 | resizeSpy.clear(); |
772 | QCOMPARE(resizeSpy.count(), 0); |
773 | |
774 | // we need to wait for the resizeTimer to make sure updateDirtyRegions is called |
775 | auto priv = static_cast<QMdiSubWindowPrivate*>(qt_widget_private(widget: window)); |
776 | QTRY_COMPARE(priv->resizeTimerId, -1); |
777 | |
778 | // Enter resize mode. |
779 | int offset = window->style()->pixelMetric(metric: QStyle::PM_MdiSubWindowFrameWidth) / 2; |
780 | QPoint mousePosition(mouseReceiver->width() - qMax(a: offset, b: 2), mouseReceiver->height() - qMax(a: offset, b: 2)); |
781 | sendMouseMove(widget: mouseReceiver, point: mousePosition, button: Qt::NoButton); |
782 | sendMousePress(widget: mouseReceiver, point: mousePosition); |
783 | |
784 | // The window itself is the grabber in rubberband mode |
785 | if (!opaqueMode) { |
786 | mouseReceiver = window; |
787 | mousePosition = QPoint(window->width() - qMax(a: offset, b: 2), window->height() - qMax(a: offset, b: 2)); |
788 | } |
789 | |
790 | // Trigger resize events |
791 | for (int i = 0; i < geometryCount; ++i) { |
792 | if (mouseReceiver == window) { |
793 | ++mousePosition.rx(); |
794 | ++mousePosition.ry(); |
795 | sendMouseMove(widget: mouseReceiver, point: mousePosition); |
796 | } else { |
797 | sendMouseMove(widget: mouseReceiver, point: mousePosition + QPoint(1, 1)); |
798 | } |
799 | } |
800 | |
801 | // Leave resize mode |
802 | sendMouseRelease(widget: mouseReceiver, point: mousePosition); |
803 | #ifdef Q_OS_WINRT |
804 | QEXPECT_FAIL("" , "Fails on WinRT - QTBUG-68297" , Abort); |
805 | #endif |
806 | QCOMPARE(resizeSpy.count(), expectedGeometryCount); |
807 | QCOMPARE(window->size(), windowSize + QSize(geometryCount, geometryCount)); |
808 | } |
809 | |
810 | // ------------------------------ move ------------------------------ |
811 | { |
812 | // setOpaqueMove |
813 | window->setOption(option: QMdiSubWindow::RubberBandMove, on: !opaqueMode); |
814 | QCOMPARE(window->testOption(QMdiSubWindow::RubberBandMove), !opaqueMode); |
815 | |
816 | EventSpy moveSpy(window, QEvent::Move); |
817 | QCOMPARE(moveSpy.count(), 0); |
818 | window->move(ax: 30, ay: 30); |
819 | QCOMPARE(moveSpy.count(), 1); |
820 | moveSpy.clear(); |
821 | |
822 | // Enter move mode |
823 | QStyleOptionTitleBar options; |
824 | options.initFrom(w: window); |
825 | int height = window->style()->pixelMetric(metric: QStyle::PM_TitleBarHeight, option: &options); |
826 | #if defined(Q_OS_MAC) |
827 | // ### Remove this after mac style has been fixed |
828 | height -= 4; |
829 | #endif |
830 | QPoint mousePosition(window->width() / 3, height - 1); |
831 | sendMouseMove(widget: window, point: mousePosition, button: Qt::NoButton); |
832 | sendMousePress(widget: window, point: mousePosition); |
833 | |
834 | // Trigger move events |
835 | for (int i = 0; i < geometryCount; ++i) { |
836 | ++mousePosition.rx(); |
837 | ++mousePosition.ry(); |
838 | sendMouseMove(widget: window, point: mousePosition); |
839 | } |
840 | |
841 | // Leave move mode |
842 | sendMouseRelease(widget: window, point: mousePosition); |
843 | QCOMPARE(moveSpy.count(), expectedGeometryCount); |
844 | QCOMPARE(window->size(), windowSize + QSize(geometryCount, geometryCount)); |
845 | } |
846 | } |
847 | #endif |
848 | |
849 | void tst_QMdiSubWindow::setWindowFlags_data() |
850 | { |
851 | QTest::addColumn<Qt::WindowType>(name: "windowType" ); |
852 | QTest::addColumn<Qt::WindowType>(name: "expectedWindowType" ); |
853 | QTest::addColumn<Qt::WindowFlags>(name: "customFlags" ); |
854 | QTest::addColumn<Qt::WindowFlags>(name: "expectedCustomFlags" ); |
855 | |
856 | // NB! If 'expectedCustomFlags' is set to 'Qt::WindowFlags(0)' |
857 | // and nothing else, it means we're expecting the same as customFlags. |
858 | |
859 | // Standard window types with no custom flags set. |
860 | QTest::newRow(dataTag: "Qt::Widget" ) << Qt::Widget << Qt::SubWindow |
861 | << Qt::WindowFlags{} << StandardWindowFlags; |
862 | QTest::newRow(dataTag: "Qt::Window" ) << Qt::Window << Qt::SubWindow |
863 | << Qt::WindowFlags{} << StandardWindowFlags; |
864 | QTest::newRow(dataTag: "Qt::Dialog" ) << Qt::Dialog << Qt::SubWindow |
865 | << Qt::WindowFlags{} << DialogWindowFlags; |
866 | QTest::newRow(dataTag: "Qt::Sheet" ) << Qt::Sheet << Qt::SubWindow |
867 | << Qt::WindowFlags{} << StandardWindowFlags; |
868 | QTest::newRow(dataTag: "Qt::Drawer" ) << Qt::Drawer << Qt::SubWindow |
869 | << Qt::WindowFlags{} << StandardWindowFlags; |
870 | QTest::newRow(dataTag: "Qt::Popup" ) << Qt::Popup << Qt::SubWindow |
871 | << Qt::WindowFlags{} << StandardWindowFlags; |
872 | QTest::newRow(dataTag: "Qt::Tool" ) << Qt::Tool << Qt::SubWindow |
873 | << Qt::WindowFlags{} << StandardWindowFlags; |
874 | QTest::newRow(dataTag: "Qt::ToolTip" ) << Qt::ToolTip << Qt::SubWindow |
875 | << Qt::WindowFlags{} << StandardWindowFlags; |
876 | QTest::newRow(dataTag: "Qt::SplashScreen" ) << Qt::SplashScreen << Qt::SubWindow |
877 | << Qt::WindowFlags{} << StandardWindowFlags; |
878 | QTest::newRow(dataTag: "Qt::Desktop" ) << Qt::Desktop << Qt::SubWindow |
879 | << Qt::WindowFlags{} << StandardWindowFlags; |
880 | QTest::newRow(dataTag: "Qt::SubWindow" ) << Qt::SubWindow << Qt::SubWindow |
881 | << Qt::WindowFlags{} << StandardWindowFlags; |
882 | |
883 | // Custom flags |
884 | QTest::newRow(dataTag: "Title" ) << Qt::SubWindow << Qt::SubWindow |
885 | << (Qt::WindowTitleHint | Qt::WindowFlags{}) |
886 | << Qt::WindowFlags{}; |
887 | QTest::newRow(dataTag: "TitleAndMin" ) << Qt::SubWindow << Qt::SubWindow |
888 | << (Qt::WindowTitleHint | Qt::WindowMinimizeButtonHint) |
889 | << Qt::WindowFlags{}; |
890 | QTest::newRow(dataTag: "TitleAndMax" ) << Qt::SubWindow << Qt::SubWindow |
891 | << (Qt::WindowTitleHint | Qt::WindowMaximizeButtonHint) |
892 | << Qt::WindowFlags{}; |
893 | QTest::newRow(dataTag: "TitleAndMinMax" ) << Qt::SubWindow << Qt::SubWindow |
894 | << (Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint) |
895 | << Qt::WindowFlags{}; |
896 | QTest::newRow(dataTag: "Standard" ) << Qt::SubWindow << Qt::SubWindow |
897 | << StandardWindowFlags |
898 | << Qt::WindowFlags{}; |
899 | QTest::newRow(dataTag: "StandardAndShade" ) << Qt::SubWindow << Qt::SubWindow |
900 | << (StandardWindowFlags | Qt::WindowShadeButtonHint) |
901 | << Qt::WindowFlags{}; |
902 | QTest::newRow(dataTag: "StandardAndContext" ) << Qt::SubWindow << Qt::SubWindow |
903 | << (StandardWindowFlags | Qt::WindowContextHelpButtonHint) |
904 | << Qt::WindowFlags{}; |
905 | QTest::newRow(dataTag: "StandardAndStaysOnTop" ) << Qt::SubWindow << Qt::SubWindow |
906 | << (StandardWindowFlags | Qt::WindowStaysOnTopHint) |
907 | << Qt::WindowFlags{}; |
908 | QTest::newRow(dataTag: "StandardAndFrameless" ) << Qt::SubWindow << Qt::SubWindow |
909 | << (StandardWindowFlags | Qt::FramelessWindowHint) |
910 | << Qt::WindowFlags(Qt::FramelessWindowHint); |
911 | QTest::newRow(dataTag: "StandardAndFramelessAndStaysOnTop" ) << Qt::SubWindow << Qt::SubWindow |
912 | << (StandardWindowFlags | Qt::FramelessWindowHint |
913 | | Qt::WindowStaysOnTopHint) |
914 | << (Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); |
915 | QTest::newRow(dataTag: "Shade" ) << Qt::SubWindow << Qt::SubWindow |
916 | << (Qt::WindowShadeButtonHint | Qt::WindowFlags{}) |
917 | << (StandardWindowFlags | Qt::WindowShadeButtonHint); |
918 | QTest::newRow(dataTag: "ShadeAndCustomize" ) << Qt::SubWindow << Qt::SubWindow |
919 | << (Qt::WindowShadeButtonHint | Qt::CustomizeWindowHint) |
920 | << Qt::WindowFlags{}; |
921 | QTest::newRow(dataTag: "Context" ) << Qt::SubWindow << Qt::SubWindow |
922 | << (Qt::WindowContextHelpButtonHint | Qt::WindowFlags{}) |
923 | << (StandardWindowFlags | Qt::WindowContextHelpButtonHint); |
924 | QTest::newRow(dataTag: "ContextAndCustomize" ) << Qt::SubWindow << Qt::SubWindow |
925 | << (Qt::WindowContextHelpButtonHint | Qt::CustomizeWindowHint) |
926 | << Qt::WindowFlags{}; |
927 | QTest::newRow(dataTag: "ShadeAndContext" ) << Qt::SubWindow << Qt::SubWindow |
928 | << (Qt::WindowShadeButtonHint | Qt::WindowContextHelpButtonHint) |
929 | << (StandardWindowFlags | Qt::WindowShadeButtonHint | Qt::WindowContextHelpButtonHint); |
930 | QTest::newRow(dataTag: "ShadeAndContextAndCustomize" ) << Qt::SubWindow << Qt::SubWindow |
931 | << (Qt::WindowShadeButtonHint | Qt::WindowContextHelpButtonHint | Qt::CustomizeWindowHint) |
932 | << Qt::WindowFlags{}; |
933 | QTest::newRow(dataTag: "OnlyCustomize" ) << Qt::SubWindow << Qt::SubWindow |
934 | << (Qt::CustomizeWindowHint | Qt::WindowFlags{}) |
935 | << Qt::WindowFlags{}; |
936 | } |
937 | |
938 | void tst_QMdiSubWindow::setWindowFlags() |
939 | { |
940 | QFETCH(Qt::WindowType, windowType); |
941 | QFETCH(Qt::WindowType, expectedWindowType); |
942 | QFETCH(Qt::WindowFlags, customFlags); |
943 | QFETCH(Qt::WindowFlags, expectedCustomFlags); |
944 | |
945 | QMdiArea workspace; |
946 | workspace.setWindowTitle(QLatin1String(QTest::currentTestFunction()) |
947 | + QLatin1String("::" ) + QLatin1String(QTest::currentDataTag())); |
948 | QMdiSubWindow *window = qobject_cast<QMdiSubWindow *>(object: workspace.addSubWindow(widget: new QWidget)); |
949 | QCoreApplication::processEvents(); |
950 | workspace.show(); |
951 | window->show(); |
952 | QVERIFY(QTest::qWaitForWindowExposed(&workspace)); |
953 | |
954 | window->setWindowFlags(windowType | customFlags); |
955 | QCOMPARE(window->windowType(), expectedWindowType); |
956 | |
957 | if (!expectedCustomFlags) // We expect the same as 'customFlags' |
958 | QCOMPARE(window->windowFlags() & ~expectedWindowType, customFlags); |
959 | else |
960 | QCOMPARE(window->windowFlags() & ~expectedWindowType, expectedCustomFlags); |
961 | } |
962 | |
963 | void tst_QMdiSubWindow::mouseDoubleClick() |
964 | { |
965 | QMdiArea workspace; |
966 | workspace.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
967 | QMdiSubWindow *window = qobject_cast<QMdiSubWindow *>(object: workspace.addSubWindow(widget: new QWidget)); |
968 | QCoreApplication::processEvents(); |
969 | workspace.show(); |
970 | window->show(); |
971 | |
972 | #ifdef Q_OS_WINRT |
973 | QEXPECT_FAIL("" , "Windows are maximized per default on WinRt " , Abort); |
974 | #endif |
975 | QVERIFY(!window->isMaximized()); |
976 | QVERIFY(!window->isShaded()); |
977 | |
978 | QRect originalGeometry = window->geometry(); |
979 | |
980 | // Calculate mouse position |
981 | QStyleOptionTitleBar options; |
982 | options.initFrom(w: window); |
983 | int height = window->style()->pixelMetric(metric: QStyle::PM_TitleBarHeight, option: &options); |
984 | // has border |
985 | if (!window->style()->styleHint(stylehint: QStyle::SH_TitleBar_NoBorder, opt: &options, widget: window)) |
986 | height += window->isMinimized() ? 8 : 4; |
987 | QPoint mousePosition(window->width() / 2, height - 1); |
988 | sendMouseMove(widget: window, point: mousePosition, button: Qt::NoButton); |
989 | |
990 | // Without Qt::WindowShadeButtonHint flag set |
991 | sendMouseDoubleClick(widget: window, point: mousePosition); |
992 | QCoreApplication::processEvents(); |
993 | QVERIFY(window->isMaximized()); |
994 | |
995 | sendMouseDoubleClick(widget: window, point: mousePosition); |
996 | QCoreApplication::processEvents(); |
997 | QVERIFY(!window->isMaximized()); |
998 | QCOMPARE(window->geometry(), originalGeometry); |
999 | |
1000 | // With Qt::WindowShadeButtonHint flag set |
1001 | window->setWindowFlags(window->windowFlags() | Qt::WindowShadeButtonHint); |
1002 | QVERIFY(window->windowFlags() & Qt::WindowShadeButtonHint); |
1003 | originalGeometry = window->geometry(); |
1004 | sendMouseDoubleClick(widget: window, point: mousePosition); |
1005 | QCoreApplication::processEvents(); |
1006 | QVERIFY(window->isShaded()); |
1007 | |
1008 | sendMouseDoubleClick(widget: window, point: mousePosition); |
1009 | QCoreApplication::processEvents(); |
1010 | QVERIFY(!window->isShaded()); |
1011 | QCOMPARE(window->geometry(), originalGeometry); |
1012 | |
1013 | window->showMinimized(); |
1014 | QVERIFY(window->isMinimized()); |
1015 | sendMouseDoubleClick(widget: window, point: mousePosition); |
1016 | QVERIFY(!window->isMinimized()); |
1017 | QCOMPARE(window->geometry(), originalGeometry); |
1018 | } |
1019 | |
1020 | void tst_QMdiSubWindow::() |
1021 | { |
1022 | QMdiSubWindow *subWindow = new QMdiSubWindow; |
1023 | subWindow->resize(w: 200, h: 50); |
1024 | QPointer<QMenu> = subWindow->systemMenu(); |
1025 | QVERIFY(systemMenu); |
1026 | QCOMPARE(subWindow->actions(), systemMenu->actions()); |
1027 | |
1028 | QMainWindow mainWindow; |
1029 | mainWindow.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
1030 | QMdiArea *mdiArea = new QMdiArea; |
1031 | mdiArea->addSubWindow(widget: subWindow); |
1032 | mainWindow.setCentralWidget(mdiArea); |
1033 | mainWindow.menuBar()->setNativeMenuBar(false); |
1034 | // Prevent the window from spanning screens |
1035 | if (QGuiApplication::screens().size() > 1) |
1036 | mainWindow.showMaximized(); |
1037 | else |
1038 | mainWindow.show(); |
1039 | QVERIFY(QTest::qWaitForWindowExposed(&mainWindow)); |
1040 | |
1041 | QTRY_VERIFY(subWindow->isVisible()); |
1042 | QPoint ; |
1043 | |
1044 | // Show system menu |
1045 | QVERIFY(!QApplication::activePopupWidget()); |
1046 | subWindow->showSystemMenu(); |
1047 | QTRY_COMPARE(QApplication::activePopupWidget(), qobject_cast<QWidget *>(systemMenu)); |
1048 | #ifdef Q_OS_WINRT |
1049 | QEXPECT_FAIL("" , "Broken on WinRT - QTBUG-68297" , Abort); |
1050 | #endif |
1051 | QTRY_COMPARE(systemMenu->mapToGlobal(QPoint(0, 0)), |
1052 | (globalPopupPos = subWindow->mapToGlobal(subWindow->contentsRect().topLeft())) ); |
1053 | |
1054 | systemMenu->hide(); |
1055 | QVERIFY(!QApplication::activePopupWidget()); |
1056 | |
1057 | QTest::ignoreMessage(type: QtWarningMsg, message: "QMdiSubWindow::setSystemMenu: system menu is already set" ); |
1058 | subWindow->setSystemMenu(systemMenu); |
1059 | |
1060 | subWindow->setSystemMenu(nullptr); |
1061 | QVERIFY(!systemMenu); // systemMenu is QPointer |
1062 | |
1063 | systemMenu = new QMenu(subWindow); |
1064 | systemMenu->addAction(icon: QIcon(subWindow->style()->standardIcon(standardIcon: QStyle::SP_TitleBarCloseButton)), |
1065 | text: QObject::tr(s: "&Close" ), receiver: subWindow, SLOT(close())); |
1066 | subWindow->setSystemMenu(systemMenu); |
1067 | QCOMPARE(subWindow->systemMenu(), qobject_cast<QMenu *>(systemMenu)); |
1068 | QCOMPARE(subWindow->systemMenu()->parentWidget(), static_cast<QWidget *>(subWindow)); |
1069 | QCOMPARE(subWindow->systemMenu()->actions().count(), 1); |
1070 | |
1071 | // Show the new system menu |
1072 | QVERIFY(!QApplication::activePopupWidget()); |
1073 | subWindow->showSystemMenu(); |
1074 | QTRY_COMPARE(QApplication::activePopupWidget(), qobject_cast<QWidget *>(systemMenu)); |
1075 | QTRY_COMPARE(systemMenu->mapToGlobal(QPoint(0, 0)), globalPopupPos); |
1076 | |
1077 | systemMenu->hide(); |
1078 | QVERIFY(!QApplication::activePopupWidget()); |
1079 | |
1080 | #if !defined (Q_OS_DARWIN) |
1081 | // System menu in menu bar. |
1082 | subWindow->showMaximized(); |
1083 | QVERIFY(subWindow->isMaximized()); |
1084 | QWidget * = subWindow->maximizedSystemMenuIconWidget(); |
1085 | QVERIFY(menuLabel); |
1086 | subWindow->showSystemMenu(); |
1087 | QTRY_COMPARE(QApplication::activePopupWidget(), qobject_cast<QWidget *>(systemMenu)); |
1088 | QCOMPARE(systemMenu->mapToGlobal(QPoint(0, 0)), |
1089 | (globalPopupPos = menuLabel->mapToGlobal(QPoint(0, menuLabel->y() + menuLabel->height())))); |
1090 | systemMenu->hide(); |
1091 | QTRY_VERIFY(!QApplication::activePopupWidget()); |
1092 | subWindow->showNormal(); |
1093 | #endif |
1094 | |
1095 | // Reverse |
1096 | LayoutDirectionGuard guard; |
1097 | QApplication::setLayoutDirection(Qt::RightToLeft); |
1098 | QCoreApplication::processEvents(); |
1099 | mainWindow.updateGeometry(); |
1100 | QTest::qWait(ms: 150); |
1101 | |
1102 | subWindow->showSystemMenu(); |
1103 | QTRY_COMPARE(QApplication::activePopupWidget(), qobject_cast<QWidget *>(systemMenu)); |
1104 | // + QPoint(1, 0) because topRight() == QPoint(left() + width() -1, top()) |
1105 | globalPopupPos = subWindow->mapToGlobal(subWindow->contentsRect().topRight()) + QPoint(1, 0); |
1106 | globalPopupPos -= QPoint(systemMenu->sizeHint().width(), 0); |
1107 | QTRY_COMPARE(systemMenu->mapToGlobal(QPoint(0, 0)), globalPopupPos); |
1108 | |
1109 | systemMenu->hide(); |
1110 | QVERIFY(!QApplication::activePopupWidget()); |
1111 | |
1112 | #if !defined (Q_OS_DARWIN) |
1113 | // System menu in menu bar in reverse mode. |
1114 | subWindow->showMaximized(); |
1115 | QVERIFY(subWindow->isMaximized()); |
1116 | menuLabel = subWindow->maximizedSystemMenuIconWidget(); |
1117 | QVERIFY(menuLabel); |
1118 | subWindow->showSystemMenu(); |
1119 | QTRY_COMPARE(QApplication::activePopupWidget(), qobject_cast<QWidget *>(systemMenu)); |
1120 | globalPopupPos = menuLabel->mapToGlobal(QPoint(menuLabel->width(), menuLabel->y() + menuLabel->height())); |
1121 | globalPopupPos -= QPoint(systemMenu->sizeHint().width(), 0); |
1122 | QTRY_COMPARE(systemMenu->mapToGlobal(QPoint(0, 0)), globalPopupPos); |
1123 | #endif |
1124 | |
1125 | delete systemMenu; |
1126 | QVERIFY(!QApplication::activePopupWidget()); |
1127 | QVERIFY(!subWindow->systemMenu()); |
1128 | } |
1129 | |
1130 | void tst_QMdiSubWindow::restoreFocus() |
1131 | { |
1132 | // Create complex layout. |
1133 | QGroupBox *box = new QGroupBox(tr(s: "GroupBox" )); |
1134 | box->setCheckable(true); |
1135 | |
1136 | QGroupBox *box1 = new QGroupBox(tr(s: "&TopLeft" )); |
1137 | box1->setLayout(new QHBoxLayout); |
1138 | box1->layout()->addWidget(w: new QTextEdit); |
1139 | |
1140 | QGroupBox *box2 = new QGroupBox(tr(s: "&TopRight" )); |
1141 | box2->setLayout(new QHBoxLayout); |
1142 | box2->layout()->addWidget(w: new QTextEdit); |
1143 | |
1144 | QGroupBox *box3 = new QGroupBox(tr(s: "&BottomLeft" )); |
1145 | box3->setLayout(new QHBoxLayout); |
1146 | box3->layout()->addWidget(w: new QTextEdit); |
1147 | |
1148 | QGroupBox *box4 = new QGroupBox(tr(s: "&BottomRight" )); |
1149 | box4->setLayout(new QHBoxLayout); |
1150 | QMdiArea *nestedWorkspace = new QMdiArea; |
1151 | for (int i = 0; i < 4; ++i) |
1152 | nestedWorkspace->addSubWindow(widget: new QTextEdit)->show(); |
1153 | QCoreApplication::processEvents(); |
1154 | nestedWorkspace->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); |
1155 | nestedWorkspace->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); |
1156 | box4->layout()->addWidget(w: nestedWorkspace); |
1157 | |
1158 | QGridLayout *layout = new QGridLayout; |
1159 | layout->addWidget(box1, row: 0, column: 0); |
1160 | layout->addWidget(box2, row: 0, column: 1); |
1161 | layout->addWidget(box3, row: 1, column: 0); |
1162 | layout->addWidget(box4, row: 1, column: 1); |
1163 | |
1164 | box->setLayout(layout); |
1165 | |
1166 | // Add complex widget to workspace. |
1167 | QMdiArea topArea; |
1168 | topArea.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
1169 | QMdiSubWindow *complexWindow = topArea.addSubWindow(widget: box); |
1170 | topArea.show(); |
1171 | box->show(); |
1172 | |
1173 | QApplication::setActiveWindow(&topArea); |
1174 | QMdiSubWindow *expectedFocusWindow = nestedWorkspace->subWindowList().last(); |
1175 | QVERIFY(expectedFocusWindow); |
1176 | QVERIFY(expectedFocusWindow->widget()); |
1177 | QCOMPARE(QApplication::focusWidget(), expectedFocusWindow->widget()); |
1178 | |
1179 | // Normal -> minimized |
1180 | expectedFocusWindow->showMinimized(); |
1181 | QCoreApplication::processEvents(); |
1182 | QVERIFY(expectedFocusWindow->isMinimized()); |
1183 | QCOMPARE(QApplication::focusWidget(), static_cast<QWidget *>(expectedFocusWindow)); |
1184 | |
1185 | // Minimized -> normal |
1186 | expectedFocusWindow->showNormal(); |
1187 | QCoreApplication::processEvents(); |
1188 | QVERIFY(!expectedFocusWindow->isMinimized()); |
1189 | QCOMPARE(QApplication::focusWidget(), expectedFocusWindow->widget()); |
1190 | |
1191 | // Normal -> maximized |
1192 | expectedFocusWindow->showMaximized(); |
1193 | QCoreApplication::processEvents(); |
1194 | QVERIFY(expectedFocusWindow->isMaximized()); |
1195 | QCOMPARE(QApplication::focusWidget(), expectedFocusWindow->widget()); |
1196 | |
1197 | // Maximized -> normal |
1198 | expectedFocusWindow->showNormal(); |
1199 | QCoreApplication::processEvents(); |
1200 | QVERIFY(!expectedFocusWindow->isMaximized()); |
1201 | QCOMPARE(QApplication::focusWidget(), expectedFocusWindow->widget()); |
1202 | |
1203 | // Minimized -> maximized |
1204 | expectedFocusWindow->showMinimized(); |
1205 | QCoreApplication::processEvents(); |
1206 | QVERIFY(expectedFocusWindow->isMinimized()); |
1207 | expectedFocusWindow->showMaximized(); |
1208 | QCoreApplication::processEvents(); |
1209 | QVERIFY(expectedFocusWindow->isMaximized()); |
1210 | QCOMPARE(QApplication::focusWidget(), expectedFocusWindow->widget()); |
1211 | |
1212 | // Maximized -> minimized |
1213 | expectedFocusWindow->showNormal(); |
1214 | QCoreApplication::processEvents(); |
1215 | QVERIFY(!expectedFocusWindow->isMaximized()); |
1216 | expectedFocusWindow->showMaximized(); |
1217 | QCoreApplication::processEvents(); |
1218 | QVERIFY(expectedFocusWindow->isMaximized()); |
1219 | expectedFocusWindow->showMinimized(); |
1220 | QCoreApplication::processEvents(); |
1221 | QVERIFY(expectedFocusWindow->isMinimized()); |
1222 | QCOMPARE(QApplication::focusWidget(), static_cast<QWidget *>(expectedFocusWindow)); |
1223 | |
1224 | complexWindow->showMinimized(); |
1225 | QCoreApplication::processEvents(); |
1226 | QVERIFY(complexWindow->isMinimized()); |
1227 | QCOMPARE(QApplication::focusWidget(), static_cast<QWidget *>(complexWindow)); |
1228 | |
1229 | complexWindow->showNormal(); |
1230 | QCoreApplication::processEvents(); |
1231 | QVERIFY(!complexWindow->isMinimized()); |
1232 | QCOMPARE(QApplication::focusWidget(), static_cast<QWidget *>(expectedFocusWindow)); |
1233 | } |
1234 | |
1235 | class MultiWidget : public QWidget { |
1236 | public: |
1237 | explicit MultiWidget(QWidget *parent = nullptr) : QWidget(parent) |
1238 | , m_lineEdit1(new QLineEdit(this)), m_lineEdit2(new QLineEdit(this)) |
1239 | { |
1240 | QVBoxLayout *lt = new QVBoxLayout(this); |
1241 | lt->addWidget(m_lineEdit1); |
1242 | lt->addWidget(m_lineEdit2); |
1243 | } |
1244 | |
1245 | QLineEdit *m_lineEdit1; |
1246 | QLineEdit *m_lineEdit2; |
1247 | }; |
1248 | |
1249 | void tst_QMdiSubWindow::restoreFocusOverCreation() |
1250 | { |
1251 | // QTBUG-38378, verify that the focus child of a subwindow |
1252 | // is not "forgotten" when adding yet another subwindow. |
1253 | QMdiArea mdiArea; |
1254 | mdiArea.resize(w: 800, h: 800); |
1255 | mdiArea.move(QGuiApplication::primaryScreen()->availableGeometry().center() - QPoint(400, 400)); |
1256 | mdiArea.setWindowTitle(QStringLiteral("restoreFocusOverCreation" )); |
1257 | |
1258 | MultiWidget *subWidget1 = new MultiWidget; |
1259 | MultiWidget *subWidget2 = new MultiWidget; |
1260 | |
1261 | QMdiSubWindow *subWindow1 = mdiArea.addSubWindow(widget: subWidget1); |
1262 | subWidget1->m_lineEdit2->setFocus(); |
1263 | subWindow1->show(); |
1264 | mdiArea.show(); |
1265 | QApplication::setActiveWindow(&mdiArea); |
1266 | QVERIFY(QTest::qWaitForWindowActive(&mdiArea)); |
1267 | QCOMPARE(QApplication::focusWidget(), subWidget1->m_lineEdit2); |
1268 | |
1269 | QMdiSubWindow *subWindow2 = mdiArea.addSubWindow(widget: subWidget2); |
1270 | subWindow2->show(); |
1271 | QTRY_COMPARE(QApplication::focusWidget(), subWidget2->m_lineEdit1); |
1272 | |
1273 | mdiArea.setActiveSubWindow(subWindow1); |
1274 | #ifdef Q_OS_WINRT |
1275 | QEXPECT_FAIL("" , "Broken on WinRt - QTBUG-68297" , Abort); |
1276 | #endif |
1277 | QTRY_COMPARE(QApplication::focusWidget(), subWidget1->m_lineEdit2); |
1278 | } |
1279 | |
1280 | void tst_QMdiSubWindow::changeFocusWithTab() |
1281 | { |
1282 | QWidget *widget = new QWidget; |
1283 | widget->setLayout(new QVBoxLayout); |
1284 | |
1285 | QLineEdit *firstLineEdit = new QLineEdit; |
1286 | widget->layout()->addWidget(w: firstLineEdit); |
1287 | QLineEdit *secondLineEdit = new QLineEdit; |
1288 | widget->layout()->addWidget(w: secondLineEdit); |
1289 | QLineEdit *thirdLineEdit = new QLineEdit; |
1290 | widget->layout()->addWidget(w: thirdLineEdit); |
1291 | |
1292 | QMdiArea mdiArea; |
1293 | mdiArea.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
1294 | mdiArea.addSubWindow(widget); |
1295 | mdiArea.show(); |
1296 | QCOMPARE(mdiArea.subWindowList().count(), 1); |
1297 | |
1298 | QApplication::setActiveWindow(&mdiArea); |
1299 | QCOMPARE(QApplication::focusWidget(), static_cast<QWidget *>(firstLineEdit)); |
1300 | |
1301 | // Next |
1302 | QTest::keyPress(widget, key: Qt::Key_Tab); |
1303 | QCOMPARE(QApplication::focusWidget(), static_cast<QWidget *>(secondLineEdit)); |
1304 | |
1305 | // Next |
1306 | QTest::keyPress(widget, key: Qt::Key_Tab); |
1307 | QCOMPARE(QApplication::focusWidget(), static_cast<QWidget *>(thirdLineEdit)); |
1308 | |
1309 | // Previous |
1310 | QTest::keyPress(widget, key: Qt::Key_Backtab); |
1311 | QCOMPARE(QApplication::focusWidget(), static_cast<QWidget *>(secondLineEdit)); |
1312 | |
1313 | // Previous |
1314 | QTest::keyPress(widget, key: Qt::Key_Backtab); |
1315 | QCOMPARE(QApplication::focusWidget(), static_cast<QWidget *>(firstLineEdit)); |
1316 | |
1317 | QMdiSubWindow *window = mdiArea.addSubWindow(widget: new TestPushButton(QLatin1String("test" ))); |
1318 | window->show(); |
1319 | QCOMPARE(mdiArea.activeSubWindow(), window); |
1320 | |
1321 | // Check that we don't give away focus to another window by |
1322 | // just hitting tab if the child widget does not accept |
1323 | // focus (which is the case for a QPushButton). |
1324 | QTest::keyPress(widget: window, key: Qt::Key_Tab); |
1325 | QCOMPARE(mdiArea.activeSubWindow(), window); |
1326 | QCOMPARE(QApplication::focusWidget(), tabAllWidgets() ? window->widget() : window); |
1327 | QTest::keyPress(widget: window, key: Qt::Key_Tab); |
1328 | QCOMPARE(mdiArea.activeSubWindow(), window); |
1329 | QCOMPARE(QApplication::focusWidget(), tabAllWidgets() ? window->widget() : window); |
1330 | } |
1331 | |
1332 | class MyTextEdit : public QTextEdit |
1333 | { |
1334 | public: |
1335 | using QTextEdit::QTextEdit; |
1336 | void setAcceptClose(bool enable = true) { acceptClose = enable; } |
1337 | protected: |
1338 | void closeEvent(QCloseEvent *closeEvent) override |
1339 | { |
1340 | if (!acceptClose) |
1341 | closeEvent->ignore(); |
1342 | } |
1343 | |
1344 | private: |
1345 | bool acceptClose = false; |
1346 | }; |
1347 | |
1348 | void tst_QMdiSubWindow::closeEvent() |
1349 | { |
1350 | QMdiArea mdiArea; |
1351 | mdiArea.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
1352 | mdiArea.show(); |
1353 | |
1354 | MyTextEdit *textEdit = new MyTextEdit; |
1355 | textEdit->setAcceptClose(false); |
1356 | QMdiSubWindow *window = mdiArea.addSubWindow(widget: textEdit); |
1357 | EventSpy closeSpy(window->widget(), QEvent::Close); |
1358 | window->show(); |
1359 | |
1360 | QCOMPARE(closeSpy.count(), 0); |
1361 | QVERIFY(window->isVisible()); |
1362 | QVERIFY(textEdit->isVisible()); |
1363 | |
1364 | QVERIFY(!window->close()); |
1365 | QCOMPARE(closeSpy.count(), 1); |
1366 | QVERIFY(window->isVisible()); |
1367 | QVERIFY(textEdit->isVisible()); |
1368 | |
1369 | QVERIFY(!textEdit->close()); |
1370 | QCOMPARE(closeSpy.count(), 2); |
1371 | QVERIFY(window->isVisible()); |
1372 | QVERIFY(textEdit->isVisible()); |
1373 | |
1374 | textEdit->setAcceptClose(true); |
1375 | |
1376 | QVERIFY(window->close()); |
1377 | QCOMPARE(closeSpy.count(), 3); |
1378 | QCOMPARE(mdiArea.subWindowList().count(), 0); |
1379 | } |
1380 | |
1381 | // There exists more tests in QMdiArea which covers window title support |
1382 | // related to QMainWindow. This test is specific for QMdiSubWindow and its |
1383 | // widget. |
1384 | void tst_QMdiSubWindow::setWindowTitle() |
1385 | { |
1386 | QString expectedWindowTitle = QLatin1String("This is teh shit[*]" ); |
1387 | QTextEdit *textEdit = new QTextEdit; |
1388 | textEdit->setWindowTitle(expectedWindowTitle); |
1389 | QCOMPARE(textEdit->windowTitle(), expectedWindowTitle); |
1390 | textEdit->setWindowModified(true); |
1391 | QCOMPARE(textEdit->isWindowModified(), true); |
1392 | |
1393 | QMdiArea mdiArea; |
1394 | QMdiSubWindow *window = new QMdiSubWindow; |
1395 | mdiArea.addSubWindow(widget: window); |
1396 | QCOMPARE(window->windowTitle(), QString()); |
1397 | QVERIFY(!window->isWindowModified()); |
1398 | |
1399 | window->setWidget(textEdit); |
1400 | QVERIFY(window->isWindowModified()); |
1401 | QCOMPARE(textEdit->windowTitle(), expectedWindowTitle); |
1402 | QCOMPARE(window->windowTitle(), window->widget()->windowTitle()); |
1403 | |
1404 | textEdit->setWindowModified(false); |
1405 | QVERIFY(!textEdit->isWindowModified()); |
1406 | QVERIFY(!window->isWindowModified()); |
1407 | // This will return the title including the astrix, but the |
1408 | // actual window title does not contain the astrix. This behavior |
1409 | // seems a bit odd, but is equal to e.g. QTextEdit (and probably all |
1410 | // other widgets which are not real top-level widgets). |
1411 | QCOMPARE(window->windowTitle(), expectedWindowTitle); |
1412 | |
1413 | textEdit->setWindowModified(true);; |
1414 | expectedWindowTitle = QLatin1String("Override child title" ); |
1415 | window->setWindowTitle(expectedWindowTitle); |
1416 | QVERIFY(window->isWindowModified()); |
1417 | QCOMPARE(window->windowTitle(), expectedWindowTitle); |
1418 | |
1419 | textEdit->setWindowTitle(QLatin1String("My parent overrides me" )); |
1420 | QCOMPARE(window->windowTitle(), expectedWindowTitle); |
1421 | |
1422 | textEdit->setWindowModified(false); |
1423 | QVERIFY(window->isWindowModified()); |
1424 | QCOMPARE(window->windowTitle(), expectedWindowTitle); |
1425 | |
1426 | window->setWindowModified(false); |
1427 | QVERIFY(!window->isWindowModified()); |
1428 | window->setWindowTitle(QString()); |
1429 | QCOMPARE(window->windowTitle(), QString()); |
1430 | |
1431 | expectedWindowTitle = QLatin1String("My parent doesn't have any title so now I can set one[*]" ); |
1432 | textEdit->setWindowTitle(expectedWindowTitle); |
1433 | QCOMPARE(window->windowTitle(), expectedWindowTitle); |
1434 | textEdit->setWindowModified(true); |
1435 | QVERIFY(window->isWindowModified()); |
1436 | |
1437 | window->setWidget(nullptr); |
1438 | QCOMPARE(window->windowTitle(), QString()); |
1439 | QVERIFY(!window->isWindowModified()); |
1440 | delete textEdit; |
1441 | } |
1442 | |
1443 | void tst_QMdiSubWindow::resizeEvents_data() |
1444 | { |
1445 | QTest::addColumn<Qt::WindowState>(name: "windowState" ); |
1446 | QTest::addColumn<int>(name: "expectedWindowResizeEvents" ); |
1447 | QTest::addColumn<int>(name: "expectedWidgetResizeEvents" ); |
1448 | QTest::addColumn<bool>(name: "isShadeMode" ); |
1449 | |
1450 | QTest::newRow(dataTag: "minimized" ) << Qt::WindowMinimized << 1 << 0 << false; |
1451 | QTest::newRow(dataTag: "maximized" ) << Qt::WindowMaximized << 1 << 1 << false; |
1452 | QTest::newRow(dataTag: "shaded" ) << Qt::WindowMinimized << 1 << 0 << true; |
1453 | } |
1454 | |
1455 | void tst_QMdiSubWindow::resizeEvents() |
1456 | { |
1457 | QFETCH(Qt::WindowState, windowState); |
1458 | QFETCH(int, expectedWindowResizeEvents); |
1459 | QFETCH(int, expectedWidgetResizeEvents); |
1460 | QFETCH(bool, isShadeMode); |
1461 | |
1462 | QMainWindow mainWindow; |
1463 | mainWindow.setWindowTitle(QLatin1String(QTest::currentTestFunction()) |
1464 | + QLatin1String("::" ) + QLatin1String(QTest::currentDataTag())); |
1465 | QMdiArea *mdiArea = new QMdiArea; |
1466 | mainWindow.setCentralWidget(mdiArea); |
1467 | mainWindow.show(); |
1468 | QVERIFY(QTest::qWaitForWindowExposed(&mainWindow)); |
1469 | |
1470 | QMdiSubWindow *window = mdiArea->addSubWindow(widget: new QTextEdit); |
1471 | window->show(); |
1472 | |
1473 | EventSpy windowResizeEventSpy(window, QEvent::Resize); |
1474 | QCOMPARE(windowResizeEventSpy.count(), 0); |
1475 | EventSpy widgetResizeEventSpy(window->widget(), QEvent::Resize); |
1476 | QCOMPARE(widgetResizeEventSpy.count(), 0); |
1477 | |
1478 | // Set the window state. |
1479 | if (!isShadeMode) |
1480 | window->setWindowState(windowState); |
1481 | else |
1482 | window->showShaded(); |
1483 | |
1484 | // Check that the window state is correct. |
1485 | QCOMPARE(window->windowState(), windowState | Qt::WindowActive); |
1486 | QCOMPARE(window->widget()->windowState(), windowState); |
1487 | |
1488 | // Make sure we got as many resize events as expected. |
1489 | #ifdef Q_OS_WINRT |
1490 | QEXPECT_FAIL("maximized" , "Broken on WinRT - QTBUG-68297" , Abort); |
1491 | #endif |
1492 | QCOMPARE(windowResizeEventSpy.count(), expectedWindowResizeEvents); |
1493 | QCOMPARE(widgetResizeEventSpy.count(), expectedWidgetResizeEvents); |
1494 | windowResizeEventSpy.clear(); |
1495 | widgetResizeEventSpy.clear(); |
1496 | |
1497 | // Normalize. |
1498 | window->showNormal(); |
1499 | |
1500 | // Check that the window state is correct. |
1501 | #ifdef Q_OS_WINRT |
1502 | QEXPECT_FAIL("minimized" , "Broken on WinRT - QTBUG-68297" , Abort); |
1503 | QEXPECT_FAIL("shaded" , "Broken on WinRT - QTBUG-68297" , Abort); |
1504 | #endif |
1505 | QCOMPARE(window->windowState(), Qt::WindowNoState | Qt::WindowActive); |
1506 | QCOMPARE(window->widget()->windowState(), Qt::WindowNoState); |
1507 | |
1508 | // Make sure we got as many resize events as expected. |
1509 | QCOMPARE(windowResizeEventSpy.count(), expectedWindowResizeEvents); |
1510 | QCOMPARE(widgetResizeEventSpy.count(), expectedWidgetResizeEvents); |
1511 | } |
1512 | |
1513 | #if defined(Q_OS_MAC) |
1514 | void tst_QMdiSubWindow::defaultSizeGrip() |
1515 | { |
1516 | if (!qApp->style()->inherits("QMacStyle" )) |
1517 | return; |
1518 | QMdiArea mdiArea; |
1519 | mdiArea.show(); |
1520 | |
1521 | // QSizeGrip on windows with decoration. |
1522 | QMdiSubWindow *windowWithDecoration = mdiArea.addSubWindow(new QWidget); |
1523 | windowWithDecoration->show(); |
1524 | QVERIFY(windowWithDecoration->findChild<QSizeGrip *>()); |
1525 | |
1526 | // ...but not on windows without decoration (Qt::FramelessWindowHint). |
1527 | QMdiSubWindow *windowWithoutDecoration = mdiArea.addSubWindow(new QWidget, Qt::FramelessWindowHint); |
1528 | windowWithoutDecoration->show(); |
1529 | QVERIFY(!windowWithoutDecoration->findChild<QSizeGrip *>()); |
1530 | } |
1531 | #endif |
1532 | |
1533 | void tst_QMdiSubWindow::hideAndShow() |
1534 | { |
1535 | // Create a QTabWidget with two tabs; QMdiArea and QTextEdit. |
1536 | QTabWidget *tabWidget = new QTabWidget; |
1537 | QMdiArea *mdiArea = new QMdiArea; |
1538 | tabWidget->addTab(widget: mdiArea, QLatin1String("QMdiArea" )); |
1539 | tabWidget->addTab(widget: new QTextEdit, QLatin1String("Dummy" )); |
1540 | |
1541 | // Set the tab widget as the central widget in QMainWindow. |
1542 | QMainWindow mainWindow; |
1543 | mainWindow.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
1544 | mainWindow.setGeometry(ax: 0, ay: 0, aw: 640, ah: 480); |
1545 | QMenuBar * = mainWindow.menuBar(); |
1546 | menuBar->setNativeMenuBar(false); |
1547 | mainWindow.setCentralWidget(tabWidget); |
1548 | mainWindow.show(); |
1549 | QVERIFY(QTest::qWaitForWindowExposed(&mainWindow)); |
1550 | |
1551 | QVERIFY(!menuBar->cornerWidget(Qt::TopRightCorner)); |
1552 | QMdiSubWindow *subWindow = mdiArea->addSubWindow(widget: new QTextEdit); |
1553 | subWindow->showMaximized(); |
1554 | #if !defined (Q_OS_DARWIN) |
1555 | QVERIFY(menuBar->cornerWidget(Qt::TopRightCorner)); |
1556 | QCOMPARE(menuBar->cornerWidget(Qt::TopRightCorner), subWindow->maximizedButtonsWidget()); |
1557 | #endif |
1558 | |
1559 | // Hide QMdiArea. |
1560 | tabWidget->setCurrentIndex(1); |
1561 | |
1562 | QVERIFY(!menuBar->cornerWidget(Qt::TopRightCorner)); |
1563 | QVERIFY(!subWindow->maximizedButtonsWidget()); |
1564 | QVERIFY(!subWindow->maximizedSystemMenuIconWidget()); |
1565 | |
1566 | // Show QMdiArea. |
1567 | tabWidget->setCurrentIndex(0); |
1568 | |
1569 | #if !defined (Q_OS_DARWIN) |
1570 | QVERIFY(menuBar->cornerWidget(Qt::TopRightCorner)); |
1571 | QVERIFY(subWindow->maximizedButtonsWidget()); |
1572 | QVERIFY(subWindow->maximizedSystemMenuIconWidget()); |
1573 | QCOMPARE(menuBar->cornerWidget(Qt::TopRightCorner), subWindow->maximizedButtonsWidget()); |
1574 | #endif |
1575 | |
1576 | // Hide QMdiArea. |
1577 | tabWidget->setCurrentIndex(1); |
1578 | |
1579 | // Add few more windows. |
1580 | for (int i = 0; i < 5; ++i) |
1581 | mdiArea->addSubWindow(widget: new QTextEdit); |
1582 | |
1583 | // Show QMdiArea. |
1584 | tabWidget->setCurrentIndex(0); |
1585 | QCoreApplication::processEvents(); |
1586 | |
1587 | subWindow = mdiArea->subWindowList().back(); |
1588 | QVERIFY(subWindow); |
1589 | QCOMPARE(mdiArea->activeSubWindow(), subWindow); |
1590 | |
1591 | #if !defined (Q_OS_DARWIN) |
1592 | QVERIFY(menuBar->cornerWidget(Qt::TopRightCorner)); |
1593 | #if defined Q_OS_QNX |
1594 | QEXPECT_FAIL("" , "QTBUG-38231" , Abort); |
1595 | #endif |
1596 | QVERIFY(subWindow->maximizedButtonsWidget()); |
1597 | QVERIFY(subWindow->maximizedSystemMenuIconWidget()); |
1598 | QCOMPARE(menuBar->cornerWidget(Qt::TopRightCorner), subWindow->maximizedButtonsWidget()); |
1599 | #endif |
1600 | |
1601 | subWindow->showNormal(); |
1602 | QVERIFY(!menuBar->cornerWidget(Qt::TopRightCorner)); |
1603 | |
1604 | // Check that newly added windows got right sizes. |
1605 | const auto subWindowList = mdiArea->subWindowList(); |
1606 | for (QMdiSubWindow *window : subWindowList) |
1607 | QCOMPARE(window->size(), window->sizeHint()); |
1608 | |
1609 | subWindow->showMaximized(); |
1610 | #ifndef Q_OS_MAC |
1611 | QCOMPARE(menuBar->cornerWidget(Qt::TopRightCorner), subWindow->maximizedButtonsWidget()); |
1612 | #endif |
1613 | |
1614 | subWindow->hide(); |
1615 | QVERIFY(!subWindow->maximizedButtonsWidget()); |
1616 | QVERIFY(!subWindow->maximizedSystemMenuIconWidget()); |
1617 | QVERIFY(!menuBar->cornerWidget(Qt::TopRightCorner)); |
1618 | |
1619 | subWindow->show(); |
1620 | #if !defined (Q_OS_DARWIN) |
1621 | QVERIFY(subWindow->maximizedButtonsWidget()); |
1622 | QVERIFY(subWindow->maximizedSystemMenuIconWidget()); |
1623 | QCOMPARE(menuBar->cornerWidget(Qt::TopRightCorner), subWindow->maximizedButtonsWidget()); |
1624 | #endif |
1625 | |
1626 | // Hide QMainWindow. |
1627 | mainWindow.hide(); |
1628 | QVERIFY(!subWindow->maximizedButtonsWidget()); |
1629 | QVERIFY(!subWindow->maximizedSystemMenuIconWidget()); |
1630 | QVERIFY(!menuBar->cornerWidget(Qt::TopRightCorner)); |
1631 | |
1632 | // Show QMainWindow. |
1633 | mainWindow.show(); |
1634 | #if !defined (Q_OS_DARWIN) |
1635 | QVERIFY(subWindow->maximizedButtonsWidget()); |
1636 | QVERIFY(subWindow->maximizedSystemMenuIconWidget()); |
1637 | QCOMPARE(menuBar->cornerWidget(Qt::TopRightCorner), subWindow->maximizedButtonsWidget()); |
1638 | #endif |
1639 | } |
1640 | |
1641 | void tst_QMdiSubWindow::keepWindowMaximizedState() |
1642 | { |
1643 | QMdiArea mdiArea; |
1644 | mdiArea.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
1645 | QMdiSubWindow *subWindow = mdiArea.addSubWindow(widget: new QTextEdit); |
1646 | mdiArea.show(); |
1647 | QVERIFY(QTest::qWaitForWindowExposed(&mdiArea)); |
1648 | |
1649 | subWindow->showMaximized(); |
1650 | QVERIFY(subWindow->isMaximized()); |
1651 | |
1652 | // move |
1653 | const QPoint newPosition = subWindow->pos() + QPoint(10, 10); |
1654 | subWindow->move(newPosition); |
1655 | QCOMPARE(subWindow->pos(), newPosition); |
1656 | QVERIFY(subWindow->isMaximized()); |
1657 | |
1658 | // resize |
1659 | const QSize newSize = subWindow->size() - QSize(10, 10); |
1660 | subWindow->resize(newSize); |
1661 | QCOMPARE(subWindow->size(), newSize); |
1662 | QVERIFY(subWindow->isMaximized()); |
1663 | |
1664 | // setGeometry |
1665 | const QRect newGeometry = QRect(newPosition - QPoint(10, 10), newSize + QSize(10, 10)); |
1666 | subWindow->setGeometry(newGeometry); |
1667 | QCOMPARE(subWindow->geometry(), newGeometry); |
1668 | QVERIFY(subWindow->isMaximized()); |
1669 | |
1670 | subWindow->showNormal(); |
1671 | |
1672 | // Verify that we don't force Qt::WindowMaximized. |
1673 | QVERIFY(!subWindow->isMaximized()); |
1674 | subWindow->setGeometry(QRect(newPosition, newSize)); |
1675 | QCOMPARE(subWindow->geometry(), QRect(newPosition, newSize)); |
1676 | QVERIFY(!subWindow->isMaximized()); |
1677 | } |
1678 | |
1679 | void tst_QMdiSubWindow::explicitlyHiddenWidget() |
1680 | { |
1681 | QMdiArea mdiArea; |
1682 | mdiArea.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
1683 | QTextEdit *textEdit = new QTextEdit; |
1684 | textEdit->hide(); |
1685 | QMdiSubWindow *subWindow = mdiArea.addSubWindow(widget: textEdit); |
1686 | mdiArea.show(); |
1687 | QVERIFY(QTest::qWaitForWindowExposed(&mdiArea)); |
1688 | |
1689 | QVERIFY(subWindow->isVisible()); |
1690 | QVERIFY(!textEdit->isVisible()); |
1691 | |
1692 | textEdit->show(); |
1693 | QVERIFY(textEdit->isVisible()); |
1694 | |
1695 | // normal -> minimized |
1696 | subWindow->showMinimized(); |
1697 | QVERIFY(subWindow->isVisible()); |
1698 | QVERIFY(!textEdit->isVisible()); |
1699 | |
1700 | // minimized -> normal |
1701 | subWindow->showNormal(); |
1702 | QVERIFY(subWindow->isVisible()); |
1703 | QVERIFY(textEdit->isVisible()); |
1704 | |
1705 | // minimized -> maximized |
1706 | subWindow->showMinimized(); |
1707 | subWindow->showMaximized(); |
1708 | QVERIFY(subWindow->isVisible()); |
1709 | QVERIFY(textEdit->isVisible()); |
1710 | |
1711 | textEdit->hide(); |
1712 | |
1713 | // maximized -> normal |
1714 | subWindow->showNormal(); |
1715 | QVERIFY(subWindow->isVisible()); |
1716 | QVERIFY(!textEdit->isVisible()); |
1717 | |
1718 | textEdit->show(); |
1719 | |
1720 | subWindow->showMinimized(); |
1721 | subWindow->setWidget(nullptr); |
1722 | delete textEdit; |
1723 | textEdit = new QTextEdit; |
1724 | textEdit->hide(); |
1725 | subWindow->setWidget(textEdit); |
1726 | subWindow->showNormal(); |
1727 | QVERIFY(subWindow->isVisible()); |
1728 | QVERIFY(!textEdit->isVisible()); |
1729 | } |
1730 | |
1731 | void tst_QMdiSubWindow::resizeTimer() |
1732 | { |
1733 | QMdiArea mdiArea; |
1734 | mdiArea.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
1735 | QMdiSubWindow *subWindow = mdiArea.addSubWindow(widget: new QWidget); |
1736 | mdiArea.show(); |
1737 | QVERIFY(QTest::qWaitForWindowExposed(&mdiArea)); |
1738 | |
1739 | EventSpy timerEventSpy(subWindow, QEvent::Timer); |
1740 | QCOMPARE(timerEventSpy.count(), 0); |
1741 | |
1742 | for (int i = 0; i < 20; ++i) { |
1743 | subWindow->resize(subWindow->size() + QSize(2, 2)); |
1744 | QCoreApplication::processEvents(); |
1745 | } |
1746 | |
1747 | QTest::qWait(ms: 500); // Wait for timer events to occur. |
1748 | |
1749 | QTRY_VERIFY(timerEventSpy.count() > 0); |
1750 | } |
1751 | |
1752 | void tst_QMdiSubWindow::fixedMinMaxSize() |
1753 | { |
1754 | QMdiArea mdiArea; |
1755 | mdiArea.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
1756 | mdiArea.setGeometry(ax: 0, ay: 0, aw: 640, ah: 480); |
1757 | mdiArea.show(); |
1758 | QVERIFY(QTest::qWaitForWindowExposed(&mdiArea)); |
1759 | |
1760 | const QSize minimumSize = QSize(250, 150); |
1761 | const QSize maximumSize = QSize(300, 200); |
1762 | |
1763 | // Add the sub window to QMdiArea and set min/max size. |
1764 | QMdiSubWindow *subWindow = new QMdiSubWindow; |
1765 | subWindow->setMinimumSize(minimumSize); |
1766 | QCOMPARE(subWindow->minimumSize(), minimumSize); |
1767 | subWindow->setMaximumSize(maximumSize); |
1768 | QCOMPARE(subWindow->maximumSize(), maximumSize); |
1769 | mdiArea.addSubWindow(widget: subWindow); |
1770 | subWindow->show(); |
1771 | #ifdef Q_OS_WINRT |
1772 | QEXPECT_FAIL("" , "Windows are maximized per default on WinRt " , Abort); |
1773 | #endif |
1774 | QCOMPARE(subWindow->size(), minimumSize); |
1775 | |
1776 | // Calculate the size of a minimized sub window. |
1777 | QStyleOptionTitleBar options; |
1778 | options.initFrom(w: subWindow); |
1779 | int minimizedHeight = subWindow->style()->pixelMetric(metric: QStyle::PM_TitleBarHeight, option: &options); |
1780 | if (!subWindow->style()->styleHint(stylehint: QStyle::SH_TitleBar_NoBorder, opt: &options, widget: subWindow)) |
1781 | minimizedHeight += 8; |
1782 | int minimizedWidth = subWindow->style()->pixelMetric(metric: QStyle::PM_MdiSubWindowMinimizedWidth, |
1783 | option: &options); |
1784 | const QSize minimizedSize = QSize(minimizedWidth, minimizedHeight); |
1785 | |
1786 | // Even though the sub window has a minimum size set, it should be possible |
1787 | // to minimize the window. |
1788 | subWindow->showMinimized(); |
1789 | QVERIFY(subWindow->isMinimized()); |
1790 | QCOMPARE(subWindow->size(), minimizedSize); |
1791 | QCOMPARE(subWindow->minimumSize(), minimizedSize); |
1792 | |
1793 | // Restore minimum size. |
1794 | subWindow->showNormal(); |
1795 | QVERIFY(!subWindow->isMinimized()); |
1796 | QCOMPARE(subWindow->size(), minimumSize); |
1797 | QCOMPARE(subWindow->minimumSize(), minimumSize); |
1798 | |
1799 | // Well, the logic here is of course broken (calling showMaximized on a window with |
1800 | // maximum size set), but we should handle it :) |
1801 | subWindow->showMaximized(); |
1802 | QVERIFY(subWindow->isMaximized()); |
1803 | QCOMPARE(subWindow->size(), maximumSize); |
1804 | |
1805 | subWindow->showNormal(); |
1806 | QVERIFY(!subWindow->isMaximized()); |
1807 | QCOMPARE(subWindow->size(), minimumSize); |
1808 | } |
1809 | |
1810 | #if !defined( Q_OS_DARWIN) |
1811 | void tst_QMdiSubWindow::() |
1812 | { |
1813 | |
1814 | QMainWindow mainWindow; |
1815 | mainWindow.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
1816 | |
1817 | QMdiArea *mdiArea = new QMdiArea; |
1818 | QMdiSubWindow *subWindow = mdiArea->addSubWindow(widget: new QTextEdit); |
1819 | subWindow->showMaximized(); |
1820 | |
1821 | mainWindow.setCentralWidget(mdiArea); |
1822 | QMenuBar * = mainWindow.menuBar(); |
1823 | menuBar1->setNativeMenuBar(false); |
1824 | mainWindow.show(); |
1825 | QVERIFY(QTest::qWaitForWindowExposed(&mainWindow)); |
1826 | |
1827 | QCoreApplication::processEvents(); |
1828 | |
1829 | #if defined Q_OS_QNX |
1830 | QEXPECT_FAIL("" , "QTBUG-38231" , Abort); |
1831 | #endif |
1832 | QVERIFY(subWindow->maximizedButtonsWidget()); |
1833 | QVERIFY(subWindow->maximizedSystemMenuIconWidget()); |
1834 | QCOMPARE(menuBar1->cornerWidget(Qt::TopLeftCorner), subWindow->maximizedSystemMenuIconWidget()); |
1835 | QCOMPARE(menuBar1->cornerWidget(Qt::TopRightCorner), subWindow->maximizedButtonsWidget()); |
1836 | |
1837 | // Replace. |
1838 | auto = new QMenuBar; |
1839 | mainWindow.setMenuBar(menuBar2); |
1840 | menuBar2->setNativeMenuBar(false); |
1841 | QCoreApplication::processEvents(); |
1842 | |
1843 | QVERIFY(subWindow->maximizedButtonsWidget()); |
1844 | QVERIFY(subWindow->maximizedSystemMenuIconWidget()); |
1845 | QCOMPARE(menuBar2->cornerWidget(Qt::TopLeftCorner), subWindow->maximizedSystemMenuIconWidget()); |
1846 | QCOMPARE(menuBar2->cornerWidget(Qt::TopRightCorner), subWindow->maximizedButtonsWidget()); |
1847 | |
1848 | subWindow->showNormal(); |
1849 | QVERIFY(!subWindow->maximizedButtonsWidget()); |
1850 | QVERIFY(!subWindow->maximizedSystemMenuIconWidget()); |
1851 | QVERIFY(!menuBar2->cornerWidget(Qt::TopLeftCorner)); |
1852 | QVERIFY(!menuBar2->cornerWidget(Qt::TopRightCorner)); |
1853 | |
1854 | // Delete and replace. |
1855 | subWindow->showMaximized(); |
1856 | delete menuBar2; |
1857 | auto = new QMenuBar; |
1858 | mainWindow.setMenuBar(menuBar3); |
1859 | QCoreApplication::processEvents(); |
1860 | |
1861 | QVERIFY(!subWindow->maximizedButtonsWidget()); |
1862 | QVERIFY(!subWindow->maximizedSystemMenuIconWidget()); |
1863 | |
1864 | subWindow->showNormal(); |
1865 | QVERIFY(!subWindow->maximizedButtonsWidget()); |
1866 | QVERIFY(!subWindow->maximizedSystemMenuIconWidget()); |
1867 | |
1868 | // Delete. |
1869 | subWindow->showMaximized(); |
1870 | mainWindow.setMenuBar(nullptr); |
1871 | QCoreApplication::processEvents(); |
1872 | QVERIFY(!mainWindow.menuWidget()); |
1873 | |
1874 | QVERIFY(!subWindow->maximizedButtonsWidget()); |
1875 | QVERIFY(!subWindow->maximizedSystemMenuIconWidget()); |
1876 | |
1877 | subWindow->showNormal(); |
1878 | QVERIFY(!subWindow->maximizedButtonsWidget()); |
1879 | QVERIFY(!subWindow->maximizedSystemMenuIconWidget()); |
1880 | delete menuBar1; |
1881 | delete menuBar3; |
1882 | } |
1883 | |
1884 | void tst_QMdiSubWindow::closeOnDoubleClick_data() |
1885 | { |
1886 | QTest::addColumn<int>(name: "actionIndex" ); |
1887 | QTest::addColumn<bool>(name: "expectClosed" ); |
1888 | |
1889 | QTest::newRow(dataTag: "close" ) << 1 << true; |
1890 | QTest::newRow(dataTag: "disabled-restore-action" ) << 0 << false; // QTBUG-48493 |
1891 | } |
1892 | |
1893 | void tst_QMdiSubWindow::closeOnDoubleClick() |
1894 | { |
1895 | QFETCH(int, actionIndex); |
1896 | QFETCH(bool, expectClosed); |
1897 | |
1898 | QMdiArea mdiArea; |
1899 | mdiArea.setWindowTitle(QLatin1String(QTest::currentTestFunction()) |
1900 | + QLatin1Char(' ') + QLatin1String(QTest::currentDataTag())); |
1901 | QPointer<QMdiSubWindow> subWindow = mdiArea.addSubWindow(widget: new QWidget); |
1902 | mdiArea.show(); |
1903 | QVERIFY(QTest::qWaitForWindowExposed(&mdiArea)); |
1904 | |
1905 | subWindow->showSystemMenu(); |
1906 | |
1907 | QPointer<QMenu> ; |
1908 | QTRY_VERIFY( (systemMenu = subWindow->systemMenu()) ); |
1909 | QVERIFY(systemMenu->isVisible()); |
1910 | |
1911 | const QRect actionGeometry = systemMenu->actionGeometry(systemMenu->actions().at(i: actionIndex)); |
1912 | sendMouseDoubleClick(widget: systemMenu, point: actionGeometry.center()); |
1913 | if (QApplication::activePopupWidget() == static_cast<QWidget *>(systemMenu)) |
1914 | systemMenu->hide(); |
1915 | QCoreApplication::processEvents(); |
1916 | QVERIFY(!systemMenu || !systemMenu->isVisible()); |
1917 | QCOMPARE(subWindow.isNull() || !subWindow->isVisible(), expectClosed); |
1918 | } |
1919 | #endif |
1920 | |
1921 | void tst_QMdiSubWindow::setFont() |
1922 | { |
1923 | QSKIP("This test function is unstable in CI, please see QTBUG-22544" ); |
1924 | QMdiArea mdiArea; |
1925 | mdiArea.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
1926 | QMdiSubWindow *subWindow = mdiArea.addSubWindow(widget: new TestPushButton(QLatin1String("test" ))); |
1927 | subWindow->resize(w: 300, h: 100); |
1928 | subWindow->setWindowTitle(QLatin1String("Window title" )); |
1929 | mdiArea.show(); |
1930 | QVERIFY(QTest::qWaitForWindowExposed(&mdiArea)); |
1931 | |
1932 | |
1933 | const QFont originalFont = QApplication::font(className: "QMdiSubWindowTitleBar" ); |
1934 | QStyleOptionTitleBar opt; |
1935 | opt.initFrom(w: subWindow); |
1936 | const int titleBarHeight = subWindow->style()->pixelMetric(metric: QStyle::PM_TitleBarHeight, option: &opt); |
1937 | const QRect titleBarRect = QRect(0, 0, subWindow->width(), titleBarHeight); |
1938 | const QImage originalTitleBar = subWindow->grab(rectangle: titleBarRect).toImage(); |
1939 | |
1940 | QFont newFont(QLatin1String("Helvetica" ), 16); |
1941 | newFont.setBold(true); |
1942 | subWindow->setFont(newFont); |
1943 | QCoreApplication::processEvents(); |
1944 | const QFont &swFont = subWindow->font(); |
1945 | QCOMPARE(swFont.family(), newFont.family()); |
1946 | QCOMPARE(swFont.pointSize(), newFont.pointSize()); |
1947 | QCOMPARE(swFont.weight(), newFont.weight()); |
1948 | QImage newTitleBar = subWindow->grab(rectangle: titleBarRect).toImage(); |
1949 | QVERIFY(newTitleBar != originalTitleBar); |
1950 | |
1951 | subWindow->setFont(originalFont); |
1952 | QCoreApplication::processEvents(); |
1953 | QCOMPARE(subWindow->font(), originalFont); |
1954 | newTitleBar = subWindow->grab(rectangle: titleBarRect).toImage(); |
1955 | QCOMPARE(newTitleBar, originalTitleBar); |
1956 | } |
1957 | |
1958 | void tst_QMdiSubWindow::task_188849() |
1959 | { |
1960 | QMainWindow mainWindow; |
1961 | mainWindow.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
1962 | // Sets a regular QWidget (and NOT a QMenuBar) as the menu bar. |
1963 | mainWindow.setMenuWidget(new QWidget); |
1964 | |
1965 | QMdiArea *mdiArea = new QMdiArea; |
1966 | QMdiSubWindow *subWindow = mdiArea->addSubWindow(widget: new QWidget); |
1967 | mainWindow.setCentralWidget(mdiArea); |
1968 | mainWindow.show(); |
1969 | QVERIFY(QTest::qWaitForWindowExposed(&mainWindow)); |
1970 | |
1971 | // QMdiSubWindow will now try to show its buttons in the menu bar. |
1972 | // Without checking that the menu bar is actually a QMenuBar |
1973 | // and not a regular QWidget, this will crash. |
1974 | subWindow->showMaximized(); |
1975 | } |
1976 | |
1977 | void tst_QMdiSubWindow::mdiArea() |
1978 | { |
1979 | QMdiArea mdiArea; |
1980 | mdiArea.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
1981 | QMdiSubWindow *subWindow = mdiArea.addSubWindow(widget: new QWidget); |
1982 | QCOMPARE(subWindow->mdiArea(), &mdiArea); |
1983 | |
1984 | subWindow->setParent(nullptr); |
1985 | QVERIFY(!subWindow->mdiArea()); |
1986 | |
1987 | // Child of the area's corner widget. |
1988 | mdiArea.setCornerWidget(new QWidget); |
1989 | subWindow->setParent(mdiArea.cornerWidget()); |
1990 | QVERIFY(!subWindow->mdiArea()); |
1991 | |
1992 | // Nested mdi area. |
1993 | QMdiArea *nestedArea = new QMdiArea; |
1994 | mdiArea.addSubWindow(widget: nestedArea); |
1995 | nestedArea->addSubWindow(widget: subWindow); |
1996 | QCOMPARE(subWindow->mdiArea(), nestedArea); |
1997 | nestedArea->setViewport(new QWidget); |
1998 | QCOMPARE(subWindow->mdiArea(), nestedArea); |
1999 | } |
2000 | |
2001 | void tst_QMdiSubWindow::task_182852() |
2002 | { |
2003 | QMdiArea *workspace = new QMdiArea; |
2004 | QMainWindow mainWindow; |
2005 | mainWindow.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
2006 | mainWindow.setCentralWidget(workspace); |
2007 | mainWindow.show(); |
2008 | mainWindow.menuBar()->setVisible(true); |
2009 | QApplication::setActiveWindow(&mainWindow); |
2010 | if (mainWindow.menuBar()->isNativeMenuBar()) |
2011 | return; // The main window's title is not overwritten if we have a native menubar (macOS, Unity etc.) |
2012 | |
2013 | QString originalWindowTitle = QString::fromLatin1(str: "MainWindow - [foo]" ); |
2014 | mainWindow.setWindowTitle(originalWindowTitle); |
2015 | |
2016 | QMdiSubWindow *window = new QMdiSubWindow; |
2017 | |
2018 | QMdiArea *nestedWorkspace = new QMdiArea; // :-) |
2019 | window->setWidget(nestedWorkspace); |
2020 | window->widget()->setWindowTitle(QString::fromLatin1(str: "Window" )); |
2021 | |
2022 | workspace->addSubWindow(widget: window); |
2023 | |
2024 | window->showMaximized(); |
2025 | QCoreApplication::processEvents(); |
2026 | QVERIFY(window->isMaximized()); |
2027 | |
2028 | QCOMPARE(mainWindow.windowTitle(), QString::fromLatin1("%1 - [%2]" ) |
2029 | .arg(originalWindowTitle, window->widget()->windowTitle())); |
2030 | |
2031 | window->showNormal(); |
2032 | QCOMPARE(mainWindow.windowTitle(), originalWindowTitle); |
2033 | |
2034 | window->widget()->setWindowTitle(QString::fromLatin1(str: "foo" )); |
2035 | window->showMaximized(); |
2036 | |
2037 | QCOMPARE(mainWindow.windowTitle(), originalWindowTitle); |
2038 | |
2039 | window->showNormal(); |
2040 | QCOMPARE(mainWindow.windowTitle(), originalWindowTitle); |
2041 | |
2042 | window->widget()->setWindowTitle(QString::fromLatin1(str: "bar" )); |
2043 | window->showMaximized(); |
2044 | |
2045 | QCOMPARE(mainWindow.windowTitle(), QString::fromLatin1("%1 - [%2]" ) |
2046 | .arg(originalWindowTitle, window->widget()->windowTitle())); |
2047 | } |
2048 | |
2049 | void tst_QMdiSubWindow::task_233197() |
2050 | { |
2051 | QMainWindow *mainWindow = new QMainWindow; |
2052 | mainWindow->setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
2053 | mainWindow->setAttribute(Qt::WA_DeleteOnClose); |
2054 | mainWindow->resize(w: 500, h: 200); |
2055 | mainWindow->show(); |
2056 | |
2057 | QMdiArea *mdiArea = new QMdiArea(mainWindow); |
2058 | mdiArea->setOption(option: QMdiArea::DontMaximizeSubWindowOnActivation, on: true); |
2059 | mainWindow->setCentralWidget(mdiArea); |
2060 | |
2061 | QMdiSubWindow *subWindow1 = new QMdiSubWindow(); |
2062 | mdiArea->addSubWindow(widget: subWindow1); |
2063 | subWindow1->showMaximized(); |
2064 | |
2065 | QMdiSubWindow *subWindow2 = new QMdiSubWindow(); |
2066 | mdiArea->addSubWindow(widget: subWindow2); |
2067 | subWindow2->showMaximized(); |
2068 | |
2069 | QMdiSubWindow *subWindow3 = new QMdiSubWindow(); |
2070 | mdiArea->addSubWindow(widget: subWindow3); |
2071 | subWindow3->showMaximized(); |
2072 | |
2073 | QMenuBar * = mainWindow->menuBar(); // force creation of a menubar |
2074 | Q_UNUSED(menuBar); |
2075 | |
2076 | QPushButton *focus1 = new TestPushButton(QLatin1String("Focus 1" ), mainWindow); |
2077 | QObject::connect(sender: focus1, signal: &QAbstractButton::clicked, receiver: subWindow1, |
2078 | slot: QOverload<>::of(ptr: &QWidget::setFocus)); |
2079 | focus1->move(ax: 5, ay: 30); |
2080 | focus1->show(); |
2081 | |
2082 | QPushButton *focus2 = new TestPushButton(QLatin1String("Focus 2" ), mainWindow); |
2083 | QObject::connect(sender: focus2, signal: &QAbstractButton::clicked, receiver: subWindow2, |
2084 | slot: QOverload<>::of(ptr: &QWidget::setFocus)); |
2085 | focus2->move(ax: 5, ay: 60); |
2086 | focus2->show(); |
2087 | |
2088 | QPushButton *close = new TestPushButton(QLatin1String("Close" ), mainWindow); |
2089 | QObject::connect(sender: close, signal: &QAbstractButton::clicked, receiver: mainWindow, slot: &QWidget::close); |
2090 | close->move(ax: 5, ay: 90); |
2091 | close->show(); |
2092 | |
2093 | QTest::qWait(ms: 200); |
2094 | |
2095 | sendMousePress(widget: focus2, point: QPoint()); |
2096 | sendMouseRelease(widget: focus2, point: QPoint()); |
2097 | |
2098 | sendMousePress(widget: focus1, point: QPoint()); |
2099 | sendMouseRelease(widget: focus1, point: QPoint()); |
2100 | |
2101 | sendMousePress(widget: focus2, point: QPoint()); |
2102 | sendMouseRelease(widget: focus2, point: QPoint()); |
2103 | |
2104 | sendMousePress(widget: close, point: QPoint()); |
2105 | sendMouseRelease(widget: close, point: QPoint()); |
2106 | |
2107 | QTest::qWait(ms: 200); |
2108 | } |
2109 | |
2110 | void tst_QMdiSubWindow::task_226929() |
2111 | { |
2112 | QMdiArea mdiArea; |
2113 | mdiArea.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
2114 | mdiArea.show(); |
2115 | QVERIFY(QTest::qWaitForWindowExposed(&mdiArea)); |
2116 | |
2117 | QMdiSubWindow *sub1 = mdiArea.addSubWindow(widget: new QTextEdit); |
2118 | sub1->showMinimized(); |
2119 | |
2120 | QMdiSubWindow *sub2 = mdiArea.addSubWindow(widget: new QTextEdit); |
2121 | sub2->showMaximized(); |
2122 | |
2123 | QTest::qWait(ms: 100); |
2124 | |
2125 | // Do not assert. |
2126 | // This window will now be activated and automatically maximized |
2127 | // (if not QMdiArea::DontMaximizeSubWindowOnActionvation is set). |
2128 | sub1->showNormal(); |
2129 | QVERIFY(sub1->isMaximized()); |
2130 | } |
2131 | |
2132 | void tst_QMdiSubWindow::styleChange() |
2133 | { |
2134 | QMdiArea mdiArea; |
2135 | mdiArea.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
2136 | mdiArea.show(); |
2137 | QVERIFY(QTest::qWaitForWindowExposed(&mdiArea)); |
2138 | |
2139 | QMdiSubWindow *sub1 = mdiArea.addSubWindow(widget: new QTextEdit); |
2140 | sub1->showMaximized(); |
2141 | |
2142 | QMdiSubWindow *sub2 = mdiArea.addSubWindow(widget: new QTextEdit); |
2143 | sub2->showMinimized(); |
2144 | |
2145 | mdiArea.setActiveSubWindow(sub1); |
2146 | |
2147 | QTest::qWait(ms: 100); |
2148 | |
2149 | qRegisterMetaType<QMdiSubWindow *>(); |
2150 | QSignalSpy spy(&mdiArea, &QMdiArea::subWindowActivated); |
2151 | QVERIFY(spy.isValid()); |
2152 | |
2153 | QEvent event(QEvent::StyleChange); |
2154 | QApplication::sendEvent(receiver: sub1, event: &event); |
2155 | QApplication::sendEvent(receiver: sub2, event: &event); |
2156 | |
2157 | // subWindowActivated should NOT be activated by a style change, |
2158 | // even if internally QMdiSubWindow un-minimizes subwindows temporarily. |
2159 | QCOMPARE(spy.count(), 0); |
2160 | } |
2161 | |
2162 | void tst_QMdiSubWindow::testFullScreenState() |
2163 | { |
2164 | QMdiArea mdiArea; |
2165 | mdiArea.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
2166 | mdiArea.showMaximized(); |
2167 | |
2168 | QMdiSubWindow *subWindow = mdiArea.addSubWindow(widget: new QWidget); |
2169 | subWindow->setGeometry(ax: 0, ay: 0, aw: 300, ah: 300); |
2170 | subWindow->showFullScreen(); // QMdiSubWindow does not support the fullscreen state. This call |
2171 | // should be equivalent to setVisible(true) (and not showNormal()) |
2172 | QVERIFY(QTest::qWaitForWindowExposed(&mdiArea)); |
2173 | #ifdef Q_OS_WINRT |
2174 | QEXPECT_FAIL("" , "Windows are maximized per default on WinRt " , Abort); |
2175 | #endif |
2176 | QCOMPARE(subWindow->size(), QSize(300, 300)); |
2177 | } |
2178 | |
2179 | void tst_QMdiSubWindow::testRemoveBaseWidget() |
2180 | { |
2181 | QMdiArea mdiArea; |
2182 | mdiArea.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
2183 | mdiArea.show(); |
2184 | |
2185 | QWidget *widget1 = new QWidget; |
2186 | mdiArea.addSubWindow(widget: widget1); |
2187 | |
2188 | QWidget *widget2 = new QWidget; |
2189 | mdiArea.addSubWindow(widget: widget2); |
2190 | |
2191 | mdiArea.removeSubWindow(widget: widget1); |
2192 | QVERIFY(!widget1->parent()); |
2193 | |
2194 | widget2->setParent(widget1); |
2195 | mdiArea.removeSubWindow(widget: widget2); |
2196 | QCOMPARE(widget2->parent(), widget1); |
2197 | |
2198 | delete widget1; |
2199 | } |
2200 | |
2201 | QTEST_MAIN(tst_QMdiSubWindow) |
2202 | #include "tst_qmdisubwindow.moc" |
2203 | |
2204 | |