1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtWidgets module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qlayout.h"
41
42#include "qapplication.h"
43#include "qlayoutengine_p.h"
44#if QT_CONFIG(menubar)
45#include "qmenubar.h"
46#endif
47#if QT_CONFIG(toolbar)
48#include "qtoolbar.h"
49#endif
50#if QT_CONFIG(sizegrip)
51#include "qsizegrip.h"
52#endif
53#include "qevent.h"
54#include "qstyle.h"
55#include "qvariant.h"
56#include "qwidget_p.h"
57#include "qlayout_p.h"
58#if QT_CONFIG(formlayout)
59#include "qformlayout.h"
60#endif
61
62QT_BEGIN_NAMESPACE
63
64static int menuBarHeightForWidth(QWidget *menubar, int w)
65{
66 if (menubar && !menubar->isHidden() && !menubar->isWindow()) {
67 int result = menubar->heightForWidth(qMax(a: w, b: menubar->minimumWidth()));
68 if (result == -1)
69 result = menubar->sizeHint().height();
70 const int min = qSmartMinSize(w: menubar).height();
71 result = qBound(min, val: result, max: menubar->maximumSize().height());
72 if (result != -1)
73 return result;
74 }
75 return 0;
76}
77
78/*!
79 \class QLayout
80 \brief The QLayout class is the base class of geometry managers.
81
82 \ingroup geomanagement
83 \inmodule QtWidgets
84
85 This is an abstract base class inherited by the concrete classes
86 QBoxLayout, QGridLayout, QFormLayout, and QStackedLayout.
87
88 For users of QLayout subclasses or of QMainWindow there is seldom
89 any need to use the basic functions provided by QLayout, such as
90 setSizeConstraint() or setMenuBar(). See \l{Layout Management}
91 for more information.
92
93 To make your own layout manager, implement the functions
94 addItem(), sizeHint(), setGeometry(), itemAt() and takeAt(). You
95 should also implement minimumSize() to ensure your layout isn't
96 resized to zero size if there is too little space. To support
97 children whose heights depend on their widths, implement
98 hasHeightForWidth() and heightForWidth(). See the
99 \l{layouts/borderlayout}{Border Layout} and
100 \l{layouts/flowlayout}{Flow Layout} examples for
101 more information about implementing custom layout managers.
102
103 Geometry management stops when the layout manager is deleted.
104
105 \sa QLayoutItem, {Layout Management}, {Basic Layouts Example},
106 {Border Layout Example}, {Flow Layout Example}
107*/
108
109
110/*!
111 Constructs a new top-level QLayout, with parent \a parent.
112 \a parent may not be \nullptr.
113
114 The layout is set directly as the top-level layout for
115 \a parent. There can be only one top-level layout for a
116 widget. It is returned by QWidget::layout().
117*/
118QLayout::QLayout(QWidget *parent)
119 : QObject(*new QLayoutPrivate, parent)
120{
121 if (!parent)
122 return;
123 parent->setLayout(this);
124}
125
126/*!
127 Constructs a new child QLayout.
128
129 This layout has to be inserted into another layout before geometry
130 management will work.
131*/
132QLayout::QLayout()
133 : QObject(*new QLayoutPrivate, nullptr)
134{
135}
136
137
138/*! \internal
139 */
140QLayout::QLayout(QLayoutPrivate &dd, QLayout *lay, QWidget *w)
141 : QObject(dd, lay ? static_cast<QObject*>(lay) : static_cast<QObject*>(w))
142{
143 Q_D(QLayout);
144 if (lay) {
145 lay->addItem(this);
146 } else if (w) {
147 if (Q_UNLIKELY(w->layout())) {
148 qWarning(msg: "QLayout: Attempting to add QLayout \"%ls\" to %s \"%ls\", which"
149 " already has a layout",
150 qUtf16Printable(QObject::objectName()), w->metaObject()->className(),
151 qUtf16Printable(w->objectName()));
152 setParent(nullptr);
153 } else {
154 d->topLevel = true;
155 w->d_func()->layout = this;
156 QT_TRY {
157 invalidate();
158 } QT_CATCH(...) {
159 w->d_func()->layout = nullptr;
160 QT_RETHROW;
161 }
162 }
163 }
164}
165
166QLayoutPrivate::QLayoutPrivate()
167 : QObjectPrivate(), insideSpacing(-1), userLeftMargin(-1), userTopMargin(-1), userRightMargin(-1),
168 userBottomMargin(-1), topLevel(false), enabled(true), activated(true), autoNewChild(false),
169 constraint(QLayout::SetDefaultConstraint), menubar(nullptr)
170{
171}
172
173void QLayoutPrivate::getMargin(int *result, int userMargin, QStyle::PixelMetric pm) const
174{
175 if (!result)
176 return;
177
178 Q_Q(const QLayout);
179 if (userMargin >= 0) {
180 *result = userMargin;
181 } else if (!topLevel) {
182 *result = 0;
183 } else if (QWidget *pw = q->parentWidget()) {
184 *result = pw->style()->pixelMetric(metric: pm, option: nullptr, widget: pw);
185 } else {
186 *result = 0;
187 }
188}
189
190// Static item factory functions that allow for hooking things in Designer
191
192QLayoutPrivate::QWidgetItemFactoryMethod QLayoutPrivate::widgetItemFactoryMethod = nullptr;
193QLayoutPrivate::QSpacerItemFactoryMethod QLayoutPrivate::spacerItemFactoryMethod = nullptr;
194
195QWidgetItem *QLayoutPrivate::createWidgetItem(const QLayout *layout, QWidget *widget)
196{
197 if (widgetItemFactoryMethod)
198 if (QWidgetItem *wi = (*widgetItemFactoryMethod)(layout, widget))
199 return wi;
200 return new QWidgetItemV2(widget);
201}
202
203QSpacerItem *QLayoutPrivate::createSpacerItem(const QLayout *layout, int w, int h, QSizePolicy::Policy hPolicy, QSizePolicy::Policy vPolicy)
204{
205 if (spacerItemFactoryMethod)
206 if (QSpacerItem *si = (*spacerItemFactoryMethod)(layout, w, h, hPolicy, vPolicy))
207 return si;
208 return new QSpacerItem(w, h, hPolicy, vPolicy);
209}
210
211
212
213/*!
214 \fn void QLayout::addItem(QLayoutItem *item)
215
216 Implemented in subclasses to add an \a item. How it is added is
217 specific to each subclass.
218
219 This function is not usually called in application code. To add a widget
220 to a layout, use the addWidget() function; to add a child layout, use the
221 addLayout() function provided by the relevant QLayout subclass.
222
223 \b{Note:} The ownership of \a item is transferred to the layout, and it's
224 the layout's responsibility to delete it.
225
226 \sa addWidget(), QBoxLayout::addLayout(), QGridLayout::addLayout()
227*/
228
229/*!
230 Adds widget \a w to this layout in a manner specific to the
231 layout. This function uses addItem().
232*/
233void QLayout::addWidget(QWidget *w)
234{
235 addChildWidget(w);
236 addItem(QLayoutPrivate::createWidgetItem(layout: this, widget: w));
237}
238
239
240
241/*!
242 Sets the alignment for widget \a w to \a alignment and returns
243 true if \a w is found in this layout (not including child
244 layouts); otherwise returns \c false.
245*/
246bool QLayout::setAlignment(QWidget *w, Qt::Alignment alignment)
247{
248 int i = 0;
249 QLayoutItem *item = itemAt(index: i);
250 while (item) {
251 if (item->widget() == w) {
252 item->setAlignment(alignment);
253 invalidate();
254 return true;
255 }
256 ++i;
257 item = itemAt(index: i);
258 }
259 return false;
260}
261
262/*!
263 \overload
264
265 Sets the alignment for the layout \a l to \a alignment and
266 returns \c true if \a l is found in this layout (not including child
267 layouts); otherwise returns \c false.
268*/
269bool QLayout::setAlignment(QLayout *l, Qt::Alignment alignment)
270{
271 int i = 0;
272 QLayoutItem *item = itemAt(index: i);
273 while (item) {
274 if (item->layout() == l) {
275 item->setAlignment(alignment);
276 invalidate();
277 return true;
278 }
279 ++i;
280 item = itemAt(index: i);
281 }
282 return false;
283}
284
285#if QT_DEPRECATED_SINCE(5, 13)
286/*!
287 \property QLayout::margin
288 \brief the width of the outside border of the layout
289 \obsolete
290
291 Use setContentsMargins() and getContentsMargins() instead.
292
293 \sa contentsRect(), spacing
294*/
295
296/*!
297 \obsolete
298*/
299int QLayout::margin() const
300{
301 int left, top, right, bottom;
302 getContentsMargins(left: &left, top: &top, right: &right, bottom: &bottom);
303 if (left == top && top == right && right == bottom) {
304 return left;
305 } else {
306 return -1;
307 }
308}
309
310/*!
311 \obsolete
312*/
313void QLayout::setMargin(int margin)
314{
315 setContentsMargins(left: margin, top: margin, right: margin, bottom: margin);
316}
317
318#endif
319/*!
320 \property QLayout::spacing
321 \brief the spacing between widgets inside the layout
322
323 If no value is explicitly set, the layout's spacing is inherited
324 from the parent layout, or from the style settings for the parent
325 widget.
326
327 For QGridLayout and QFormLayout, it is possible to set different horizontal and
328 vertical spacings using \l{QGridLayout::}{setHorizontalSpacing()}
329 and \l{QGridLayout::}{setVerticalSpacing()}. In that case,
330 spacing() returns -1.
331
332 \sa contentsRect(), getContentsMargins(), QStyle::layoutSpacing(),
333 QStyle::pixelMetric()
334*/
335
336int QLayout::spacing() const
337{
338 if (const QBoxLayout* boxlayout = qobject_cast<const QBoxLayout*>(object: this)) {
339 return boxlayout->spacing();
340 } else if (const QGridLayout* gridlayout = qobject_cast<const QGridLayout*>(object: this)) {
341 return gridlayout->spacing();
342#if QT_CONFIG(formlayout)
343 } else if (const QFormLayout* formlayout = qobject_cast<const QFormLayout*>(object: this)) {
344 return formlayout->spacing();
345#endif
346 } else {
347 Q_D(const QLayout);
348 if (d->insideSpacing >=0) {
349 return d->insideSpacing;
350 } else {
351 // arbitrarily prefer horizontal spacing to vertical spacing
352 return qSmartSpacing(layout: this, pm: QStyle::PM_LayoutHorizontalSpacing);
353 }
354 }
355}
356
357void QLayout::setSpacing(int spacing)
358{
359 if (QBoxLayout* boxlayout = qobject_cast<QBoxLayout*>(object: this)) {
360 boxlayout->setSpacing(spacing);
361 } else if (QGridLayout* gridlayout = qobject_cast<QGridLayout*>(object: this)) {
362 gridlayout->setSpacing(spacing);
363#if QT_CONFIG(formlayout)
364 } else if (QFormLayout* formlayout = qobject_cast<QFormLayout*>(object: this)) {
365 formlayout->setSpacing(spacing);
366#endif
367 } else {
368 Q_D(QLayout);
369 d->insideSpacing = spacing;
370 invalidate();
371 }
372}
373
374/*!
375 \since 4.3
376
377 Sets the \a left, \a top, \a right, and \a bottom margins to use
378 around the layout.
379
380 By default, QLayout uses the values provided by the style. On
381 most platforms, the margin is 11 pixels in all directions.
382
383 \sa getContentsMargins(), QStyle::pixelMetric(),
384 {QStyle::}{PM_LayoutLeftMargin},
385 {QStyle::}{PM_LayoutTopMargin},
386 {QStyle::}{PM_LayoutRightMargin},
387 {QStyle::}{PM_LayoutBottomMargin}
388*/
389void QLayout::setContentsMargins(int left, int top, int right, int bottom)
390{
391 Q_D(QLayout);
392
393 if (d->userLeftMargin == left && d->userTopMargin == top &&
394 d->userRightMargin == right && d->userBottomMargin == bottom)
395 return;
396
397 d->userLeftMargin = left;
398 d->userTopMargin = top;
399 d->userRightMargin = right;
400 d->userBottomMargin = bottom;
401 invalidate();
402}
403
404/*!
405 \since 4.6
406
407 Sets the \a margins to use around the layout.
408
409 By default, QLayout uses the values provided by the style. On
410 most platforms, the margin is 11 pixels in all directions.
411
412 \sa contentsMargins()
413*/
414void QLayout::setContentsMargins(const QMargins &margins)
415{
416 setContentsMargins(left: margins.left(), top: margins.top(), right: margins.right(), bottom: margins.bottom());
417}
418
419/*!
420 \since 4.3
421
422 For each of \a left, \a top, \a right and \a bottom that is not
423 \nullptr, stores the size of the margin named in the location the
424 pointer refers to.
425
426 By default, QLayout uses the values provided by the style. On
427 most platforms, the margin is 11 pixels in all directions.
428
429 \sa setContentsMargins(), QStyle::pixelMetric(),
430 {QStyle::}{PM_LayoutLeftMargin},
431 {QStyle::}{PM_LayoutTopMargin},
432 {QStyle::}{PM_LayoutRightMargin},
433 {QStyle::}{PM_LayoutBottomMargin}
434*/
435void QLayout::getContentsMargins(int *left, int *top, int *right, int *bottom) const
436{
437 Q_D(const QLayout);
438 d->getMargin(result: left, userMargin: d->userLeftMargin, pm: QStyle::PM_LayoutLeftMargin);
439 d->getMargin(result: top, userMargin: d->userTopMargin, pm: QStyle::PM_LayoutTopMargin);
440 d->getMargin(result: right, userMargin: d->userRightMargin, pm: QStyle::PM_LayoutRightMargin);
441 d->getMargin(result: bottom, userMargin: d->userBottomMargin, pm: QStyle::PM_LayoutBottomMargin);
442}
443
444/*!
445 \since 4.6
446
447 Returns the margins used around the layout.
448
449 By default, QLayout uses the values provided by the style. On
450 most platforms, the margin is 11 pixels in all directions.
451
452 \sa setContentsMargins()
453*/
454QMargins QLayout::contentsMargins() const
455{
456 int left, top, right, bottom;
457 getContentsMargins(left: &left, top: &top, right: &right, bottom: &bottom);
458 return QMargins(left, top, right, bottom);
459}
460
461/*!
462 \since 4.3
463
464 Returns the layout's geometry() rectangle, but taking into account the
465 contents margins.
466
467 \sa setContentsMargins(), getContentsMargins()
468*/
469QRect QLayout::contentsRect() const
470{
471 Q_D(const QLayout);
472 int left, top, right, bottom;
473 getContentsMargins(left: &left, top: &top, right: &right, bottom: &bottom);
474 return d->rect.adjusted(xp1: +left, yp1: +top, xp2: -right, yp2: -bottom);
475}
476
477
478/*!
479 Returns the parent widget of this layout, or \nullptr if this
480 layout is not installed on any widget.
481
482 If the layout is a sub-layout, this function returns the parent
483 widget of the parent layout.
484
485 \sa parent()
486*/
487QWidget *QLayout::parentWidget() const
488{
489 Q_D(const QLayout);
490 if (!d->topLevel) {
491 if (parent()) {
492 QLayout *parentLayout = qobject_cast<QLayout*>(object: parent());
493 if (Q_UNLIKELY(!parentLayout)) {
494 qWarning(msg: "QLayout::parentWidget: A layout can only have another layout as a parent.");
495 return nullptr;
496 }
497 return parentLayout->parentWidget();
498 } else {
499 return nullptr;
500 }
501 } else {
502 Q_ASSERT(parent() && parent()->isWidgetType());
503 return static_cast<QWidget *>(parent());
504 }
505}
506
507/*!
508 \reimp
509*/
510bool QLayout::isEmpty() const
511{
512 int i = 0;
513 QLayoutItem *item = itemAt(index: i);
514 while (item) {
515 if (!item->isEmpty())
516 return false;
517 ++i;
518 item = itemAt(index: i);
519 }
520 return true;
521}
522
523/*!
524 \reimp
525*/
526QSizePolicy::ControlTypes QLayout::controlTypes() const
527{
528 if (count() == 0)
529 return QSizePolicy::DefaultType;
530 QSizePolicy::ControlTypes types;
531 for (int i = count() - 1; i >= 0; --i)
532 types |= itemAt(index: i)->controlTypes();
533 return types;
534}
535
536/*!
537 \reimp
538*/
539void QLayout::setGeometry(const QRect &r)
540{
541 Q_D(QLayout);
542 d->rect = r;
543}
544
545/*!
546 \reimp
547*/
548QRect QLayout::geometry() const
549{
550 Q_D(const QLayout);
551 return d->rect;
552}
553
554/*!
555 \reimp
556*/
557void QLayout::invalidate()
558{
559 Q_D(QLayout);
560 d->rect = QRect();
561 update();
562}
563
564static bool removeWidgetRecursively(QLayoutItem *li, QObject *w)
565{
566 QLayout *lay = li->layout();
567 if (!lay)
568 return false;
569 int i = 0;
570 QLayoutItem *child;
571 while ((child = lay->itemAt(index: i))) {
572 if (child->widget() == w) {
573 delete lay->takeAt(index: i);
574 lay->invalidate();
575 return true;
576 } else if (removeWidgetRecursively(li: child, w)) {
577 return true;
578 } else {
579 ++i;
580 }
581 }
582 return false;
583}
584
585
586void QLayoutPrivate::doResize()
587{
588 Q_Q(QLayout);
589 QWidget *mw = q->parentWidget();
590 QRect rect = mw->testAttribute(attribute: Qt::WA_LayoutOnEntireRect) ? mw->rect() : mw->contentsRect();
591 const int mbh = menuBarHeightForWidth(menubar, w: rect.width());
592 const int mbTop = rect.top();
593 rect.setTop(mbTop + mbh);
594 q->setGeometry(rect);
595#if QT_CONFIG(menubar)
596 if (menubar)
597 menubar->setGeometry(ax: rect.left(), ay: mbTop, aw: rect.width(), ah: mbh);
598#endif
599}
600
601
602/*!
603 \internal
604 Performs child widget layout when the parent widget is
605 resized. Also handles removal of widgets. \a e is the
606 event
607*/
608void QLayout::widgetEvent(QEvent *e)
609{
610 Q_D(QLayout);
611 if (!d->enabled)
612 return;
613
614 switch (e->type()) {
615 case QEvent::Resize:
616 if (d->activated)
617 d->doResize();
618 else
619 activate();
620 break;
621 case QEvent::ChildRemoved:
622 {
623 QChildEvent *c = (QChildEvent *)e;
624 if (c->child()->isWidgetType()) {
625#if QT_CONFIG(menubar)
626 if (c->child() == d->menubar)
627 d->menubar = nullptr;
628#endif
629 removeWidgetRecursively(li: this, w: c->child());
630 }
631 }
632 break;
633 case QEvent::LayoutRequest:
634 if (static_cast<QWidget *>(parent())->isVisible())
635 activate();
636 break;
637 default:
638 break;
639 }
640}
641
642/*!
643 \reimp
644*/
645void QLayout::childEvent(QChildEvent *e)
646{
647 Q_D(QLayout);
648 if (!d->enabled)
649 return;
650
651 if (e->type() != QEvent::ChildRemoved)
652 return;
653
654 if (QLayout *childLayout = qobject_cast<QLayout *>(object: e->child()))
655 removeItem(childLayout);
656}
657
658/*!
659 \internal
660 Also takes contentsMargins and menu bar into account.
661*/
662int QLayout::totalHeightForWidth(int w) const
663{
664 Q_D(const QLayout);
665 int side=0, top=0;
666 if (d->topLevel) {
667 QWidget *parent = parentWidget();
668 parent->ensurePolished();
669 QWidgetPrivate *wd = parent->d_func();
670 side += wd->leftmargin + wd->rightmargin;
671 top += wd->topmargin + wd->bottommargin;
672 }
673 int h = heightForWidth(w - side) + top;
674#if QT_CONFIG(menubar)
675 h += menuBarHeightForWidth(menubar: d->menubar, w);
676#endif
677 return h;
678}
679
680/*!
681 \internal
682 Also takes contentsMargins and menu bar into account.
683*/
684QSize QLayout::totalMinimumSize() const
685{
686 Q_D(const QLayout);
687 int side=0, top=0;
688 if (d->topLevel) {
689 QWidget *pw = parentWidget();
690 pw->ensurePolished();
691 QWidgetPrivate *wd = pw->d_func();
692 side += wd->leftmargin + wd->rightmargin;
693 top += wd->topmargin + wd->bottommargin;
694 }
695
696 QSize s = minimumSize();
697#if QT_CONFIG(menubar)
698 top += menuBarHeightForWidth(menubar: d->menubar, w: s.width() + side);
699#endif
700 return s + QSize(side, top);
701}
702
703/*!
704 \internal
705 Also takes contentsMargins and menu bar into account.
706*/
707QSize QLayout::totalSizeHint() const
708{
709 Q_D(const QLayout);
710 int side=0, top=0;
711 if (d->topLevel) {
712 QWidget *pw = parentWidget();
713 pw->ensurePolished();
714 QWidgetPrivate *wd = pw->d_func();
715 side += wd->leftmargin + wd->rightmargin;
716 top += wd->topmargin + wd->bottommargin;
717 }
718
719 QSize s = sizeHint();
720 if (hasHeightForWidth())
721 s.setHeight(heightForWidth(s.width() + side));
722#if QT_CONFIG(menubar)
723 top += menuBarHeightForWidth(menubar: d->menubar, w: s.width());
724#endif
725 return s + QSize(side, top);
726}
727
728/*!
729 \internal
730 Also takes contentsMargins and menu bar into account.
731*/
732QSize QLayout::totalMaximumSize() const
733{
734 Q_D(const QLayout);
735 int side=0, top=0;
736 if (d->topLevel) {
737 QWidget *pw = parentWidget();
738 pw->ensurePolished();
739 QWidgetPrivate *wd = pw->d_func();
740 side += wd->leftmargin + wd->rightmargin;
741 top += wd->topmargin + wd->bottommargin;
742 }
743
744 QSize s = maximumSize();
745#if QT_CONFIG(menubar)
746 top += menuBarHeightForWidth(menubar: d->menubar, w: s.width());
747#endif
748
749 if (d->topLevel)
750 s = QSize(qMin(a: s.width() + side, b: QLAYOUTSIZE_MAX),
751 qMin(a: s.height() + top, b: QLAYOUTSIZE_MAX));
752 return s;
753}
754
755/*!
756 \internal
757 Destroys the layout, deleting all child layouts.
758 Geometry management stops when a top-level layout is deleted.
759
760 The layout classes will probably be fatally confused if you delete
761 a sublayout.
762*/
763QLayout::~QLayout()
764{
765 Q_D(QLayout);
766 if (d->topLevel && parent() && parent()->isWidgetType() && parentWidget()->layout() == this)
767 parentWidget()->d_func()->layout = nullptr;
768 else if (QLayout *parentLayout = qobject_cast<QLayout *>(object: parent()))
769 parentLayout->removeItem(this);
770}
771
772
773/*!
774 This function is called from \c addLayout() or \c insertLayout() functions in
775 subclasses to add layout \a l as a sub-layout.
776
777 The only scenario in which you need to call it directly is if you
778 implement a custom layout that supports nested layouts.
779
780 \sa QBoxLayout::addLayout(), QBoxLayout::insertLayout(), QGridLayout::addLayout()
781*/
782void QLayout::addChildLayout(QLayout *l)
783{
784 if (Q_UNLIKELY(l->parent())) {
785 qWarning(msg: "QLayout::addChildLayout: layout \"%ls\" already has a parent",
786 qUtf16Printable(l->objectName()));
787 return;
788 }
789 l->setParent(this);
790
791 if (QWidget *mw = parentWidget()) {
792 l->d_func()->reparentChildWidgets(mw);
793 }
794
795}
796
797/*!
798 \internal
799 */
800bool QLayout::adoptLayout(QLayout *layout)
801{
802 const bool ok = !layout->parent();
803 addChildLayout(l: layout);
804 return ok;
805}
806
807#ifdef QT_DEBUG
808static bool layoutDebug()
809{
810 static int checked_env = -1;
811 if(checked_env == -1)
812 checked_env = !!qEnvironmentVariableIntValue(varName: "QT_LAYOUT_DEBUG");
813
814 return checked_env;
815}
816#endif
817
818void QLayoutPrivate::reparentChildWidgets(QWidget *mw)
819{
820 Q_Q(QLayout);
821 int n = q->count();
822
823#if QT_CONFIG(menubar)
824 if (menubar && menubar->parentWidget() != mw) {
825 menubar->setParent(mw);
826 }
827#endif
828 bool mwVisible = mw && mw->isVisible();
829 for (int i = 0; i < n; ++i) {
830 QLayoutItem *item = q->itemAt(index: i);
831 if (QWidget *w = item->widget()) {
832 QWidget *pw = w->parentWidget();
833#ifdef QT_DEBUG
834 if (Q_UNLIKELY(pw && pw != mw && layoutDebug())) {
835 qWarning(msg: "QLayout::addChildLayout: widget %s \"%ls\" in wrong parent; moved to correct parent",
836 w->metaObject()->className(), qUtf16Printable(w->objectName()));
837 }
838#endif
839 bool needShow = mwVisible && !(w->isHidden() && w->testAttribute(attribute: Qt::WA_WState_ExplicitShowHide));
840 if (pw != mw)
841 w->setParent(mw);
842 if (needShow)
843 QMetaObject::invokeMethod(obj: w, member: "_q_showIfNotHidden", type: Qt::QueuedConnection); //show later
844 } else if (QLayout *l = item->layout()) {
845 l->d_func()->reparentChildWidgets(mw);
846 }
847 }
848}
849
850/*!
851 Returns \c true if the \a widget can be added to the \a layout;
852 otherwise returns \c false.
853*/
854bool QLayoutPrivate::checkWidget(QWidget *widget) const
855{
856 Q_Q(const QLayout);
857 if (Q_UNLIKELY(!widget)) {
858 qWarning(msg: "QLayout: Cannot add a null widget to %s/%ls", q->metaObject()->className(),
859 qUtf16Printable(q->objectName()));
860 return false;
861 }
862 if (Q_UNLIKELY(widget == q->parentWidget())) {
863 qWarning(msg: "QLayout: Cannot add parent widget %s/%ls to its child layout %s/%ls",
864 widget->metaObject()->className(), qUtf16Printable(widget->objectName()),
865 q->metaObject()->className(), qUtf16Printable(q->objectName()));
866 return false;
867 }
868 return true;
869}
870
871/*!
872 Returns \c true if the \a otherLayout can be added to the \a layout;
873 otherwise returns \c false.
874*/
875bool QLayoutPrivate::checkLayout(QLayout *otherLayout) const
876{
877 Q_Q(const QLayout);
878 if (Q_UNLIKELY(!otherLayout)) {
879 qWarning(msg: "QLayout: Cannot add a null layout to %s/%ls",
880 q->metaObject()->className(), qUtf16Printable(q->objectName()));
881 return false;
882 }
883 if (Q_UNLIKELY(otherLayout == q)) {
884 qWarning(msg: "QLayout: Cannot add layout %s/%ls to itself",
885 q->metaObject()->className(), qUtf16Printable(q->objectName()));
886 return false;
887 }
888 return true;
889}
890
891/*!
892 This function is called from \c addWidget() functions in
893 subclasses to add \a w as a managed widget of a layout.
894
895 If \a w is already managed by a layout, this function will give a warning
896 and remove \a w from that layout. This function must therefore be
897 called before adding \a w to the layout's data structure.
898*/
899void QLayout::addChildWidget(QWidget *w)
900{
901 QWidget *mw = parentWidget();
902 QWidget *pw = w->parentWidget();
903
904 //Qt::WA_LaidOut is never reset. It only means that the widget at some point has
905 //been in a layout.
906 if (pw && w->testAttribute(attribute: Qt::WA_LaidOut)) {
907 QLayout *l = pw->layout();
908 if (l && removeWidgetRecursively(li: l, w)) {
909#ifdef QT_DEBUG
910 if (Q_UNLIKELY(layoutDebug()))
911 qWarning(msg: "QLayout::addChildWidget: %s \"%ls\" is already in a layout; moved to new layout",
912 w->metaObject()->className(), qUtf16Printable(w->objectName()));
913#endif
914 }
915 }
916 if (pw && mw && pw != mw) {
917#ifdef QT_DEBUG
918 if (Q_UNLIKELY(layoutDebug()))
919 qWarning(msg: "QLayout::addChildWidget: %s \"%ls\" in wrong parent; moved to correct parent",
920 w->metaObject()->className(), qUtf16Printable(w->objectName()));
921#endif
922 pw = nullptr;
923 }
924 bool needShow = mw && mw->isVisible() && !(w->isHidden() && w->testAttribute(attribute: Qt::WA_WState_ExplicitShowHide));
925 if (!pw && mw)
926 w->setParent(mw);
927 w->setAttribute(Qt::WA_LaidOut);
928 if (needShow)
929 QMetaObject::invokeMethod(obj: w, member: "_q_showIfNotHidden", type: Qt::QueuedConnection); //show later
930}
931
932
933
934
935
936
937
938
939/*!
940 Tells the geometry manager to place the menu bar \a widget at the
941 top of parentWidget(), outside QWidget::contentsMargins(). All
942 child widgets are placed below the bottom edge of the menu bar.
943*/
944void QLayout::setMenuBar(QWidget *widget)
945{
946 Q_D(QLayout);
947 if (widget)
948 addChildWidget(w: widget);
949 d->menubar = widget;
950}
951
952/*!
953 Returns the menu bar set for this layout, or \nullptr if no
954 menu bar is set.
955*/
956
957QWidget *QLayout::menuBar() const
958{
959 Q_D(const QLayout);
960 return d->menubar;
961}
962
963
964/*!
965 Returns the minimum size of this layout. This is the smallest
966 size that the layout can have while still respecting the
967 specifications.
968
969 The returned value doesn't include the space required by
970 QWidget::setContentsMargins() or menuBar().
971
972 The default implementation allows unlimited resizing.
973*/
974QSize QLayout::minimumSize() const
975{
976 return QSize(0, 0);
977}
978
979/*!
980 Returns the maximum size of this layout. This is the largest size
981 that the layout can have while still respecting the
982 specifications.
983
984 The returned value doesn't include the space required by
985 QWidget::setContentsMargins() or menuBar().
986
987 The default implementation allows unlimited resizing.
988*/
989QSize QLayout::maximumSize() const
990{
991 return QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX);
992}
993
994/*!
995 Returns whether this layout can make use of more space than
996 sizeHint(). A value of Qt::Vertical or Qt::Horizontal means that
997 it wants to grow in only one dimension, whereas Qt::Vertical |
998 Qt::Horizontal means that it wants to grow in both dimensions.
999
1000 The default implementation returns Qt::Horizontal | Qt::Vertical.
1001 Subclasses reimplement it to return a meaningful value based on
1002 their child widgets's \l{QSizePolicy}{size policies}.
1003
1004 \sa sizeHint()
1005*/
1006Qt::Orientations QLayout::expandingDirections() const
1007{
1008 return Qt::Horizontal | Qt::Vertical;
1009}
1010
1011void QLayout::activateRecursiveHelper(QLayoutItem *item)
1012{
1013 item->invalidate();
1014 QLayout *layout = item->layout();
1015 if (layout) {
1016 QLayoutItem *child;
1017 int i=0;
1018 while ((child = layout->itemAt(index: i++)))
1019 activateRecursiveHelper(item: child);
1020 layout->d_func()->activated = true;
1021 }
1022}
1023
1024/*!
1025 Updates the layout for parentWidget().
1026
1027 You should generally not need to call this because it is
1028 automatically called at the most appropriate times.
1029
1030 \sa activate(), invalidate()
1031*/
1032
1033void QLayout::update()
1034{
1035 QLayout *layout = this;
1036 while (layout && layout->d_func()->activated) {
1037 layout->d_func()->activated = false;
1038 if (layout->d_func()->topLevel) {
1039 Q_ASSERT(layout->parent()->isWidgetType());
1040 QWidget *mw = static_cast<QWidget*>(layout->parent());
1041 QCoreApplication::postEvent(receiver: mw, event: new QEvent(QEvent::LayoutRequest));
1042 break;
1043 }
1044 layout = static_cast<QLayout*>(layout->parent());
1045 }
1046}
1047
1048/*!
1049 Redoes the layout for parentWidget() if necessary.
1050
1051 You should generally not need to call this because it is
1052 automatically called at the most appropriate times. It returns
1053 true if the layout was redone.
1054
1055 \sa update(), QWidget::updateGeometry()
1056*/
1057bool QLayout::activate()
1058{
1059 Q_D(QLayout);
1060 if (!d->enabled || !parent())
1061 return false;
1062 if (!d->topLevel)
1063 return static_cast<QLayout*>(parent())->activate();
1064 if (d->activated)
1065 return false;
1066 QWidget *mw = static_cast<QWidget*>(parent());
1067 if (Q_UNLIKELY(!mw)) {
1068 qWarning(msg: "QLayout::activate: %s \"%ls\" does not have a main widget",
1069 metaObject()->className(), qUtf16Printable(objectName()));
1070 return false;
1071 }
1072 activateRecursiveHelper(item: this);
1073
1074 QWidgetPrivate *md = mw->d_func();
1075 uint explMin = md->extra ? md->extra->explicitMinSize : 0;
1076 uint explMax = md->extra ? md->extra->explicitMaxSize : 0;
1077
1078 switch (d->constraint) {
1079 case SetFixedSize:
1080 // will trigger resize
1081 mw->setFixedSize(totalSizeHint());
1082 break;
1083 case SetMinimumSize:
1084 mw->setMinimumSize(totalMinimumSize());
1085 break;
1086 case SetMaximumSize:
1087 mw->setMaximumSize(totalMaximumSize());
1088 break;
1089 case SetMinAndMaxSize:
1090 mw->setMinimumSize(totalMinimumSize());
1091 mw->setMaximumSize(totalMaximumSize());
1092 break;
1093 case SetDefaultConstraint: {
1094 bool widthSet = explMin & Qt::Horizontal;
1095 bool heightSet = explMin & Qt::Vertical;
1096 if (mw->isWindow()) {
1097 QSize ms = totalMinimumSize();
1098 if (widthSet)
1099 ms.setWidth(mw->minimumSize().width());
1100 if (heightSet)
1101 ms.setHeight(mw->minimumSize().height());
1102 mw->setMinimumSize(ms);
1103 } else if (!widthSet || !heightSet) {
1104 QSize ms = mw->minimumSize();
1105 if (!widthSet)
1106 ms.setWidth(0);
1107 if (!heightSet)
1108 ms.setHeight(0);
1109 mw->setMinimumSize(ms);
1110 }
1111 break;
1112 }
1113 case SetNoConstraint:
1114 break;
1115 }
1116
1117 d->doResize();
1118
1119 if (md->extra) {
1120 md->extra->explicitMinSize = explMin;
1121 md->extra->explicitMaxSize = explMax;
1122 }
1123 // ideally only if sizeHint() or sizePolicy() has changed
1124 mw->updateGeometry();
1125 return true;
1126}
1127
1128/*!
1129 \since 5.2
1130
1131 Searches for widget \a from and replaces it with widget \a to if found.
1132 Returns the layout item that contains the widget \a from on success.
1133 Otherwise \nullptr is returned.
1134 If \a options contains \c Qt::FindChildrenRecursively (the default),
1135 sub-layouts are searched for doing the replacement.
1136 Any other flag in \a options is ignored.
1137
1138 Notice that the returned item therefore might not belong to this layout,
1139 but to a sub-layout.
1140
1141 The returned layout item is no longer owned by the layout and should be
1142 either deleted or inserted to another layout. The widget \a from is no
1143 longer managed by the layout and may need to be deleted or hidden. The
1144 parent of widget \a from is left unchanged.
1145
1146 This function works for the built-in Qt layouts, but might not work for
1147 custom layouts.
1148
1149 \sa indexOf()
1150*/
1151
1152QLayoutItem *QLayout::replaceWidget(QWidget *from, QWidget *to, Qt::FindChildOptions options)
1153{
1154 Q_D(QLayout);
1155 if (!from || !to)
1156 return nullptr;
1157 if (from == to) // Do not return a QLayoutItem for \a from, since ownership still
1158 return nullptr; // belongs to the layout (since nothing was changed)
1159
1160 int index = -1;
1161 QLayoutItem *item = nullptr;
1162 for (int u = 0; u < count(); ++u) {
1163 item = itemAt(index: u);
1164 if (!item)
1165 continue;
1166
1167 if (item->widget() == from) {
1168 index = u;
1169 break;
1170 }
1171
1172 if (item->layout() && (options & Qt::FindChildrenRecursively)) {
1173 QLayoutItem *r = item->layout()->replaceWidget(from, to, options);
1174 if (r)
1175 return r;
1176 }
1177 }
1178 if (index == -1)
1179 return nullptr;
1180
1181 addChildWidget(w: to);
1182 QLayoutItem *newitem = new QWidgetItem(to);
1183 newitem->setAlignment(item->alignment());
1184 QLayoutItem *r = d->replaceAt(index, newitem);
1185 if (!r)
1186 delete newitem;
1187 return r;
1188}
1189
1190/*!
1191 \fn QLayoutItem *QLayout::itemAt(int index) const
1192
1193 Must be implemented in subclasses to return the layout item at \a
1194 index. If there is no such item, the function must return 0.
1195 Items are numbered consecutively from 0. If an item is deleted, other items will be renumbered.
1196
1197 This function can be used to iterate over a layout. The following
1198 code will draw a rectangle for each layout item in the layout structure of the widget.
1199
1200 \snippet code/src_gui_kernel_qlayout.cpp 0
1201
1202 \sa count(), takeAt()
1203*/
1204
1205/*!
1206 \fn QLayoutItem *QLayout::takeAt(int index)
1207
1208 Must be implemented in subclasses to remove the layout item at \a
1209 index from the layout, and return the item. If there is no such
1210 item, the function must do nothing and return 0. Items are numbered
1211 consecutively from 0. If an item is removed, other items will be
1212 renumbered.
1213
1214 The following code fragment shows a safe way to remove all items
1215 from a layout:
1216
1217 \snippet code/src_gui_kernel_qlayout.cpp 1
1218
1219 \sa itemAt(), count()
1220*/
1221
1222/*!
1223 \fn int *QLayout::count() const
1224
1225 Must be implemented in subclasses to return the number of items
1226 in the layout.
1227
1228 \sa itemAt()
1229*/
1230
1231/*!
1232 Searches for widget \a widget in this layout (not including child
1233 layouts).
1234
1235 Returns the index of \a widget, or -1 if \a widget is not found.
1236
1237 The default implementation iterates over all items using itemAt()
1238*/
1239int QLayout::indexOf(QWidget *widget) const
1240{
1241 int i = 0;
1242 QLayoutItem *item = itemAt(index: i);
1243 while (item) {
1244 if (item->widget() == widget)
1245 return i;
1246 ++i;
1247 item = itemAt(index: i);
1248 }
1249 return -1;
1250}
1251
1252/*!
1253 \since 5.12
1254 Searches for layout item \a layoutItem in this layout (not including child
1255 layouts).
1256
1257 Returns the index of \a layoutItem, or -1 if \a layoutItem is not found.
1258*/
1259int QLayout::indexOf(QLayoutItem *layoutItem) const
1260{
1261 int i = 0;
1262 QLayoutItem *item = itemAt(index: i);
1263 while (item) {
1264 if (item == layoutItem)
1265 return i;
1266 ++i;
1267 item = itemAt(index: i);
1268 }
1269 return -1;
1270}
1271
1272/*!
1273 \enum QLayout::SizeConstraint
1274
1275 The possible values are:
1276
1277 \value SetDefaultConstraint The main widget's minimum size is set
1278 to minimumSize(), unless the widget already has
1279 a minimum size.
1280
1281 \value SetFixedSize The main widget's size is set to sizeHint(); it
1282 cannot be resized at all.
1283 \value SetMinimumSize The main widget's minimum size is set to
1284 minimumSize(); it cannot be smaller.
1285
1286 \value SetMaximumSize The main widget's maximum size is set to
1287 maximumSize(); it cannot be larger.
1288
1289 \value SetMinAndMaxSize The main widget's minimum size is set to
1290 minimumSize() and its maximum size is set to
1291 maximumSize().
1292
1293 \value SetNoConstraint The widget is not constrained.
1294
1295 \sa setSizeConstraint()
1296*/
1297
1298/*!
1299 \property QLayout::sizeConstraint
1300 \brief the resize mode of the layout
1301
1302 The default mode is \l {QLayout::SetDefaultConstraint}
1303 {SetDefaultConstraint}.
1304*/
1305void QLayout::setSizeConstraint(SizeConstraint constraint)
1306{
1307 Q_D(QLayout);
1308 if (constraint == d->constraint)
1309 return;
1310
1311 d->constraint = constraint;
1312 invalidate();
1313}
1314
1315QLayout::SizeConstraint QLayout::sizeConstraint() const
1316{
1317 Q_D(const QLayout);
1318 return d->constraint;
1319}
1320
1321/*!
1322 Returns the rectangle that should be covered when the geometry of
1323 this layout is set to \a r, provided that this layout supports
1324 setAlignment().
1325
1326 The result is derived from sizeHint() and expanding(). It is never
1327 larger than \a r.
1328*/
1329QRect QLayout::alignmentRect(const QRect &r) const
1330{
1331 QSize s = sizeHint();
1332 Qt::Alignment a = alignment();
1333
1334 /*
1335 This is a hack to obtain the real maximum size, not
1336 QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX), the value consistently
1337 returned by QLayoutItems that have an alignment.
1338 */
1339 QLayout *that = const_cast<QLayout *>(this);
1340 that->setAlignment({ });
1341 QSize ms = that->maximumSize();
1342 that->setAlignment(a);
1343
1344 if ((expandingDirections() & Qt::Horizontal) ||
1345 !(a & Qt::AlignHorizontal_Mask)) {
1346 s.setWidth(qMin(a: r.width(), b: ms.width()));
1347 }
1348 if ((expandingDirections() & Qt::Vertical) ||
1349 !(a & Qt::AlignVertical_Mask)) {
1350 s.setHeight(qMin(a: r.height(), b: ms.height()));
1351 } else if (hasHeightForWidth()) {
1352 int hfw = heightForWidth(s.width());
1353 if (hfw < s.height())
1354 s.setHeight(qMin(a: hfw, b: ms.height()));
1355 }
1356
1357 s = s.boundedTo(otherSize: r.size());
1358 int x = r.x();
1359 int y = r.y();
1360
1361 if (a & Qt::AlignBottom)
1362 y += (r.height() - s.height());
1363 else if (!(a & Qt::AlignTop))
1364 y += (r.height() - s.height()) / 2;
1365
1366 QWidget *parent = parentWidget();
1367 a = QStyle::visualAlignment(direction: parent ? parent->layoutDirection() : QGuiApplication::layoutDirection(), alignment: a);
1368 if (a & Qt::AlignRight)
1369 x += (r.width() - s.width());
1370 else if (!(a & Qt::AlignLeft))
1371 x += (r.width() - s.width()) / 2;
1372
1373 return QRect(x, y, s.width(), s.height());
1374}
1375
1376/*!
1377 Removes the widget \a widget from the layout. After this call, it
1378 is the caller's responsibility to give the widget a reasonable
1379 geometry or to put the widget back into a layout or to explicitly
1380 hide it if necessary.
1381
1382 \b{Note:} The ownership of \a widget remains the same as
1383 when it was added.
1384
1385 \sa removeItem(), QWidget::setGeometry(), addWidget()
1386*/
1387void QLayout::removeWidget(QWidget *widget)
1388{
1389 if (Q_UNLIKELY(!widget)) {
1390 qWarning(msg: "QLayout::removeWidget: Cannot remove a null widget.");
1391 return;
1392 }
1393
1394 int i = 0;
1395 QLayoutItem *child;
1396 while ((child = itemAt(index: i))) {
1397 if (child->widget() == widget) {
1398 delete takeAt(index: i);
1399 invalidate();
1400 } else {
1401 ++i;
1402 }
1403 }
1404}
1405
1406/*!
1407 Removes the layout item \a item from the layout. It is the
1408 caller's responsibility to delete the item.
1409
1410 Notice that \a item can be a layout (since QLayout inherits
1411 QLayoutItem).
1412
1413 \sa removeWidget(), addItem()
1414*/
1415void QLayout::removeItem(QLayoutItem *item)
1416{
1417 int i = 0;
1418 QLayoutItem *child;
1419 while ((child = itemAt(index: i))) {
1420 if (child == item) {
1421 takeAt(index: i);
1422 invalidate();
1423 } else {
1424 ++i;
1425 }
1426 }
1427}
1428
1429/*!
1430 Enables this layout if \a enable is true, otherwise disables it.
1431
1432 An enabled layout adjusts dynamically to changes; a disabled
1433 layout acts as if it did not exist.
1434
1435 By default all layouts are enabled.
1436
1437 \sa isEnabled()
1438*/
1439void QLayout::setEnabled(bool enable)
1440{
1441 Q_D(QLayout);
1442 d->enabled = enable;
1443}
1444
1445/*!
1446 Returns \c true if the layout is enabled; otherwise returns \c false.
1447
1448 \sa setEnabled()
1449*/
1450bool QLayout::isEnabled() const
1451{
1452 Q_D(const QLayout);
1453 return d->enabled;
1454}
1455
1456/*!
1457 Returns a size that satisfies all size constraints on \a widget,
1458 including heightForWidth() and that is as close as possible to \a
1459 size.
1460*/
1461
1462QSize QLayout::closestAcceptableSize(const QWidget *widget, const QSize &size)
1463{
1464 QSize result = size.boundedTo(otherSize: qSmartMaxSize(w: widget));
1465 result = result.expandedTo(otherSize: qSmartMinSize(w: widget));
1466 QLayout *l = widget->layout();
1467 if (l && l->hasHeightForWidth() && result.height() < l->minimumHeightForWidth(result.width()) ) {
1468 QSize current = widget->size();
1469 int currentHfw = l->minimumHeightForWidth(current.width());
1470 int newHfw = l->minimumHeightForWidth(result.width());
1471 if (current.height() < currentHfw || currentHfw == newHfw) {
1472 //handle the constant hfw case and the vertical-only case, as well as the
1473 // current-size-is-not-correct case
1474 result.setHeight(newHfw);
1475 } else {
1476 // binary search; assume hfw is decreasing ###
1477
1478 int maxw = qMax(a: widget->width(),b: result.width());
1479 int maxh = qMax(a: widget->height(), b: result.height());
1480 int minw = qMin(a: widget->width(),b: result.width());
1481 int minh = qMin(a: widget->height(), b: result.height());
1482
1483 int minhfw = l->minimumHeightForWidth(minw);
1484 int maxhfw = l->minimumHeightForWidth(maxw);
1485 while (minw < maxw) {
1486 if (minhfw > maxh) { //assume decreasing
1487 minw = maxw - (maxw-minw)/2;
1488 minhfw = l->minimumHeightForWidth(minw);
1489 } else if (maxhfw < minh ) { //assume decreasing
1490 maxw = minw + (maxw-minw)/2;
1491 maxhfw = l->minimumHeightForWidth(maxw);
1492 } else {
1493 break;
1494 }
1495 }
1496 result = result.expandedTo(otherSize: QSize(minw, minhfw));
1497 }
1498 }
1499 return result;
1500}
1501
1502QT_END_NAMESPACE
1503
1504#include "moc_qlayout.cpp"
1505

source code of qtbase/src/widgets/kernel/qlayout.cpp