diff options
Diffstat (limited to 'src/widgets/widgets/qmdiarea.cpp')
-rw-r--r-- | src/widgets/widgets/qmdiarea.cpp | 359 |
1 files changed, 176 insertions, 183 deletions
diff --git a/src/widgets/widgets/qmdiarea.cpp b/src/widgets/widgets/qmdiarea.cpp index f32cd26478..79b83453ac 100644 --- a/src/widgets/widgets/qmdiarea.cpp +++ b/src/widgets/widgets/qmdiarea.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only /*! \class QMdiArea @@ -51,7 +15,7 @@ applications, but can also be placed in any layout. The following code adds an area to a main window: - \snippet mdiareasnippets.cpp 0 + \snippet mdiarea/mdiareasnippets.cpp 0 Unlike the window managers for top-level windows, all window flags (Qt::WindowFlags) are supported by QMdiArea as long as the flags @@ -63,7 +27,7 @@ Subwindows in QMdiArea are instances of QMdiSubWindow. They are added to an MDI area with addSubWindow(). It is common to pass a QWidget, which is set as the internal widget, to this function, - but it is also possible to pass a QMdiSubWindow directly.The class + but it is also possible to pass a QMdiSubWindow directly. The class inherits QWidget, and you can use the same API as with a normal top-level window when programming. QMdiSubWindow also has behavior that is specific to MDI windows. See the QMdiSubWindow class @@ -103,8 +67,8 @@ \fn void QMdiArea::subWindowActivated(QMdiSubWindow *window) QMdiArea emits this signal after \a window has been activated. When \a - window is 0, QMdiArea has just deactivated its last active window, and - there are no active windows on the workspace. + window is \nullptr, QMdiArea has just deactivated its last active window, + and there are no active windows on the workspace. \sa QMdiArea::activeSubWindow() */ @@ -165,8 +129,6 @@ #include <QPainter> #include <QFontMetrics> #include <QStyleOption> -#include <QDesktopWidget> -#include <private/qdesktopwidget_p.h> #include <QDebug> #include <qmath.h> #if QT_CONFIG(menu) @@ -178,6 +140,7 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; using namespace QMdi; // Asserts in debug mode, gives warning otherwise. @@ -245,7 +208,7 @@ static inline bool useScrollBar(const QRect &childrenRect, const QSize &maxViewp static inline QMdiArea *mdiAreaParent(QWidget *widget) { if (!widget) - return 0; + return nullptr; QWidget *parent = widget->parentWidget(); while (parent) { @@ -253,23 +216,11 @@ static inline QMdiArea *mdiAreaParent(QWidget *widget) return area; parent = parent->parentWidget(); } - return 0; + return nullptr; } #if QT_CONFIG(tabwidget) -static inline QTabBar::Shape tabBarShapeFrom(QTabWidget::TabShape shape, QTabWidget::TabPosition position) -{ - const bool rounded = (shape == QTabWidget::Rounded); - if (position == QTabWidget::North) - return rounded ? QTabBar::RoundedNorth : QTabBar::TriangularNorth; - if (position == QTabWidget::South) - return rounded ? QTabBar::RoundedSouth : QTabBar::TriangularSouth; - if (position == QTabWidget::East) - return rounded ? QTabBar::RoundedEast : QTabBar::TriangularEast; - if (position == QTabWidget::West) - return rounded ? QTabBar::RoundedWest : QTabBar::TriangularWest; - return QTabBar::RoundedNorth; -} +QTabBar::Shape _q_tb_tabBarShapeFrom(QTabWidget::TabShape shape, QTabWidget::TabPosition position); #endif // QT_CONFIG(tabwidget) static inline QString tabTextFor(QMdiSubWindow *subWindow) @@ -279,7 +230,7 @@ static inline QString tabTextFor(QMdiSubWindow *subWindow) QString title = subWindow->windowTitle(); if (subWindow->isWindowModified()) { - title.replace(QLatin1String("[*]"), QLatin1String("*")); + title.replace("[*]"_L1, "*"_L1); } else { extern QString qt_setWindowTitle_helperHelper(const QString&, const QWidget*); title = qt_setWindowTitle_helperHelper(title, subWindow); @@ -352,7 +303,7 @@ void SimpleCascader::rearrange(QList<QWidget *> &widgets, const QRect &domain) c int titleBarHeight = widgets.at(0)->style()->pixelMetric(QStyle::PM_TitleBarHeight, &options, widgets.at(0)); const QFontMetrics fontMetrics = QFontMetrics(QApplication::font("QMdiSubWindowTitleBar")); const int dy = qMax(titleBarHeight - (titleBarHeight - fontMetrics.height()) / 2, 1) - + widgets.at(0)->style()->pixelMetric(QStyle::PM_FocusFrameVMargin, 0, widgets.at(0)); + + widgets.at(0)->style()->pixelMetric(QStyle::PM_FocusFrameVMargin, nullptr, widgets.at(0)); const int n = widgets.size(); const int nrows = qMax((domain.height() - (topOffset + bottomOffset)) / dy, 1); @@ -410,7 +361,7 @@ void IconTiler::rearrange(QList<QWidget *> &widgets, const QRect &domain) const \internal Calculates the accumulated overlap (intersection area) between 'source' and 'rects'. */ -int MinOverlapPlacer::accumulatedOverlap(const QRect &source, const QVector<QRect> &rects) +int MinOverlapPlacer::accumulatedOverlap(const QRect &source, const QList<QRect> &rects) { int accOverlap = 0; for (const QRect &rect : rects) { @@ -426,7 +377,7 @@ int MinOverlapPlacer::accumulatedOverlap(const QRect &source, const QVector<QRec Finds among 'source' the rectangle with the minimum accumulated overlap with the rectangles in 'rects'. */ -QRect MinOverlapPlacer::findMinOverlapRect(const QVector<QRect> &source, const QVector<QRect> &rects) +QRect MinOverlapPlacer::findMinOverlapRect(const QList<QRect> &source, const QList<QRect> &rects) { int minAccOverlap = -1; QRect minAccOverlapRect; @@ -444,16 +395,16 @@ QRect MinOverlapPlacer::findMinOverlapRect(const QVector<QRect> &source, const Q \internal Gets candidates for the final placement. */ -QVector<QRect> MinOverlapPlacer::getCandidatePlacements(const QSize &size, const QVector<QRect> &rects, - const QRect &domain) +QList<QRect> MinOverlapPlacer::getCandidatePlacements(const QSize &size, const QList<QRect> &rects, + const QRect &domain) { - QVector<QRect> result; + QList<QRect> result; - QVector<int> xlist; + QList<int> xlist; xlist.reserve(2 + rects.size()); xlist << domain.left() << domain.right() - size.width() + 1; - QVector<int> ylist; + QList<int> ylist; ylist.reserve(2 + rects.size()); ylist << domain.top(); if (domain.bottom() - size.height() + 1 >= 0) @@ -471,8 +422,8 @@ QVector<QRect> MinOverlapPlacer::getCandidatePlacements(const QSize &size, const ylist.erase(std::unique(ylist.begin(), ylist.end()), ylist.end()); result.reserve(ylist.size() * xlist.size()); - for (int y : qAsConst(ylist)) - for (int x : qAsConst(xlist)) + for (int y : std::as_const(ylist)) + for (int x : std::as_const(xlist)) result << QRect(QPoint(x, y), size); return result; } @@ -482,14 +433,14 @@ QVector<QRect> MinOverlapPlacer::getCandidatePlacements(const QSize &size, const Finds all rectangles in 'source' not completely inside 'domain'. The result is stored in 'result' and also removed from 'source'. */ -QVector<QRect> MinOverlapPlacer::findNonInsiders(const QRect &domain, QVector<QRect> &source) +QList<QRect> MinOverlapPlacer::findNonInsiders(const QRect &domain, QList<QRect> &source) { const auto containedInDomain = [domain](const QRect &srcRect) { return domain.contains(srcRect); }; const auto firstOut = std::stable_partition(source.begin(), source.end(), containedInDomain); - QVector<QRect> result; + QList<QRect> result; result.reserve(source.end() - firstOut); std::copy(firstOut, source.end(), std::back_inserter(result)); @@ -503,9 +454,9 @@ QVector<QRect> MinOverlapPlacer::findNonInsiders(const QRect &domain, QVector<QR Finds all rectangles in 'source' that overlaps 'domain' by the maximum overlap area between 'domain' and any rectangle in 'source'. The result is stored in 'result'. */ -QVector<QRect> MinOverlapPlacer::findMaxOverlappers(const QRect &domain, const QVector<QRect> &source) +QList<QRect> MinOverlapPlacer::findMaxOverlappers(const QRect &domain, const QList<QRect> &source) { - QVector<QRect> result; + QList<QRect> result; result.reserve(source.size()); int maxOverlap = -1; @@ -530,15 +481,15 @@ QVector<QRect> MinOverlapPlacer::findMaxOverlappers(const QRect &domain, const Q placement that overlaps the rectangles in 'rects' as little as possible while at the same time being as much as possible inside 'domain'. */ -QPoint MinOverlapPlacer::findBestPlacement(const QRect &domain, const QVector<QRect> &rects, - QVector<QRect> &source) +QPoint MinOverlapPlacer::findBestPlacement(const QRect &domain, const QList<QRect> &rects, + QList<QRect> &source) { - const QVector<QRect> nonInsiders = findNonInsiders(domain, source); + const QList<QRect> nonInsiders = findNonInsiders(domain, source); if (!source.empty()) return findMinOverlapRect(source, rects).topLeft(); - QVector<QRect> maxOverlappers = findMaxOverlappers(domain, nonInsiders); + QList<QRect> maxOverlappers = findMaxOverlappers(domain, nonInsiders); return findMinOverlapRect(maxOverlappers, rects).topLeft(); } @@ -549,7 +500,7 @@ QPoint MinOverlapPlacer::findBestPlacement(const QRect &domain, const QVector<QR overlaps 'rects' as little as possible and 'domain' as much as possible. Returns the position of the resulting rectangle. */ -QPoint MinOverlapPlacer::place(const QSize &size, const QVector<QRect> &rects, +QPoint MinOverlapPlacer::place(const QSize &size, const QList<QRect> &rects, const QRect &domain) const { if (size.isEmpty() || !domain.isValid()) @@ -559,7 +510,7 @@ QPoint MinOverlapPlacer::place(const QSize &size, const QVector<QRect> &rects, return QPoint(); } - QVector<QRect> candidates = getCandidatePlacements(size, rects, domain); + QList<QRect> candidates = getCandidatePlacements(size, rects, domain); return findBestPlacement(domain, rects, candidates); } @@ -584,12 +535,12 @@ private: */ void QMdiAreaTabBar::mousePressEvent(QMouseEvent *event) { - if (event->button() != Qt::MidButton) { + if (event->button() != Qt::MiddleButton) { QTabBar::mousePressEvent(event); return; } - QMdiSubWindow *subWindow = subWindowFromIndex(tabAt(event->pos())); + QMdiSubWindow *subWindow = subWindowFromIndex(tabAt(event->position().toPoint())); if (!subWindow) { event->ignore(); return; @@ -648,7 +599,7 @@ void QMdiAreaTabBar::contextMenuEvent(QContextMenuEvent *event) QMdiSubWindow *QMdiAreaTabBar::subWindowFromIndex(int index) const { if (index < 0 || index >= count()) - return 0; + return nullptr; QMdiArea *mdiArea = qobject_cast<QMdiArea *>(parentWidget()); Q_ASSERT(mdiArea); @@ -667,15 +618,15 @@ QMdiSubWindow *QMdiAreaTabBar::subWindowFromIndex(int index) const \internal */ QMdiAreaPrivate::QMdiAreaPrivate() - : cascader(0), - regularTiler(0), - iconTiler(0), - placer(0), + : cascader(nullptr), + regularTiler(nullptr), + iconTiler(nullptr), + placer(nullptr), #if QT_CONFIG(rubberband) - rubberBand(0), + rubberBand(nullptr), #endif #if QT_CONFIG(tabbar) - tabBar(0), + tabBar(nullptr), #endif activationOrder(QMdiArea::CreationOrder), viewMode(QMdiArea::SubWindowView), @@ -720,12 +671,16 @@ void QMdiAreaPrivate::_q_deactivateAllWindows(QMdiSubWindow *aboutToActivate) aboutToBecomeActive = aboutToActivate; Q_ASSERT(aboutToBecomeActive); - foreach (QMdiSubWindow *child, childWindows) { + // Take a copy because child->showNormal() could indirectly call + // QCoreApplication::sendEvent(), which could call unknown code that e.g. + // recurses into the class modifying childWindows. + const auto subWindows = childWindows; + for (QMdiSubWindow *child : subWindows) { if (!sanityCheck(child, "QMdiArea::deactivateAllWindows") || aboutToBecomeActive == child) continue; // We don't want to handle signals caused by child->showNormal(). ignoreWindowStateChange = true; - if(!(options & QMdiArea::DontMaximizeSubWindowOnActivation) && !showActiveWindowMaximized) + if (!(options & QMdiArea::DontMaximizeSubWindowOnActivation) && !showActiveWindowMaximized) showActiveWindowMaximized = child->isMaximized() && child->isVisible(); if (showActiveWindowMaximized && child->isMaximized()) { if (q->updatesEnabled()) { @@ -787,7 +742,7 @@ void QMdiAreaPrivate::_q_currentTabChanged(int index) // If the previous active sub-window was hidden, disable the tab. if (indexToLastActiveTab >= 0 && indexToLastActiveTab < tabBar->count() - && indexToLastActiveTab < childWindows.count()) { + && indexToLastActiveTab < childWindows.size()) { QMdiSubWindow *lastActive = childWindows.at(indexToLastActiveTab); if (lastActive && lastActive->isHidden()) tabBar->setTabEnabled(indexToLastActiveTab, false); @@ -861,7 +816,7 @@ void QMdiAreaPrivate::appendChild(QMdiSubWindow *child) if (tabBar) { tabBar->addTab(child->windowIcon(), tabTextFor(child)); updateTabBarGeometry(); - if (childWindows.count() == 1 && !(options & QMdiArea::DontMaximizeSubWindowOnActivation)) + if (childWindows.size() == 1 && !(options & QMdiArea::DontMaximizeSubWindowOnActivation)) showActiveWindowMaximized = true; } #endif @@ -892,10 +847,10 @@ void QMdiAreaPrivate::place(Placer *placer, QMdiSubWindow *child) return; } - QVector<QRect> rects; + QList<QRect> rects; rects.reserve(childWindows.size()); QRect parentRect = q->rect(); - foreach (QMdiSubWindow *window, childWindows) { + for (QMdiSubWindow *window : std::as_const(childWindows)) { if (!sanityCheck(window, "QMdiArea::place") || window == child || !window->isVisibleTo(q) || !window->testAttribute(Qt::WA_Moved)) { continue; @@ -937,7 +892,7 @@ void QMdiAreaPrivate::rearrange(Rearranger *rearranger) const bool reverseList = rearranger->type() == Rearranger::RegularTiler; const QList<QMdiSubWindow *> subWindows = subWindowList(activationOrder, reverseList); QSize minSubWindowSize; - foreach (QMdiSubWindow *child, subWindows) { + for (QMdiSubWindow *child : subWindows) { if (!sanityCheck(child, "QMdiArea::rearrange") || !child->isVisible()) continue; if (rearranger->type() == Rearranger::IconTiler) { @@ -954,17 +909,9 @@ void QMdiAreaPrivate::rearrange(Rearranger *rearranger) } } - if (active && rearranger->type() == Rearranger::RegularTiler && !tileCalledFromResizeEvent) { - // Move active window in front if necessary. That's the case if we - // have any windows with staysOnTopHint set. - int indexToActive = widgets.indexOf((QWidget *)active); - if (indexToActive > 0) - widgets.move(indexToActive, 0); - } - QRect domain = viewport->rect(); if (rearranger->type() == Rearranger::RegularTiler && !widgets.isEmpty()) - domain = resizeToMinimumTileSize(minSubWindowSize, widgets.count()); + domain = resizeToMinimumTileSize(minSubWindowSize, widgets.size()); rearranger->rearrange(widgets, domain); @@ -1010,6 +957,10 @@ void QMdiAreaPrivate::activateWindow(QMdiSubWindow *child) if (child->isHidden() || child == active) return; + + if (child->d_func()->isActive && active == nullptr) + child->d_func()->isActive = false; + child->d_func()->setActive(true); } @@ -1078,7 +1029,7 @@ void QMdiAreaPrivate::emitWindowActivated(QMdiSubWindow *activeWindow) Q_ASSERT(aboutToBecomeActive == activeWindow); active = activeWindow; - aboutToBecomeActive = 0; + aboutToBecomeActive = nullptr; Q_ASSERT(active->d_func()->isActive); #if QT_CONFIG(tabbar) @@ -1101,20 +1052,20 @@ void QMdiAreaPrivate::resetActiveWindow(QMdiSubWindow *deactivatedWindow) if (deactivatedWindow) { if (deactivatedWindow != active) return; - active = 0; + active = nullptr; if ((aboutToBecomeActive || isActivated || lastWindowAboutToBeDestroyed()) && !isExplicitlyDeactivated(deactivatedWindow) && !q->window()->isMinimized()) { return; } - emit q->subWindowActivated(0); + emit q->subWindowActivated(nullptr); return; } if (aboutToBecomeActive) return; - active = 0; - emit q->subWindowActivated(0); + active = nullptr; + emit q->subWindowActivated(nullptr); } /*! @@ -1179,7 +1130,7 @@ void QMdiAreaPrivate::updateScrollBars() QSize hbarExtent = hbar->sizeHint(); QSize vbarExtent = vbar->sizeHint(); - if (q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, 0, q)) { + if (q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, nullptr, q)) { const int doubleFrameWidth = frameWidth * 2; if (hbarpolicy == Qt::ScrollBarAlwaysOn) maxSize.rheight() -= doubleFrameWidth; @@ -1240,7 +1191,7 @@ void QMdiAreaPrivate::internalRaise(QMdiSubWindow *mdiChild) const if (!sanityCheck(mdiChild, "QMdiArea::internalRaise") || childWindows.size() < 2) return; - QMdiSubWindow *stackUnderChild = 0; + QMdiSubWindow *stackUnderChild = nullptr; if (!windowStaysOnTop(mdiChild)) { const auto children = viewport->children(); // take a copy, as raising/stacking under changes the order for (QObject *object : children) { @@ -1290,13 +1241,17 @@ QRect QMdiAreaPrivate::resizeToMinimumTileSize(const QSize &minSubWindowSize, in minAreaHeight += hbar->height(); if (vbar->isVisible()) minAreaWidth += vbar->width(); - if (q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, 0, q)) { - const int frame = q->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, q); + if (q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, nullptr, q)) { + const int frame = q->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, q); minAreaWidth += 2 * frame; minAreaHeight += 2 * frame; } const QSize diff = QSize(minAreaWidth, minAreaHeight).expandedTo(q->size()) - q->size(); - topLevel->resize(topLevel->size() + diff); + // Only resize topLevel widget if scroll bars are disabled. + if (hbarpolicy == Qt::ScrollBarAlwaysOff) + topLevel->resize(topLevel->size().width() + diff.width(), topLevel->size().height()); + if (vbarpolicy == Qt::ScrollBarAlwaysOff) + topLevel->resize(topLevel->size().width(), topLevel->size().height() + diff.height()); } QRect domain = viewport->rect(); @@ -1333,7 +1288,7 @@ bool QMdiAreaPrivate::scrollBarsEnabled() const */ bool QMdiAreaPrivate::lastWindowAboutToBeDestroyed() const { - if (childWindows.count() != 1) + if (childWindows.size() != 1) return false; QMdiSubWindow *last = childWindows.at(0); @@ -1351,7 +1306,7 @@ bool QMdiAreaPrivate::lastWindowAboutToBeDestroyed() const */ void QMdiAreaPrivate::setChildActivationEnabled(bool enable, bool onlyNextActivationEvent) const { - foreach (QMdiSubWindow *subWindow, childWindows) { + for (QMdiSubWindow *subWindow : childWindows) { if (!subWindow || !subWindow->isVisible()) continue; if (onlyNextActivationEvent) @@ -1373,7 +1328,11 @@ void QMdiAreaPrivate::scrollBarPolicyChanged(Qt::Orientation orientation, Qt::Sc const QMdiSubWindow::SubWindowOption option = orientation == Qt::Horizontal ? QMdiSubWindow::AllowOutsideAreaHorizontally : QMdiSubWindow::AllowOutsideAreaVertically; const bool enable = policy != Qt::ScrollBarAlwaysOff; - foreach (QMdiSubWindow *child, childWindows) { + // Take a copy because child->setOption() may indirectly call QCoreApplication::sendEvent(), + // the latter could call unknown code that could e.g. recurse into the class + // modifying childWindows. + const auto subWindows = childWindows; + for (QMdiSubWindow *child : subWindows) { if (!sanityCheck(child, "QMdiArea::scrollBarPolicyChanged")) continue; child->setOption(option, enable); @@ -1389,7 +1348,7 @@ QMdiAreaPrivate::subWindowList(QMdiArea::WindowOrder order, bool reversed) const return list; if (order == QMdiArea::CreationOrder) { - foreach (QMdiSubWindow *child, childWindows) { + for (QMdiSubWindow *child : childWindows) { if (!child) continue; if (!reversed) @@ -1409,7 +1368,7 @@ QMdiAreaPrivate::subWindowList(QMdiArea::WindowOrder order, bool reversed) const } } else { // ActivationHistoryOrder Q_ASSERT(indicesToActivatedChildren.size() == childWindows.size()); - for (int i = indicesToActivatedChildren.count() - 1; i >= 0; --i) { + for (int i = indicesToActivatedChildren.size() - 1; i >= 0; --i) { QMdiSubWindow *child = childWindows.at(indicesToActivatedChildren.at(i)); if (!child) continue; @@ -1431,7 +1390,7 @@ void QMdiAreaPrivate::disconnectSubWindow(QObject *subWindow) return; Q_Q(QMdiArea); - QObject::disconnect(subWindow, 0, q, 0); + QObject::disconnect(subWindow, nullptr, q, nullptr); subWindow->removeEventFilter(q); } @@ -1442,11 +1401,11 @@ QMdiSubWindow *QMdiAreaPrivate::nextVisibleSubWindow(int increaseFactor, QMdiAre int removedIndex, int fromIndex) const { if (childWindows.isEmpty()) - return 0; + return nullptr; Q_Q(const QMdiArea); const QList<QMdiSubWindow *> subWindows = q->subWindowList(order); - QMdiSubWindow *current = 0; + QMdiSubWindow *current = nullptr; if (removedIndex < 0) { if (fromIndex >= 0 && fromIndex < subWindows.size()) @@ -1486,7 +1445,7 @@ QMdiSubWindow *QMdiAreaPrivate::nextVisibleSubWindow(int increaseFactor, QMdiAre if (!subWindows.at(index)->isHidden()) return subWindows.at(index); - return 0; + return nullptr; } /*! @@ -1517,7 +1476,7 @@ void QMdiAreaPrivate::highlightNextSubWindow(int increaseFactor) if (!rubberBand) { rubberBand = new QRubberBand(QRubberBand::Rectangle, q); // For accessibility to identify this special widget. - rubberBand->setObjectName(QLatin1String("qt_rubberband")); + rubberBand->setObjectName("qt_rubberband"_L1); rubberBand->setWindowFlags(rubberBand->windowFlags() | Qt::WindowStaysOnTopHint); } #endif @@ -1570,12 +1529,17 @@ void QMdiAreaPrivate::setViewMode(QMdiArea::ViewMode mode) tabBar->setTabsClosable(tabsClosable); tabBar->setMovable(tabsMovable); #if QT_CONFIG(tabwidget) - tabBar->setShape(tabBarShapeFrom(tabShape, tabPosition)); + tabBar->setShape(_q_tb_tabBarShapeFrom(tabShape, tabPosition)); #endif isSubWindowsTiled = false; - foreach (QMdiSubWindow *subWindow, childWindows) + // Take a copy as tabBar->addTab() will (indirectly) create a connection between + // the tab close button clicked() signal and the _q_closeTab() slot, which may + // indirectly call QCoreApplication::sendEvent(), the latter could result in + // invoking unknown code that could e.g. recurse into the class modifying childWindows. + const auto subWindows = childWindows; + for (QMdiSubWindow *subWindow : subWindows) tabBar->addTab(subWindow->windowIcon(), tabTextFor(subWindow)); QMdiSubWindow *current = q->currentSubWindow(); @@ -1607,7 +1571,7 @@ void QMdiAreaPrivate::setViewMode(QMdiArea::ViewMode mode) { // SubWindowView #if QT_CONFIG(tabbar) delete tabBar; - tabBar = 0; + tabBar = nullptr; #endif // QT_CONFIG(tabbar) viewMode = mode; @@ -1634,7 +1598,7 @@ void QMdiAreaPrivate::updateTabBarGeometry() Q_Q(QMdiArea); #if QT_CONFIG(tabwidget) - Q_ASSERT(tabBarShapeFrom(tabShape, tabPosition) == tabBar->shape()); + Q_ASSERT(_q_tb_tabBarShapeFrom(tabShape, tabPosition) == tabBar->shape()); #endif const QSize tabBarSizeHint = tabBar->sizeHint(); @@ -1691,7 +1655,7 @@ void QMdiAreaPrivate::refreshTabBar() tabBar->setTabsClosable(tabsClosable); tabBar->setMovable(tabsMovable); #if QT_CONFIG(tabwidget) - tabBar->setShape(tabBarShapeFrom(tabShape, tabPosition)); + tabBar->setShape(_q_tb_tabBarShapeFrom(tabShape, tabPosition)); #endif updateTabBarGeometry(); } @@ -1708,7 +1672,7 @@ QMdiArea::QMdiArea(QWidget *parent) setFrameStyle(QFrame::NoFrame); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setViewport(0); + setViewport(nullptr); setFocusPolicy(Qt::NoFocus); QApplication::instance()->installEventFilter(this); } @@ -1720,16 +1684,16 @@ QMdiArea::~QMdiArea() { Q_D(QMdiArea); delete d->cascader; - d->cascader = 0; + d->cascader = nullptr; delete d->regularTiler; - d->regularTiler = 0; + d->regularTiler = nullptr; delete d->iconTiler; - d->iconTiler = 0; + d->iconTiler = nullptr; delete d->placer; - d->placer = 0; + d->placer = nullptr; } /*! @@ -1737,7 +1701,7 @@ QMdiArea::~QMdiArea() */ QSize QMdiArea::sizeHint() const { - // Calculate a proper scale factor for QDesktopWidget::size(). + // Calculate a proper scale factor for the desktop's size. // This also takes into account that we can have nested workspaces. int nestedCount = 0; QWidget *widget = this->parentWidget(); @@ -1748,14 +1712,14 @@ QSize QMdiArea::sizeHint() const } const int scaleFactor = 3 * (nestedCount + 1); - QSize desktopSize = QDesktopWidgetPrivate::size(); + QSize desktopSize = QGuiApplication::primaryScreen()->virtualSize(); QSize size(desktopSize.width() * 2 / scaleFactor, desktopSize.height() * 2 / scaleFactor); for (QMdiSubWindow *child : d_func()->childWindows) { if (!sanityCheck(child, "QMdiArea::sizeHint")) continue; size = size.expandedTo(child->sizeHint()); } - return size.expandedTo(QApplication::globalStrut()); + return size; } /*! @@ -1764,8 +1728,8 @@ QSize QMdiArea::sizeHint() const QSize QMdiArea::minimumSizeHint() const { Q_D(const QMdiArea); - QSize size(style()->pixelMetric(QStyle::PM_MdiSubWindowMinimizedWidth, 0, this), - style()->pixelMetric(QStyle::PM_TitleBarHeight, 0, this)); + QSize size(style()->pixelMetric(QStyle::PM_MdiSubWindowMinimizedWidth, nullptr, this), + style()->pixelMetric(QStyle::PM_TitleBarHeight, nullptr, this)); size = size.expandedTo(QAbstractScrollArea::minimumSizeHint()); if (!d->scrollBarsEnabled()) { for (QMdiSubWindow *child : d->childWindows) { @@ -1774,11 +1738,11 @@ QSize QMdiArea::minimumSizeHint() const size = size.expandedTo(child->minimumSizeHint()); } } - return size.expandedTo(QApplication::globalStrut()); + return size; } /*! - Returns a pointer to the current subwindow, or 0 if there is + Returns a pointer to the current subwindow, or \nullptr if there is no current subwindow. This function will return the same as activeSubWindow() if @@ -1790,15 +1754,15 @@ QMdiSubWindow *QMdiArea::currentSubWindow() const { Q_D(const QMdiArea); if (d->childWindows.isEmpty()) - return 0; + return nullptr; if (d->active) return d->active; if (d->isActivated && !window()->isMinimized()) - return 0; + return nullptr; - Q_ASSERT(d->indicesToActivatedChildren.count() > 0); + Q_ASSERT(d->indicesToActivatedChildren.size() > 0); int index = d->indicesToActivatedChildren.at(0); Q_ASSERT(index >= 0 && index < d->childWindows.size()); QMdiSubWindow *current = d->childWindows.at(index); @@ -1808,7 +1772,7 @@ QMdiSubWindow *QMdiArea::currentSubWindow() const /*! Returns a pointer to the current active subwindow. If no - window is currently active, 0 is returned. + window is currently active, \nullptr is returned. Subwindows are treated as top-level windows with respect to window state, i.e., if a widget outside the MDI area is the active @@ -1825,7 +1789,7 @@ QMdiSubWindow *QMdiArea::activeSubWindow() const } /*! - Activates the subwindow \a window. If \a window is 0, any + Activates the subwindow \a window. If \a window is \nullptr, any current active window is deactivated. \sa activeSubWindow() @@ -1834,7 +1798,7 @@ void QMdiArea::setActiveSubWindow(QMdiSubWindow *window) { Q_D(QMdiArea); if (!window) { - d->activateWindow(0); + d->activateWindow(nullptr); return; } @@ -1897,7 +1861,11 @@ void QMdiArea::closeAllSubWindows() return; d->isSubWindowsTiled = false; - foreach (QMdiSubWindow *child, d->childWindows) { + // Take a copy because the child->close() call below may end up indirectly calling + // QCoreApplication::send{Spontaneous}Event(), which may call unknown code that + // could e.g. recurse into the class modifying d->childWindows. + const auto subWindows = d->childWindows; + for (QMdiSubWindow *child : subWindows) { if (!sanityCheck(child, "QMdiArea::closeAllSubWindows")) continue; child->close(); @@ -1954,7 +1922,7 @@ void QMdiArea::activatePreviousSubWindow() \note Once the subwindow has been added, its parent will be the \e{viewport widget} of the QMdiArea. - \snippet mdiareasnippets.cpp 1 + \snippet mdiarea/mdiareasnippets.cpp 1 When you create your own subwindow, you must set the Qt::WA_DeleteOnClose widget attribute if you want the window to be @@ -1969,7 +1937,7 @@ QMdiSubWindow *QMdiArea::addSubWindow(QWidget *widget, Qt::WindowFlags windowFla { if (Q_UNLIKELY(!widget)) { qWarning("QMdiArea::addSubWindow: null pointer to widget"); - return 0; + return nullptr; } Q_D(QMdiArea); @@ -1992,9 +1960,11 @@ QMdiSubWindow *QMdiArea::addSubWindow(QWidget *widget, Qt::WindowFlags windowFla Q_ASSERT(child->testAttribute(Qt::WA_DeleteOnClose)); } + d->appendChild(child); + if (childFocus) childFocus->setFocus(); - d->appendChild(child); + return child; } @@ -2002,9 +1972,9 @@ QMdiSubWindow *QMdiArea::addSubWindow(QWidget *widget, Qt::WindowFlags windowFla Removes \a widget from the MDI area. The \a widget must be either a QMdiSubWindow or a widget that is the internal widget of a subwindow. Note \a widget is never actually deleted by QMdiArea. - If a QMdiSubWindow is passed in its parent is set to 0 and it is - removed, but if an internal widget is passed in the child widget - is set to 0 but the QMdiSubWindow is not removed. + If a QMdiSubWindow is passed in, its parent is set to \nullptr and it is + removed; but if an internal widget is passed in, the child widget + is set to \nullptr and the QMdiSubWindow is \e not removed. \sa addSubWindow() */ @@ -2029,16 +1999,20 @@ void QMdiArea::removeSubWindow(QWidget *widget) d->childWindows.removeAll(child); d->indicesToActivatedChildren.removeAll(index); d->updateActiveWindow(index, d->active == child); - child->setParent(0); + child->setParent(nullptr); return; } bool found = false; - foreach (QMdiSubWindow *child, d->childWindows) { + // Take a copy because child->setWidget(nullptr) will indirectly + // QCoreApplication::sendEvent(); the latter could call unknown code that could + // e.g. recurse into the class modifying d->childWindows. + const auto subWindows = d->childWindows; + for (QMdiSubWindow *child : subWindows) { if (!sanityCheck(child, "QMdiArea::removeSubWindow")) continue; if (child->widget() == widget) { - child->setWidget(0); + child->setWidget(nullptr); Q_ASSERT(!child->widget()); found = true; break; @@ -2315,10 +2289,21 @@ void QMdiArea::resizeEvent(QResizeEvent *resizeEvent) // Resize maximized views. bool hasMaximizedSubWindow = false; - foreach (QMdiSubWindow *child, d->childWindows) { + // Take a copy because child->resize() may call QCoreApplication::sendEvent() + // which may invoke unknown code, that could e.g. recurse into the class + // modifying d->childWindows. + const auto subWindows = d->childWindows; + for (QMdiSubWindow *child : subWindows) { if (sanityCheck(child, "QMdiArea::resizeEvent") && child->isMaximized() && child->size() != resizeEvent->size()) { - child->resize(resizeEvent->size()); + auto realSize = resizeEvent->size(); + const auto minSizeHint = child->minimumSizeHint(); + // QMdiSubWindow is no tlw so minimumSize() is not set by the layout manager + // and therefore we have to take care by ourself that we're not getting smaller + // than allowed + if (minSizeHint.isValid()) + realSize = realSize.expandedTo(minSizeHint); + child->resize(realSize); if (!hasMaximizedSubWindow) hasMaximizedSubWindow = true; } @@ -2367,7 +2352,9 @@ void QMdiArea::showEvent(QShowEvent *showEvent) Q_D(QMdiArea); if (!d->pendingRearrangements.isEmpty()) { bool skipPlacement = false; - foreach (Rearranger *rearranger, d->pendingRearrangements) { + // Take a copy because d->rearrange() may modify d->pendingRearrangements + const auto pendingRearrange = d->pendingRearrangements; + for (Rearranger *rearranger : pendingRearrange) { // If this is the case, we don't have to lay out pending child windows // since the rearranger will find a placement for them. if (rearranger->type() != Rearranger::IconTiler && !skipPlacement) @@ -2381,9 +2368,19 @@ void QMdiArea::showEvent(QShowEvent *showEvent) } if (!d->pendingPlacements.isEmpty()) { - foreach (QMdiSubWindow *window, d->pendingPlacements) { + // Nothing obvious in the loop body changes the container (in this code path) + // during iteration, this is calling into a non-const method that does change + // the container when called from other places. So take a copy anyway for good + // measure. + const auto copy = d->pendingPlacements; + for (QMdiSubWindow *window : copy) { if (!window) continue; + if (d->viewMode == TabbedView && window->d_func()->isActive && !d->active) { + d->showActiveWindowMaximized = true; + d->emitWindowActivated(window); // Also maximizes the window + continue; + } if (!window->testAttribute(Qt::WA_Resized)) { QSize newSize(window->sizeHint().boundedTo(viewport()->size())); window->resize(newSize.expandedTo(qSmartMinSize(window))); @@ -2495,13 +2492,6 @@ bool QMdiArea::event(QEvent *event) { Q_D(QMdiArea); switch (event->type()) { -#if 0 // Used to be included in Qt4 for Q_WS_WIN - // QWidgetPrivate::hide_helper activates another sub-window when closing a - // modal dialog on Windows (see activateWindow() inside the ifdef). - case QEvent::WindowUnblocked: - d->activateCurrentWindow(); - break; -#endif case QEvent::WindowActivate: { d->isActivated = true; if (d->childWindows.isEmpty()) @@ -2524,12 +2514,16 @@ bool QMdiArea::event(QEvent *event) d->isSubWindowsTiled = true; } break; - case QEvent::WindowIconChange: - foreach (QMdiSubWindow *window, d->childWindows) { + case QEvent::WindowIconChange: { + // Take a copy because QCoreApplication::sendEvent() may call unknown code, + // that may cause recursing into the class + const auto subWindows = d->childWindows; + for (QMdiSubWindow *window : subWindows) { if (sanityCheck(window, "QMdiArea::WindowIconChange")) - QApplication::sendEvent(window, event); + QCoreApplication::sendEvent(window, event); } break; + } case QEvent::Hide: d->setActive(d->active, false, false); d->setChildActivationEnabled(false); @@ -2558,12 +2552,8 @@ bool QMdiArea::eventFilter(QObject *object, QEvent *event) if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) { QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); - // Ingore key events without a Ctrl modifier (except for press/release on the modifier itself). -#if 0 // Used to be included in Qt4 for Q_WS_MAC - if (!(keyEvent->modifiers() & Qt::MetaModifier) && keyEvent->key() != Qt::Key_Meta) -#else + // Ignore key events without a Ctrl modifier (except for press/release on the modifier itself). if (!(keyEvent->modifiers() & Qt::ControlModifier) && keyEvent->key() != Qt::Key_Control) -#endif return QAbstractScrollArea::eventFilter(object, event); // Find closest mdi area (in case we have a nested workspace). @@ -2578,11 +2568,7 @@ bool QMdiArea::eventFilter(QObject *object, QEvent *event) // 3) Ctrl-Shift-Tab (Tab, Tab, ...) -> iterate through all windows in the opposite // direction (activatePreviousSubWindow()) switch (keyEvent->key()) { -#if 0 // Used to be included in Qt4 for Q_WS_MAC - case Qt::Key_Meta: -#else case Qt::Key_Control: -#endif if (keyPress) area->d_func()->startTabToPreviousTimer(); else @@ -2640,7 +2626,11 @@ bool QMdiArea::eventFilter(QObject *object, QEvent *event) #endif // QT_CONFIG(tabbar) Q_FALLTHROUGH(); case QEvent::Hide: - d->isSubWindowsTiled = false; + // Do not reset the isSubWindowsTiled flag if the event is a spontaneous system window event. + // This ensures that tiling will be performed during the resizeEvent after an application + // window minimize (hide) and then restore (show). + if (!event->spontaneous()) + d->isSubWindowsTiled = false; break; #if QT_CONFIG(rubberband) case QEvent::Close: @@ -2688,7 +2678,10 @@ void QMdiArea::setupViewport(QWidget *viewport) Q_D(QMdiArea); if (viewport) viewport->setAttribute(Qt::WA_OpaquePaintEvent, d->background.isOpaque()); - foreach (QMdiSubWindow *child, d->childWindows) { + // Take a copy because the child->setParent() call below may call QCoreApplication::sendEvent() + // which may call unknown code that could e.g. recurse into the class modifying d->childWindows. + const auto subWindows = d->childWindows; + for (QMdiSubWindow *child : subWindows) { if (!sanityCheck(child, "QMdiArea::setupViewport")) continue; child->setParent(viewport, child->windowFlags()); |