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

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