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
54QT_BEGIN_NAMESPACE
55extern bool qt_tab_all_widgets();
56QT_END_NAMESPACE
57
58static 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
67static 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 ---
108static 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
114static 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
120static 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
127static 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
135static const Qt::WindowFlags StandardWindowFlags
136 = Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint;
137static const Qt::WindowFlags DialogWindowFlags
138 = Qt::WindowTitleHint | Qt::WindowSystemMenuHint;
139
140class LayoutDirectionGuard
141{
142public:
143 Q_DISABLE_COPY(LayoutDirectionGuard);
144
145 LayoutDirectionGuard() = default;
146 ~LayoutDirectionGuard() { QApplication::setLayoutDirection(Qt::LeftToRight); }
147};
148
149Q_DECLARE_METATYPE(Qt::WindowState);
150Q_DECLARE_METATYPE(Qt::WindowStates);
151Q_DECLARE_METATYPE(Qt::WindowType);
152Q_DECLARE_METATYPE(Qt::WindowFlags);
153Q_DECLARE_METATYPE(QMdiSubWindow*);
154
155class TestPushButton : public QPushButton
156{
157public:
158 TestPushButton(const QString &title, QWidget *parent = nullptr)
159 : QPushButton(title, parent)
160 {
161 }
162
163protected:
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
171class tst_QMdiSubWindow : public QObject
172{
173 Q_OBJECT
174private 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
228void 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
239void tst_QMdiSubWindow::cleanup()
240{
241 QVERIFY(QApplication::topLevelWidgets().isEmpty());
242}
243
244void 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
256void 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
279void 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
300void 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
339void 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
348void 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
394void 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 nativeMenuBar = 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
507void 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
520void 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
582void 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
663void 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
672void 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
696class EventSpy : public QObject
697{
698public:
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
709protected:
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
717private:
718 const QEvent::Type eventToSpy;
719 int _count = 0;
720};
721
722#ifndef QT_NO_CURSOR
723void 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
735void 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
849void 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
938void 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
963void 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
1020void tst_QMdiSubWindow::setSystemMenu()
1021{
1022 QMdiSubWindow *subWindow = new QMdiSubWindow;
1023 subWindow->resize(w: 200, h: 50);
1024 QPointer<QMenu>systemMenu = 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 globalPopupPos;
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 *menuLabel = 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
1130void 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
1235class MultiWidget : public QWidget {
1236public:
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
1249void 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
1280void 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
1332class MyTextEdit : public QTextEdit
1333{
1334public:
1335 using QTextEdit::QTextEdit;
1336 void setAcceptClose(bool enable = true) { acceptClose = enable; }
1337protected:
1338 void closeEvent(QCloseEvent *closeEvent) override
1339 {
1340 if (!acceptClose)
1341 closeEvent->ignore();
1342 }
1343
1344private:
1345 bool acceptClose = false;
1346};
1347
1348void 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.
1384void 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
1443void 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
1455void 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)
1514void 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
1533void 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 *menuBar = 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
1641void 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
1679void 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
1731void 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
1752void 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)
1811void tst_QMdiSubWindow::replaceMenuBarWhileMaximized()
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 *menuBar1 = 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 menuBar2 = 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 menuBar3 = 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
1884void 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
1893void 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> systemMenu;
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
1921void 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
1958void 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
1977void 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
2001void 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
2049void 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 *menuBar = 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
2110void 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
2132void 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
2162void 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
2179void 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
2201QTEST_MAIN(tst_QMdiSubWindow)
2202#include "tst_qmdisubwindow.moc"
2203
2204

source code of qtbase/tests/auto/widgets/widgets/qmdisubwindow/tst_qmdisubwindow.cpp