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 "qgridlayout.h"
41 #include "qapplication.h"
42 #include "qwidget.h"
43 #include "qlist.h"
44 #include "qsizepolicy.h"
45 #include "qvector.h"
46 #include "qvarlengtharray.h"
47 #include "qlayoutengine_p.h"
48 #include "qlayout_p.h"
49 
50 QT_BEGIN_NAMESPACE
51 
52 struct QGridLayoutSizeTriple
53 {
54     QSize minS;
55     QSize hint;
56     QSize maxS;
57 };
58 
59 /*
60   Three internal classes related to QGridLayout: (1) QGridBox is a
61   QLayoutItem with (row, column) information and (torow, tocolumn) information; (3) QGridLayoutData is
62   the internal representation of a QGridLayout.
63 */
64 
65 class QGridBox
66 {
67 public:
QGridBox(QLayoutItem * lit)68     QGridBox(QLayoutItem *lit) { item_ = lit; }
69 
QGridBox(const QLayout * l,QWidget * wid)70     QGridBox(const QLayout *l, QWidget *wid) { item_ = QLayoutPrivate::createWidgetItem(l, wid); }
~QGridBox()71     ~QGridBox() { delete item_; }
72 
sizeHint() const73     QSize sizeHint() const { return item_->sizeHint(); }
minimumSize() const74     QSize minimumSize() const { return item_->minimumSize(); }
maximumSize() const75     QSize maximumSize() const { return item_->maximumSize(); }
expandingDirections() const76     Qt::Orientations expandingDirections() const { return item_->expandingDirections(); }
isEmpty() const77     bool isEmpty() const { return item_->isEmpty(); }
78 
hasHeightForWidth() const79     bool hasHeightForWidth() const { return item_->hasHeightForWidth(); }
heightForWidth(int w) const80     int heightForWidth(int w) const { return item_->heightForWidth(w); }
81 
setAlignment(Qt::Alignment a)82     void setAlignment(Qt::Alignment a) { item_->setAlignment(a); }
setGeometry(const QRect & r)83     void setGeometry(const QRect &r) { item_->setGeometry(r); }
alignment() const84     Qt::Alignment alignment() const { return item_->alignment(); }
item()85     QLayoutItem *item() { return item_; }
setItem(QLayoutItem * newitem)86     void setItem(QLayoutItem *newitem) { item_ = newitem; }
takeItem()87     QLayoutItem *takeItem() { QLayoutItem *i = item_; item_ = nullptr; return i; }
88 
hStretch()89     int hStretch() { return item_->widget() ?
90                          item_->widget()->sizePolicy().horizontalStretch() : 0; }
vStretch()91     int vStretch() { return item_->widget() ?
92                          item_->widget()->sizePolicy().verticalStretch() : 0; }
93 
94 private:
95     friend class QGridLayoutPrivate;
96     friend class QGridLayout;
97 
toRow(int rr) const98     inline int toRow(int rr) const { return torow >= 0 ? torow : rr - 1; }
toCol(int cc) const99     inline int toCol(int cc) const { return tocol >= 0 ? tocol : cc - 1; }
100 
101     QLayoutItem *item_;
102     int row, col;
103     int torow, tocol;
104 };
105 
106 class QGridLayoutPrivate : public QLayoutPrivate
107 {
108     Q_DECLARE_PUBLIC(QGridLayout)
109 public:
110     QGridLayoutPrivate();
111 
112     void add(QGridBox*, int row, int col);
113     void add(QGridBox*, int row1, int row2, int col1, int col2);
114     QSize sizeHint(int hSpacing, int vSpacing) const;
115     QSize minimumSize(int hSpacing, int vSpacing) const;
116     QSize maximumSize(int hSpacing, int vSpacing) const;
117 
118     Qt::Orientations expandingDirections(int hSpacing, int vSpacing) const;
119 
120     void distribute(QRect rect, int hSpacing, int vSpacing);
numRows() const121     inline int numRows() const { return rr; }
numCols() const122     inline int numCols() const { return cc; }
expand(int rows,int cols)123     inline void expand(int rows, int cols)
124         { setSize(qMax(rows, rr), qMax(cols, cc)); }
setRowStretch(int r,int s)125     inline void setRowStretch(int r, int s)
126         { expand(r + 1, 0); rStretch[r] = s; setDirty(); }
setColStretch(int c,int s)127     inline void setColStretch(int c, int s)
128         { expand(0, c + 1); cStretch[c] = s; setDirty(); }
rowStretch(int r) const129     inline int rowStretch(int r) const { return rStretch.at(r); }
colStretch(int c) const130     inline int colStretch(int c) const { return cStretch.at(c); }
setRowMinimumHeight(int r,int s)131     inline void setRowMinimumHeight(int r, int s)
132         { expand(r + 1, 0); rMinHeights[r] = s; setDirty(); }
setColumnMinimumWidth(int c,int s)133     inline void setColumnMinimumWidth(int c, int s)
134         { expand(0, c + 1); cMinWidths[c] = s; setDirty(); }
rowSpacing(int r) const135     inline int rowSpacing(int r) const { return rMinHeights.at(r); }
colSpacing(int c) const136     inline int colSpacing(int c) const { return cMinWidths.at(c); }
137 
setReversed(bool r,bool c)138     inline void setReversed(bool r, bool c) { hReversed = c; vReversed = r; }
horReversed() const139     inline bool horReversed() const { return hReversed; }
verReversed() const140     inline bool verReversed() const { return vReversed; }
setDirty()141     inline void setDirty() { needRecalc = true; hfw_width = -1; }
isDirty() const142     inline bool isDirty() const { return needRecalc; }
143     bool hasHeightForWidth(int hSpacing, int vSpacing);
144     int heightForWidth(int width, int hSpacing, int vSpacing);
145     int minimumHeightForWidth(int width, int hSpacing, int vSpacing);
146 
getNextPos(int & row,int & col)147     inline void getNextPos(int &row, int &col) { row = nextR; col = nextC; }
count() const148     inline int count() const { return things.count(); }
149     QRect cellRect(int row, int col) const;
150 
itemAt(int index) const151     inline QLayoutItem *itemAt(int index) const {
152         if (index < things.count())
153             return things.at(index)->item();
154         else
155             return nullptr;
156     }
takeAt(int index)157     inline QLayoutItem *takeAt(int index) {
158         Q_Q(QGridLayout);
159         if (index < things.count()) {
160             if (QGridBox *b = things.takeAt(index)) {
161                 QLayoutItem *item = b->takeItem();
162                 if (QLayout *l = item->layout()) {
163                     // sanity check in case the user passed something weird to QObject::setParent()
164                     if (l->parent() == q)
165                         l->setParent(nullptr);
166                 }
167                 delete b;
168                 return item;
169             }
170         }
171         return nullptr;
172     }
replaceAt(int index,QLayoutItem * newitem)173     QLayoutItem* replaceAt(int index, QLayoutItem *newitem) override
174     {
175         if (!newitem)
176             return nullptr;
177         QLayoutItem *item = nullptr;
178         QGridBox *b = things.value(index);
179         if (b) {
180             item = b->takeItem();
181             b->setItem(newitem);
182         }
183         return item;
184     }
185 
getItemPosition(int index,int * row,int * column,int * rowSpan,int * columnSpan) const186     void getItemPosition(int index, int *row, int *column, int *rowSpan, int *columnSpan) const {
187         if (index < things.count()) {
188             const QGridBox *b =  things.at(index);
189             int toRow = b->toRow(rr);
190             int toCol = b->toCol(cc);
191             *row = b->row;
192             *column = b->col;
193             *rowSpan = toRow - *row + 1;
194             *columnSpan = toCol - *column +1;
195         }
196     }
197     void deleteAll();
198 
199 private:
200     void setNextPosAfter(int r, int c);
201     void recalcHFW(int w);
202     void addHfwData(QGridBox *box, int width);
203     void init();
204     QSize findSize(int QLayoutStruct::*, int hSpacing, int vSpacing) const;
205     void addData(QGridBox *b, const QGridLayoutSizeTriple &sizes, bool r, bool c);
206     void setSize(int rows, int cols);
207     void setupSpacings(QVector<QLayoutStruct> &chain, QGridBox *grid[], int fixedSpacing,
208                        Qt::Orientation orientation);
209     void setupLayoutData(int hSpacing, int vSpacing);
210     void setupHfwLayoutData();
211     void effectiveMargins(int *left, int *top, int *right, int *bottom) const;
212 
213     int rr;
214     int cc;
215     QVector<QLayoutStruct> rowData;
216     QVector<QLayoutStruct> colData;
217     QVector<QLayoutStruct> *hfwData;
218     QVector<int> rStretch;
219     QVector<int> cStretch;
220     QVector<int> rMinHeights;
221     QVector<int> cMinWidths;
222     QList<QGridBox *> things;
223 
224     int hfw_width;
225     int hfw_height;
226     int hfw_minheight;
227     int nextR;
228     int nextC;
229 
230     int horizontalSpacing;
231     int verticalSpacing;
232     int leftMargin;
233     int topMargin;
234     int rightMargin;
235     int bottomMargin;
236 
237     uint hReversed : 1;
238     uint vReversed : 1;
239     uint needRecalc : 1;
240     uint has_hfw : 1;
241     uint addVertical : 1;
242 };
243 
effectiveMargins(int * left,int * top,int * right,int * bottom) const244 void QGridLayoutPrivate::effectiveMargins(int *left, int *top, int *right, int *bottom) const
245 {
246     int l = leftMargin;
247     int t = topMargin;
248     int r = rightMargin;
249     int b = bottomMargin;
250 #ifdef Q_OS_MAC
251     int leftMost = INT_MAX;
252     int topMost = INT_MAX;
253     int rightMost = 0;
254     int bottomMost = 0;
255 
256     QWidget *w = 0;
257     const int n = things.count();
258     for (int i = 0; i < n; ++i) {
259         QGridBox *box = things.at(i);
260         QLayoutItem *itm = box->item();
261         w = itm->widget();
262         if (w) {
263             bool visualHReversed = hReversed != (w->layoutDirection() == Qt::RightToLeft);
264             QRect lir = itm->geometry();
265             QRect wr = w->geometry();
266             if (box->col <= leftMost) {
267                 if (box->col < leftMost) {
268                     // we found an item even closer to the margin, discard.
269                     leftMost = box->col;
270                     if (visualHReversed)
271                         r = rightMargin;
272                     else
273                         l = leftMargin;
274                 }
275                 if (visualHReversed) {
276                     r = qMax(r, wr.right() - lir.right());
277                 } else {
278                     l = qMax(l, lir.left() - wr.left());
279                 }
280             }
281             if (box->row <= topMost) {
282                 if (box->row < topMost) {
283                     // we found an item even closer to the margin, discard.
284                     topMost = box->row;
285                     if (vReversed)
286                         b = bottomMargin;
287                     else
288                         t = topMargin;
289                 }
290                 if (vReversed)
291                     b = qMax(b, wr.bottom() - lir.bottom());
292                 else
293                     t = qMax(t, lir.top() - wr.top());
294             }
295             if (box->toCol(cc) >= rightMost) {
296                 if (box->toCol(cc) > rightMost) {
297                     // we found an item even closer to the margin, discard.
298                     rightMost = box->toCol(cc);
299                     if (visualHReversed)
300                         l = leftMargin;
301                     else
302                         r = rightMargin;
303                 }
304                 if (visualHReversed) {
305                     l = qMax(l, lir.left() - wr.left());
306                 } else {
307                     r = qMax(r, wr.right() - lir.right());
308                 }
309 
310             }
311             if (box->toRow(rr) >= bottomMost) {
312                 if (box->toRow(rr) > bottomMost) {
313                     // we found an item even closer to the margin, discard.
314                     bottomMost = box->toRow(rr);
315                     if (vReversed)
316                         t = topMargin;
317                     else
318                         b = bottomMargin;
319                 }
320                 if (vReversed)
321                     t = qMax(t, lir.top() - wr.top());
322                 else
323                     b = qMax(b, wr.bottom() - lir.bottom());
324             }
325         }
326     }
327 
328 #endif
329     if (left)
330         *left = l;
331     if (top)
332         *top = t;
333     if (right)
334         *right = r;
335     if (bottom)
336         *bottom = b;
337 }
338 
QGridLayoutPrivate()339 QGridLayoutPrivate::QGridLayoutPrivate()
340 {
341     addVertical = false;
342     setDirty();
343     rr = cc = 0;
344     nextR = nextC = 0;
345     hfwData = nullptr;
346     hReversed = false;
347     vReversed = false;
348     horizontalSpacing = -1;
349     verticalSpacing = -1;
350 }
351 
352 #if 0
353 QGridLayoutPrivate::QGridLayoutPrivate(int nRows, int nCols)
354     : rowData(0), colData(0)
355 {
356     init();
357     if (nRows  < 0) {
358         nRows = 1;
359         addVertical = false;
360     }
361     if (nCols  < 0) {
362         nCols = 1;
363         addVertical = true;
364     }
365     setSize(nRows, nCols);
366 }
367 #endif
368 
deleteAll()369 void QGridLayoutPrivate::deleteAll()
370 {
371     while (!things.isEmpty())
372         delete things.takeFirst();
373     delete hfwData;
374 }
375 
hasHeightForWidth(int hSpacing,int vSpacing)376 bool QGridLayoutPrivate::hasHeightForWidth(int hSpacing, int vSpacing)
377 {
378     setupLayoutData(hSpacing, vSpacing);
379     return has_hfw;
380 }
381 
382 /*
383   Assumes that setupLayoutData() has been called, and that
384   qGeomCalc() has filled in colData with appropriate values.
385 */
recalcHFW(int w)386 void QGridLayoutPrivate::recalcHFW(int w)
387 {
388     /*
389       Go through all children, using colData and heightForWidth()
390       and put the results in hfwData.
391     */
392     if (!hfwData)
393         hfwData = new QVector<QLayoutStruct>(rr);
394     setupHfwLayoutData();
395     QVector<QLayoutStruct> &rData = *hfwData;
396 
397     int h = 0;
398     int mh = 0;
399     for (int r = 0; r < rr; r++) {
400         int spacing = rData.at(r).spacing;
401         h += rData.at(r).sizeHint + spacing;
402         mh += rData.at(r).minimumSize + spacing;
403     }
404 
405     hfw_width = w;
406     hfw_height = qMin(QLAYOUTSIZE_MAX, h);
407     hfw_minheight = qMin(QLAYOUTSIZE_MAX, mh);
408 }
409 
heightForWidth(int w,int hSpacing,int vSpacing)410 int QGridLayoutPrivate::heightForWidth(int w, int hSpacing, int vSpacing)
411 {
412     setupLayoutData(hSpacing, vSpacing);
413     if (!has_hfw)
414         return -1;
415     int left, top, right, bottom;
416     effectiveMargins(&left, &top, &right, &bottom);
417 
418     int hMargins = left + right;
419     if (w - hMargins != hfw_width) {
420         qGeomCalc(colData, 0, cc, 0, w - hMargins);
421         recalcHFW(w - hMargins);
422     }
423     return hfw_height + top + bottom;
424 }
425 
minimumHeightForWidth(int w,int hSpacing,int vSpacing)426 int QGridLayoutPrivate::minimumHeightForWidth(int w, int hSpacing, int vSpacing)
427 {
428     (void)heightForWidth(w, hSpacing, vSpacing);
429     if (!has_hfw)
430         return -1;
431     int top, bottom;
432     effectiveMargins(nullptr, &top, nullptr, &bottom);
433     return hfw_minheight + top + bottom;
434 }
435 
findSize(int QLayoutStruct::* size,int hSpacing,int vSpacing) const436 QSize QGridLayoutPrivate::findSize(int QLayoutStruct::*size, int hSpacing, int vSpacing) const
437 {
438     QGridLayoutPrivate *that = const_cast<QGridLayoutPrivate*>(this);
439     that->setupLayoutData(hSpacing, vSpacing);
440 
441     int w = 0;
442     int h = 0;
443 
444     for (int r = 0; r < rr; r++)
445         h += rowData.at(r).*size + rowData.at(r).spacing;
446     for (int c = 0; c < cc; c++)
447         w += colData.at(c).*size + colData.at(c).spacing;
448 
449     w = qMin(QLAYOUTSIZE_MAX, w);
450     h = qMin(QLAYOUTSIZE_MAX, h);
451 
452     return QSize(w, h);
453 }
454 
expandingDirections(int hSpacing,int vSpacing) const455 Qt::Orientations QGridLayoutPrivate::expandingDirections(int hSpacing, int vSpacing) const
456 {
457     QGridLayoutPrivate *that = const_cast<QGridLayoutPrivate*>(this);
458     that->setupLayoutData(hSpacing, vSpacing);
459     Qt::Orientations ret;
460 
461     for (int r = 0; r < rr; r++) {
462         if (rowData.at(r).expansive) {
463             ret |= Qt::Vertical;
464             break;
465         }
466     }
467     for (int c = 0; c < cc; c++) {
468         if (colData.at(c).expansive) {
469             ret |= Qt::Horizontal;
470             break;
471         }
472     }
473     return ret;
474 }
475 
sizeHint(int hSpacing,int vSpacing) const476 QSize QGridLayoutPrivate::sizeHint(int hSpacing, int vSpacing) const
477 {
478     return findSize(&QLayoutStruct::sizeHint, hSpacing, vSpacing);
479 }
480 
maximumSize(int hSpacing,int vSpacing) const481 QSize QGridLayoutPrivate::maximumSize(int hSpacing, int vSpacing) const
482 {
483     return findSize(&QLayoutStruct::maximumSize, hSpacing, vSpacing);
484 }
485 
minimumSize(int hSpacing,int vSpacing) const486 QSize QGridLayoutPrivate::minimumSize(int hSpacing, int vSpacing) const
487 {
488     return findSize(&QLayoutStruct::minimumSize, hSpacing, vSpacing);
489 }
490 
setSize(int r,int c)491 void QGridLayoutPrivate::setSize(int r, int c)
492 {
493     if ((int)rowData.size() < r) {
494         int newR = qMax(r, rr * 2);
495         rowData.resize(newR);
496         rStretch.resize(newR);
497         rMinHeights.resize(newR);
498         for (int i = rr; i < newR; i++) {
499             rowData[i].init();
500             rowData[i].maximumSize = 0;
501             rowData[i].pos = 0;
502             rowData[i].size = 0;
503             rStretch[i] = 0;
504             rMinHeights[i] = 0;
505         }
506     }
507     if ((int)colData.size() < c) {
508         int newC = qMax(c, cc * 2);
509         colData.resize(newC);
510         cStretch.resize(newC);
511         cMinWidths.resize(newC);
512         for (int i = cc; i < newC; i++) {
513             colData[i].init();
514             colData[i].maximumSize = 0;
515             colData[i].pos = 0;
516             colData[i].size = 0;
517             cStretch[i] = 0;
518             cMinWidths[i] = 0;
519         }
520     }
521 
522     if (hfwData && (int)hfwData->size() < r) {
523         delete hfwData;
524         hfwData = nullptr;
525         hfw_width = -1;
526     }
527     rr = r;
528     cc = c;
529 }
530 
setNextPosAfter(int row,int col)531 void QGridLayoutPrivate::setNextPosAfter(int row, int col)
532 {
533     if (addVertical) {
534         if (col > nextC || (col == nextC && row >= nextR)) {
535             nextR = row + 1;
536             nextC = col;
537             if (nextR >= rr) {
538                 nextR = 0;
539                 nextC++;
540             }
541         }
542     } else {
543         if (row > nextR || (row == nextR && col >= nextC)) {
544             nextR = row;
545             nextC = col + 1;
546             if (nextC >= cc) {
547                 nextC = 0;
548                 nextR++;
549             }
550         }
551     }
552 }
553 
add(QGridBox * box,int row,int col)554 void QGridLayoutPrivate::add(QGridBox *box, int row, int col)
555 {
556     expand(row + 1, col + 1);
557     box->row = box->torow = row;
558     box->col = box->tocol = col;
559     things.append(box);
560     setDirty();
561     setNextPosAfter(row, col);
562 }
563 
add(QGridBox * box,int row1,int row2,int col1,int col2)564 void QGridLayoutPrivate::add(QGridBox *box, int row1, int row2, int col1, int col2)
565 {
566     if (Q_UNLIKELY(row2 >= 0 && row2 < row1))
567         qWarning("QGridLayout: Multi-cell fromRow greater than toRow");
568     if (Q_UNLIKELY(col2 >= 0 && col2 < col1))
569         qWarning("QGridLayout: Multi-cell fromCol greater than toCol");
570     if (row1 == row2 && col1 == col2) {
571         add(box, row1, col1);
572         return;
573     }
574     expand(qMax(row1, row2) + 1, qMax(col1, col2) + 1);
575     box->row = row1;
576     box->col = col1;
577 
578     box->torow = row2;
579     box->tocol = col2;
580 
581     things.append(box);
582     setDirty();
583     if (col2 < 0)
584         col2 = cc - 1;
585 
586     setNextPosAfter(row2, col2);
587 }
588 
addData(QGridBox * box,const QGridLayoutSizeTriple & sizes,bool r,bool c)589 void QGridLayoutPrivate::addData(QGridBox *box, const QGridLayoutSizeTriple &sizes, bool r, bool c)
590 {
591     const QWidget *widget = box->item()->widget();
592 
593     if (box->isEmpty() && widget)
594         return;
595 
596     if (c) {
597         QLayoutStruct *data = &colData[box->col];
598         if (!cStretch.at(box->col))
599             data->stretch = qMax(data->stretch, box->hStretch());
600         data->sizeHint = qMax(sizes.hint.width(), data->sizeHint);
601         data->minimumSize = qMax(sizes.minS.width(), data->minimumSize);
602 
603         qMaxExpCalc(data->maximumSize, data->expansive, data->empty, sizes.maxS.width(),
604                     box->expandingDirections() & Qt::Horizontal, box->isEmpty());
605     }
606     if (r) {
607         QLayoutStruct *data = &rowData[box->row];
608         if (!rStretch.at(box->row))
609             data->stretch = qMax(data->stretch, box->vStretch());
610         data->sizeHint = qMax(sizes.hint.height(), data->sizeHint);
611         data->minimumSize = qMax(sizes.minS.height(), data->minimumSize);
612 
613         qMaxExpCalc(data->maximumSize, data->expansive, data->empty, sizes.maxS.height(),
614                     box->expandingDirections() & Qt::Vertical, box->isEmpty());
615     }
616 }
617 
initEmptyMultiBox(QVector<QLayoutStruct> & chain,int start,int end)618 static void initEmptyMultiBox(QVector<QLayoutStruct> &chain, int start, int end)
619 {
620     for (int i = start; i <= end; i++) {
621         QLayoutStruct *data = &chain[i];
622         if (data->empty && data->maximumSize == 0) // truly empty box
623             data->maximumSize = QWIDGETSIZE_MAX;
624         data->empty = false;
625     }
626 }
627 
distributeMultiBox(QVector<QLayoutStruct> & chain,int start,int end,int minSize,int sizeHint,QVector<int> & stretchArray,int stretch)628 static void distributeMultiBox(QVector<QLayoutStruct> &chain, int start, int end, int minSize,
629                                int sizeHint, QVector<int> &stretchArray, int stretch)
630 {
631     int i;
632     int w = 0;
633     int wh = 0;
634     int max = 0;
635 
636     for (i = start; i <= end; i++) {
637         QLayoutStruct *data = &chain[i];
638         w += data->minimumSize;
639         wh += data->sizeHint;
640         max += data->maximumSize;
641         if (stretchArray.at(i) == 0)
642             data->stretch = qMax(data->stretch, stretch);
643 
644         if (i != end) {
645             int spacing = data->spacing;
646             w += spacing;
647             wh += spacing;
648             max += spacing;
649         }
650     }
651 
652     if (max < minSize) { // implies w < minSize
653         /*
654           We must increase the maximum size of at least one of the
655           items. qGeomCalc() will put the extra space in between the
656           items. We must recover that extra space and put it
657           somewhere. It does not really matter where, since the user
658           can always specify stretch factors and avoid this code.
659         */
660         qGeomCalc(chain, start, end - start + 1, 0, minSize);
661         int pos = 0;
662         for (i = start; i <= end; i++) {
663             QLayoutStruct *data = &chain[i];
664             int nextPos = (i == end) ? minSize : chain.at(i + 1).pos;
665             int realSize = nextPos - pos;
666             if (i != end)
667                 realSize -= data->spacing;
668             if (data->minimumSize < realSize)
669                 data->minimumSize = realSize;
670             if (data->maximumSize < data->minimumSize)
671                 data->maximumSize = data->minimumSize;
672             pos = nextPos;
673         }
674     } else if (w < minSize) {
675         qGeomCalc(chain, start, end - start + 1, 0, minSize);
676         for (i = start; i <= end; i++) {
677             QLayoutStruct *data = &chain[i];
678             if (data->minimumSize < data->size)
679                 data->minimumSize = data->size;
680         }
681     }
682 
683     if (wh < sizeHint) {
684         qGeomCalc(chain, start, end - start + 1, 0, sizeHint);
685         for (i = start; i <= end; i++) {
686             QLayoutStruct *data = &chain[i];
687             if (data->sizeHint < data->size)
688                 data->sizeHint = data->size;
689         }
690     }
691 }
692 
gridAt(QGridBox * grid[],int r,int c,int cc,Qt::Orientation orientation=Qt::Vertical)693 static QGridBox *&gridAt(QGridBox *grid[], int r, int c, int cc,
694                          Qt::Orientation orientation = Qt::Vertical)
695 {
696     if (orientation == Qt::Horizontal)
697         qSwap(r, c);
698     return grid[(r * cc) + c];
699 }
700 
setupSpacings(QVector<QLayoutStruct> & chain,QGridBox * grid[],int fixedSpacing,Qt::Orientation orientation)701 void QGridLayoutPrivate::setupSpacings(QVector<QLayoutStruct> &chain,
702                                        QGridBox *grid[], int fixedSpacing,
703                                        Qt::Orientation orientation)
704 {
705     Q_Q(QGridLayout);
706     int numRows = rr;       // or columns if orientation is horizontal
707     int numColumns = cc;    // or rows if orientation is horizontal
708 
709     if (orientation == Qt::Horizontal) {
710         qSwap(numRows, numColumns);
711     }
712 
713     QStyle *style = nullptr;
714     if (fixedSpacing < 0) {
715         if (QWidget *parentWidget = q->parentWidget())
716             style = parentWidget->style();
717     }
718 
719     for (int c = 0; c < numColumns; ++c) {
720         QGridBox *previousBox = nullptr;
721         int previousRow = -1;       // previous *non-empty* row
722 
723         for (int r = 0; r < numRows; ++r) {
724             if (chain.at(r).empty)
725                 continue;
726 
727             QGridBox *box = gridAt(grid, r, c, cc, orientation);
728             if (previousRow != -1 && (!box || previousBox != box)) {
729                 int spacing = fixedSpacing;
730                 if (spacing < 0) {
731                     QSizePolicy::ControlTypes controlTypes1 = QSizePolicy::DefaultType;
732                     QSizePolicy::ControlTypes controlTypes2 = QSizePolicy::DefaultType;
733                     if (previousBox)
734                         controlTypes1 = previousBox->item()->controlTypes();
735                     if (box)
736                         controlTypes2 = box->item()->controlTypes();
737 
738                     if ((orientation == Qt::Horizontal && hReversed)
739                             || (orientation == Qt::Vertical && vReversed))
740                         qSwap(controlTypes1, controlTypes2);
741 
742                     if (style)
743                         spacing = style->combinedLayoutSpacing(controlTypes1, controlTypes2,
744                                              orientation, nullptr, q->parentWidget());
745                 } else {
746                     if (orientation == Qt::Vertical) {
747                         QGridBox *sibling = vReversed ? previousBox : box;
748                         if (sibling) {
749                             if (sibling->item()->isEmpty()) {
750                                 spacing = 0;
751                             } else {
752                                 QWidget *wid = sibling->item()->widget();
753                                 if (wid)
754                                     spacing = qMax(spacing, sibling->item()->geometry().top() - wid->geometry().top());
755                             }
756                         }
757                     }
758                 }
759 
760                 if (spacing > chain.at(previousRow).spacing)
761                     chain[previousRow].spacing = spacing;
762             }
763 
764             previousBox = box;
765             previousRow = r;
766         }
767     }
768 }
769 
770 //#define QT_LAYOUT_DISABLE_CACHING
771 
setupLayoutData(int hSpacing,int vSpacing)772 void QGridLayoutPrivate::setupLayoutData(int hSpacing, int vSpacing)
773 {
774     Q_Q(QGridLayout);
775 
776 #ifndef QT_LAYOUT_DISABLE_CACHING
777     if (!needRecalc)
778         return;
779 #endif
780     has_hfw = false;
781     int i;
782 
783     for (i = 0; i < rr; i++) {
784         rowData[i].init(rStretch.at(i), rMinHeights.at(i));
785         rowData[i].maximumSize = rStretch.at(i) ? QLAYOUTSIZE_MAX : rMinHeights.at(i);
786     }
787     for (i = 0; i < cc; i++) {
788         colData[i].init(cStretch.at(i), cMinWidths.at(i));
789         colData[i].maximumSize = cStretch.at(i) ? QLAYOUTSIZE_MAX : cMinWidths.at(i);
790     }
791 
792     int n = things.size();
793     QVarLengthArray<QGridLayoutSizeTriple> sizes(n);
794 
795     bool has_multi = false;
796 
797     /*
798         Grid of items. We use it to determine which items are
799         adjacent to which and compute the spacings correctly.
800     */
801     QVarLengthArray<QGridBox *> grid(rr * cc);
802     memset(grid.data(), 0, rr * cc * sizeof(QGridBox *));
803 
804     /*
805         Initialize 'sizes' and 'grid' data structures, and insert
806         non-spanning items to our row and column data structures.
807     */
808     for (i = 0; i < n; ++i) {
809         QGridBox * const box = things.at(i);
810         sizes[i].minS = box->minimumSize();
811         sizes[i].hint = box->sizeHint();
812         sizes[i].maxS = box->maximumSize();
813 
814         if (box->hasHeightForWidth())
815             has_hfw = true;
816 
817         if (box->row == box->toRow(rr)) {
818             addData(box, sizes[i], true, false);
819         } else {
820             initEmptyMultiBox(rowData, box->row, box->toRow(rr));
821             has_multi = true;
822         }
823 
824         if (box->col == box->toCol(cc)) {
825             addData(box, sizes[i], false, true);
826         } else {
827             initEmptyMultiBox(colData, box->col, box->toCol(cc));
828             has_multi = true;
829         }
830 
831         for (int r = box->row; r <= box->toRow(rr); ++r) {
832             for (int c = box->col; c <= box->toCol(cc); ++c) {
833                 gridAt(grid.data(), r, c, cc) = box;
834             }
835         }
836     }
837 
838     setupSpacings(colData, grid.data(), hSpacing, Qt::Horizontal);
839     setupSpacings(rowData, grid.data(), vSpacing, Qt::Vertical);
840 
841     /*
842         Insert multicell items to our row and column data structures.
843         This must be done after the non-spanning items to obtain a
844         better distribution in distributeMultiBox().
845     */
846     if (has_multi) {
847         for (i = 0; i < n; ++i) {
848             QGridBox * const box = things.at(i);
849 
850             if (box->row != box->toRow(rr))
851                 distributeMultiBox(rowData, box->row, box->toRow(rr), sizes[i].minS.height(),
852                                    sizes[i].hint.height(), rStretch, box->vStretch());
853             if (box->col != box->toCol(cc))
854                 distributeMultiBox(colData, box->col, box->toCol(cc), sizes[i].minS.width(),
855                                    sizes[i].hint.width(), cStretch, box->hStretch());
856         }
857     }
858 
859     for (i = 0; i < rr; i++)
860         rowData[i].expansive = rowData.at(i).expansive || rowData.at(i).stretch > 0;
861     for (i = 0; i < cc; i++)
862         colData[i].expansive = colData.at(i).expansive || colData.at(i).stretch > 0;
863 
864     q->getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
865 
866     needRecalc = false;
867 }
868 
addHfwData(QGridBox * box,int width)869 void QGridLayoutPrivate::addHfwData(QGridBox *box, int width)
870 {
871     QVector<QLayoutStruct> &rData = *hfwData;
872     if (box->hasHeightForWidth()) {
873         int hint = box->heightForWidth(width);
874         rData[box->row].sizeHint = qMax(hint, rData.at(box->row).sizeHint);
875         rData[box->row].minimumSize = qMax(hint, rData.at(box->row).minimumSize);
876     } else {
877         QSize hint = box->sizeHint();
878         QSize minS = box->minimumSize();
879         rData[box->row].sizeHint = qMax(hint.height(), rData.at(box->row).sizeHint);
880         rData[box->row].minimumSize = qMax(minS.height(), rData.at(box->row).minimumSize);
881     }
882 }
883 
884 /*
885   Similar to setupLayoutData(), but uses heightForWidth(colData)
886   instead of sizeHint(). Assumes that setupLayoutData() and
887   qGeomCalc(colData) has been called.
888 */
setupHfwLayoutData()889 void QGridLayoutPrivate::setupHfwLayoutData()
890 {
891     QVector<QLayoutStruct> &rData = *hfwData;
892     for (int i = 0; i < rr; i++) {
893         rData[i] = rowData.at(i);
894         rData[i].minimumSize = rData[i].sizeHint = rMinHeights.at(i);
895     }
896 
897     for (int pass = 0; pass < 2; ++pass) {
898         for (int i = 0; i < things.size(); ++i) {
899             QGridBox *box = things.at(i);
900             int r1 = box->row;
901             int c1 = box->col;
902             int r2 = box->toRow(rr);
903             int c2 = box->toCol(cc);
904             int w = colData.at(c2).pos + colData.at(c2).size - colData.at(c1).pos;
905 
906             if (r1 == r2) {
907                 if (pass == 0)
908                     addHfwData(box, w);
909             } else {
910                 if (pass == 0) {
911                     initEmptyMultiBox(rData, r1, r2);
912                 } else {
913                     QSize hint = box->sizeHint();
914                     QSize min = box->minimumSize();
915                     if (box->hasHeightForWidth()) {
916                         int hfwh = box->heightForWidth(w);
917                         if (hfwh > hint.height())
918                             hint.setHeight(hfwh);
919                         if (hfwh > min.height())
920                             min.setHeight(hfwh);
921                     }
922                     distributeMultiBox(rData, r1, r2, min.height(), hint.height(),
923                                        rStretch, box->vStretch());
924                 }
925             }
926         }
927     }
928     for (int i = 0; i < rr; i++)
929         rData[i].expansive = rData.at(i).expansive || rData.at(i).stretch > 0;
930 }
931 
distribute(QRect r,int hSpacing,int vSpacing)932 void QGridLayoutPrivate::distribute(QRect r, int hSpacing, int vSpacing)
933 {
934     Q_Q(QGridLayout);
935     bool visualHReversed = hReversed;
936     QWidget *parent = q->parentWidget();
937     if (parent && parent->isRightToLeft())
938         visualHReversed = !visualHReversed;
939 
940     setupLayoutData(hSpacing, vSpacing);
941 
942     int left, top, right, bottom;
943     effectiveMargins(&left, &top, &right, &bottom);
944     r.adjust(+left, +top, -right, -bottom);
945 
946     qGeomCalc(colData, 0, cc, r.x(), r.width());
947     QVector<QLayoutStruct> *rDataPtr;
948     if (has_hfw) {
949         recalcHFW(r.width());
950         qGeomCalc(*hfwData, 0, rr, r.y(), r.height());
951         rDataPtr = hfwData;
952     } else {
953         qGeomCalc(rowData, 0, rr, r.y(), r.height());
954         rDataPtr = &rowData;
955     }
956     QVector<QLayoutStruct> &rData = *rDataPtr;
957     int i;
958 
959     bool reverse = ((r.bottom() > rect.bottom()) || (r.bottom() == rect.bottom()
960                                                      && ((r.right() > rect.right()) != visualHReversed)));
961     int n = things.size();
962     for (i = 0; i < n; ++i) {
963         QGridBox *box = things.at(reverse ? n-i-1 : i);
964         int r2 = box->toRow(rr);
965         int c2 = box->toCol(cc);
966 
967         int x = colData.at(box->col).pos;
968         int y = rData.at(box->row).pos;
969         int x2p = colData.at(c2).pos + colData.at(c2).size; // x2+1
970         int y2p = rData.at(r2).pos + rData.at(r2).size;    // y2+1
971         int w = x2p - x;
972         int h = y2p - y;
973 
974         if (visualHReversed)
975             x = r.left() + r.right() - x - w + 1;
976         if (vReversed)
977             y = r.top() + r.bottom() - y - h + 1;
978 
979         box->setGeometry(QRect(x, y, w, h));
980     }
981 }
982 
cellRect(int row,int col) const983 QRect QGridLayoutPrivate::cellRect(int row, int col) const
984 {
985     if (row < 0 || row >= rr || col < 0 || col >= cc)
986         return QRect();
987 
988     const QVector<QLayoutStruct> *rDataPtr;
989     if (has_hfw && hfwData)
990         rDataPtr = hfwData;
991     else
992         rDataPtr = &rowData;
993     return QRect(colData.at(col).pos, rDataPtr->at(row).pos,
994                  colData.at(col).size, rDataPtr->at(row).size);
995 }
996 
997 /*!
998     \class QGridLayout
999 
1000     \brief The QGridLayout class lays out widgets in a grid.
1001 
1002     \ingroup geomanagement
1003     \inmodule QtWidgets
1004 
1005     QGridLayout takes the space made available to it (by its parent
1006     layout or by the parentWidget()), divides it up into rows and
1007     columns, and puts each widget it manages into the correct cell.
1008 
1009     Columns and rows behave identically; we will discuss columns, but
1010     there are equivalent functions for rows.
1011 
1012     Each column has a minimum width and a stretch factor. The minimum
1013     width is the greatest of that set using setColumnMinimumWidth() and the
1014     minimum width of each widget in that column. The stretch factor is
1015     set using setColumnStretch() and determines how much of the available
1016     space the column will get over and above its necessary minimum.
1017 
1018     Normally, each managed widget or layout is put into a cell of its
1019     own using addWidget(). It is also possible for a widget to occupy
1020     multiple cells using the row and column spanning overloads of
1021     addItem() and addWidget(). If you do this, QGridLayout will guess
1022     how to distribute the size over the columns/rows (based on the
1023     stretch factors).
1024 
1025     To remove a widget from a layout, call removeWidget(). Calling
1026     QWidget::hide() on a widget also effectively removes the widget
1027     from the layout until QWidget::show() is called.
1028 
1029     This illustration shows a fragment of a dialog with a five-column,
1030     three-row grid (the grid is shown overlaid in magenta):
1031 
1032     \image qgridlayout.png A grid layout
1033 
1034     Columns 0, 2 and 4 in this dialog fragment are made up of a
1035     QLabel, a QLineEdit, and a QListBox. Columns 1 and 3 are
1036     placeholders made with setColumnMinimumWidth(). Row 0 consists of three
1037     QLabel objects, row 1 of three QLineEdit objects and row 2 of
1038     three QListBox objects. We used placeholder columns (1 and 3) to
1039     get the right amount of space between the columns.
1040 
1041     Note that the columns and rows are not equally wide or tall. If
1042     you want two columns to have the same width, you must set their
1043     minimum widths and stretch factors to be the same yourself. You do
1044     this using setColumnMinimumWidth() and setColumnStretch().
1045 
1046     If the QGridLayout is not the top-level layout (i.e. does not
1047     manage all of the widget's area and children), you must add it to
1048     its parent layout when you create it, but before you do anything
1049     with it. The normal way to add a layout is by calling
1050     addLayout() on the parent layout.
1051 
1052     Once you have added your layout you can start putting widgets and
1053     other layouts into the cells of your grid layout using
1054     addWidget(), addItem(), and addLayout().
1055 
1056     QGridLayout also includes two margin widths:
1057     the \l{getContentsMargins()}{contents margin} and the spacing().
1058     The contents margin is the width of the reserved space along each
1059     of the QGridLayout's four sides. The spacing() is the width of the
1060     automatically allocated spacing between neighboring boxes.
1061 
1062     The default contents margin values are provided by the
1063     \l{QStyle::pixelMetric()}{style}. The default value Qt styles specify
1064     is 9 for child widgets and 11 for windows. The spacing defaults to the same as
1065     the margin width for a top-level layout, or to the same as the
1066     parent layout.
1067 
1068     \sa QBoxLayout, QStackedLayout, {Layout Management}, {Basic Layouts Example}
1069 */
1070 
1071 
1072 /*!
1073     Constructs a new QGridLayout with parent widget, \a parent.  The
1074     layout has one row and one column initially, and will expand when
1075     new items are inserted.
1076 
1077     The layout is set directly as the top-level layout for \a parent.
1078     There can be only one top-level layout for a widget. It is returned
1079     by QWidget::layout().
1080 
1081     \sa QWidget::setLayout()
1082 */
QGridLayout(QWidget * parent)1083 QGridLayout::QGridLayout(QWidget *parent)
1084     : QLayout(*new QGridLayoutPrivate, nullptr, parent)
1085 {
1086     Q_D(QGridLayout);
1087     d->expand(1, 1);
1088 }
1089 
1090 /*!
1091     Constructs a new grid layout.
1092 
1093     You must insert this grid into another layout. You can insert
1094     widgets and layouts into this layout at any time, but laying out
1095     will not be performed before this is inserted into another layout.
1096 */
QGridLayout()1097 QGridLayout::QGridLayout()
1098     : QLayout(*new QGridLayoutPrivate, nullptr, nullptr)
1099 {
1100     Q_D(QGridLayout);
1101     d->expand(1, 1);
1102 }
1103 
1104 
1105 
1106 
1107 /*!
1108 \internal (mostly)
1109 
1110 Sets the positioning mode used by addItem(). If \a orient is
1111 Qt::Horizontal, this layout is expanded to \a n columns, and items
1112 will be added columns-first. Otherwise it is expanded to \a n rows and
1113 items will be added rows-first.
1114 */
1115 
setDefaultPositioning(int n,Qt::Orientation orient)1116 void QGridLayout::setDefaultPositioning(int n, Qt::Orientation orient)
1117 {
1118     Q_D(QGridLayout);
1119     if (orient == Qt::Horizontal) {
1120         d->expand(1, n);
1121         d->addVertical = false;
1122     } else {
1123         d->expand(n,1);
1124         d->addVertical = true;
1125     }
1126 }
1127 
1128 
1129 /*!
1130     Destroys the grid layout. Geometry management is terminated if
1131     this is a top-level grid.
1132 
1133     The layout's widgets aren't destroyed.
1134 */
~QGridLayout()1135 QGridLayout::~QGridLayout()
1136 {
1137     Q_D(QGridLayout);
1138     d->deleteAll();
1139 }
1140 
1141 /*!
1142     \property QGridLayout::horizontalSpacing
1143     \brief the spacing between widgets that are laid out side by side
1144     \since 4.3
1145 
1146     If no value is explicitly set, the layout's horizontal spacing is
1147     inherited from the parent layout, or from the style settings for
1148     the parent widget.
1149 
1150     \sa verticalSpacing, QStyle::pixelMetric(), {QStyle::}{PM_LayoutHorizontalSpacing}
1151 */
setHorizontalSpacing(int spacing)1152 void QGridLayout::setHorizontalSpacing(int spacing)
1153 {
1154     Q_D(QGridLayout);
1155     d->horizontalSpacing = spacing;
1156     invalidate();
1157 }
1158 
horizontalSpacing() const1159 int QGridLayout::horizontalSpacing() const
1160 {
1161     Q_D(const QGridLayout);
1162     if (d->horizontalSpacing >= 0) {
1163         return d->horizontalSpacing;
1164     } else {
1165         return qSmartSpacing(this, QStyle::PM_LayoutHorizontalSpacing);
1166     }
1167 }
1168 
1169 /*!
1170     \property QGridLayout::verticalSpacing
1171     \brief the spacing between widgets that are laid out on top of each other
1172     \since 4.3
1173 
1174     If no value is explicitly set, the layout's vertical spacing is
1175     inherited from the parent layout, or from the style settings for
1176     the parent widget.
1177 
1178     \sa horizontalSpacing, QStyle::pixelMetric(), {QStyle::}{PM_LayoutHorizontalSpacing}
1179 */
setVerticalSpacing(int spacing)1180 void QGridLayout::setVerticalSpacing(int spacing)
1181 {
1182     Q_D(QGridLayout);
1183     d->verticalSpacing = spacing;
1184     invalidate();
1185 }
1186 
verticalSpacing() const1187 int QGridLayout::verticalSpacing() const
1188 {
1189     Q_D(const QGridLayout);
1190     if (d->verticalSpacing >= 0) {
1191         return d->verticalSpacing;
1192     } else {
1193         return qSmartSpacing(this, QStyle::PM_LayoutVerticalSpacing);
1194     }
1195 }
1196 
1197 /*!
1198     This function sets both the vertical and horizontal spacing to
1199     \a spacing.
1200 
1201     \sa setVerticalSpacing(), setHorizontalSpacing()
1202 */
setSpacing(int spacing)1203 void QGridLayout::setSpacing(int spacing)
1204 {
1205     Q_D(QGridLayout);
1206     d->horizontalSpacing = d->verticalSpacing = spacing;
1207     invalidate();
1208 }
1209 
1210 /*!
1211     If the vertical spacing is equal to the horizontal spacing,
1212     this function returns that value; otherwise it return -1.
1213 
1214     \sa setSpacing(), verticalSpacing(), horizontalSpacing()
1215 */
spacing() const1216 int QGridLayout::spacing() const
1217 {
1218     int hSpacing = horizontalSpacing();
1219     if (hSpacing == verticalSpacing()) {
1220         return hSpacing;
1221     } else {
1222         return -1;
1223     }
1224 }
1225 
1226 /*!
1227     Returns the number of rows in this grid.
1228 */
rowCount() const1229 int QGridLayout::rowCount() const
1230 {
1231     Q_D(const QGridLayout);
1232     return d->numRows();
1233 }
1234 
1235 /*!
1236     Returns the number of columns in this grid.
1237 */
columnCount() const1238 int QGridLayout::columnCount() const
1239 {
1240     Q_D(const QGridLayout);
1241     return d->numCols();
1242 }
1243 
1244 /*!
1245     \reimp
1246 */
sizeHint() const1247 QSize QGridLayout::sizeHint() const
1248 {
1249     Q_D(const QGridLayout);
1250     QSize result(d->sizeHint(horizontalSpacing(), verticalSpacing()));
1251     int left, top, right, bottom;
1252     d->effectiveMargins(&left, &top, &right, &bottom);
1253     result += QSize(left + right, top + bottom);
1254     return result;
1255 }
1256 
1257 /*!
1258     \reimp
1259 */
minimumSize() const1260 QSize QGridLayout::minimumSize() const
1261 {
1262     Q_D(const QGridLayout);
1263     QSize result(d->minimumSize(horizontalSpacing(), verticalSpacing()));
1264     int left, top, right, bottom;
1265     d->effectiveMargins(&left, &top, &right, &bottom);
1266     result += QSize(left + right, top + bottom);
1267     return result;
1268 }
1269 
1270 /*!
1271     \reimp
1272 */
maximumSize() const1273 QSize QGridLayout::maximumSize() const
1274 {
1275     Q_D(const QGridLayout);
1276 
1277     QSize s = d->maximumSize(horizontalSpacing(), verticalSpacing());
1278     int left, top, right, bottom;
1279     d->effectiveMargins(&left, &top, &right, &bottom);
1280     s += QSize(left + right, top + bottom);
1281     s = s.boundedTo(QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX));
1282     if (alignment() & Qt::AlignHorizontal_Mask)
1283         s.setWidth(QLAYOUTSIZE_MAX);
1284     if (alignment() & Qt::AlignVertical_Mask)
1285         s.setHeight(QLAYOUTSIZE_MAX);
1286     return s;
1287 }
1288 
1289 /*!
1290     \reimp
1291 */
hasHeightForWidth() const1292 bool QGridLayout::hasHeightForWidth() const
1293 {
1294     return const_cast<QGridLayout*>(this)->d_func()->hasHeightForWidth(horizontalSpacing(), verticalSpacing());
1295 }
1296 
1297 /*!
1298     \reimp
1299 */
heightForWidth(int w) const1300 int QGridLayout::heightForWidth(int w) const
1301 {
1302     Q_D(const QGridLayout);
1303     QGridLayoutPrivate *dat = const_cast<QGridLayoutPrivate *>(d);
1304     return dat->heightForWidth(w, horizontalSpacing(), verticalSpacing());
1305 }
1306 
1307 /*!
1308     \reimp
1309 */
minimumHeightForWidth(int w) const1310 int QGridLayout::minimumHeightForWidth(int w) const
1311 {
1312     Q_D(const QGridLayout);
1313     QGridLayoutPrivate *dat = const_cast<QGridLayoutPrivate *>(d);
1314     return dat->minimumHeightForWidth(w, horizontalSpacing(), verticalSpacing());
1315 }
1316 
1317 /*!
1318     \reimp
1319 */
count() const1320 int QGridLayout::count() const
1321 {
1322     Q_D(const QGridLayout);
1323     return d->count();
1324 }
1325 
1326 
1327 /*!
1328     \reimp
1329 */
itemAt(int index) const1330 QLayoutItem *QGridLayout::itemAt(int index) const
1331 {
1332     Q_D(const QGridLayout);
1333     return d->itemAt(index);
1334 }
1335 
1336 /*!
1337     \since 4.4
1338 
1339     Returns the layout item that occupies cell (\a row, \a column), or
1340     \nullptr if the cell is empty.
1341 
1342     \sa getItemPosition(), indexOf()
1343 */
itemAtPosition(int row,int column) const1344 QLayoutItem *QGridLayout::itemAtPosition(int row, int column) const
1345 {
1346     Q_D(const QGridLayout);
1347     int n = d->things.count();
1348     for (int i = 0; i < n; ++i) {
1349         QGridBox *box = d->things.at(i);
1350         if (row >= box->row && row <= box->toRow(d->rr)
1351                 && column >= box->col && column <= box->toCol(d->cc)) {
1352             return box->item();
1353         }
1354     }
1355     return nullptr;
1356 }
1357 
1358 /*!
1359     \reimp
1360 */
takeAt(int index)1361 QLayoutItem *QGridLayout::takeAt(int index)
1362 {
1363     Q_D(QGridLayout);
1364     return d->takeAt(index);
1365 }
1366 
1367 /*!
1368   Returns the position information of the item with the given \a index.
1369 
1370   The variables passed as \a row and \a column are updated with the position of the
1371   item in the layout, and the \a rowSpan and \a columnSpan variables are updated
1372   with the vertical and horizontal spans of the item.
1373 
1374   \sa itemAtPosition(), itemAt()
1375 */
getItemPosition(int index,int * row,int * column,int * rowSpan,int * columnSpan) const1376 void QGridLayout::getItemPosition(int index, int *row, int *column, int *rowSpan, int *columnSpan) const
1377 {
1378     Q_D(const QGridLayout);
1379     d->getItemPosition(index, row, column, rowSpan, columnSpan);
1380 }
1381 
1382 
1383 /*!
1384     \reimp
1385 */
setGeometry(const QRect & rect)1386 void QGridLayout::setGeometry(const QRect &rect)
1387 {
1388     Q_D(QGridLayout);
1389     if (d->isDirty() || rect != geometry()) {
1390         QRect cr = alignment() ? alignmentRect(rect) : rect;
1391         d->distribute(cr, horizontalSpacing(), verticalSpacing());
1392         QLayout::setGeometry(rect);
1393     }
1394 }
1395 
1396 /*!
1397     Returns the geometry of the cell with row \a row and column \a column
1398     in the grid. Returns an invalid rectangle if \a row or \a column is
1399     outside the grid.
1400 
1401     \warning in the current version of Qt this function does not
1402     return valid results until setGeometry() has been called, i.e.
1403     after the parentWidget() is visible.
1404 */
cellRect(int row,int column) const1405 QRect QGridLayout::cellRect(int row, int column) const
1406 {
1407     Q_D(const QGridLayout);
1408     return d->cellRect(row, column);
1409 }
1410 
1411 /*!
1412     \reimp
1413 */
addItem(QLayoutItem * item)1414 void QGridLayout::addItem(QLayoutItem *item)
1415 {
1416     Q_D(QGridLayout);
1417     int r, c;
1418     d->getNextPos(r, c);
1419     addItem(item, r, c);
1420 }
1421 
1422 /*!
1423     Adds \a item at position \a row, \a column, spanning \a rowSpan
1424     rows and \a columnSpan columns, and aligns it according to \a
1425     alignment. If \a rowSpan and/or \a columnSpan is -1, then the item
1426     will extend to the bottom and/or right edge, respectively. The
1427     layout takes ownership of the \a item.
1428 
1429     \warning Do not use this function to add child layouts or child
1430     widget items. Use addLayout() or addWidget() instead.
1431 */
addItem(QLayoutItem * item,int row,int column,int rowSpan,int columnSpan,Qt::Alignment alignment)1432 void QGridLayout::addItem(QLayoutItem *item, int row, int column, int rowSpan, int columnSpan, Qt::Alignment alignment)
1433 {
1434     Q_D(QGridLayout);
1435     QGridBox *b = new QGridBox(item);
1436     b->setAlignment(alignment);
1437     d->add(b, row, (rowSpan < 0) ? -1 : row + rowSpan - 1, column, (columnSpan < 0) ? -1 : column + columnSpan - 1);
1438     invalidate();
1439 }
1440 
1441 /*!
1442     Adds the given \a widget to the cell grid at \a row, \a column. The
1443     top-left position is (0, 0) by default.
1444 
1445     The alignment is specified by \a alignment. The default
1446     alignment is 0, which means that the widget fills the entire cell.
1447 
1448 */
addWidget(QWidget * widget,int row,int column,Qt::Alignment alignment)1449 void QGridLayout::addWidget(QWidget *widget, int row, int column, Qt::Alignment alignment)
1450 {
1451     Q_D(QGridLayout);
1452     if (!d->checkWidget(widget))
1453         return;
1454     if (Q_UNLIKELY(row < 0 || column < 0)) {
1455         qWarning("QGridLayout: Cannot add %s/%s to %s/%s at row %d column %d",
1456                  widget->metaObject()->className(), widget->objectName().toLocal8Bit().data(),
1457                  metaObject()->className(), objectName().toLocal8Bit().data(), row, column);
1458         return;
1459     }
1460     addChildWidget(widget);
1461     QWidgetItem *b = QLayoutPrivate::createWidgetItem(this, widget);
1462     addItem(b, row, column, 1, 1, alignment);
1463 }
1464 
1465 /*!
1466     \overload
1467 
1468     This version adds the given \a widget to the cell grid, spanning
1469     multiple rows/columns. The cell will start at \a fromRow, \a
1470     fromColumn spanning \a rowSpan rows and \a columnSpan columns. The
1471     \a widget will have the given \a alignment.
1472 
1473     If \a rowSpan and/or \a columnSpan is -1, then the widget will
1474     extend to the bottom and/or right edge, respectively.
1475 
1476 */
addWidget(QWidget * widget,int fromRow,int fromColumn,int rowSpan,int columnSpan,Qt::Alignment alignment)1477 void QGridLayout::addWidget(QWidget *widget, int fromRow, int fromColumn,
1478                             int rowSpan, int columnSpan, Qt::Alignment alignment)
1479 {
1480     Q_D(QGridLayout);
1481     if (!d->checkWidget(widget))
1482         return;
1483     int toRow = (rowSpan < 0) ? -1 : fromRow + rowSpan - 1;
1484     int toColumn = (columnSpan < 0) ? -1 : fromColumn + columnSpan - 1;
1485     addChildWidget(widget);
1486     QGridBox *b = new QGridBox(this, widget);
1487     b->setAlignment(alignment);
1488     d->add(b, fromRow, toRow, fromColumn, toColumn);
1489     invalidate();
1490 }
1491 
1492 /*!
1493     \fn void QGridLayout::addWidget(QWidget *widget)
1494 
1495     \overload
1496     \internal
1497 */
1498 
1499 /*!
1500     Places the \a layout at position (\a row, \a column) in the grid. The
1501     top-left position is (0, 0).
1502 
1503     The alignment is specified by \a alignment. The default
1504     alignment is 0, which means that the widget fills the entire cell.
1505 
1506     A non-zero alignment indicates that the layout should not grow to
1507     fill the available space but should be sized according to
1508     sizeHint().
1509 
1510 
1511     \a layout becomes a child of the grid layout.
1512 */
addLayout(QLayout * layout,int row,int column,Qt::Alignment alignment)1513 void QGridLayout::addLayout(QLayout *layout, int row, int column, Qt::Alignment alignment)
1514 {
1515     Q_D(QGridLayout);
1516     if (!d->checkLayout(layout))
1517         return;
1518     if (!adoptLayout(layout))
1519         return;
1520     QGridBox *b = new QGridBox(layout);
1521     b->setAlignment(alignment);
1522     d->add(b, row, column);
1523 }
1524 
1525 /*!
1526   \overload
1527     This version adds the layout \a layout to the cell grid, spanning multiple
1528     rows/columns. The cell will start at \a row, \a column spanning \a
1529     rowSpan rows and \a columnSpan columns.
1530 
1531     If \a rowSpan and/or \a columnSpan is -1, then the layout will extend to the bottom
1532     and/or right edge, respectively.
1533 */
addLayout(QLayout * layout,int row,int column,int rowSpan,int columnSpan,Qt::Alignment alignment)1534 void QGridLayout::addLayout(QLayout *layout, int row, int column,
1535                                       int rowSpan, int columnSpan, Qt::Alignment alignment)
1536 {
1537     Q_D(QGridLayout);
1538     if (!d->checkLayout(layout))
1539         return;
1540     if (!adoptLayout(layout))
1541         return;
1542     QGridBox *b = new QGridBox(layout);
1543     b->setAlignment(alignment);
1544     d->add(b, row, (rowSpan < 0) ? -1 : row + rowSpan - 1, column, (columnSpan < 0) ? -1 : column + columnSpan - 1);
1545 }
1546 
1547 /*!
1548     Sets the stretch factor of row \a row to \a stretch. The first row
1549     is number 0.
1550 
1551     The stretch factor is relative to the other rows in this grid.
1552     Rows with a higher stretch factor take more of the available
1553     space.
1554 
1555     The default stretch factor is 0. If the stretch factor is 0 and no
1556     other row in this table can grow at all, the row may still grow.
1557 
1558     \sa rowStretch(), setRowMinimumHeight(), setColumnStretch()
1559 */
setRowStretch(int row,int stretch)1560 void QGridLayout::setRowStretch(int row, int stretch)
1561 {
1562     Q_D(QGridLayout);
1563     d->setRowStretch(row, stretch);
1564     invalidate();
1565 }
1566 
1567 /*!
1568     Returns the stretch factor for row \a row.
1569 
1570     \sa setRowStretch()
1571 */
rowStretch(int row) const1572 int QGridLayout::rowStretch(int row) const
1573 {
1574     Q_D(const QGridLayout);
1575     return d->rowStretch(row);
1576 }
1577 
1578 /*!
1579     Returns the stretch factor for column \a column.
1580 
1581     \sa setColumnStretch()
1582 */
columnStretch(int column) const1583 int QGridLayout::columnStretch(int column) const
1584 {
1585     Q_D(const QGridLayout);
1586     return d->colStretch(column);
1587 }
1588 
1589 /*!
1590     Sets the stretch factor of column \a column to \a stretch. The first
1591     column is number 0.
1592 
1593     The stretch factor is relative to the other columns in this grid.
1594     Columns with a higher stretch factor take more of the available
1595     space.
1596 
1597     The default stretch factor is 0. If the stretch factor is 0 and no
1598     other column in this table can grow at all, the column may still
1599     grow.
1600 
1601     An alternative approach is to add spacing using addItem() with a
1602     QSpacerItem.
1603 
1604     \sa columnStretch(), setRowStretch()
1605 */
setColumnStretch(int column,int stretch)1606 void QGridLayout::setColumnStretch(int column, int stretch)
1607 {
1608     Q_D(QGridLayout);
1609     d->setColStretch(column, stretch);
1610     invalidate();
1611 }
1612 
1613 
1614 
1615 /*!
1616     Sets the minimum height of row \a row to \a minSize pixels.
1617 
1618     \sa rowMinimumHeight(), setColumnMinimumWidth()
1619 */
setRowMinimumHeight(int row,int minSize)1620 void QGridLayout::setRowMinimumHeight(int row, int minSize)
1621 {
1622     Q_D(QGridLayout);
1623     d->setRowMinimumHeight(row, minSize);
1624     invalidate();
1625 }
1626 
1627 /*!
1628     Returns the minimum width set for row \a row.
1629 
1630     \sa setRowMinimumHeight()
1631 */
rowMinimumHeight(int row) const1632 int QGridLayout::rowMinimumHeight(int row) const
1633 {
1634     Q_D(const QGridLayout);
1635     return d->rowSpacing(row);
1636 }
1637 
1638 /*!
1639     Sets the minimum width of column \a column to \a minSize pixels.
1640 
1641     \sa columnMinimumWidth(), setRowMinimumHeight()
1642 */
setColumnMinimumWidth(int column,int minSize)1643 void QGridLayout::setColumnMinimumWidth(int column, int minSize)
1644 {
1645     Q_D(QGridLayout);
1646     d->setColumnMinimumWidth(column, minSize);
1647     invalidate();
1648 }
1649 
1650 /*!
1651     Returns the column spacing for column \a column.
1652 
1653     \sa setColumnMinimumWidth()
1654 */
columnMinimumWidth(int column) const1655 int QGridLayout::columnMinimumWidth(int column) const
1656 {
1657     Q_D(const QGridLayout);
1658     return d->colSpacing(column);
1659 }
1660 
1661 /*!
1662     \reimp
1663 */
expandingDirections() const1664 Qt::Orientations QGridLayout::expandingDirections() const
1665 {
1666     Q_D(const QGridLayout);
1667     return d->expandingDirections(horizontalSpacing(), verticalSpacing());
1668 }
1669 
1670 /*!
1671     Sets the grid's origin corner, i.e. position (0, 0), to \a corner.
1672 */
setOriginCorner(Qt::Corner corner)1673 void QGridLayout::setOriginCorner(Qt::Corner corner)
1674 {
1675     Q_D(QGridLayout);
1676     d->setReversed(corner == Qt::BottomLeftCorner || corner == Qt::BottomRightCorner,
1677                    corner == Qt::TopRightCorner || corner == Qt::BottomRightCorner);
1678 }
1679 
1680 /*!
1681     Returns the corner that's used for the grid's origin, i.e. for
1682     position (0, 0).
1683 */
originCorner() const1684 Qt::Corner QGridLayout::originCorner() const
1685 {
1686     Q_D(const QGridLayout);
1687     if (d->horReversed()) {
1688         return d->verReversed() ? Qt::BottomRightCorner : Qt::TopRightCorner;
1689     } else {
1690         return d->verReversed() ? Qt::BottomLeftCorner : Qt::TopLeftCorner;
1691     }
1692 }
1693 
1694 /*!
1695     \reimp
1696 */
invalidate()1697 void QGridLayout::invalidate()
1698 {
1699     Q_D(QGridLayout);
1700     d->setDirty();
1701     QLayout::invalidate();
1702 }
1703 
1704 QT_END_NAMESPACE
1705 
1706 #include "moc_qgridlayout.cpp"
1707