1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtTest/QtTest>
31
32#include <QMdiSubWindow>
33#include <QMdiArea>
34
35#include <QApplication>
36#include <QMainWindow>
37#include <QMenuBar>
38#include <QPushButton>
39#include <QStyle>
40#include <QStyleOption>
41#include <QVBoxLayout>
42#include <QLineEdit>
43#include <QDesktopWidget>
44#include <QDockWidget>
45#include <QScrollBar>
46#include <QTextEdit>
47#ifndef QT_NO_OPENGL
48#include <QtOpenGL>
49#include <QOpenGLContext>
50#endif
51#include <QStyleHints>
52
53static const Qt::WindowFlags DefaultWindowFlags
54 = Qt::SubWindow | Qt::WindowSystemMenuHint
55 | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint;
56
57Q_DECLARE_METATYPE(QMdiArea::WindowOrder)
58Q_DECLARE_METATYPE(QTabWidget::TabPosition)
59
60static bool tabBetweenSubWindowsIn(QMdiArea *mdiArea, int tabCount = -1, bool reverse = false)
61{
62 if (!mdiArea) {
63 qWarning(msg: "Null pointer to mdi area");
64 return false;
65 }
66
67 QList<QMdiSubWindow *> subWindows = mdiArea->subWindowList();
68 const bool walkThrough = tabCount == -1;
69
70 if (walkThrough) {
71 QMdiSubWindow *active = reverse ? subWindows.front() : subWindows.back();
72 mdiArea->setActiveSubWindow(active);
73 if (mdiArea->activeSubWindow() != active) {
74 qWarning(msg: "Failed to set active sub window");
75 return false;
76 }
77 tabCount = subWindows.size();
78 }
79
80 QWidget *focusWidget = qApp->focusWidget();
81 if (!focusWidget) {
82 qWarning(msg: "No focus widget");
83 return false;
84 }
85
86 Qt::KeyboardModifiers modifiers = reverse ? Qt::ShiftModifier : Qt::NoModifier;
87 Qt::Key key;
88#ifdef Q_OS_MAC
89 key = Qt::Key_Meta;
90 modifiers |= Qt::MetaModifier;
91#else
92 key = Qt::Key_Control;
93 modifiers |= Qt::ControlModifier;
94#endif
95
96 QTest::keyPress(widget: focusWidget, key, modifier: modifiers);
97 for (int i = 0; i < tabCount; ++i) {
98 QTest::keyPress(widget: focusWidget, key: reverse ? Qt::Key_Backtab : Qt::Key_Tab, modifier: modifiers);
99 if (tabCount > 1)
100 QTest::qWait(ms: 500);
101 if (walkThrough) {
102 QRubberBand *rubberBand = mdiArea->findChild<QRubberBand *>();
103 if (!rubberBand) {
104 qWarning(msg: "No rubber band");
105 return false;
106 }
107 QMdiSubWindow *subWindow = subWindows.at(i: reverse ? subWindows.size() -1 - i : i);
108 if (rubberBand->geometry() != subWindow->geometry()) {
109 qWarning().nospace() << "Rubber band of tab " << i << " has different geometry: "
110 << rubberBand->geometry() << " (sub window: " << subWindow->geometry() << ").";
111 return false;
112 }
113 }
114 qApp->processEvents();
115 }
116 QTest::keyRelease(widget: focusWidget, key);
117
118 return true;
119}
120
121static inline QTabBar::Shape tabBarShapeFrom(QTabWidget::TabShape shape, QTabWidget::TabPosition position)
122{
123 const bool rounded = (shape == QTabWidget::Rounded);
124 if (position == QTabWidget::North)
125 return rounded ? QTabBar::RoundedNorth : QTabBar::TriangularNorth;
126 if (position == QTabWidget::South)
127 return rounded ? QTabBar::RoundedSouth : QTabBar::TriangularSouth;
128 if (position == QTabWidget::East)
129 return rounded ? QTabBar::RoundedEast : QTabBar::TriangularEast;
130 if (position == QTabWidget::West)
131 return rounded ? QTabBar::RoundedWest : QTabBar::TriangularWest;
132 return QTabBar::RoundedNorth;
133}
134
135static int cascadedDeltaY(const QMdiArea *area)
136{
137 // Calculate the delta (dx, dy) between two cascaded subwindows.
138 const QWidget *subWindow = area->subWindowList().first();
139 const QStyle *style = subWindow->style();
140 QStyleOptionTitleBar options;
141 options.initFrom(w: subWindow);
142 int titleBarHeight = style->pixelMetric(metric: QStyle::PM_TitleBarHeight, option: &options);
143 // ### Remove this after the QMacStyle has been fixed
144 if (style->inherits(classname: "QMacStyle"))
145 titleBarHeight -= 4;
146 const QFontMetrics fontMetrics = QFontMetrics(QApplication::font(className: "QMdiSubWindowTitleBar"));
147 return qMax(a: titleBarHeight - (titleBarHeight - fontMetrics.height()) / 2, b: 1)
148 + style->pixelMetric(metric: QStyle::PM_FocusFrameVMargin);
149}
150
151enum Arrangement {
152 Tiled,
153 Cascaded
154};
155
156static bool verifyArrangement(QMdiArea *mdiArea, Arrangement arrangement, const QList<int> &expectedIndices)
157{
158 if (!mdiArea || expectedIndices.isEmpty() || mdiArea->subWindowList().isEmpty())
159 return false;
160
161 const QList<QMdiSubWindow *> subWindows = mdiArea->subWindowList();
162
163 switch (arrangement) {
164 case Tiled:
165 {
166 // Calculate the number of rows and columns.
167 const int n = subWindows.count();
168 const int numColumns = qMax(a: qCeil(v: qSqrt(v: qreal(n))), b: 1);
169 const int numRows = qMax(a: (n % numColumns) ? (n / numColumns + 1) : (n / numColumns), b: 1);
170
171 // Ensure that the geometry of all the subwindows are as expected by using
172 // QWidget::childAt starting from the middle of the topleft cell and subsequently
173 // adding rowWidth and rowHeight (going from left to right).
174 const int columnWidth = mdiArea->viewport()->width() / numColumns;
175 const int rowHeight = mdiArea->viewport()->height() / numRows;
176 QPoint subWindowPos(columnWidth / 2, rowHeight / 2);
177 for (int i = 0; i < numRows; ++i) {
178 for (int j = 0; j < numColumns; ++j) {
179 const int index = expectedIndices.at(i: i * numColumns + j);
180 QWidget *actual = mdiArea->viewport()->childAt(p: subWindowPos);
181 QMdiSubWindow *expected = subWindows.at(i: index);
182 if (actual != expected && !expected->isAncestorOf(child: actual))
183 return false;
184 subWindowPos.rx() += columnWidth;
185 }
186 subWindowPos.rx() = columnWidth / 2;
187 subWindowPos.ry() += rowHeight;
188 }
189 break;
190 }
191 case Cascaded:
192 {
193 const int dy = cascadedDeltaY(area: mdiArea);
194 const int dx = 10;
195
196 // Current activation/stacking order.
197 const QList<QMdiSubWindow *> activationOrderList = mdiArea->subWindowList(order: QMdiArea::ActivationHistoryOrder);
198
199 // Ensure that the geometry of all the subwindows are as expected by using
200 // QWidget::childAt with the position of the first one and subsequently adding
201 // dx and dy.
202 QPoint subWindowPos(20, 5);
203 foreach (int expectedIndex, expectedIndices) {
204 QMdiSubWindow *expected = subWindows.at(i: expectedIndex);
205 expected->raise();
206 if (mdiArea->viewport()->childAt(p: subWindowPos) != expected)
207 return false;
208 expected->lower();
209 subWindowPos.rx() += dx;
210 subWindowPos.ry() += dy;
211 }
212
213 // Restore stacking order.
214 foreach (QMdiSubWindow *subWindow, activationOrderList) {
215 mdiArea->setActiveSubWindow(subWindow);
216 qApp->processEvents();
217 }
218 break;
219 }
220 default:
221 return false;
222 }
223 return true;
224}
225
226class tst_QMdiArea : public QObject
227{
228 Q_OBJECT
229public:
230 tst_QMdiArea();
231public slots:
232 void cleanup();
233
234protected slots:
235 void activeChanged(QMdiSubWindow *child);
236
237private slots:
238 // Tests from QWorkspace
239 void subWindowActivated_data();
240 void subWindowActivated();
241 void subWindowActivated2();
242 void subWindowActivatedWithMinimize();
243 void showWindows();
244 void changeWindowTitle();
245 void changeModified();
246 void childSize();
247 void fixedSize();
248 // New tests
249 void minimumSizeHint();
250 void sizeHint();
251 void setActiveSubWindow();
252 void activeSubWindow();
253 void currentSubWindow();
254 void addAndRemoveWindows();
255 void addAndRemoveWindowsWithReparenting();
256 void removeSubWindow_2();
257 void closeWindows();
258 void activateNextAndPreviousWindow();
259 void subWindowList_data();
260 void subWindowList();
261 void setBackground();
262 void setViewport();
263 void tileSubWindows();
264 void cascadeAndTileSubWindows();
265 void resizeMaximizedChildWindows_data();
266 void resizeMaximizedChildWindows();
267 void focusWidgetAfterAddSubWindow();
268 void dontMaximizeSubWindowOnActivation();
269 void delayedPlacement();
270 void iconGeometryInMenuBar();
271 void resizeTimer();
272 void updateScrollBars();
273 void setActivationOrder_data();
274 void setActivationOrder();
275 void tabBetweenSubWindows();
276 void setViewMode();
277 void setTabsClosable();
278 void setTabsMovable();
279 void setTabShape();
280 void setTabPosition_data();
281 void setTabPosition();
282 void nativeSubWindows();
283 void task_209615();
284 void task_236750();
285 void qtbug92240_title_data();
286 void qtbug92240_title();
287
288private:
289 QMdiSubWindow *activeWindow;
290};
291
292tst_QMdiArea::tst_QMdiArea()
293 : activeWindow(0)
294{
295 qRegisterMetaType<QMdiSubWindow *>();
296}
297
298void tst_QMdiArea::cleanup()
299{
300 QVERIFY(QApplication::topLevelWidgets().isEmpty());
301}
302
303// Old QWorkspace tests
304void tst_QMdiArea::activeChanged(QMdiSubWindow *child)
305{
306 activeWindow = child;
307}
308
309void tst_QMdiArea::subWindowActivated_data()
310{
311 // define the test elements we're going to use
312 QTest::addColumn<int>(name: "count");
313
314 // create a first testdata instance and fill it with data
315 QTest::newRow( dataTag: "data0" ) << 0;
316 QTest::newRow( dataTag: "data1" ) << 1;
317 QTest::newRow( dataTag: "data2" ) << 2;
318}
319
320void tst_QMdiArea::subWindowActivated()
321{
322 QMainWindow mw(0) ;
323 mw.menuBar();
324 QMdiArea *workspace = new QMdiArea(&mw);
325 workspace->setObjectName(QLatin1String("testWidget"));
326 mw.setCentralWidget(workspace);
327 QSignalSpy spy(workspace, SIGNAL(subWindowActivated(QMdiSubWindow*)));
328 connect( sender: workspace, SIGNAL(subWindowActivated(QMdiSubWindow*)), receiver: this, SLOT(activeChanged(QMdiSubWindow*)));
329 mw.show();
330 qApp->setActiveWindow(&mw);
331
332 QFETCH( int, count );
333 int i;
334
335 for ( i = 0; i < count; ++i ) {
336 QWidget *widget = new QWidget(workspace, {});
337 widget->setAttribute(Qt::WA_DeleteOnClose);
338 widget->setFocus();
339 workspace->addSubWindow(widget)->show();
340 widget->show();
341 qApp->processEvents();
342 QVERIFY( activeWindow == workspace->activeSubWindow() );
343 QCOMPARE(spy.count(), 1);
344 spy.clear();
345 }
346
347 QList<QMdiSubWindow *> windows = workspace->subWindowList();
348 QCOMPARE( (int)windows.count(), count );
349
350 for ( i = 0; i < count; ++i ) {
351 QMdiSubWindow *window = windows.at(i);
352 window->showMinimized();
353 qApp->processEvents();
354 QVERIFY( activeWindow == workspace->activeSubWindow() );
355 if ( i == 1 )
356 QVERIFY( activeWindow == window );
357 }
358
359 for ( i = 0; i < count; ++i ) {
360 QMdiSubWindow *window = windows.at(i);
361 window->showNormal();
362 qApp->processEvents();
363#ifdef Q_OS_WINRT
364 QEXPECT_FAIL("data2", "Broken on WinRT - QTBUG-68297", Abort);
365#endif
366 QVERIFY( window == activeWindow );
367 QVERIFY( activeWindow == workspace->activeSubWindow() );
368 }
369 spy.clear();
370
371 while (workspace->activeSubWindow() ) {
372 workspace->activeSubWindow()->close();
373 qApp->processEvents();
374 QCOMPARE(activeWindow, workspace->activeSubWindow());
375 QCOMPARE(spy.count(), 1);
376 spy.clear();
377 }
378
379 QVERIFY(!activeWindow);
380 QVERIFY(!workspace->activeSubWindow());
381 QCOMPARE(workspace->subWindowList().count(), 0);
382
383 {
384 workspace->hide();
385 QWidget *widget = new QWidget(workspace);
386 widget->setAttribute(Qt::WA_DeleteOnClose);
387 QMdiSubWindow *window = workspace->addSubWindow(widget);
388 widget->show();
389 QCOMPARE(spy.count(), 0);
390 workspace->show();
391 QCOMPARE(spy.count(), 1);
392 spy.clear();
393 QVERIFY( activeWindow == window );
394 window->close();
395 qApp->processEvents();
396 QCOMPARE(spy.count(), 1);
397 spy.clear();
398 QVERIFY( activeWindow == 0 );
399 }
400
401 {
402 workspace->hide();
403 QWidget *widget = new QWidget(workspace);
404 widget->setAttribute(Qt::WA_DeleteOnClose);
405 QMdiSubWindow *window = workspace->addSubWindow(widget);
406 widget->showMaximized();
407 qApp->sendPostedEvents();
408 QCOMPARE(spy.count(), 0);
409 spy.clear();
410 workspace->show();
411 QCOMPARE(spy.count(), 1);
412 spy.clear();
413 QVERIFY( activeWindow == window );
414 window->close();
415 qApp->processEvents();
416 QCOMPARE(spy.count(), 1);
417 spy.clear();
418 QVERIFY( activeWindow == 0 );
419 }
420
421 {
422 QWidget *widget = new QWidget(workspace);
423 widget->setAttribute(Qt::WA_DeleteOnClose);
424 QMdiSubWindow *window = workspace->addSubWindow(widget);
425 widget->showMinimized();
426 QCOMPARE(spy.count(), 1);
427 spy.clear();
428 QVERIFY( activeWindow == window );
429 QCOMPARE(workspace->activeSubWindow(), window);
430 window->close();
431 qApp->processEvents();
432 QCOMPARE(spy.count(), 1);
433 spy.clear();
434 QVERIFY(!workspace->activeSubWindow());
435 QVERIFY(!activeWindow);
436 }
437}
438
439#ifdef Q_OS_MAC
440#include <Security/AuthSession.h>
441bool macHasAccessToWindowsServer()
442{
443 SecuritySessionId mySession;
444 SessionAttributeBits sessionInfo;
445 SessionGetInfo(callerSecuritySession, &mySession, &sessionInfo);
446 return (sessionInfo & sessionHasGraphicAccess);
447}
448#endif
449
450
451void tst_QMdiArea::subWindowActivated2()
452{
453 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
454 QSKIP("Wayland: This fails. Figure out why.");
455
456 QMdiArea mdiArea;
457 QSignalSpy spy(&mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)));
458 for (int i = 0; i < 5; ++i)
459 mdiArea.addSubWindow(widget: new QWidget);
460 QCOMPARE(spy.count(), 0);
461 mdiArea.show();
462 mdiArea.activateWindow();
463 QVERIFY(QTest::qWaitForWindowActive(&mdiArea));
464
465 QTRY_COMPARE(spy.count(), 5);
466 QCOMPARE(mdiArea.activeSubWindow(), mdiArea.subWindowList().back());
467 spy.clear();
468
469 // Just to make sure another widget is on top wrt. stacking order.
470 // This will typically become the active window if things are broken.
471 QMdiSubWindow *staysOnTopWindow = mdiArea.subWindowList().at(i: 3);
472 staysOnTopWindow->setWindowFlags(Qt::WindowStaysOnTopHint);
473 mdiArea.setActiveSubWindow(staysOnTopWindow);
474 QCOMPARE(spy.count(), 1);
475 QCOMPARE(mdiArea.activeSubWindow(), staysOnTopWindow);
476 spy.clear();
477
478 QMdiSubWindow *activeSubWindow = mdiArea.subWindowList().at(i: 2);
479 mdiArea.setActiveSubWindow(activeSubWindow);
480 QCOMPARE(spy.count(), 1);
481 QCOMPARE(mdiArea.activeSubWindow(), activeSubWindow);
482 spy.clear();
483
484 // Check that we only emit _one_ signal and the active window
485 // is unchanged after hide/show.
486 mdiArea.hide();
487 QTest::qWait(ms: 100);
488 QTRY_COMPARE(spy.count(), 1);
489 QVERIFY(!mdiArea.activeSubWindow());
490 QCOMPARE(mdiArea.currentSubWindow(), activeSubWindow);
491 spy.clear();
492
493 mdiArea.show();
494 mdiArea.activateWindow();
495 QVERIFY(QTest::qWaitForWindowActive(&mdiArea));
496 QTRY_VERIFY(!spy.isEmpty()); // Normally 1, but 2 events might be received on some X11 window managers
497 QVERIFY(mdiArea.currentSubWindow());
498 QTRY_COMPARE(mdiArea.activeSubWindow(), activeSubWindow);
499 spy.clear();
500
501 if (qGuiApp->styleHints()->showIsFullScreen())
502 QSKIP("Platform is auto maximizing, so no showMinimized()");
503
504 // Check that we only emit _one_ signal and the active window
505 // is unchanged after showMinimized/showNormal.
506 mdiArea.showMinimized();
507#if defined (Q_OS_MAC)
508 if (!macHasAccessToWindowsServer())
509 QEXPECT_FAIL("", "showMinimized doesn't really minimize if you don't have access to the server", Abort);
510#endif
511#ifdef Q_OS_MAC
512 QSKIP("QTBUG-25298: This test is unstable on Mac.");
513#endif
514 if (!QGuiApplication::platformName().compare(other: QLatin1String("xcb"), cs: Qt::CaseInsensitive))
515 QSKIP("QTBUG-25298: Unstable on some X11 window managers");
516 QTRY_COMPARE(spy.count(), 1);
517 QVERIFY(!mdiArea.activeSubWindow());
518 QCOMPARE(mdiArea.currentSubWindow(), activeSubWindow);
519 spy.clear();
520
521 // For this test, the QMdiArea widget must be active after minimizing and
522 // showing it again. QMdiArea has no active sub window if it is inactive itself.
523 mdiArea.showNormal();
524 mdiArea.activateWindow();
525 QVERIFY(QTest::qWaitForWindowActive(&mdiArea));
526#ifdef Q_OS_WINRT
527 QEXPECT_FAIL("", "Broken on WinRT - QTBUG-68297", Abort);
528#endif
529 QTRY_COMPARE(spy.count(), 1);
530 QCOMPARE(mdiArea.activeSubWindow(), activeSubWindow);
531 spy.clear();
532}
533
534void tst_QMdiArea::subWindowActivatedWithMinimize()
535{
536 QMainWindow mw(0) ;
537 mw.menuBar();
538 QMdiArea *workspace = new QMdiArea(&mw);
539 workspace->setObjectName(QLatin1String("testWidget"));
540 mw.setCentralWidget(workspace);
541 QSignalSpy spy(workspace, SIGNAL(subWindowActivated(QMdiSubWindow*)));
542 connect( sender: workspace, SIGNAL(subWindowActivated(QMdiSubWindow*)), receiver: this, SLOT(activeChanged(QMdiSubWindow*)) );
543 mw.show();
544 qApp->setActiveWindow(&mw);
545 QWidget *widget = new QWidget(workspace);
546 widget->setAttribute(Qt::WA_DeleteOnClose);
547 QMdiSubWindow *window1 = workspace->addSubWindow(widget);
548 QWidget *widget2 = new QWidget(workspace);
549 widget2->setAttribute(Qt::WA_DeleteOnClose);
550 QMdiSubWindow *window2 = workspace->addSubWindow(widget: widget2);
551
552 widget->showMinimized();
553 QVERIFY( activeWindow == window1 );
554 widget2->showMinimized();
555 QVERIFY( activeWindow == window2 );
556
557 window2->close();
558 qApp->processEvents();
559 QVERIFY( activeWindow == window1 );
560
561 window1->close();
562 qApp->processEvents();
563 QVERIFY(!workspace->activeSubWindow());
564 QVERIFY(!activeWindow);
565
566 QVERIFY( workspace->subWindowList().count() == 0 );
567}
568
569void tst_QMdiArea::showWindows()
570{
571 QMdiArea *ws = new QMdiArea( 0 );
572
573 QWidget *widget = 0;
574 ws->show();
575
576 widget = new QWidget(ws);
577 widget->show();
578 QVERIFY( widget->isVisible() );
579
580 widget = new QWidget(ws);
581 widget->showMaximized();
582 QVERIFY( widget->isMaximized() );
583 widget->showNormal();
584 QVERIFY( !widget->isMaximized() );
585
586 widget = new QWidget(ws);
587 widget->showMinimized();
588 QVERIFY( widget->isMinimized() );
589 widget->showNormal();
590 QVERIFY( !widget->isMinimized() );
591
592 ws->hide();
593
594 widget = new QWidget(ws);
595 ws->show();
596 QVERIFY( widget->isVisible() );
597
598 ws->hide();
599
600 widget = new QWidget(ws);
601 widget->showMaximized();
602 QVERIFY( widget->isMaximized() );
603 ws->show();
604 QVERIFY( widget->isVisible() );
605 QVERIFY( widget->isMaximized() );
606 ws->hide();
607
608 widget = new QWidget(ws);
609 widget->showMinimized();
610 ws->show();
611 QVERIFY( widget->isMinimized() );
612 ws->hide();
613
614 delete ws;
615}
616
617
618//#define USE_SHOW
619
620static inline QString windowTitle(const QString &t, const QString &f)
621{
622 return t + QLatin1String(" - [") + f + QLatin1Char(']');
623}
624
625void tst_QMdiArea::changeWindowTitle()
626{
627 const QString mwc = QString::fromLatin1(str: "MainWindow's Caption");
628 const QString mwc2 = QString::fromLatin1(str: "MainWindow's New Caption");
629 const QString wc = QString::fromLatin1(str: "Widget's Caption");
630 const QString wc2 = QString::fromLatin1(str: "Widget's New Caption");
631
632 QMainWindow *mw = new QMainWindow;
633 mw->setWindowTitle( mwc );
634 QMdiArea *ws = new QMdiArea( mw );
635 mw->setCentralWidget( ws );
636 mw->menuBar()->setNativeMenuBar(false);
637 mw->show();
638 QVERIFY(QTest::qWaitForWindowExposed(mw));
639
640 QWidget *widget = new QWidget( ws );
641 widget->setWindowTitle( wc );
642 ws->addSubWindow(widget);
643
644 QCOMPARE( mw->windowTitle(), mwc );
645
646#ifdef USE_SHOW
647 widget->showMaximized();
648#else
649 widget->setWindowState(Qt::WindowMaximized);
650#endif
651#if !defined(Q_OS_DARWIN)
652 QTRY_COMPARE( mw->windowTitle(), windowTitle(mwc, wc) );
653#endif
654
655 mw->hide();
656 qApp->processEvents();
657 mw->show();
658 QVERIFY(QTest::qWaitForWindowExposed(mw));
659
660#if !defined(Q_OS_DARWIN)
661 QTRY_COMPARE( mw->windowTitle(), windowTitle(mwc, wc) );
662#endif
663
664#ifdef USE_SHOW
665 widget->showNormal();
666#else
667 widget->setWindowState(Qt::WindowNoState);
668#endif
669 qApp->processEvents();
670 QCOMPARE( mw->windowTitle(), mwc );
671
672#ifdef USE_SHOW
673 widget->showMaximized();
674#else
675 widget->setWindowState(Qt::WindowMaximized);
676#endif
677 qApp->processEvents();
678#if !defined(Q_OS_DARWIN)
679 QTRY_COMPARE( mw->windowTitle(), windowTitle(mwc, wc) );
680 widget->setWindowTitle( wc2 );
681 QCOMPARE( mw->windowTitle(), windowTitle(mwc, wc2) );
682 mw->setWindowTitle( mwc2 );
683 QCOMPARE( mw->windowTitle(), windowTitle(mwc2, wc2) );
684#endif
685
686 mw->show();
687 qApp->setActiveWindow(mw);
688
689#ifdef USE_SHOW
690 mw->showFullScreen();
691#else
692 mw->setWindowState(Qt::WindowFullScreen);
693#endif
694
695 qApp->processEvents();
696#if !defined(Q_OS_DARWIN)
697 QCOMPARE( mw->windowTitle(), windowTitle(mwc2, wc2) );
698#endif
699#ifdef USE_SHOW
700 widget->showNormal();
701#else
702 widget->setWindowState(Qt::WindowNoState);
703#endif
704 qApp->processEvents();
705#if defined(Q_OS_DARWIN)
706 QCOMPARE(mw->windowTitle(), mwc);
707#else
708 QCOMPARE( mw->windowTitle(), mwc2 );
709#endif
710
711#ifdef USE_SHOW
712 widget->showMaximized();
713#else
714 widget->setWindowState(Qt::WindowMaximized);
715#endif
716 qApp->processEvents();
717#if !defined(Q_OS_DARWIN)
718 QCOMPARE( mw->windowTitle(), windowTitle(mwc2, wc2) );
719#endif
720
721#ifdef USE_SHOW
722 mw->showNormal();
723#else
724 mw->setWindowState(Qt::WindowNoState);
725#endif
726 qApp->processEvents();
727#ifdef USE_SHOW
728 widget->showNormal();
729#else
730 widget->setWindowState(Qt::WindowNoState);
731#endif
732
733 delete mw;
734}
735
736void tst_QMdiArea::changeModified()
737{
738 const QString mwc = QString::fromLatin1(str: "MainWindow's Caption");
739 const QString wc = QString::fromLatin1(str: "Widget's Caption[*]");
740
741 QMainWindow *mw = new QMainWindow(0);
742 mw->setWindowTitle( mwc );
743 QMdiArea *ws = new QMdiArea( mw );
744 mw->setCentralWidget( ws );
745 mw->menuBar()->setNativeMenuBar(false);
746 mw->show();
747
748 QWidget *widget = new QWidget( ws );
749 widget->setWindowTitle( wc );
750 ws->addSubWindow(widget);
751
752 QCOMPARE( mw->isWindowModified(), false);
753 QCOMPARE( widget->isWindowModified(), false);
754 widget->setWindowState(Qt::WindowMaximized);
755 QCOMPARE( mw->isWindowModified(), false);
756 QCOMPARE( widget->isWindowModified(), false);
757
758 widget->setWindowState(Qt::WindowNoState);
759 QCOMPARE( mw->isWindowModified(), false);
760 QCOMPARE( widget->isWindowModified(), false);
761
762 widget->setWindowModified(true);
763 QCOMPARE( mw->isWindowModified(), false);
764 QCOMPARE( widget->isWindowModified(), true);
765 widget->setWindowState(Qt::WindowMaximized);
766#if !defined(Q_OS_DARWIN)
767 QCOMPARE( mw->isWindowModified(), true);
768#endif
769 QCOMPARE( widget->isWindowModified(), true);
770
771 widget->setWindowState(Qt::WindowNoState);
772 QCOMPARE( mw->isWindowModified(), false);
773 QCOMPARE( widget->isWindowModified(), true);
774
775 widget->setWindowState(Qt::WindowMaximized);
776#if !defined(Q_OS_DARWIN)
777 QCOMPARE( mw->isWindowModified(), true);
778#endif
779 QCOMPARE( widget->isWindowModified(), true);
780
781 widget->setWindowModified(false);
782 QCOMPARE( mw->isWindowModified(), false);
783 QCOMPARE( widget->isWindowModified(), false);
784
785 widget->setWindowModified(true);
786#if !defined(Q_OS_DARWIN)
787 QCOMPARE( mw->isWindowModified(), true);
788#endif
789 QCOMPARE( widget->isWindowModified(), true);
790
791 widget->setWindowState(Qt::WindowNoState);
792 QCOMPARE( mw->isWindowModified(), false);
793 QCOMPARE( widget->isWindowModified(), true);
794
795 delete mw;
796}
797
798class MyChild : public QWidget
799{
800public:
801 MyChild(QWidget *parent = 0) : QWidget(parent) {}
802 QSize sizeHint() const { return QSize(234, 123); }
803};
804
805void tst_QMdiArea::childSize()
806{
807 QMdiArea ws;
808
809 MyChild *child = new MyChild(&ws);
810 child->show();
811 QCOMPARE(child->size(), child->sizeHint());
812 delete child;
813
814 child = new MyChild(&ws);
815 child->setFixedSize(w: 200, h: 200);
816 child->show();
817 QCOMPARE(child->size(), child->minimumSize());
818 delete child;
819
820 child = new MyChild(&ws);
821 child->resize(w: 150, h: 150);
822 child->show();
823 QCOMPARE(child->size(), QSize(150,150));
824 delete child;
825}
826
827void tst_QMdiArea::fixedSize()
828{
829 QMdiArea *ws = new QMdiArea;
830 int i;
831
832 ws->resize(w: 500, h: 500);
833// ws->show();
834
835 QSize fixed(300, 300);
836 for (i = 0; i < 4; ++i) {
837 QWidget *child = new QWidget(ws);
838 child->setFixedSize(fixed);
839 child->show();
840 }
841
842 QList<QMdiSubWindow *> windows = ws->subWindowList();
843 for (i = 0; i < (int)windows.count(); ++i) {
844 QMdiSubWindow *child = windows.at(i);
845 QCOMPARE(child->size(), fixed);
846 }
847
848 ws->cascadeSubWindows();
849 ws->resize(w: 800, h: 800);
850 for (i = 0; i < (int)windows.count(); ++i) {
851 QMdiSubWindow *child = windows.at(i);
852 QCOMPARE(child->size(), fixed);
853 }
854 ws->resize(w: 500, h: 500);
855
856 ws->tileSubWindows();
857 ws->resize(w: 800, h: 800);
858 for (i = 0; i < (int)windows.count(); ++i) {
859 QMdiSubWindow *child = windows.at(i);
860 QCOMPARE(child->size(), fixed);
861 }
862 ws->resize(w: 500, h: 500);
863
864 for (i = 0; i < (int)windows.count(); ++i) {
865 QMdiSubWindow *child = windows.at(i);
866 delete child;
867 }
868
869 delete ws;
870}
871
872class LargeWidget : public QWidget
873{
874public:
875 LargeWidget(QWidget *parent = 0) : QWidget(parent) {}
876 QSize sizeHint() const { return QSize(1280, 1024); }
877 QSize minimumSizeHint() const { return QSize(300, 300); }
878};
879
880// New tests
881void tst_QMdiArea::minimumSizeHint()
882{
883 QMdiArea workspace;
884 workspace.show();
885 QSize expectedSize(workspace.style()->pixelMetric(metric: QStyle::PM_MdiSubWindowMinimizedWidth),
886 workspace.style()->pixelMetric(metric: QStyle::PM_TitleBarHeight));
887 qApp->processEvents();
888 QAbstractScrollArea dummyScrollArea;
889 dummyScrollArea.setFrameStyle(QFrame::NoFrame);
890 expectedSize = expectedSize.expandedTo(otherSize: dummyScrollArea.minimumSizeHint());
891 QCOMPARE(workspace.minimumSizeHint(), expectedSize.expandedTo(qApp->globalStrut()));
892
893 QWidget *window = workspace.addSubWindow(widget: new QWidget);
894 qApp->processEvents();
895 window->show();
896 QCOMPARE(workspace.minimumSizeHint(), expectedSize.expandedTo(window->minimumSizeHint()));
897
898 QMdiSubWindow *subWindow = workspace.addSubWindow(widget: new LargeWidget);
899 subWindow->show();
900 QCOMPARE(workspace.minimumSizeHint(), expectedSize.expandedTo(subWindow->minimumSizeHint()));
901
902 workspace.setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
903 workspace.setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
904 QCOMPARE(workspace.minimumSizeHint(), expectedSize);
905}
906
907void tst_QMdiArea::sizeHint()
908{
909 QMdiArea workspace;
910 workspace.show();
911 QSize desktopSize = QApplication::desktop()->size();
912 QSize expectedSize(desktopSize.width() * 2/3, desktopSize.height() * 2/3);
913 QCOMPARE(workspace.sizeHint(), expectedSize.expandedTo(qApp->globalStrut()));
914
915 QWidget *window = workspace.addSubWindow(widget: new QWidget);
916 qApp->processEvents();
917 window->show();
918 QCOMPARE(workspace.sizeHint(), expectedSize.expandedTo(window->sizeHint()));
919
920 QMdiSubWindow *nested = workspace.addSubWindow(widget: new QMdiArea);
921 expectedSize = QSize(desktopSize.width() * 2/6, desktopSize.height() * 2/6);
922 QCOMPARE(nested->widget()->sizeHint(), expectedSize);
923}
924
925void tst_QMdiArea::setActiveSubWindow()
926{
927 QMdiArea workspace;
928 workspace.show();
929
930 QSignalSpy spy(&workspace, SIGNAL(subWindowActivated(QMdiSubWindow*)));
931 connect(sender: &workspace, SIGNAL(subWindowActivated(QMdiSubWindow*)), receiver: this, SLOT(activeChanged(QMdiSubWindow*)));
932 qApp->setActiveWindow(&workspace);
933
934 // Activate hidden windows
935 const int windowCount = 10;
936 QMdiSubWindow *windows[windowCount];
937 for (int i = 0; i < windowCount; ++i) {
938 windows[i] = qobject_cast<QMdiSubWindow *>(object: workspace.addSubWindow(widget: new QWidget));
939 qApp->processEvents();
940 QVERIFY(windows[i]->isHidden());
941 workspace.setActiveSubWindow(windows[i]);
942 }
943 QCOMPARE(spy.count(), 0);
944 QVERIFY(!activeWindow);
945 spy.clear();
946
947 // Activate visible windows
948 for (int i = 0; i < windowCount; ++i) {
949 windows[i]->show();
950 QVERIFY(!windows[i]->isHidden());
951 workspace.setActiveSubWindow(windows[i]);
952 qApp->processEvents();
953 QCOMPARE(spy.count(), 1);
954 QCOMPARE(activeWindow, windows[i]);
955 spy.clear();
956 }
957
958 // Deactivate active window
959 QCOMPARE(workspace.activeSubWindow(), windows[windowCount - 1]);
960 workspace.setActiveSubWindow(0);
961 QCOMPARE(spy.count(), 1);
962 QVERIFY(!activeWindow);
963 QVERIFY(!workspace.activeSubWindow());
964
965 // Activate widget which is not child of any window inside workspace
966 QMdiSubWindow fakeWindow;
967 QTest::ignoreMessage(type: QtWarningMsg, message: "QMdiArea::setActiveSubWindow: window is not inside workspace");
968 workspace.setActiveSubWindow(&fakeWindow);
969
970}
971
972void tst_QMdiArea::activeSubWindow()
973{
974 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
975 QSKIP("Wayland: This fails. Figure out why.");
976
977 QMainWindow mainWindow;
978
979 QMdiArea *mdiArea = new QMdiArea;
980 QLineEdit *subWindowLineEdit = new QLineEdit;
981 QMdiSubWindow *subWindow = mdiArea->addSubWindow(widget: subWindowLineEdit);
982 mainWindow.setCentralWidget(mdiArea);
983
984 QDockWidget *dockWidget = new QDockWidget(QLatin1String("Dock Widget"), &mainWindow);
985 dockWidget->setAllowedAreas(Qt::LeftDockWidgetArea);
986 QLineEdit *dockWidgetLineEdit = new QLineEdit;
987 dockWidget->setWidget(dockWidgetLineEdit);
988 mainWindow.addDockWidget(area: Qt::LeftDockWidgetArea, dockwidget: dockWidget);
989
990 mainWindow.show();
991 qApp->setActiveWindow(&mainWindow);
992 QVERIFY(QTest::qWaitForWindowActive(&mainWindow));
993 QCOMPARE(mdiArea->activeSubWindow(), subWindow);
994 QCOMPARE(qApp->focusWidget(), (QWidget *)subWindowLineEdit);
995
996 dockWidgetLineEdit->setFocus();
997 QCOMPARE(qApp->focusWidget(), (QWidget *)dockWidgetLineEdit);
998 QCOMPARE(mdiArea->activeSubWindow(), subWindow);
999
1000 QEvent deactivateEvent(QEvent::WindowDeactivate);
1001 qApp->sendEvent(receiver: subWindow, event: &deactivateEvent);
1002 QVERIFY(!mdiArea->activeSubWindow());
1003 QCOMPARE(qApp->focusWidget(), (QWidget *)dockWidgetLineEdit);
1004
1005 QEvent activateEvent(QEvent::WindowActivate);
1006 qApp->sendEvent(receiver: subWindow, event: &activateEvent);
1007 QCOMPARE(mdiArea->activeSubWindow(), subWindow);
1008 QCOMPARE(qApp->focusWidget(), (QWidget *)subWindowLineEdit);
1009
1010 QLineEdit dummyTopLevel;
1011 dummyTopLevel.show();
1012 QVERIFY(QTest::qWaitForWindowExposed(&dummyTopLevel));
1013
1014 qApp->setActiveWindow(&dummyTopLevel);
1015 QCOMPARE(mdiArea->activeSubWindow(), subWindow);
1016
1017 qApp->setActiveWindow(&mainWindow);
1018 QCOMPARE(mdiArea->activeSubWindow(), subWindow);
1019
1020 //task 202657
1021 dockWidgetLineEdit->setFocus();
1022 qApp->setActiveWindow(&mainWindow);
1023 QVERIFY(dockWidgetLineEdit->hasFocus());
1024}
1025
1026void tst_QMdiArea::currentSubWindow()
1027{
1028 QMdiArea mdiArea;
1029 mdiArea.show();
1030 QVERIFY(QTest::qWaitForWindowExposed(&mdiArea));
1031
1032 for (int i = 0; i < 5; ++i)
1033 mdiArea.addSubWindow(widget: new QLineEdit)->show();
1034
1035 qApp->setActiveWindow(&mdiArea);
1036 QCOMPARE(qApp->activeWindow(), (QWidget *)&mdiArea);
1037
1038 // Check that the last added window is the active and the current.
1039 QMdiSubWindow *active = mdiArea.activeSubWindow();
1040 QVERIFY(active);
1041 QCOMPARE(mdiArea.subWindowList().back(), active);
1042 QCOMPARE(mdiArea.currentSubWindow(), active);
1043
1044 QLineEdit dummyTopLevel;
1045 dummyTopLevel.show();
1046 QVERIFY(QTest::qWaitForWindowExposed(&dummyTopLevel));
1047
1048 // Move focus to another top-level and check that we still
1049 // have an active window.
1050 qApp->setActiveWindow(&dummyTopLevel);
1051 QCOMPARE(qApp->activeWindow(), (QWidget *)&dummyTopLevel);
1052 QVERIFY(mdiArea.activeSubWindow());
1053
1054 delete active;
1055 active = 0;
1056
1057 // We just deleted the current sub-window -> current should then
1058 // be the next in list (which in this case is the first sub-window).
1059 QVERIFY(mdiArea.currentSubWindow());
1060 QCOMPARE(mdiArea.currentSubWindow(), mdiArea.subWindowList().front());
1061
1062 // Activate mdi area and check that active == current.
1063 qApp->setActiveWindow(&mdiArea);
1064 active = mdiArea.activeSubWindow();
1065 QVERIFY(active);
1066 QCOMPARE(mdiArea.activeSubWindow(), mdiArea.subWindowList().front());
1067
1068 active->hide();
1069 QCOMPARE(mdiArea.activeSubWindow(), active);
1070 QCOMPARE(mdiArea.currentSubWindow(), active);
1071
1072 qApp->setActiveWindow(&dummyTopLevel);
1073 QVERIFY(mdiArea.activeSubWindow());
1074 QCOMPARE(mdiArea.currentSubWindow(), active);
1075
1076 qApp->setActiveWindow(&mdiArea);
1077 active->show();
1078 QCOMPARE(mdiArea.activeSubWindow(), active);
1079
1080 mdiArea.setActiveSubWindow(0);
1081 QVERIFY(!mdiArea.activeSubWindow());
1082 QVERIFY(!mdiArea.currentSubWindow());
1083
1084 mdiArea.setActiveSubWindow(active);
1085 QCOMPARE(mdiArea.activeSubWindow(), active);
1086 QEvent windowDeactivate(QEvent::WindowDeactivate);
1087 qApp->sendEvent(receiver: active, event: &windowDeactivate);
1088 QVERIFY(!mdiArea.activeSubWindow());
1089 QVERIFY(!mdiArea.currentSubWindow());
1090
1091 QEvent windowActivate(QEvent::WindowActivate);
1092 qApp->sendEvent(receiver: active, event: &windowActivate);
1093 QVERIFY(mdiArea.activeSubWindow());
1094 QVERIFY(mdiArea.currentSubWindow());
1095}
1096
1097void tst_QMdiArea::addAndRemoveWindows()
1098{
1099 QWidget topLevel;
1100 QMdiArea workspace(&topLevel);
1101 workspace.resize(w: 800, h: 600);
1102 topLevel.show();
1103 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
1104
1105 { // addSubWindow with large widget
1106 QCOMPARE(workspace.subWindowList().count(), 0);
1107 QWidget *window = workspace.addSubWindow(widget: new LargeWidget);
1108 QVERIFY(window);
1109 qApp->processEvents();
1110 QCOMPARE(workspace.subWindowList().count(), 1);
1111 QCOMPARE(window->windowFlags(), DefaultWindowFlags);
1112 QCOMPARE(window->size(), workspace.viewport()->size());
1113 }
1114
1115 { // addSubWindow, minimumSize set.
1116 QMdiSubWindow *window = new QMdiSubWindow;
1117 window->setMinimumSize(minw: 900, minh: 900);
1118 workspace.addSubWindow(widget: window);
1119 QVERIFY(window);
1120 qApp->processEvents();
1121 QCOMPARE(workspace.subWindowList().count(), 2);
1122 QCOMPARE(window->windowFlags(), DefaultWindowFlags);
1123 QCOMPARE(window->size(), window->minimumSize());
1124 }
1125
1126 { // addSubWindow, resized
1127 QMdiSubWindow *window = new QMdiSubWindow;
1128 window->setWidget(new QWidget);
1129 window->resize(w: 1500, h: 1500);
1130 workspace.addSubWindow(widget: window);
1131 QVERIFY(window);
1132 qApp->processEvents();
1133 QCOMPARE(workspace.subWindowList().count(), 3);
1134 QCOMPARE(window->windowFlags(), DefaultWindowFlags);
1135 QCOMPARE(window->size(), QSize(1500, 1500));
1136 }
1137
1138 { // addSubWindow with 0 pointer
1139 QTest::ignoreMessage(type: QtWarningMsg, message: "QMdiArea::addSubWindow: null pointer to widget");
1140 QWidget *window = workspace.addSubWindow(widget: 0);
1141 QVERIFY(!window);
1142 QCOMPARE(workspace.subWindowList().count(), 3);
1143 }
1144
1145 { // addChildWindow
1146 QMdiSubWindow *window = new QMdiSubWindow;
1147 workspace.addSubWindow(widget: window);
1148 qApp->processEvents();
1149 QCOMPARE(window->windowFlags(), DefaultWindowFlags);
1150 window->setWidget(new QWidget);
1151 QCOMPARE(workspace.subWindowList().count(), 4);
1152 QTest::ignoreMessage(type: QtWarningMsg, message: "QMdiArea::addSubWindow: window is already added");
1153 workspace.addSubWindow(widget: window);
1154 }
1155
1156 { // addChildWindow with 0 pointer
1157 QTest::ignoreMessage(type: QtWarningMsg, message: "QMdiArea::addSubWindow: null pointer to widget");
1158 workspace.addSubWindow(widget: 0);
1159 QCOMPARE(workspace.subWindowList().count(), 4);
1160 }
1161
1162 // removeSubWindow
1163 foreach (QWidget *window, workspace.subWindowList()) {
1164 workspace.removeSubWindow(widget: window);
1165 delete window;
1166 }
1167 QCOMPARE(workspace.subWindowList().count(), 0);
1168
1169 // removeSubWindow with 0 pointer
1170 QTest::ignoreMessage(type: QtWarningMsg, message: "QMdiArea::removeSubWindow: null pointer to widget");
1171 workspace.removeSubWindow(widget: 0);
1172
1173 workspace.addSubWindow(widget: new QPushButton(QLatin1String("Dummy to make workspace non-empty")));
1174 qApp->processEvents();
1175 QCOMPARE(workspace.subWindowList().count(), 1);
1176
1177 // removeSubWindow with window not inside workspace
1178 QTest::ignoreMessage(type: QtWarningMsg,message: "QMdiArea::removeSubWindow: window is not inside workspace");
1179 QMdiSubWindow *fakeWindow = new QMdiSubWindow;
1180 workspace.removeSubWindow(widget: fakeWindow);
1181 delete fakeWindow;
1182
1183 // Check that newly added windows don't occupy maximized windows'
1184 // restore space.
1185 workspace.closeAllSubWindows();
1186 workspace.setOption(option: QMdiArea::DontMaximizeSubWindowOnActivation);
1187 workspace.show();
1188 QMdiSubWindow *window1 = workspace.addSubWindow(widget: new QWidget);
1189 window1->show();
1190 const QRect window1RestoreGeometry = window1->geometry();
1191 QCOMPARE(window1RestoreGeometry.topLeft(), QPoint(0, 0));
1192
1193 window1->showMinimized();
1194
1195 // Occupy space.
1196 QMdiSubWindow *window2 = workspace.addSubWindow(widget: new QWidget);
1197 window2->show();
1198 const QRect window2RestoreGeometry = window2->geometry();
1199 QCOMPARE(window2RestoreGeometry.topLeft(), QPoint(0, 0));
1200
1201 window2->showMaximized();
1202
1203 // Don't occupy space.
1204 QMdiSubWindow *window3 = workspace.addSubWindow(widget: new QWidget);
1205 window3->show();
1206#ifdef Q_OS_WINRT
1207 QEXPECT_FAIL("", "Windows are maximized by default on WinRT", Abort);
1208#endif
1209 QCOMPARE(window3->geometry().topLeft(), QPoint(window2RestoreGeometry.right() + 1, 0));
1210}
1211
1212void tst_QMdiArea::addAndRemoveWindowsWithReparenting()
1213{
1214 QMdiArea workspace;
1215 QMdiSubWindow window(&workspace);
1216 QCOMPARE(window.windowFlags(), DefaultWindowFlags);
1217
1218 // 0 because the window list contains widgets and not actual
1219 // windows. Silly, but that's the behavior.
1220 QCOMPARE(workspace.subWindowList().count(), 0);
1221 window.setWidget(new QWidget);
1222 qApp->processEvents();
1223
1224 QCOMPARE(workspace.subWindowList().count(), 1);
1225 window.setParent(0); // Will also reset window flags
1226 QCOMPARE(workspace.subWindowList().count(), 0);
1227 window.setParent(&workspace);
1228 QCOMPARE(workspace.subWindowList().count(), 1);
1229 QCOMPARE(window.windowFlags(), DefaultWindowFlags);
1230
1231 QTest::ignoreMessage(type: QtWarningMsg, message: "QMdiArea::addSubWindow: window is already added");
1232 workspace.addSubWindow(widget: &window);
1233 QCOMPARE(workspace.subWindowList().count(), 1);
1234}
1235
1236class MySubWindow : public QMdiSubWindow
1237{
1238public:
1239 using QObject::receivers;
1240};
1241
1242static int numberOfConnectedSignals(MySubWindow *subWindow)
1243{
1244 if (!subWindow)
1245 return 0;
1246
1247 int numConnectedSignals = 0;
1248 for (int i = 0; i < subWindow->metaObject()->methodCount(); ++i) {
1249 QMetaMethod method = subWindow->metaObject()->method(index: i);
1250 if (method.methodType() == QMetaMethod::Signal) {
1251 QString signature(QLatin1String("2"));
1252 signature += QLatin1String(method.methodSignature().constData());
1253 numConnectedSignals += subWindow->receivers(signal: signature.toLatin1());
1254 }
1255 }
1256 return numConnectedSignals;
1257}
1258
1259void tst_QMdiArea::removeSubWindow_2()
1260{
1261 QMdiArea mdiArea;
1262 MySubWindow *subWindow = new MySubWindow;
1263 QCOMPARE(numberOfConnectedSignals(subWindow), 0);
1264
1265 // Connected to aboutToActivate() and windowStateChanged().
1266 mdiArea.addSubWindow(widget: subWindow);
1267 QVERIFY(numberOfConnectedSignals(subWindow) >= 2);
1268
1269 // Ensure we disconnect from all signals.
1270 mdiArea.removeSubWindow(widget: subWindow);
1271 QCOMPARE(numberOfConnectedSignals(subWindow), 0);
1272
1273 mdiArea.addSubWindow(widget: subWindow);
1274 QVERIFY(numberOfConnectedSignals(subWindow) >= 2);
1275 subWindow->setParent(0);
1276 QScopedPointer<MySubWindow> subWindowGuard(subWindow);
1277 QCOMPARE(numberOfConnectedSignals(subWindow), 0);
1278}
1279
1280void tst_QMdiArea::closeWindows()
1281{
1282 QMdiArea workspace;
1283 workspace.show();
1284 qApp->setActiveWindow(&workspace);
1285
1286 // Close widget
1287 QWidget *widget = new QWidget;
1288 QMdiSubWindow *subWindow = workspace.addSubWindow(widget);
1289 qApp->processEvents();
1290 QCOMPARE(workspace.subWindowList().count(), 1);
1291 subWindow->close();
1292 QCOMPARE(workspace.subWindowList().count(), 0);
1293
1294 // Close window
1295 QWidget *window = workspace.addSubWindow(widget: new QWidget);
1296 qApp->processEvents();
1297 QCOMPARE(workspace.subWindowList().count(), 1);
1298 window->close();
1299 qApp->processEvents();
1300 QCOMPARE(workspace.subWindowList().count(), 0);
1301
1302 const int windowCount = 10;
1303
1304 // Close active window
1305 for (int i = 0; i < windowCount; ++i)
1306 workspace.addSubWindow(widget: new QWidget)->show();
1307 qApp->processEvents();
1308 QCOMPARE(workspace.subWindowList().count(), windowCount);
1309 int activeSubWindowCount = 0;
1310 while (workspace.activeSubWindow()) {
1311 workspace.activeSubWindow()->close();
1312 qApp->processEvents();
1313 ++activeSubWindowCount;
1314 }
1315 QCOMPARE(activeSubWindowCount, windowCount);
1316 QCOMPARE(workspace.subWindowList().count(), 0);
1317
1318 // Close all windows
1319 for (int i = 0; i < windowCount; ++i)
1320 workspace.addSubWindow(widget: new QWidget)->show();
1321 qApp->processEvents();
1322 QCOMPARE(workspace.subWindowList().count(), windowCount);
1323 QSignalSpy spy(&workspace, SIGNAL(subWindowActivated(QMdiSubWindow*)));
1324 connect(sender: &workspace, SIGNAL(subWindowActivated(QMdiSubWindow*)), receiver: this, SLOT(activeChanged(QMdiSubWindow*)));
1325 workspace.closeAllSubWindows();
1326 qApp->processEvents();
1327 QCOMPARE(workspace.subWindowList().count(), 0);
1328 QCOMPARE(spy.count(), 1);
1329 QVERIFY(!activeWindow);
1330}
1331
1332void tst_QMdiArea::activateNextAndPreviousWindow()
1333{
1334 QMdiArea workspace;
1335 workspace.show();
1336 qApp->setActiveWindow(&workspace);
1337
1338 const int windowCount = 10;
1339 QMdiSubWindow *windows[windowCount];
1340 for (int i = 0; i < windowCount; ++i) {
1341 windows[i] = qobject_cast<QMdiSubWindow *>(object: workspace.addSubWindow(widget: new QWidget));
1342 windows[i]->show();
1343 qApp->processEvents();
1344 }
1345
1346 QSignalSpy spy(&workspace, SIGNAL(subWindowActivated(QMdiSubWindow*)));
1347 connect(sender: &workspace, SIGNAL(subWindowActivated(QMdiSubWindow*)), receiver: this, SLOT(activeChanged(QMdiSubWindow*)));
1348
1349 // activateNextSubWindow
1350 for (int i = 0; i < windowCount; ++i) {
1351 workspace.activateNextSubWindow();
1352 qApp->processEvents();
1353 QCOMPARE(workspace.activeSubWindow(), windows[i]);
1354 QCOMPARE(spy.count(), 1);
1355 spy.clear();
1356 }
1357 QVERIFY(activeWindow);
1358 QCOMPARE(workspace.activeSubWindow(), windows[windowCount - 1]);
1359 QCOMPARE(workspace.activeSubWindow(), activeWindow);
1360
1361 // activatePreviousSubWindow
1362 for (int i = windowCount - 2; i >= 0; --i) {
1363 workspace.activatePreviousSubWindow();
1364 qApp->processEvents();
1365 QCOMPARE(workspace.activeSubWindow(), windows[i]);
1366 QCOMPARE(spy.count(), 1);
1367 spy.clear();
1368 if (i % 2 == 0)
1369 windows[i]->hide(); // 10, 8, 6, 4, 2, 0
1370 }
1371 QVERIFY(activeWindow);
1372 QCOMPARE(workspace.activeSubWindow(), windows[0]);
1373 QCOMPARE(workspace.activeSubWindow(), activeWindow);
1374
1375 // activateNextSubWindow with every 2nd window hidden
1376 for (int i = 0; i < windowCount / 2; ++i) {
1377 workspace.activateNextSubWindow(); // 1, 3, 5, 7, 9
1378 QCOMPARE(spy.count(), 1);
1379 spy.clear();
1380 }
1381 QCOMPARE(workspace.activeSubWindow(), windows[windowCount - 1]);
1382
1383 // activatePreviousSubWindow with every 2nd window hidden
1384 for (int i = 0; i < windowCount / 2; ++i) {
1385 workspace.activatePreviousSubWindow(); // 7, 5, 3, 1, 9
1386 QCOMPARE(spy.count(), 1);
1387 spy.clear();
1388 }
1389 QCOMPARE(workspace.activeSubWindow(), windows[windowCount - 1]);
1390
1391 workspace.setActiveSubWindow(0);
1392 QVERIFY(!activeWindow);
1393}
1394
1395void tst_QMdiArea::subWindowList_data()
1396{
1397 QTest::addColumn<QMdiArea::WindowOrder>(name: "windowOrder");
1398 QTest::addColumn<int>(name: "windowCount");
1399 QTest::addColumn<int>(name: "activeSubWindow");
1400 QTest::addColumn<int>(name: "staysOnTop1");
1401 QTest::addColumn<int>(name: "staysOnTop2");
1402
1403 QTest::newRow(dataTag: "CreationOrder") << QMdiArea::CreationOrder << 10 << 4 << 8 << 5;
1404 QTest::newRow(dataTag: "StackingOrder") << QMdiArea::StackingOrder << 10 << 6 << 3 << 9;
1405 QTest::newRow(dataTag: "ActivationHistoryOrder") << QMdiArea::ActivationHistoryOrder << 10 << 7 << 2 << 1;
1406}
1407void tst_QMdiArea::subWindowList()
1408{
1409 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1410 QSKIP("Wayland: This fails. Figure out why.");
1411
1412 QFETCH(QMdiArea::WindowOrder, windowOrder);
1413 QFETCH(int, windowCount);
1414 QFETCH(int, activeSubWindow);
1415 QFETCH(int, staysOnTop1);
1416 QFETCH(int, staysOnTop2);
1417
1418 QMdiArea workspace;
1419 workspace.show();
1420 qApp->setActiveWindow(&workspace);
1421 QVERIFY(QTest::qWaitForWindowActive(&workspace));
1422
1423 QList<QMdiSubWindow *> activationOrder;
1424 QVector<QMdiSubWindow *> windows;
1425 for (int i = 0; i < windowCount; ++i) {
1426 windows.append(t: qobject_cast<QMdiSubWindow *>(object: workspace.addSubWindow(widget: new QWidget)));
1427 windows[i]->show();
1428 activationOrder.append(t: windows[i]);
1429 }
1430
1431 {
1432 QList<QMdiSubWindow *> widgets = workspace.subWindowList(order: windowOrder);
1433 QCOMPARE(widgets.count(), windowCount);
1434 for (int i = 0; i < widgets.count(); ++i)
1435 QCOMPARE(widgets.at(i), windows[i]);
1436 }
1437
1438 windows[staysOnTop1]->setWindowFlags(windows[staysOnTop1]->windowFlags() | Qt::WindowStaysOnTopHint);
1439 workspace.setActiveSubWindow(windows[activeSubWindow]);
1440 QTRY_COMPARE(workspace.activeSubWindow(), windows[activeSubWindow]);
1441 activationOrder.move(from: activationOrder.indexOf(t: windows[activeSubWindow]), to: windowCount - 1);
1442
1443 QList<QMdiSubWindow *> subWindows = workspace.subWindowList(order: windowOrder);
1444 if (windowOrder == QMdiArea::CreationOrder) {
1445 QCOMPARE(subWindows.at(activeSubWindow), windows[activeSubWindow]);
1446 QCOMPARE(subWindows.at(staysOnTop1), windows[staysOnTop1]);
1447 for (int i = 0; i < windowCount; ++i)
1448 QCOMPARE(subWindows.at(i), windows[i]);
1449 return;
1450 }
1451
1452 if (windowOrder == QMdiArea::StackingOrder) {
1453 QCOMPARE(subWindows.at(subWindows.count() - 1), windows[staysOnTop1]);
1454 QCOMPARE(subWindows.at(subWindows.count() - 2), windows[activeSubWindow]);
1455 QCOMPARE(subWindows.count(), windowCount);
1456 } else { // ActivationHistoryOrder
1457 QCOMPARE(subWindows, activationOrder);
1458 }
1459
1460 windows[staysOnTop2]->setWindowFlags(windows[staysOnTop2]->windowFlags() | Qt::WindowStaysOnTopHint);
1461 workspace.setActiveSubWindow(windows[staysOnTop2]);
1462 QTRY_COMPARE(workspace.activeSubWindow(), windows[staysOnTop2]);
1463 activationOrder.move(from: activationOrder.indexOf(t: windows[staysOnTop2]), to: windowCount - 1);
1464
1465 workspace.setActiveSubWindow(windows[activeSubWindow]);
1466 QTRY_COMPARE(workspace.activeSubWindow(), windows[activeSubWindow]);
1467 activationOrder.move(from: activationOrder.indexOf(t: windows[activeSubWindow]), to: windowCount - 1);
1468
1469 QList<QMdiSubWindow *> widgets = workspace.subWindowList(order: windowOrder);
1470 QCOMPARE(widgets.count(), windowCount);
1471 if (windowOrder == QMdiArea::StackingOrder) {
1472#ifdef Q_OS_WINRT
1473 QEXPECT_FAIL("", "Broken on WinRT - QTBUG-68297", Abort);
1474#endif
1475 QCOMPARE(widgets.at(widgets.count() - 1), windows[staysOnTop2]);
1476 QCOMPARE(widgets.at(widgets.count() - 2), windows[staysOnTop1]);
1477 QCOMPARE(widgets.at(widgets.count() - 3), windows[activeSubWindow]);
1478 } else { // ActivationHistory
1479 QCOMPARE(widgets, activationOrder);
1480 }
1481
1482 windows[activeSubWindow]->raise();
1483 windows[staysOnTop2]->lower();
1484
1485 widgets = workspace.subWindowList(order: windowOrder);
1486 if (windowOrder == QMdiArea::StackingOrder) {
1487 QCOMPARE(widgets.at(widgets.count() - 1), windows[activeSubWindow]);
1488 QCOMPARE(widgets.at(widgets.count() - 2), windows[staysOnTop1]);
1489 QCOMPARE(widgets.at(0), windows[staysOnTop2]);
1490 } else { // ActivationHistoryOrder
1491 QCOMPARE(widgets, activationOrder);
1492 }
1493
1494 windows[activeSubWindow]->stackUnder(windows[staysOnTop1]);
1495 windows[staysOnTop2]->raise();
1496
1497 widgets = workspace.subWindowList(order: windowOrder);
1498 if (windowOrder == QMdiArea::StackingOrder) {
1499 QCOMPARE(widgets.at(widgets.count() - 1), windows[staysOnTop2]);
1500 QCOMPARE(widgets.at(widgets.count() - 2), windows[staysOnTop1]);
1501 QCOMPARE(widgets.at(widgets.count() - 3), windows[activeSubWindow]);
1502 } else { // ActivationHistoryOrder
1503 QCOMPARE(widgets, activationOrder);
1504 }
1505
1506 workspace.setActiveSubWindow(windows[staysOnTop1]);
1507 activationOrder.move(from: activationOrder.indexOf(t: windows[staysOnTop1]), to: windowCount - 1);
1508
1509 widgets = workspace.subWindowList(order: windowOrder);
1510 if (windowOrder == QMdiArea::StackingOrder) {
1511 QCOMPARE(widgets.at(widgets.count() - 1), windows[staysOnTop1]);
1512 QCOMPARE(widgets.at(widgets.count() - 2), windows[staysOnTop2]);
1513 QCOMPARE(widgets.at(widgets.count() - 3), windows[activeSubWindow]);
1514 } else { // ActivationHistoryOrder
1515 QCOMPARE(widgets, activationOrder);
1516 }
1517}
1518
1519void tst_QMdiArea::setBackground()
1520{
1521 QMdiArea workspace;
1522 QCOMPARE(workspace.background(), workspace.palette().brush(QPalette::Dark));
1523 workspace.setBackground(QBrush(Qt::green));
1524 QCOMPARE(workspace.background(), QBrush(Qt::green));
1525}
1526
1527void tst_QMdiArea::setViewport()
1528{
1529#ifdef Q_OS_MACOS
1530 QSKIP("Sometimes crashes in the CI, see QTBUG-58520");
1531#endif
1532
1533 QMdiArea workspace;
1534 workspace.show();
1535
1536 QWidget *firstViewport = workspace.viewport();
1537 QVERIFY(firstViewport);
1538
1539 const int windowCount = 10;
1540 for (int i = 0; i < windowCount; ++i) {
1541 QMdiSubWindow *window = workspace.addSubWindow(widget: new QWidget);
1542 window->show();
1543 if (i % 2 == 0) {
1544 window->showMinimized();
1545 QVERIFY(window->isMinimized());
1546 } else {
1547 window->showMaximized();
1548 QVERIFY(window->isMaximized());
1549 }
1550 }
1551
1552 qApp->processEvents();
1553 QList<QMdiSubWindow *> windowsBeforeViewportChange = workspace.subWindowList();
1554 QCOMPARE(windowsBeforeViewportChange.count(), windowCount);
1555
1556 workspace.setViewport(new QWidget);
1557 qApp->processEvents();
1558 QVERIFY(workspace.viewport() != firstViewport);
1559
1560 QList<QMdiSubWindow *> windowsAfterViewportChange = workspace.subWindowList();
1561 QCOMPARE(windowsAfterViewportChange.count(), windowCount);
1562 QCOMPARE(windowsAfterViewportChange, windowsBeforeViewportChange);
1563
1564 // for (int i = 0; i < windowCount; ++i) {
1565 // QMdiSubWindow *window = windowsAfterViewportChange.at(i);
1566 // if (i % 2 == 0)
1567 // QVERIFY(!window->isMinimized());
1568 //else
1569 // QVERIFY(!window->isMaximized());
1570 // }
1571
1572 QTest::ignoreMessage(type: QtWarningMsg, message: "QMdiArea: Deleting the view port is undefined, "
1573 "use setViewport instead.");
1574 delete workspace.viewport();
1575 qApp->processEvents();
1576
1577 QCOMPARE(workspace.subWindowList().count(), 0);
1578 QVERIFY(!workspace.activeSubWindow());
1579}
1580
1581void tst_QMdiArea::tileSubWindows()
1582{
1583 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1584 QSKIP("Wayland: This fails. Figure out why.");
1585
1586 QMdiArea workspace;
1587 workspace.resize(w: 600,h: 480);
1588 workspace.show();
1589 QVERIFY(QTest::qWaitForWindowExposed(&workspace));
1590
1591 const int windowCount = 10;
1592 for (int i = 0; i < windowCount; ++i) {
1593 QMdiSubWindow *subWindow = workspace.addSubWindow(widget: new QWidget);
1594 subWindow->setMinimumSize(minw: 50, minh: 30);
1595 subWindow->show();
1596 }
1597 workspace.tileSubWindows();
1598 workspace.setActiveSubWindow(0);
1599 QCOMPARE(workspace.viewport()->childrenRect(), workspace.viewport()->rect());
1600
1601 QList<QMdiSubWindow *> windows = workspace.subWindowList();
1602 for (int i = 0; i < windowCount; ++i) {
1603 QMdiSubWindow *window = windows.at(i);
1604 for (int j = 0; j < windowCount; ++j) {
1605 if (i == j)
1606 continue;
1607 QVERIFY(!window->geometry().intersects(windows.at(j)->geometry()));
1608 }
1609 }
1610
1611 // Keep the views tiled through any subsequent resize events.
1612 for (int i = 0; i < 5; ++i) {
1613 workspace.resize(workspace.size() - QSize(10, 10));
1614 qApp->processEvents();
1615 }
1616 workspace.setActiveSubWindow(0);
1617 QCOMPARE(workspace.viewport()->childrenRect(), workspace.viewport()->rect());
1618
1619 QMdiSubWindow *window = windows.at(i: 0);
1620
1621 // Change the geometry of one of the children and verify
1622 // that the views are not tiled anymore.
1623 window->move(ax: window->x() + 1, ay: window->y());
1624 workspace.resize(workspace.size() - QSize(10, 10));
1625 workspace.setActiveSubWindow(0);
1626 QVERIFY(workspace.viewport()->childrenRect() != workspace.viewport()->rect());
1627 qApp->processEvents();
1628
1629 // Re-tile.
1630 workspace.tileSubWindows();
1631 workspace.setActiveSubWindow(0);
1632 QCOMPARE(workspace.viewport()->childrenRect(), workspace.viewport()->rect());
1633
1634 // Close one of the children and verify that the views
1635 // are not tiled anymore.
1636 window->close();
1637 workspace.resize(workspace.size() - QSize(10, 10));
1638 workspace.setActiveSubWindow(0);
1639 QVERIFY(workspace.viewport()->childrenRect() != workspace.viewport()->rect());
1640 qApp->processEvents();
1641
1642 // Re-tile.
1643 workspace.tileSubWindows();
1644 workspace.setActiveSubWindow(0);
1645 QCOMPARE(workspace.viewport()->childrenRect(), workspace.viewport()->rect());
1646
1647 window = windows.at(i: 1);
1648
1649 // Maximize one of the children and verify that the views
1650 // are not tiled anymore.
1651 workspace.tileSubWindows();
1652 window->showMaximized();
1653 workspace.resize(workspace.size() - QSize(10, 10));
1654 workspace.setActiveSubWindow(0);
1655 QVERIFY(workspace.viewport()->childrenRect() != workspace.viewport()->rect());
1656 qApp->processEvents();
1657
1658 // Re-tile.
1659 workspace.tileSubWindows();
1660 workspace.setActiveSubWindow(0);
1661 QCOMPARE(workspace.viewport()->childrenRect(), workspace.viewport()->rect());
1662
1663 // Minimize one of the children and verify that the views
1664 // are not tiled anymore.
1665 workspace.tileSubWindows();
1666 window->showMinimized();
1667 workspace.resize(workspace.size() - QSize(10, 10));
1668 workspace.setActiveSubWindow(0);
1669 QVERIFY(workspace.viewport()->childrenRect() != workspace.viewport()->rect());
1670 qApp->processEvents();
1671
1672 // Re-tile.
1673 workspace.tileSubWindows();
1674 workspace.setActiveSubWindow(0);
1675 QCOMPARE(workspace.viewport()->childrenRect(), workspace.viewport()->rect());
1676
1677 // Active/deactivate windows and verify that the views are tiled.
1678 workspace.setActiveSubWindow(windows.at(i: 5));
1679 workspace.resize(workspace.size() - QSize(10, 10));
1680 workspace.setActiveSubWindow(0);
1681 QTest::qWait(ms: 250); // delayed re-arrange of minimized windows
1682 QTRY_COMPARE(workspace.viewport()->childrenRect(), workspace.viewport()->rect());
1683
1684 // Add another window and verify that the views are not tiled anymore.
1685 workspace.addSubWindow(widget: new QPushButton(QLatin1String("I'd like to mess up tiled views")))->show();
1686 workspace.resize(workspace.size() - QSize(10, 10));
1687 workspace.setActiveSubWindow(0);
1688 QVERIFY(workspace.viewport()->childrenRect() != workspace.viewport()->rect());
1689
1690 // Re-tile.
1691 workspace.tileSubWindows();
1692 workspace.setActiveSubWindow(0);
1693#ifdef Q_OS_WINRT
1694 QEXPECT_FAIL("", "Broken on WinRT - QTBUG-68297", Abort);
1695#endif
1696 QCOMPARE(workspace.viewport()->childrenRect(), workspace.viewport()->rect());
1697
1698 // Cascade and verify that the views are not tiled anymore.
1699 workspace.cascadeSubWindows();
1700 workspace.resize(workspace.size() - QSize(10, 10));
1701 workspace.setActiveSubWindow(0);
1702 QVERIFY(workspace.viewport()->childrenRect() != workspace.viewport()->rect());
1703
1704 // Make sure the active window does not move position after a tile regardless
1705 // of whether we have any windows with staysOnTopHint or not.
1706 workspace.tileSubWindows();
1707 windows.at(i: 3)->setWindowFlags(windows.at(i: 3)->windowFlags() | Qt::WindowStaysOnTopHint);
1708 QMdiSubWindow *activeSubWindow = windows.at(i: 6);
1709 workspace.setActiveSubWindow(activeSubWindow);
1710 QCOMPARE(workspace.activeSubWindow(), activeSubWindow);
1711 QPoint pos = activeSubWindow->geometry().topLeft();
1712 workspace.tileSubWindows();
1713 QCOMPARE(activeSubWindow->geometry().topLeft(), pos);
1714
1715 // Verify that we try to resize the area such that all sub-windows are visible.
1716 // It's important that tiled windows are NOT overlapping.
1717 workspace.resize(w: 350, h: 150);
1718 qApp->processEvents();
1719 QTRY_COMPARE(workspace.size(), QSize(350, 150));
1720
1721 const QSize minSize(600, 130);
1722 foreach (QMdiSubWindow *subWindow, workspace.subWindowList())
1723 subWindow->setMinimumSize(minSize);
1724
1725 QCOMPARE(workspace.size(), QSize(350, 150));
1726
1727 // Prevent scrollbars from messing up the expected viewport calculation below
1728 workspace.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1729 workspace.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1730 QCOMPARE(workspace.horizontalScrollBarPolicy(), Qt::ScrollBarAlwaysOff);
1731 QCOMPARE(workspace.verticalScrollBarPolicy(), Qt::ScrollBarAlwaysOff);
1732
1733 workspace.tileSubWindows();
1734 // The sub-windows are now tiled like this:
1735 // | win 1 || win 2 || win 3 |
1736 // +-------++-------++-------+
1737 // +-------++-------++-------+
1738 // | win 4 || win 5 || win 6 |
1739 // +-------++-------++-------+
1740 // +-------++-------++-------+
1741 // | win 7 || win 8 || win 9 |
1742 workspace.setActiveSubWindow(0);
1743 int frameWidth = 0;
1744 if (workspace.style()->styleHint(stylehint: QStyle::SH_ScrollView_FrameOnlyAroundContents, opt: 0, widget: &workspace))
1745 frameWidth = workspace.style()->pixelMetric(metric: QStyle::PM_DefaultFrameWidth);
1746 const int spacing = 2 * frameWidth + 2;
1747 const QSize expectedViewportSize(3 * minSize.width() + spacing, 3 * minSize.height() + spacing);
1748 QTRY_COMPARE(workspace.viewport()->rect().size(), expectedViewportSize);
1749
1750 // Enable scroll bar for test below (default property for QMdiArea is Qt::ScrollBarAlwaysOff)
1751 workspace.setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
1752 workspace.setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
1753 QCOMPARE(workspace.horizontalScrollBarPolicy(), Qt::ScrollBarAsNeeded);
1754 QCOMPARE(workspace.verticalScrollBarPolicy(), Qt::ScrollBarAsNeeded);
1755
1756 // Not enough space for all sub-windows to be visible -> provide scroll bars.
1757 workspace.resize(w: 160, h: 150);
1758 qApp->processEvents();
1759 QTRY_COMPARE(workspace.size(), QSize(160, 150));
1760
1761 // Horizontal scroll bar.
1762 QScrollBar *hBar = workspace.horizontalScrollBar();
1763 QCOMPARE(workspace.horizontalScrollBarPolicy(), Qt::ScrollBarAsNeeded);
1764 QTRY_VERIFY(hBar->isVisible());
1765 QCOMPARE(hBar->value(), 0);
1766 QCOMPARE(hBar->minimum(), 0);
1767
1768 // Vertical scroll bar.
1769 QScrollBar *vBar = workspace.verticalScrollBar();
1770 QCOMPARE(workspace.verticalScrollBarPolicy(), Qt::ScrollBarAsNeeded);
1771 QVERIFY(vBar->isVisible());
1772 QCOMPARE(vBar->value(), 0);
1773 QCOMPARE(vBar->minimum(), 0);
1774
1775 // Tile windows with scroll bars enabled.
1776 workspace.tileSubWindows();
1777 QVERIFY(QTest::qWaitForWindowExposed(&workspace));
1778 qApp->processEvents();
1779
1780 // Workspace should not have changed size after tile.
1781 QTRY_VERIFY(workspace.size() == QSize(160, 150));
1782 // Scroll bars should be visible.
1783 QTRY_VERIFY(vBar->isVisible());
1784 QTRY_VERIFY(hBar->isVisible());
1785}
1786
1787void tst_QMdiArea::cascadeAndTileSubWindows()
1788{
1789 QMdiArea workspace;
1790 workspace.resize(w: 400, h: 400);
1791 workspace.show();
1792 QVERIFY(QTest::qWaitForWindowExposed(&workspace));
1793
1794 const int windowCount = 10;
1795 QList<QMdiSubWindow *> windows;
1796 for (int i = 0; i < windowCount; ++i) {
1797 QMdiSubWindow *window = workspace.addSubWindow(widget: new MyChild);
1798 if (i % 3 == 0) {
1799 window->showMinimized();
1800 QVERIFY(window->isMinimized());
1801 } else {
1802 window->showMaximized();
1803 QVERIFY(window->isMaximized());
1804 }
1805 windows.append(t: window);
1806 }
1807
1808 // cascadeSubWindows
1809 qApp->processEvents();
1810 workspace.cascadeSubWindows();
1811 qApp->processEvents();
1812
1813 // Check dy between two cascaded windows
1814 const int dy = cascadedDeltaY(area: &workspace);
1815#ifdef Q_OS_MAC
1816 QEXPECT_FAIL("", "QTBUG-25298", Abort);
1817#endif
1818 QCOMPARE(windows.at(2)->geometry().top() - windows.at(1)->geometry().top(), dy);
1819
1820 for (int i = 0; i < windows.count(); ++i) {
1821 QMdiSubWindow *window = windows.at(i);
1822 if (i % 3 == 0) {
1823 QVERIFY(window->isMinimized());
1824 } else {
1825 QVERIFY(!window->isMaximized());
1826 QCOMPARE(window->size(), window->sizeHint());
1827 window->showMaximized();
1828 QVERIFY(window->isMaximized());
1829 }
1830 }
1831}
1832
1833void tst_QMdiArea::resizeMaximizedChildWindows_data()
1834{
1835 QTest::addColumn<int>(name: "startSize");
1836 QTest::addColumn<int>(name: "increment");
1837 QTest::addColumn<int>(name: "windowCount");
1838
1839 QTest::newRow(dataTag: "multiple children") << 400 << 20 << 10;
1840}
1841
1842void tst_QMdiArea::resizeMaximizedChildWindows()
1843{
1844 QFETCH(int, startSize);
1845 QFETCH(int, increment);
1846 QFETCH(int, windowCount);
1847
1848 QWidget topLevel;
1849 QMdiArea workspace(&topLevel);
1850 topLevel.show();
1851 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
1852 workspace.resize(w: startSize, h: startSize);
1853 workspace.setOption(option: QMdiArea::DontMaximizeSubWindowOnActivation);
1854 QSize workspaceSize = workspace.size();
1855 QVERIFY(workspaceSize.isValid());
1856 QCOMPARE(workspaceSize, QSize(startSize, startSize));
1857
1858 QList<QMdiSubWindow *> windows;
1859 for (int i = 0; i < windowCount; ++i) {
1860 QMdiSubWindow *window = workspace.addSubWindow(widget: new QWidget);
1861 windows.append(t: window);
1862 qApp->processEvents();
1863 window->showMaximized();
1864 QTest::qWait(ms: 100);
1865 QVERIFY(window->isMaximized());
1866 QSize windowSize = window->size();
1867 QVERIFY(windowSize.isValid());
1868 QCOMPARE(window->rect(), workspace.contentsRect());
1869
1870 workspace.resize(workspaceSize + QSize(increment, increment));
1871 QTest::qWait(ms: 100);
1872 qApp->processEvents();
1873 QTRY_COMPARE(workspace.size(), workspaceSize + QSize(increment, increment));
1874 QTRY_COMPARE(window->size(), windowSize + QSize(increment, increment));
1875 workspaceSize = workspace.size();
1876 }
1877
1878 int newSize = startSize + increment * windowCount;
1879 QCOMPARE(workspaceSize, QSize(newSize, newSize));
1880 foreach (QWidget *window, windows)
1881 QCOMPARE(window->rect(), workspace.contentsRect());
1882}
1883
1884// QWidget::setParent clears focusWidget so make sure
1885// we restore it after QMdiArea::addSubWindow.
1886void tst_QMdiArea::focusWidgetAfterAddSubWindow()
1887{
1888 QWidget *view = new QWidget;
1889 view->setLayout(new QVBoxLayout);
1890
1891 QLineEdit *lineEdit1 = new QLineEdit;
1892 QLineEdit *lineEdit2 = new QLineEdit;
1893 view->layout()->addWidget(w: lineEdit1);
1894 view->layout()->addWidget(w: lineEdit2);
1895
1896 lineEdit2->setFocus();
1897 QCOMPARE(view->focusWidget(), static_cast<QWidget *>(lineEdit2));
1898
1899 QMdiArea mdiArea;
1900 mdiArea.addSubWindow(widget: view);
1901 QCOMPARE(view->focusWidget(), static_cast<QWidget *>(lineEdit2));
1902
1903 mdiArea.show();
1904 view->show();
1905 qApp->setActiveWindow(&mdiArea);
1906 QCOMPARE(qApp->focusWidget(), static_cast<QWidget *>(lineEdit2));
1907}
1908
1909void tst_QMdiArea::dontMaximizeSubWindowOnActivation()
1910{
1911 QMdiArea mdiArea;
1912 mdiArea.show();
1913 QVERIFY(QTest::qWaitForWindowExposed(&mdiArea));
1914 qApp->setActiveWindow(&mdiArea);
1915
1916 // Add one maximized window.
1917 mdiArea.addSubWindow(widget: new QWidget)->showMaximized();
1918 QVERIFY(mdiArea.activeSubWindow());
1919 QVERIFY(mdiArea.activeSubWindow()->isMaximized());
1920
1921 // Add few more windows and verify that they are maximized.
1922 for (int i = 0; i < 5; ++i) {
1923 QMdiSubWindow *window = mdiArea.addSubWindow(widget: new QWidget);
1924 window->show();
1925#if defined Q_OS_QNX
1926 QEXPECT_FAIL("", "QTBUG-38231", Abort);
1927#endif
1928 QVERIFY(window->isMaximized());
1929 qApp->processEvents();
1930 }
1931
1932 // Verify that activated windows still are maximized on activation.
1933 QList<QMdiSubWindow *> subWindows = mdiArea.subWindowList();
1934 for (int i = 0; i < subWindows.count(); ++i) {
1935 mdiArea.activateNextSubWindow();
1936 QMdiSubWindow *window = subWindows.at(i);
1937 QCOMPARE(mdiArea.activeSubWindow(), window);
1938 QVERIFY(window->isMaximized());
1939 qApp->processEvents();
1940 }
1941
1942 // Restore active window and verify that other windows aren't
1943 // maximized on activation.
1944 mdiArea.activeSubWindow()->showNormal();
1945 for (int i = 0; i < subWindows.count(); ++i) {
1946 mdiArea.activateNextSubWindow();
1947 QMdiSubWindow *window = subWindows.at(i);
1948 QCOMPARE(mdiArea.activeSubWindow(), window);
1949 QVERIFY(!window->isMaximized());
1950 qApp->processEvents();
1951 }
1952
1953 // Enable 'DontMaximizedSubWindowOnActivation' and maximize the active window.
1954 mdiArea.setOption(option: QMdiArea::DontMaximizeSubWindowOnActivation);
1955 mdiArea.activeSubWindow()->showMaximized();
1956 int indexOfMaximized = subWindows.indexOf(t: mdiArea.activeSubWindow());
1957
1958 // Verify that windows are not maximized on activation.
1959 for (int i = 0; i < subWindows.count(); ++i) {
1960 mdiArea.activateNextSubWindow();
1961 QMdiSubWindow *window = subWindows.at(i);
1962 QCOMPARE(mdiArea.activeSubWindow(), window);
1963 if (indexOfMaximized != i)
1964 QVERIFY(!window->isMaximized());
1965 qApp->processEvents();
1966 }
1967 QVERIFY(mdiArea.activeSubWindow()->isMaximized());
1968
1969 // Minimize all windows.
1970 foreach (QMdiSubWindow *window, subWindows) {
1971 window->showMinimized();
1972 QVERIFY(window->isMinimized());
1973 qApp->processEvents();
1974 }
1975
1976 // Disable 'DontMaximizedSubWindowOnActivation' and maximize the active window.
1977 mdiArea.setOption(option: QMdiArea::DontMaximizeSubWindowOnActivation, on: false);
1978 mdiArea.activeSubWindow()->showMaximized();
1979
1980 // Verify that minimized windows are maximized on activation.
1981 for (int i = 0; i < subWindows.count(); ++i) {
1982 mdiArea.activateNextSubWindow();
1983 QMdiSubWindow *window = subWindows.at(i);
1984 QCOMPARE(mdiArea.activeSubWindow(), window);
1985 QVERIFY(window->isMaximized());
1986 qApp->processEvents();
1987 }
1988
1989 // Verify that activated windows are maximized after closing
1990 // the active window
1991 for (int i = 0; i < subWindows.count(); ++i) {
1992 QVERIFY(mdiArea.activeSubWindow());
1993 QVERIFY(mdiArea.activeSubWindow()->isMaximized());
1994 mdiArea.activeSubWindow()->close();
1995 qApp->processEvents();
1996 }
1997
1998 QVERIFY(!mdiArea.activeSubWindow());
1999 QCOMPARE(mdiArea.subWindowList().size(), 0);
2000
2001 // Verify that new windows are not maximized.
2002 mdiArea.addSubWindow(widget: new QWidget)->show();
2003 QVERIFY(mdiArea.activeSubWindow());
2004#ifdef Q_OS_WINRT
2005 QEXPECT_FAIL("", "Broken on WinRT - QTBUG-68297", Abort);
2006#endif
2007 QVERIFY(!mdiArea.activeSubWindow()->isMaximized());
2008}
2009
2010void tst_QMdiArea::delayedPlacement()
2011{
2012 QMdiArea mdiArea;
2013
2014 QMdiSubWindow *window1 = mdiArea.addSubWindow(widget: new QWidget);
2015 QCOMPARE(window1->geometry().topLeft(), QPoint(0, 0));
2016
2017 QMdiSubWindow *window2 = mdiArea.addSubWindow(widget: new QWidget);
2018 QCOMPARE(window2->geometry().topLeft(), QPoint(0, 0));
2019
2020 QMdiSubWindow *window3 = mdiArea.addSubWindow(widget: new QWidget);
2021 QCOMPARE(window3->geometry().topLeft(), QPoint(0, 0));
2022
2023 mdiArea.resize(w: window3->minimumSizeHint().width() * 3, h: 400);
2024 mdiArea.show();
2025 QVERIFY(QTest::qWaitForWindowExposed(&mdiArea));
2026
2027 QCOMPARE(window1->geometry().topLeft(), QPoint(0, 0));
2028#ifdef Q_OS_WINRT
2029 QEXPECT_FAIL("", "Broken on WinRT - QTBUG-68297", Abort);
2030#endif
2031 QCOMPARE(window2->geometry().topLeft(), window1->geometry().topRight() + QPoint(1, 0));
2032 QCOMPARE(window3->geometry().topLeft(), window2->geometry().topRight() + QPoint(1, 0));
2033}
2034
2035void tst_QMdiArea::iconGeometryInMenuBar()
2036{
2037#if !defined (Q_OS_DARWIN)
2038 QMainWindow mainWindow;
2039 QMenuBar *menuBar = mainWindow.menuBar();
2040 menuBar->setNativeMenuBar(false);
2041 QMdiArea *mdiArea = new QMdiArea;
2042 QMdiSubWindow *subWindow = mdiArea->addSubWindow(widget: new QWidget);
2043 mainWindow.setCentralWidget(mdiArea);
2044 mainWindow.show();
2045 QVERIFY(QTest::qWaitForWindowExposed(&mainWindow));
2046
2047 subWindow->showMaximized();
2048 QVERIFY(subWindow->isMaximized());
2049
2050 QWidget *leftCornerWidget = menuBar->cornerWidget(corner: Qt::TopLeftCorner);
2051 QVERIFY(leftCornerWidget);
2052 int topMargin = (menuBar->height() - leftCornerWidget->height()) / 2;
2053 int leftMargin = qApp->style()->pixelMetric(metric: QStyle::PM_MenuBarHMargin)
2054 + qApp->style()->pixelMetric(metric: QStyle::PM_MenuBarPanelWidth);
2055 QPoint pos(leftMargin, topMargin);
2056 QRect geometry = QStyle::visualRect(qApp->layoutDirection(), boundingRect: menuBar->rect(),
2057 logicalRect: QRect(pos, leftCornerWidget->size()));
2058 QCOMPARE(leftCornerWidget->geometry(), geometry);
2059#endif
2060}
2061
2062class EventSpy : public QObject
2063{
2064public:
2065 EventSpy(QObject *object, QEvent::Type event)
2066 : eventToSpy(event), _count(0)
2067 {
2068 if (object)
2069 object->installEventFilter(filterObj: this);
2070 }
2071
2072 int count() const { return _count; }
2073 void clear() { _count = 0; }
2074
2075protected:
2076 bool eventFilter(QObject *object, QEvent *event)
2077 {
2078 if (event->type() == eventToSpy)
2079 ++_count;
2080 return QObject::eventFilter(watched: object, event);
2081 }
2082
2083private:
2084 QEvent::Type eventToSpy;
2085 int _count;
2086};
2087
2088void tst_QMdiArea::resizeTimer()
2089{
2090 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
2091 QSKIP("Wayland: This fails. Figure out why.");
2092
2093 QMdiArea mdiArea;
2094 QMdiSubWindow *subWindow = mdiArea.addSubWindow(widget: new QWidget);
2095 mdiArea.show();
2096 QVERIFY(QTest::qWaitForWindowActive(&mdiArea));
2097
2098 int time = 250;
2099
2100 EventSpy timerEventSpy(subWindow, QEvent::Timer);
2101 QCOMPARE(timerEventSpy.count(), 0);
2102
2103 mdiArea.tileSubWindows();
2104 QTest::qWait(ms: time); // Wait for timer events to occur.
2105 QCOMPARE(timerEventSpy.count(), 1);
2106 timerEventSpy.clear();
2107
2108 mdiArea.resize(mdiArea.size() + QSize(2, 2));
2109 QTest::qWait(ms: time); // Wait for timer events to occur.
2110 QCOMPARE(timerEventSpy.count(), 1);
2111 timerEventSpy.clear();
2112
2113 // Check that timers are killed.
2114 QTest::qWait(ms: time); // Wait for timer events to occur.
2115 QCOMPARE(timerEventSpy.count(), 0);
2116}
2117
2118void tst_QMdiArea::updateScrollBars()
2119{
2120 QMdiArea mdiArea;
2121 mdiArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
2122 mdiArea.setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
2123
2124 QMdiSubWindow *subWindow1 = mdiArea.addSubWindow(widget: new QWidget);
2125 QMdiSubWindow *subWindow2 = mdiArea.addSubWindow(widget: new QWidget);
2126
2127 mdiArea.show();
2128 QVERIFY(QTest::qWaitForWindowExposed(&mdiArea));
2129 qApp->processEvents();
2130
2131 QScrollBar *hbar = mdiArea.horizontalScrollBar();
2132 QVERIFY(hbar);
2133 QVERIFY(hbar->style()->styleHint(QStyle::SH_ScrollBar_Transient) || !hbar->isVisible());
2134
2135 QScrollBar *vbar = mdiArea.verticalScrollBar();
2136 QVERIFY(vbar);
2137 QVERIFY(vbar->style()->styleHint(QStyle::SH_ScrollBar_Transient) || !vbar->isVisible());
2138
2139 // Move sub-window 2 away.
2140 subWindow2->move(ax: 10000, ay: 10000);
2141 qApp->processEvents();
2142 QVERIFY(hbar->style()->styleHint(QStyle::SH_ScrollBar_Transient) || hbar->isVisible());
2143 QVERIFY(vbar->style()->styleHint(QStyle::SH_ScrollBar_Transient) || vbar->isVisible());
2144
2145 for (int i = 0; i < 2; ++i) {
2146 // Maximize sub-window 1 and make sure we don't have any scroll bars.
2147 subWindow1->showMaximized();
2148 qApp->processEvents();
2149 QVERIFY(subWindow1->isMaximized());
2150 QVERIFY(hbar->style()->styleHint(QStyle::SH_ScrollBar_Transient) || !hbar->isVisible());
2151 QVERIFY(vbar->style()->styleHint(QStyle::SH_ScrollBar_Transient) || !vbar->isVisible());
2152
2153 // We still shouldn't get any scroll bars.
2154 mdiArea.resize(mdiArea.size() - QSize(20, 20));
2155 QVERIFY(QTest::qWaitForWindowExposed(&mdiArea));
2156 qApp->processEvents();
2157 QVERIFY(subWindow1->isMaximized());
2158 QVERIFY(hbar->style()->styleHint(QStyle::SH_ScrollBar_Transient) || !hbar->isVisible());
2159 QVERIFY(vbar->style()->styleHint(QStyle::SH_ScrollBar_Transient) || !vbar->isVisible());
2160
2161 // Restore sub-window 1 and make sure we have scroll bars again.
2162 subWindow1->showNormal();
2163 qApp->processEvents();
2164 QVERIFY(!subWindow1->isMaximized());
2165#ifdef Q_OS_WINRT
2166 QEXPECT_FAIL("", "Widgets are maximized by default on WinRT, so scroll bars might not be"
2167 "visible", Abort);
2168#endif
2169 QVERIFY(hbar->style()->styleHint(QStyle::SH_ScrollBar_Transient) || hbar->isVisible());
2170 QVERIFY(vbar->style()->styleHint(QStyle::SH_ScrollBar_Transient) || vbar->isVisible());
2171 if (i == 0) {
2172 // Now, do the same when the viewport is scrolled.
2173 hbar->setValue(1000);
2174 vbar->setValue(1000);
2175 }
2176 }
2177}
2178
2179void tst_QMdiArea::setActivationOrder_data()
2180{
2181 QTest::addColumn<QMdiArea::WindowOrder>(name: "activationOrder");
2182 QTest::addColumn<int>(name: "subWindowCount");
2183 QTest::addColumn<int>(name: "staysOnTopIndex");
2184 QTest::addColumn<int>(name: "firstActiveIndex");
2185 QTest::addColumn<QList<int> >(name: "expectedActivationIndices");
2186 // The order of expectedCascadeIndices:
2187 // window 1 -> (index 0)
2188 // window 2 -> (index 1)
2189 // window 3 -> (index 2)
2190 // ....
2191 QTest::addColumn<QList<int> >(name: "expectedCascadeIndices");
2192
2193 // The order of expectedTileIndices (the same as reading a book LTR).
2194 // +--------------------+--------------------+--------------------+
2195 // | window 1 (index 0) | window 2 (index 1) | window 3 (index 2) |
2196 // | +--------------------+--------------------+
2197 // | (index 3) | window 4 (index 4) | window 5 (index 5) |
2198 // +--------------------------------------------------------------+
2199 QTest::addColumn<QList<int> >(name: "expectedTileIndices");
2200
2201 QList<int> list;
2202 QList<int> list2;
2203 QList<int> list3;
2204
2205 list << 2 << 1 << 0 << 1 << 2 << 3 << 4;
2206 list2 << 0 << 1 << 2 << 3 << 4;
2207 list3 << 4 << 3 << 2 << 4 << 1 << 0; // Most recently created window is in top-left position
2208 QTest::newRow(dataTag: "CreationOrder") << QMdiArea::CreationOrder << 5 << 3 << 1 << list << list2 << list3;
2209
2210 list = QList<int>();
2211 list << 3 << 1 << 4 << 3 << 1 << 2 << 0;
2212 list2 = QList<int>();
2213 list2 << 0 << 2 << 4 << 1 << 3;
2214 list3 = QList<int>();
2215 list3 << 3 << 1 << 4 << 3 << 2 << 0; // Window with "stays-on-top" flag set will be in the top-left position
2216 QTest::newRow(dataTag: "StackingOrder") << QMdiArea::StackingOrder << 5 << 3 << 1 << list << list2 << list3;
2217
2218 list = QList<int>();
2219 list << 0 << 1 << 0 << 1 << 4 << 3 << 2;
2220 list2 = QList<int>();
2221 list2 << 0 << 2 << 3 << 4 << 1;
2222 list3 = QList<int>();
2223 list3 << 1 << 4 << 3 << 1 << 2 << 0;
2224 QTest::newRow(dataTag: "ActivationHistoryOrder") << QMdiArea::ActivationHistoryOrder << 5 << 3 << 1 << list << list2 << list3;
2225}
2226
2227void tst_QMdiArea::setActivationOrder()
2228{
2229 QFETCH(QMdiArea::WindowOrder, activationOrder);
2230 QFETCH(int, subWindowCount);
2231 QFETCH(int, staysOnTopIndex);
2232 QFETCH(int, firstActiveIndex);
2233 QFETCH(QList<int>, expectedActivationIndices);
2234 QFETCH(QList<int>, expectedCascadeIndices);
2235 QFETCH(QList<int>, expectedTileIndices);
2236
2237 // Default order.
2238 QMdiArea mdiArea;
2239 QCOMPARE(mdiArea.activationOrder(), QMdiArea::CreationOrder);
2240
2241 // New order.
2242 mdiArea.setActivationOrder(activationOrder);
2243 QCOMPARE(mdiArea.activationOrder(), activationOrder);
2244
2245 QList<QMdiSubWindow *> subWindows;
2246 for (int i = 0; i < subWindowCount; ++i)
2247 subWindows << mdiArea.addSubWindow(widget: new QPushButton(tr(s: "%1").arg(a: i)));
2248 QCOMPARE(mdiArea.subWindowList(activationOrder), subWindows);
2249
2250 mdiArea.show();
2251 QVERIFY(QTest::qWaitForWindowExposed(&mdiArea));
2252
2253 for (int i = 0; i < subWindows.count(); ++i) {
2254 mdiArea.activateNextSubWindow();
2255 QCOMPARE(mdiArea.activeSubWindow(), subWindows.at(i));
2256 qApp->processEvents();
2257 }
2258
2259 QMdiSubWindow *staysOnTop = subWindows.at(i: staysOnTopIndex);
2260 staysOnTop->setWindowFlags(staysOnTop->windowFlags() | Qt::WindowStaysOnTopHint);
2261 staysOnTop->raise();
2262
2263 mdiArea.setActiveSubWindow(subWindows.at(i: firstActiveIndex));
2264 QCOMPARE(mdiArea.activeSubWindow(), subWindows.at(firstActiveIndex));
2265
2266 // Verify the actual arrangement/geometry.
2267 mdiArea.tileSubWindows();
2268 QTest::qWait(ms: 100);
2269 QVERIFY(verifyArrangement(&mdiArea, Tiled, expectedTileIndices));
2270
2271 mdiArea.cascadeSubWindows();
2272#ifdef Q_OS_MAC
2273 QEXPECT_FAIL("", "QTBUG-25298", Abort);
2274#endif
2275 QVERIFY(verifyArrangement(&mdiArea, Cascaded, expectedCascadeIndices));
2276 QTest::qWait(ms: 100);
2277
2278 mdiArea.activateNextSubWindow();
2279 QCOMPARE(mdiArea.activeSubWindow(), subWindows.at(expectedActivationIndices.takeFirst()));
2280
2281 mdiArea.activatePreviousSubWindow();
2282 QCOMPARE(mdiArea.activeSubWindow(), subWindows.at(expectedActivationIndices.takeFirst()));
2283
2284 mdiArea.activatePreviousSubWindow();
2285 QCOMPARE(mdiArea.activeSubWindow(), subWindows.at(expectedActivationIndices.takeFirst()));
2286
2287 for (int i = 0; i < subWindowCount; ++i) {
2288 mdiArea.closeActiveSubWindow();
2289 qApp->processEvents();
2290 if (i == subWindowCount - 1) { // Last window closed.
2291 QVERIFY(!mdiArea.activeSubWindow());
2292 break;
2293 }
2294 QVERIFY(mdiArea.activeSubWindow());
2295 QCOMPARE(mdiArea.activeSubWindow(), subWindows.at(expectedActivationIndices.takeFirst()));
2296 }
2297
2298 QVERIFY(mdiArea.subWindowList(activationOrder).isEmpty());
2299 QVERIFY(expectedActivationIndices.isEmpty());
2300}
2301
2302void tst_QMdiArea::tabBetweenSubWindows()
2303{
2304 QMdiArea mdiArea;
2305 QList<QMdiSubWindow *> subWindows;
2306 for (int i = 0; i < 5; ++i)
2307 subWindows << mdiArea.addSubWindow(widget: new QLineEdit);
2308
2309 mdiArea.show();
2310 QVERIFY(QTest::qWaitForWindowExposed(&mdiArea));
2311
2312 qApp->setActiveWindow(&mdiArea);
2313 QWidget *focusWidget = subWindows.back()->widget();
2314 QCOMPARE(qApp->focusWidget(), focusWidget);
2315
2316 QSignalSpy spy(&mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)));
2317 QCOMPARE(spy.count(), 0);
2318
2319 // Walk through the entire list of sub windows.
2320#ifdef Q_OS_MAC
2321 QEXPECT_FAIL("", "QTBUG-25298", Abort);
2322#endif
2323 QVERIFY(tabBetweenSubWindowsIn(&mdiArea));
2324 QCOMPARE(mdiArea.activeSubWindow(), subWindows.back());
2325 QCOMPARE(spy.count(), 0);
2326
2327 mdiArea.setActiveSubWindow(subWindows.front());
2328 QCOMPARE(mdiArea.activeSubWindow(), subWindows.front());
2329 spy.clear();
2330
2331 // Walk through the entire list of sub windows in the opposite direction (Ctrl-Shift-Tab).
2332 QVERIFY(tabBetweenSubWindowsIn(&mdiArea, -1, true));
2333 QCOMPARE(mdiArea.activeSubWindow(), subWindows.front());
2334 QCOMPARE(spy.count(), 0);
2335
2336 // Ctrl-Tab-Tab-Tab
2337 QVERIFY(tabBetweenSubWindowsIn(&mdiArea, 3));
2338 QCOMPARE(mdiArea.activeSubWindow(), subWindows.at(3));
2339 QCOMPARE(spy.count(), 1);
2340
2341 mdiArea.setActiveSubWindow(subWindows.at(i: 1));
2342 QCOMPARE(mdiArea.activeSubWindow(), subWindows.at(1));
2343 spy.clear();
2344
2345 // Quick switch (Ctrl-Tab once) -> switch back to the previously active sub-window.
2346 QVERIFY(tabBetweenSubWindowsIn(&mdiArea, 1));
2347 QCOMPARE(mdiArea.activeSubWindow(), subWindows.at(3));
2348 QCOMPARE(spy.count(), 1);
2349}
2350
2351void tst_QMdiArea::setViewMode()
2352{
2353 QMdiArea mdiArea;
2354
2355 QPixmap iconPixmap(16, 16);
2356 iconPixmap.fill(fillColor: Qt::red);
2357 for (int i = 0; i < 5; ++i) {
2358 QMdiSubWindow *subWindow = mdiArea.addSubWindow(widget: new QWidget);
2359 subWindow->setWindowTitle(QLatin1String("Title ") + QString::number(i));
2360 subWindow->setWindowIcon(iconPixmap);
2361 }
2362
2363 mdiArea.show();
2364 QVERIFY(QTest::qWaitForWindowExposed(&mdiArea));
2365
2366 QMdiSubWindow *activeSubWindow = mdiArea.activeSubWindow();
2367 QList<QMdiSubWindow *> subWindows = mdiArea.subWindowList();
2368
2369 // Default.
2370#ifdef Q_OS_WINRT
2371 QEXPECT_FAIL("", "Widgets are maximized by default on WinRT, so scroll bars might not be"
2372 "visible", Abort);
2373#endif
2374 QVERIFY(!activeSubWindow->isMaximized());
2375 QTabBar *tabBar = mdiArea.findChild<QTabBar *>();
2376 QVERIFY(!tabBar);
2377 QCOMPARE(mdiArea.viewMode(), QMdiArea::SubWindowView);
2378
2379 // Tabbed view.
2380 mdiArea.setViewMode(QMdiArea::TabbedView);
2381 QCOMPARE(mdiArea.viewMode(), QMdiArea::TabbedView);
2382 tabBar = mdiArea.findChild<QTabBar *>();
2383 QVERIFY(tabBar);
2384 QVERIFY(tabBar->isVisible());
2385
2386 QCOMPARE(tabBar->count(), subWindows.count());
2387 QVERIFY(activeSubWindow->isMaximized());
2388 QCOMPARE(tabBar->currentIndex(), subWindows.indexOf(activeSubWindow));
2389
2390 // Check that tabIcon and tabText are set properly.
2391 for (int i = 0; i < subWindows.size(); ++i) {
2392 QMdiSubWindow *subWindow = subWindows.at(i);
2393 QCOMPARE(tabBar->tabText(i), subWindow->windowTitle());
2394 QCOMPARE(tabBar->tabIcon(i), subWindow->windowIcon());
2395 }
2396
2397 // Check that tabText and tabIcon are updated.
2398 activeSubWindow->setWindowTitle(QLatin1String("Dude, I want another window title"));
2399 QCOMPARE(tabBar->tabText(tabBar->currentIndex()), activeSubWindow->windowTitle());
2400 iconPixmap.fill(fillColor: Qt::green);
2401 activeSubWindow->setWindowIcon(iconPixmap);
2402 QCOMPARE(tabBar->tabIcon(tabBar->currentIndex()), activeSubWindow->windowIcon());
2403
2404 // If there's an empty window title, tabText should return "(Untitled)" (as in firefox).
2405 activeSubWindow->setWindowTitle(QString());
2406 QCOMPARE(tabBar->tabText(tabBar->currentIndex()), QLatin1String("(Untitled)"));
2407
2408 // If there's no window icon, tabIcon should return ... an empty icon :)
2409 activeSubWindow->setWindowIcon(QIcon());
2410 QCOMPARE(tabBar->tabIcon(tabBar->currentIndex()), QIcon());
2411
2412 // Check that the current tab changes when activating another sub-window.
2413 for (int i = 0; i < subWindows.size(); ++i) {
2414 mdiArea.activateNextSubWindow();
2415 activeSubWindow = mdiArea.activeSubWindow();
2416 QCOMPARE(tabBar->currentIndex(), subWindows.indexOf(activeSubWindow));
2417 }
2418
2419 activeSubWindow = mdiArea.activeSubWindow();
2420 const int tabIndex = tabBar->currentIndex();
2421
2422 // The current tab should not change when the sub-window is hidden.
2423 activeSubWindow->hide();
2424 QCOMPARE(tabBar->currentIndex(), tabIndex);
2425 activeSubWindow->show();
2426 QCOMPARE(tabBar->currentIndex(), tabIndex);
2427
2428 // Disable the tab when the sub-window is hidden and another sub-window is activated.
2429 activeSubWindow->hide();
2430 mdiArea.activateNextSubWindow();
2431 QVERIFY(tabBar->currentIndex() != tabIndex);
2432 QVERIFY(!tabBar->isTabEnabled(tabIndex));
2433
2434 // Enable it again.
2435 activeSubWindow->show();
2436 QCOMPARE(tabBar->currentIndex(), tabIndex);
2437 QVERIFY(tabBar->isTabEnabled(tabIndex));
2438
2439 // Remove sub-windows and make sure the tab is removed.
2440 foreach (QMdiSubWindow *subWindow, subWindows) {
2441 if (subWindow != activeSubWindow) {
2442 mdiArea.removeSubWindow(widget: subWindow);
2443 delete subWindow;
2444 }
2445 }
2446 subWindows.clear();
2447 QCOMPARE(tabBar->count(), 1);
2448
2449 // Go back to default (QMdiArea::SubWindowView).
2450 mdiArea.setViewMode(QMdiArea::SubWindowView);
2451 QVERIFY(!activeSubWindow->isMaximized());
2452 tabBar = mdiArea.findChild<QTabBar *>();
2453 QVERIFY(!tabBar);
2454 QCOMPARE(mdiArea.viewMode(), QMdiArea::SubWindowView);
2455}
2456
2457void tst_QMdiArea::setTabsClosable()
2458{
2459 QMdiArea mdiArea;
2460 mdiArea.addSubWindow(widget: new QWidget);
2461
2462 // test default
2463 QCOMPARE(mdiArea.tabsClosable(), false);
2464
2465 // change value before tab bar exists
2466 QTabBar *tabBar = mdiArea.findChild<QTabBar *>();
2467 QVERIFY(!tabBar);
2468 mdiArea.setTabsClosable(true);
2469 QCOMPARE(mdiArea.tabsClosable(), true);
2470
2471 // force tab bar creation
2472 mdiArea.setViewMode(QMdiArea::TabbedView);
2473 tabBar = mdiArea.findChild<QTabBar *>();
2474 QVERIFY(tabBar);
2475
2476 // value must've been propagated
2477 QCOMPARE(tabBar->tabsClosable(), true);
2478
2479 // change value when tab bar exists
2480 mdiArea.setTabsClosable(false);
2481 QCOMPARE(mdiArea.tabsClosable(), false);
2482 QCOMPARE(tabBar->tabsClosable(), false);
2483}
2484
2485void tst_QMdiArea::setTabsMovable()
2486{
2487 QMdiArea mdiArea;
2488 QMdiSubWindow *subWindow1 = mdiArea.addSubWindow(widget: new QWidget);
2489 QMdiSubWindow *subWindow2 = mdiArea.addSubWindow(widget: new QWidget);
2490 QMdiSubWindow *subWindow3 = mdiArea.addSubWindow(widget: new QWidget);
2491
2492 // test default
2493 QCOMPARE(mdiArea.tabsMovable(), false);
2494
2495 // change value before tab bar exists
2496 QTabBar *tabBar = mdiArea.findChild<QTabBar *>();
2497 QVERIFY(!tabBar);
2498 mdiArea.setTabsMovable(true);
2499 QCOMPARE(mdiArea.tabsMovable(), true);
2500
2501 // force tab bar creation
2502 mdiArea.setViewMode(QMdiArea::TabbedView);
2503 tabBar = mdiArea.findChild<QTabBar *>();
2504 QVERIFY(tabBar);
2505
2506 // value must've been propagated
2507 QCOMPARE(tabBar->isMovable(), true);
2508
2509 // test tab moving
2510 QList<QMdiSubWindow *> subWindows;
2511 subWindows << subWindow1 << subWindow2 << subWindow3;
2512 QCOMPARE(mdiArea.subWindowList(QMdiArea::CreationOrder), subWindows);
2513 tabBar->moveTab(from: 1, to: 2); // 1,3,2
2514 subWindows.clear();
2515 subWindows << subWindow1 << subWindow3 << subWindow2;
2516 QCOMPARE(mdiArea.subWindowList(QMdiArea::CreationOrder), subWindows);
2517 tabBar->moveTab(from: 0, to: 2); // 3,2,1
2518 subWindows.clear();
2519 subWindows << subWindow3 << subWindow2 << subWindow1;
2520 QCOMPARE(mdiArea.subWindowList(QMdiArea::CreationOrder), subWindows);
2521
2522 // change value when tab bar exists
2523 mdiArea.setTabsMovable(false);
2524 QCOMPARE(mdiArea.tabsMovable(), false);
2525 QCOMPARE(tabBar->isMovable(), false);
2526}
2527
2528void tst_QMdiArea::setTabShape()
2529{
2530 QMdiArea mdiArea;
2531 mdiArea.addSubWindow(widget: new QWidget);
2532 mdiArea.show();
2533 QVERIFY(QTest::qWaitForWindowExposed(&mdiArea));
2534
2535 // Default.
2536 QCOMPARE(mdiArea.tabShape(), QTabWidget::Rounded);
2537
2538 // Triangular.
2539 mdiArea.setTabShape(QTabWidget::Triangular);
2540 QCOMPARE(mdiArea.tabShape(), QTabWidget::Triangular);
2541
2542 mdiArea.setViewMode(QMdiArea::TabbedView);
2543
2544 QTabBar *tabBar = mdiArea.findChild<QTabBar *>();
2545 QVERIFY(tabBar);
2546 QCOMPARE(tabBar->shape(), QTabBar::TriangularNorth);
2547
2548 // Back to default (Rounded).
2549 mdiArea.setTabShape(QTabWidget::Rounded);
2550 QCOMPARE(mdiArea.tabShape(), QTabWidget::Rounded);
2551 QCOMPARE(tabBar->shape(), QTabBar::RoundedNorth);
2552}
2553
2554void tst_QMdiArea::setTabPosition_data()
2555{
2556 QTest::addColumn<QTabWidget::TabPosition>(name: "tabPosition");
2557 QTest::addColumn<bool>(name: "hasLeftMargin");
2558 QTest::addColumn<bool>(name: "hasTopMargin");
2559 QTest::addColumn<bool>(name: "hasRightMargin");
2560 QTest::addColumn<bool>(name: "hasBottomMargin");
2561
2562 QTest::newRow(dataTag: "North") << QTabWidget::North << false << true << false << false;
2563 QTest::newRow(dataTag: "South") << QTabWidget::South << false << false << false << true;
2564 QTest::newRow(dataTag: "East") << QTabWidget::East << false << false << true << false;
2565 QTest::newRow(dataTag: "West") << QTabWidget::West << true << false << false << false;
2566}
2567
2568void tst_QMdiArea::setTabPosition()
2569{
2570 QFETCH(QTabWidget::TabPosition, tabPosition);
2571 QFETCH(bool, hasLeftMargin);
2572 QFETCH(bool, hasTopMargin);
2573 QFETCH(bool, hasRightMargin);
2574 QFETCH(bool, hasBottomMargin);
2575
2576 QMdiArea mdiArea;
2577 mdiArea.addSubWindow(widget: new QWidget);
2578 mdiArea.show();
2579 QVERIFY(QTest::qWaitForWindowExposed(&mdiArea));
2580
2581 // Make sure there are no margins.
2582 mdiArea.setContentsMargins(left: 0, top: 0, right: 0, bottom: 0);
2583
2584 // Default.
2585 QCOMPARE(mdiArea.tabPosition(), QTabWidget::North);
2586 mdiArea.setViewMode(QMdiArea::TabbedView);
2587 QTabBar *tabBar = mdiArea.findChild<QTabBar *>();
2588 QVERIFY(tabBar);
2589 QCOMPARE(tabBar->shape(), QTabBar::RoundedNorth);
2590
2591 // New position.
2592 mdiArea.setTabPosition(tabPosition);
2593 QCOMPARE(mdiArea.tabPosition(), tabPosition);
2594 QCOMPARE(tabBar->shape(), tabBarShapeFrom(QTabWidget::Rounded, tabPosition));
2595
2596 const Qt::LayoutDirection originalLayoutDirection = qApp->layoutDirection();
2597
2598 // Check that we have correct geometry in both RightToLeft and LeftToRight.
2599 for (int i = 0; i < 2; ++i) {
2600 // Check viewportMargins.
2601 const QRect viewportGeometry = mdiArea.viewport()->geometry();
2602 const int left = viewportGeometry.left();
2603 const int top = viewportGeometry.y();
2604 const int right = mdiArea.width() - viewportGeometry.width();
2605 const int bottom = mdiArea.height() - viewportGeometry.height();
2606
2607 const QSize sizeHint = tabBar->sizeHint();
2608
2609 if (hasLeftMargin)
2610 QCOMPARE(qApp->isLeftToRight() ? left : right, sizeHint.width());
2611 if (hasRightMargin)
2612 QCOMPARE(qApp->isLeftToRight() ? right : left, sizeHint.width());
2613 if (hasTopMargin || hasBottomMargin)
2614 QCOMPARE(hasTopMargin ? top : bottom, sizeHint.height());
2615
2616 // Check actual tab bar geometry.
2617 const QRegion expectedTabBarGeometry = QRegion(mdiArea.rect()).subtracted(r: viewportGeometry);
2618 QVERIFY(!expectedTabBarGeometry.isEmpty());
2619 QCOMPARE(QRegion(tabBar->geometry()), expectedTabBarGeometry);
2620
2621 if (i == 0)
2622 qApp->setLayoutDirection(originalLayoutDirection == Qt::LeftToRight ? Qt::RightToLeft : Qt::LeftToRight);
2623 qApp->processEvents();
2624 }
2625
2626 qApp->setLayoutDirection(originalLayoutDirection);
2627}
2628
2629void tst_QMdiArea::nativeSubWindows()
2630{
2631 const QString platformName = QGuiApplication::platformName();
2632 if (platformName != QLatin1String("xcb") && platformName != QLatin1String("windows"))
2633 QSKIP(qPrintable(QString::fromLatin1("nativeSubWindows() does not work on this platform (%1).").arg(platformName)));
2634#if defined(Q_OS_WIN) && !defined(QT_NO_OPENGL)
2635 if (QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGL)
2636 QSKIP("nativeSubWindows() does not work with ANGLE on Windows, QTBUG-28545.");
2637#endif
2638 { // Add native widgets after show.
2639 QMdiArea mdiArea;
2640 mdiArea.addSubWindow(widget: new QWidget);
2641 mdiArea.addSubWindow(widget: new QWidget);
2642 mdiArea.show();
2643 QVERIFY(QTest::qWaitForWindowExposed(&mdiArea));
2644
2645 // No native widgets.
2646 QVERIFY(!mdiArea.viewport()->internalWinId());
2647 foreach (QMdiSubWindow *subWindow, mdiArea.subWindowList())
2648 QVERIFY(!subWindow->internalWinId());
2649
2650 QWidget *nativeWidget = new QWidget;
2651 QVERIFY(nativeWidget->winId()); // enforce native window.
2652 QMdiSubWindow *subWin = mdiArea.addSubWindow(widget: nativeWidget);
2653 QVERIFY(subWin->internalWinId());
2654
2655 // The viewport and all the sub-windows must be native.
2656 QVERIFY(mdiArea.viewport()->internalWinId());
2657 foreach (QMdiSubWindow *subWindow, mdiArea.subWindowList())
2658 QVERIFY(subWindow->internalWinId());
2659
2660 // Add a non-native widget. This should become native.
2661 QMdiSubWindow *subWindow = new QMdiSubWindow;
2662 subWindow->setWidget(new QWidget);
2663 QVERIFY(!subWindow->internalWinId());
2664 mdiArea.addSubWindow(widget: subWindow);
2665 QVERIFY(subWindow->internalWinId());
2666 }
2667
2668 { // Add native widgets before show.
2669 QMdiArea mdiArea;
2670 mdiArea.addSubWindow(widget: new QWidget);
2671 QWidget *nativeWidget = new QWidget;
2672 (void)nativeWidget->winId();
2673 mdiArea.addSubWindow(widget: nativeWidget);
2674 mdiArea.show();
2675 QVERIFY(QTest::qWaitForWindowExposed(&mdiArea));
2676
2677 // The viewport and all the sub-windows must be native.
2678 QVERIFY(mdiArea.viewport()->internalWinId());
2679 foreach (QMdiSubWindow *subWindow, mdiArea.subWindowList())
2680 QVERIFY(subWindow->internalWinId());
2681 }
2682
2683 { // Make a sub-window native *after* it's added to the area.
2684 QMdiArea mdiArea;
2685 mdiArea.addSubWindow(widget: new QWidget);
2686 mdiArea.addSubWindow(widget: new QWidget);
2687 mdiArea.show();
2688 QVERIFY(QTest::qWaitForWindowExposed(&mdiArea));
2689
2690 QMdiSubWindow *nativeSubWindow = mdiArea.subWindowList().last();
2691 QVERIFY(!nativeSubWindow->internalWinId());
2692 (void)nativeSubWindow->winId();
2693
2694 // All the sub-windows should be native at this point
2695 QVERIFY(mdiArea.viewport()->internalWinId());
2696 foreach (QMdiSubWindow *subWindow, mdiArea.subWindowList())
2697 QVERIFY(subWindow->internalWinId());
2698 }
2699
2700#ifndef QT_NO_OPENGL
2701 {
2702 if (!QGLFormat::hasOpenGL())
2703 QSKIP("QGL not supported on this platform");
2704
2705 QMdiArea mdiArea;
2706 QGLWidget *glViewport = new QGLWidget;
2707 mdiArea.setViewport(glViewport);
2708 mdiArea.addSubWindow(widget: new QWidget);
2709 mdiArea.addSubWindow(widget: new QWidget);
2710 mdiArea.show();
2711 QVERIFY(QTest::qWaitForWindowExposed(&mdiArea));
2712
2713 const QGLContext *context = glViewport->context();
2714 if (!context || !context->isValid())
2715 QSKIP("QGL is broken, cannot continue test");
2716
2717 // The viewport and all the sub-windows must be native.
2718 QVERIFY(mdiArea.viewport()->internalWinId());
2719 foreach (QMdiSubWindow *subWindow, mdiArea.subWindowList())
2720 QVERIFY(subWindow->internalWinId());
2721 }
2722#endif
2723}
2724
2725void tst_QMdiArea::task_209615()
2726{
2727 QTabWidget tabWidget;
2728 QMdiArea *mdiArea1 = new QMdiArea;
2729 QMdiArea *mdiArea2 = new QMdiArea;
2730 QMdiSubWindow *subWindow = mdiArea1->addSubWindow(widget: new QLineEdit);
2731
2732 tabWidget.addTab(widget: mdiArea1, QLatin1String("1"));
2733 tabWidget.addTab(widget: mdiArea2, QLatin1String("2"));
2734 tabWidget.show();
2735
2736 mdiArea1->removeSubWindow(widget: subWindow);
2737 mdiArea2->addSubWindow(widget: subWindow);
2738
2739 // Please do not assert/crash.
2740 tabWidget.setCurrentIndex(1);
2741}
2742
2743void tst_QMdiArea::task_236750()
2744{
2745 QMdiArea mdiArea;
2746 QMdiSubWindow *subWindow = mdiArea.addSubWindow(widget: new QTextEdit);
2747 mdiArea.show();
2748
2749 subWindow->setWindowFlags(subWindow->windowFlags() | Qt::FramelessWindowHint);
2750 // Please do not crash (floating point exception).
2751 subWindow->showMinimized();
2752}
2753
2754// QTBUG-92240: When subwindows are maximized, their title is supposed to
2755// appear on the main window. When DontMaximizeSubWindowOnActivation was set,
2756// titles of previously created maximized windows interfered, resulting in
2757// "QTBUG-92240 - [1] - [2]".
2758void tst_QMdiArea::qtbug92240_title_data()
2759{
2760 QTest::addColumn<bool>(name: "dontMaximize");
2761 QTest::newRow(dataTag: "default") << false;
2762 QTest::newRow(dataTag: "dontMaximize") << true;
2763}
2764
2765void tst_QMdiArea::qtbug92240_title()
2766{
2767 QFETCH(bool, dontMaximize);
2768
2769#ifdef Q_OS_MACOS
2770 QSKIP("Not supported on macOS");
2771#endif
2772
2773 QMainWindow w;
2774 const QString title = QStringLiteral("QTBUG-92240");
2775 w.setWindowTitle(title);
2776 w.menuBar()->addMenu(QStringLiteral("File"));
2777 w.show();
2778
2779 auto *mdiArea = new QMdiArea;
2780 w.setCentralWidget(mdiArea);
2781 if (dontMaximize)
2782 mdiArea->setOption(option: QMdiArea::DontMaximizeSubWindowOnActivation);
2783 auto *sw1 = mdiArea->addSubWindow(widget: new QWidget);
2784 sw1->setWindowTitle(QStringLiteral("1"));
2785 sw1->showMaximized();
2786 QTRY_COMPARE(w.windowTitle(), QLatin1String("QTBUG-92240 - [1]"));
2787 auto *sw2 = mdiArea->addSubWindow(widget: new QWidget);
2788 sw2->setWindowTitle(QStringLiteral("2"));
2789 sw2->showMaximized();
2790 QTRY_COMPARE(w.windowTitle(), QLatin1String("QTBUG-92240 - [2]"));
2791}
2792
2793QTEST_MAIN(tst_QMdiArea)
2794#include "tst_qmdiarea.moc"
2795
2796

source code of qtbase/tests/auto/widgets/widgets/qmdiarea/tst_qmdiarea.cpp