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

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