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 
50 QT_BEGIN_NAMESPACE
51 
52 struct QBoxLayoutItem
53 {
QBoxLayoutItemQBoxLayoutItem54     QBoxLayoutItem(QLayoutItem *it, int stretch_ = 0)
55         : item(it), stretch(stretch_), magic(false) { }
~QBoxLayoutItemQBoxLayoutItem56     ~QBoxLayoutItem() { delete item; }
57 
hfwQBoxLayoutItem58     int hfw(int w) {
59         if (item->hasHeightForWidth()) {
60             return item->heightForWidth(w);
61         } else {
62             return item->sizeHint().height();
63         }
64     }
mhfwQBoxLayoutItem65     int mhfw(int w) {
66         if (item->hasHeightForWidth()) {
67             return item->heightForWidth(w);
68         } else {
69             return item->minimumSize().height();
70         }
71     }
hStretchQBoxLayoutItem72     int hStretch() {
73         if (stretch == 0 && item->widget()) {
74             return item->widget()->sizePolicy().horizontalStretch();
75         } else {
76             return stretch;
77         }
78     }
vStretchQBoxLayoutItem79     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 
92 class QBoxLayoutPrivate : public QLayoutPrivate
93 {
94     Q_DECLARE_PUBLIC(QBoxLayout)
95 public:
QBoxLayoutPrivate()96     QBoxLayoutPrivate() : hfwWidth(-1), dirty(true), spacing(-1) { }
97     ~QBoxLayoutPrivate();
98 
setDirty()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 
deleteAll()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 
~QBoxLayoutPrivate()130 QBoxLayoutPrivate::~QBoxLayoutPrivate()
131 {
132 }
133 
horz(QBoxLayout::Direction dir)134 static 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  */
effectiveMargins(int * left,int * top,int * right,int * bottom) const144 void 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 */
setupGeom()254 void 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(actual1, actual2);
317 
318                     if (style) {
319                         spacing = style->combinedLayoutSpacing(actual1, actual2,
320                                              horz(dir) ? Qt::Horizontal : Qt::Vertical,
321                                              nullptr, 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(maxh, verexp, dummy,
343                             max.height(), exp & Qt::Vertical, box->item->isEmpty());
344             minh = qMax(minh, min.height());
345             hinth = qMax(hinth, 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(maxw, horexp, dummy,
360                             max.width(), exp & Qt::Horizontal, box->item->isEmpty());
361             minw = qMax(minw, min.width());
362             hintw = qMax(hintw, 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(minSize);
384     sizeHint = QSize(hintw, hinth).expandedTo(minSize).boundedTo(maxSize);
385 
386     q->getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
387     int left, top, right, bottom;
388     effectiveMargins(&left, &top, &right, &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 */
calcHfw(int w)401 void 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(a, 0, n, 0, w);
412         for (int i = 0; i < n; i++) {
413             QBoxLayoutItem *box = list.at(i);
414             h = qMax(h, box->hfw(a.at(i).size));
415             mh = qMax(mh, box->mhfw(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 
replaceAt(int index,QLayoutItem * item)432 QLayoutItem* QBoxLayoutPrivate::replaceAt(int index, QLayoutItem *item)
433 {
434     Q_Q(QBoxLayout);
435     if (!item)
436         return nullptr;
437     QBoxLayoutItem *b = list.value(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 */
QBoxLayout(Direction dir,QWidget * parent)557 QBoxLayout::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 */
~QBoxLayout()571 QBoxLayout::~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  */
spacing() const588 int QBoxLayout::spacing() const
589 {
590     Q_D(const QBoxLayout);
591     if (d->spacing >=0) {
592         return d->spacing;
593     } else {
594         return qSmartSpacing(this, 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  */
setSpacing(int spacing)606 void QBoxLayout::setSpacing(int spacing)
607 {
608     Q_D(QBoxLayout);
609     d->spacing = spacing;
610     invalidate();
611 }
612 
613 /*!
614     \reimp
615 */
sizeHint() const616 QSize 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 */
minimumSize() const627 QSize 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 */
maximumSize() const638 QSize 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(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 */
hasHeightForWidth() const656 bool 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 */
heightForWidth(int w) const667 int 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, &top, &right, &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 */
minimumHeightForWidth(int w) const686 int QBoxLayout::minimumHeightForWidth(int w) const
687 {
688     Q_D(const QBoxLayout);
689     (void) heightForWidth(w);
690     int top, bottom;
691     d->effectiveMargins(nullptr, &top, nullptr, &bottom);
692     return d->hasHfw ? (d->hfwMinHeight + top + bottom) : -1;
693 }
694 
695 /*!
696     Resets cached information.
697 */
invalidate()698 void QBoxLayout::invalidate()
699 {
700     Q_D(QBoxLayout);
701     d->setDirty();
702     QLayout::invalidate();
703 }
704 
705 /*!
706     \reimp
707 */
count() const708 int QBoxLayout::count() const
709 {
710     Q_D(const QBoxLayout);
711     return d->list.count();
712 }
713 
714 /*!
715     \reimp
716 */
itemAt(int index) const717 QLayoutItem *QBoxLayout::itemAt(int index) const
718 {
719     Q_D(const QBoxLayout);
720     return index >= 0 && index < d->list.count() ? d->list.at(index)->item : nullptr;
721 }
722 
723 /*!
724     \reimp
725 */
takeAt(int index)726 QLayoutItem *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(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 */
expandingDirections() const750 Qt::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 */
setGeometry(const QRect & r)761 void 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, &top, &right, &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(d->dir) ? s.x() : s.y();
779         int space = horz(d->dir) ? s.width() : s.height();
780         int n = a.count();
781         if (d->hasHfw && !horz(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(box->item->minimumSize().width(), s.width(), 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(a, 0, n, pos, space);
802 
803         bool reverse = (horz(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 */
addItem(QLayoutItem * item)833 void QBoxLayout::addItem(QLayoutItem *item)
834 {
835     Q_D(QBoxLayout);
836     QBoxLayoutItem *it = new QBoxLayoutItem(item);
837     d->list.append(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 */
insertItem(int index,QLayoutItem * item)848 void 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(index, 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 */
insertSpacing(int index,int size)868 void 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(d->dir))
876         b = QLayoutPrivate::createSpacerItem(this, size, 0, QSizePolicy::Fixed, QSizePolicy::Minimum);
877     else
878         b = QLayoutPrivate::createSpacerItem(this, 0, size, QSizePolicy::Minimum, QSizePolicy::Fixed);
879 
880     QBoxLayoutItem *it = new QBoxLayoutItem(b);
881     it->magic = true;
882     d->list.insert(index, 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 */
insertStretch(int index,int stretch)893 void 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(d->dir))
901         b = QLayoutPrivate::createSpacerItem(this, 0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum);
902     else
903         b = QLayoutPrivate::createSpacerItem(this, 0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
904 
905     QBoxLayoutItem *it = new QBoxLayoutItem(b, stretch);
906     it->magic = true;
907     d->list.insert(index, 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 */
insertSpacerItem(int index,QSpacerItem * spacerItem)920 void 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(index, 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 */
insertLayout(int index,QLayout * layout,int stretch)940 void QBoxLayout::insertLayout(int index, QLayout *layout, int stretch)
941 {
942     Q_D(QBoxLayout);
943     if (!d->checkLayout(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(index, 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 */
insertWidget(int index,QWidget * widget,int stretch,Qt::Alignment alignment)974 void 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(widget);
981     if (index < 0)                                // append
982         index = d->list.count();
983     QWidgetItem *b = QLayoutPrivate::createWidgetItem(this, widget);
984     b->setAlignment(alignment);
985 
986     QBoxLayoutItem *it = new QBoxLayoutItem(b, stretch);
987     d->list.insert(index, 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 */
addSpacing(int size)998 void QBoxLayout::addSpacing(int size)
999 {
1000     insertSpacing(-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 */
addStretch(int stretch)1009 void QBoxLayout::addStretch(int stretch)
1010 {
1011     insertStretch(-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 */
addSpacerItem(QSpacerItem * spacerItem)1021 void QBoxLayout::addSpacerItem(QSpacerItem *spacerItem)
1022 {
1023     insertSpacerItem(-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 */
addWidget(QWidget * widget,int stretch,Qt::Alignment alignment)1046 void QBoxLayout::addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
1047 {
1048     insertWidget(-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 */
addLayout(QLayout * layout,int stretch)1057 void QBoxLayout::addLayout(QLayout *layout, int stretch)
1058 {
1059     insertLayout(-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 */
addStrut(int size)1069 void QBoxLayout::addStrut(int size)
1070 {
1071     Q_D(QBoxLayout);
1072     QLayoutItem *b;
1073     if (horz(d->dir))
1074         b = QLayoutPrivate::createSpacerItem(this, 0, size, QSizePolicy::Fixed, QSizePolicy::Minimum);
1075     else
1076         b = QLayoutPrivate::createSpacerItem(this, size, 0, QSizePolicy::Minimum, QSizePolicy::Fixed);
1077 
1078     QBoxLayoutItem *it = new QBoxLayoutItem(b);
1079     it->magic = true;
1080     d->list.append(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 */
setStretchFactor(QWidget * widget,int stretch)1091 bool 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 */
setStretchFactor(QLayout * layout,int stretch)1114 bool 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 
setStretch(int index,int stretch)1136 void 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(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 
stretch(int index) const1154 int QBoxLayout::stretch(int index) const
1155 {
1156     Q_D(const QBoxLayout);
1157     if (index >= 0 && index < d->list.size())
1158         return d->list.at(index)->stretch;
1159     return -1;
1160 }
1161 
1162 /*!
1163     Sets the direction of this layout to \a direction.
1164 */
setDirection(Direction direction)1165 void QBoxLayout::setDirection(Direction direction)
1166 {
1167     Q_D(QBoxLayout);
1168     if (d->dir == direction)
1169         return;
1170     if (horz(d->dir) != horz(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(s.height(), s.width(),
1184                             horz(direction) ? QSizePolicy::Fixed:QSizePolicy::Minimum,
1185                             horz(direction) ? QSizePolicy::Minimum:QSizePolicy::Fixed);
1186 
1187                     } else {
1188                         //stretch
1189                         if (horz(direction))
1190                             sp->changeSize(0, 0, QSizePolicy::Expanding,
1191                                             QSizePolicy::Minimum);
1192                         else
1193                             sp->changeSize(0, 0, QSizePolicy::Minimum,
1194                                             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 
direction() const1213 QBoxLayout::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 */
QHBoxLayout(QWidget * parent)1265 QHBoxLayout::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 */
QHBoxLayout()1274 QHBoxLayout::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 */
~QHBoxLayout()1288 QHBoxLayout::~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 */
QVBoxLayout(QWidget * parent)1337 QVBoxLayout::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 */
QVBoxLayout()1347 QVBoxLayout::QVBoxLayout()
1348     : QBoxLayout(TopToBottom)
1349 {
1350 }
1351 
1352 
1353 /*!
1354     Destroys this box layout.
1355 
1356     The layout's widgets aren't destroyed.
1357 */
~QVBoxLayout()1358 QVBoxLayout::~QVBoxLayout()
1359 {
1360 }
1361 
1362 QT_END_NAMESPACE
1363 
1364 #include "moc_qboxlayout.cpp"
1365