1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtGui module of the Qt Toolkit.
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 Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
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 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
42 #include "qgridlayout.h"
43 #include "qapplication.h"
46 #include "qsizepolicy.h"
48 #include "qvarlengtharray.h"
49 #include "qlayoutengine_p.h"
50 #include "qlayout_p.h"
54 struct QGridLayoutSizeTriple
62 Three internal classes related to QGridLayout: (1) QGridBox is a
63 QLayoutItem with (row, column) information and (torow, tocolumn) information; (3) QGridLayoutData is
64 the internal representation of a QGridLayout.
70 QGridBox(QLayoutItem *lit) { item_ = lit; }
72 QGridBox(const QLayout *l, QWidget *wid) { item_ = QLayoutPrivate::createWidgetItem(l, wid); }
73 ~QGridBox() { delete item_; }
75 QSize sizeHint() const { return item_->sizeHint(); }
76 QSize minimumSize() const { return item_->minimumSize(); }
77 QSize maximumSize() const { return item_->maximumSize(); }
78 Qt::Orientations expandingDirections() const { return item_->expandingDirections(); }
79 bool isEmpty() const { return item_->isEmpty(); }
81 bool hasHeightForWidth() const { return item_->hasHeightForWidth(); }
82 int heightForWidth(int w) const { return item_->heightForWidth(w); }
84 void setAlignment(Qt::Alignment a) { item_->setAlignment(a); }
85 void setGeometry(const QRect &r) { item_->setGeometry(r); }
86 Qt::Alignment alignment() const { return item_->alignment(); }
87 QLayoutItem *item() { return item_; }
88 QLayoutItem *takeItem() { QLayoutItem *i = item_; item_ = 0; return i; }
90 int hStretch() { return item_->widget() ?
91 item_->widget()->sizePolicy().horizontalStretch() : 0; }
92 int vStretch() { return item_->widget() ?
93 item_->widget()->sizePolicy().verticalStretch() : 0; }
96 friend class QGridLayoutPrivate;
97 friend class QGridLayout;
99 inline int toRow(int rr) const { return torow >= 0 ? torow : rr - 1; }
100 inline int toCol(int cc) const { return tocol >= 0 ? tocol : cc - 1; }
107 class QGridLayoutPrivate : public QLayoutPrivate
109 Q_DECLARE_PUBLIC(QGridLayout)
111 QGridLayoutPrivate();
113 void add(QGridBox*, int row, int col);
114 void add(QGridBox*, int row1, int row2, int col1, int col2);
115 QSize sizeHint(int hSpacing, int vSpacing) const;
116 QSize minimumSize(int hSpacing, int vSpacing) const;
117 QSize maximumSize(int hSpacing, int vSpacing) const;
119 Qt::Orientations expandingDirections(int hSpacing, int vSpacing) const;
121 void distribute(QRect rect, int hSpacing, int vSpacing);
122 inline int numRows() const { return rr; }
123 inline int numCols() const { return cc; }
124 inline void expand(int rows, int cols)
125 { setSize(qMax(rows, rr), qMax(cols, cc)); }
126 inline void setRowStretch(int r, int s)
127 { expand(r + 1, 0); rStretch[r] = s; setDirty(); }
128 inline void setColStretch(int c, int s)
129 { expand(0, c + 1); cStretch[c] = s; setDirty(); }
130 inline int rowStretch(int r) const { return rStretch.at(r); }
131 inline int colStretch(int c) const { return cStretch.at(c); }
132 inline void setRowMinimumHeight(int r, int s)
133 { expand(r + 1, 0); rMinHeights[r] = s; setDirty(); }
134 inline void setColumnMinimumWidth(int c, int s)
135 { expand(0, c + 1); cMinWidths[c] = s; setDirty(); }
136 inline int rowSpacing(int r) const { return rMinHeights.at(r); }
137 inline int colSpacing(int c) const { return cMinWidths.at(c); }
139 inline void setReversed(bool r, bool c) { hReversed = c; vReversed = r; }
140 inline bool horReversed() const { return hReversed; }
141 inline bool verReversed() const { return vReversed; }
142 inline void setDirty() { needRecalc = true; hfw_width = -1; }
143 inline bool isDirty() const { return needRecalc; }
144 bool hasHeightForWidth(int hSpacing, int vSpacing);
145 int heightForWidth(int width, int hSpacing, int vSpacing);
146 int minimumHeightForWidth(int width, int hSpacing, int vSpacing);
148 inline void getNextPos(int &row, int &col) { row = nextR; col = nextC; }
149 inline int count() const { return things.count(); }
150 QRect cellRect(int row, int col) const;
152 inline QLayoutItem *itemAt(int index) const {
153 if (index < things.count())
154 return things.at(index)->item();
158 inline QLayoutItem *takeAt(int index) {
159 QLayoutItem *item = 0;
160 if (index < things.count()) {
161 QGridBox *b = things.takeAt(index);
163 item = b->takeItem();
170 void getItemPosition(int index, int *row, int *column, int *rowSpan, int *columnSpan) const {
171 if (index < things.count()) {
172 const QGridBox *b = things.at(index);
173 int toRow = b->toRow(rr);
174 int toCol = b->toCol(cc);
177 *rowSpan = toRow - *row + 1;
178 *columnSpan = toCol - *column +1;
184 void setNextPosAfter(int r, int c);
185 void recalcHFW(int w);
186 void addHfwData(QGridBox *box, int width);
188 QSize findSize(int QLayoutStruct::*, int hSpacing, int vSpacing) const;
189 void addData(QGridBox *b, const QGridLayoutSizeTriple &sizes, bool r, bool c);
190 void setSize(int rows, int cols);
191 void setupSpacings(QVector<QLayoutStruct> &chain, QGridBox *grid[], int fixedSpacing,
192 Qt::Orientation orientation);
193 void setupLayoutData(int hSpacing, int vSpacing);
194 void setupHfwLayoutData();
195 void effectiveMargins(int *left, int *top, int *right, int *bottom) const;
199 QVector<QLayoutStruct> rowData;
200 QVector<QLayoutStruct> colData;
201 QVector<QLayoutStruct> *hfwData;
202 QVector<int> rStretch;
203 QVector<int> cStretch;
204 QVector<int> rMinHeights;
205 QVector<int> cMinWidths;
206 QList<QGridBox *> things;
214 int horizontalSpacing;
225 uint addVertical : 1;
228 void QGridLayoutPrivate::effectiveMargins(int *left, int *top, int *right, int *bottom) const
233 int b = bottomMargin;
235 int leftMost = INT_MAX;
236 int topMost = INT_MAX;
241 const int n = things.count();
242 for (int i = 0; i < n; ++i) {
243 QGridBox *box = things.at(i);
244 QLayoutItem *itm = box->item();
247 bool visualHReversed = hReversed != (w->layoutDirection() == Qt::RightToLeft);
248 QRect lir = itm->geometry();
249 QRect wr = w->geometry();
250 if (box->col <= leftMost) {
251 if (box->col < leftMost) {
252 // we found an item even closer to the margin, discard.
259 if (visualHReversed) {
260 r = qMax(r, wr.right() - lir.right());
262 l = qMax(l, lir.left() - wr.left());
265 if (box->row <= topMost) {
266 if (box->row < topMost) {
267 // we found an item even closer to the margin, discard.
275 b = qMax(b, wr.bottom() - lir.bottom());
277 t = qMax(t, lir.top() - wr.top());
279 if (box->toCol(cc) >= rightMost) {
280 if (box->toCol(cc) > rightMost) {
281 // we found an item even closer to the margin, discard.
282 rightMost = box->toCol(cc);
288 if (visualHReversed) {
289 l = qMax(l, lir.left() - wr.left());
291 r = qMax(r, wr.right() - lir.right());
295 if (box->toRow(rr) >= bottomMost) {
296 if (box->toRow(rr) > bottomMost) {
297 // we found an item even closer to the margin, discard.
298 bottomMost = box->toRow(rr);
305 t = qMax(t, lir.top() - wr.top());
307 b = qMax(b, wr.bottom() - lir.bottom());
323 QGridLayoutPrivate::QGridLayoutPrivate()
332 horizontalSpacing = -1;
333 verticalSpacing = -1;
337 QGridLayoutPrivate::QGridLayoutPrivate(int nRows, int nCols)
338 : rowData(0), colData(0)
349 setSize(nRows, nCols);
353 void QGridLayoutPrivate::deleteAll()
355 while (!things.isEmpty())
356 delete things.takeFirst();
360 bool QGridLayoutPrivate::hasHeightForWidth(int hSpacing, int vSpacing)
362 setupLayoutData(hSpacing, vSpacing);
367 Assumes that setupLayoutData() has been called, and that
368 qGeomCalc() has filled in colData with appropriate values.
370 void QGridLayoutPrivate::recalcHFW(int w)
373 Go through all children, using colData and heightForWidth()
374 and put the results in hfwData.
377 hfwData = new QVector<QLayoutStruct>(rr);
378 setupHfwLayoutData();
379 QVector<QLayoutStruct> &rData = *hfwData;
383 for (int r = 0; r < rr; r++) {
384 int spacing = rData.at(r).spacing;
385 h += rData.at(r).sizeHint + spacing;
386 mh += rData.at(r).minimumSize + spacing;
390 hfw_height = qMin(QLAYOUTSIZE_MAX, h);
391 hfw_minheight = qMin(QLAYOUTSIZE_MAX, mh);
394 int QGridLayoutPrivate::heightForWidth(int w, int hSpacing, int vSpacing)
396 setupLayoutData(hSpacing, vSpacing);
399 int left, top, right, bottom;
400 effectiveMargins(&left, &top, &right, &bottom);
402 int hMargins = left + right;
403 if (w - hMargins != hfw_width) {
404 qGeomCalc(colData, 0, cc, 0, w - hMargins);
405 recalcHFW(w - hMargins);
407 return hfw_height + top + bottom;
410 int QGridLayoutPrivate::minimumHeightForWidth(int w, int hSpacing, int vSpacing)
412 (void)heightForWidth(w, hSpacing, vSpacing);
416 effectiveMargins(0, &top, 0, &bottom);
417 return hfw_minheight + top + bottom;
420 QSize QGridLayoutPrivate::findSize(int QLayoutStruct::*size, int hSpacing, int vSpacing) const
422 QGridLayoutPrivate *that = const_cast<QGridLayoutPrivate*>(this);
423 that->setupLayoutData(hSpacing, vSpacing);
428 for (int r = 0; r < rr; r++)
429 h += rowData.at(r).*size + rowData.at(r).spacing;
430 for (int c = 0; c < cc; c++)
431 w += colData.at(c).*size + colData.at(c).spacing;
433 w = qMin(QLAYOUTSIZE_MAX, w);
434 h = qMin(QLAYOUTSIZE_MAX, h);
439 Qt::Orientations QGridLayoutPrivate::expandingDirections(int hSpacing, int vSpacing) const
441 QGridLayoutPrivate *that = const_cast<QGridLayoutPrivate*>(this);
442 that->setupLayoutData(hSpacing, vSpacing);
443 Qt::Orientations ret;
445 for (int r = 0; r < rr; r++) {
446 if (rowData.at(r).expansive) {
451 for (int c = 0; c < cc; c++) {
452 if (colData.at(c).expansive) {
453 ret |= Qt::Horizontal;
460 QSize QGridLayoutPrivate::sizeHint(int hSpacing, int vSpacing) const
462 return findSize(&QLayoutStruct::sizeHint, hSpacing, vSpacing);
465 QSize QGridLayoutPrivate::maximumSize(int hSpacing, int vSpacing) const
467 return findSize(&QLayoutStruct::maximumSize, hSpacing, vSpacing);
470 QSize QGridLayoutPrivate::minimumSize(int hSpacing, int vSpacing) const
472 return findSize(&QLayoutStruct::minimumSize, hSpacing, vSpacing);
475 void QGridLayoutPrivate::setSize(int r, int c)
477 if ((int)rowData.size() < r) {
478 int newR = qMax(r, rr * 2);
479 rowData.resize(newR);
480 rStretch.resize(newR);
481 rMinHeights.resize(newR);
482 for (int i = rr; i < newR; i++) {
484 rowData[i].maximumSize = 0;
491 if ((int)colData.size() < c) {
492 int newC = qMax(c, cc * 2);
493 colData.resize(newC);
494 cStretch.resize(newC);
495 cMinWidths.resize(newC);
496 for (int i = cc; i < newC; i++) {
498 colData[i].maximumSize = 0;
506 if (hfwData && (int)hfwData->size() < r) {
515 void QGridLayoutPrivate::setNextPosAfter(int row, int col)
518 if (col > nextC || (col == nextC && row >= nextR)) {
527 if (row > nextR || (row == nextR && col >= nextC)) {
538 void QGridLayoutPrivate::add(QGridBox *box, int row, int col)
540 expand(row + 1, col + 1);
541 box->row = box->torow = row;
542 box->col = box->tocol = col;
545 setNextPosAfter(row, col);
548 void QGridLayoutPrivate::add(QGridBox *box, int row1, int row2, int col1, int col2)
550 if (row2 >= 0 && row2 < row1)
551 qWarning("QGridLayout: Multi-cell fromRow greater than toRow");
552 if (col2 >= 0 && col2 < col1)
553 qWarning("QGridLayout: Multi-cell fromCol greater than toCol");
554 if (row1 == row2 && col1 == col2) {
555 add(box, row1, col1);
558 expand(row2 + 1, col2 + 1);
570 setNextPosAfter(row2, col2);
573 void QGridLayoutPrivate::addData(QGridBox *box, const QGridLayoutSizeTriple &sizes, bool r, bool c)
575 const QWidget *widget = box->item()->widget();
577 if (box->isEmpty() && widget)
581 QLayoutStruct *data = &colData[box->col];
582 if (!cStretch.at(box->col))
583 data->stretch = qMax(data->stretch, box->hStretch());
584 data->sizeHint = qMax(sizes.hint.width(), data->sizeHint);
585 data->minimumSize = qMax(sizes.minS.width(), data->minimumSize);
587 qMaxExpCalc(data->maximumSize, data->expansive, data->empty, sizes.maxS.width(),
588 box->expandingDirections() & Qt::Horizontal, box->isEmpty());
591 QLayoutStruct *data = &rowData[box->row];
592 if (!rStretch.at(box->row))
593 data->stretch = qMax(data->stretch, box->vStretch());
594 data->sizeHint = qMax(sizes.hint.height(), data->sizeHint);
595 data->minimumSize = qMax(sizes.minS.height(), data->minimumSize);
597 qMaxExpCalc(data->maximumSize, data->expansive, data->empty, sizes.maxS.height(),
598 box->expandingDirections() & Qt::Vertical, box->isEmpty());
602 static void initEmptyMultiBox(QVector<QLayoutStruct> &chain, int start, int end)
604 for (int i = start; i <= end; i++) {
605 QLayoutStruct *data = &chain[i];
606 if (data->empty && data->maximumSize == 0) // truly empty box
607 data->maximumSize = QWIDGETSIZE_MAX;
612 static void distributeMultiBox(QVector<QLayoutStruct> &chain, int start, int end, int minSize,
613 int sizeHint, QVector<int> &stretchArray, int stretch)
620 for (i = start; i <= end; i++) {
621 QLayoutStruct *data = &chain[i];
622 w += data->minimumSize;
623 wh += data->sizeHint;
624 max += data->maximumSize;
625 if (stretchArray.at(i) == 0)
626 data->stretch = qMax(data->stretch, stretch);
629 int spacing = data->spacing;
636 if (max < minSize) { // implies w < minSize
638 We must increase the maximum size of at least one of the
639 items. qGeomCalc() will put the extra space in between the
640 items. We must recover that extra space and put it
641 somewhere. It does not really matter where, since the user
642 can always specify stretch factors and avoid this code.
644 qGeomCalc(chain, start, end - start + 1, 0, minSize);
646 for (i = start; i <= end; i++) {
647 QLayoutStruct *data = &chain[i];
648 int nextPos = (i == end) ? minSize : chain.at(i + 1).pos;
649 int realSize = nextPos - pos;
651 realSize -= data->spacing;
652 if (data->minimumSize < realSize)
653 data->minimumSize = realSize;
654 if (data->maximumSize < data->minimumSize)
655 data->maximumSize = data->minimumSize;
658 } else if (w < minSize) {
659 qGeomCalc(chain, start, end - start + 1, 0, minSize);
660 for (i = start; i <= end; i++) {
661 QLayoutStruct *data = &chain[i];
662 if (data->minimumSize < data->size)
663 data->minimumSize = data->size;
668 qGeomCalc(chain, start, end - start + 1, 0, sizeHint);
669 for (i = start; i <= end; i++) {
670 QLayoutStruct *data = &chain[i];
671 if (data->sizeHint < data->size)
672 data->sizeHint = data->size;
677 static QGridBox *&gridAt(QGridBox *grid[], int r, int c, int cc,
678 Qt::Orientation orientation = Qt::Vertical)
680 if (orientation == Qt::Horizontal)
682 return grid[(r * cc) + c];
685 void QGridLayoutPrivate::setupSpacings(QVector<QLayoutStruct> &chain,
686 QGridBox *grid[], int fixedSpacing,
687 Qt::Orientation orientation)
690 int numRows = rr; // or columns if orientation is horizontal
691 int numColumns = cc; // or rows if orientation is horizontal
693 if (orientation == Qt::Horizontal) {
694 qSwap(numRows, numColumns);
698 if (fixedSpacing < 0) {
699 if (QWidget *parentWidget = q->parentWidget())
700 style = parentWidget->style();
703 for (int c = 0; c < numColumns; ++c) {
704 QGridBox *previousBox = 0;
705 int previousRow = -1; // previous *non-empty* row
707 for (int r = 0; r < numRows; ++r) {
708 if (chain.at(r).empty)
711 QGridBox *box = gridAt(grid, r, c, cc, orientation);
712 if (previousRow != -1 && (!box || previousBox != box)) {
713 int spacing = fixedSpacing;
715 QSizePolicy::ControlTypes controlTypes1 = QSizePolicy::DefaultType;
716 QSizePolicy::ControlTypes controlTypes2 = QSizePolicy::DefaultType;
718 controlTypes1 = previousBox->item()->controlTypes();
720 controlTypes2 = box->item()->controlTypes();
722 if ((orientation == Qt::Horizontal && hReversed)
723 || (orientation == Qt::Vertical && vReversed))
724 qSwap(controlTypes1, controlTypes2);
727 spacing = style->combinedLayoutSpacing(controlTypes1, controlTypes2,
728 orientation, 0, q->parentWidget());
730 if (orientation == Qt::Vertical) {
731 QGridBox *sibling = vReversed ? previousBox : box;
733 QWidget *wid = sibling->item()->widget();
735 spacing = qMax(spacing, sibling->item()->geometry().top() - wid->geometry().top() );
740 if (spacing > chain.at(previousRow).spacing)
741 chain[previousRow].spacing = spacing;
750 //#define QT_LAYOUT_DISABLE_CACHING
752 void QGridLayoutPrivate::setupLayoutData(int hSpacing, int vSpacing)
756 #ifndef QT_LAYOUT_DISABLE_CACHING
763 for (i = 0; i < rr; i++) {
764 rowData[i].init(rStretch.at(i), rMinHeights.at(i));
765 rowData[i].maximumSize = rStretch.at(i) ? QLAYOUTSIZE_MAX : rMinHeights.at(i);
767 for (i = 0; i < cc; i++) {
768 colData[i].init(cStretch.at(i), cMinWidths.at(i));
769 colData[i].maximumSize = cStretch.at(i) ? QLAYOUTSIZE_MAX : cMinWidths.at(i);
772 int n = things.size();
773 QVarLengthArray<QGridLayoutSizeTriple> sizes(n);
775 bool has_multi = false;
778 Grid of items. We use it to determine which items are
779 adjacent to which and compute the spacings correctly.
781 QVarLengthArray<QGridBox *> grid(rr * cc);
782 memset(grid.data(), 0, rr * cc * sizeof(QGridBox *));
785 Initialize 'sizes' and 'grid' data structures, and insert
786 non-spanning items to our row and column data structures.
788 for (i = 0; i < n; ++i) {
789 QGridBox * const box = things.at(i);
790 sizes[i].minS = box->minimumSize();
791 sizes[i].hint = box->sizeHint();
792 sizes[i].maxS = box->maximumSize();
794 if (box->hasHeightForWidth())
797 if (box->row == box->toRow(rr)) {
798 addData(box, sizes[i], true, false);
800 initEmptyMultiBox(rowData, box->row, box->toRow(rr));
804 if (box->col == box->toCol(cc)) {
805 addData(box, sizes[i], false, true);
807 initEmptyMultiBox(colData, box->col, box->toCol(cc));
811 for (int r = box->row; r <= box->toRow(rr); ++r) {
812 for (int c = box->col; c <= box->toCol(cc); ++c) {
813 gridAt(grid.data(), r, c, cc) = box;
818 setupSpacings(colData, grid.data(), hSpacing, Qt::Horizontal);
819 setupSpacings(rowData, grid.data(), vSpacing, Qt::Vertical);
822 Insert multicell items to our row and column data structures.
823 This must be done after the non-spanning items to obtain a
824 better distribution in distributeMultiBox().
827 for (i = 0; i < n; ++i) {
828 QGridBox * const box = things.at(i);
830 if (box->row != box->toRow(rr))
831 distributeMultiBox(rowData, box->row, box->toRow(rr), sizes[i].minS.height(),
832 sizes[i].hint.height(), rStretch, box->vStretch());
833 if (box->col != box->toCol(cc))
834 distributeMultiBox(colData, box->col, box->toCol(cc), sizes[i].minS.width(),
835 sizes[i].hint.width(), cStretch, box->hStretch());
839 for (i = 0; i < rr; i++)
840 rowData[i].expansive = rowData.at(i).expansive || rowData.at(i).stretch > 0;
841 for (i = 0; i < cc; i++)
842 colData[i].expansive = colData.at(i).expansive || colData.at(i).stretch > 0;
844 q->getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
849 void QGridLayoutPrivate::addHfwData(QGridBox *box, int width)
851 QVector<QLayoutStruct> &rData = *hfwData;
852 if (box->hasHeightForWidth()) {
853 int hint = box->heightForWidth(width);
854 rData[box->row].sizeHint = qMax(hint, rData.at(box->row).sizeHint);
855 rData[box->row].minimumSize = qMax(hint, rData.at(box->row).minimumSize);
857 QSize hint = box->sizeHint();
858 QSize minS = box->minimumSize();
859 rData[box->row].sizeHint = qMax(hint.height(), rData.at(box->row).sizeHint);
860 rData[box->row].minimumSize = qMax(minS.height(), rData.at(box->row).minimumSize);
865 Similar to setupLayoutData(), but uses heightForWidth(colData)
866 instead of sizeHint(). Assumes that setupLayoutData() and
867 qGeomCalc(colData) has been called.
869 void QGridLayoutPrivate::setupHfwLayoutData()
871 QVector<QLayoutStruct> &rData = *hfwData;
872 for (int i = 0; i < rr; i++) {
873 rData[i] = rowData.at(i);
874 rData[i].minimumSize = rData[i].sizeHint = rMinHeights.at(i);
877 for (int pass = 0; pass < 2; ++pass) {
878 for (int i = 0; i < things.size(); ++i) {
879 QGridBox *box = things.at(i);
882 int r2 = box->toRow(rr);
883 int c2 = box->toCol(cc);
884 int w = colData.at(c2).pos + colData.at(c2).size - colData.at(c1).pos;
891 initEmptyMultiBox(rData, r1, r2);
893 QSize hint = box->sizeHint();
894 QSize min = box->minimumSize();
895 if (box->hasHeightForWidth()) {
896 int hfwh = box->heightForWidth(w);
897 if (hfwh > hint.height())
898 hint.setHeight(hfwh);
899 if (hfwh > min.height())
902 distributeMultiBox(rData, r1, r2, min.height(), hint.height(),
903 rStretch, box->vStretch());
908 for (int i = 0; i < rr; i++)
909 rData[i].expansive = rData.at(i).expansive || rData.at(i).stretch > 0;
912 void QGridLayoutPrivate::distribute(QRect r, int hSpacing, int vSpacing)
915 bool visualHReversed = hReversed;
916 QWidget *parent = q->parentWidget();
917 if (parent && parent->isRightToLeft())
918 visualHReversed = !visualHReversed;
920 setupLayoutData(hSpacing, vSpacing);
922 int left, top, right, bottom;
923 effectiveMargins(&left, &top, &right, &bottom);
924 r.adjust(+left, +top, -right, -bottom);
926 qGeomCalc(colData, 0, cc, r.x(), r.width());
927 QVector<QLayoutStruct> *rDataPtr;
929 recalcHFW(r.width());
930 qGeomCalc(*hfwData, 0, rr, r.y(), r.height());
933 qGeomCalc(rowData, 0, rr, r.y(), r.height());
936 QVector<QLayoutStruct> &rData = *rDataPtr;
939 bool reverse = ((r.bottom() > rect.bottom()) || (r.bottom() == rect.bottom()
940 && ((r.right() > rect.right()) != visualHReversed)));
941 int n = things.size();
942 for (i = 0; i < n; ++i) {
943 QGridBox *box = things.at(reverse ? n-i-1 : i);
944 int r2 = box->toRow(rr);
945 int c2 = box->toCol(cc);
947 int x = colData.at(box->col).pos;
948 int y = rData.at(box->row).pos;
949 int x2p = colData.at(c2).pos + colData.at(c2).size; // x2+1
950 int y2p = rData.at(r2).pos + rData.at(r2).size; // y2+1
955 x = r.left() + r.right() - x - w + 1;
957 y = r.top() + r.bottom() - y - h + 1;
959 box->setGeometry(QRect(x, y, w, h));
963 QRect QGridLayoutPrivate::cellRect(int row, int col) const
965 if (row < 0 || row >= rr || col < 0 || col >= cc)
968 const QVector<QLayoutStruct> *rDataPtr;
969 if (has_hfw && hfwData)
973 return QRect(colData.at(col).pos, rDataPtr->at(row).pos,
974 colData.at(col).size, rDataPtr->at(row).size);
980 \brief The QGridLayout class lays out widgets in a grid.
982 \ingroup geomanagement
985 QGridLayout takes the space made available to it (by its parent
986 layout or by the parentWidget()), divides it up into rows and
987 columns, and puts each widget it manages into the correct cell.
989 Columns and rows behave identically; we will discuss columns, but
990 there are equivalent functions for rows.
992 Each column has a minimum width and a stretch factor. The minimum
993 width is the greatest of that set using setColumnMinimumWidth() and the
994 minimum width of each widget in that column. The stretch factor is
995 set using setColumnStretch() and determines how much of the available
996 space the column will get over and above its necessary minimum.
998 Normally, each managed widget or layout is put into a cell of its
999 own using addWidget(). It is also possible for a widget to occupy
1000 multiple cells using the row and column spanning overloads of
1001 addItem() and addWidget(). If you do this, QGridLayout will guess
1002 how to distribute the size over the columns/rows (based on the
1005 To remove a widget from a layout, call removeWidget(). Calling
1006 QWidget::hide() on a widget also effectively removes the widget
1007 from the layout until QWidget::show() is called.
1009 This illustration shows a fragment of a dialog with a five-column,
1010 three-row grid (the grid is shown overlaid in magenta):
1012 \image gridlayout.png A grid layout
1014 Columns 0, 2 and 4 in this dialog fragment are made up of a
1015 QLabel, a QLineEdit, and a QListBox. Columns 1 and 3 are
1016 placeholders made with setColumnMinimumWidth(). Row 0 consists of three
1017 QLabel objects, row 1 of three QLineEdit objects and row 2 of
1018 three QListBox objects. We used placeholder columns (1 and 3) to
1019 get the right amount of space between the columns.
1021 Note that the columns and rows are not equally wide or tall. If
1022 you want two columns to have the same width, you must set their
1023 minimum widths and stretch factors to be the same yourself. You do
1024 this using setColumnMinimumWidth() and setColumnStretch().
1026 If the QGridLayout is not the top-level layout (i.e. does not
1027 manage all of the widget's area and children), you must add it to
1028 its parent layout when you create it, but before you do anything
1029 with it. The normal way to add a layout is by calling
1030 addLayout() on the parent layout.
1032 Once you have added your layout you can start putting widgets and
1033 other layouts into the cells of your grid layout using
1034 addWidget(), addItem(), and addLayout().
1036 QGridLayout also includes two margin widths:
1037 the \l{getContentsMargins()}{contents margin} and the spacing().
1038 The contents margin is the width of the reserved space along each
1039 of the QGridLayout's four sides. The spacing() is the width of the
1040 automatically allocated spacing between neighboring boxes.
1042 The default contents margin values are provided by the
1043 \l{QStyle::pixelMetric()}{style}. The default value Qt styles specify
1044 is 9 for child widgets and 11 for windows. The spacing defaults to the same as
1045 the margin width for a top-level layout, or to the same as the
1048 \sa QBoxLayout, QStackedLayout, {Layout Management}, {Basic Layouts Example}
1053 Constructs a new QGridLayout with parent widget, \a parent. The
1054 layout has one row and one column initially, and will expand when
1055 new items are inserted.
1057 QGridLayout::QGridLayout(QWidget *parent)
1058 : QLayout(*new QGridLayoutPrivate, 0, parent)
1065 Constructs a new grid layout.
1067 You must insert this grid into another layout. You can insert
1068 widgets and layouts into this layout at any time, but laying out
1069 will not be performed before this is inserted into another layout.
1071 QGridLayout::QGridLayout()
1072 : QLayout(*new QGridLayoutPrivate, 0, 0)
1084 Sets the positioning mode used by addItem(). If \a orient is
1085 Qt::Horizontal, this layout is expanded to \a n columns, and items
1086 will be added columns-first. Otherwise it is expanded to \a n rows and
1087 items will be added rows-first.
1090 void QGridLayout::setDefaultPositioning(int n, Qt::Orientation orient)
1093 if (orient == Qt::Horizontal) {
1095 d->addVertical = false;
1098 d->addVertical = true;
1104 Destroys the grid layout. Geometry management is terminated if
1105 this is a top-level grid.
1107 The layout's widgets aren't destroyed.
1109 QGridLayout::~QGridLayout()
1116 \property QGridLayout::horizontalSpacing
1117 \brief the spacing between widgets that are laid out side by side
1120 If no value is explicitly set, the layout's horizontal spacing is
1121 inherited from the parent layout, or from the style settings for
1124 \sa verticalSpacing, QStyle::pixelMetric(), {QStyle::}{PM_LayoutHorizontalSpacing}
1126 void QGridLayout::setHorizontalSpacing(int spacing)
1129 d->horizontalSpacing = spacing;
1133 int QGridLayout::horizontalSpacing() const
1135 Q_D(const QGridLayout);
1136 if (d->horizontalSpacing >= 0) {
1137 return d->horizontalSpacing;
1139 return qSmartSpacing(this, QStyle::PM_LayoutHorizontalSpacing);
1144 \property QGridLayout::verticalSpacing
1145 \brief the spacing between widgets that are laid out on top of each other
1148 If no value is explicitly set, the layout's vertical spacing is
1149 inherited from the parent layout, or from the style settings for
1152 \sa horizontalSpacing, QStyle::pixelMetric(), {QStyle::}{PM_LayoutHorizontalSpacing}
1154 void QGridLayout::setVerticalSpacing(int spacing)
1157 d->verticalSpacing = spacing;
1161 int QGridLayout::verticalSpacing() const
1163 Q_D(const QGridLayout);
1164 if (d->verticalSpacing >= 0) {
1165 return d->verticalSpacing;
1167 return qSmartSpacing(this, QStyle::PM_LayoutVerticalSpacing);
1172 This function sets both the vertical and horizontal spacing to
1175 \sa setVerticalSpacing(), setHorizontalSpacing()
1177 void QGridLayout::setSpacing(int spacing)
1180 d->horizontalSpacing = d->verticalSpacing = spacing;
1185 If the vertical spacing is equal to the horizontal spacing,
1186 this function returns that value; otherwise it return -1.
1188 \sa setSpacing(), verticalSpacing(), horizontalSpacing()
1190 int QGridLayout::spacing() const
1192 int hSpacing = horizontalSpacing();
1193 if (hSpacing == verticalSpacing()) {
1201 Returns the number of rows in this grid.
1203 int QGridLayout::rowCount() const
1205 Q_D(const QGridLayout);
1206 return d->numRows();
1210 Returns the number of columns in this grid.
1212 int QGridLayout::columnCount() const
1214 Q_D(const QGridLayout);
1215 return d->numCols();
1221 QSize QGridLayout::sizeHint() const
1223 Q_D(const QGridLayout);
1224 QSize result(d->sizeHint(horizontalSpacing(), verticalSpacing()));
1225 int left, top, right, bottom;
1226 d->effectiveMargins(&left, &top, &right, &bottom);
1227 result += QSize(left + right, top + bottom);
1234 QSize QGridLayout::minimumSize() const
1236 Q_D(const QGridLayout);
1237 QSize result(d->minimumSize(horizontalSpacing(), verticalSpacing()));
1238 int left, top, right, bottom;
1239 d->effectiveMargins(&left, &top, &right, &bottom);
1240 result += QSize(left + right, top + bottom);
1247 QSize QGridLayout::maximumSize() const
1249 Q_D(const QGridLayout);
1251 QSize s = d->maximumSize(horizontalSpacing(), verticalSpacing());
1252 int left, top, right, bottom;
1253 d->effectiveMargins(&left, &top, &right, &bottom);
1254 s += QSize(left + right, top + bottom);
1255 s = s.boundedTo(QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX));
1256 if (alignment() & Qt::AlignHorizontal_Mask)
1257 s.setWidth(QLAYOUTSIZE_MAX);
1258 if (alignment() & Qt::AlignVertical_Mask)
1259 s.setHeight(QLAYOUTSIZE_MAX);
1266 bool QGridLayout::hasHeightForWidth() const
1268 return ((QGridLayout*)this)->d_func()->hasHeightForWidth(horizontalSpacing(), verticalSpacing());
1274 int QGridLayout::heightForWidth(int w) const
1276 Q_D(const QGridLayout);
1277 QGridLayoutPrivate *dat = const_cast<QGridLayoutPrivate *>(d);
1278 return dat->heightForWidth(w, horizontalSpacing(), verticalSpacing());
1284 int QGridLayout::minimumHeightForWidth(int w) const
1286 Q_D(const QGridLayout);
1287 QGridLayoutPrivate *dat = const_cast<QGridLayoutPrivate *>(d);
1288 return dat->minimumHeightForWidth(w, horizontalSpacing(), verticalSpacing());
1294 int QGridLayout::count() const
1296 Q_D(const QGridLayout);
1304 QLayoutItem *QGridLayout::itemAt(int index) const
1306 Q_D(const QGridLayout);
1307 return d->itemAt(index);
1313 Returns the layout item that occupies cell (\a row, \a column), or 0 if
1316 \sa getItemPosition(), indexOf()
1318 QLayoutItem *QGridLayout::itemAtPosition(int row, int column) const
1320 Q_D(const QGridLayout);
1321 int n = d->things.count();
1322 for (int i = 0; i < n; ++i) {
1323 QGridBox *box = d->things.at(i);
1324 if (row >= box->row && row <= box->toRow(d->rr)
1325 && column >= box->col && column <= box->toCol(d->cc)) {
1335 QLayoutItem *QGridLayout::takeAt(int index)
1338 return d->takeAt(index);
1342 Returns the position information of the item with the given \a index.
1344 The variables passed as \a row and \a column are updated with the position of the
1345 item in the layout, and the \a rowSpan and \a columnSpan variables are updated
1346 with the vertical and horizontal spans of the item.
1348 \sa itemAtPosition(), itemAt()
1350 void QGridLayout::getItemPosition(int index, int *row, int *column, int *rowSpan, int *columnSpan) const
1352 Q_D(const QGridLayout);
1353 d->getItemPosition(index, row, column, rowSpan, columnSpan);
1360 void QGridLayout::setGeometry(const QRect &rect)
1363 if (d->isDirty() || rect != geometry()) {
1364 QRect cr = alignment() ? alignmentRect(rect) : rect;
1365 d->distribute(cr, horizontalSpacing(), verticalSpacing());
1366 QLayout::setGeometry(rect);
1371 Returns the geometry of the cell with row \a row and column \a column
1372 in the grid. Returns an invalid rectangle if \a row or \a column is
1375 \warning in the current version of Qt this function does not
1376 return valid results until setGeometry() has been called, i.e.
1377 after the parentWidget() is visible.
1379 QRect QGridLayout::cellRect(int row, int column) const
1381 Q_D(const QGridLayout);
1382 return d->cellRect(row, column);
1388 void QGridLayout::addItem(QLayoutItem *item)
1392 d->getNextPos(r, c);
1393 addItem(item, r, c);
1397 Adds \a item at position \a row, \a column, spanning \a rowSpan
1398 rows and \a columnSpan columns, and aligns it according to \a
1399 alignment. If \a rowSpan and/or \a columnSpan is -1, then the item
1400 will extend to the bottom and/or right edge, respectively. The
1401 layout takes ownership of the \a item.
1403 \warning Do not use this function to add child layouts or child
1404 widget items. Use addLayout() or addWidget() instead.
1406 void QGridLayout::addItem(QLayoutItem *item, int row, int column, int rowSpan, int columnSpan, Qt::Alignment alignment)
1409 QGridBox *b = new QGridBox(item);
1410 b->setAlignment(alignment);
1411 d->add(b, row, (rowSpan < 0) ? -1 : row + rowSpan - 1, column, (columnSpan < 0) ? -1 : column + columnSpan - 1);
1416 Returns true if the widget \a w can be added to the layout \a l;
1417 otherwise returns false.
1419 static bool checkWidget(QLayout *l, QWidget *w)
1422 qWarning("QLayout: Cannot add null widget to %s/%s", l->metaObject()->className(),
1423 l->objectName().toLocal8Bit().data());
1430 Adds the given \a widget to the cell grid at \a row, \a column. The
1431 top-left position is (0, 0) by default.
1433 The alignment is specified by \a alignment. The default
1434 alignment is 0, which means that the widget fills the entire cell.
1437 void QGridLayout::addWidget(QWidget *widget, int row, int column, Qt::Alignment alignment)
1439 if (!checkWidget(this, widget))
1441 if (row < 0 || column < 0) {
1442 qWarning("QGridLayout: Cannot add %s/%s to %s/%s at row %d column %d",
1443 widget->metaObject()->className(), widget->objectName().toLocal8Bit().data(),
1444 metaObject()->className(), objectName().toLocal8Bit().data(), row, column);
1447 addChildWidget(widget);
1448 QWidgetItem *b = QLayoutPrivate::createWidgetItem(this, widget);
1449 addItem(b, row, column, 1, 1, alignment);
1455 This version adds the given \a widget to the cell grid, spanning
1456 multiple rows/columns. The cell will start at \a fromRow, \a
1457 fromColumn spanning \a rowSpan rows and \a columnSpan columns. The
1458 \a widget will have the given \a alignment.
1460 If \a rowSpan and/or \a columnSpan is -1, then the widget will
1461 extend to the bottom and/or right edge, respectively.
1464 void QGridLayout::addWidget(QWidget *widget, int fromRow, int fromColumn,
1465 int rowSpan, int columnSpan, Qt::Alignment alignment)
1468 if (!checkWidget(this, widget))
1470 int toRow = (rowSpan < 0) ? -1 : fromRow + rowSpan - 1;
1471 int toColumn = (columnSpan < 0) ? -1 : fromColumn + columnSpan - 1;
1472 addChildWidget(widget);
1473 QGridBox *b = new QGridBox(this, widget);
1474 b->setAlignment(alignment);
1475 d->add(b, fromRow, toRow, fromColumn, toColumn);
1480 \fn void QGridLayout::addWidget(QWidget *widget)
1487 Places the \a layout at position (\a row, \a column) in the grid. The
1488 top-left position is (0, 0).
1490 The alignment is specified by \a alignment. The default
1491 alignment is 0, which means that the widget fills the entire cell.
1493 A non-zero alignment indicates that the layout should not grow to
1494 fill the available space but should be sized according to
1498 \a layout becomes a child of the grid layout.
1500 void QGridLayout::addLayout(QLayout *layout, int row, int column, Qt::Alignment alignment)
1503 addChildLayout(layout);
1504 QGridBox *b = new QGridBox(layout);
1505 b->setAlignment(alignment);
1506 d->add(b, row, column);
1511 This version adds the layout \a layout to the cell grid, spanning multiple
1512 rows/columns. The cell will start at \a row, \a column spanning \a
1513 rowSpan rows and \a columnSpan columns.
1515 If \a rowSpan and/or \a columnSpan is -1, then the layout will extend to the bottom
1516 and/or right edge, respectively.
1518 void QGridLayout::addLayout(QLayout *layout, int row, int column,
1519 int rowSpan, int columnSpan, Qt::Alignment alignment)
1522 addChildLayout(layout);
1523 QGridBox *b = new QGridBox(layout);
1524 b->setAlignment(alignment);
1525 d->add(b, row, (rowSpan < 0) ? -1 : row + rowSpan - 1, column, (columnSpan < 0) ? -1 : column + columnSpan - 1);
1529 Sets the stretch factor of row \a row to \a stretch. The first row
1532 The stretch factor is relative to the other rows in this grid.
1533 Rows with a higher stretch factor take more of the available
1536 The default stretch factor is 0. If the stretch factor is 0 and no
1537 other row in this table can grow at all, the row may still grow.
1539 \sa rowStretch(), setRowMinimumHeight(), setColumnStretch()
1541 void QGridLayout::setRowStretch(int row, int stretch)
1544 d->setRowStretch(row, stretch);
1549 Returns the stretch factor for row \a row.
1553 int QGridLayout::rowStretch(int row) const
1555 Q_D(const QGridLayout);
1556 return d->rowStretch(row);
1560 Returns the stretch factor for column \a column.
1562 \sa setColumnStretch()
1564 int QGridLayout::columnStretch(int column) const
1566 Q_D(const QGridLayout);
1567 return d->colStretch(column);
1571 Sets the stretch factor of column \a column to \a stretch. The first
1574 The stretch factor is relative to the other columns in this grid.
1575 Columns with a higher stretch factor take more of the available
1578 The default stretch factor is 0. If the stretch factor is 0 and no
1579 other column in this table can grow at all, the column may still
1582 An alternative approach is to add spacing using addItem() with a
1585 \sa columnStretch(), setRowStretch()
1587 void QGridLayout::setColumnStretch(int column, int stretch)
1590 d->setColStretch(column, stretch);
1597 Sets the minimum height of row \a row to \a minSize pixels.
1599 \sa rowMinimumHeight(), setColumnMinimumWidth()
1601 void QGridLayout::setRowMinimumHeight(int row, int minSize)
1604 d->setRowMinimumHeight(row, minSize);
1609 Returns the minimum width set for row \a row.
1611 \sa setRowMinimumHeight()
1613 int QGridLayout::rowMinimumHeight(int row) const
1615 Q_D(const QGridLayout);
1616 return d->rowSpacing(row);
1620 Sets the minimum width of column \a column to \a minSize pixels.
1622 \sa columnMinimumWidth(), setRowMinimumHeight()
1624 void QGridLayout::setColumnMinimumWidth(int column, int minSize)
1627 d->setColumnMinimumWidth(column, minSize);
1632 Returns the column spacing for column \a column.
1634 \sa setColumnMinimumWidth()
1636 int QGridLayout::columnMinimumWidth(int column) const
1638 Q_D(const QGridLayout);
1639 return d->colSpacing(column);
1645 Qt::Orientations QGridLayout::expandingDirections() const
1647 Q_D(const QGridLayout);
1648 return d->expandingDirections(horizontalSpacing(), verticalSpacing());
1652 Sets the grid's origin corner, i.e. position (0, 0), to \a corner.
1654 void QGridLayout::setOriginCorner(Qt::Corner corner)
1657 d->setReversed(corner == Qt::BottomLeftCorner || corner == Qt::BottomRightCorner,
1658 corner == Qt::TopRightCorner || corner == Qt::BottomRightCorner);
1662 Returns the corner that's used for the grid's origin, i.e. for
1665 Qt::Corner QGridLayout::originCorner() const
1667 Q_D(const QGridLayout);
1668 if (d->horReversed()) {
1669 return d->verReversed() ? Qt::BottomRightCorner : Qt::TopRightCorner;
1671 return d->verReversed() ? Qt::BottomLeftCorner : Qt::TopLeftCorner;
1678 void QGridLayout::invalidate()
1682 QLayout::invalidate();