Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qmdiarea.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4/*!
5 \class QMdiArea
6 \brief The QMdiArea widget provides an area in which MDI windows are displayed.
7 \since 4.3
8 \ingroup mainwindow-classes
9 \inmodule QtWidgets
10
11 QMdiArea functions, essentially, like a window manager for MDI
12 windows. For instance, it draws the windows it manages on itself
13 and arranges them in a cascading or tile pattern. QMdiArea is
14 commonly used as the center widget in a QMainWindow to create MDI
15 applications, but can also be placed in any layout. The following
16 code adds an area to a main window:
17
18 \snippet mdiarea/mdiareasnippets.cpp 0
19
20 Unlike the window managers for top-level windows, all window flags
21 (Qt::WindowFlags) are supported by QMdiArea as long as the flags
22 are supported by the current widget style.
23
24 Subwindows in QMdiArea are instances of QMdiSubWindow. They
25 are added to an MDI area with addSubWindow(). It is common to pass
26 a QWidget, which is set as the internal widget, to this function,
27 but it is also possible to pass a QMdiSubWindow directly. The class
28 inherits QWidget, and you can use the same API as with a normal
29 top-level window when programming. QMdiSubWindow also has behavior
30 that is specific to MDI windows. See the QMdiSubWindow class
31 description for more details.
32
33 A subwindow becomes active when it gets the keyboard focus, or
34 when setFocus() is called. The user activates a window by moving
35 focus in the usual ways. The MDI area emits the
36 subWindowActivated() signal when the active window changes, and
37 the activeSubWindow() function returns the active subwindow.
38
39 The convenience function subWindowList() returns a list of all
40 subwindows. This information could be used in a popup menu
41 containing a list of windows, for example.
42
43 The subwindows are sorted by the current
44 \l{QMdiArea::}{WindowOrder}. This is used for the subWindowList()
45 and for activateNextSubWindow() and activatePreviousSubWindow().
46 Also, it is used when cascading or tiling the windows with
47 cascadeSubWindows() and tileSubWindows().
48
49 QMdiArea provides two built-in layout strategies for
50 subwindows: cascadeSubWindows() and tileSubWindows(). Both are
51 slots and are easily connected to menu entries.
52
53 \table
54 \row \li \inlineimage mdi-cascade.png
55 \li \inlineimage mdi-tile.png
56 \endtable
57
58 \note The default scroll bar property for QMdiArea is Qt::ScrollBarAlwaysOff.
59
60 \sa QMdiSubWindow
61*/
62
63/*!
64 \fn void QMdiArea::subWindowActivated(QMdiSubWindow *window)
65
66 QMdiArea emits this signal after \a window has been activated. When \a
67 window is \nullptr, QMdiArea has just deactivated its last active window,
68 and there are no active windows on the workspace.
69
70 \sa QMdiArea::activeSubWindow()
71*/
72
73/*!
74 \enum QMdiArea::AreaOption
75
76 This enum describes options that customize the behavior of the
77 QMdiArea.
78
79 \value DontMaximizeSubWindowOnActivation When the active subwindow
80 is maximized, the default behavior is to maximize the next
81 subwindow that is activated. Set this option if you do not want
82 this behavior.
83*/
84
85/*!
86 \enum QMdiArea::WindowOrder
87
88 Specifies the criteria to use for ordering the list of child windows
89 returned by subWindowList(). The functions cascadeSubWindows() and
90 tileSubWindows() follow this order when arranging the windows.
91
92 \value CreationOrder The windows are returned in the order of
93 their creation.
94
95 \value StackingOrder The windows are returned in the order in
96 which they are stacked, with the top-most window being last in
97 the list.
98
99 \value ActivationHistoryOrder The windows are returned in the order in
100 which they were activated.
101
102 \sa subWindowList()
103*/
104
105/*!
106 \enum QMdiArea::ViewMode
107 \since 4.4
108
109 This enum describes the view mode of the area; i.e. how sub-windows
110 will be displayed.
111
112 \value SubWindowView Display sub-windows with window frames (default).
113 \value TabbedView Display sub-windows with tabs in a tab bar.
114
115 \sa setViewMode()
116*/
117
118#include "qmdiarea_p.h"
119
120#include <QApplication>
121#include <QStyle>
122#include <QChildEvent>
123#include <QResizeEvent>
124#include <QScrollBar>
125#include <QtAlgorithms>
126#include <QPainter>
127#include <QFontMetrics>
128#include <QStyleOption>
129#include <QDebug>
130#include <qmath.h>
131#if QT_CONFIG(menu)
132#include <qmenu.h>
133#endif
134#include <private/qlayoutengine_p.h>
135
136#include <algorithm>
137
138QT_BEGIN_NAMESPACE
139
140using namespace Qt::StringLiterals;
141using namespace QMdi;
142
143// Asserts in debug mode, gives warning otherwise.
144static bool sanityCheck(const QMdiSubWindow * const child, const char *where)
145{
146 if (Q_UNLIKELY(!child)) {
147 const char error[] = "null pointer";
148 Q_ASSERT_X(false, where, error);
149 qWarning("%s:%s", where, error);
150 return false;
151 }
152 return true;
153}
154
155static bool sanityCheck(const QList<QWidget *> &widgets, const int index, const char *where)
156{
157 if (Q_UNLIKELY(index < 0 || index >= widgets.size())) {
158 const char error[] = "index out of range";
159 Q_ASSERT_X(false, where, error);
160 qWarning("%s:%s", where, error);
161 return false;
162 }
163 if (Q_UNLIKELY(!widgets.at(index))) {
164 const char error[] = "null pointer";
165 Q_ASSERT_X(false, where, error);
166 qWarning("%s:%s", where, error);
167 return false;
168 }
169 return true;
170}
171
172static void setIndex(int *index, int candidate, int min, int max, bool isIncreasing)
173{
174 if (!index)
175 return;
176
177 if (isIncreasing) {
178 if (candidate > max)
179 *index = min;
180 else
181 *index = qMax(candidate, min);
182 } else {
183 if (candidate < min)
184 *index = max;
185 else
186 *index = qMin(candidate, max);
187 }
188 Q_ASSERT(*index >= min && *index <= max);
189}
190
191static inline bool useScrollBar(const QRect &childrenRect, const QSize &maxViewportSize,
192 Qt::Orientation orientation)
193{
194 if (orientation == Qt::Horizontal)
195 return childrenRect.width() > maxViewportSize.width()
196 || childrenRect.left() < 0
197 || childrenRect.right() >= maxViewportSize.width();
198 else
199 return childrenRect.height() > maxViewportSize.height()
200 || childrenRect.top() < 0
201 || childrenRect.bottom() >= maxViewportSize.height();
202}
203
204// Returns the closest mdi area containing the widget (if any).
205static inline QMdiArea *mdiAreaParent(QWidget *widget)
206{
207 if (!widget)
208 return nullptr;
209
210 QWidget *parent = widget->parentWidget();
211 while (parent) {
212 if (QMdiArea *area = qobject_cast<QMdiArea *>(parent))
213 return area;
214 parent = parent->parentWidget();
215 }
216 return nullptr;
217}
218
219#if QT_CONFIG(tabwidget)
220QTabBar::Shape _q_tb_tabBarShapeFrom(QTabWidget::TabShape shape, QTabWidget::TabPosition position);
221#endif // QT_CONFIG(tabwidget)
222
223static inline QString tabTextFor(QMdiSubWindow *subWindow)
224{
225 if (!subWindow)
226 return QString();
227
228 QString title = subWindow->windowTitle();
229 if (subWindow->isWindowModified()) {
230 title.replace("[*]"_L1, "*"_L1);
231 } else {
232 extern QString qt_setWindowTitle_helperHelper(const QString&, const QWidget*);
233 title = qt_setWindowTitle_helperHelper(title, subWindow);
234 }
235
236 return title.isEmpty() ? QMdiArea::tr("(Untitled)") : title;
237}
238
239/*!
240 \internal
241*/
242void RegularTiler::rearrange(QList<QWidget *> &widgets, const QRect &domain) const
243{
244 if (widgets.isEmpty())
245 return;
246
247 const int n = widgets.size();
248 const int ncols = qMax(qCeil(qSqrt(qreal(n))), 1);
249 const int nrows = qMax((n % ncols) ? (n / ncols + 1) : (n / ncols), 1);
250 const int nspecial = (n % ncols) ? (ncols - n % ncols) : 0;
251 const int dx = domain.width() / ncols;
252 const int dy = domain.height() / nrows;
253
254 int i = 0;
255 for (int row = 0; row < nrows; ++row) {
256 const int y1 = int(row * (dy + 1));
257 for (int col = 0; col < ncols; ++col) {
258 if (row == 1 && col < nspecial)
259 continue;
260 const int x1 = int(col * (dx + 1));
261 int x2 = int(x1 + dx);
262 int y2 = int(y1 + dy);
263 if (row == 0 && col < nspecial) {
264 y2 *= 2;
265 if (nrows != 2)
266 y2 += 1;
267 else
268 y2 = domain.bottom();
269 }
270 if (col == ncols - 1 && x2 != domain.right())
271 x2 = domain.right();
272 if (row == nrows - 1 && y2 != domain.bottom())
273 y2 = domain.bottom();
274 if (!sanityCheck(widgets, i, "RegularTiler"))
275 continue;
276 QWidget *widget = widgets.at(i++);
277 QRect newGeometry = QRect(QPoint(x1, y1), QPoint(x2, y2));
278 widget->setGeometry(QStyle::visualRect(widget->layoutDirection(), domain, newGeometry));
279 }
280 }
281}
282
283/*!
284 \internal
285*/
286void SimpleCascader::rearrange(QList<QWidget *> &widgets, const QRect &domain) const
287{
288 if (widgets.isEmpty())
289 return;
290
291 // Tunables:
292 const int topOffset = 0;
293 const int bottomOffset = 50;
294 const int leftOffset = 0;
295 const int rightOffset = 100;
296 const int dx = 10;
297
298 QStyleOptionTitleBar options;
299 options.initFrom(widgets.at(0));
300 int titleBarHeight = widgets.at(0)->style()->pixelMetric(QStyle::PM_TitleBarHeight, &options, widgets.at(0));
301 const QFontMetrics fontMetrics = QFontMetrics(QApplication::font("QMdiSubWindowTitleBar"));
302 const int dy = qMax(titleBarHeight - (titleBarHeight - fontMetrics.height()) / 2, 1)
303 + widgets.at(0)->style()->pixelMetric(QStyle::PM_FocusFrameVMargin, nullptr, widgets.at(0));
304
305 const int n = widgets.size();
306 const int nrows = qMax((domain.height() - (topOffset + bottomOffset)) / dy, 1);
307 const int ncols = qMax(n / nrows + ((n % nrows) ? 1 : 0), 1);
308 const int dcol = (domain.width() - (leftOffset + rightOffset)) / ncols;
309
310 int i = 0;
311 for (int row = 0; row < nrows; ++row) {
312 for (int col = 0; col < ncols; ++col) {
313 const int x = leftOffset + row * dx + col * dcol;
314 const int y = topOffset + row * dy;
315 if (!sanityCheck(widgets, i, "SimpleCascader"))
316 continue;
317 QWidget *widget = widgets.at(i++);
318 QRect newGeometry = QRect(QPoint(x, y), widget->sizeHint());
319 widget->setGeometry(QStyle::visualRect(widget->layoutDirection(), domain, newGeometry));
320 if (i == n)
321 return;
322 }
323 }
324}
325
326/*!
327 \internal
328*/
329void IconTiler::rearrange(QList<QWidget *> &widgets, const QRect &domain) const
330{
331 if (widgets.isEmpty() || !sanityCheck(widgets, 0, "IconTiler"))
332 return;
333
334 const int n = widgets.size();
335 const int width = qMax(widgets.at(0)->width(), 1);
336 const int height = widgets.at(0)->height();
337 const int ncols = qMax(domain.width() / width, 1);
338 const int nrows = n / ncols + ((n % ncols) ? 1 : 0);
339
340 int i = 0;
341 for (int row = 0; row < nrows; ++row) {
342 for (int col = 0; col < ncols; ++col) {
343 const int x = col * width;
344 const int y = domain.height() - height - row * height;
345 if (!sanityCheck(widgets, i, "IconTiler"))
346 continue;
347 QWidget *widget = widgets.at(i++);
348 QPoint newPos(x, y);
349 QRect newGeometry = QRect(newPos.x(), newPos.y(), widget->width(), widget->height());
350 widget->setGeometry(QStyle::visualRect(widget->layoutDirection(), domain, newGeometry));
351 if (i == n)
352 return;
353 }
354 }
355}
356
357/*!
358 \internal
359 Calculates the accumulated overlap (intersection area) between 'source' and 'rects'.
360*/
361int MinOverlapPlacer::accumulatedOverlap(const QRect &source, const QList<QRect> &rects)
362{
363 int accOverlap = 0;
364 for (const QRect &rect : rects) {
365 QRect intersection = source.intersected(rect);
366 accOverlap += intersection.width() * intersection.height();
367 }
368 return accOverlap;
369}
370
371
372/*!
373 \internal
374 Finds among 'source' the rectangle with the minimum accumulated overlap with the
375 rectangles in 'rects'.
376*/
377QRect MinOverlapPlacer::findMinOverlapRect(const QList<QRect> &source, const QList<QRect> &rects)
378{
379 int minAccOverlap = -1;
380 QRect minAccOverlapRect;
381 for (const QRect &srcRect : source) {
382 const int accOverlap = accumulatedOverlap(srcRect, rects);
383 if (accOverlap < minAccOverlap || minAccOverlap == -1) {
384 minAccOverlap = accOverlap;
385 minAccOverlapRect = srcRect;
386 }
387 }
388 return minAccOverlapRect;
389}
390
391/*!
392 \internal
393 Gets candidates for the final placement.
394*/
395QList<QRect> MinOverlapPlacer::getCandidatePlacements(const QSize &size, const QList<QRect> &rects,
396 const QRect &domain)
397{
398 QList<QRect> result;
399
400 QList<int> xlist;
401 xlist.reserve(2 + rects.size());
402 xlist << domain.left() << domain.right() - size.width() + 1;
403
404 QList<int> ylist;
405 ylist.reserve(2 + rects.size());
406 ylist << domain.top();
407 if (domain.bottom() - size.height() + 1 >= 0)
408 ylist << domain.bottom() - size.height() + 1;
409
410 for (const QRect &rect : rects) {
411 xlist << rect.right() + 1;
412 ylist << rect.bottom() + 1;
413 }
414
415 std::sort(xlist.begin(), xlist.end());
416 xlist.erase(std::unique(xlist.begin(), xlist.end()), xlist.end());
417
418 std::sort(ylist.begin(), ylist.end());
419 ylist.erase(std::unique(ylist.begin(), ylist.end()), ylist.end());
420
421 result.reserve(ylist.size() * xlist.size());
422 for (int y : std::as_const(ylist))
423 for (int x : std::as_const(xlist))
424 result << QRect(QPoint(x, y), size);
425 return result;
426}
427
428/*!
429 \internal
430 Finds all rectangles in 'source' not completely inside 'domain'. The result is stored
431 in 'result' and also removed from 'source'.
432*/
433QList<QRect> MinOverlapPlacer::findNonInsiders(const QRect &domain, QList<QRect> &source)
434{
435 const auto containedInDomain =
436 [domain](const QRect &srcRect) { return domain.contains(srcRect); };
437
438 const auto firstOut = std::stable_partition(source.begin(), source.end(), containedInDomain);
439
440 QList<QRect> result;
441 result.reserve(source.end() - firstOut);
442 std::copy(firstOut, source.end(), std::back_inserter(result));
443
444 source.erase(firstOut, source.end());
445
446 return result;
447}
448
449/*!
450 \internal
451 Finds all rectangles in 'source' that overlaps 'domain' by the maximum overlap area
452 between 'domain' and any rectangle in 'source'. The result is stored in 'result'.
453*/
454QList<QRect> MinOverlapPlacer::findMaxOverlappers(const QRect &domain, const QList<QRect> &source)
455{
456 QList<QRect> result;
457 result.reserve(source.size());
458
459 int maxOverlap = -1;
460 for (const QRect &srcRect : source) {
461 QRect intersection = domain.intersected(srcRect);
462 const int overlap = intersection.width() * intersection.height();
463 if (overlap >= maxOverlap || maxOverlap == -1) {
464 if (overlap > maxOverlap) {
465 maxOverlap = overlap;
466 result.clear();
467 }
468 result << srcRect;
469 }
470 }
471
472 return result;
473}
474
475/*!
476 \internal
477 Finds among the rectangles in 'source' the best placement. Here, 'best' means the
478 placement that overlaps the rectangles in 'rects' as little as possible while at the
479 same time being as much as possible inside 'domain'.
480*/
481QPoint MinOverlapPlacer::findBestPlacement(const QRect &domain, const QList<QRect> &rects,
482 QList<QRect> &source)
483{
484 const QList<QRect> nonInsiders = findNonInsiders(domain, source);
485
486 if (!source.empty())
487 return findMinOverlapRect(source, rects).topLeft();
488
489 QList<QRect> maxOverlappers = findMaxOverlappers(domain, nonInsiders);
490 return findMinOverlapRect(maxOverlappers, rects).topLeft();
491}
492
493
494/*!
495 \internal
496 Places the rectangle defined by 'size' relative to 'rects' and 'domain' so that it
497 overlaps 'rects' as little as possible and 'domain' as much as possible.
498 Returns the position of the resulting rectangle.
499*/
500QPoint MinOverlapPlacer::place(const QSize &size, const QList<QRect> &rects,
501 const QRect &domain) const
502{
503 if (size.isEmpty() || !domain.isValid())
504 return QPoint();
505 for (const QRect &rect : rects) {
506 if (!rect.isValid())
507 return QPoint();
508 }
509
510 QList<QRect> candidates = getCandidatePlacements(size, rects, domain);
511 return findBestPlacement(domain, rects, candidates);
512}
513
514#if QT_CONFIG(tabbar)
515class QMdiAreaTabBar : public QTabBar
516{
517public:
518 QMdiAreaTabBar(QWidget *parent) : QTabBar(parent) {}
519
520protected:
521 void mousePressEvent(QMouseEvent *event) override;
522#ifndef QT_NO_CONTEXTMENU
523 void contextMenuEvent(QContextMenuEvent *event) override;
524#endif
525
526private:
527 QMdiSubWindow *subWindowFromIndex(int index) const;
528};
529
530/*!
531 \internal
532*/
533void QMdiAreaTabBar::mousePressEvent(QMouseEvent *event)
534{
535 if (event->button() != Qt::MiddleButton) {
536 QTabBar::mousePressEvent(event);
537 return;
538 }
539
540 QMdiSubWindow *subWindow = subWindowFromIndex(tabAt(event->position().toPoint()));
541 if (!subWindow) {
542 event->ignore();
543 return;
544 }
545
546 subWindow->close();
547}
548
549#ifndef QT_NO_CONTEXTMENU
550/*!
551 \internal
552*/
553void QMdiAreaTabBar::contextMenuEvent(QContextMenuEvent *event)
554{
555 QPointer<QMdiSubWindow> subWindow = subWindowFromIndex(tabAt(event->pos()));
556 if (!subWindow || subWindow->isHidden()) {
557 event->ignore();
558 return;
559 }
560
561#if QT_CONFIG(menu)
562 QMdiSubWindowPrivate *subWindowPrivate = subWindow->d_func();
563 if (!subWindowPrivate->systemMenu) {
564 event->ignore();
565 return;
566 }
567
568 QMdiSubWindow *currentSubWindow = subWindowFromIndex(currentIndex());
569 Q_ASSERT(currentSubWindow);
570
571 // We don't want these actions to show up in the system menu when the
572 // current sub-window is maximized, i.e. covers the entire viewport.
573 if (currentSubWindow->isMaximized()) {
574 subWindowPrivate->setVisible(QMdiSubWindowPrivate::MoveAction, false);
575 subWindowPrivate->setVisible(QMdiSubWindowPrivate::ResizeAction, false);
576 subWindowPrivate->setVisible(QMdiSubWindowPrivate::MinimizeAction, false);
577 subWindowPrivate->setVisible(QMdiSubWindowPrivate::MaximizeAction, false);
578 subWindowPrivate->setVisible(QMdiSubWindowPrivate::RestoreAction, false);
579 subWindowPrivate->setVisible(QMdiSubWindowPrivate::StayOnTopAction, false);
580 }
581
582 // Show system menu.
583 subWindowPrivate->systemMenu->exec(event->globalPos());
584 if (!subWindow)
585 return;
586
587 // Restore action visibility.
588 subWindowPrivate->updateActions();
589#endif // QT_CONFIG(menu)
590}
591#endif // QT_NO_CONTEXTMENU
592
593/*!
594 \internal
595*/
596QMdiSubWindow *QMdiAreaTabBar::subWindowFromIndex(int index) const
597{
598 if (index < 0 || index >= count())
599 return nullptr;
600
601 QMdiArea *mdiArea = qobject_cast<QMdiArea *>(parentWidget());
602 Q_ASSERT(mdiArea);
603
604 const QList<QMdiSubWindow *> subWindows = mdiArea->subWindowList();
605 Q_ASSERT(index < subWindows.size());
606
607 QMdiSubWindow *subWindow = mdiArea->subWindowList().at(index);
608 Q_ASSERT(subWindow);
609
610 return subWindow;
611}
612#endif // QT_CONFIG(tabbar)
613
614/*!
615 \internal
616*/
617QMdiAreaPrivate::QMdiAreaPrivate()
618 : cascader(nullptr),
619 regularTiler(nullptr),
620 iconTiler(nullptr),
621 placer(nullptr),
622#if QT_CONFIG(rubberband)
623 rubberBand(nullptr),
624#endif
625#if QT_CONFIG(tabbar)
626 tabBar(nullptr),
627#endif
628 activationOrder(QMdiArea::CreationOrder),
629 viewMode(QMdiArea::SubWindowView),
630#if QT_CONFIG(tabbar)
631 documentMode(false),
632 tabsClosable(false),
633 tabsMovable(false),
634#endif
635#if QT_CONFIG(tabwidget)
636 tabShape(QTabWidget::Rounded),
637 tabPosition(QTabWidget::North),
638#endif
641 isActivated(false),
642 isSubWindowsTiled(false),
645 updatesDisabledByUs(false),
646 inViewModeChange(false),
651{
652}
653
654/*!
655 \internal
656*/
657void QMdiAreaPrivate::_q_deactivateAllWindows(QMdiSubWindow *aboutToActivate)
658{
660 return;
661
662 Q_Q(QMdiArea);
663 if (!aboutToActivate)
664 aboutToBecomeActive = qobject_cast<QMdiSubWindow *>(q->sender());
665 else
666 aboutToBecomeActive = aboutToActivate;
667 Q_ASSERT(aboutToBecomeActive);
668
669 // Take a copy because child->showNormal() could indirectly call
670 // QCoreApplication::sendEvent(), which could call unknown code that e.g.
671 // recurses into the class modifying childWindows.
672 const auto subWindows = childWindows;
673 for (QMdiSubWindow *child : subWindows) {
674 if (!sanityCheck(child, "QMdiArea::deactivateAllWindows") || aboutToBecomeActive == child)
675 continue;
676 // We don't want to handle signals caused by child->showNormal().
677 ignoreWindowStateChange = true;
678 if (!(options & QMdiArea::DontMaximizeSubWindowOnActivation) && !showActiveWindowMaximized)
679 showActiveWindowMaximized = child->isMaximized() && child->isVisible();
680 if (showActiveWindowMaximized && child->isMaximized()) {
681 if (q->updatesEnabled()) {
682 updatesDisabledByUs = true;
683 q->setUpdatesEnabled(false);
684 }
685 child->showNormal();
686 }
687 if (child->isMinimized() && !child->isShaded() && !windowStaysOnTop(child))
688 child->lower();
689 ignoreWindowStateChange = false;
690 child->d_func()->setActive(false);
691 }
692}
693
694/*!
695 \internal
696*/
697void QMdiAreaPrivate::_q_processWindowStateChanged(Qt::WindowStates oldState,
698 Qt::WindowStates newState)
699{
701 return;
702
703 Q_Q(QMdiArea);
704 QMdiSubWindow *child = qobject_cast<QMdiSubWindow *>(q->sender());
705 if (!child)
706 return;
707
708 // windowActivated
709 if (!(oldState & Qt::WindowActive) && (newState & Qt::WindowActive))
711 // windowDeactivated
712 else if ((oldState & Qt::WindowActive) && !(newState & Qt::WindowActive))
714
715 // windowMinimized
716 if (!(oldState & Qt::WindowMinimized) && (newState & Qt::WindowMinimized)) {
717 isSubWindowsTiled = false;
719 // windowMaximized
720 } else if (!(oldState & Qt::WindowMaximized) && (newState & Qt::WindowMaximized)) {
721 internalRaise(child);
722 // windowRestored
723 } else if (!(newState & (Qt::WindowMaximized | Qt::WindowMinimized))) {
724 internalRaise(child);
725 if (oldState & Qt::WindowMinimized)
727 }
728}
729
731{
732#if !QT_CONFIG(tabbar)
733 Q_UNUSED(index);
734#else
735 if (!tabBar || index < 0)
736 return;
737
738 // If the previous active sub-window was hidden, disable the tab.
739 if (indexToLastActiveTab >= 0 && indexToLastActiveTab < tabBar->count()
740 && indexToLastActiveTab < childWindows.size()) {
741 QMdiSubWindow *lastActive = childWindows.at(indexToLastActiveTab);
742 if (lastActive && lastActive->isHidden())
743 tabBar->setTabEnabled(indexToLastActiveTab, false);
744 }
745
746 indexToLastActiveTab = index;
747 Q_ASSERT(childWindows.size() > index);
748 QMdiSubWindow *subWindow = childWindows.at(index);
749 Q_ASSERT(subWindow);
750 activateWindow(subWindow);
751#endif // QT_CONFIG(tabbar)
752}
753
755{
756#if !QT_CONFIG(tabbar)
757 Q_UNUSED(index);
758#else
759 QMdiSubWindow *subWindow = childWindows.at(index);
760 Q_ASSERT(subWindow);
761 subWindow->close();
762#endif // QT_CONFIG(tabbar)
763}
764
765void QMdiAreaPrivate::_q_moveTab(int from, int to)
766{
767#if !QT_CONFIG(tabbar)
768 Q_UNUSED(from);
769 Q_UNUSED(to);
770#else
771 childWindows.move(from, to);
772
773 // Put the active window in front to update activation order.
774 const int indexToActiveWindow = childWindows.indexOf(active);
775 if (indexToActiveWindow != -1) {
776 const int index = indicesToActivatedChildren.indexOf(indexToActiveWindow);
777 Q_ASSERT(index != -1);
778 if (index != 0) { // if it's not in front
779 indicesToActivatedChildren.move(index, 0);
780 internalRaise(active);
781 }
782 }
783#endif // QT_CONFIG(tabbar)
784}
785
786/*!
787 \internal
788*/
789void QMdiAreaPrivate::appendChild(QMdiSubWindow *child)
790{
791 Q_Q(QMdiArea);
792 Q_ASSERT(child && childWindows.indexOf(child) == -1);
793
794 if (child->parent() != viewport)
795 child->setParent(viewport, child->windowFlags());
796 childWindows.append(QPointer<QMdiSubWindow>(child));
797
798 if (!child->testAttribute(Qt::WA_Resized) && q->isVisible()) {
799 QSize newSize(child->sizeHint().boundedTo(viewport->size()));
800 child->resize(newSize.expandedTo(qSmartMinSize(child)));
801 }
802
803 if (!placer)
805 place(placer, child);
806
807 if (hbarpolicy != Qt::ScrollBarAlwaysOff)
808 child->setOption(QMdiSubWindow::AllowOutsideAreaHorizontally, true);
809 else
810 child->setOption(QMdiSubWindow::AllowOutsideAreaHorizontally, false);
811
812 if (vbarpolicy != Qt::ScrollBarAlwaysOff)
813 child->setOption(QMdiSubWindow::AllowOutsideAreaVertically, true);
814 else
815 child->setOption(QMdiSubWindow::AllowOutsideAreaVertically, false);
816
817 internalRaise(child);
818 indicesToActivatedChildren.prepend(childWindows.size() - 1);
819 Q_ASSERT(indicesToActivatedChildren.size() == childWindows.size());
820
821#if QT_CONFIG(tabbar)
822 if (tabBar) {
823 tabBar->addTab(child->windowIcon(), tabTextFor(child));
824 updateTabBarGeometry();
825 if (childWindows.size() == 1 && !(options & QMdiArea::DontMaximizeSubWindowOnActivation))
826 showActiveWindowMaximized = true;
827 }
828#endif
829
830 if (!(child->windowFlags() & Qt::SubWindow))
831 child->setWindowFlags(Qt::SubWindow);
832 child->installEventFilter(q);
833
834 QObject::connect(child, SIGNAL(aboutToActivate()), q, SLOT(_q_deactivateAllWindows()));
835 QObject::connect(child, SIGNAL(windowStateChanged(Qt::WindowStates,Qt::WindowStates)),
836 q, SLOT(_q_processWindowStateChanged(Qt::WindowStates,Qt::WindowStates)));
837}
838
839/*!
840 \internal
841*/
842void QMdiAreaPrivate::place(Placer *placer, QMdiSubWindow *child)
843{
844 if (!placer || !child)
845 return;
846
847 Q_Q(QMdiArea);
848 if (!q->isVisible()) {
849 // The window is only laid out when it's added to QMdiArea,
850 // so there's no need to check that we don't have it in the
851 // list already. appendChild() ensures that.
852 pendingPlacements.append(child);
853 return;
854 }
855
856 QList<QRect> rects;
857 rects.reserve(childWindows.size());
858 QRect parentRect = q->rect();
859 for (QMdiSubWindow *window : std::as_const(childWindows)) {
860 if (!sanityCheck(window, "QMdiArea::place") || window == child || !window->isVisibleTo(q)
861 || !window->testAttribute(Qt::WA_Moved)) {
862 continue;
863 }
864 QRect occupiedGeometry;
865 if (window->isMaximized()) {
866 occupiedGeometry = QRect(window->d_func()->oldGeometry.topLeft(),
867 window->d_func()->restoreSize);
868 } else {
869 occupiedGeometry = window->geometry();
870 }
871 rects.append(QStyle::visualRect(child->layoutDirection(), parentRect, occupiedGeometry));
872 }
873 QPoint newPos = placer->place(child->size(), rects, parentRect);
874 QRect newGeometry = QRect(newPos.x(), newPos.y(), child->width(), child->height());
875 child->setGeometry(QStyle::visualRect(child->layoutDirection(), parentRect, newGeometry));
876}
877
878/*!
879 \internal
880*/
882{
883 if (!rearranger)
884 return;
885
886 Q_Q(QMdiArea);
887 if (!q->isVisible()) {
888 // Compress if we already have the rearranger in the list.
889 int index = pendingRearrangements.indexOf(rearranger);
890 if (index != -1)
891 pendingRearrangements.move(index, pendingRearrangements.size() - 1);
892 else
893 pendingRearrangements.append(rearranger);
894 return;
895 }
896
897 QList<QWidget *> widgets;
898 const bool reverseList = rearranger->type() == Rearranger::RegularTiler;
899 const QList<QMdiSubWindow *> subWindows = subWindowList(activationOrder, reverseList);
900 QSize minSubWindowSize;
901 for (QMdiSubWindow *child : subWindows) {
902 if (!sanityCheck(child, "QMdiArea::rearrange") || !child->isVisible())
903 continue;
904 if (rearranger->type() == Rearranger::IconTiler) {
905 if (child->isMinimized() && !child->isShaded())
906 widgets.append(child);
907 } else {
908 if (child->isMinimized() && !child->isShaded())
909 continue;
910 if (child->isMaximized() || child->isShaded())
911 child->showNormal();
912 minSubWindowSize = minSubWindowSize.expandedTo(child->minimumSize())
913 .expandedTo(child->d_func()->internalMinimumSize);
914 widgets.append(child);
915 }
916 }
917
918 QRect domain = viewport->rect();
919 if (rearranger->type() == Rearranger::RegularTiler && !widgets.isEmpty())
920 domain = resizeToMinimumTileSize(minSubWindowSize, widgets.size());
921
922 rearranger->rearrange(widgets, domain);
923
924 if (rearranger->type() == Rearranger::RegularTiler && !widgets.isEmpty()) {
925 isSubWindowsTiled = true;
927 } else if (rearranger->type() == Rearranger::SimpleCascader) {
928 isSubWindowsTiled = false;
929 }
930}
931
932/*!
933 \internal
934
935 Arranges all minimized windows at the bottom of the workspace.
936*/
938{
939 if (!iconTiler)
940 iconTiler = new IconTiler;
942}
943
944/*!
945 \internal
946*/
947void QMdiAreaPrivate::activateWindow(QMdiSubWindow *child)
948{
949 if (childWindows.isEmpty()) {
950 Q_ASSERT(!child);
951 Q_ASSERT(!active);
952 return;
953 }
954
955 if (!child) {
956 if (active) {
957 Q_ASSERT(active->d_func()->isActive);
958 active->d_func()->setActive(false);
960 }
961 return;
962 }
963
964 if (child->isHidden() || child == active)
965 return;
966
967 if (child->d_func()->isActive && active == nullptr)
968 child->d_func()->isActive = false;
969
970 child->d_func()->setActive(true);
971}
972
973/*!
974 \internal
975*/
977{
978 QMdiSubWindow *current = q_func()->currentSubWindow();
979 if (current && !isExplicitlyDeactivated(current)) {
980 current->d_func()->activationEnabled = true;
981 current->d_func()->setActive(true, /*changeFocus=*/false);
982 }
983}
984
986{
987 if (indexToHighlighted < 0)
988 return;
989
990 Q_ASSERT(indexToHighlighted < childWindows.size());
991 if (tabToPreviousTimer.isActive())
992 activateWindow(nextVisibleSubWindow(-1, QMdiArea::ActivationHistoryOrder));
993 else
994 activateWindow(childWindows.at(indexToHighlighted));
995#if QT_CONFIG(rubberband)
996 hideRubberBand();
997#endif
998}
999
1000/*!
1001 \internal
1002*/
1003void QMdiAreaPrivate::emitWindowActivated(QMdiSubWindow *activeWindow)
1004{
1005 Q_Q(QMdiArea);
1006 Q_ASSERT(activeWindow);
1007 if (activeWindow == active)
1008 return;
1009 Q_ASSERT(activeWindow->d_func()->isActive);
1010
1011 if (!aboutToBecomeActive)
1012 _q_deactivateAllWindows(activeWindow);
1013 Q_ASSERT(aboutToBecomeActive);
1014
1015 // This is true only if 'DontMaximizeSubWindowOnActivation' is disabled
1016 // and the previous active window was maximized.
1018 if (!activeWindow->isMaximized())
1019 activeWindow->showMaximized();
1021 }
1022
1023 // Put in front to update activation order.
1024 const int indexToActiveWindow = childWindows.indexOf(activeWindow);
1025 Q_ASSERT(indexToActiveWindow != -1);
1026 const int index = indicesToActivatedChildren.indexOf(indexToActiveWindow);
1027 Q_ASSERT(index != -1);
1028 indicesToActivatedChildren.move(index, 0);
1029 internalRaise(activeWindow);
1030
1031 if (updatesDisabledByUs) {
1032 q->setUpdatesEnabled(true);
1033 updatesDisabledByUs = false;
1034 }
1035
1036 Q_ASSERT(aboutToBecomeActive == activeWindow);
1037 active = activeWindow;
1038 aboutToBecomeActive = nullptr;
1039 Q_ASSERT(active->d_func()->isActive);
1040
1041#if QT_CONFIG(tabbar)
1042 if (tabBar && tabBar->currentIndex() != indexToActiveWindow)
1043 tabBar->setCurrentIndex(indexToActiveWindow);
1044#endif
1045
1046 if (active->isMaximized() && scrollBarsEnabled())
1048
1049 emit q->subWindowActivated(active);
1050}
1051
1052/*!
1053 \internal
1054*/
1055void QMdiAreaPrivate::resetActiveWindow(QMdiSubWindow *deactivatedWindow)
1056{
1057 Q_Q(QMdiArea);
1058 if (deactivatedWindow) {
1059 if (deactivatedWindow != active)
1060 return;
1061 active = nullptr;
1062 if ((aboutToBecomeActive || isActivated || lastWindowAboutToBeDestroyed())
1063 && !isExplicitlyDeactivated(deactivatedWindow) && !q->window()->isMinimized()) {
1064 return;
1065 }
1066 emit q->subWindowActivated(nullptr);
1067 return;
1068 }
1069
1070 if (aboutToBecomeActive)
1071 return;
1072
1073 active = nullptr;
1074 emit q->subWindowActivated(nullptr);
1075}
1076
1077/*!
1078 \internal
1079*/
1080void QMdiAreaPrivate::updateActiveWindow(int removedIndex, bool activeRemoved)
1081{
1082 Q_ASSERT(indicesToActivatedChildren.size() == childWindows.size());
1083
1084 // Update indices list first so that we don't rely
1085 for (int i = 0; i < indicesToActivatedChildren.size(); ++i) {
1086 int &index = indicesToActivatedChildren[i];
1087 if (index > removedIndex)
1088 --index;
1089 }
1090
1091#if QT_CONFIG(tabbar)
1092 if (tabBar && removedIndex >= 0) {
1093 const QSignalBlocker blocker(tabBar);
1094 tabBar->removeTab(removedIndex);
1095 updateTabBarGeometry();
1096 }
1097#endif
1098
1099 if (childWindows.isEmpty()) {
1102 return;
1103 }
1104
1105 if (indexToHighlighted >= 0) {
1106#if QT_CONFIG(rubberband)
1107 // Hide rubber band if highlighted window is removed.
1108 if (indexToHighlighted == removedIndex)
1109 hideRubberBand();
1110 else
1111#endif
1112 // or update index if necessary.
1113 if (indexToHighlighted > removedIndex)
1115 }
1116
1117 if (!activeRemoved)
1118 return;
1119
1120 // Activate next window.
1121 QMdiSubWindow *next = nextVisibleSubWindow(0, activationOrder, removedIndex);
1122 if (next)
1123 activateWindow(next);
1124}
1125
1126/*!
1127 \internal
1128*/
1130{
1132 return;
1133
1134 Q_Q(QMdiArea);
1135 QSize maxSize = q->maximumViewportSize();
1136 QSize hbarExtent = hbar->sizeHint();
1137 QSize vbarExtent = vbar->sizeHint();
1138
1139 if (q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, nullptr, q)) {
1140 const int doubleFrameWidth = frameWidth * 2;
1141 if (hbarpolicy == Qt::ScrollBarAlwaysOn)
1142 maxSize.rheight() -= doubleFrameWidth;
1143 if (vbarpolicy == Qt::ScrollBarAlwaysOn)
1144 maxSize.rwidth() -= doubleFrameWidth;
1145 hbarExtent.rheight() += doubleFrameWidth;
1146 vbarExtent.rwidth() += doubleFrameWidth;
1147 }
1148
1149 const QRect childrenRect = active && active->isMaximized()
1150 ? active->geometry() : viewport->childrenRect();
1151 bool useHorizontalScrollBar = useScrollBar(childrenRect, maxSize, Qt::Horizontal);
1152 bool useVerticalScrollBar = useScrollBar(childrenRect, maxSize, Qt::Vertical);
1153
1154 if (useHorizontalScrollBar && !useVerticalScrollBar) {
1155 const QSize max = maxSize - QSize(0, hbarExtent.height());
1156 useVerticalScrollBar = useScrollBar(childrenRect, max, Qt::Vertical);
1157 }
1158
1159 if (useVerticalScrollBar && !useHorizontalScrollBar) {
1160 const QSize max = maxSize - QSize(vbarExtent.width(), 0);
1161 useHorizontalScrollBar = useScrollBar(childrenRect, max, Qt::Horizontal);
1162 }
1163
1164 if (useHorizontalScrollBar && hbarpolicy != Qt::ScrollBarAlwaysOn)
1165 maxSize.rheight() -= hbarExtent.height();
1166 if (useVerticalScrollBar && vbarpolicy != Qt::ScrollBarAlwaysOn)
1167 maxSize.rwidth() -= vbarExtent.width();
1168
1169 QRect viewportRect(QPoint(0, 0), maxSize);
1170 const int startX = q->isLeftToRight() ? childrenRect.left() : viewportRect.right()
1171 - childrenRect.right();
1172
1173 // Horizontal scroll bar.
1174 if (isSubWindowsTiled && hbar->value() != 0)
1175 hbar->setValue(0);
1176 const int xOffset = startX + hbar->value();
1177 hbar->setRange(qMin(0, xOffset),
1178 qMax(0, xOffset + childrenRect.width() - viewportRect.width()));
1179 hbar->setPageStep(childrenRect.width());
1180 hbar->setSingleStep(childrenRect.width() / 20);
1181
1182 // Vertical scroll bar.
1183 if (isSubWindowsTiled && vbar->value() != 0)
1184 vbar->setValue(0);
1185 const int yOffset = childrenRect.top() + vbar->value();
1186 vbar->setRange(qMin(0, yOffset),
1187 qMax(0, yOffset + childrenRect.height() - viewportRect.height()));
1188 vbar->setPageStep(childrenRect.height());
1189 vbar->setSingleStep(childrenRect.height() / 20);
1190}
1191
1192/*!
1193 \internal
1194*/
1195void QMdiAreaPrivate::internalRaise(QMdiSubWindow *mdiChild) const
1196{
1197 if (!sanityCheck(mdiChild, "QMdiArea::internalRaise") || childWindows.size() < 2)
1198 return;
1199
1200 QMdiSubWindow *stackUnderChild = nullptr;
1201 if (!windowStaysOnTop(mdiChild)) {
1202 const auto children = viewport->children(); // take a copy, as raising/stacking under changes the order
1203 for (QObject *object : children) {
1204 QMdiSubWindow *child = qobject_cast<QMdiSubWindow *>(object);
1205 if (!child || !childWindows.contains(child))
1206 continue;
1207 if (!child->isHidden() && windowStaysOnTop(child)) {
1208 if (stackUnderChild)
1209 child->stackUnder(stackUnderChild);
1210 else
1211 child->raise();
1212 stackUnderChild = child;
1213 }
1214 }
1215 }
1216
1217 if (stackUnderChild)
1218 mdiChild->stackUnder(stackUnderChild);
1219 else
1220 mdiChild->raise();
1221}
1222
1223QRect QMdiAreaPrivate::resizeToMinimumTileSize(const QSize &minSubWindowSize, int subWindowCount)
1224{
1225 Q_Q(QMdiArea);
1226 if (!minSubWindowSize.isValid() || subWindowCount <= 0)
1227 return viewport->rect();
1228
1229 // Calculate minimum size.
1230 const int columns = qMax(qCeil(qSqrt(qreal(subWindowCount))), 1);
1231 const int rows = qMax((subWindowCount % columns) ? (subWindowCount / columns + 1)
1232 : (subWindowCount / columns), 1);
1233 const int minWidth = minSubWindowSize.width() * columns;
1234 const int minHeight = minSubWindowSize.height() * rows;
1235
1236 // Increase area size if necessary. Scroll bars are provided if we're not able
1237 // to resize to the minimum size.
1239 QWidget *topLevel = q;
1240 // Find the topLevel for this area, either a real top-level or a sub-window.
1241 while (topLevel && !topLevel->isWindow() && topLevel->windowType() != Qt::SubWindow)
1242 topLevel = topLevel->parentWidget();
1243 // We don't want sub-subwindows to be placed at the edge, thus add 2 pixels.
1244 int minAreaWidth = minWidth + left + right + 2;
1245 int minAreaHeight = minHeight + top + bottom + 2;
1246 if (hbar->isVisible())
1247 minAreaHeight += hbar->height();
1248 if (vbar->isVisible())
1249 minAreaWidth += vbar->width();
1250 if (q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, nullptr, q)) {
1251 const int frame = q->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, q);
1252 minAreaWidth += 2 * frame;
1253 minAreaHeight += 2 * frame;
1254 }
1255 const QSize diff = QSize(minAreaWidth, minAreaHeight).expandedTo(q->size()) - q->size();
1256 // Only resize topLevel widget if scroll bars are disabled.
1257 if (hbarpolicy == Qt::ScrollBarAlwaysOff)
1258 topLevel->resize(topLevel->size().width() + diff.width(), topLevel->size().height());
1259 if (vbarpolicy == Qt::ScrollBarAlwaysOff)
1260 topLevel->resize(topLevel->size().width(), topLevel->size().height() + diff.height());
1261 }
1262
1263 QRect domain = viewport->rect();
1264
1265 // Adjust domain width and provide horizontal scroll bar.
1266 if (domain.width() < minWidth) {
1267 domain.setWidth(minWidth);
1268 if (hbarpolicy == Qt::ScrollBarAlwaysOff)
1269 q->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
1270 else
1271 hbar->setValue(0);
1272 }
1273 // Adjust domain height and provide vertical scroll bar.
1274 if (domain.height() < minHeight) {
1275 domain.setHeight(minHeight);
1276 if (vbarpolicy == Qt::ScrollBarAlwaysOff)
1277 q->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
1278 else
1279 vbar->setValue(0);
1280 }
1281 return domain;
1282}
1283
1284/*!
1285 \internal
1286*/
1288{
1289 return hbarpolicy != Qt::ScrollBarAlwaysOff || vbarpolicy != Qt::ScrollBarAlwaysOff;
1290}
1291
1292/*!
1293 \internal
1294*/
1296{
1297 if (childWindows.size() != 1)
1298 return false;
1299
1300 QMdiSubWindow *last = childWindows.at(0);
1301 if (!last)
1302 return true;
1303
1304 if (!last->testAttribute(Qt::WA_DeleteOnClose))
1305 return false;
1306
1307 return last->d_func()->data.is_closing;
1308}
1309
1310/*!
1311 \internal
1312*/
1313void QMdiAreaPrivate::setChildActivationEnabled(bool enable, bool onlyNextActivationEvent) const
1314{
1315 for (QMdiSubWindow *subWindow : childWindows) {
1316 if (!subWindow || !subWindow->isVisible())
1317 continue;
1318 if (onlyNextActivationEvent)
1319 subWindow->d_func()->ignoreNextActivationEvent = !enable;
1320 else
1321 subWindow->d_func()->activationEnabled = enable;
1322 }
1323}
1324
1325/*!
1326 \internal
1327 \reimp
1328*/
1329void QMdiAreaPrivate::scrollBarPolicyChanged(Qt::Orientation orientation, Qt::ScrollBarPolicy policy)
1330{
1331 if (childWindows.isEmpty())
1332 return;
1333
1334 const QMdiSubWindow::SubWindowOption option = orientation == Qt::Horizontal ?
1335 QMdiSubWindow::AllowOutsideAreaHorizontally : QMdiSubWindow::AllowOutsideAreaVertically;
1336 const bool enable = policy != Qt::ScrollBarAlwaysOff;
1337 // Take a copy because child->setOption() may indirectly call QCoreApplication::sendEvent(),
1338 // the latter could call unknown code that could e.g. recurse into the class
1339 // modifying childWindows.
1340 const auto subWindows = childWindows;
1341 for (QMdiSubWindow *child : subWindows) {
1342 if (!sanityCheck(child, "QMdiArea::scrollBarPolicyChanged"))
1343 continue;
1344 child->setOption(option, enable);
1345 }
1347}
1348
1350QMdiAreaPrivate::subWindowList(QMdiArea::WindowOrder order, bool reversed) const
1351{
1352 QList<QMdiSubWindow *> list;
1353 if (childWindows.isEmpty())
1354 return list;
1355
1356 if (order == QMdiArea::CreationOrder) {
1357 for (QMdiSubWindow *child : childWindows) {
1358 if (!child)
1359 continue;
1360 if (!reversed)
1361 list.append(child);
1362 else
1363 list.prepend(child);
1364 }
1365 } else if (order == QMdiArea::StackingOrder) {
1366 for (QObject *object : viewport->children()) {
1367 QMdiSubWindow *child = qobject_cast<QMdiSubWindow *>(object);
1368 if (!child || !childWindows.contains(child))
1369 continue;
1370 if (!reversed)
1371 list.append(child);
1372 else
1373 list.prepend(child);
1374 }
1375 } else { // ActivationHistoryOrder
1376 Q_ASSERT(indicesToActivatedChildren.size() == childWindows.size());
1377 for (int i = indicesToActivatedChildren.size() - 1; i >= 0; --i) {
1378 QMdiSubWindow *child = childWindows.at(indicesToActivatedChildren.at(i));
1379 if (!child)
1380 continue;
1381 if (!reversed)
1382 list.append(child);
1383 else
1384 list.prepend(child);
1385 }
1386 }
1387 return list;
1388}
1389
1390/*!
1391 \internal
1392*/
1393void QMdiAreaPrivate::disconnectSubWindow(QObject *subWindow)
1394{
1395 if (!subWindow)
1396 return;
1397
1398 Q_Q(QMdiArea);
1399 QObject::disconnect(subWindow, nullptr, q, nullptr);
1400 subWindow->removeEventFilter(q);
1401}
1402
1403/*!
1404 \internal
1405*/
1406QMdiSubWindow *QMdiAreaPrivate::nextVisibleSubWindow(int increaseFactor, QMdiArea::WindowOrder order,
1407 int removedIndex, int fromIndex) const
1408{
1409 if (childWindows.isEmpty())
1410 return nullptr;
1411
1412 Q_Q(const QMdiArea);
1413 const QList<QMdiSubWindow *> subWindows = q->subWindowList(order);
1414 QMdiSubWindow *current = nullptr;
1415
1416 if (removedIndex < 0) {
1417 if (fromIndex >= 0 && fromIndex < subWindows.size())
1418 current = childWindows.at(fromIndex);
1419 else
1420 current = q->currentSubWindow();
1421 }
1422
1423 // There's no current sub-window (removed or deactivated),
1424 // so we have to pick the last active or the next in creation order.
1425 if (!current) {
1426 if (removedIndex >= 0 && order == QMdiArea::CreationOrder) {
1427 int candidateIndex = -1;
1428 setIndex(&candidateIndex, removedIndex, 0, subWindows.size() - 1, true);
1429 current = childWindows.at(candidateIndex);
1430 } else {
1431 current = subWindows.back();
1432 }
1433 }
1434 Q_ASSERT(current);
1435
1436 // Find the index for the current sub-window in the given activation order
1437 const int indexToCurrent = subWindows.indexOf(current);
1438 const bool increasing = increaseFactor > 0;
1439
1440 // and use that index + increseFactor as a candidate.
1441 int index = -1;
1442 setIndex(&index, indexToCurrent + increaseFactor, 0, subWindows.size() - 1, increasing);
1443 Q_ASSERT(index != -1);
1444
1445 // Try to find another window if the candidate is hidden.
1446 while (subWindows.at(index)->isHidden()) {
1447 setIndex(&index, index + increaseFactor, 0, subWindows.size() - 1, increasing);
1448 if (index == indexToCurrent)
1449 break;
1450 }
1451
1452 if (!subWindows.at(index)->isHidden())
1453 return subWindows.at(index);
1454 return nullptr;
1455}
1456
1457/*!
1458 \internal
1459*/
1461{
1462 if (childWindows.size() == 1)
1463 return;
1464
1465 Q_Q(QMdiArea);
1466 // There's no highlighted sub-window atm, use current.
1467 if (indexToHighlighted < 0) {
1468 QMdiSubWindow *current = q->currentSubWindow();
1469 if (!current)
1470 return;
1471 indexToHighlighted = childWindows.indexOf(current);
1472 }
1473
1474 Q_ASSERT(indexToHighlighted >= 0);
1475 Q_ASSERT(indexToHighlighted < childWindows.size());
1476
1477 QMdiSubWindow *highlight = nextVisibleSubWindow(increaseFactor, activationOrder, -1, indexToHighlighted);
1478 if (!highlight)
1479 return;
1480
1481#if QT_CONFIG(rubberband)
1482 if (!rubberBand) {
1483 rubberBand = new QRubberBand(QRubberBand::Rectangle, q);
1484 // For accessibility to identify this special widget.
1485 rubberBand->setObjectName("qt_rubberband"_L1);
1486 rubberBand->setWindowFlags(rubberBand->windowFlags() | Qt::WindowStaysOnTopHint);
1487 }
1488#endif
1489
1490 // Only highlight if we're not switching back to the previously active window (Ctrl-Tab once).
1491#if QT_CONFIG(rubberband)
1492 if (!tabToPreviousTimer.isActive())
1493 showRubberBandFor(highlight);
1494#endif
1495
1496 indexToHighlighted = childWindows.indexOf(highlight);
1497 Q_ASSERT(indexToHighlighted >= 0);
1498}
1499
1500#if QT_CONFIG(rubberband)
1501void QMdiAreaPrivate::showRubberBandFor(QMdiSubWindow *subWindow)
1502{
1503 if (!subWindow || !rubberBand)
1504 return;
1505
1506#if QT_CONFIG(tabbar)
1507 if (viewMode == QMdiArea::TabbedView)
1508 rubberBand->setGeometry(tabBar->tabRect(childWindows.indexOf(subWindow)));
1509 else
1510#endif
1511 rubberBand->setGeometry(subWindow->geometry());
1512
1513 rubberBand->raise();
1514 rubberBand->show();
1515}
1516#endif // QT_CONFIG(rubberBand)
1517/*!
1518 \internal
1519 \since 4.4
1520*/
1521void QMdiAreaPrivate::setViewMode(QMdiArea::ViewMode mode)
1522{
1523 Q_Q(QMdiArea);
1524 if (viewMode == mode || inViewModeChange)
1525 return;
1526
1527 // Just a guard since we cannot set viewMode = mode here.
1528 inViewModeChange = true;
1529
1530#if QT_CONFIG(tabbar)
1531 if (mode == QMdiArea::TabbedView) {
1532 Q_ASSERT(!tabBar);
1533 tabBar = new QMdiAreaTabBar(q);
1534 tabBar->setDocumentMode(documentMode);
1535 tabBar->setTabsClosable(tabsClosable);
1536 tabBar->setMovable(tabsMovable);
1537#if QT_CONFIG(tabwidget)
1538 tabBar->setShape(_q_tb_tabBarShapeFrom(tabShape, tabPosition));
1539#endif
1540
1541 isSubWindowsTiled = false;
1542
1543 // Take a copy as tabBar->addTab() will (indirectly) create a connection between
1544 // the tab close button clicked() signal and the _q_closeTab() slot, which may
1545 // indirectly call QCoreApplication::sendEvent(), the latter could result in
1546 // invoking unknown code that could e.g. recurse into the class modifying childWindows.
1547 const auto subWindows = childWindows;
1548 for (QMdiSubWindow *subWindow : subWindows)
1549 tabBar->addTab(subWindow->windowIcon(), tabTextFor(subWindow));
1550
1551 QMdiSubWindow *current = q->currentSubWindow();
1552 if (current) {
1553 tabBar->setCurrentIndex(childWindows.indexOf(current));
1554 // Restore sub-window (i.e. cleanup buttons in menu bar and window title).
1555 if (current->isMaximized())
1556 current->showNormal();
1557
1558 viewMode = mode;
1559
1560 // Now, maximize it.
1561 if (!q->testOption(QMdiArea::DontMaximizeSubWindowOnActivation)) {
1562 current->showMaximized();
1563 }
1564 } else {
1565 viewMode = mode;
1566 }
1567
1568 if (q->isVisible())
1569 tabBar->show();
1570 updateTabBarGeometry();
1571
1572 QObject::connect(tabBar, SIGNAL(currentChanged(int)), q, SLOT(_q_currentTabChanged(int)));
1573 QObject::connect(tabBar, SIGNAL(tabCloseRequested(int)), q, SLOT(_q_closeTab(int)));
1574 QObject::connect(tabBar, SIGNAL(tabMoved(int,int)), q, SLOT(_q_moveTab(int,int)));
1575 } else
1576#endif // QT_CONFIG(tabbar)
1577 { // SubWindowView
1578#if QT_CONFIG(tabbar)
1579 delete tabBar;
1580 tabBar = nullptr;
1581#endif // QT_CONFIG(tabbar)
1582
1583 viewMode = mode;
1584 q->setViewportMargins(0, 0, 0, 0);
1586
1587 QMdiSubWindow *current = q->currentSubWindow();
1588 if (current && current->isMaximized())
1589 current->showNormal();
1590 }
1591
1592 Q_ASSERT(viewMode == mode);
1593 inViewModeChange = false;
1594}
1595
1596#if QT_CONFIG(tabbar)
1597/*!
1598 \internal
1599*/
1600void QMdiAreaPrivate::updateTabBarGeometry()
1601{
1602 if (!tabBar)
1603 return;
1604
1605 Q_Q(QMdiArea);
1606#if QT_CONFIG(tabwidget)
1607 Q_ASSERT(_q_tb_tabBarShapeFrom(tabShape, tabPosition) == tabBar->shape());
1608#endif
1609 const QSize tabBarSizeHint = tabBar->sizeHint();
1610
1611 int areaHeight = q->height();
1612 if (hbar && hbar->isVisible())
1613 areaHeight -= hbar->height();
1614
1615 int areaWidth = q->width();
1616 if (vbar && vbar->isVisible())
1617 areaWidth -= vbar->width();
1618
1619 QRect tabBarRect;
1620#if QT_CONFIG(tabwidget)
1621 switch (tabPosition) {
1622 case QTabWidget::North:
1623 q->setViewportMargins(0, tabBarSizeHint.height(), 0, 0);
1624 tabBarRect = QRect(0, 0, areaWidth, tabBarSizeHint.height());
1625 break;
1626 case QTabWidget::South:
1627 q->setViewportMargins(0, 0, 0, tabBarSizeHint.height());
1628 tabBarRect = QRect(0, areaHeight - tabBarSizeHint.height(), areaWidth, tabBarSizeHint.height());
1629 break;
1630 case QTabWidget::East:
1631 if (q->layoutDirection() == Qt::LeftToRight)
1632 q->setViewportMargins(0, 0, tabBarSizeHint.width(), 0);
1633 else
1634 q->setViewportMargins(tabBarSizeHint.width(), 0, 0, 0);
1635 tabBarRect = QRect(areaWidth - tabBarSizeHint.width(), 0, tabBarSizeHint.width(), areaHeight);
1636 break;
1637 case QTabWidget::West:
1638 if (q->layoutDirection() == Qt::LeftToRight)
1639 q->setViewportMargins(tabBarSizeHint.width(), 0, 0, 0);
1640 else
1641 q->setViewportMargins(0, 0, tabBarSizeHint.width(), 0);
1642 tabBarRect = QRect(0, 0, tabBarSizeHint.width(), areaHeight);
1643 break;
1644 default:
1645 break;
1646 }
1647#endif // QT_CONFIG(tabwidget)
1648
1649 tabBar->setGeometry(QStyle::visualRect(q->layoutDirection(), q->contentsRect(), tabBarRect));
1650}
1651
1652/*!
1653 \internal
1654*/
1655void QMdiAreaPrivate::refreshTabBar()
1656{
1657 if (!tabBar)
1658 return;
1659
1660 tabBar->setDocumentMode(documentMode);
1661 tabBar->setTabsClosable(tabsClosable);
1662 tabBar->setMovable(tabsMovable);
1663#if QT_CONFIG(tabwidget)
1664 tabBar->setShape(_q_tb_tabBarShapeFrom(tabShape, tabPosition));
1665#endif
1666 updateTabBarGeometry();
1667}
1668#endif // QT_CONFIG(tabbar)
1669
1670/*!
1671 Constructs an empty mdi area. \a parent is passed to QWidget's
1672 constructor.
1673*/
1674QMdiArea::QMdiArea(QWidget *parent)
1675 : QAbstractScrollArea(*new QMdiAreaPrivate, parent)
1676{
1677 setBackground(palette().brush(QPalette::Dark));
1678 setFrameStyle(QFrame::NoFrame);
1679 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1680 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1681 setViewport(nullptr);
1682 setFocusPolicy(Qt::NoFocus);
1683 QApplication::instance()->installEventFilter(this);
1684}
1685
1686/*!
1687 Destroys the MDI area.
1688*/
1689QMdiArea::~QMdiArea()
1690{
1691 Q_D(QMdiArea);
1692 delete d->cascader;
1693 d->cascader = nullptr;
1694
1695 delete d->regularTiler;
1696 d->regularTiler = nullptr;
1697
1698 delete d->iconTiler;
1699 d->iconTiler = nullptr;
1700
1701 delete d->placer;
1702 d->placer = nullptr;
1703}
1704
1705/*!
1706 \reimp
1707*/
1708QSize QMdiArea::sizeHint() const
1709{
1710 // Calculate a proper scale factor for the desktop's size.
1711 // This also takes into account that we can have nested workspaces.
1712 int nestedCount = 0;
1713 QWidget *widget = this->parentWidget();
1714 while (widget) {
1715 if (qobject_cast<QMdiArea *>(widget))
1716 ++nestedCount;
1717 widget = widget->parentWidget();
1718 }
1719 const int scaleFactor = 3 * (nestedCount + 1);
1720
1721 QSize desktopSize = QGuiApplication::primaryScreen()->virtualSize();
1722 QSize size(desktopSize.width() * 2 / scaleFactor, desktopSize.height() * 2 / scaleFactor);
1723 for (QMdiSubWindow *child : d_func()->childWindows) {
1724 if (!sanityCheck(child, "QMdiArea::sizeHint"))
1725 continue;
1726 size = size.expandedTo(child->sizeHint());
1727 }
1728 return size;
1729}
1730
1731/*!
1732 \reimp
1733*/
1734QSize QMdiArea::minimumSizeHint() const
1735{
1736 Q_D(const QMdiArea);
1737 QSize size(style()->pixelMetric(QStyle::PM_MdiSubWindowMinimizedWidth, nullptr, this),
1738 style()->pixelMetric(QStyle::PM_TitleBarHeight, nullptr, this));
1739 size = size.expandedTo(QAbstractScrollArea::minimumSizeHint());
1740 if (!d->scrollBarsEnabled()) {
1741 for (QMdiSubWindow *child : d->childWindows) {
1742 if (!sanityCheck(child, "QMdiArea::sizeHint"))
1743 continue;
1744 size = size.expandedTo(child->minimumSizeHint());
1745 }
1746 }
1747 return size;
1748}
1749
1750/*!
1751 Returns a pointer to the current subwindow, or \nullptr if there is
1752 no current subwindow.
1753
1754 This function will return the same as activeSubWindow() if
1755 the QApplication containing QMdiArea is active.
1756
1757 \sa activeSubWindow(), QApplication::activeWindow()
1758*/
1759QMdiSubWindow *QMdiArea::currentSubWindow() const
1760{
1761 Q_D(const QMdiArea);
1762 if (d->childWindows.isEmpty())
1763 return nullptr;
1764
1765 if (d->active)
1766 return d->active;
1767
1768 if (d->isActivated && !window()->isMinimized())
1769 return nullptr;
1770
1771 Q_ASSERT(d->indicesToActivatedChildren.size() > 0);
1772 int index = d->indicesToActivatedChildren.at(0);
1773 Q_ASSERT(index >= 0 && index < d->childWindows.size());
1774 QMdiSubWindow *current = d->childWindows.at(index);
1775 Q_ASSERT(current);
1776 return current;
1777}
1778
1779/*!
1780 Returns a pointer to the current active subwindow. If no
1781 window is currently active, \nullptr is returned.
1782
1783 Subwindows are treated as top-level windows with respect to
1784 window state, i.e., if a widget outside the MDI area is the active
1785 window, no subwindow will be active. Note that if a widget in the
1786 window in which the MDI area lives gains focus, the window will be
1787 activated.
1788
1789 \sa setActiveSubWindow(), Qt::WindowState
1790*/
1791QMdiSubWindow *QMdiArea::activeSubWindow() const
1792{
1793 Q_D(const QMdiArea);
1794 return d->active;
1795}
1796
1797/*!
1798 Activates the subwindow \a window. If \a window is \nullptr, any
1799 current active window is deactivated.
1800
1801 \sa activeSubWindow()
1802*/
1803void QMdiArea::setActiveSubWindow(QMdiSubWindow *window)
1804{
1805 Q_D(QMdiArea);
1806 if (!window) {
1807 d->activateWindow(nullptr);
1808 return;
1809 }
1810
1811 if (Q_UNLIKELY(d->childWindows.isEmpty())) {
1812 qWarning("QMdiArea::setActiveSubWindow: workspace is empty");
1813 return;
1814 }
1815
1816 if (Q_UNLIKELY(d->childWindows.indexOf(window) == -1)) {
1817 qWarning("QMdiArea::setActiveSubWindow: window is not inside workspace");
1818 return;
1819 }
1820
1821 d->activateWindow(window);
1822}
1823
1824/*!
1825 Closes the active subwindow.
1826
1827 \sa closeAllSubWindows()
1828*/
1829void QMdiArea::closeActiveSubWindow()
1830{
1831 Q_D(QMdiArea);
1832 if (d->active)
1833 d->active->close();
1834}
1835
1836/*!
1837 Returns a list of all subwindows in the MDI area. If \a order is
1838 CreationOrder (the default), the windows are sorted in the order
1839 in which they were inserted into the workspace. If \a order is
1840 StackingOrder, the windows are listed in their stacking order,
1841 with the topmost window as the last item in the list. If \a order
1842 is ActivationHistoryOrder, the windows are listed according to
1843 their recent activation history.
1844
1845 \sa WindowOrder
1846*/
1847QList<QMdiSubWindow *> QMdiArea::subWindowList(WindowOrder order) const
1848{
1849 Q_D(const QMdiArea);
1850 return d->subWindowList(order, false);
1851}
1852
1853/*!
1854 Closes all subwindows by sending a QCloseEvent to each window.
1855 You may receive subWindowActivated() signals from subwindows
1856 before they are closed (if the MDI area activates the subwindow
1857 when another is closing).
1858
1859 Subwindows that ignore the close event will remain open.
1860
1861 \sa closeActiveSubWindow()
1862*/
1863void QMdiArea::closeAllSubWindows()
1864{
1865 Q_D(QMdiArea);
1866 if (d->childWindows.isEmpty())
1867 return;
1868
1869 d->isSubWindowsTiled = false;
1870 // Take a copy because the child->close() call below may end up indirectly calling
1871 // QCoreApplication::send{Spontaneous}Event(), which may call unknown code that
1872 // could e.g. recurse into the class modifying d->childWindows.
1873 const auto subWindows = d->childWindows;
1874 for (QMdiSubWindow *child : subWindows) {
1875 if (!sanityCheck(child, "QMdiArea::closeAllSubWindows"))
1876 continue;
1877 child->close();
1878 }
1879
1880 d->updateScrollBars();
1881}
1882
1883/*!
1884 Gives the keyboard focus to another window in the list of child
1885 windows. The window activated will be the next one determined
1886 by the current \l{QMdiArea::WindowOrder} {activation order}.
1887
1888 \sa activatePreviousSubWindow(), QMdiArea::WindowOrder
1889*/
1890void QMdiArea::activateNextSubWindow()
1891{
1892 Q_D(QMdiArea);
1893 if (d->childWindows.isEmpty())
1894 return;
1895
1896 QMdiSubWindow *next = d->nextVisibleSubWindow(1, d->activationOrder);
1897 if (next)
1898 d->activateWindow(next);
1899}
1900
1901/*!
1902 Gives the keyboard focus to another window in the list of child
1903 windows. The window activated will be the previous one determined
1904 by the current \l{QMdiArea::WindowOrder} {activation order}.
1905
1906 \sa activateNextSubWindow(), QMdiArea::WindowOrder
1907*/
1908void QMdiArea::activatePreviousSubWindow()
1909{
1910 Q_D(QMdiArea);
1911 if (d->childWindows.isEmpty())
1912 return;
1913
1914 QMdiSubWindow *previous = d->nextVisibleSubWindow(-1, d->activationOrder);
1915 if (previous)
1916 d->activateWindow(previous);
1917}
1918
1919/*!
1920 Adds \a widget as a new subwindow to the MDI area. If \a
1921 windowFlags are non-zero, they will override the flags set on the
1922 widget.
1923
1924 The \a widget can be either a QMdiSubWindow or another QWidget
1925 (in which case the MDI area will create a subwindow and set the \a
1926 widget as the internal widget).
1927
1928 \note Once the subwindow has been added, its parent will be the
1929 \e{viewport widget} of the QMdiArea.
1930
1931 \snippet mdiarea/mdiareasnippets.cpp 1
1932
1933 When you create your own subwindow, you must set the
1934 Qt::WA_DeleteOnClose widget attribute if you want the window to be
1935 deleted when closed in the MDI area. If not, the window will be
1936 hidden and the MDI area will not activate the next subwindow.
1937
1938 Returns the QMdiSubWindow that is added to the MDI area.
1939
1940 \sa removeSubWindow()
1941*/
1942QMdiSubWindow *QMdiArea::addSubWindow(QWidget *widget, Qt::WindowFlags windowFlags)
1943{
1944 if (Q_UNLIKELY(!widget)) {
1945 qWarning("QMdiArea::addSubWindow: null pointer to widget");
1946 return nullptr;
1947 }
1948
1949 Q_D(QMdiArea);
1950 // QWidget::setParent clears focusWidget so store it
1951 QWidget *childFocus = widget->focusWidget();
1952 QMdiSubWindow *child = qobject_cast<QMdiSubWindow *>(widget);
1953
1954 // Widget is already a QMdiSubWindow
1955 if (child) {
1956 if (Q_UNLIKELY(d->childWindows.indexOf(child) != -1)) {
1957 qWarning("QMdiArea::addSubWindow: window is already added");
1958 return child;
1959 }
1960 child->setParent(viewport(), windowFlags ? windowFlags : child->windowFlags());
1961 // Create a QMdiSubWindow
1962 } else {
1963 child = new QMdiSubWindow(viewport(), windowFlags);
1964 child->setAttribute(Qt::WA_DeleteOnClose);
1965 child->setWidget(widget);
1966 Q_ASSERT(child->testAttribute(Qt::WA_DeleteOnClose));
1967 }
1968
1969 d->appendChild(child);
1970
1971 if (childFocus)
1972 childFocus->setFocus();
1973
1974 return child;
1975}
1976
1977/*!
1978 Removes \a widget from the MDI area. The \a widget must be
1979 either a QMdiSubWindow or a widget that is the internal widget of
1980 a subwindow. Note \a widget is never actually deleted by QMdiArea.
1981 If a QMdiSubWindow is passed in, its parent is set to \nullptr and it is
1982 removed; but if an internal widget is passed in, the child widget
1983 is set to \nullptr and the QMdiSubWindow is \e not removed.
1984
1985 \sa addSubWindow()
1986*/
1987void QMdiArea::removeSubWindow(QWidget *widget)
1988{
1989 if (Q_UNLIKELY(!widget)) {
1990 qWarning("QMdiArea::removeSubWindow: null pointer to widget");
1991 return;
1992 }
1993
1994 Q_D(QMdiArea);
1995 if (d->childWindows.isEmpty())
1996 return;
1997
1998 if (QMdiSubWindow *child = qobject_cast<QMdiSubWindow *>(widget)) {
1999 int index = d->childWindows.indexOf(child);
2000 if (Q_UNLIKELY(index == -1)) {
2001 qWarning("QMdiArea::removeSubWindow: window is not inside workspace");
2002 return;
2003 }
2004 d->disconnectSubWindow(child);
2005 d->childWindows.removeAll(child);
2006 d->indicesToActivatedChildren.removeAll(index);
2007 d->updateActiveWindow(index, d->active == child);
2008 child->setParent(nullptr);
2009 return;
2010 }
2011
2012 bool found = false;
2013 // Take a copy because child->setWidget(nullptr) will indirectly
2014 // QCoreApplication::sendEvent(); the latter could call unknown code that could
2015 // e.g. recurse into the class modifying d->childWindows.
2016 const auto subWindows = d->childWindows;
2017 for (QMdiSubWindow *child : subWindows) {
2018 if (!sanityCheck(child, "QMdiArea::removeSubWindow"))
2019 continue;
2020 if (child->widget() == widget) {
2021 child->setWidget(nullptr);
2022 Q_ASSERT(!child->widget());
2023 found = true;
2024 break;
2025 }
2026 }
2027
2028 if (Q_UNLIKELY(!found))
2029 qWarning("QMdiArea::removeSubWindow: widget is not child of any window inside QMdiArea");
2030}
2031
2032/*!
2033 \property QMdiArea::background
2034 \brief the background brush for the workspace
2035
2036 This property sets the background brush for the workspace area
2037 itself. By default, it is a gray color, but can be any brush
2038 (e.g., colors, gradients or pixmaps).
2039*/
2040QBrush QMdiArea::background() const
2041{
2042 return d_func()->background;
2043}
2044
2045void QMdiArea::setBackground(const QBrush &brush)
2046{
2047 Q_D(QMdiArea);
2048 if (d->background != brush) {
2049 d->background = brush;
2050 d->viewport->setAttribute(Qt::WA_OpaquePaintEvent, brush.isOpaque());
2051 d->viewport->update();
2052 }
2053}
2054
2055
2056/*!
2057 \property QMdiArea::activationOrder
2058 \brief the ordering criteria for subwindow lists
2059 \since 4.4
2060
2061 This property specifies the ordering criteria for the list of
2062 subwindows returned by subWindowList(). By default, it is the window
2063 creation order.
2064
2065 \sa subWindowList()
2066*/
2067QMdiArea::WindowOrder QMdiArea::activationOrder() const
2068{
2069 Q_D(const QMdiArea);
2070 return d->activationOrder;
2071}
2072
2073void QMdiArea::setActivationOrder(WindowOrder order)
2074{
2075 Q_D(QMdiArea);
2076 if (order != d->activationOrder)
2077 d->activationOrder = order;
2078}
2079
2080/*!
2081 If \a on is true, \a option is enabled on the MDI area; otherwise
2082 it is disabled. See AreaOption for the effect of each option.
2083
2084 \sa AreaOption, testOption()
2085*/
2086void QMdiArea::setOption(AreaOption option, bool on)
2087{
2088 Q_D(QMdiArea);
2089 d->options.setFlag(option, on);
2090}
2091
2092/*!
2093 Returns \c true if \a option is enabled; otherwise returns \c false.
2094
2095 \sa AreaOption, setOption()
2096*/
2097bool QMdiArea::testOption(AreaOption option) const
2098{
2099 return d_func()->options & option;
2100}
2101
2102/*!
2103 \property QMdiArea::viewMode
2104 \brief the way sub-windows are displayed in the QMdiArea.
2105 \since 4.4
2106
2107 By default, the SubWindowView is used to display sub-windows.
2108
2109 \sa ViewMode, setTabShape(), setTabPosition()
2110*/
2111QMdiArea::ViewMode QMdiArea::viewMode() const
2112{
2113 Q_D(const QMdiArea);
2114 return d->viewMode;
2115}
2116
2117void QMdiArea::setViewMode(ViewMode mode)
2118{
2119 Q_D(QMdiArea);
2120 d->setViewMode(mode);
2121}
2122
2123#if QT_CONFIG(tabbar)
2124/*!
2125 \property QMdiArea::documentMode
2126 \brief whether the tab bar is set to document mode in tabbed view mode.
2127 \since 4.5
2128
2129 Document mode is disabled by default.
2130
2131 \sa QTabBar::documentMode, setViewMode()
2132*/
2133bool QMdiArea::documentMode() const
2134{
2135 Q_D(const QMdiArea);
2136 return d->documentMode;
2137}
2138
2139void QMdiArea::setDocumentMode(bool enabled)
2140{
2141 Q_D(QMdiArea);
2142 if (d->documentMode == enabled)
2143 return;
2144
2145 d->documentMode = enabled;
2146 d->refreshTabBar();
2147}
2148
2149/*!
2150 \property QMdiArea::tabsClosable
2151 \brief whether the tab bar should place close buttons on each tab in tabbed view mode.
2152 \since 4.8
2153
2154 Tabs are not closable by default.
2155
2156 \sa QTabBar::tabsClosable, setViewMode()
2157*/
2158bool QMdiArea::tabsClosable() const
2159{
2160 Q_D(const QMdiArea);
2161 return d->tabsClosable;
2162}
2163
2164void QMdiArea::setTabsClosable(bool closable)
2165{
2166 Q_D(QMdiArea);
2167 if (d->tabsClosable == closable)
2168 return;
2169
2170 d->tabsClosable = closable;
2171 d->refreshTabBar();
2172}
2173
2174/*!
2175 \property QMdiArea::tabsMovable
2176 \brief whether the user can move the tabs within the tabbar area in tabbed view mode.
2177 \since 4.8
2178
2179 Tabs are not movable by default.
2180
2181 \sa QTabBar::movable, setViewMode()
2182*/
2183bool QMdiArea::tabsMovable() const
2184{
2185 Q_D(const QMdiArea);
2186 return d->tabsMovable;
2187}
2188
2189void QMdiArea::setTabsMovable(bool movable)
2190{
2191 Q_D(QMdiArea);
2192 if (d->tabsMovable == movable)
2193 return;
2194
2195 d->tabsMovable = movable;
2196 d->refreshTabBar();
2197}
2198#endif // QT_CONFIG(tabbar)
2199
2200#if QT_CONFIG(tabwidget)
2201/*!
2202 \property QMdiArea::tabShape
2203 \brief the shape of the tabs in tabbed view mode.
2204 \since 4.4
2205
2206 Possible values for this property are QTabWidget::Rounded
2207 (default) or QTabWidget::Triangular.
2208
2209 \sa QTabWidget::TabShape, setViewMode()
2210*/
2211QTabWidget::TabShape QMdiArea::tabShape() const
2212{
2213 Q_D(const QMdiArea);
2214 return d->tabShape;
2215}
2216
2217void QMdiArea::setTabShape(QTabWidget::TabShape shape)
2218{
2219 Q_D(QMdiArea);
2220 if (d->tabShape == shape)
2221 return;
2222
2223 d->tabShape = shape;
2224 d->refreshTabBar();
2225}
2226
2227/*!
2228 \property QMdiArea::tabPosition
2229 \brief the position of the tabs in tabbed view mode.
2230 \since 4.4
2231
2232 Possible values for this property are described by the
2233 QTabWidget::TabPosition enum.
2234
2235 \sa QTabWidget::TabPosition, setViewMode()
2236*/
2237QTabWidget::TabPosition QMdiArea::tabPosition() const
2238{
2239 Q_D(const QMdiArea);
2240 return d->tabPosition;
2241}
2242
2243void QMdiArea::setTabPosition(QTabWidget::TabPosition position)
2244{
2245 Q_D(QMdiArea);
2246 if (d->tabPosition == position)
2247 return;
2248
2249 d->tabPosition = position;
2250 d->refreshTabBar();
2251}
2252#endif // QT_CONFIG(tabwidget)
2253
2254/*!
2255 \reimp
2256*/
2257void QMdiArea::childEvent(QChildEvent *childEvent)
2258{
2259 Q_D(QMdiArea);
2260 if (childEvent->type() == QEvent::ChildPolished) {
2261 if (QMdiSubWindow *mdiChild = qobject_cast<QMdiSubWindow *>(childEvent->child())) {
2262 if (d->childWindows.indexOf(mdiChild) == -1)
2263 d->appendChild(mdiChild);
2264 }
2265 }
2266}
2267
2268/*!
2269 \reimp
2270*/
2271void QMdiArea::resizeEvent(QResizeEvent *resizeEvent)
2272{
2273 Q_D(QMdiArea);
2274 if (d->childWindows.isEmpty()) {
2275 resizeEvent->ignore();
2276 return;
2277 }
2278
2279#if QT_CONFIG(tabbar)
2280 d->updateTabBarGeometry();
2281#endif
2282
2283 // Re-tile the views if we're in tiled mode. Re-tile means we will change
2284 // the geometry of the children, which in turn means 'isSubWindowsTiled'
2285 // is set to false, so we have to update the state at the end.
2286 if (d->isSubWindowsTiled) {
2287 d->tileCalledFromResizeEvent = true;
2288 tileSubWindows();
2289 d->tileCalledFromResizeEvent = false;
2290 d->isSubWindowsTiled = true;
2291 d->startResizeTimer();
2292 // We don't have scroll bars or any maximized views.
2293 return;
2294 }
2295
2296 // Resize maximized views.
2297 bool hasMaximizedSubWindow = false;
2298 // Take a copy because child->resize() may call QCoreApplication::sendEvent()
2299 // which may invoke unknown code, that could e.g. recurse into the class
2300 // modifying d->childWindows.
2301 const auto subWindows = d->childWindows;
2302 for (QMdiSubWindow *child : subWindows) {
2303 if (sanityCheck(child, "QMdiArea::resizeEvent") && child->isMaximized()
2304 && child->size() != resizeEvent->size()) {
2305 auto realSize = resizeEvent->size();
2306 const auto minSizeHint = child->minimumSizeHint();
2307 // QMdiSubWindow is no tlw so minimumSize() is not set by the layout manager
2308 // and therefore we have to take care by ourself that we're not getting smaller
2309 // than allowed
2310 if (minSizeHint.isValid())
2311 realSize = realSize.expandedTo(minSizeHint);
2312 child->resize(realSize);
2313 if (!hasMaximizedSubWindow)
2314 hasMaximizedSubWindow = true;
2315 }
2316 }
2317
2318 d->updateScrollBars();
2319
2320 // Minimized views are stacked under maximized views so there's
2321 // no need to re-arrange minimized views on-demand. Start a timer
2322 // just to make things faster with subsequent resize events.
2323 if (hasMaximizedSubWindow)
2324 d->startResizeTimer();
2325 else
2326 d->arrangeMinimizedSubWindows();
2327}
2328
2329/*!
2330 \reimp
2331*/
2332void QMdiArea::timerEvent(QTimerEvent *timerEvent)
2333{
2334 Q_D(QMdiArea);
2335 if (timerEvent->id() == d->resizeTimer.id()) {
2336 d->resizeTimer.stop();
2337 d->arrangeMinimizedSubWindows();
2338 } else if (timerEvent->id() == d->tabToPreviousTimer.id()) {
2339 d->tabToPreviousTimer.stop();
2340 if (d->indexToHighlighted < 0)
2341 return;
2342#if QT_CONFIG(rubberband)
2343 // We're not doing a "quick switch" ... show rubber band.
2344 Q_ASSERT(d->indexToHighlighted < d->childWindows.size());
2345 Q_ASSERT(d->rubberBand);
2346 d->showRubberBandFor(d->childWindows.at(d->indexToHighlighted));
2347#endif
2348 }
2349}
2350
2351/*!
2352 \reimp
2353*/
2354void QMdiArea::showEvent(QShowEvent *showEvent)
2355{
2356 Q_D(QMdiArea);
2357 if (!d->pendingRearrangements.isEmpty()) {
2358 bool skipPlacement = false;
2359 // Take a copy because d->rearrange() may modify d->pendingRearrangements
2360 const auto pendingRearrange = d->pendingRearrangements;
2361 for (Rearranger *rearranger : pendingRearrange) {
2362 // If this is the case, we don't have to lay out pending child windows
2363 // since the rearranger will find a placement for them.
2364 if (rearranger->type() != Rearranger::IconTiler && !skipPlacement)
2365 skipPlacement = true;
2366 d->rearrange(rearranger);
2367 }
2368 d->pendingRearrangements.clear();
2369
2370 if (skipPlacement && !d->pendingPlacements.isEmpty())
2371 d->pendingPlacements.clear();
2372 }
2373
2374 if (!d->pendingPlacements.isEmpty()) {
2375 // Nothing obvious in the loop body changes the container (in this code path)
2376 // during iteration, this is calling into a non-const method that does change
2377 // the container when called from other places. So take a copy anyway for good
2378 // measure.
2379 const auto copy = d->pendingPlacements;
2380 for (QMdiSubWindow *window : copy) {
2381 if (!window)
2382 continue;
2383 if (d->viewMode == TabbedView && window->d_func()->isActive && !d->active) {
2384 d->showActiveWindowMaximized = true;
2385 d->emitWindowActivated(window); // Also maximizes the window
2386 continue;
2387 }
2388 if (!window->testAttribute(Qt::WA_Resized)) {
2389 QSize newSize(window->sizeHint().boundedTo(viewport()->size()));
2390 window->resize(newSize.expandedTo(qSmartMinSize(window)));
2391 }
2392 if (!window->testAttribute(Qt::WA_Moved) && !window->isMinimized()
2393 && !window->isMaximized()) {
2394 d->place(d->placer, window);
2395 }
2396 }
2397 d->pendingPlacements.clear();
2398 }
2399
2400 d->setChildActivationEnabled(true);
2401 d->activateCurrentWindow();
2402
2403 QAbstractScrollArea::showEvent(showEvent);
2404}
2405
2406/*!
2407 \reimp
2408*/
2409bool QMdiArea::viewportEvent(QEvent *event)
2410{
2411 Q_D(QMdiArea);
2412 switch (event->type()) {
2413 case QEvent::ChildRemoved: {
2414 d->isSubWindowsTiled = false;
2415 QObject *removedChild = static_cast<QChildEvent *>(event)->child();
2416 for (int i = 0; i < d->childWindows.size(); ++i) {
2417 QObject *child = d->childWindows.at(i);
2418 if (!child || child == removedChild || !child->parent()
2419 || child->parent() != viewport()) {
2420 if (!testOption(DontMaximizeSubWindowOnActivation)) {
2421 // In this case we can only rely on the child being a QObject
2422 // (or 0), but let's try and see if we can get more information.
2423 QWidget *mdiChild = qobject_cast<QWidget *>(removedChild);
2424 if (mdiChild && mdiChild->isMaximized())
2425 d->showActiveWindowMaximized = true;
2426 }
2427 d->disconnectSubWindow(child);
2428 const bool activeRemoved = i == d->indicesToActivatedChildren.at(0);
2429 d->childWindows.removeAt(i);
2430 d->indicesToActivatedChildren.removeAll(i);
2431 d->updateActiveWindow(i, activeRemoved);
2432 d->arrangeMinimizedSubWindows();
2433 break;
2434 }
2435 }
2436 d->updateScrollBars();
2437 break;
2438 }
2439 case QEvent::Destroy:
2440 d->isSubWindowsTiled = false;
2441 d->resetActiveWindow();
2442 d->childWindows.clear();
2443 qWarning("QMdiArea: Deleting the view port is undefined, use setViewport instead.");
2444 break;
2445 default:
2446 break;
2447 }
2448 return QAbstractScrollArea::viewportEvent(event);
2449}
2450
2451/*!
2452 \reimp
2453*/
2454void QMdiArea::scrollContentsBy(int dx, int dy)
2455{
2456 Q_D(QMdiArea);
2457 const bool wasSubWindowsTiled = d->isSubWindowsTiled;
2458 d->ignoreGeometryChange = true;
2459 viewport()->scroll(isLeftToRight() ? dx : -dx, dy);
2460 d->arrangeMinimizedSubWindows();
2461 d->ignoreGeometryChange = false;
2462 if (wasSubWindowsTiled)
2463 d->isSubWindowsTiled = true;
2464}
2465
2466/*!
2467 Arranges all child windows in a tile pattern.
2468
2469 \sa cascadeSubWindows()
2470*/
2471void QMdiArea::tileSubWindows()
2472{
2473 Q_D(QMdiArea);
2474 if (!d->regularTiler)
2475 d->regularTiler = new RegularTiler;
2476 d->rearrange(d->regularTiler);
2477}
2478
2479/*!
2480 Arranges all the child windows in a cascade pattern.
2481
2482 \sa tileSubWindows()
2483*/
2484void QMdiArea::cascadeSubWindows()
2485{
2486 Q_D(QMdiArea);
2487 if (!d->cascader)
2488 d->cascader = new SimpleCascader;
2489 d->rearrange(d->cascader);
2490}
2491
2492/*!
2493 \reimp
2494*/
2495bool QMdiArea::event(QEvent *event)
2496{
2497 Q_D(QMdiArea);
2498 switch (event->type()) {
2499 case QEvent::WindowActivate: {
2500 d->isActivated = true;
2501 if (d->childWindows.isEmpty())
2502 break;
2503 if (!d->active)
2504 d->activateCurrentWindow();
2505 d->setChildActivationEnabled(false, true);
2506 break;
2507 }
2508 case QEvent::WindowDeactivate:
2509 d->isActivated = false;
2510 d->setChildActivationEnabled(false, true);
2511 break;
2512 case QEvent::StyleChange:
2513 // Re-tile the views if we're in tiled mode. Re-tile means we will change
2514 // the geometry of the children, which in turn means 'isSubWindowsTiled'
2515 // is set to false, so we have to update the state at the end.
2516 if (d->isSubWindowsTiled) {
2517 tileSubWindows();
2518 d->isSubWindowsTiled = true;
2519 }
2520 break;
2521 case QEvent::WindowIconChange: {
2522 // Take a copy because QCoreApplication::sendEvent() may call unknown code,
2523 // that may cause recursing into the class
2524 const auto subWindows = d->childWindows;
2525 for (QMdiSubWindow *window : subWindows) {
2526 if (sanityCheck(window, "QMdiArea::WindowIconChange"))
2527 QCoreApplication::sendEvent(window, event);
2528 }
2529 break;
2530 }
2531 case QEvent::Hide:
2532 d->setActive(d->active, false, false);
2533 d->setChildActivationEnabled(false);
2534 break;
2535#if QT_CONFIG(tabbar)
2536 case QEvent::LayoutDirectionChange:
2537 d->updateTabBarGeometry();
2538 break;
2539#endif
2540 default:
2541 break;
2542 }
2543 return QAbstractScrollArea::event(event);
2544}
2545
2546/*!
2547 \reimp
2548*/
2549bool QMdiArea::eventFilter(QObject *object, QEvent *event)
2550{
2551 if (!object)
2552 return QAbstractScrollArea::eventFilter(object, event);
2553
2554 Q_D(QMdiArea);
2555 // Global key events with Ctrl modifier.
2556 if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
2557
2558 QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
2559 // Ignore key events without a Ctrl modifier (except for press/release on the modifier itself).
2560 if (!(keyEvent->modifiers() & Qt::ControlModifier) && keyEvent->key() != Qt::Key_Control)
2561 return QAbstractScrollArea::eventFilter(object, event);
2562
2563 // Find closest mdi area (in case we have a nested workspace).
2564 QMdiArea *area = mdiAreaParent(static_cast<QWidget *>(object));
2565 if (!area)
2566 return QAbstractScrollArea::eventFilter(object, event);
2567
2568 const bool keyPress = (event->type() == QEvent::KeyPress);
2569
2570 // 1) Ctrl-Tab once -> activate the previously active window.
2571 // 2) Ctrl-Tab (Tab, Tab, ...) -> iterate through all windows (activateNextSubWindow()).
2572 // 3) Ctrl-Shift-Tab (Tab, Tab, ...) -> iterate through all windows in the opposite
2573 // direction (activatePreviousSubWindow())
2574 switch (keyEvent->key()) {
2575 case Qt::Key_Control:
2576 if (keyPress)
2577 area->d_func()->startTabToPreviousTimer();
2578 else
2579 area->d_func()->activateHighlightedWindow();
2580 break;
2581 case Qt::Key_Tab:
2582 case Qt::Key_Backtab:
2583 if (keyPress)
2584 area->d_func()->highlightNextSubWindow(keyEvent->key() == Qt::Key_Tab ? 1 : -1);
2585 return true;
2586#if QT_CONFIG(rubberband)
2587 case Qt::Key_Escape:
2588 area->d_func()->hideRubberBand();
2589 break;
2590#endif
2591 default:
2592 break;
2593 }
2594 return QAbstractScrollArea::eventFilter(object, event);
2595 }
2596
2597 QMdiSubWindow *subWindow = qobject_cast<QMdiSubWindow *>(object);
2598
2599 if (!subWindow) {
2600 // QApplication events:
2601 if (event->type() == QEvent::ApplicationActivate && !d->active
2602 && isVisible() && !window()->isMinimized()) {
2603 d->activateCurrentWindow();
2604 } else if (event->type() == QEvent::ApplicationDeactivate && d->active) {
2605 d->setActive(d->active, false, false);
2606 }
2607 return QAbstractScrollArea::eventFilter(object, event);
2608 }
2609
2610 if (subWindow->mdiArea() != this)
2611 return QAbstractScrollArea::eventFilter(object, event);
2612
2613 // QMdiSubWindow events:
2614 switch (event->type()) {
2615 case QEvent::Move:
2616 case QEvent::Resize:
2617 if (d->tileCalledFromResizeEvent)
2618 break;
2619 d->updateScrollBars();
2620 if (!subWindow->isMinimized())
2621 d->isSubWindowsTiled = false;
2622 break;
2623 case QEvent::Show:
2624#if QT_CONFIG(tabbar)
2625 if (d->tabBar) {
2626 const int tabIndex = d->childWindows.indexOf(subWindow);
2627 if (!d->tabBar->isTabEnabled(tabIndex))
2628 d->tabBar->setTabEnabled(tabIndex, true);
2629 }
2630#endif // QT_CONFIG(tabbar)
2631 Q_FALLTHROUGH();
2632 case QEvent::Hide:
2633 // Do not reset the isSubWindowsTiled flag if the event is a spontaneous system window event.
2634 // This ensures that tiling will be performed during the resizeEvent after an application
2635 // window minimize (hide) and then restore (show).
2636 if (!event->spontaneous())
2637 d->isSubWindowsTiled = false;
2638 break;
2639#if QT_CONFIG(rubberband)
2640 case QEvent::Close:
2641 if (d->childWindows.indexOf(subWindow) == d->indexToHighlighted)
2642 d->hideRubberBand();
2643 break;
2644#endif
2645#if QT_CONFIG(tabbar)
2646 case QEvent::WindowTitleChange:
2647 case QEvent::ModifiedChange:
2648 if (d->tabBar)
2649 d->tabBar->setTabText(d->childWindows.indexOf(subWindow), tabTextFor(subWindow));
2650 break;
2651 case QEvent::WindowIconChange:
2652 if (d->tabBar)
2653 d->tabBar->setTabIcon(d->childWindows.indexOf(subWindow), subWindow->windowIcon());
2654 break;
2655#endif // QT_CONFIG(tabbar)
2656 default:
2657 break;
2658 }
2659 return QAbstractScrollArea::eventFilter(object, event);
2660}
2661
2662/*!
2663 \reimp
2664*/
2665void QMdiArea::paintEvent(QPaintEvent *paintEvent)
2666{
2667 Q_D(QMdiArea);
2668 QPainter painter(d->viewport);
2669 for (const QRect &exposedRect : paintEvent->region())
2670 painter.fillRect(exposedRect, d->background);
2671}
2672
2673/*!
2674 This slot is called by QAbstractScrollArea after setViewport() has been
2675 called. Reimplement this function in a subclass of QMdiArea to
2676 initialize the new \a viewport before it is used.
2677
2678 \sa setViewport()
2679*/
2680void QMdiArea::setupViewport(QWidget *viewport)
2681{
2682 Q_D(QMdiArea);
2683 if (viewport)
2684 viewport->setAttribute(Qt::WA_OpaquePaintEvent, d->background.isOpaque());
2685 // Take a copy because the child->setParent() call below may call QCoreApplication::sendEvent()
2686 // which may call unknown code that could e.g. recurse into the class modifying d->childWindows.
2687 const auto subWindows = d->childWindows;
2688 for (QMdiSubWindow *child : subWindows) {
2689 if (!sanityCheck(child, "QMdiArea::setupViewport"))
2690 continue;
2691 child->setParent(viewport, child->windowFlags());
2692 }
2693}
2694
2695QT_END_NAMESPACE
2696
2697#include "moc_qmdiarea.cpp"
void internalRaise(QMdiSubWindow *child) const
void place(QMdi::Placer *placer, QMdiSubWindow *child)
Definition qmdiarea.cpp:842
void resetActiveWindow(QMdiSubWindow *child=nullptr)
QMdi::Rearranger * cascader
Definition qmdiarea_p.h:111
QMdi::Placer * placer
Definition qmdiarea_p.h:114
QMdi::Rearranger * regularTiler
Definition qmdiarea_p.h:112
void activateCurrentWindow()
Definition qmdiarea.cpp:976
bool scrollBarsEnabled() const
void appendChild(QMdiSubWindow *child)
Definition qmdiarea.cpp:789
void activateHighlightedWindow()
Definition qmdiarea.cpp:985
void emitWindowActivated(QMdiSubWindow *child)
QMdi::Rearranger * iconTiler
Definition qmdiarea_p.h:113
QList< QMdiSubWindow * > subWindowList(QMdiArea::WindowOrder, bool reversed=false) const
void updateScrollBars()
QRect resizeToMinimumTileSize(const QSize &minSubWindowSize, int subWindowCount)
bool isExplicitlyDeactivated(QMdiSubWindow *subWindow) const
Definition qmdiarea_p.h:212
void arrangeMinimizedSubWindows()
Definition qmdiarea.cpp:937
QMdiAreaTabBar * tabBar
Definition qmdiarea_p.h:118
bool lastWindowAboutToBeDestroyed() const
bool ignoreGeometryChange
Definition qmdiarea_p.h:138
void activateWindow(QMdiSubWindow *child)
Definition qmdiarea.cpp:947
void highlightNextSubWindow(int increaseFactor)
bool tileCalledFromResizeEvent
Definition qmdiarea_p.h:143
void disconnectSubWindow(QObject *subWindow)
void _q_moveTab(int from, int to)
Definition qmdiarea.cpp:765
bool windowStaysOnTop(QMdiSubWindow *subWindow) const
Definition qmdiarea_p.h:205
bool ignoreWindowStateChange
Definition qmdiarea_p.h:139
void _q_currentTabChanged(int index)
Definition qmdiarea.cpp:730
bool updatesDisabledByUs
Definition qmdiarea_p.h:144
void _q_deactivateAllWindows(QMdiSubWindow *aboutToActivate=nullptr)
Definition qmdiarea.cpp:657
void scrollBarPolicyChanged(Qt::Orientation, Qt::ScrollBarPolicy) override
bool showActiveWindowMaximized
Definition qmdiarea_p.h:142
int indexToPreviousWindow
Definition qmdiarea_p.h:147
void setViewMode(QMdiArea::ViewMode mode)
void updateActiveWindow(int removedIndex, bool activeRemoved)
void rearrange(QMdi::Rearranger *rearranger)
Definition qmdiarea.cpp:881
void setChildActivationEnabled(bool enable=true, bool onlyNextActivationEvent=false) const
void _q_closeTab(int index)
Definition qmdiarea.cpp:754
void _q_processWindowStateChanged(Qt::WindowStates oldState, Qt::WindowStates newState)
Definition qmdiarea.cpp:697
QMdiSubWindow * nextVisibleSubWindow(int increaseFactor, QMdiArea::WindowOrder, int removed=-1, int fromIndex=-1) const
void rearrange(QList< QWidget * > &widgets, const QRect &domain) const override
Definition qmdiarea.cpp:329
QPoint place(const QSize &size, const QList< QRect > &rects, const QRect &domain) const override
Definition qmdiarea.cpp:500
virtual Type type() const =0
void rearrange(QList< QWidget * > &widgets, const QRect &domain) const override
Definition qmdiarea.cpp:242
void rearrange(QList< QWidget * > &widgets, const QRect &domain) const override
Definition qmdiarea.cpp:286
static bool sanityCheck(const QList< QWidget * > &widgets, const int index, const char *where)
Definition qmdiarea.cpp:155
static QString tabTextFor(QMdiSubWindow *subWindow)
Definition qmdiarea.cpp:223
static bool sanityCheck(const QMdiSubWindow *const child, const char *where)
Definition qmdiarea.cpp:144
static QMdiArea * mdiAreaParent(QWidget *widget)
Definition qmdiarea.cpp:205
static bool useScrollBar(const QRect &childrenRect, const QSize &maxViewportSize, Qt::Orientation orientation)
Definition qmdiarea.cpp:191
static void setIndex(int *index, int candidate, int min, int max, bool isIncreasing)
Definition qmdiarea.cpp:172
QString qt_setWindowTitle_helperHelper(const QString &, const QWidget *)
Returns a modified window title with the [*] place holder replaced according to the rules described i...
Definition qwidget.cpp:6000