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 "qapplication.h"
5#include "qboxlayout.h"
6#include "qlist.h"
7#include "qsizepolicy.h"
8#include "qwidget.h"
9
10#include "qlayout_p.h"
11#include "qlayoutengine_p.h"
12
13QT_BEGIN_NAMESPACE
14
15struct QBoxLayoutItem
16{
17 QBoxLayoutItem(QLayoutItem *it, int stretch_ = 0)
18 : item(it), stretch(stretch_), magic(false) { }
19 ~QBoxLayoutItem() { delete item; }
20
21 int hfw(int w) {
22 if (item->hasHeightForWidth()) {
23 return item->heightForWidth(w);
24 } else {
25 return item->sizeHint().height();
26 }
27 }
28 int mhfw(int w) {
29 if (item->hasHeightForWidth()) {
30 return item->minimumHeightForWidth(w);
31 } else {
32 return item->minimumSize().height();
33 }
34 }
35 int hStretch() {
36 if (stretch == 0 && item->widget()) {
37 return item->widget()->sizePolicy().horizontalStretch();
38 } else {
39 return stretch;
40 }
41 }
42 int vStretch() {
43 if (stretch == 0 && item->widget()) {
44 return item->widget()->sizePolicy().verticalStretch();
45 } else {
46 return stretch;
47 }
48 }
49
50 QLayoutItem *item;
51 int stretch;
52 bool magic;
53};
54
55class QBoxLayoutPrivate : public QLayoutPrivate
56{
57 Q_DECLARE_PUBLIC(QBoxLayout)
58public:
59 QBoxLayoutPrivate() : hfwWidth(-1), dirty(true), spacing(-1) { }
60 ~QBoxLayoutPrivate();
61
62 void setDirty() {
63 geomArray.clear();
64 hfwWidth = -1;
65 hfwHeight = -1;
66 dirty = true;
67 }
68
69 QList<QBoxLayoutItem *> list;
70 QList<QLayoutStruct> geomArray;
71 int hfwWidth;
72 int hfwHeight;
73 int hfwMinHeight;
74 QSize sizeHint;
75 QSize minSize;
76 QSize maxSize;
77 int leftMargin, topMargin, rightMargin, bottomMargin;
78 Qt::Orientations expanding;
79 uint hasHfw : 1;
80 uint dirty : 1;
81 QBoxLayout::Direction dir;
82 int spacing;
83
84 inline void deleteAll() { while (!list.isEmpty()) delete list.takeFirst(); }
85
86 void setupGeom();
87 void calcHfw(int);
88
89 void effectiveMargins(int *left, int *top, int *right, int *bottom) const;
90 QLayoutItem* replaceAt(int index, QLayoutItem*) override;
91 int validateIndex(int index) const;
92};
93
94QBoxLayoutPrivate::~QBoxLayoutPrivate()
95{
96}
97
98static inline bool horz(QBoxLayout::Direction dir)
99{
100 return dir == QBoxLayout::RightToLeft || dir == QBoxLayout::LeftToRight;
101}
102
103/**
104 * The purpose of this function is to make sure that widgets are not laid out outside its layout.
105 * E.g. the layoutItemRect margins are only meant to take of the surrounding margins/spacings.
106 * However, if the margin is 0, it can easily cover the area of a widget above it.
107 */
108void QBoxLayoutPrivate::effectiveMargins(int *left, int *top, int *right, int *bottom) const
109{
110 int l = leftMargin;
111 int t = topMargin;
112 int r = rightMargin;
113 int b = bottomMargin;
114#ifdef Q_OS_MAC
115 Q_Q(const QBoxLayout);
116 if (horz(dir)) {
117 QBoxLayoutItem *leftBox = nullptr;
118 QBoxLayoutItem *rightBox = nullptr;
119
120 if (left || right) {
121 leftBox = list.value(0);
122 rightBox = list.value(list.count() - 1);
123 if (dir == QBoxLayout::RightToLeft)
124 qSwap(leftBox, rightBox);
125
126 int leftDelta = 0;
127 int rightDelta = 0;
128 if (leftBox) {
129 QLayoutItem *itm = leftBox->item;
130 if (QWidget *w = itm->widget())
131 leftDelta = itm->geometry().left() - w->geometry().left();
132 }
133 if (rightBox) {
134 QLayoutItem *itm = rightBox->item;
135 if (QWidget *w = itm->widget())
136 rightDelta = w->geometry().right() - itm->geometry().right();
137 }
138 QWidget *w = q->parentWidget();
139 Qt::LayoutDirection layoutDirection = w ? w->layoutDirection() : QGuiApplication::layoutDirection();
140 if (layoutDirection == Qt::RightToLeft)
141 qSwap(leftDelta, rightDelta);
142
143 l = qMax(l, leftDelta);
144 r = qMax(r, rightDelta);
145 }
146
147 int count = top || bottom ? list.count() : 0;
148 for (int i = 0; i < count; ++i) {
149 QBoxLayoutItem *box = list.at(i);
150 QLayoutItem *itm = box->item;
151 QWidget *w = itm->widget();
152 if (w) {
153 QRect lir = itm->geometry();
154 QRect wr = w->geometry();
155 if (top)
156 t = qMax(t, lir.top() - wr.top());
157 if (bottom)
158 b = qMax(b, wr.bottom() - lir.bottom());
159 }
160 }
161 } else { // vertical layout
162 QBoxLayoutItem *topBox = nullptr;
163 QBoxLayoutItem *bottomBox = nullptr;
164
165 if (top || bottom) {
166 topBox = list.value(0);
167 bottomBox = list.value(list.count() - 1);
168 if (dir == QBoxLayout::BottomToTop) {
169 qSwap(topBox, bottomBox);
170 }
171
172 if (top && topBox) {
173 QLayoutItem *itm = topBox->item;
174 QWidget *w = itm->widget();
175 if (w)
176 t = qMax(t, itm->geometry().top() - w->geometry().top());
177 }
178
179 if (bottom && bottomBox) {
180 QLayoutItem *itm = bottomBox->item;
181 QWidget *w = itm->widget();
182 if (w)
183 b = qMax(b, w->geometry().bottom() - itm->geometry().bottom());
184 }
185 }
186
187 int count = left || right ? list.count() : 0;
188 for (int i = 0; i < count; ++i) {
189 QBoxLayoutItem *box = list.at(i);
190 QLayoutItem *itm = box->item;
191 QWidget *w = itm->widget();
192 if (w) {
193 QRect lir = itm->geometry();
194 QRect wr = w->geometry();
195 if (left)
196 l = qMax(l, lir.left() - wr.left());
197 if (right)
198 r = qMax(r, wr.right() - lir.right());
199 }
200 }
201 }
202#endif
203 if (left)
204 *left = l;
205 if (top)
206 *top = t;
207 if (right)
208 *right = r;
209 if (bottom)
210 *bottom = b;
211}
212
213
214/*
215 Initializes the data structure needed by qGeomCalc and
216 recalculates max/min and size hint.
217*/
218void QBoxLayoutPrivate::setupGeom()
219{
220 if (!dirty)
221 return;
222
223 Q_Q(QBoxLayout);
224 int maxw = horz(dir) ? 0 : QLAYOUTSIZE_MAX;
225 int maxh = horz(dir) ? QLAYOUTSIZE_MAX : 0;
226 int minw = 0;
227 int minh = 0;
228 int hintw = 0;
229 int hinth = 0;
230
231 bool horexp = false;
232 bool verexp = false;
233
234 hasHfw = false;
235
236 int n = list.size();
237 geomArray.clear();
238 QList<QLayoutStruct> a(n);
239
240 QSizePolicy::ControlTypes controlTypes1;
241 QSizePolicy::ControlTypes controlTypes2;
242 int fixedSpacing = q->spacing();
243 int previousNonEmptyIndex = -1;
244
245 QStyle *style = nullptr;
246 if (fixedSpacing < 0) {
247 if (QWidget *parentWidget = q->parentWidget())
248 style = parentWidget->style();
249 }
250
251 for (int i = 0; i < n; i++) {
252 QBoxLayoutItem *box = list.at(i);
253 QSize max = box->item->maximumSize();
254 QSize min = box->item->minimumSize();
255 QSize hint = box->item->sizeHint();
256 Qt::Orientations exp = box->item->expandingDirections();
257 bool empty = box->item->isEmpty();
258 int spacing = 0;
259
260 if (!empty) {
261 if (fixedSpacing >= 0) {
262 spacing = (previousNonEmptyIndex >= 0) ? fixedSpacing : 0;
263#ifdef Q_OS_MAC
264 if (!horz(dir) && previousNonEmptyIndex >= 0) {
265 QBoxLayoutItem *sibling = (dir == QBoxLayout::TopToBottom ? box : list.at(previousNonEmptyIndex));
266 if (sibling) {
267 QWidget *wid = sibling->item->widget();
268 if (wid)
269 spacing = qMax(spacing, sibling->item->geometry().top() - wid->geometry().top());
270 }
271 }
272#endif
273 } else {
274 controlTypes1 = controlTypes2;
275 controlTypes2 = box->item->controlTypes();
276 if (previousNonEmptyIndex >= 0) {
277 QSizePolicy::ControlTypes actual1 = controlTypes1;
278 QSizePolicy::ControlTypes actual2 = controlTypes2;
279 if (dir == QBoxLayout::RightToLeft || dir == QBoxLayout::BottomToTop)
280 qSwap(value1&: actual1, value2&: actual2);
281
282 if (style) {
283 spacing = style->combinedLayoutSpacing(controls1: actual1, controls2: actual2,
284 orientation: horz(dir) ? Qt::Horizontal : Qt::Vertical,
285 option: nullptr, widget: q->parentWidget());
286 if (spacing < 0)
287 spacing = 0;
288 }
289 }
290 }
291
292 if (previousNonEmptyIndex >= 0)
293 a[previousNonEmptyIndex].spacing = spacing;
294 previousNonEmptyIndex = i;
295 }
296
297 bool ignore = empty && box->item->widget(); // ignore hidden widgets
298 bool dummy = true;
299 if (horz(dir)) {
300 bool expand = (exp & Qt::Horizontal || box->stretch > 0);
301 horexp = horexp || expand;
302 maxw += spacing + max.width();
303 minw += spacing + min.width();
304 hintw += spacing + hint.width();
305 if (!ignore)
306 qMaxExpCalc(max&: maxh, exp&: verexp, empty&: dummy,
307 boxmax: max.height(), boxexp: exp & Qt::Vertical, boxempty: box->item->isEmpty());
308 minh = qMax(a: minh, b: min.height());
309 hinth = qMax(a: hinth, b: hint.height());
310
311 a[i].sizeHint = hint.width();
312 a[i].maximumSize = max.width();
313 a[i].minimumSize = min.width();
314 a[i].expansive = expand;
315 a[i].stretch = box->stretch ? box->stretch : box->hStretch();
316 } else {
317 bool expand = (exp & Qt::Vertical || box->stretch > 0);
318 verexp = verexp || expand;
319 maxh += spacing + max.height();
320 minh += spacing + min.height();
321 hinth += spacing + hint.height();
322 if (!ignore)
323 qMaxExpCalc(max&: maxw, exp&: horexp, empty&: dummy,
324 boxmax: max.width(), boxexp: exp & Qt::Horizontal, boxempty: box->item->isEmpty());
325 minw = qMax(a: minw, b: min.width());
326 hintw = qMax(a: hintw, b: hint.width());
327
328 a[i].sizeHint = hint.height();
329 a[i].maximumSize = max.height();
330 a[i].minimumSize = min.height();
331 a[i].expansive = expand;
332 a[i].stretch = box->stretch ? box->stretch : box->vStretch();
333 }
334
335 a[i].empty = empty;
336 a[i].spacing = 0; // might be initialized with a non-zero value in a later iteration
337 hasHfw = hasHfw || box->item->hasHeightForWidth();
338 }
339
340 geomArray = a;
341
342 expanding = (Qt::Orientations)
343 ((horexp ? Qt::Horizontal : 0)
344 | (verexp ? Qt::Vertical : 0));
345
346 minSize = QSize(minw, minh);
347 maxSize = QSize(maxw, maxh).expandedTo(otherSize: minSize);
348 sizeHint = QSize(hintw, hinth).expandedTo(otherSize: minSize).boundedTo(otherSize: maxSize);
349
350 q->getContentsMargins(left: &leftMargin, top: &topMargin, right: &rightMargin, bottom: &bottomMargin);
351 int left, top, right, bottom;
352 effectiveMargins(left: &left, top: &top, right: &right, bottom: &bottom);
353 QSize extra(left + right, top + bottom);
354
355 minSize += extra;
356 maxSize += extra;
357 sizeHint += extra;
358
359 dirty = false;
360}
361
362/*
363 Calculates and stores the preferred height given the width \a w.
364*/
365void QBoxLayoutPrivate::calcHfw(int w)
366{
367 QList<QLayoutStruct> &a = geomArray;
368 int n = a.size();
369 int h = 0;
370 int mh = 0;
371
372 Q_ASSERT(n == list.size());
373
374 if (horz(dir)) {
375 qGeomCalc(chain&: a, start: 0, count: n, pos: 0, space: w);
376 for (int i = 0; i < n; i++) {
377 QBoxLayoutItem *box = list.at(i);
378 h = qMax(a: h, b: box->hfw(w: a.at(i).size));
379 mh = qMax(a: mh, b: box->mhfw(w: a.at(i).size));
380 }
381 } else {
382 for (int i = 0; i < n; ++i) {
383 QBoxLayoutItem *box = list.at(i);
384 int spacing = a.at(i).spacing;
385 h += box->hfw(w);
386 mh += box->mhfw(w);
387 h += spacing;
388 mh += spacing;
389 }
390 }
391 hfwWidth = w;
392 hfwHeight = h;
393 hfwMinHeight = mh;
394}
395
396QLayoutItem* QBoxLayoutPrivate::replaceAt(int index, QLayoutItem *item)
397{
398 Q_Q(QBoxLayout);
399 if (!item)
400 return nullptr;
401 QBoxLayoutItem *b = list.value(i: index);
402 if (!b)
403 return nullptr;
404 QLayoutItem *r = b->item;
405
406 b->item = item;
407 q->invalidate();
408 return r;
409}
410
411int QBoxLayoutPrivate::validateIndex(int index) const
412{
413 if (index < 0)
414 return list.size(); // append
415
416 if (index > list.size())
417 qWarning(msg: "QBoxLayout::insert: index %d out of range (max: %d)", index, int(list.size()));
418 return index <= list.size() ? index : list.size();
419}
420
421/*!
422 \class QBoxLayout
423
424 \brief The QBoxLayout class lines up child widgets horizontally or
425 vertically.
426
427 \ingroup geomanagement
428 \inmodule QtWidgets
429
430 QBoxLayout takes the space it gets (from its parent layout or from
431 the parentWidget()), divides it up into a row of boxes, and makes
432 each managed widget fill one box.
433
434 \image qhboxlayout-with-5-children.png Horizontal box layout with five child widgets
435
436 If the QBoxLayout's orientation is Qt::Horizontal the boxes are
437 placed in a row, with suitable sizes. Each widget (or other box)
438 will get at least its minimum size and at most its maximum size.
439 Any excess space is shared according to the stretch factors (more
440 about that below).
441
442 \image qvboxlayout-with-5-children.png Vertical box layout with five child widgets
443
444 If the QBoxLayout's orientation is Qt::Vertical, the boxes are
445 placed in a column, again with suitable sizes.
446
447 The easiest way to create a QBoxLayout is to use one of the
448 convenience classes, e.g. QHBoxLayout (for Qt::Horizontal boxes)
449 or QVBoxLayout (for Qt::Vertical boxes). You can also use the
450 QBoxLayout constructor directly, specifying its direction as
451 LeftToRight, RightToLeft, TopToBottom, or BottomToTop.
452
453 If the QBoxLayout is not the top-level layout (i.e. it is not
454 managing all of the widget's area and children), you must add it
455 to its parent layout before you can do anything with it. The
456 normal way to add a layout is by calling
457 parentLayout-\>addLayout().
458
459 Once you have done this, you can add boxes to the QBoxLayout using
460 one of four functions:
461
462 \list
463 \li addWidget() to add a widget to the QBoxLayout and set the
464 widget's stretch factor. (The stretch factor is along the row of
465 boxes.)
466
467 \li addSpacing() to create an empty box; this is one of the
468 functions you use to create nice and spacious dialogs. See below
469 for ways to set margins.
470
471 \li addStretch() to create an empty, stretchable box.
472
473 \li addLayout() to add a box containing another QLayout to the row
474 and set that layout's stretch factor.
475 \endlist
476
477 Use insertWidget(), insertSpacing(), insertStretch() or
478 insertLayout() to insert a box at a specified position in the
479 layout.
480
481 QBoxLayout also includes two margin widths:
482
483 \list
484 \li setContentsMargins() sets the width of the outer border on
485 each side of the widget. This is the width of the reserved space
486 along each of the QBoxLayout's four sides.
487 \li setSpacing() sets the width between neighboring boxes. (You
488 can use addSpacing() to get more space at a particular spot.)
489 \endlist
490
491 The margin default is provided by the style. The default margin
492 most Qt styles specify is 9 for child widgets and 11 for windows.
493 The spacing defaults to the same as the margin width for a
494 top-level layout, or to the same as the parent layout.
495
496 To remove a widget from a layout, call removeWidget(). Calling
497 QWidget::hide() on a widget also effectively removes the widget
498 from the layout until QWidget::show() is called.
499
500 You will almost always want to use QVBoxLayout and QHBoxLayout
501 rather than QBoxLayout because of their convenient constructors.
502
503 \sa QGridLayout, QStackedLayout, {Layout Management}
504*/
505
506/*!
507 \enum QBoxLayout::Direction
508
509 This type is used to determine the direction of a box layout.
510
511 \value LeftToRight Horizontal from left to right.
512 \value RightToLeft Horizontal from right to left.
513 \value TopToBottom Vertical from top to bottom.
514 \value BottomToTop Vertical from bottom to top.
515
516 \omitvalue Down
517 \omitvalue Up
518*/
519
520/*!
521 Constructs a new QBoxLayout with direction \a dir and parent widget \a
522 parent.
523
524 The layout is set directly as the top-level layout for \a parent.
525 There can be only one top-level layout for a widget. It is returned
526 by QWidget::layout().
527
528 \sa direction(), QWidget::setLayout()
529*/
530QBoxLayout::QBoxLayout(Direction dir, QWidget *parent)
531 : QLayout(*new QBoxLayoutPrivate, nullptr, parent)
532{
533 Q_D(QBoxLayout);
534 d->dir = dir;
535}
536
537
538
539/*!
540 Destroys this box layout.
541
542 The layout's widgets aren't destroyed.
543*/
544QBoxLayout::~QBoxLayout()
545{
546 Q_D(QBoxLayout);
547 d->deleteAll(); // must do it before QObject deletes children, so can't be in ~QBoxLayoutPrivate
548}
549
550/*!
551 Reimplements QLayout::spacing(). If the spacing property is
552 valid, that value is returned. Otherwise, a value for the spacing
553 property is computed and returned. Since layout spacing in a widget
554 is style dependent, if the parent is a widget, it queries the style
555 for the (horizontal or vertical) spacing of the layout. Otherwise,
556 the parent is a layout, and it queries the parent layout for the
557 spacing().
558
559 \sa QLayout::spacing(), setSpacing()
560 */
561int QBoxLayout::spacing() const
562{
563 Q_D(const QBoxLayout);
564 if (d->spacing >=0) {
565 return d->spacing;
566 } else {
567 return qSmartSpacing(layout: this, pm: d->dir == LeftToRight || d->dir == RightToLeft
568 ? QStyle::PM_LayoutHorizontalSpacing
569 : QStyle::PM_LayoutVerticalSpacing);
570 }
571}
572
573/*!
574 Reimplements QLayout::setSpacing(). Sets the spacing
575 property to \a spacing.
576
577 \sa QLayout::setSpacing(), spacing()
578 */
579void QBoxLayout::setSpacing(int spacing)
580{
581 Q_D(QBoxLayout);
582 d->spacing = spacing;
583 invalidate();
584}
585
586/*!
587 \reimp
588*/
589QSize QBoxLayout::sizeHint() const
590{
591 Q_D(const QBoxLayout);
592 if (d->dirty)
593 const_cast<QBoxLayout*>(this)->d_func()->setupGeom();
594 return d->sizeHint;
595}
596
597/*!
598 \reimp
599*/
600QSize QBoxLayout::minimumSize() const
601{
602 Q_D(const QBoxLayout);
603 if (d->dirty)
604 const_cast<QBoxLayout*>(this)->d_func()->setupGeom();
605 return d->minSize;
606}
607
608/*!
609 \reimp
610*/
611QSize QBoxLayout::maximumSize() const
612{
613 Q_D(const QBoxLayout);
614 if (d->dirty)
615 const_cast<QBoxLayout*>(this)->d_func()->setupGeom();
616
617 QSize s = d->maxSize.boundedTo(otherSize: QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX));
618
619 if (alignment() & Qt::AlignHorizontal_Mask)
620 s.setWidth(QLAYOUTSIZE_MAX);
621 if (alignment() & Qt::AlignVertical_Mask)
622 s.setHeight(QLAYOUTSIZE_MAX);
623 return s;
624}
625
626/*!
627 \reimp
628*/
629bool QBoxLayout::hasHeightForWidth() const
630{
631 Q_D(const QBoxLayout);
632 if (d->dirty)
633 const_cast<QBoxLayout*>(this)->d_func()->setupGeom();
634 return d->hasHfw;
635}
636
637/*!
638 \reimp
639*/
640int QBoxLayout::heightForWidth(int w) const
641{
642 Q_D(const QBoxLayout);
643 if (!hasHeightForWidth())
644 return -1;
645
646 int left, top, right, bottom;
647 d->effectiveMargins(left: &left, top: &top, right: &right, bottom: &bottom);
648
649 w -= left + right;
650 if (w != d->hfwWidth)
651 const_cast<QBoxLayout*>(this)->d_func()->calcHfw(w);
652
653 return d->hfwHeight + top + bottom;
654}
655
656/*!
657 \reimp
658*/
659int QBoxLayout::minimumHeightForWidth(int w) const
660{
661 Q_D(const QBoxLayout);
662 (void) heightForWidth(w);
663 int top, bottom;
664 d->effectiveMargins(left: nullptr, top: &top, right: nullptr, bottom: &bottom);
665 return d->hasHfw ? (d->hfwMinHeight + top + bottom) : -1;
666}
667
668/*!
669 Resets cached information.
670*/
671void QBoxLayout::invalidate()
672{
673 Q_D(QBoxLayout);
674 d->setDirty();
675 QLayout::invalidate();
676}
677
678/*!
679 \reimp
680*/
681int QBoxLayout::count() const
682{
683 Q_D(const QBoxLayout);
684 return d->list.size();
685}
686
687/*!
688 \reimp
689*/
690QLayoutItem *QBoxLayout::itemAt(int index) const
691{
692 Q_D(const QBoxLayout);
693 return index >= 0 && index < d->list.size() ? d->list.at(i: index)->item : nullptr;
694}
695
696/*!
697 \reimp
698*/
699QLayoutItem *QBoxLayout::takeAt(int index)
700{
701 Q_D(QBoxLayout);
702 if (index < 0 || index >= d->list.size())
703 return nullptr;
704 QBoxLayoutItem *b = d->list.takeAt(i: index);
705 QLayoutItem *item = b->item;
706 b->item = nullptr;
707 delete b;
708
709 if (QLayout *l = item->layout()) {
710 // sanity check in case the user passed something weird to QObject::setParent()
711 if (l->parent() == this)
712 l->setParent(nullptr);
713 }
714
715 invalidate();
716 return item;
717}
718
719
720/*!
721 \reimp
722*/
723Qt::Orientations QBoxLayout::expandingDirections() const
724{
725 Q_D(const QBoxLayout);
726 if (d->dirty)
727 const_cast<QBoxLayout*>(this)->d_func()->setupGeom();
728 return d->expanding;
729}
730
731/*!
732 \reimp
733*/
734void QBoxLayout::setGeometry(const QRect &r)
735{
736 Q_D(QBoxLayout);
737 if (d->dirty || r != geometry()) {
738 QRect oldRect = geometry();
739 QLayout::setGeometry(r);
740 if (d->dirty)
741 d->setupGeom();
742 QRect cr = alignment() ? alignmentRect(r) : r;
743
744 int left, top, right, bottom;
745 d->effectiveMargins(left: &left, top: &top, right: &right, bottom: &bottom);
746 QRect s(cr.x() + left, cr.y() + top,
747 cr.width() - (left + right),
748 cr.height() - (top + bottom));
749
750 QList<QLayoutStruct> a = d->geomArray;
751 int pos = horz(dir: d->dir) ? s.x() : s.y();
752 int space = horz(dir: d->dir) ? s.width() : s.height();
753 int n = a.size();
754 if (d->hasHfw && !horz(dir: d->dir)) {
755 for (int i = 0; i < n; i++) {
756 QBoxLayoutItem *box = d->list.at(i);
757 if (box->item->hasHeightForWidth()) {
758 int width = qBound(min: box->item->minimumSize().width(), val: s.width(), max: box->item->maximumSize().width());
759 a[i].sizeHint = a[i].minimumSize =
760 box->item->heightForWidth(width);
761 }
762 }
763 }
764
765 Direction visualDir = d->dir;
766 QWidget *parent = parentWidget();
767 if (parent && parent->isRightToLeft()) {
768 if (d->dir == LeftToRight)
769 visualDir = RightToLeft;
770 else if (d->dir == RightToLeft)
771 visualDir = LeftToRight;
772 }
773
774 qGeomCalc(chain&: a, start: 0, count: n, pos, space);
775
776 bool reverse = (horz(dir: visualDir)
777 ? ((r.right() > oldRect.right()) != (visualDir == RightToLeft))
778 : r.bottom() > oldRect.bottom());
779 for (int j = 0; j < n; j++) {
780 int i = reverse ? n-j-1 : j;
781 QBoxLayoutItem *box = d->list.at(i);
782
783 switch (visualDir) {
784 case LeftToRight:
785 box->item->setGeometry(QRect(a.at(i).pos, s.y(), a.at(i).size, s.height()));
786 break;
787 case RightToLeft:
788 box->item->setGeometry(QRect(s.left() + s.right() - a.at(i).pos - a.at(i).size + 1,
789 s.y(), a.at(i).size, s.height()));
790 break;
791 case TopToBottom:
792 box->item->setGeometry(QRect(s.x(), a.at(i).pos, s.width(), a.at(i).size));
793 break;
794 case BottomToTop:
795 box->item->setGeometry(QRect(s.x(),
796 s.top() + s.bottom() - a.at(i).pos - a.at(i).size + 1,
797 s.width(), a.at(i).size));
798 }
799 }
800 }
801}
802
803/*!
804 \reimp
805*/
806void QBoxLayout::addItem(QLayoutItem *item)
807{
808 Q_D(QBoxLayout);
809 QBoxLayoutItem *it = new QBoxLayoutItem(item);
810 d->list.append(t: it);
811 invalidate();
812}
813
814/*!
815 Inserts \a item into this box layout at position \a index.
816 Index must be either negative or within the range 0 to count(),
817 inclusive. If \a index is negative or count(), the item is
818 added at the end.
819
820 \sa addItem(), insertWidget(), insertLayout(), insertStretch(),
821 insertSpacing()
822*/
823void QBoxLayout::insertItem(int index, QLayoutItem *item)
824{
825 Q_D(QBoxLayout);
826 index = d->validateIndex(index);
827 QBoxLayoutItem *it = new QBoxLayoutItem(item);
828 d->list.insert(i: index, t: it);
829 invalidate();
830}
831
832/*!
833 Inserts a non-stretchable space (a QSpacerItem) at position \a index, with
834 size \a size. If \a index is negative the space is added at the end.
835
836 The box layout has default margin and spacing. This function adds
837 additional space.
838
839 \sa addSpacing(), insertItem(), QSpacerItem
840*/
841void QBoxLayout::insertSpacing(int index, int size)
842{
843 Q_D(QBoxLayout);
844 index = d->validateIndex(index);
845 QLayoutItem *b;
846 if (horz(dir: d->dir))
847 b = QLayoutPrivate::createSpacerItem(layout: this, w: size, h: 0, hPolicy: QSizePolicy::Fixed, vPolicy: QSizePolicy::Minimum);
848 else
849 b = QLayoutPrivate::createSpacerItem(layout: this, w: 0, h: size, hPolicy: QSizePolicy::Minimum, vPolicy: QSizePolicy::Fixed);
850
851 QBoxLayoutItem *it = new QBoxLayoutItem(b);
852 it->magic = true;
853 d->list.insert(i: index, t: it);
854 invalidate();
855}
856
857/*!
858 Inserts a stretchable space (a QSpacerItem) at position \a
859 index, with zero minimum size and stretch factor \a stretch. If \a
860 index is negative the space is added at the end.
861
862 \sa addStretch(), insertItem(), QSpacerItem
863*/
864void QBoxLayout::insertStretch(int index, int stretch)
865{
866 Q_D(QBoxLayout);
867 index = d->validateIndex(index);
868 QLayoutItem *b;
869 if (horz(dir: d->dir))
870 b = QLayoutPrivate::createSpacerItem(layout: this, w: 0, h: 0, hPolicy: QSizePolicy::Expanding, vPolicy: QSizePolicy::Minimum);
871 else
872 b = QLayoutPrivate::createSpacerItem(layout: this, w: 0, h: 0, hPolicy: QSizePolicy::Minimum, vPolicy: QSizePolicy::Expanding);
873
874 QBoxLayoutItem *it = new QBoxLayoutItem(b, stretch);
875 it->magic = true;
876 d->list.insert(i: index, t: it);
877 invalidate();
878}
879
880/*!
881 \since 4.4
882
883 Inserts \a spacerItem at position \a index, with zero minimum
884 size and stretch factor. If \a index is negative the
885 space is added at the end.
886
887 \sa addSpacerItem(), insertStretch(), insertSpacing()
888*/
889void QBoxLayout::insertSpacerItem(int index, QSpacerItem *spacerItem)
890{
891 Q_D(QBoxLayout);
892 index = d->validateIndex(index);
893 QBoxLayoutItem *it = new QBoxLayoutItem(spacerItem);
894 it->magic = true;
895 d->list.insert(i: index, t: it);
896 invalidate();
897}
898
899/*!
900 Inserts \a layout at position \a index, with stretch factor \a
901 stretch. If \a index is negative, the layout is added at the end.
902
903 \a layout becomes a child of the box layout.
904
905 \sa addLayout(), insertItem()
906*/
907void QBoxLayout::insertLayout(int index, QLayout *layout, int stretch)
908{
909 Q_D(QBoxLayout);
910 if (!d->checkLayout(otherLayout: layout))
911 return;
912 if (!adoptLayout(layout))
913 return;
914 index = d->validateIndex(index);
915 QBoxLayoutItem *it = new QBoxLayoutItem(layout, stretch);
916 d->list.insert(i: index, t: it);
917 invalidate();
918}
919
920/*!
921 Inserts \a widget at position \a index, with stretch factor \a
922 stretch and alignment \a alignment. If \a index is negative, the
923 widget is added at the end.
924
925 The stretch factor applies only in the \l{direction()}{direction}
926 of the QBoxLayout, and is relative to the other boxes and widgets
927 in this QBoxLayout. Widgets and boxes with higher stretch factors
928 grow more.
929
930 If the stretch factor is 0 and nothing else in the QBoxLayout has
931 a stretch factor greater than zero, the space is distributed
932 according to the QWidget:sizePolicy() of each widget that's
933 involved.
934
935 The alignment is specified by \a alignment. The default alignment
936 is 0, which means that the widget fills the entire cell.
937
938 \sa addWidget(), insertItem()
939*/
940void QBoxLayout::insertWidget(int index, QWidget *widget, int stretch,
941 Qt::Alignment alignment)
942{
943 Q_D(QBoxLayout);
944 if (!d->checkWidget(widget))
945 return;
946 addChildWidget(w: widget);
947 index = d->validateIndex(index);
948 QWidgetItem *b = QLayoutPrivate::createWidgetItem(layout: this, widget);
949 b->setAlignment(alignment);
950
951 QBoxLayoutItem *it = new QBoxLayoutItem(b, stretch);
952 d->list.insert(i: index, t: it);
953 invalidate();
954}
955
956/*!
957 Adds a non-stretchable space (a QSpacerItem) with size \a size
958 to the end of this box layout. QBoxLayout provides default margin
959 and spacing. This function adds additional space.
960
961 \sa insertSpacing(), addItem(), QSpacerItem
962*/
963void QBoxLayout::addSpacing(int size)
964{
965 insertSpacing(index: -1, size);
966}
967
968/*!
969 Adds a stretchable space (a QSpacerItem) with zero minimum
970 size and stretch factor \a stretch to the end of this box layout.
971
972 \sa insertStretch(), addItem(), QSpacerItem
973*/
974void QBoxLayout::addStretch(int stretch)
975{
976 insertStretch(index: -1, stretch);
977}
978
979/*!
980 \since 4.4
981
982 Adds \a spacerItem to the end of this box layout.
983
984 \sa addSpacing(), addStretch()
985*/
986void QBoxLayout::addSpacerItem(QSpacerItem *spacerItem)
987{
988 insertSpacerItem(index: -1, spacerItem);
989}
990
991/*!
992 Adds \a widget to the end of this box layout, with a stretch
993 factor of \a stretch and alignment \a alignment.
994
995 The stretch factor applies only in the \l{direction()}{direction}
996 of the QBoxLayout, and is relative to the other boxes and widgets
997 in this QBoxLayout. Widgets and boxes with higher stretch factors
998 grow more.
999
1000 If the stretch factor is 0 and nothing else in the QBoxLayout has
1001 a stretch factor greater than zero, the space is distributed
1002 according to the QWidget:sizePolicy() of each widget that's
1003 involved.
1004
1005 The alignment is specified by \a alignment. The default
1006 alignment is 0, which means that the widget fills the entire cell.
1007
1008 \sa insertWidget(), addItem(), addLayout(), addStretch(),
1009 addSpacing(), addStrut()
1010*/
1011void QBoxLayout::addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
1012{
1013 insertWidget(index: -1, widget, stretch, alignment);
1014}
1015
1016/*!
1017 Adds \a layout to the end of the box, with serial stretch factor
1018 \a stretch.
1019
1020 \sa insertLayout(), addItem(), addWidget()
1021*/
1022void QBoxLayout::addLayout(QLayout *layout, int stretch)
1023{
1024 insertLayout(index: -1, layout, stretch);
1025}
1026
1027/*!
1028 Limits the perpendicular dimension of the box (e.g. height if the
1029 box is \l LeftToRight) to a minimum of \a size. Other constraints
1030 may increase the limit.
1031
1032 \sa addItem()
1033*/
1034void QBoxLayout::addStrut(int size)
1035{
1036 Q_D(QBoxLayout);
1037 QLayoutItem *b;
1038 if (horz(dir: d->dir))
1039 b = QLayoutPrivate::createSpacerItem(layout: this, w: 0, h: size, hPolicy: QSizePolicy::Fixed, vPolicy: QSizePolicy::Minimum);
1040 else
1041 b = QLayoutPrivate::createSpacerItem(layout: this, w: size, h: 0, hPolicy: QSizePolicy::Minimum, vPolicy: QSizePolicy::Fixed);
1042
1043 QBoxLayoutItem *it = new QBoxLayoutItem(b);
1044 it->magic = true;
1045 d->list.append(t: it);
1046 invalidate();
1047}
1048
1049/*!
1050 Sets the stretch factor for \a widget to \a stretch and returns
1051 true if \a widget is found in this layout (not including child
1052 layouts); otherwise returns \c false.
1053
1054 \sa setAlignment()
1055*/
1056bool QBoxLayout::setStretchFactor(QWidget *widget, int stretch)
1057{
1058 Q_D(QBoxLayout);
1059 if (!widget)
1060 return false;
1061 for (int i = 0; i < d->list.size(); ++i) {
1062 QBoxLayoutItem *box = d->list.at(i);
1063 if (box->item->widget() == widget) {
1064 box->stretch = stretch;
1065 invalidate();
1066 return true;
1067 }
1068 }
1069 return false;
1070}
1071
1072/*!
1073 \overload
1074
1075 Sets the stretch factor for the layout \a layout to \a stretch and
1076 returns \c true if \a layout is found in this layout (not including
1077 child layouts); otherwise returns \c false.
1078*/
1079bool QBoxLayout::setStretchFactor(QLayout *layout, int stretch)
1080{
1081 Q_D(QBoxLayout);
1082 for (int i = 0; i < d->list.size(); ++i) {
1083 QBoxLayoutItem *box = d->list.at(i);
1084 if (box->item->layout() == layout) {
1085 if (box->stretch != stretch) {
1086 box->stretch = stretch;
1087 invalidate();
1088 }
1089 return true;
1090 }
1091 }
1092 return false;
1093}
1094
1095/*!
1096 Sets the stretch factor at position \a index. to \a stretch.
1097
1098 \since 4.5
1099*/
1100
1101void QBoxLayout::setStretch(int index, int stretch)
1102{
1103 Q_D(QBoxLayout);
1104 if (index >= 0 && index < d->list.size()) {
1105 QBoxLayoutItem *box = d->list.at(i: index);
1106 if (box->stretch != stretch) {
1107 box->stretch = stretch;
1108 invalidate();
1109 }
1110 }
1111}
1112
1113/*!
1114 Returns the stretch factor at position \a index.
1115
1116 \since 4.5
1117*/
1118
1119int QBoxLayout::stretch(int index) const
1120{
1121 Q_D(const QBoxLayout);
1122 if (index >= 0 && index < d->list.size())
1123 return d->list.at(i: index)->stretch;
1124 return -1;
1125}
1126
1127/*!
1128 Sets the direction of this layout to \a direction.
1129*/
1130void QBoxLayout::setDirection(Direction direction)
1131{
1132 Q_D(QBoxLayout);
1133 if (d->dir == direction)
1134 return;
1135 if (horz(dir: d->dir) != horz(dir: direction)) {
1136 //swap around the spacers (the "magic" bits)
1137 //#### a bit yucky, knows too much.
1138 //#### probably best to add access functions to spacerItem
1139 //#### or even a QSpacerItem::flip()
1140 for (int i = 0; i < d->list.size(); ++i) {
1141 QBoxLayoutItem *box = d->list.at(i);
1142 if (box->magic) {
1143 QSpacerItem *sp = box->item->spacerItem();
1144 if (sp) {
1145 if (sp->expandingDirections() == Qt::Orientations{} /*No Direction*/) {
1146 //spacing or strut
1147 QSize s = sp->sizeHint();
1148 sp->changeSize(w: s.height(), h: s.width(),
1149 hData: horz(dir: direction) ? QSizePolicy::Fixed:QSizePolicy::Minimum,
1150 vData: horz(dir: direction) ? QSizePolicy::Minimum:QSizePolicy::Fixed);
1151
1152 } else {
1153 //stretch
1154 if (horz(dir: direction))
1155 sp->changeSize(w: 0, h: 0, hData: QSizePolicy::Expanding,
1156 vData: QSizePolicy::Minimum);
1157 else
1158 sp->changeSize(w: 0, h: 0, hData: QSizePolicy::Minimum,
1159 vData: QSizePolicy::Expanding);
1160 }
1161 }
1162 }
1163 }
1164 }
1165 d->dir = direction;
1166 invalidate();
1167}
1168
1169/*!
1170 \fn QBoxLayout::Direction QBoxLayout::direction() const
1171
1172 Returns the direction of the box. addWidget() and addSpacing()
1173 work in this direction; the stretch stretches in this direction.
1174
1175 \sa QBoxLayout::Direction, addWidget(), addSpacing()
1176*/
1177
1178QBoxLayout::Direction QBoxLayout::direction() const
1179{
1180 Q_D(const QBoxLayout);
1181 return d->dir;
1182}
1183
1184/*!
1185 \class QHBoxLayout
1186 \brief The QHBoxLayout class lines up widgets horizontally.
1187
1188 \ingroup geomanagement
1189 \inmodule QtWidgets
1190
1191 This class is used to construct horizontal box layout objects. See
1192 QBoxLayout for details.
1193
1194 The simplest use of the class is like this:
1195
1196 \snippet layouts/layouts.cpp 0
1197 \snippet layouts/layouts.cpp 1
1198 \snippet layouts/layouts.cpp 2
1199 \codeline
1200 \snippet layouts/layouts.cpp 3
1201 \snippet layouts/layouts.cpp 4
1202 \snippet layouts/layouts.cpp 5
1203
1204 First, we create the widgets we want to add to the layout. Then,
1205 we create the QHBoxLayout object, setting \c window as parent by
1206 passing it in the constructor; next we add the widgets to the
1207 layout. \c window will be the parent of the widgets that are
1208 added to the layout.
1209
1210 If you don't pass a parent \c window to the constructor, you can
1211 at a later point use QWidget::setLayout() to install the QHBoxLayout
1212 object onto \c window. At that point, the widgets in the layout are
1213 reparented to have \c window as their parent.
1214
1215 \image qhboxlayout-with-5-children.png Horizontal box layout with five child widgets
1216
1217 \sa QVBoxLayout, QGridLayout, QStackedLayout, {Layout Management}, {Basic Layouts Example}
1218*/
1219
1220
1221/*!
1222 Constructs a new top-level horizontal box with parent \a parent.
1223
1224 The layout is set directly as the top-level layout for \a parent.
1225 There can be only one top-level layout for a widget. It is returned
1226 by QWidget::layout().
1227
1228 \sa QWidget::setLayout()
1229*/
1230QHBoxLayout::QHBoxLayout(QWidget *parent)
1231 : QBoxLayout(LeftToRight, parent)
1232{
1233}
1234
1235/*!
1236 Constructs a new horizontal box. You must add
1237 it to another layout.
1238*/
1239QHBoxLayout::QHBoxLayout()
1240 : QBoxLayout(LeftToRight)
1241{
1242}
1243
1244
1245
1246
1247
1248/*!
1249 Destroys this box layout.
1250
1251 The layout's widgets aren't destroyed.
1252*/
1253QHBoxLayout::~QHBoxLayout()
1254{
1255}
1256
1257/*!
1258 \class QVBoxLayout
1259 \brief The QVBoxLayout class lines up widgets vertically.
1260
1261 \ingroup geomanagement
1262 \inmodule QtWidgets
1263
1264 This class is used to construct vertical box layout objects. See
1265 QBoxLayout for details.
1266
1267 The simplest use of the class is like this:
1268
1269 \snippet layouts/layouts.cpp 6
1270 \snippet layouts/layouts.cpp 7
1271 \snippet layouts/layouts.cpp 8
1272 \codeline
1273 \snippet layouts/layouts.cpp 9
1274 \snippet layouts/layouts.cpp 10
1275 \snippet layouts/layouts.cpp 11
1276
1277 First, we create the widgets we want to add to the layout. Then,
1278 we create the QVBoxLayout object, setting \c window as parent by
1279 passing it in the constructor; next we add the widgets to the
1280 layout. \c window will be the parent of the widgets that are
1281 added to the layout.
1282
1283 If you don't pass a parent \c window to the constructor, you can
1284 at a later point use QWidget::setLayout() to install the QVBoxLayout
1285 object onto \c window. At that point, the widgets in the layout are
1286 reparented to have \c window as their parent.
1287
1288 \image qvboxlayout-with-5-children.png Horizontal box layout with five child widgets
1289
1290 \sa QHBoxLayout, QGridLayout, QStackedLayout, {Layout Management}, {Basic Layouts Example}
1291*/
1292
1293/*!
1294 Constructs a new top-level vertical box with parent \a parent.
1295
1296 The layout is set directly as the top-level layout for \a parent.
1297 There can be only one top-level layout for a widget. It is returned
1298 by QWidget::layout().
1299
1300 \sa QWidget::setLayout()
1301*/
1302QVBoxLayout::QVBoxLayout(QWidget *parent)
1303 : QBoxLayout(TopToBottom, parent)
1304{
1305}
1306
1307/*!
1308 Constructs a new vertical box. You must add
1309 it to another layout.
1310
1311*/
1312QVBoxLayout::QVBoxLayout()
1313 : QBoxLayout(TopToBottom)
1314{
1315}
1316
1317
1318/*!
1319 Destroys this box layout.
1320
1321 The layout's widgets aren't destroyed.
1322*/
1323QVBoxLayout::~QVBoxLayout()
1324{
1325}
1326
1327QT_END_NAMESPACE
1328
1329#include "moc_qboxlayout.cpp"
1330

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