| /**************************************************************************** |
| ** |
| ** 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$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qlayout.h" |
| |
| #include "qapplication.h" |
| #include "qlayoutengine_p.h" |
| #if QT_CONFIG(menubar) |
| #include "qmenubar.h" |
| #endif |
| #if QT_CONFIG(toolbar) |
| #include "qtoolbar.h" |
| #endif |
| #if QT_CONFIG(sizegrip) |
| #include "qsizegrip.h" |
| #endif |
| #include "qevent.h" |
| #include "qstyle.h" |
| #include "qvariant.h" |
| #include "qwidget_p.h" |
| #include "qlayout_p.h" |
| #if QT_CONFIG(formlayout) |
| #include "qformlayout.h" |
| #endif |
| |
| QT_BEGIN_NAMESPACE |
| |
| static int menuBarHeightForWidth(QWidget *menubar, int w) |
| { |
| if (menubar && !menubar->isHidden() && !menubar->isWindow()) { |
| int result = menubar->heightForWidth(qMax(w, menubar->minimumWidth())); |
| if (result == -1) |
| result = menubar->sizeHint().height(); |
| const int min = qSmartMinSize(menubar).height(); |
| result = qBound(min, result, menubar->maximumSize().height()); |
| if (result != -1) |
| return result; |
| } |
| return 0; |
| } |
| |
| /*! |
| \class QLayout |
| \brief The QLayout class is the base class of geometry managers. |
| |
| \ingroup geomanagement |
| \inmodule QtWidgets |
| |
| This is an abstract base class inherited by the concrete classes |
| QBoxLayout, QGridLayout, QFormLayout, and QStackedLayout. |
| |
| For users of QLayout subclasses or of QMainWindow there is seldom |
| any need to use the basic functions provided by QLayout, such as |
| setSizeConstraint() or setMenuBar(). See \l{Layout Management} |
| for more information. |
| |
| To make your own layout manager, implement the functions |
| addItem(), sizeHint(), setGeometry(), itemAt() and takeAt(). You |
| should also implement minimumSize() to ensure your layout isn't |
| resized to zero size if there is too little space. To support |
| children whose heights depend on their widths, implement |
| hasHeightForWidth() and heightForWidth(). See the |
| \l{layouts/borderlayout}{Border Layout} and |
| \l{layouts/flowlayout}{Flow Layout} examples for |
| more information about implementing custom layout managers. |
| |
| Geometry management stops when the layout manager is deleted. |
| |
| \sa QLayoutItem, {Layout Management}, {Basic Layouts Example}, |
| {Border Layout Example}, {Flow Layout Example} |
| */ |
| |
| |
| /*! |
| Constructs a new top-level QLayout, with parent \a parent. |
| \a parent may not be \nullptr. |
| |
| The layout is set directly as the top-level layout for |
| \a parent. There can be only one top-level layout for a |
| widget. It is returned by QWidget::layout(). |
| */ |
| QLayout::QLayout(QWidget *parent) |
| : QObject(*new QLayoutPrivate, parent) |
| { |
| if (!parent) |
| return; |
| parent->setLayout(this); |
| } |
| |
| /*! |
| Constructs a new child QLayout. |
| |
| This layout has to be inserted into another layout before geometry |
| management will work. |
| */ |
| QLayout::QLayout() |
| : QObject(*new QLayoutPrivate, 0) |
| { |
| } |
| |
| |
| /*! \internal |
| */ |
| QLayout::QLayout(QLayoutPrivate &dd, QLayout *lay, QWidget *w) |
| : QObject(dd, lay ? static_cast<QObject*>(lay) : static_cast<QObject*>(w)) |
| { |
| Q_D(QLayout); |
| if (lay) { |
| lay->addItem(this); |
| } else if (w) { |
| if (Q_UNLIKELY(w->layout())) { |
| qWarning("QLayout: Attempting to add QLayout \"%ls\" to %s \"%ls\", which" |
| " already has a layout", |
| qUtf16Printable(QObject::objectName()), w->metaObject()->className(), |
| qUtf16Printable(w->objectName())); |
| setParent(0); |
| } else { |
| d->topLevel = true; |
| w->d_func()->layout = this; |
| QT_TRY { |
| invalidate(); |
| } QT_CATCH(...) { |
| w->d_func()->layout = 0; |
| QT_RETHROW; |
| } |
| } |
| } |
| } |
| |
| QLayoutPrivate::QLayoutPrivate() |
| : QObjectPrivate(), insideSpacing(-1), userLeftMargin(-1), userTopMargin(-1), userRightMargin(-1), |
| userBottomMargin(-1), topLevel(false), enabled(true), activated(true), autoNewChild(false), |
| constraint(QLayout::SetDefaultConstraint), menubar(0) |
| { |
| } |
| |
| void QLayoutPrivate::getMargin(int *result, int userMargin, QStyle::PixelMetric pm) const |
| { |
| if (!result) |
| return; |
| |
| Q_Q(const QLayout); |
| if (userMargin >= 0) { |
| *result = userMargin; |
| } else if (!topLevel) { |
| *result = 0; |
| } else if (QWidget *pw = q->parentWidget()) { |
| *result = pw->style()->pixelMetric(pm, 0, pw); |
| } else { |
| *result = 0; |
| } |
| } |
| |
| // Static item factory functions that allow for hooking things in Designer |
| |
| QLayoutPrivate::QWidgetItemFactoryMethod QLayoutPrivate::widgetItemFactoryMethod = 0; |
| QLayoutPrivate::QSpacerItemFactoryMethod QLayoutPrivate::spacerItemFactoryMethod = 0; |
| |
| QWidgetItem *QLayoutPrivate::createWidgetItem(const QLayout *layout, QWidget *widget) |
| { |
| if (widgetItemFactoryMethod) |
| if (QWidgetItem *wi = (*widgetItemFactoryMethod)(layout, widget)) |
| return wi; |
| return new QWidgetItemV2(widget); |
| } |
| |
| QSpacerItem *QLayoutPrivate::createSpacerItem(const QLayout *layout, int w, int h, QSizePolicy::Policy hPolicy, QSizePolicy::Policy vPolicy) |
| { |
| if (spacerItemFactoryMethod) |
| if (QSpacerItem *si = (*spacerItemFactoryMethod)(layout, w, h, hPolicy, vPolicy)) |
| return si; |
| return new QSpacerItem(w, h, hPolicy, vPolicy); |
| } |
| |
| |
| |
| /*! |
| \fn void QLayout::addItem(QLayoutItem *item) |
| |
| Implemented in subclasses to add an \a item. How it is added is |
| specific to each subclass. |
| |
| This function is not usually called in application code. To add a widget |
| to a layout, use the addWidget() function; to add a child layout, use the |
| addLayout() function provided by the relevant QLayout subclass. |
| |
| \b{Note:} The ownership of \a item is transferred to the layout, and it's |
| the layout's responsibility to delete it. |
| |
| \sa addWidget(), QBoxLayout::addLayout(), QGridLayout::addLayout() |
| */ |
| |
| /*! |
| Adds widget \a w to this layout in a manner specific to the |
| layout. This function uses addItem(). |
| */ |
| void QLayout::addWidget(QWidget *w) |
| { |
| addChildWidget(w); |
| addItem(QLayoutPrivate::createWidgetItem(this, w)); |
| } |
| |
| |
| |
| /*! |
| Sets the alignment for widget \a w to \a alignment and returns |
| true if \a w is found in this layout (not including child |
| layouts); otherwise returns \c false. |
| */ |
| bool QLayout::setAlignment(QWidget *w, Qt::Alignment alignment) |
| { |
| int i = 0; |
| QLayoutItem *item = itemAt(i); |
| while (item) { |
| if (item->widget() == w) { |
| item->setAlignment(alignment); |
| invalidate(); |
| return true; |
| } |
| ++i; |
| item = itemAt(i); |
| } |
| return false; |
| } |
| |
| /*! |
| \overload |
| |
| Sets the alignment for the layout \a l to \a alignment and |
| returns \c true if \a l is found in this layout (not including child |
| layouts); otherwise returns \c false. |
| */ |
| bool QLayout::setAlignment(QLayout *l, Qt::Alignment alignment) |
| { |
| int i = 0; |
| QLayoutItem *item = itemAt(i); |
| while (item) { |
| if (item->layout() == l) { |
| item->setAlignment(alignment); |
| invalidate(); |
| return true; |
| } |
| ++i; |
| item = itemAt(i); |
| } |
| return false; |
| } |
| |
| #if QT_DEPRECATED_SINCE(5, 13) |
| /*! |
| \property QLayout::margin |
| \brief the width of the outside border of the layout |
| \obsolete |
| |
| Use setContentsMargins() and getContentsMargins() instead. |
| |
| \sa contentsRect(), spacing |
| */ |
| |
| /*! |
| \obsolete |
| */ |
| int QLayout::margin() const |
| { |
| int left, top, right, bottom; |
| getContentsMargins(&left, &top, &right, &bottom); |
| if (left == top && top == right && right == bottom) { |
| return left; |
| } else { |
| return -1; |
| } |
| } |
| |
| /*! |
| \obsolete |
| */ |
| void QLayout::setMargin(int margin) |
| { |
| setContentsMargins(margin, margin, margin, margin); |
| } |
| |
| #endif |
| /*! |
| \property QLayout::spacing |
| \brief the spacing between widgets inside the layout |
| |
| If no value is explicitly set, the layout's spacing is inherited |
| from the parent layout, or from the style settings for the parent |
| widget. |
| |
| For QGridLayout and QFormLayout, it is possible to set different horizontal and |
| vertical spacings using \l{QGridLayout::}{setHorizontalSpacing()} |
| and \l{QGridLayout::}{setVerticalSpacing()}. In that case, |
| spacing() returns -1. |
| |
| \sa contentsRect(), getContentsMargins(), QStyle::layoutSpacing(), |
| QStyle::pixelMetric() |
| */ |
| |
| int QLayout::spacing() const |
| { |
| if (const QBoxLayout* boxlayout = qobject_cast<const QBoxLayout*>(this)) { |
| return boxlayout->spacing(); |
| } else if (const QGridLayout* gridlayout = qobject_cast<const QGridLayout*>(this)) { |
| return gridlayout->spacing(); |
| #if QT_CONFIG(formlayout) |
| } else if (const QFormLayout* formlayout = qobject_cast<const QFormLayout*>(this)) { |
| return formlayout->spacing(); |
| #endif |
| } else { |
| Q_D(const QLayout); |
| if (d->insideSpacing >=0) { |
| return d->insideSpacing; |
| } else { |
| // arbitrarily prefer horizontal spacing to vertical spacing |
| return qSmartSpacing(this, QStyle::PM_LayoutHorizontalSpacing); |
| } |
| } |
| } |
| |
| void QLayout::setSpacing(int spacing) |
| { |
| if (QBoxLayout* boxlayout = qobject_cast<QBoxLayout*>(this)) { |
| boxlayout->setSpacing(spacing); |
| } else if (QGridLayout* gridlayout = qobject_cast<QGridLayout*>(this)) { |
| gridlayout->setSpacing(spacing); |
| #if QT_CONFIG(formlayout) |
| } else if (QFormLayout* formlayout = qobject_cast<QFormLayout*>(this)) { |
| formlayout->setSpacing(spacing); |
| #endif |
| } else { |
| Q_D(QLayout); |
| d->insideSpacing = spacing; |
| invalidate(); |
| } |
| } |
| |
| /*! |
| \since 4.3 |
| |
| Sets the \a left, \a top, \a right, and \a bottom margins to use |
| around the layout. |
| |
| By default, QLayout uses the values provided by the style. On |
| most platforms, the margin is 11 pixels in all directions. |
| |
| \sa getContentsMargins(), QStyle::pixelMetric(), |
| {QStyle::}{PM_LayoutLeftMargin}, |
| {QStyle::}{PM_LayoutTopMargin}, |
| {QStyle::}{PM_LayoutRightMargin}, |
| {QStyle::}{PM_LayoutBottomMargin} |
| */ |
| void QLayout::setContentsMargins(int left, int top, int right, int bottom) |
| { |
| Q_D(QLayout); |
| |
| if (d->userLeftMargin == left && d->userTopMargin == top && |
| d->userRightMargin == right && d->userBottomMargin == bottom) |
| return; |
| |
| d->userLeftMargin = left; |
| d->userTopMargin = top; |
| d->userRightMargin = right; |
| d->userBottomMargin = bottom; |
| invalidate(); |
| } |
| |
| /*! |
| \since 4.6 |
| |
| Sets the \a margins to use around the layout. |
| |
| By default, QLayout uses the values provided by the style. On |
| most platforms, the margin is 11 pixels in all directions. |
| |
| \sa contentsMargins() |
| */ |
| void QLayout::setContentsMargins(const QMargins &margins) |
| { |
| setContentsMargins(margins.left(), margins.top(), margins.right(), margins.bottom()); |
| } |
| |
| /*! |
| \since 4.3 |
| |
| For each of \a left, \a top, \a right and \a bottom that is not |
| \nullptr, stores the size of the margin named in the location the |
| pointer refers to. |
| |
| By default, QLayout uses the values provided by the style. On |
| most platforms, the margin is 11 pixels in all directions. |
| |
| \sa setContentsMargins(), QStyle::pixelMetric(), |
| {QStyle::}{PM_LayoutLeftMargin}, |
| {QStyle::}{PM_LayoutTopMargin}, |
| {QStyle::}{PM_LayoutRightMargin}, |
| {QStyle::}{PM_LayoutBottomMargin} |
| */ |
| void QLayout::getContentsMargins(int *left, int *top, int *right, int *bottom) const |
| { |
| Q_D(const QLayout); |
| d->getMargin(left, d->userLeftMargin, QStyle::PM_LayoutLeftMargin); |
| d->getMargin(top, d->userTopMargin, QStyle::PM_LayoutTopMargin); |
| d->getMargin(right, d->userRightMargin, QStyle::PM_LayoutRightMargin); |
| d->getMargin(bottom, d->userBottomMargin, QStyle::PM_LayoutBottomMargin); |
| } |
| |
| /*! |
| \since 4.6 |
| |
| Returns the margins used around the layout. |
| |
| By default, QLayout uses the values provided by the style. On |
| most platforms, the margin is 11 pixels in all directions. |
| |
| \sa setContentsMargins() |
| */ |
| QMargins QLayout::contentsMargins() const |
| { |
| int left, top, right, bottom; |
| getContentsMargins(&left, &top, &right, &bottom); |
| return QMargins(left, top, right, bottom); |
| } |
| |
| /*! |
| \since 4.3 |
| |
| Returns the layout's geometry() rectangle, but taking into account the |
| contents margins. |
| |
| \sa setContentsMargins(), getContentsMargins() |
| */ |
| QRect QLayout::contentsRect() const |
| { |
| Q_D(const QLayout); |
| int left, top, right, bottom; |
| getContentsMargins(&left, &top, &right, &bottom); |
| return d->rect.adjusted(+left, +top, -right, -bottom); |
| } |
| |
| |
| /*! |
| Returns the parent widget of this layout, or \nullptr if this |
| layout is not installed on any widget. |
| |
| If the layout is a sub-layout, this function returns the parent |
| widget of the parent layout. |
| |
| \sa parent() |
| */ |
| QWidget *QLayout::parentWidget() const |
| { |
| Q_D(const QLayout); |
| if (!d->topLevel) { |
| if (parent()) { |
| QLayout *parentLayout = qobject_cast<QLayout*>(parent()); |
| if (Q_UNLIKELY(!parentLayout)) { |
| qWarning("QLayout::parentWidget: A layout can only have another layout as a parent."); |
| return nullptr; |
| } |
| return parentLayout->parentWidget(); |
| } else { |
| return nullptr; |
| } |
| } else { |
| Q_ASSERT(parent() && parent()->isWidgetType()); |
| return static_cast<QWidget *>(parent()); |
| } |
| } |
| |
| /*! |
| \reimp |
| */ |
| bool QLayout::isEmpty() const |
| { |
| int i = 0; |
| QLayoutItem *item = itemAt(i); |
| while (item) { |
| if (!item->isEmpty()) |
| return false; |
| ++i; |
| item = itemAt(i); |
| } |
| return true; |
| } |
| |
| /*! |
| \reimp |
| */ |
| QSizePolicy::ControlTypes QLayout::controlTypes() const |
| { |
| if (count() == 0) |
| return QSizePolicy::DefaultType; |
| QSizePolicy::ControlTypes types; |
| for (int i = count() - 1; i >= 0; --i) |
| types |= itemAt(i)->controlTypes(); |
| return types; |
| } |
| |
| /*! |
| \reimp |
| */ |
| void QLayout::setGeometry(const QRect &r) |
| { |
| Q_D(QLayout); |
| d->rect = r; |
| } |
| |
| /*! |
| \reimp |
| */ |
| QRect QLayout::geometry() const |
| { |
| Q_D(const QLayout); |
| return d->rect; |
| } |
| |
| /*! |
| \reimp |
| */ |
| void QLayout::invalidate() |
| { |
| Q_D(QLayout); |
| d->rect = QRect(); |
| update(); |
| } |
| |
| static bool removeWidgetRecursively(QLayoutItem *li, QObject *w) |
| { |
| QLayout *lay = li->layout(); |
| if (!lay) |
| return false; |
| int i = 0; |
| QLayoutItem *child; |
| while ((child = lay->itemAt(i))) { |
| if (child->widget() == w) { |
| delete lay->takeAt(i); |
| lay->invalidate(); |
| return true; |
| } else if (removeWidgetRecursively(child, w)) { |
| return true; |
| } else { |
| ++i; |
| } |
| } |
| return false; |
| } |
| |
| |
| void QLayoutPrivate::doResize(const QSize &r) |
| { |
| Q_Q(QLayout); |
| int mbh = menuBarHeightForWidth(menubar, r.width()); |
| QWidget *mw = q->parentWidget(); |
| QRect rect = mw->testAttribute(Qt::WA_LayoutOnEntireRect) ? mw->rect() : mw->contentsRect(); |
| const int mbTop = rect.top(); |
| rect.setTop(mbTop + mbh); |
| q->setGeometry(rect); |
| #if QT_CONFIG(menubar) |
| if (menubar) |
| menubar->setGeometry(rect.left(), mbTop, r.width(), mbh); |
| #endif |
| } |
| |
| |
| /*! |
| \internal |
| Performs child widget layout when the parent widget is |
| resized. Also handles removal of widgets. \a e is the |
| event |
| */ |
| void QLayout::widgetEvent(QEvent *e) |
| { |
| Q_D(QLayout); |
| if (!d->enabled) |
| return; |
| |
| switch (e->type()) { |
| case QEvent::Resize: |
| if (d->activated) { |
| QResizeEvent *r = (QResizeEvent *)e; |
| d->doResize(r->size()); |
| } else { |
| activate(); |
| } |
| break; |
| case QEvent::ChildRemoved: |
| { |
| QChildEvent *c = (QChildEvent *)e; |
| if (c->child()->isWidgetType()) { |
| #if QT_CONFIG(menubar) |
| if (c->child() == d->menubar) |
| d->menubar = 0; |
| #endif |
| removeWidgetRecursively(this, c->child()); |
| } |
| } |
| break; |
| case QEvent::LayoutRequest: |
| if (static_cast<QWidget *>(parent())->isVisible()) |
| activate(); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| /*! |
| \reimp |
| */ |
| void QLayout::childEvent(QChildEvent *e) |
| { |
| Q_D(QLayout); |
| if (!d->enabled) |
| return; |
| |
| if (e->type() != QEvent::ChildRemoved) |
| return; |
| |
| if (QLayout *childLayout = qobject_cast<QLayout *>(e->child())) |
| removeItem(childLayout); |
| } |
| |
| /*! |
| \internal |
| Also takes contentsMargins and menu bar into account. |
| */ |
| int QLayout::totalHeightForWidth(int w) const |
| { |
| Q_D(const QLayout); |
| int side=0, top=0; |
| if (d->topLevel) { |
| QWidget *parent = parentWidget(); |
| parent->ensurePolished(); |
| QWidgetPrivate *wd = parent->d_func(); |
| side += wd->leftmargin + wd->rightmargin; |
| top += wd->topmargin + wd->bottommargin; |
| } |
| int h = heightForWidth(w - side) + top; |
| #if QT_CONFIG(menubar) |
| h += menuBarHeightForWidth(d->menubar, w); |
| #endif |
| return h; |
| } |
| |
| /*! |
| \internal |
| Also takes contentsMargins and menu bar into account. |
| */ |
| QSize QLayout::totalMinimumSize() const |
| { |
| Q_D(const QLayout); |
| int side=0, top=0; |
| if (d->topLevel) { |
| QWidget *pw = parentWidget(); |
| pw->ensurePolished(); |
| QWidgetPrivate *wd = pw->d_func(); |
| side += wd->leftmargin + wd->rightmargin; |
| top += wd->topmargin + wd->bottommargin; |
| } |
| |
| QSize s = minimumSize(); |
| #if QT_CONFIG(menubar) |
| top += menuBarHeightForWidth(d->menubar, s.width() + side); |
| #endif |
| return s + QSize(side, top); |
| } |
| |
| /*! |
| \internal |
| Also takes contentsMargins and menu bar into account. |
| */ |
| QSize QLayout::totalSizeHint() const |
| { |
| Q_D(const QLayout); |
| int side=0, top=0; |
| if (d->topLevel) { |
| QWidget *pw = parentWidget(); |
| pw->ensurePolished(); |
| QWidgetPrivate *wd = pw->d_func(); |
| side += wd->leftmargin + wd->rightmargin; |
| top += wd->topmargin + wd->bottommargin; |
| } |
| |
| QSize s = sizeHint(); |
| if (hasHeightForWidth()) |
| s.setHeight(heightForWidth(s.width() + side)); |
| #if QT_CONFIG(menubar) |
| top += menuBarHeightForWidth(d->menubar, s.width()); |
| #endif |
| return s + QSize(side, top); |
| } |
| |
| /*! |
| \internal |
| Also takes contentsMargins and menu bar into account. |
| */ |
| QSize QLayout::totalMaximumSize() const |
| { |
| Q_D(const QLayout); |
| int side=0, top=0; |
| if (d->topLevel) { |
| QWidget *pw = parentWidget(); |
| pw->ensurePolished(); |
| QWidgetPrivate *wd = pw->d_func(); |
| side += wd->leftmargin + wd->rightmargin; |
| top += wd->topmargin + wd->bottommargin; |
| } |
| |
| QSize s = maximumSize(); |
| #if QT_CONFIG(menubar) |
| top += menuBarHeightForWidth(d->menubar, s.width()); |
| #endif |
| |
| if (d->topLevel) |
| s = QSize(qMin(s.width() + side, QLAYOUTSIZE_MAX), |
| qMin(s.height() + top, QLAYOUTSIZE_MAX)); |
| return s; |
| } |
| |
| /*! |
| \internal |
| Destroys the layout, deleting all child layouts. |
| Geometry management stops when a top-level layout is deleted. |
| |
| The layout classes will probably be fatally confused if you delete |
| a sublayout. |
| */ |
| QLayout::~QLayout() |
| { |
| Q_D(QLayout); |
| if (d->topLevel && parent() && parent()->isWidgetType() && parentWidget()->layout() == this) |
| parentWidget()->d_func()->layout = 0; |
| else if (QLayout *parentLayout = qobject_cast<QLayout *>(parent())) |
| parentLayout->removeItem(this); |
| } |
| |
| |
| /*! |
| This function is called from \c addLayout() or \c insertLayout() functions in |
| subclasses to add layout \a l as a sub-layout. |
| |
| The only scenario in which you need to call it directly is if you |
| implement a custom layout that supports nested layouts. |
| |
| \sa QBoxLayout::addLayout(), QBoxLayout::insertLayout(), QGridLayout::addLayout() |
| */ |
| void QLayout::addChildLayout(QLayout *l) |
| { |
| if (Q_UNLIKELY(l->parent())) { |
| qWarning("QLayout::addChildLayout: layout \"%ls\" already has a parent", |
| qUtf16Printable(l->objectName())); |
| return; |
| } |
| l->setParent(this); |
| |
| if (QWidget *mw = parentWidget()) { |
| l->d_func()->reparentChildWidgets(mw); |
| } |
| |
| } |
| |
| /*! |
| \internal |
| */ |
| bool QLayout::adoptLayout(QLayout *layout) |
| { |
| const bool ok = !layout->parent(); |
| addChildLayout(layout); |
| return ok; |
| } |
| |
| #ifdef QT_DEBUG |
| static bool layoutDebug() |
| { |
| static int checked_env = -1; |
| if(checked_env == -1) |
| checked_env = !!qEnvironmentVariableIntValue("QT_LAYOUT_DEBUG"); |
| |
| return checked_env; |
| } |
| #endif |
| |
| void QLayoutPrivate::reparentChildWidgets(QWidget *mw) |
| { |
| Q_Q(QLayout); |
| int n = q->count(); |
| |
| #if QT_CONFIG(menubar) |
| if (menubar && menubar->parentWidget() != mw) { |
| menubar->setParent(mw); |
| } |
| #endif |
| bool mwVisible = mw && mw->isVisible(); |
| for (int i = 0; i < n; ++i) { |
| QLayoutItem *item = q->itemAt(i); |
| if (QWidget *w = item->widget()) { |
| QWidget *pw = w->parentWidget(); |
| #ifdef QT_DEBUG |
| if (Q_UNLIKELY(pw && pw != mw && layoutDebug())) { |
| qWarning("QLayout::addChildLayout: widget %s \"%ls\" in wrong parent; moved to correct parent", |
| w->metaObject()->className(), qUtf16Printable(w->objectName())); |
| } |
| #endif |
| bool needShow = mwVisible && !(w->isHidden() && w->testAttribute(Qt::WA_WState_ExplicitShowHide)); |
| if (pw != mw) |
| w->setParent(mw); |
| if (needShow) |
| QMetaObject::invokeMethod(w, "_q_showIfNotHidden", Qt::QueuedConnection); //show later |
| } else if (QLayout *l = item->layout()) { |
| l->d_func()->reparentChildWidgets(mw); |
| } |
| } |
| } |
| |
| /*! |
| Returns \c true if the \a widget can be added to the \a layout; |
| otherwise returns \c false. |
| */ |
| bool QLayoutPrivate::checkWidget(QWidget *widget) const |
| { |
| Q_Q(const QLayout); |
| if (Q_UNLIKELY(!widget)) { |
| qWarning("QLayout: Cannot add a null widget to %s/%ls", q->metaObject()->className(), |
| qUtf16Printable(q->objectName())); |
| return false; |
| } |
| if (Q_UNLIKELY(widget == q->parentWidget())) { |
| qWarning("QLayout: Cannot add parent widget %s/%ls to its child layout %s/%ls", |
| widget->metaObject()->className(), qUtf16Printable(widget->objectName()), |
| q->metaObject()->className(), qUtf16Printable(q->objectName())); |
| return false; |
| } |
| return true; |
| } |
| |
| /*! |
| Returns \c true if the \a otherLayout can be added to the \a layout; |
| otherwise returns \c false. |
| */ |
| bool QLayoutPrivate::checkLayout(QLayout *otherLayout) const |
| { |
| Q_Q(const QLayout); |
| if (Q_UNLIKELY(!otherLayout)) { |
| qWarning("QLayout: Cannot add a null layout to %s/%ls", |
| q->metaObject()->className(), qUtf16Printable(q->objectName())); |
| return false; |
| } |
| if (Q_UNLIKELY(otherLayout == q)) { |
| qWarning("QLayout: Cannot add layout %s/%ls to itself", |
| q->metaObject()->className(), qUtf16Printable(q->objectName())); |
| return false; |
| } |
| return true; |
| } |
| |
| /*! |
| This function is called from \c addWidget() functions in |
| subclasses to add \a w as a managed widget of a layout. |
| |
| If \a w is already managed by a layout, this function will give a warning |
| and remove \a w from that layout. This function must therefore be |
| called before adding \a w to the layout's data structure. |
| */ |
| void QLayout::addChildWidget(QWidget *w) |
| { |
| QWidget *mw = parentWidget(); |
| QWidget *pw = w->parentWidget(); |
| |
| //Qt::WA_LaidOut is never reset. It only means that the widget at some point has |
| //been in a layout. |
| if (pw && w->testAttribute(Qt::WA_LaidOut)) { |
| QLayout *l = pw->layout(); |
| if (l && removeWidgetRecursively(l, w)) { |
| #ifdef QT_DEBUG |
| if (Q_UNLIKELY(layoutDebug())) |
| qWarning("QLayout::addChildWidget: %s \"%ls\" is already in a layout; moved to new layout", |
| w->metaObject()->className(), qUtf16Printable(w->objectName())); |
| #endif |
| } |
| } |
| if (pw && mw && pw != mw) { |
| #ifdef QT_DEBUG |
| if (Q_UNLIKELY(layoutDebug())) |
| qWarning("QLayout::addChildWidget: %s \"%ls\" in wrong parent; moved to correct parent", |
| w->metaObject()->className(), qUtf16Printable(w->objectName())); |
| #endif |
| pw = 0; |
| } |
| bool needShow = mw && mw->isVisible() && !(w->isHidden() && w->testAttribute(Qt::WA_WState_ExplicitShowHide)); |
| if (!pw && mw) |
| w->setParent(mw); |
| w->setAttribute(Qt::WA_LaidOut); |
| if (needShow) |
| QMetaObject::invokeMethod(w, "_q_showIfNotHidden", Qt::QueuedConnection); //show later |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| /*! |
| Tells the geometry manager to place the menu bar \a widget at the |
| top of parentWidget(), outside QWidget::contentsMargins(). All |
| child widgets are placed below the bottom edge of the menu bar. |
| */ |
| void QLayout::setMenuBar(QWidget *widget) |
| { |
| Q_D(QLayout); |
| if (widget) |
| addChildWidget(widget); |
| d->menubar = widget; |
| } |
| |
| /*! |
| Returns the menu bar set for this layout, or \nullptr if no |
| menu bar is set. |
| */ |
| |
| QWidget *QLayout::menuBar() const |
| { |
| Q_D(const QLayout); |
| return d->menubar; |
| } |
| |
| |
| /*! |
| Returns the minimum size of this layout. This is the smallest |
| size that the layout can have while still respecting the |
| specifications. |
| |
| The returned value doesn't include the space required by |
| QWidget::setContentsMargins() or menuBar(). |
| |
| The default implementation allows unlimited resizing. |
| */ |
| QSize QLayout::minimumSize() const |
| { |
| return QSize(0, 0); |
| } |
| |
| /*! |
| Returns the maximum size of this layout. This is the largest size |
| that the layout can have while still respecting the |
| specifications. |
| |
| The returned value doesn't include the space required by |
| QWidget::setContentsMargins() or menuBar(). |
| |
| The default implementation allows unlimited resizing. |
| */ |
| QSize QLayout::maximumSize() const |
| { |
| return QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX); |
| } |
| |
| /*! |
| Returns whether this layout can make use of more space than |
| sizeHint(). A value of Qt::Vertical or Qt::Horizontal means that |
| it wants to grow in only one dimension, whereas Qt::Vertical | |
| Qt::Horizontal means that it wants to grow in both dimensions. |
| |
| The default implementation returns Qt::Horizontal | Qt::Vertical. |
| Subclasses reimplement it to return a meaningful value based on |
| their child widgets's \l{QSizePolicy}{size policies}. |
| |
| \sa sizeHint() |
| */ |
| Qt::Orientations QLayout::expandingDirections() const |
| { |
| return Qt::Horizontal | Qt::Vertical; |
| } |
| |
| void QLayout::activateRecursiveHelper(QLayoutItem *item) |
| { |
| item->invalidate(); |
| QLayout *layout = item->layout(); |
| if (layout) { |
| QLayoutItem *child; |
| int i=0; |
| while ((child = layout->itemAt(i++))) |
| activateRecursiveHelper(child); |
| layout->d_func()->activated = true; |
| } |
| } |
| |
| /*! |
| Updates the layout for parentWidget(). |
| |
| You should generally not need to call this because it is |
| automatically called at the most appropriate times. |
| |
| \sa activate(), invalidate() |
| */ |
| |
| void QLayout::update() |
| { |
| QLayout *layout = this; |
| while (layout && layout->d_func()->activated) { |
| layout->d_func()->activated = false; |
| if (layout->d_func()->topLevel) { |
| Q_ASSERT(layout->parent()->isWidgetType()); |
| QWidget *mw = static_cast<QWidget*>(layout->parent()); |
| QCoreApplication::postEvent(mw, new QEvent(QEvent::LayoutRequest)); |
| break; |
| } |
| layout = static_cast<QLayout*>(layout->parent()); |
| } |
| } |
| |
| /*! |
| Redoes the layout for parentWidget() if necessary. |
| |
| You should generally not need to call this because it is |
| automatically called at the most appropriate times. It returns |
| true if the layout was redone. |
| |
| \sa update(), QWidget::updateGeometry() |
| */ |
| bool QLayout::activate() |
| { |
| Q_D(QLayout); |
| if (!d->enabled || !parent()) |
| return false; |
| if (!d->topLevel) |
| return static_cast<QLayout*>(parent())->activate(); |
| if (d->activated) |
| return false; |
| QWidget *mw = static_cast<QWidget*>(parent()); |
| if (Q_UNLIKELY(!mw)) { |
| qWarning("QLayout::activate: %s \"%ls\" does not have a main widget", |
| metaObject()->className(), qUtf16Printable(objectName())); |
| return false; |
| } |
| activateRecursiveHelper(this); |
| |
| QWidgetPrivate *md = mw->d_func(); |
| uint explMin = md->extra ? md->extra->explicitMinSize : 0; |
| uint explMax = md->extra ? md->extra->explicitMaxSize : 0; |
| |
| switch (d->constraint) { |
| case SetFixedSize: |
| // will trigger resize |
| mw->setFixedSize(totalSizeHint()); |
| break; |
| case SetMinimumSize: |
| mw->setMinimumSize(totalMinimumSize()); |
| break; |
| case SetMaximumSize: |
| mw->setMaximumSize(totalMaximumSize()); |
| break; |
| case SetMinAndMaxSize: |
| mw->setMinimumSize(totalMinimumSize()); |
| mw->setMaximumSize(totalMaximumSize()); |
| break; |
| case SetDefaultConstraint: { |
| bool widthSet = explMin & Qt::Horizontal; |
| bool heightSet = explMin & Qt::Vertical; |
| if (mw->isWindow()) { |
| QSize ms = totalMinimumSize(); |
| if (widthSet) |
| ms.setWidth(mw->minimumSize().width()); |
| if (heightSet) |
| ms.setHeight(mw->minimumSize().height()); |
| mw->setMinimumSize(ms); |
| } else if (!widthSet || !heightSet) { |
| QSize ms = mw->minimumSize(); |
| if (!widthSet) |
| ms.setWidth(0); |
| if (!heightSet) |
| ms.setHeight(0); |
| mw->setMinimumSize(ms); |
| } |
| break; |
| } |
| case SetNoConstraint: |
| break; |
| } |
| |
| d->doResize(mw->size()); |
| |
| if (md->extra) { |
| md->extra->explicitMinSize = explMin; |
| md->extra->explicitMaxSize = explMax; |
| } |
| // ideally only if sizeHint() or sizePolicy() has changed |
| mw->updateGeometry(); |
| return true; |
| } |
| |
| /*! |
| \since 5.2 |
| |
| Searches for widget \a from and replaces it with widget \a to if found. |
| Returns the layout item that contains the widget \a from on success. |
| Otherwise \nullptr is returned. |
| If \a options contains \c Qt::FindChildrenRecursively (the default), |
| sub-layouts are searched for doing the replacement. |
| Any other flag in \a options is ignored. |
| |
| Notice that the returned item therefore might not belong to this layout, |
| but to a sub-layout. |
| |
| The returned layout item is no longer owned by the layout and should be |
| either deleted or inserted to another layout. The widget \a from is no |
| longer managed by the layout and may need to be deleted or hidden. The |
| parent of widget \a from is left unchanged. |
| |
| This function works for the built-in Qt layouts, but might not work for |
| custom layouts. |
| |
| \sa indexOf() |
| */ |
| |
| QLayoutItem *QLayout::replaceWidget(QWidget *from, QWidget *to, Qt::FindChildOptions options) |
| { |
| Q_D(QLayout); |
| if (!from || !to) |
| return 0; |
| if (from == to) // Do not return a QLayoutItem for \a from, since ownership still |
| return nullptr; // belongs to the layout (since nothing was changed) |
| |
| int index = -1; |
| QLayoutItem *item = 0; |
| for (int u = 0; u < count(); ++u) { |
| item = itemAt(u); |
| if (!item) |
| continue; |
| |
| if (item->widget() == from) { |
| index = u; |
| break; |
| } |
| |
| if (item->layout() && (options & Qt::FindChildrenRecursively)) { |
| QLayoutItem *r = item->layout()->replaceWidget(from, to, options); |
| if (r) |
| return r; |
| } |
| } |
| if (index == -1) |
| return 0; |
| |
| addChildWidget(to); |
| QLayoutItem *newitem = new QWidgetItem(to); |
| newitem->setAlignment(item->alignment()); |
| QLayoutItem *r = d->replaceAt(index, newitem); |
| if (!r) |
| delete newitem; |
| return r; |
| } |
| |
| /*! |
| \fn QLayoutItem *QLayout::itemAt(int index) const |
| |
| Must be implemented in subclasses to return the layout item at \a |
| index. If there is no such item, the function must return 0. |
| Items are numbered consecutively from 0. If an item is deleted, other items will be renumbered. |
| |
| This function can be used to iterate over a layout. The following |
| code will draw a rectangle for each layout item in the layout structure of the widget. |
| |
| \snippet code/src_gui_kernel_qlayout.cpp 0 |
| |
| \sa count(), takeAt() |
| */ |
| |
| /*! |
| \fn QLayoutItem *QLayout::takeAt(int index) |
| |
| Must be implemented in subclasses to remove the layout item at \a |
| index from the layout, and return the item. If there is no such |
| item, the function must do nothing and return 0. Items are numbered |
| consecutively from 0. If an item is removed, other items will be |
| renumbered. |
| |
| The following code fragment shows a safe way to remove all items |
| from a layout: |
| |
| \snippet code/src_gui_kernel_qlayout.cpp 1 |
| |
| \sa itemAt(), count() |
| */ |
| |
| /*! |
| \fn int *QLayout::count() const |
| |
| Must be implemented in subclasses to return the number of items |
| in the layout. |
| |
| \sa itemAt() |
| */ |
| |
| /*! |
| Searches for widget \a widget in this layout (not including child |
| layouts). |
| |
| Returns the index of \a widget, or -1 if \a widget is not found. |
| |
| The default implementation iterates over all items using itemAt() |
| */ |
| int QLayout::indexOf(QWidget *widget) const |
| { |
| int i = 0; |
| QLayoutItem *item = itemAt(i); |
| while (item) { |
| if (item->widget() == widget) |
| return i; |
| ++i; |
| item = itemAt(i); |
| } |
| return -1; |
| } |
| |
| /*! |
| \since 5.12 |
| Searches for layout item \a layoutItem in this layout (not including child |
| layouts). |
| |
| Returns the index of \a layoutItem, or -1 if \a layoutItem is not found. |
| */ |
| int QLayout::indexOf(QLayoutItem *layoutItem) const |
| { |
| int i = 0; |
| QLayoutItem *item = itemAt(i); |
| while (item) { |
| if (item == layoutItem) |
| return i; |
| ++i; |
| item = itemAt(i); |
| } |
| return -1; |
| } |
| |
| /*! |
| \enum QLayout::SizeConstraint |
| |
| The possible values are: |
| |
| \value SetDefaultConstraint The main widget's minimum size is set |
| to minimumSize(), unless the widget already has |
| a minimum size. |
| |
| \value SetFixedSize The main widget's size is set to sizeHint(); it |
| cannot be resized at all. |
| \value SetMinimumSize The main widget's minimum size is set to |
| minimumSize(); it cannot be smaller. |
| |
| \value SetMaximumSize The main widget's maximum size is set to |
| maximumSize(); it cannot be larger. |
| |
| \value SetMinAndMaxSize The main widget's minimum size is set to |
| minimumSize() and its maximum size is set to |
| maximumSize(). |
| |
| \value SetNoConstraint The widget is not constrained. |
| |
| \sa setSizeConstraint() |
| */ |
| |
| /*! |
| \property QLayout::sizeConstraint |
| \brief the resize mode of the layout |
| |
| The default mode is \l {QLayout::SetDefaultConstraint} |
| {SetDefaultConstraint}. |
| */ |
| void QLayout::setSizeConstraint(SizeConstraint constraint) |
| { |
| Q_D(QLayout); |
| if (constraint == d->constraint) |
| return; |
| |
| d->constraint = constraint; |
| invalidate(); |
| } |
| |
| QLayout::SizeConstraint QLayout::sizeConstraint() const |
| { |
| Q_D(const QLayout); |
| return d->constraint; |
| } |
| |
| /*! |
| Returns the rectangle that should be covered when the geometry of |
| this layout is set to \a r, provided that this layout supports |
| setAlignment(). |
| |
| The result is derived from sizeHint() and expanding(). It is never |
| larger than \a r. |
| */ |
| QRect QLayout::alignmentRect(const QRect &r) const |
| { |
| QSize s = sizeHint(); |
| Qt::Alignment a = alignment(); |
| |
| /* |
| This is a hack to obtain the real maximum size, not |
| QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX), the value consistently |
| returned by QLayoutItems that have an alignment. |
| */ |
| QLayout *that = const_cast<QLayout *>(this); |
| that->setAlignment(0); |
| QSize ms = that->maximumSize(); |
| that->setAlignment(a); |
| |
| if ((expandingDirections() & Qt::Horizontal) || |
| !(a & Qt::AlignHorizontal_Mask)) { |
| s.setWidth(qMin(r.width(), ms.width())); |
| } |
| if ((expandingDirections() & Qt::Vertical) || |
| !(a & Qt::AlignVertical_Mask)) { |
| s.setHeight(qMin(r.height(), ms.height())); |
| } else if (hasHeightForWidth()) { |
| int hfw = heightForWidth(s.width()); |
| if (hfw < s.height()) |
| s.setHeight(qMin(hfw, ms.height())); |
| } |
| |
| s = s.boundedTo(r.size()); |
| int x = r.x(); |
| int y = r.y(); |
| |
| if (a & Qt::AlignBottom) |
| y += (r.height() - s.height()); |
| else if (!(a & Qt::AlignTop)) |
| y += (r.height() - s.height()) / 2; |
| |
| QWidget *parent = parentWidget(); |
| a = QStyle::visualAlignment(parent ? parent->layoutDirection() : QGuiApplication::layoutDirection(), a); |
| if (a & Qt::AlignRight) |
| x += (r.width() - s.width()); |
| else if (!(a & Qt::AlignLeft)) |
| x += (r.width() - s.width()) / 2; |
| |
| return QRect(x, y, s.width(), s.height()); |
| } |
| |
| /*! |
| Removes the widget \a widget from the layout. After this call, it |
| is the caller's responsibility to give the widget a reasonable |
| geometry or to put the widget back into a layout or to explicitly |
| hide it if necessary. |
| |
| \b{Note:} The ownership of \a widget remains the same as |
| when it was added. |
| |
| \sa removeItem(), QWidget::setGeometry(), addWidget() |
| */ |
| void QLayout::removeWidget(QWidget *widget) |
| { |
| int i = 0; |
| QLayoutItem *child; |
| while ((child = itemAt(i))) { |
| if (child->widget() == widget) { |
| delete takeAt(i); |
| invalidate(); |
| } else { |
| ++i; |
| } |
| } |
| } |
| |
| /*! |
| Removes the layout item \a item from the layout. It is the |
| caller's responsibility to delete the item. |
| |
| Notice that \a item can be a layout (since QLayout inherits |
| QLayoutItem). |
| |
| \sa removeWidget(), addItem() |
| */ |
| void QLayout::removeItem(QLayoutItem *item) |
| { |
| int i = 0; |
| QLayoutItem *child; |
| while ((child = itemAt(i))) { |
| if (child == item) { |
| takeAt(i); |
| invalidate(); |
| } else { |
| ++i; |
| } |
| } |
| } |
| |
| /*! |
| Enables this layout if \a enable is true, otherwise disables it. |
| |
| An enabled layout adjusts dynamically to changes; a disabled |
| layout acts as if it did not exist. |
| |
| By default all layouts are enabled. |
| |
| \sa isEnabled() |
| */ |
| void QLayout::setEnabled(bool enable) |
| { |
| Q_D(QLayout); |
| d->enabled = enable; |
| } |
| |
| /*! |
| Returns \c true if the layout is enabled; otherwise returns \c false. |
| |
| \sa setEnabled() |
| */ |
| bool QLayout::isEnabled() const |
| { |
| Q_D(const QLayout); |
| return d->enabled; |
| } |
| |
| /*! |
| Returns a size that satisfies all size constraints on \a widget, |
| including heightForWidth() and that is as close as possible to \a |
| size. |
| */ |
| |
| QSize QLayout::closestAcceptableSize(const QWidget *widget, const QSize &size) |
| { |
| QSize result = size.boundedTo(qSmartMaxSize(widget)); |
| result = result.expandedTo(qSmartMinSize(widget)); |
| QLayout *l = widget->layout(); |
| if (l && l->hasHeightForWidth() && result.height() < l->minimumHeightForWidth(result.width()) ) { |
| QSize current = widget->size(); |
| int currentHfw = l->minimumHeightForWidth(current.width()); |
| int newHfw = l->minimumHeightForWidth(result.width()); |
| if (current.height() < currentHfw || currentHfw == newHfw) { |
| //handle the constant hfw case and the vertical-only case, as well as the |
| // current-size-is-not-correct case |
| result.setHeight(newHfw); |
| } else { |
| // binary search; assume hfw is decreasing ### |
| |
| int maxw = qMax(widget->width(),result.width()); |
| int maxh = qMax(widget->height(), result.height()); |
| int minw = qMin(widget->width(),result.width()); |
| int minh = qMin(widget->height(), result.height()); |
| |
| int minhfw = l->minimumHeightForWidth(minw); |
| int maxhfw = l->minimumHeightForWidth(maxw); |
| while (minw < maxw) { |
| if (minhfw > maxh) { //assume decreasing |
| minw = maxw - (maxw-minw)/2; |
| minhfw = l->minimumHeightForWidth(minw); |
| } else if (maxhfw < minh ) { //assume decreasing |
| maxw = minw + (maxw-minw)/2; |
| maxhfw = l->minimumHeightForWidth(maxw); |
| } else { |
| break; |
| } |
| } |
| result = result.expandedTo(QSize(minw, minhfw)); |
| } |
| } |
| return result; |
| } |
| |
| QT_END_NAMESPACE |
| |
| #include "moc_qlayout.cpp" |