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 Q_ASSERT_X(index >= 0 && index <= list.size(), "QBoxLayout::insert", "index out of range");
417 return index;
418}
419
420/*!
421 \class QBoxLayout
422
423 \brief The QBoxLayout class lines up child widgets horizontally or
424 vertically.
425
426 \ingroup geomanagement
427 \inmodule QtWidgets
428
429 QBoxLayout takes the space it gets (from its parent layout or from
430 the parentWidget()), divides it up into a row of boxes, and makes
431 each managed widget fill one box.
432
433 \image qhboxlayout-with-5-children.png Horizontal box layout with five child widgets
434
435 If the QBoxLayout's orientation is Qt::Horizontal the boxes are
436 placed in a row, with suitable sizes. Each widget (or other box)
437 will get at least its minimum size and at most its maximum size.
438 Any excess space is shared according to the stretch factors (more
439 about that below).
440
441 \image qvboxlayout-with-5-children.png Vertical box layout with five child widgets
442
443 If the QBoxLayout's orientation is Qt::Vertical, the boxes are
444 placed in a column, again with suitable sizes.
445
446 The easiest way to create a QBoxLayout is to use one of the
447 convenience classes, e.g. QHBoxLayout (for Qt::Horizontal boxes)
448 or QVBoxLayout (for Qt::Vertical boxes). You can also use the
449 QBoxLayout constructor directly, specifying its direction as
450 LeftToRight, RightToLeft, TopToBottom, or BottomToTop.
451
452 If the QBoxLayout is not the top-level layout (i.e. it is not
453 managing all of the widget's area and children), you must add it
454 to its parent layout before you can do anything with it. The
455 normal way to add a layout is by calling
456 parentLayout-\>addLayout().
457
458 Once you have done this, you can add boxes to the QBoxLayout using
459 one of four functions:
460
461 \list
462 \li addWidget() to add a widget to the QBoxLayout and set the
463 widget's stretch factor. (The stretch factor is along the row of
464 boxes.)
465
466 \li addSpacing() to create an empty box; this is one of the
467 functions you use to create nice and spacious dialogs. See below
468 for ways to set margins.
469
470 \li addStretch() to create an empty, stretchable box.
471
472 \li addLayout() to add a box containing another QLayout to the row
473 and set that layout's stretch factor.
474 \endlist
475
476 Use insertWidget(), insertSpacing(), insertStretch() or
477 insertLayout() to insert a box at a specified position in the
478 layout.
479
480 QBoxLayout also includes two margin widths:
481
482 \list
483 \li setContentsMargins() sets the width of the outer border on
484 each side of the widget. This is the width of the reserved space
485 along each of the QBoxLayout's four sides.
486 \li setSpacing() sets the width between neighboring boxes. (You
487 can use addSpacing() to get more space at a particular spot.)
488 \endlist
489
490 The margin default is provided by the style. The default margin
491 most Qt styles specify is 9 for child widgets and 11 for windows.
492 The spacing defaults to the same as the margin width for a
493 top-level layout, or to the same as the parent layout.
494
495 To remove a widget from a layout, call removeWidget(). Calling
496 QWidget::hide() on a widget also effectively removes the widget
497 from the layout until QWidget::show() is called.
498
499 You will almost always want to use QVBoxLayout and QHBoxLayout
500 rather than QBoxLayout because of their convenient constructors.
501
502 \sa QGridLayout, QStackedLayout, {Layout Management}
503*/
504
505/*!
506 \enum QBoxLayout::Direction
507
508 This type is used to determine the direction of a box layout.
509
510 \value LeftToRight Horizontal from left to right.
511 \value RightToLeft Horizontal from right to left.
512 \value TopToBottom Vertical from top to bottom.
513 \value BottomToTop Vertical from bottom to top.
514
515 \omitvalue Down
516 \omitvalue Up
517*/
518
519/*!
520 Constructs a new QBoxLayout with direction \a dir and parent widget \a
521 parent.
522
523 The layout is set directly as the top-level layout for \a parent.
524 There can be only one top-level layout for a widget. It is returned
525 by QWidget::layout().
526
527 \sa direction(), QWidget::setLayout()
528*/
529QBoxLayout::QBoxLayout(Direction dir, QWidget *parent)
530 : QLayout(*new QBoxLayoutPrivate, nullptr, parent)
531{
532 Q_D(QBoxLayout);
533 d->dir = dir;
534}
535
536
537
538/*!
539 Destroys this box layout.
540
541 The layout's widgets aren't destroyed.
542*/
543QBoxLayout::~QBoxLayout()
544{
545 Q_D(QBoxLayout);
546 d->deleteAll(); // must do it before QObject deletes children, so can't be in ~QBoxLayoutPrivate
547}
548
549/*!
550 Reimplements QLayout::spacing(). If the spacing property is
551 valid, that value is returned. Otherwise, a value for the spacing
552 property is computed and returned. Since layout spacing in a widget
553 is style dependent, if the parent is a widget, it queries the style
554 for the (horizontal or vertical) spacing of the layout. Otherwise,
555 the parent is a layout, and it queries the parent layout for the
556 spacing().
557
558 \sa QLayout::spacing(), setSpacing()
559 */
560int QBoxLayout::spacing() const
561{
562 Q_D(const QBoxLayout);
563 if (d->spacing >=0) {
564 return d->spacing;
565 } else {
566 return qSmartSpacing(layout: this, pm: d->dir == LeftToRight || d->dir == RightToLeft
567 ? QStyle::PM_LayoutHorizontalSpacing
568 : QStyle::PM_LayoutVerticalSpacing);
569 }
570}
571
572/*!
573 Reimplements QLayout::setSpacing(). Sets the spacing
574 property to \a spacing.
575
576 \sa QLayout::setSpacing(), spacing()
577 */
578void QBoxLayout::setSpacing(int spacing)
579{
580 Q_D(QBoxLayout);
581 d->spacing = spacing;
582 invalidate();
583}
584
585/*!
586 \reimp
587*/
588QSize QBoxLayout::sizeHint() const
589{
590 Q_D(const QBoxLayout);
591 if (d->dirty)
592 const_cast<QBoxLayout*>(this)->d_func()->setupGeom();
593 return d->sizeHint;
594}
595
596/*!
597 \reimp
598*/
599QSize QBoxLayout::minimumSize() const
600{
601 Q_D(const QBoxLayout);
602 if (d->dirty)
603 const_cast<QBoxLayout*>(this)->d_func()->setupGeom();
604 return d->minSize;
605}
606
607/*!
608 \reimp
609*/
610QSize QBoxLayout::maximumSize() const
611{
612 Q_D(const QBoxLayout);
613 if (d->dirty)
614 const_cast<QBoxLayout*>(this)->d_func()->setupGeom();
615
616 QSize s = d->maxSize.boundedTo(otherSize: QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX));
617
618 if (alignment() & Qt::AlignHorizontal_Mask)
619 s.setWidth(QLAYOUTSIZE_MAX);
620 if (alignment() & Qt::AlignVertical_Mask)
621 s.setHeight(QLAYOUTSIZE_MAX);
622 return s;
623}
624
625/*!
626 \reimp
627*/
628bool QBoxLayout::hasHeightForWidth() const
629{
630 Q_D(const QBoxLayout);
631 if (d->dirty)
632 const_cast<QBoxLayout*>(this)->d_func()->setupGeom();
633 return d->hasHfw;
634}
635
636/*!
637 \reimp
638*/
639int QBoxLayout::heightForWidth(int w) const
640{
641 Q_D(const QBoxLayout);
642 if (!hasHeightForWidth())
643 return -1;
644
645 int left, top, right, bottom;
646 d->effectiveMargins(left: &left, top: &top, right: &right, bottom: &bottom);
647
648 w -= left + right;
649 if (w != d->hfwWidth)
650 const_cast<QBoxLayout*>(this)->d_func()->calcHfw(w);
651
652 return d->hfwHeight + top + bottom;
653}
654
655/*!
656 \reimp
657*/
658int QBoxLayout::minimumHeightForWidth(int w) const
659{
660 Q_D(const QBoxLayout);
661 (void) heightForWidth(w);
662 int top, bottom;
663 d->effectiveMargins(left: nullptr, top: &top, right: nullptr, bottom: &bottom);
664 return d->hasHfw ? (d->hfwMinHeight + top + bottom) : -1;
665}
666
667/*!
668 Resets cached information.
669*/
670void QBoxLayout::invalidate()
671{
672 Q_D(QBoxLayout);
673 d->setDirty();
674 QLayout::invalidate();
675}
676
677/*!
678 \reimp
679*/
680int QBoxLayout::count() const
681{
682 Q_D(const QBoxLayout);
683 return d->list.size();
684}
685
686/*!
687 \reimp
688*/
689QLayoutItem *QBoxLayout::itemAt(int index) const
690{
691 Q_D(const QBoxLayout);
692 return index >= 0 && index < d->list.size() ? d->list.at(i: index)->item : nullptr;
693}
694
695/*!
696 \reimp
697*/
698QLayoutItem *QBoxLayout::takeAt(int index)
699{
700 Q_D(QBoxLayout);
701 if (index < 0 || index >= d->list.size())
702 return nullptr;
703 QBoxLayoutItem *b = d->list.takeAt(i: index);
704 QLayoutItem *item = b->item;
705 b->item = nullptr;
706 delete b;
707
708 if (QLayout *l = item->layout()) {
709 // sanity check in case the user passed something weird to QObject::setParent()
710 if (l->parent() == this)
711 l->setParent(nullptr);
712 }
713
714 invalidate();
715 return item;
716}
717
718
719/*!
720 \reimp
721*/
722Qt::Orientations QBoxLayout::expandingDirections() const
723{
724 Q_D(const QBoxLayout);
725 if (d->dirty)
726 const_cast<QBoxLayout*>(this)->d_func()->setupGeom();
727 return d->expanding;
728}
729
730/*!
731 \reimp
732*/
733void QBoxLayout::setGeometry(const QRect &r)
734{
735 Q_D(QBoxLayout);
736 if (d->dirty || r != geometry()) {
737 QRect oldRect = geometry();
738 QLayout::setGeometry(r);
739 if (d->dirty)
740 d->setupGeom();
741 QRect cr = alignment() ? alignmentRect(r) : r;
742
743 int left, top, right, bottom;
744 d->effectiveMargins(left: &left, top: &top, right: &right, bottom: &bottom);
745 QRect s(cr.x() + left, cr.y() + top,
746 cr.width() - (left + right),
747 cr.height() - (top + bottom));
748
749 QList<QLayoutStruct> a = d->geomArray;
750 int pos = horz(dir: d->dir) ? s.x() : s.y();
751 int space = horz(dir: d->dir) ? s.width() : s.height();
752 int n = a.size();
753 if (d->hasHfw && !horz(dir: d->dir)) {
754 for (int i = 0; i < n; i++) {
755 QBoxLayoutItem *box = d->list.at(i);
756 if (box->item->hasHeightForWidth()) {
757 int width = qBound(min: box->item->minimumSize().width(), val: s.width(), max: box->item->maximumSize().width());
758 a[i].sizeHint = a[i].minimumSize =
759 box->item->heightForWidth(width);
760 }
761 }
762 }
763
764 Direction visualDir = d->dir;
765 QWidget *parent = parentWidget();
766 if (parent && parent->isRightToLeft()) {
767 if (d->dir == LeftToRight)
768 visualDir = RightToLeft;
769 else if (d->dir == RightToLeft)
770 visualDir = LeftToRight;
771 }
772
773 qGeomCalc(chain&: a, start: 0, count: n, pos, space);
774
775 bool reverse = (horz(dir: visualDir)
776 ? ((r.right() > oldRect.right()) != (visualDir == RightToLeft))
777 : r.bottom() > oldRect.bottom());
778 for (int j = 0; j < n; j++) {
779 int i = reverse ? n-j-1 : j;
780 QBoxLayoutItem *box = d->list.at(i);
781
782 switch (visualDir) {
783 case LeftToRight:
784 box->item->setGeometry(QRect(a.at(i).pos, s.y(), a.at(i).size, s.height()));
785 break;
786 case RightToLeft:
787 box->item->setGeometry(QRect(s.left() + s.right() - a.at(i).pos - a.at(i).size + 1,
788 s.y(), a.at(i).size, s.height()));
789 break;
790 case TopToBottom:
791 box->item->setGeometry(QRect(s.x(), a.at(i).pos, s.width(), a.at(i).size));
792 break;
793 case BottomToTop:
794 box->item->setGeometry(QRect(s.x(),
795 s.top() + s.bottom() - a.at(i).pos - a.at(i).size + 1,
796 s.width(), a.at(i).size));
797 }
798 }
799 }
800}
801
802/*!
803 \reimp
804*/
805void QBoxLayout::addItem(QLayoutItem *item)
806{
807 Q_D(QBoxLayout);
808 QBoxLayoutItem *it = new QBoxLayoutItem(item);
809 d->list.append(t: it);
810 invalidate();
811}
812
813/*!
814 Inserts \a item into this box layout at position \a index. If \a
815 index is negative, the item is added at the end.
816
817 \sa addItem(), insertWidget(), insertLayout(), insertStretch(),
818 insertSpacing()
819*/
820void QBoxLayout::insertItem(int index, QLayoutItem *item)
821{
822 Q_D(QBoxLayout);
823 index = d->validateIndex(index);
824 QBoxLayoutItem *it = new QBoxLayoutItem(item);
825 d->list.insert(i: index, t: it);
826 invalidate();
827}
828
829/*!
830 Inserts a non-stretchable space (a QSpacerItem) at position \a index, with
831 size \a size. If \a index is negative the space is added at the end.
832
833 The box layout has default margin and spacing. This function adds
834 additional space.
835
836 \sa addSpacing(), insertItem(), QSpacerItem
837*/
838void QBoxLayout::insertSpacing(int index, int size)
839{
840 Q_D(QBoxLayout);
841 index = d->validateIndex(index);
842 QLayoutItem *b;
843 if (horz(dir: d->dir))
844 b = QLayoutPrivate::createSpacerItem(layout: this, w: size, h: 0, hPolicy: QSizePolicy::Fixed, vPolicy: QSizePolicy::Minimum);
845 else
846 b = QLayoutPrivate::createSpacerItem(layout: this, w: 0, h: size, hPolicy: QSizePolicy::Minimum, vPolicy: QSizePolicy::Fixed);
847
848 QBoxLayoutItem *it = new QBoxLayoutItem(b);
849 it->magic = true;
850 d->list.insert(i: index, t: it);
851 invalidate();
852}
853
854/*!
855 Inserts a stretchable space (a QSpacerItem) at position \a
856 index, with zero minimum size and stretch factor \a stretch. If \a
857 index is negative the space is added at the end.
858
859 \sa addStretch(), insertItem(), QSpacerItem
860*/
861void QBoxLayout::insertStretch(int index, int stretch)
862{
863 Q_D(QBoxLayout);
864 index = d->validateIndex(index);
865 QLayoutItem *b;
866 if (horz(dir: d->dir))
867 b = QLayoutPrivate::createSpacerItem(layout: this, w: 0, h: 0, hPolicy: QSizePolicy::Expanding, vPolicy: QSizePolicy::Minimum);
868 else
869 b = QLayoutPrivate::createSpacerItem(layout: this, w: 0, h: 0, hPolicy: QSizePolicy::Minimum, vPolicy: QSizePolicy::Expanding);
870
871 QBoxLayoutItem *it = new QBoxLayoutItem(b, stretch);
872 it->magic = true;
873 d->list.insert(i: index, t: it);
874 invalidate();
875}
876
877/*!
878 \since 4.4
879
880 Inserts \a spacerItem at position \a index, with zero minimum
881 size and stretch factor. If \a index is negative the
882 space is added at the end.
883
884 \sa addSpacerItem(), insertStretch(), insertSpacing()
885*/
886void QBoxLayout::insertSpacerItem(int index, QSpacerItem *spacerItem)
887{
888 Q_D(QBoxLayout);
889 index = d->validateIndex(index);
890 QBoxLayoutItem *it = new QBoxLayoutItem(spacerItem);
891 it->magic = true;
892 d->list.insert(i: index, t: it);
893 invalidate();
894}
895
896/*!
897 Inserts \a layout at position \a index, with stretch factor \a
898 stretch. If \a index is negative, the layout is added at the end.
899
900 \a layout becomes a child of the box layout.
901
902 \sa addLayout(), insertItem()
903*/
904void QBoxLayout::insertLayout(int index, QLayout *layout, int stretch)
905{
906 Q_D(QBoxLayout);
907 if (!d->checkLayout(otherLayout: layout))
908 return;
909 if (!adoptLayout(layout))
910 return;
911 index = d->validateIndex(index);
912 QBoxLayoutItem *it = new QBoxLayoutItem(layout, stretch);
913 d->list.insert(i: index, t: it);
914 invalidate();
915}
916
917/*!
918 Inserts \a widget at position \a index, with stretch factor \a
919 stretch and alignment \a alignment. If \a index is negative, the
920 widget is added at the end.
921
922 The stretch factor applies only in the \l{direction()}{direction}
923 of the QBoxLayout, and is relative to the other boxes and widgets
924 in this QBoxLayout. Widgets and boxes with higher stretch factors
925 grow more.
926
927 If the stretch factor is 0 and nothing else in the QBoxLayout has
928 a stretch factor greater than zero, the space is distributed
929 according to the QWidget:sizePolicy() of each widget that's
930 involved.
931
932 The alignment is specified by \a alignment. The default alignment
933 is 0, which means that the widget fills the entire cell.
934
935 \sa addWidget(), insertItem()
936*/
937void QBoxLayout::insertWidget(int index, QWidget *widget, int stretch,
938 Qt::Alignment alignment)
939{
940 Q_D(QBoxLayout);
941 if (!d->checkWidget(widget))
942 return;
943 addChildWidget(w: widget);
944 index = d->validateIndex(index);
945 QWidgetItem *b = QLayoutPrivate::createWidgetItem(layout: this, widget);
946 b->setAlignment(alignment);
947
948 QBoxLayoutItem *it = new QBoxLayoutItem(b, stretch);
949 d->list.insert(i: index, t: it);
950 invalidate();
951}
952
953/*!
954 Adds a non-stretchable space (a QSpacerItem) with size \a size
955 to the end of this box layout. QBoxLayout provides default margin
956 and spacing. This function adds additional space.
957
958 \sa insertSpacing(), addItem(), QSpacerItem
959*/
960void QBoxLayout::addSpacing(int size)
961{
962 insertSpacing(index: -1, size);
963}
964
965/*!
966 Adds a stretchable space (a QSpacerItem) with zero minimum
967 size and stretch factor \a stretch to the end of this box layout.
968
969 \sa insertStretch(), addItem(), QSpacerItem
970*/
971void QBoxLayout::addStretch(int stretch)
972{
973 insertStretch(index: -1, stretch);
974}
975
976/*!
977 \since 4.4
978
979 Adds \a spacerItem to the end of this box layout.
980
981 \sa addSpacing(), addStretch()
982*/
983void QBoxLayout::addSpacerItem(QSpacerItem *spacerItem)
984{
985 insertSpacerItem(index: -1, spacerItem);
986}
987
988/*!
989 Adds \a widget to the end of this box layout, with a stretch
990 factor of \a stretch and alignment \a alignment.
991
992 The stretch factor applies only in the \l{direction()}{direction}
993 of the QBoxLayout, and is relative to the other boxes and widgets
994 in this QBoxLayout. Widgets and boxes with higher stretch factors
995 grow more.
996
997 If the stretch factor is 0 and nothing else in the QBoxLayout has
998 a stretch factor greater than zero, the space is distributed
999 according to the QWidget:sizePolicy() of each widget that's
1000 involved.
1001
1002 The alignment is specified by \a alignment. The default
1003 alignment is 0, which means that the widget fills the entire cell.
1004
1005 \sa insertWidget(), addItem(), addLayout(), addStretch(),
1006 addSpacing(), addStrut()
1007*/
1008void QBoxLayout::addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
1009{
1010 insertWidget(index: -1, widget, stretch, alignment);
1011}
1012
1013/*!
1014 Adds \a layout to the end of the box, with serial stretch factor
1015 \a stretch.
1016
1017 \sa insertLayout(), addItem(), addWidget()
1018*/
1019void QBoxLayout::addLayout(QLayout *layout, int stretch)
1020{
1021 insertLayout(index: -1, layout, stretch);
1022}
1023
1024/*!
1025 Limits the perpendicular dimension of the box (e.g. height if the
1026 box is \l LeftToRight) to a minimum of \a size. Other constraints
1027 may increase the limit.
1028
1029 \sa addItem()
1030*/
1031void QBoxLayout::addStrut(int size)
1032{
1033 Q_D(QBoxLayout);
1034 QLayoutItem *b;
1035 if (horz(dir: d->dir))
1036 b = QLayoutPrivate::createSpacerItem(layout: this, w: 0, h: size, hPolicy: QSizePolicy::Fixed, vPolicy: QSizePolicy::Minimum);
1037 else
1038 b = QLayoutPrivate::createSpacerItem(layout: this, w: size, h: 0, hPolicy: QSizePolicy::Minimum, vPolicy: QSizePolicy::Fixed);
1039
1040 QBoxLayoutItem *it = new QBoxLayoutItem(b);
1041 it->magic = true;
1042 d->list.append(t: it);
1043 invalidate();
1044}
1045
1046/*!
1047 Sets the stretch factor for \a widget to \a stretch and returns
1048 true if \a widget is found in this layout (not including child
1049 layouts); otherwise returns \c false.
1050
1051 \sa setAlignment()
1052*/
1053bool QBoxLayout::setStretchFactor(QWidget *widget, int stretch)
1054{
1055 Q_D(QBoxLayout);
1056 if (!widget)
1057 return false;
1058 for (int i = 0; i < d->list.size(); ++i) {
1059 QBoxLayoutItem *box = d->list.at(i);
1060 if (box->item->widget() == widget) {
1061 box->stretch = stretch;
1062 invalidate();
1063 return true;
1064 }
1065 }
1066 return false;
1067}
1068
1069/*!
1070 \overload
1071
1072 Sets the stretch factor for the layout \a layout to \a stretch and
1073 returns \c true if \a layout is found in this layout (not including
1074 child layouts); otherwise returns \c false.
1075*/
1076bool QBoxLayout::setStretchFactor(QLayout *layout, int stretch)
1077{
1078 Q_D(QBoxLayout);
1079 for (int i = 0; i < d->list.size(); ++i) {
1080 QBoxLayoutItem *box = d->list.at(i);
1081 if (box->item->layout() == layout) {
1082 if (box->stretch != stretch) {
1083 box->stretch = stretch;
1084 invalidate();
1085 }
1086 return true;
1087 }
1088 }
1089 return false;
1090}
1091
1092/*!
1093 Sets the stretch factor at position \a index. to \a stretch.
1094
1095 \since 4.5
1096*/
1097
1098void QBoxLayout::setStretch(int index, int stretch)
1099{
1100 Q_D(QBoxLayout);
1101 if (index >= 0 && index < d->list.size()) {
1102 QBoxLayoutItem *box = d->list.at(i: index);
1103 if (box->stretch != stretch) {
1104 box->stretch = stretch;
1105 invalidate();
1106 }
1107 }
1108}
1109
1110/*!
1111 Returns the stretch factor at position \a index.
1112
1113 \since 4.5
1114*/
1115
1116int QBoxLayout::stretch(int index) const
1117{
1118 Q_D(const QBoxLayout);
1119 if (index >= 0 && index < d->list.size())
1120 return d->list.at(i: index)->stretch;
1121 return -1;
1122}
1123
1124/*!
1125 Sets the direction of this layout to \a direction.
1126*/
1127void QBoxLayout::setDirection(Direction direction)
1128{
1129 Q_D(QBoxLayout);
1130 if (d->dir == direction)
1131 return;
1132 if (horz(dir: d->dir) != horz(dir: direction)) {
1133 //swap around the spacers (the "magic" bits)
1134 //#### a bit yucky, knows too much.
1135 //#### probably best to add access functions to spacerItem
1136 //#### or even a QSpacerItem::flip()
1137 for (int i = 0; i < d->list.size(); ++i) {
1138 QBoxLayoutItem *box = d->list.at(i);
1139 if (box->magic) {
1140 QSpacerItem *sp = box->item->spacerItem();
1141 if (sp) {
1142 if (sp->expandingDirections() == Qt::Orientations{} /*No Direction*/) {
1143 //spacing or strut
1144 QSize s = sp->sizeHint();
1145 sp->changeSize(w: s.height(), h: s.width(),
1146 hData: horz(dir: direction) ? QSizePolicy::Fixed:QSizePolicy::Minimum,
1147 vData: horz(dir: direction) ? QSizePolicy::Minimum:QSizePolicy::Fixed);
1148
1149 } else {
1150 //stretch
1151 if (horz(dir: direction))
1152 sp->changeSize(w: 0, h: 0, hData: QSizePolicy::Expanding,
1153 vData: QSizePolicy::Minimum);
1154 else
1155 sp->changeSize(w: 0, h: 0, hData: QSizePolicy::Minimum,
1156 vData: QSizePolicy::Expanding);
1157 }
1158 }
1159 }
1160 }
1161 }
1162 d->dir = direction;
1163 invalidate();
1164}
1165
1166/*!
1167 \fn QBoxLayout::Direction QBoxLayout::direction() const
1168
1169 Returns the direction of the box. addWidget() and addSpacing()
1170 work in this direction; the stretch stretches in this direction.
1171
1172 \sa QBoxLayout::Direction, addWidget(), addSpacing()
1173*/
1174
1175QBoxLayout::Direction QBoxLayout::direction() const
1176{
1177 Q_D(const QBoxLayout);
1178 return d->dir;
1179}
1180
1181/*!
1182 \class QHBoxLayout
1183 \brief The QHBoxLayout class lines up widgets horizontally.
1184
1185 \ingroup geomanagement
1186 \inmodule QtWidgets
1187
1188 This class is used to construct horizontal box layout objects. See
1189 QBoxLayout for details.
1190
1191 The simplest use of the class is like this:
1192
1193 \snippet layouts/layouts.cpp 0
1194 \snippet layouts/layouts.cpp 1
1195 \snippet layouts/layouts.cpp 2
1196 \codeline
1197 \snippet layouts/layouts.cpp 3
1198 \snippet layouts/layouts.cpp 4
1199 \snippet layouts/layouts.cpp 5
1200
1201 First, we create the widgets we want to add to the layout. Then,
1202 we create the QHBoxLayout object, setting \c window as parent by
1203 passing it in the constructor; next we add the widgets to the
1204 layout. \c window will be the parent of the widgets that are
1205 added to the layout.
1206
1207 If you don't pass a parent \c window to the constructor, you can
1208 at a later point use QWidget::setLayout() to install the QHBoxLayout
1209 object onto \c window. At that point, the widgets in the layout are
1210 reparented to have \c window as their parent.
1211
1212 \image qhboxlayout-with-5-children.png Horizontal box layout with five child widgets
1213
1214 \sa QVBoxLayout, QGridLayout, QStackedLayout, {Layout Management}, {Basic Layouts Example}
1215*/
1216
1217
1218/*!
1219 Constructs a new top-level horizontal box with parent \a parent.
1220
1221 The layout is set directly as the top-level layout for \a parent.
1222 There can be only one top-level layout for a widget. It is returned
1223 by QWidget::layout().
1224
1225 \sa QWidget::setLayout()
1226*/
1227QHBoxLayout::QHBoxLayout(QWidget *parent)
1228 : QBoxLayout(LeftToRight, parent)
1229{
1230}
1231
1232/*!
1233 Constructs a new horizontal box. You must add
1234 it to another layout.
1235*/
1236QHBoxLayout::QHBoxLayout()
1237 : QBoxLayout(LeftToRight)
1238{
1239}
1240
1241
1242
1243
1244
1245/*!
1246 Destroys this box layout.
1247
1248 The layout's widgets aren't destroyed.
1249*/
1250QHBoxLayout::~QHBoxLayout()
1251{
1252}
1253
1254/*!
1255 \class QVBoxLayout
1256 \brief The QVBoxLayout class lines up widgets vertically.
1257
1258 \ingroup geomanagement
1259 \inmodule QtWidgets
1260
1261 This class is used to construct vertical box layout objects. See
1262 QBoxLayout for details.
1263
1264 The simplest use of the class is like this:
1265
1266 \snippet layouts/layouts.cpp 6
1267 \snippet layouts/layouts.cpp 7
1268 \snippet layouts/layouts.cpp 8
1269 \codeline
1270 \snippet layouts/layouts.cpp 9
1271 \snippet layouts/layouts.cpp 10
1272 \snippet layouts/layouts.cpp 11
1273
1274 First, we create the widgets we want to add to the layout. Then,
1275 we create the QVBoxLayout object, setting \c window as parent by
1276 passing it in the constructor; next we add the widgets to the
1277 layout. \c window will be the parent of the widgets that are
1278 added to the layout.
1279
1280 If you don't pass a parent \c window to the constructor, you can
1281 at a later point use QWidget::setLayout() to install the QVBoxLayout
1282 object onto \c window. At that point, the widgets in the layout are
1283 reparented to have \c window as their parent.
1284
1285 \image qvboxlayout-with-5-children.png Horizontal box layout with five child widgets
1286
1287 \sa QHBoxLayout, QGridLayout, QStackedLayout, {Layout Management}, {Basic Layouts Example}
1288*/
1289
1290/*!
1291 Constructs a new top-level vertical box with parent \a parent.
1292
1293 The layout is set directly as the top-level layout for \a parent.
1294 There can be only one top-level layout for a widget. It is returned
1295 by QWidget::layout().
1296
1297 \sa QWidget::setLayout()
1298*/
1299QVBoxLayout::QVBoxLayout(QWidget *parent)
1300 : QBoxLayout(TopToBottom, parent)
1301{
1302}
1303
1304/*!
1305 Constructs a new vertical box. You must add
1306 it to another layout.
1307
1308*/
1309QVBoxLayout::QVBoxLayout()
1310 : QBoxLayout(TopToBottom)
1311{
1312}
1313
1314
1315/*!
1316 Destroys this box layout.
1317
1318 The layout's widgets aren't destroyed.
1319*/
1320QVBoxLayout::~QVBoxLayout()
1321{
1322}
1323
1324QT_END_NAMESPACE
1325
1326#include "moc_qboxlayout.cpp"
1327

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