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