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 "qapplication.h"
41 #include "qdebug.h"
42 #include "qformlayout.h"
43 #include "qlabel.h"
44 #include "qlayout_p.h"
45 #include "qlayoutengine_p.h"
46 #include "qrect.h"
47 #include "qvector.h"
48 #include "qwidget.h"
49 
50 QT_BEGIN_NAMESPACE
51 
52 namespace {
53 // Fixed column matrix, stores items as [i11, i12, i21, i22...],
54 // with FORTRAN-style index operator(r, c).
55 template <class T, int NumColumns>
56 class FixedColumnMatrix {
57 public:
58     typedef QVector<T> Storage;
59 
FixedColumnMatrix()60     FixedColumnMatrix() { }
61 
clear()62     void clear() { m_storage.clear(); }
63 
operator ()(int r,int c) const64     const T &operator()(int r, int c) const { return m_storage[r * NumColumns + c]; }
operator ()(int r,int c)65     T &operator()(int r, int c) { return m_storage[r * NumColumns + c]; }
66 
rowCount() const67     int rowCount() const { return m_storage.size() / NumColumns; }
68     void insertRow(int r, const T &value);
69     void removeRow(int r);
70 
71     // Hmmpf.. Some things are faster that way.
storage() const72     const Storage &storage() const { return m_storage; }
73 
74     static void storageIndexToPosition(int idx, int *rowPtr, int *colPtr);
75 
76 private:
77     Storage m_storage;
78 };
79 
80 template <class T, int NumColumns>
insertRow(int r,const T & value)81 void FixedColumnMatrix<T, NumColumns>::insertRow(int r, const T &value)
82 {
83     typename Storage::iterator it = m_storage.begin();
84     it += r * NumColumns;
85     m_storage.insert(it, NumColumns, value);
86 }
87 
88 template <class T, int NumColumns>
removeRow(int r)89 void FixedColumnMatrix<T, NumColumns>::removeRow(int r)
90 {
91     m_storage.remove(r * NumColumns, NumColumns);
92 }
93 
94 template <class T, int NumColumns>
storageIndexToPosition(int idx,int * rowPtr,int * colPtr)95 void FixedColumnMatrix<T, NumColumns>::storageIndexToPosition(int idx, int *rowPtr, int *colPtr)
96 {
97     *rowPtr = idx / NumColumns;
98     *colPtr = idx % NumColumns;
99 }
100 } // namespace
101 
102 // special values for unset fields; must not clash with values of FieldGrowthPolicy or
103 // RowWrapPolicy
104 const uint DefaultFieldGrowthPolicy = 255;
105 const uint DefaultRowWrapPolicy = 255;
106 
107 enum { ColumnCount = 2 };
108 
109 // -- our data structure for our items
110 // This owns the QLayoutItem
111 struct QFormLayoutItem
112 {
QFormLayoutItemQFormLayoutItem113     QFormLayoutItem(QLayoutItem* i) : item(i), fullRow(false), isHfw(false) { }
~QFormLayoutItemQFormLayoutItem114     ~QFormLayoutItem() { delete item; }
115 
116     // Wrappers
widgetQFormLayoutItem117     QWidget *widget() const { return item->widget(); }
layoutQFormLayoutItem118     QLayout *layout() const { return item->layout(); }
119 
hasHeightForWidthQFormLayoutItem120     bool hasHeightForWidth() const { return item->hasHeightForWidth(); }
heightForWidthQFormLayoutItem121     int heightForWidth(int width) const { return item->heightForWidth(width); }
minimumHeightForWidthQFormLayoutItem122     int minimumHeightForWidth(int width) const { return item->minimumHeightForWidth(width); }
expandingDirectionsQFormLayoutItem123     Qt::Orientations expandingDirections() const { return item->expandingDirections(); }
controlTypesQFormLayoutItem124     QSizePolicy::ControlTypes controlTypes() const { return item->controlTypes(); }
vStretchQFormLayoutItem125     int vStretch() const { return widget() ? widget()->sizePolicy().verticalStretch() : 0; }
126 
setGeometryQFormLayoutItem127     void setGeometry(const QRect& r) { item->setGeometry(r); }
geometryQFormLayoutItem128     QRect geometry() const { return item->geometry(); }
129 
130     // For use with FixedColumnMatrix
operator ==QFormLayoutItem131     bool operator==(const QFormLayoutItem& other) { return item == other.item; }
132 
133     QLayoutItem *item;
134     bool fullRow;
135 
136     // set by updateSizes
137     bool isHfw;
138     QSize minSize;
139     QSize sizeHint;
140     QSize maxSize;
141 
142     // also set by updateSizes
143     int sbsHSpace; // only used for side by side, for the field item only (not label)
144     int vSpace; // This is the spacing to the item in the row above
145 
146     // set by setupVerticalLayoutData
147     bool sideBySide;
148     int vLayoutIndex;
149 
150     // set by setupHorizontalLayoutData
151     int layoutPos;
152     int layoutWidth;
153 };
154 
155 class QFormLayoutPrivate : public QLayoutPrivate
156 {
157     Q_DECLARE_PUBLIC(QFormLayout)
158 
159 public:
160     typedef FixedColumnMatrix<QFormLayoutItem *, ColumnCount> ItemMatrix;
161 
162     QFormLayoutPrivate();
~QFormLayoutPrivate()163     ~QFormLayoutPrivate() { }
164 
165     int insertRow(int row);
166     void insertRows(int row, int count);
167     void removeRow(int row);
168     bool setItem(int row, QFormLayout::ItemRole role, QLayoutItem *item);
169     void setLayout(int row, QFormLayout::ItemRole role, QLayout *layout);
170     void setWidget(int row, QFormLayout::ItemRole role, QWidget *widget);
171 
172     void arrangeWidgets(const QVector<QLayoutStruct>& layouts, QRect &rect);
173 
174     void updateSizes();
175 
176     void setupVerticalLayoutData(int width);
177     void setupHorizontalLayoutData(int width);
178 
179     QStyle* getStyle() const;
180 
haveHfwCached(int width) const181     inline bool haveHfwCached(int width) const
182     {
183         return (hfw_width == width) || (width == sh_width && hfw_sh_height >= 0);
184     }
185 
186     void recalcHFW(int w);
187     void setupHfwLayoutData();
188 
189     uint fieldGrowthPolicy : 8;
190     uint rowWrapPolicy : 8;
191     uint has_hfw : 2;
192     uint dirty : 2; // have we laid out yet?
193     uint sizesDirty : 2; // have we (not) gathered layout item sizes?
194     uint expandVertical : 1; // Do we expand vertically?
195     uint expandHorizontal : 1; // Do we expand horizonally?
196     Qt::Alignment labelAlignment;
197     Qt::Alignment formAlignment;
198 
199     ItemMatrix m_matrix;
200     QList<QFormLayoutItem *> m_things;
201 
202     int layoutWidth = -1;    // the last width that we called setupVerticalLayoutData on (for vLayouts)
203 
204     int hfw_width = -1;  // the last width we calculated HFW for
205     int hfw_height = -1; // what that height was
206 
207     int hfw_sh_height = -1;  // the hfw for sh_width
208     int hfw_sh_minheight = -1; // the minhfw for sh_width
209 
210     int min_width = -1;  // the width that gets turned into minSize (from updateSizes)
211     int sh_width = -1;   // the width that gets turned into prefSize (from updateSizes)
212     int thresh_width = QLAYOUTSIZE_MAX; // the width that we start splitting label/field pairs at (from updateSizes)
213     QSize minSize;
214     QSize prefSize;
215     int formMaxWidth;
216     void calcSizeHints();
217 
218     QVector<QLayoutStruct> vLayouts; // set by setupVerticalLayoutData;
219     int vLayoutCount;               // Number of rows we calculated in setupVerticalLayoutData
220     int maxLabelWidth;              // the label width we calculated in setupVerticalLayoutData
221 
222     QVector<QLayoutStruct> hfwLayouts;
223 
224     int hSpacing = -1;
225     int vSpacing = -1;
226     QLayoutItem* replaceAt(int index, QLayoutItem*) override;
227 };
228 
QFormLayoutPrivate()229 QFormLayoutPrivate::QFormLayoutPrivate()
230     : fieldGrowthPolicy(DefaultFieldGrowthPolicy),
231       rowWrapPolicy(DefaultRowWrapPolicy), has_hfw(false), dirty(true), sizesDirty(true),
232       expandVertical(0), expandHorizontal(0)
233 {
234 }
235 
fixedAlignment(Qt::Alignment alignment,Qt::LayoutDirection layoutDirection)236 static Qt::Alignment fixedAlignment(Qt::Alignment alignment, Qt::LayoutDirection layoutDirection)
237 {
238     if (layoutDirection == Qt::RightToLeft && alignment & Qt::AlignAbsolute) {
239         // swap left and right, and eliminate absolute flag
240         return Qt::Alignment((alignment & ~(Qt::AlignLeft | Qt::AlignRight | Qt::AlignAbsolute))
241                              | ((alignment & Qt::AlignRight) ? Qt::AlignLeft : 0)
242                              | ((alignment & Qt::AlignLeft) ? Qt::AlignRight : 0));
243     } else {
244         return alignment & ~Qt::AlignAbsolute;
245     }
246 }
247 
storageIndexFromLayoutItem(const QFormLayoutPrivate::ItemMatrix & m,QFormLayoutItem * item)248 static int storageIndexFromLayoutItem(const QFormLayoutPrivate::ItemMatrix &m,
249                                       QFormLayoutItem *item)
250 {
251     if (item) {
252         return m.storage().indexOf(item);
253     } else {
254         return -1;
255     }
256 }
257 
updateFormLayoutItem(QFormLayoutItem * item,int userVSpacing,QFormLayout::FieldGrowthPolicy fieldGrowthPolicy,bool fullRow)258 static void updateFormLayoutItem(QFormLayoutItem *item, int userVSpacing,
259                                         QFormLayout::FieldGrowthPolicy fieldGrowthPolicy,
260                                         bool fullRow)
261 {
262     item->minSize = item->item->minimumSize();
263     item->sizeHint = item->item->sizeHint();
264     item->maxSize = item->item->maximumSize();
265 
266     if (!fullRow && (fieldGrowthPolicy == QFormLayout::FieldsStayAtSizeHint
267                      || (fieldGrowthPolicy == QFormLayout::ExpandingFieldsGrow
268                          && !(item->item->expandingDirections() & Qt::Horizontal))))
269         item->maxSize.setWidth(item->sizeHint.width());
270 
271     item->isHfw = item->item->hasHeightForWidth();
272     item->vSpace = userVSpacing;
273 }
274 
275 /*
276    Iterate over all the controls and gather their size information
277    (min, sizeHint and max). Also work out what the spacing between
278    pairs of controls should be, and figure out the min and sizeHint
279    widths.
280 */
updateSizes()281 void QFormLayoutPrivate::updateSizes()
282 {
283     Q_Q(QFormLayout);
284 
285     if (sizesDirty) {
286         QFormLayout::RowWrapPolicy wrapPolicy = q->rowWrapPolicy();
287         bool wrapAllRows = (wrapPolicy == QFormLayout::WrapAllRows);
288         bool dontWrapRows = (wrapPolicy == QFormLayout::DontWrapRows);
289         int rr = m_matrix.rowCount();
290 
291         has_hfw = false;
292 
293         // If any control can expand, so can this layout
294         // Wrapping doesn't affect expansion, though, just the minsize
295         bool expandH = false;
296         bool expandV = false;
297 
298         QFormLayoutItem *prevLbl = nullptr;
299         QFormLayoutItem *prevFld = nullptr;
300 
301         QWidget *parent = q->parentWidget();
302         QStyle *style = parent ? parent->style() : nullptr;
303 
304         int userVSpacing = q->verticalSpacing();
305         int userHSpacing = wrapAllRows ? 0 : q->horizontalSpacing();
306 
307         int maxMinLblWidth = 0;
308         int maxMinFldWidth = 0; // field with label
309         int maxMinIfldWidth = 0; // independent field
310 
311         int maxShLblWidth = 0;
312         int maxShFldWidth = 0;
313         int maxShIfldWidth = 0;
314 
315         for (int i = 0; i < rr; ++i) {
316             QFormLayoutItem *label = m_matrix(i, 0);
317             QFormLayoutItem *field = m_matrix(i, 1);
318 
319             // Skip empty rows
320             if (!label && !field)
321                 continue;
322 
323             if (label) {
324                 updateFormLayoutItem(label, userVSpacing, q->fieldGrowthPolicy(), false);
325                 if (label->isHfw)
326                     has_hfw = true;
327                 Qt::Orientations o = label->expandingDirections();
328 
329                 if (o & Qt::Vertical)
330                     expandV = true;
331                 if (o & Qt::Horizontal)
332                     expandH = true;
333             }
334             if (field) {
335                 updateFormLayoutItem(field, userVSpacing, q->fieldGrowthPolicy(), !label && field->fullRow);
336                 field->sbsHSpace = (!label && field->fullRow) ? 0 : userHSpacing;
337                 if (field->isHfw)
338                     has_hfw = true;
339 
340                 Qt::Orientations o = field->expandingDirections();
341 
342                 if (o & Qt::Vertical)
343                     expandV = true;
344                 if (o & Qt::Horizontal)
345                     expandH = true;
346             }
347 
348             // See if we need to calculate default spacings
349             if ((userHSpacing < 0 || userVSpacing < 0) && style) {
350                 QSizePolicy::ControlTypes lbltypes =
351                     QSizePolicy::ControlTypes(label ? label->controlTypes() : QSizePolicy::DefaultType);
352                 QSizePolicy::ControlTypes fldtypes =
353                     QSizePolicy::ControlTypes(field ? field->controlTypes() : QSizePolicy::DefaultType);
354 
355                 // VSpacing
356                 if (userVSpacing < 0) {
357                     if (wrapAllRows) {
358                         // label spacing is to a previous item
359                         QFormLayoutItem *lbltop = prevFld ? prevFld : prevLbl;
360                         // field spacing is to the label (or a previous item)
361                         QFormLayoutItem *fldtop = label ? label : lbltop;
362                         QSizePolicy::ControlTypes lbltoptypes =
363                             QSizePolicy::ControlTypes(lbltop ? lbltop->controlTypes() : QSizePolicy::DefaultType);
364                         QSizePolicy::ControlTypes fldtoptypes =
365                             QSizePolicy::ControlTypes(fldtop ? fldtop->controlTypes() : QSizePolicy::DefaultType);
366                         if (label && lbltop)
367                             label->vSpace = style->combinedLayoutSpacing(lbltoptypes, lbltypes, Qt::Vertical, nullptr, parent);
368                         if (field && fldtop)
369                             field->vSpace = style->combinedLayoutSpacing(fldtoptypes, fldtypes, Qt::Vertical, nullptr, parent);
370                     } else {
371                         // Side by side..  we have to also consider the spacings to empty cells, which can strangely be more than
372                         // non empty cells..
373                         QFormLayoutItem *lbltop = prevLbl ? prevLbl : prevFld;
374                         QFormLayoutItem *fldtop = prevFld;
375                         QSizePolicy::ControlTypes lbltoptypes =
376                             QSizePolicy::ControlTypes(lbltop ? lbltop->controlTypes() : QSizePolicy::DefaultType);
377                         QSizePolicy::ControlTypes fldtoptypes =
378                             QSizePolicy::ControlTypes(fldtop ? fldtop->controlTypes() : QSizePolicy::DefaultType);
379 
380                         // To be compatible to QGridLayout, we have to compare solitary labels & fields with both predecessors
381                         if (label) {
382                             if (!field) {
383                                 int lblspacing = style->combinedLayoutSpacing(lbltoptypes, lbltypes, Qt::Vertical, nullptr, parent);
384                                 int fldspacing = style->combinedLayoutSpacing(fldtoptypes, lbltypes, Qt::Vertical, nullptr, parent);
385                                 label->vSpace = qMax(lblspacing, fldspacing);
386                             } else
387                                 label->vSpace = style->combinedLayoutSpacing(lbltoptypes, lbltypes, Qt::Vertical, nullptr, parent);
388                         }
389 
390                         if (field) {
391                             // check spacing against both the previous label and field
392                             if (!label) {
393                                 int lblspacing = style->combinedLayoutSpacing(lbltoptypes, fldtypes, Qt::Vertical, nullptr, parent);
394                                 int fldspacing = style->combinedLayoutSpacing(fldtoptypes, fldtypes, Qt::Vertical, nullptr, parent);
395                                 field->vSpace = qMax(lblspacing, fldspacing);
396                             } else
397                                 field->vSpace = style->combinedLayoutSpacing(fldtoptypes, fldtypes, Qt::Vertical, nullptr, parent);
398                         }
399                     }
400                 }
401 
402                 // HSpacing
403                 // hard-coded the left and right control types so that all the rows have the same
404                 // inter-column spacing (otherwise the right column isn't always left aligned)
405                 if (userHSpacing < 0 && !wrapAllRows && (label || !field->fullRow) && field)
406                     field->sbsHSpace = style->combinedLayoutSpacing(QSizePolicy::Label, QSizePolicy::LineEdit, Qt::Horizontal, nullptr, parent);
407             }
408 
409             // Now update our min/sizehint widths
410             // We choose to put the spacing in the field side in sbs, so
411             // the right edge of the labels will align, but fields may
412             // be a little ragged.. since different controls may have
413             // different appearances, a slight raggedness in the left
414             // edges of fields can be tolerated.
415             // (Note - field->sbsHSpace is 0 for WrapAllRows mode)
416             if (label) {
417                 maxMinLblWidth = qMax(maxMinLblWidth, label->minSize.width());
418                 maxShLblWidth = qMax(maxShLblWidth, label->sizeHint.width());
419             }
420             if (field) {
421                 if (field->fullRow) {
422                     maxMinIfldWidth = qMax(maxMinIfldWidth, field->minSize.width());
423                     maxShIfldWidth = qMax(maxShIfldWidth, field->sizeHint.width());
424                 } else {
425                     maxMinFldWidth = qMax(maxMinFldWidth, field->minSize.width() + field->sbsHSpace);
426                     maxShFldWidth = qMax(maxShFldWidth, field->sizeHint.width() + field->sbsHSpace);
427                 }
428             }
429 
430             prevLbl = label;
431             prevFld = field;
432         }
433 
434         // Now, finally update the min/sizeHint widths
435         if (wrapAllRows) {
436             sh_width = qMax(maxShLblWidth, qMax(maxShIfldWidth, maxShFldWidth));
437             min_width = qMax(maxMinLblWidth, qMax(maxMinIfldWidth, maxMinFldWidth));
438             // in two line, we don't care as much about the threshold width
439             thresh_width = 0;
440         } else if (dontWrapRows) {
441             // This is just the max widths glommed together
442             sh_width = qMax(maxShLblWidth + maxShFldWidth, maxShIfldWidth);
443             min_width = qMax(maxMinLblWidth + maxMinFldWidth, maxMinIfldWidth);
444             thresh_width = QWIDGETSIZE_MAX;
445         } else {
446             // This is just the max widths glommed together
447             sh_width = qMax(maxShLblWidth + maxShFldWidth, maxShIfldWidth);
448             // min width needs to be the min when everything is wrapped,
449             // otherwise we'll never get set with a width that causes wrapping
450             min_width = qMax(maxMinLblWidth, qMax(maxMinIfldWidth, maxMinFldWidth));
451             // We split a pair at label sh + field min (### for now..)
452             thresh_width = maxShLblWidth + maxMinFldWidth;
453         }
454 
455         // Update the expansions
456         expandVertical = expandV;
457         expandHorizontal = expandH;
458     }
459     sizesDirty = false;
460 }
461 
recalcHFW(int w)462 void QFormLayoutPrivate::recalcHFW(int w)
463 {
464     setupHfwLayoutData();
465 
466     int h = 0;
467     int mh = 0;
468 
469     for (int r = 0; r < vLayoutCount; ++r) {
470         int spacing = hfwLayouts.at(r).spacing;
471         h += hfwLayouts.at(r).sizeHint + spacing;
472         mh += hfwLayouts.at(r).minimumSize + spacing;
473     }
474 
475     if (sh_width > 0 && sh_width == w) {
476         hfw_sh_height = qMin(QLAYOUTSIZE_MAX, h);
477         hfw_sh_minheight = qMin(QLAYOUTSIZE_MAX, mh);
478     } else {
479         hfw_width = w;
480         hfw_height = qMin(QLAYOUTSIZE_MAX, h);
481     }
482 }
483 
setupHfwLayoutData()484 void QFormLayoutPrivate::setupHfwLayoutData()
485 {
486     // setupVerticalLayoutData must be called before this
487     // setupHorizontalLayoutData must also be called before this
488     // copies non hfw data into hfw
489     // then updates size and min
490 
491 
492     // Note: QGridLayout doesn't call minimumHeightForWidth,
493     // but instead uses heightForWidth for both min and sizeHint.
494     // For the common case where minimumHeightForWidth just calls
495     // heightForWidth, we do the calculation twice, which can be
496     // very expensive for word wrapped QLabels/QTextEdits, for example.
497     // So we just use heightForWidth as well.
498     int i;
499     int rr = m_matrix.rowCount();
500 
501     hfwLayouts.clear();
502     hfwLayouts.resize(vLayoutCount);
503     for (i = 0; i < vLayoutCount; ++i)
504         hfwLayouts[i] = vLayouts.at(i);
505 
506     for (i = 0; i < rr; ++i) {
507         QFormLayoutItem *label = m_matrix(i, 0);
508         QFormLayoutItem *field = m_matrix(i, 1);
509 
510         if (label) {
511             if (label->isHfw) {
512                 // We don't check sideBySide here, since a label is only
513                 // ever side by side with its field
514                 int hfw = label->heightForWidth(label->layoutWidth);
515                 hfwLayouts[label->vLayoutIndex].minimumSize = hfw;
516                 hfwLayouts[label->vLayoutIndex].sizeHint = hfw;
517             } else {
518                 // Reset these here, so the field can do a qMax below (the previous value may have
519                 // been the fields non-hfw values, which are often larger than hfw)
520                 hfwLayouts[label->vLayoutIndex].sizeHint = label->sizeHint.height();
521                 hfwLayouts[label->vLayoutIndex].minimumSize = label->minSize.height();
522             }
523         }
524 
525         if (field) {
526             int hfw = field->isHfw ? field->heightForWidth(field->layoutWidth) : 0;
527             int h = field->isHfw ? hfw : field->sizeHint.height();
528             int mh = field->isHfw ? hfw : field->minSize.height();
529 
530             if (field->sideBySide) {
531                 int oh = hfwLayouts.at(field->vLayoutIndex).sizeHint;
532                 int omh = hfwLayouts.at(field->vLayoutIndex).minimumSize;
533 
534                 hfwLayouts[field->vLayoutIndex].sizeHint = qMax(h, oh);
535                 hfwLayouts[field->vLayoutIndex].minimumSize = qMax(mh, omh);
536             } else {
537                 hfwLayouts[field->vLayoutIndex].sizeHint = h;
538                 hfwLayouts[field->vLayoutIndex].minimumSize = mh;
539             }
540         }
541     }
542 }
543 
544 /*
545   Given up to four items involved in a vertical spacing calculation
546   (two rows * two columns), return the max vertical spacing for the
547   row containing item1 (which may also include item2)
548   We assume parent and item1 are not null.
549 
550   If a particular row is split, then the spacings for that row and
551   the following row are affected, and this function should be
552   called with recalculate = true for both rows (note: only rows with both
553   a label and a field can be split).
554 
555   In particular:
556 
557   1) the split label's row vspace needs to be changed to qMax(label/prevLabel, label/prevField)
558     [call with item1 = label, item2 = null, prevItem1 & prevItem2 as before]
559   2) the split field's row vspace needs to be changed to the label/field spacing
560     [call with item1 = field, item2 = null, prevItem1 = label, prevItem2 = null]
561 
562  [if the next row has one item, 'item']
563   3a) the following row's vspace needs to be changed to item/field spacing (would
564       previously been the qMax(item/label, item/field) spacings)
565     [call with item1 = item, item2 = null, prevItem1 = field, prevItem2 = null]
566 
567   [if the next row has two items, 'label2' and 'field2']
568   3b) the following row's vspace needs to be changed to be qMax(field/label2, field/field2) spacing
569     [call with item1 = label2, item2 = field2, prevItem1 = field, prevItem2 = null]
570 
571   In the (common) non split case, we can just use the precalculated vspace (possibly qMaxed between
572   label and field).
573 
574   If recalculate is true, we expect:
575   -  parent != null
576   -  item1 != null
577   -  item2 can be null
578   -  prevItem1 can be null
579   -  if item2 is not null, prevItem2 will be null (e.g. steps 1 or 3 above)
580   -  if prevItem1 is null, prevItem2 will be null
581 */
spacingHelper(QWidget * parent,QStyle * style,int userVSpacing,bool recalculate,QFormLayoutItem * item1,QFormLayoutItem * item2,QFormLayoutItem * prevItem1,QFormLayoutItem * prevItem2)582 static inline int spacingHelper(QWidget* parent, QStyle *style, int userVSpacing, bool recalculate, QFormLayoutItem* item1, QFormLayoutItem* item2, QFormLayoutItem* prevItem1, QFormLayoutItem *prevItem2)
583 {
584     int spacing = userVSpacing;
585     if (spacing < 0) {
586         if (!recalculate) {
587             if (item1)
588                 spacing = item1->vSpace;
589             if (item2)
590                 spacing = qMax(spacing, item2->vSpace);
591         } else {
592             if (style && prevItem1) {
593                 QSizePolicy::ControlTypes itemtypes =
594                     QSizePolicy::ControlTypes(item1 ? item1->controlTypes() : QSizePolicy::DefaultType);
595                 int spacing2 = 0;
596 
597                 spacing = style->combinedLayoutSpacing(itemtypes, prevItem1->controlTypes(), Qt::Vertical, nullptr, parent);
598 
599                 // At most of one of item2 and prevItem2 will be nonnull
600                 if (item2)
601                     spacing2 = style->combinedLayoutSpacing(item2->controlTypes(), prevItem1->controlTypes(), Qt::Vertical, nullptr, parent);
602                 else if (prevItem2)
603                     spacing2 = style->combinedLayoutSpacing(itemtypes, prevItem2->controlTypes(), Qt::Vertical, nullptr, parent);
604 
605                 spacing = qMax(spacing, spacing2);
606             }
607         }
608     } else {
609         if (prevItem1) {
610             QWidget *wid = prevItem1->item->widget();
611             if (wid)
612                 spacing = qMax(spacing, prevItem1->geometry().top() - wid->geometry().top() );
613         }
614         if (prevItem2) {
615             QWidget *wid = prevItem2->item->widget();
616             if (wid)
617                 spacing = qMax(spacing, prevItem2->geometry().top() - wid->geometry().top() );
618         }
619     }
620     return qMax(spacing, 0);
621 }
622 
initLayoutStruct(QLayoutStruct & sl,QFormLayoutItem * item)623 static inline void initLayoutStruct(QLayoutStruct& sl, QFormLayoutItem* item)
624 {
625     sl.init(item->vStretch(), item->minSize.height());
626     sl.sizeHint = item->sizeHint.height();
627     sl.maximumSize = item->maxSize.height();
628     sl.expansive = (item->expandingDirections() & Qt::Vertical);
629     sl.empty = false;
630 }
631 
setupVerticalLayoutData(int width)632 void QFormLayoutPrivate::setupVerticalLayoutData(int width)
633 {
634     Q_Q(QFormLayout);
635 
636     // Early out if we have no changes that would cause a change in vertical layout
637     if ((width == layoutWidth || (width >= thresh_width && layoutWidth >= thresh_width)) && !dirty && !sizesDirty)
638         return;
639 
640     layoutWidth = width;
641 
642     int rr = m_matrix.rowCount();
643     int vidx = 1;
644     QFormLayout::RowWrapPolicy rowWrapPolicy = q->rowWrapPolicy();
645     bool wrapAllRows = (rowWrapPolicy == QFormLayout::WrapAllRows);
646     bool addTopBottomStretch = true;
647 
648     vLayouts.clear();
649     vLayouts.resize((2 * rr) + 2); // a max, some may be unused
650 
651     QStyle *style = nullptr;
652 
653     int userVSpacing = q->verticalSpacing();
654 
655     if (userVSpacing < 0) {
656         if (QWidget *widget = q->parentWidget())
657             style = widget->style();
658     }
659 
660     // make sure our sizes are up to date
661     updateSizes();
662 
663     // Grab the widest label width here
664     // This might be different from the value computed during
665     // sizeHint/minSize, since we don't count label/field pairs that
666     // are split.
667     maxLabelWidth = 0;
668     if (!wrapAllRows) {
669         for (int i = 0; i < rr; ++i) {
670             const QFormLayoutItem *label = m_matrix(i, 0);
671             const QFormLayoutItem *field = m_matrix(i, 1);
672             if (label && (label->sizeHint.width() + (field ? field->minSize.width() : 0) <= width))
673                 maxLabelWidth = qMax(maxLabelWidth, label->sizeHint.width());
674         }
675     } else {
676         maxLabelWidth = width;
677     }
678 
679     QFormLayoutItem *prevItem1 = nullptr;
680     QFormLayoutItem *prevItem2 = nullptr;
681     bool prevRowSplit = false;
682 
683     for (int i = 0; i < rr; ++i) {
684         QFormLayoutItem *label =  m_matrix(i, 0);
685         QFormLayoutItem *field = m_matrix(i, 1);
686 
687         // Totally ignore empty rows...
688         if (!label && !field)
689             continue;
690 
691         QSize min1;
692         QSize min2;
693         QSize sh1;
694         QSize sh2;
695         if (label) {
696             min1 = label->minSize;
697             sh1 = label->sizeHint;
698         }
699         if (field) {
700             min2 = field->minSize;
701             sh2 = field->sizeHint;
702         }
703 
704         // In separate lines, we make a vLayout for everything that isn't null
705         // in side by side, we only separate label/field if we're going to wrap it
706         bool splitSideBySide = (rowWrapPolicy == QFormLayout::WrapLongRows)
707                                 && ((maxLabelWidth < sh1.width()) || (width < (maxLabelWidth + min2.width())));
708 
709         if (wrapAllRows || splitSideBySide) {
710             if (label) {
711                 initLayoutStruct(vLayouts[vidx], label);
712 
713                 if (vidx > 1)
714                     vLayouts[vidx - 1].spacing = spacingHelper(q->parentWidget(), style, userVSpacing, splitSideBySide || prevRowSplit, label, nullptr, prevItem1, prevItem2);
715 
716                 label->vLayoutIndex = vidx;
717                 label->sideBySide = false;
718 
719                 prevItem1 = label;
720                 prevItem2 = nullptr;
721 
722                 if (vLayouts[vidx].stretch > 0)
723                     addTopBottomStretch = false;
724 
725                 ++vidx;
726             }
727 
728             if (field) {
729                 initLayoutStruct(vLayouts[vidx], field);
730 
731                 if (vidx > 1)
732                     vLayouts[vidx - 1].spacing = spacingHelper(q->parentWidget(), style, userVSpacing, splitSideBySide || prevRowSplit, field, nullptr, prevItem1, prevItem2);
733 
734                 field->vLayoutIndex = vidx;
735                 field->sideBySide = false;
736 
737                 prevItem1 = field;
738                 prevItem2 = nullptr;
739 
740                 if (vLayouts[vidx].stretch > 0)
741                     addTopBottomStretch = false;
742 
743                 ++vidx;
744             }
745 
746             prevRowSplit = splitSideBySide;
747         } else {
748             // we're in side by side mode, and we have enough space to do that
749             QSize max1(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
750             QSize max2(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
751 
752             int stretch1 = 0;
753             int stretch2 = 0;
754             bool expanding = false;
755 
756             if (label) {
757                 max1 = label->maxSize;
758                 if (label->expandingDirections() & Qt::Vertical)
759                     expanding = true;
760 
761                 label->sideBySide = (field != nullptr);
762                 label->vLayoutIndex = vidx;
763                 stretch1 = label->vStretch();
764             }
765 
766             if (field) {
767                 max2 = field->maxSize;
768                 if (field->expandingDirections() & Qt::Vertical)
769                     expanding = true;
770 
771                 field->sideBySide = (label || !field->fullRow);
772                 field->vLayoutIndex = vidx;
773                 stretch2 = field->vStretch();
774             }
775 
776             vLayouts[vidx].init(qMax(stretch1, stretch2), qMax(min1.height(), min2.height()));
777             vLayouts[vidx].sizeHint = qMax(sh1.height(), sh2.height());
778             vLayouts[vidx].maximumSize = qMin(max1.height(), max2.height());
779             vLayouts[vidx].expansive = expanding || (vLayouts[vidx].stretch > 0);
780             vLayouts[vidx].empty = false;
781 
782             if (vLayouts[vidx].expansive)
783                 addTopBottomStretch = false;
784 
785             if (vidx > 1)
786                 vLayouts[vidx - 1].spacing = spacingHelper(q->parentWidget(), style, userVSpacing, prevRowSplit, label, field, prevItem1, prevItem2);
787 
788             if (label) {
789                 prevItem1 = label;
790                 prevItem2 = field;
791             } else {
792                 prevItem1 = field;
793                 prevItem2 = nullptr;
794             }
795 
796             prevRowSplit = false;
797             ++vidx;
798         }
799     }
800 
801     if (addTopBottomStretch) {
802         Qt::Alignment formAlignment = q->formAlignment();
803 
804         if (!(formAlignment & Qt::AlignBottom)) {
805             // AlignTop (default if unspecified) or AlignVCenter: We add a stretch at the bottom
806             vLayouts[vidx].init(1, 0);
807             vLayouts[vidx].expansive = true;
808             ++vidx;
809         }
810 
811         if (formAlignment & (Qt::AlignVCenter | Qt::AlignBottom)) {
812             // AlignVCenter or AlignBottom: We add a stretch at the top
813             vLayouts[0].init(1, 0);
814             vLayouts[0].expansive = true;
815         } else {
816             vLayouts[0].init(0, 0);
817         }
818     } else {
819         vLayouts[0].init(0, 0);
820     }
821 
822     vLayoutCount = vidx;
823     dirty = false;
824 }
825 
setupHorizontalLayoutData(int width)826 void QFormLayoutPrivate::setupHorizontalLayoutData(int width)
827 {
828     Q_Q(QFormLayout);
829 
830     // requires setupVerticalLayoutData to be called first
831 
832     int fieldMaxWidth = 0;
833 
834     int rr = m_matrix.rowCount();
835     bool wrapAllRows = (q->rowWrapPolicy() == QFormLayout::WrapAllRows);
836 
837     for (int i = 0; i < rr; ++i) {
838         QFormLayoutItem *label = m_matrix(i, 0);
839         QFormLayoutItem *field = m_matrix(i, 1);
840 
841         // Totally ignore empty rows...
842         if (!label && !field)
843             continue;
844 
845         if (label) {
846             // if there is a field, and we're side by side, we use maxLabelWidth
847             // otherwise we just use the sizehint
848             label->layoutWidth = (field && label->sideBySide) ? maxLabelWidth : label->sizeHint.width();
849             label->layoutPos = 0;
850         }
851 
852         if (field) {
853             // This is the default amount allotted to fields in sbs
854             int fldwidth = width - maxLabelWidth - field->sbsHSpace;
855 
856             // If we've split a row, we still decide to align
857             // the field with all the other field if it will fit
858             // Fields in sbs mode get the remnants of the maxLabelWidth
859             if (!field->sideBySide) {
860                 if (wrapAllRows || (!label && field->fullRow) || field->sizeHint.width() > fldwidth) {
861                     field->layoutWidth = width;
862                     field->layoutPos = 0;
863                 } else {
864                     field->layoutWidth = fldwidth;
865                     field->layoutPos = width - fldwidth;
866                 }
867             } else {
868                 // We're sbs, so we should have a label
869                 field->layoutWidth = fldwidth;
870                 field->layoutPos = width - fldwidth;
871             }
872 
873             fieldMaxWidth = qMax(fieldMaxWidth, field->maxSize.width());
874         }
875     }
876 
877     formMaxWidth = maxLabelWidth + fieldMaxWidth;
878 }
879 
calcSizeHints()880 void QFormLayoutPrivate::calcSizeHints()
881 {
882     Q_Q(QFormLayout);
883 
884     int leftMargin, topMargin, rightMargin, bottomMargin;
885     q->getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
886 
887     updateSizes();
888     setupVerticalLayoutData(QLAYOUTSIZE_MAX);
889     // Don't need to call setupHorizontal here
890 
891     int h = topMargin + bottomMargin;
892     int mh = topMargin + bottomMargin;
893 
894     // The following are set in updateSizes
895     int w = sh_width + leftMargin + rightMargin;
896     int mw = min_width + leftMargin + rightMargin;
897 
898     for (int i = 0; i < vLayoutCount; ++i) {
899         int spacing = vLayouts.at(i).spacing;
900         h += vLayouts.at(i).sizeHint + spacing;
901         mh += vLayouts.at(i).minimumSize + spacing;
902     }
903 
904     minSize.rwidth() = qMin(mw, QLAYOUTSIZE_MAX);
905     minSize.rheight() = qMin(mh, QLAYOUTSIZE_MAX);
906     prefSize.rwidth() = qMin(w, QLAYOUTSIZE_MAX);
907     prefSize.rheight() = qMin(h, QLAYOUTSIZE_MAX);
908 }
909 
insertRow(int row)910 int QFormLayoutPrivate::insertRow(int row)
911 {
912     int rowCnt = m_matrix.rowCount();
913     if (uint(row) > uint(rowCnt))
914         row = rowCnt;
915 
916     insertRows(row, 1);
917     return row;
918 }
919 
insertRows(int row,int count)920 void QFormLayoutPrivate::insertRows(int row, int count)
921 {
922     while (count > 0) {
923         m_matrix.insertRow(row, 0);
924         --count;
925     }
926 }
927 
removeRow(int row)928 void QFormLayoutPrivate::removeRow(int row)
929 {
930     if (uint(row) < uint(m_matrix.rowCount()))
931         m_matrix.removeRow(row);
932 }
933 
setItem(int row,QFormLayout::ItemRole role,QLayoutItem * item)934 bool QFormLayoutPrivate::setItem(int row, QFormLayout::ItemRole role, QLayoutItem *item)
935 {
936     const bool fullRow = role == QFormLayout::SpanningRole;
937     const int column =  role == QFormLayout::SpanningRole ? 1 : static_cast<int>(role);
938     if (Q_UNLIKELY(uint(row) >= uint(m_matrix.rowCount()) || uint(column) > 1U)) {
939         qWarning("QFormLayoutPrivate::setItem: Invalid cell (%d, %d)", row, column);
940         return false;
941     }
942 
943     if (!item)
944         return false;
945 
946     if (Q_UNLIKELY(m_matrix(row, column))) {
947         qWarning("QFormLayoutPrivate::setItem: Cell (%d, %d) already occupied", row, column);
948         return false;
949     }
950 
951     QFormLayoutItem *i = new QFormLayoutItem(item);
952     i->fullRow = fullRow;
953     m_matrix(row, column) = i;
954 
955     m_things.append(i);
956     return true;
957 }
958 
setLayout(int row,QFormLayout::ItemRole role,QLayout * layout)959 void QFormLayoutPrivate::setLayout(int row, QFormLayout::ItemRole role, QLayout *layout)
960 {
961     if (layout) {
962         Q_Q(QFormLayout);
963         if (q->adoptLayout(layout))
964             setItem(row, role, layout);
965     }
966 }
967 
setWidget(int row,QFormLayout::ItemRole role,QWidget * widget)968 void QFormLayoutPrivate::setWidget(int row, QFormLayout::ItemRole role, QWidget *widget)
969 {
970     if (widget) {
971         Q_Q(QFormLayout);
972         q->addChildWidget(widget);
973         QWidgetItem *item = QLayoutPrivate::createWidgetItem(q, widget);
974         if (!setItem(row, role, item))
975             delete item;
976     }
977 }
978 
getStyle() const979 QStyle* QFormLayoutPrivate::getStyle() const
980 {
981     Q_Q(const QFormLayout);
982 
983     // ### cache
984     if (QWidget *parentWidget = q->parentWidget())
985         return parentWidget->style();
986     else
987         return QApplication::style();
988 }
989 
replaceAt(int index,QLayoutItem * newitem)990 QLayoutItem* QFormLayoutPrivate::replaceAt(int index, QLayoutItem *newitem)
991 {
992     Q_Q(QFormLayout);
993     if (!newitem)
994         return nullptr;
995     const int storageIndex = storageIndexFromLayoutItem(m_matrix, m_things.value(index));
996     if (Q_UNLIKELY(storageIndex == -1)) {
997         // ### Qt6 - fix warning too when this class becomes public
998         qWarning("QFormLayoutPrivate::replaceAt: Invalid index %d", index);
999         return nullptr;
1000     }
1001 
1002     int row, col;
1003     QFormLayoutPrivate::ItemMatrix::storageIndexToPosition(storageIndex, &row, &col);
1004     Q_ASSERT(m_matrix(row, col));
1005 
1006     QFormLayoutItem *item = m_matrix(row, col);
1007     Q_ASSERT(item);
1008 
1009     QLayoutItem *olditem = item->item;
1010     item->item = newitem;
1011 
1012     q->invalidate();
1013     return olditem;
1014 }
1015 
1016 /*!
1017     \class QFormLayout
1018     \since 4.4
1019     \brief The QFormLayout class manages forms of input widgets and their associated labels.
1020 
1021     \ingroup geomanagement
1022     \inmodule QtWidgets
1023 
1024     QFormLayout is a convenience layout class that lays out its
1025     children in a two-column form. The left column consists of labels
1026     and the right column consists of "field" widgets (line editors,
1027     spin boxes, etc.).
1028 
1029     Traditionally, such two-column form layouts were achieved using
1030     QGridLayout. QFormLayout is a higher-level alternative that
1031     provides the following advantages:
1032 
1033     \list
1034     \li \b{Adherence to the different platform's look and feel guidelines.}
1035 
1036         For example, the
1037         \l{http://developer.apple.com/library/mac/#documentation/UserExperience/Conceptual/AppleHIGuidelines/Intro/Intro.html}{\macos Aqua} and KDE guidelines specify that the
1038         labels should be right-aligned, whereas Windows and GNOME
1039         applications normally use left-alignment.
1040 
1041     \li \b{Support for wrapping long rows.}
1042 
1043        For devices with small displays, QFormLayout can be set to
1044        \l{WrapLongRows}{wrap long rows}, or even to
1045        \l{WrapAllRows}{wrap all rows}.
1046 
1047     \li \b{Convenient API for creating label--field pairs.}
1048 
1049        The addRow() overload that takes a QString and a QWidget *
1050        creates a QLabel behind the scenes and automatically set up
1051        its buddy. We can then write code like this:
1052 
1053     \snippet code/src_gui_kernel_qformlayout.cpp 0
1054 
1055        Compare this with the following code, written using QGridLayout:
1056 
1057     \snippet code/src_gui_kernel_qformlayout.cpp 1
1058     \endlist
1059 
1060     The table below shows the default appearance in different styles.
1061 
1062     \table
1063     \header
1064         \li QCommonStyle derived styles (except QPlastiqueStyle)
1065         \li QMacStyle
1066         \li QPlastiqueStyle
1067         \li Qt Extended styles
1068     \row
1069         \li \inlineimage qformlayout-win.png
1070         \li \inlineimage qformlayout-mac.png
1071         \li \inlineimage qformlayout-kde.png
1072         \li \inlineimage qformlayout-qpe.png
1073     \row
1074         \li Traditional style used for Windows, GNOME, and earlier
1075            versions of KDE. Labels are left aligned, and expanding
1076            fields grow to fill the available space. (This normally
1077            corresponds to what we would get using a two-column
1078            QGridLayout.)
1079         \li Style based on the
1080            \l{http://developer.apple.com/library/mac/#documentation/UserExperience/Conceptual/AppleHIGuidelines/Intro/Intro.html}{\macos Aqua} guidelines. Labels are right-aligned,
1081            the fields don't grow beyond their size hint, and the
1082            form is horizontally centered.
1083         \li Recommended style for
1084            \l{KDE applications}. Similar to MacStyle, except that the form
1085            is left-aligned and all fields grow to fill the available
1086            space.
1087         \li Default style for Qt Extended styles. Labels are right-aligned,
1088            expanding fields grow to fill the available space, and row
1089            wrapping is enabled for long lines.
1090     \endtable
1091 
1092     The form styles can be also be overridden individually by calling
1093     setLabelAlignment(), setFormAlignment(), setFieldGrowthPolicy(),
1094     and setRowWrapPolicy().  For example, to simulate the form layout
1095     appearance of QMacStyle on all platforms, but with left-aligned
1096     labels, you could write:
1097 
1098     \snippet code/src_gui_kernel_qformlayout.cpp 2
1099 
1100     \sa QGridLayout, QBoxLayout, QStackedLayout
1101 */
1102 
1103 
1104 /*!
1105     \enum QFormLayout::FieldGrowthPolicy
1106 
1107     This enum specifies the different policies that can be used to
1108     control the way in which the form's fields grow.
1109 
1110     \value FieldsStayAtSizeHint
1111            The fields never grow beyond their
1112            \l{QWidgetItem::sizeHint()}{effective size hint}. This is
1113            the default for QMacStyle.
1114 
1115     \value ExpandingFieldsGrow
1116            Fields with an horizontal \l{QSizePolicy}{size policy} of
1117            \l{QSizePolicy::}{Expanding} or
1118            \l{QSizePolicy::}{MinimumExpanding} will grow to fill the
1119            available space. The other fields will not grow beyond
1120            their effective size hint. This is the default policy for
1121            Plastique.
1122 
1123     \value AllNonFixedFieldsGrow
1124            All fields with a size policy that allows them to grow
1125            will grow to fill the available space. This is the default
1126            policy for most styles.
1127 
1128     \sa fieldGrowthPolicy
1129 */
1130 
1131 /*!
1132     \enum QFormLayout::RowWrapPolicy
1133 
1134     This enum specifies the different policies that can be used to
1135     control the way in which the form's rows wrap.
1136 
1137     \value DontWrapRows
1138            Fields are always laid out next to their label.  This is
1139            the default policy for all styles except Qt Extended styles.
1140 
1141     \value WrapLongRows
1142            Labels are given enough horizontal space to fit the widest label,
1143            and the rest of the space is given to the fields. If the minimum
1144            size of a field pair is wider than the available space, the field
1145            is wrapped to the next line.  This is the default policy for
1146            Qt Extended styles.
1147 
1148     \value WrapAllRows
1149            Fields are always laid out below their label.
1150 
1151     \sa rowWrapPolicy
1152 */
1153 
1154 /*!
1155     \enum QFormLayout::ItemRole
1156 
1157     This enum specifies the types of widgets (or other layout items)
1158     that may appear in a row.
1159 
1160     \value LabelRole A label widget.
1161     \value FieldRole A field widget.
1162     \value SpanningRole A widget that spans label and field columns.
1163 
1164     \sa itemAt(), getItemPosition()
1165 */
1166 
1167 /*!
1168 
1169     \class QFormLayout::TakeRowResult
1170 
1171     \brief Contains the result of a QFormLayout::takeRow() call.
1172     \inmodule QtWidgets
1173     \since 5.8
1174     \sa QFormLayout::takeRow()
1175 */
1176 
1177 /*!
1178     \variable QFormLayout::TakeRowResult::labelItem
1179 
1180     Contains the layout item corresponding to the label of the row.
1181 */
1182 
1183 /*!
1184     \variable QFormLayout::TakeRowResult::fieldItem
1185 
1186     Contains the layout item corresponding to the field of the row.
1187 */
1188 
1189 /*!
1190     Constructs a new form layout with the given \a parent widget.
1191 
1192     The layout is set directly as the top-level layout for \a parent.
1193     There can be only one top-level layout for a widget. It is returned
1194     by QWidget::layout().
1195 
1196     \sa QWidget::setLayout()
1197 */
QFormLayout(QWidget * parent)1198 QFormLayout::QFormLayout(QWidget *parent)
1199     : QLayout(*new QFormLayoutPrivate, nullptr, parent)
1200 {
1201 }
1202 
1203 /*!
1204     Destroys the form layout.
1205 */
~QFormLayout()1206 QFormLayout::~QFormLayout()
1207 {
1208     Q_D(QFormLayout);
1209 
1210     /*
1211         The clearing and destruction order here is important. We start by clearing
1212         m_things so that QLayout and the rest of the world know that we don't babysit
1213         the layout items anymore and don't care if they are destroyed.
1214     */
1215     d->m_things.clear();
1216     qDeleteAll(d->m_matrix.storage());
1217     d->m_matrix.clear();
1218 }
1219 
1220 /*!
1221     Adds a new row to the bottom of this form layout, with the given
1222     \a label and \a field.
1223 
1224     \sa insertRow()
1225 */
addRow(QWidget * label,QWidget * field)1226 void QFormLayout::addRow(QWidget *label, QWidget *field)
1227 {
1228     insertRow(-1, label, field);
1229 }
1230 
1231 /*!
1232     \overload
1233 */
addRow(QWidget * label,QLayout * field)1234 void QFormLayout::addRow(QWidget *label, QLayout *field)
1235 {
1236     insertRow(-1, label, field);
1237 }
1238 
1239 /*!
1240     \overload
1241 
1242     This overload automatically creates a QLabel behind the scenes
1243     with \a labelText as its text. The \a field is set as the new
1244     QLabel's \l{QLabel::setBuddy()}{buddy}.
1245 */
addRow(const QString & labelText,QWidget * field)1246 void QFormLayout::addRow(const QString &labelText, QWidget *field)
1247 {
1248     insertRow(-1, labelText, field);
1249 }
1250 
1251 /*!
1252     \overload
1253 
1254     This overload automatically creates a QLabel behind the scenes
1255     with \a labelText as its text.
1256 */
addRow(const QString & labelText,QLayout * field)1257 void QFormLayout::addRow(const QString &labelText, QLayout *field)
1258 {
1259     insertRow(-1, labelText, field);
1260 }
1261 
1262 /*!
1263     \overload
1264 
1265     Adds the specified \a widget at the end of this form layout. The
1266     \a widget spans both columns.
1267 */
addRow(QWidget * widget)1268 void QFormLayout::addRow(QWidget *widget)
1269 {
1270     insertRow(-1, widget);
1271 }
1272 
1273 /*!
1274     \overload
1275 
1276     Adds the specified \a layout at the end of this form layout. The
1277     \a layout spans both columns.
1278 */
addRow(QLayout * layout)1279 void QFormLayout::addRow(QLayout *layout)
1280 {
1281     insertRow(-1, layout);
1282 }
1283 
1284 /*!
1285     Inserts a new row at position \a row in this form layout, with
1286     the given \a label and \a field. If \a row is out of bounds, the
1287     new row is added at the end.
1288 
1289     \sa addRow()
1290 */
insertRow(int row,QWidget * label,QWidget * field)1291 void QFormLayout::insertRow(int row, QWidget *label, QWidget *field)
1292 {
1293     Q_D(QFormLayout);
1294     if ((label && !d->checkWidget(label)) || (field && !d->checkWidget(field)))
1295         return;
1296 
1297     row = d->insertRow(row);
1298     if (label)
1299         d->setWidget(row, LabelRole, label);
1300     if (field)
1301         d->setWidget(row, FieldRole, field);
1302     invalidate();
1303 }
1304 
1305 /*!
1306     \overload
1307 */
insertRow(int row,QWidget * label,QLayout * field)1308 void QFormLayout::insertRow(int row, QWidget *label, QLayout *field)
1309 {
1310     Q_D(QFormLayout);
1311     if ((label && !d->checkWidget(label)) || (field && !d->checkLayout(field)))
1312         return;
1313 
1314     row = d->insertRow(row);
1315     if (label)
1316         d->setWidget(row, LabelRole, label);
1317     if (field)
1318         d->setLayout(row, FieldRole, field);
1319     invalidate();
1320 }
1321 
1322 /*!
1323     \overload
1324 
1325     This overload automatically creates a QLabel behind the scenes
1326     with \a labelText as its text. The \a field is set as the new
1327     QLabel's \l{QLabel::setBuddy()}{buddy}.
1328 */
insertRow(int row,const QString & labelText,QWidget * field)1329 void QFormLayout::insertRow(int row, const QString &labelText, QWidget *field)
1330 {
1331     Q_D(QFormLayout);
1332     if (field && !d->checkWidget(field))
1333         return;
1334 
1335     QLabel *label = nullptr;
1336     if (!labelText.isEmpty()) {
1337         label = new QLabel(labelText);
1338 #ifndef QT_NO_SHORTCUT
1339         label->setBuddy(field);
1340 #endif
1341     }
1342     insertRow(row, label, field);
1343 }
1344 
1345 /*!
1346     \overload
1347 
1348     This overload automatically creates a QLabel behind the scenes
1349     with \a labelText as its text.
1350 */
insertRow(int row,const QString & labelText,QLayout * field)1351 void QFormLayout::insertRow(int row, const QString &labelText, QLayout *field)
1352 {
1353     Q_D(QFormLayout);
1354     if (field && !d->checkLayout(field))
1355         return;
1356 
1357     insertRow(row, labelText.isEmpty() ? nullptr : new QLabel(labelText), field);
1358 }
1359 
1360 /*!
1361     \overload
1362 
1363     Inserts the specified \a widget at position \a row in this form
1364     layout. The \a widget spans both columns. If \a row is out of
1365     bounds, the widget is added at the end.
1366 */
insertRow(int row,QWidget * widget)1367 void QFormLayout::insertRow(int row, QWidget *widget)
1368 {
1369     Q_D(QFormLayout);
1370     if (!d->checkWidget(widget))
1371         return;
1372 
1373     row = d->insertRow(row);
1374     d->setWidget(row, SpanningRole, widget);
1375     invalidate();
1376 }
1377 
1378 /*!
1379     \overload
1380 
1381     Inserts the specified \a layout at position \a row in this form
1382     layout. The \a layout spans both columns. If \a row is out of
1383     bounds, the widget is added at the end.
1384 */
insertRow(int row,QLayout * layout)1385 void QFormLayout::insertRow(int row, QLayout *layout)
1386 {
1387     Q_D(QFormLayout);
1388     if (!d->checkLayout(layout))
1389         return;
1390 
1391     row = d->insertRow(row);
1392     d->setLayout(row, SpanningRole, layout);
1393     invalidate();
1394 }
1395 
ownershipCleanedItem(QFormLayoutItem * item,QFormLayout * layout)1396 static QLayoutItem *ownershipCleanedItem(QFormLayoutItem *item, QFormLayout *layout)
1397 {
1398     if (!item)
1399         return nullptr;
1400 
1401     // grab ownership back from the QFormLayoutItem
1402     QLayoutItem *i = item->item;
1403     item->item = nullptr;
1404     delete item;
1405 
1406     if (QLayout *l = i->layout()) {
1407         // sanity check in case the user passed something weird to QObject::setParent()
1408         if (l->parent() == layout)
1409             l->setParent(nullptr);
1410     }
1411 
1412     return i;
1413 }
1414 
clearAndDestroyQLayoutItem(QLayoutItem * item)1415 static void clearAndDestroyQLayoutItem(QLayoutItem *item)
1416 {
1417     if (Q_LIKELY(item)) {
1418         delete item->widget();
1419         if (QLayout *layout = item->layout()) {
1420             while (QLayoutItem *child = layout->takeAt(0))
1421                 clearAndDestroyQLayoutItem(child);
1422         }
1423         delete item;
1424     }
1425 }
1426 
1427 /*!
1428     \since 5.8
1429 
1430     Deletes row \a row from this form layout.
1431 
1432     \a row must be non-negative and less than rowCount().
1433 
1434     After this call, rowCount() is decremented by one. All widgets and
1435     nested layouts that occupied this row are deleted. That includes both
1436     the field widget(s) and the label, if any. All following rows are shifted
1437     up one row and the freed vertical space is redistributed amongst the remaining rows.
1438 
1439     You can use this function to undo a previous addRow() or insertRow():
1440     \snippet code/src_gui_kernel_qformlayout.cpp 3
1441 
1442     If you want to remove the row from the layout without deleting the widgets, use takeRow() instead.
1443 
1444     \sa takeRow()
1445 */
removeRow(int row)1446 void QFormLayout::removeRow(int row)
1447 {
1448     TakeRowResult result = takeRow(row);
1449     clearAndDestroyQLayoutItem(result.labelItem);
1450     clearAndDestroyQLayoutItem(result.fieldItem);
1451 }
1452 
1453 /*!
1454     \since 5.8
1455 
1456     \overload
1457 
1458     Deletes the row corresponding to \a widget from this form layout.
1459 
1460     After this call, rowCount() is decremented by one. All widgets and
1461     nested layouts that occupied this row are deleted. That includes both
1462     the field widget(s) and the label, if any. All following rows are shifted
1463     up one row and the freed vertical space is redistributed amongst the remaining rows.
1464 
1465     You can use this function to undo a previous addRow() or insertRow():
1466     \snippet code/src_gui_kernel_qformlayout.cpp 4
1467 
1468     If you want to remove the row from the layout without deleting the widgets, use takeRow() instead.
1469 
1470     \sa takeRow()
1471 */
removeRow(QWidget * widget)1472 void QFormLayout::removeRow(QWidget *widget)
1473 {
1474     TakeRowResult result = takeRow(widget);
1475     clearAndDestroyQLayoutItem(result.labelItem);
1476     clearAndDestroyQLayoutItem(result.fieldItem);
1477 }
1478 
1479 /*!
1480     \since 5.8
1481 
1482     \overload
1483 
1484     Deletes the row corresponding to \a layout from this form layout.
1485 
1486     After this call, rowCount() is decremented by one. All widgets and
1487     nested layouts that occupied this row are deleted. That includes both
1488     the field widget(s) and the label, if any. All following rows are shifted
1489     up one row and the freed vertical space is redistributed amongst the remaining rows.
1490 
1491     You can use this function to undo a previous addRow() or insertRow():
1492     \snippet code/src_gui_kernel_qformlayout.cpp 5
1493 
1494     If you want to remove the row from the form layout without deleting the inserted layout,
1495     use takeRow() instead.
1496 
1497     \sa takeRow()
1498 */
removeRow(QLayout * layout)1499 void QFormLayout::removeRow(QLayout *layout)
1500 {
1501     TakeRowResult result = takeRow(layout);
1502     clearAndDestroyQLayoutItem(result.labelItem);
1503     clearAndDestroyQLayoutItem(result.fieldItem);
1504 }
1505 
1506 /*!
1507     \since 5.8
1508 
1509     Removes the specified \a row from this form layout.
1510 
1511     \a row must be non-negative and less than rowCount().
1512 
1513     \note This function doesn't delete anything.
1514 
1515     After this call, rowCount() is decremented by one. All following rows are shifted
1516     up one row and the freed vertical space is redistributed amongst the remaining rows.
1517 
1518     You can use this function to undo a previous addRow() or insertRow():
1519     \snippet code/src_gui_kernel_qformlayout.cpp 6
1520 
1521     If you want to remove the row from the layout and delete the widgets, use removeRow() instead.
1522 
1523     \return A structure containing both the widget and
1524     corresponding label layout items
1525 
1526     \sa removeRow()
1527 */
takeRow(int row)1528 QFormLayout::TakeRowResult QFormLayout::takeRow(int row)
1529 {
1530     Q_D(QFormLayout);
1531 
1532     if (Q_UNLIKELY(!(uint(row) < uint(d->m_matrix.rowCount())))) {
1533         qWarning("QFormLayout::takeRow: Invalid row %d", row);
1534         return TakeRowResult();
1535     }
1536 
1537     QFormLayoutItem *label = d->m_matrix(row, 0);
1538     QFormLayoutItem *field = d->m_matrix(row, 1);
1539 
1540     d->m_things.removeOne(label);
1541     d->m_things.removeOne(field);
1542     d->m_matrix.removeRow(row);
1543 
1544     invalidate();
1545 
1546     TakeRowResult result;
1547     result.labelItem = ownershipCleanedItem(label, this);
1548     result.fieldItem = ownershipCleanedItem(field, this);
1549     return result;
1550 }
1551 
1552 /*!
1553     \since 5.8
1554 
1555     \overload
1556 
1557     Removes the specified \a widget from this form layout.
1558 
1559     \note This function doesn't delete anything.
1560 
1561     After this call, rowCount() is decremented by one. All following rows are shifted
1562     up one row and the freed vertical space is redistributed amongst the remaining rows.
1563 
1564     \snippet code/src_gui_kernel_qformlayout.cpp 7
1565 
1566     If you want to remove the row from the layout and delete the widgets, use removeRow() instead.
1567 
1568     \return A structure containing both the widget and
1569     corresponding label layout items
1570 
1571     \sa removeRow()
1572 */
takeRow(QWidget * widget)1573 QFormLayout::TakeRowResult QFormLayout::takeRow(QWidget *widget)
1574 {
1575     Q_D(QFormLayout);
1576     if (Q_UNLIKELY(!d->checkWidget(widget)))
1577         return TakeRowResult();
1578 
1579     int row;
1580     ItemRole role;
1581     getWidgetPosition(widget, &row, &role);
1582 
1583     if (Q_UNLIKELY(row < 0)) {
1584         qWarning("QFormLayout::takeRow: Invalid widget");
1585         return TakeRowResult();
1586     }
1587 
1588     return takeRow(row);
1589 }
1590 
1591 /*!
1592     \since 5.8
1593 
1594     \overload
1595 
1596     Removes the specified \a layout from this form layout.
1597 
1598     \note This function doesn't delete anything.
1599 
1600     After this call, rowCount() is decremented by one. All following rows are shifted
1601     up one row and the freed vertical space is redistributed amongst the remaining rows.
1602 
1603     \snippet code/src_gui_kernel_qformlayout.cpp 8
1604 
1605     If you want to remove the row from the form layout and delete the inserted layout,
1606     use removeRow() instead.
1607 
1608     \return A structure containing both the widget and
1609     corresponding label layout items
1610 
1611     \sa removeRow()
1612 */
takeRow(QLayout * layout)1613 QFormLayout::TakeRowResult QFormLayout::takeRow(QLayout *layout)
1614 {
1615     Q_D(QFormLayout);
1616     if (Q_UNLIKELY(!d->checkLayout(layout)))
1617         return TakeRowResult();
1618 
1619     int row;
1620     ItemRole role;
1621     getLayoutPosition(layout, &row, &role);
1622 
1623     if (Q_UNLIKELY(row < 0)) {
1624         qWarning("QFormLayout::takeRow: Invalid layout");
1625         return TakeRowResult();
1626     }
1627 
1628     return takeRow(row);
1629 }
1630 
1631 /*!
1632     \reimp
1633 */
addItem(QLayoutItem * item)1634 void QFormLayout::addItem(QLayoutItem *item)
1635 {
1636     Q_D(QFormLayout);
1637 
1638     int row = d->insertRow(d->m_matrix.rowCount());
1639     d->setItem(row, FieldRole, item);
1640     invalidate();
1641 }
1642 
1643 /*!
1644     \reimp
1645 */
count() const1646 int QFormLayout::count() const
1647 {
1648     Q_D(const QFormLayout);
1649     return d->m_things.count();
1650 }
1651 
1652 /*!
1653     \reimp
1654 */
itemAt(int index) const1655 QLayoutItem *QFormLayout::itemAt(int index) const
1656 {
1657     Q_D(const QFormLayout);
1658     if (QFormLayoutItem *formItem = d->m_things.value(index))
1659         return formItem->item;
1660     return nullptr;
1661 }
1662 
1663 /*!
1664     \reimp
1665 */
takeAt(int index)1666 QLayoutItem *QFormLayout::takeAt(int index)
1667 {
1668     Q_D(QFormLayout);
1669 
1670     const int storageIndex = storageIndexFromLayoutItem(d->m_matrix, d->m_things.value(index));
1671     if (Q_UNLIKELY(storageIndex == -1)) {
1672         qWarning("QFormLayout::takeAt: Invalid index %d", index);
1673         return nullptr;
1674     }
1675 
1676     int row, col;
1677     QFormLayoutPrivate::ItemMatrix::storageIndexToPosition(storageIndex, &row, &col);
1678     Q_ASSERT(d->m_matrix(row, col));
1679 
1680     QFormLayoutItem *item = d->m_matrix(row, col);
1681     Q_ASSERT(item);
1682     d->m_things.removeAt(index);
1683     d->m_matrix(row, col) = 0;
1684 
1685     invalidate();
1686 
1687     return ownershipCleanedItem(item, this);
1688 }
1689 
1690 /*!
1691     \reimp
1692 */
expandingDirections() const1693 Qt::Orientations QFormLayout::expandingDirections() const
1694 {
1695     Q_D(const QFormLayout);
1696     QFormLayoutPrivate *e = const_cast<QFormLayoutPrivate *>(d);
1697     e->updateSizes();
1698 
1699     Qt::Orientations o;
1700     if (e->expandHorizontal)
1701         o = Qt::Horizontal;
1702     if (e->expandVertical)
1703         o |= Qt::Vertical;
1704     return o;
1705 }
1706 
1707 /*!
1708     \reimp
1709 */
hasHeightForWidth() const1710 bool QFormLayout::hasHeightForWidth() const
1711 {
1712     Q_D(const QFormLayout);
1713     QFormLayoutPrivate *e = const_cast<QFormLayoutPrivate *>(d);
1714     e->updateSizes();
1715     return (d->has_hfw || rowWrapPolicy() == WrapLongRows);
1716 }
1717 
1718 /*!
1719     \reimp
1720 */
heightForWidth(int width) const1721 int QFormLayout::heightForWidth(int width) const
1722 {
1723     Q_D(const QFormLayout);
1724     if (!hasHeightForWidth())
1725         return -1;
1726 
1727     int leftMargin, topMargin, rightMargin, bottomMargin;
1728     getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
1729 
1730     int targetWidth = width - leftMargin - rightMargin;
1731 
1732     if (!d->haveHfwCached(targetWidth)) {
1733         QFormLayoutPrivate *dat = const_cast<QFormLayoutPrivate *>(d);
1734         dat->setupVerticalLayoutData(targetWidth);
1735         dat->setupHorizontalLayoutData(targetWidth);
1736         dat->recalcHFW(targetWidth);
1737     }
1738     if (targetWidth == d->sh_width)
1739         return d->hfw_sh_height + topMargin + bottomMargin;
1740     else
1741         return d->hfw_height + topMargin + bottomMargin;
1742 }
1743 
1744 /*!
1745     \reimp
1746 */
setGeometry(const QRect & rect)1747 void QFormLayout::setGeometry(const QRect &rect)
1748 {
1749     Q_D(QFormLayout);
1750     if (d->dirty || rect != geometry()) {
1751         QRect cr = rect;
1752         int leftMargin, topMargin, rightMargin, bottomMargin;
1753         getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
1754         cr.adjust(+leftMargin, +topMargin, -rightMargin, -bottomMargin);
1755 
1756         bool hfw = hasHeightForWidth();
1757         d->setupVerticalLayoutData(cr.width());
1758         d->setupHorizontalLayoutData(cr.width());
1759         if (hfw && (!d->haveHfwCached(cr.width()) || d->hfwLayouts.size() != d->vLayoutCount))
1760             d->recalcHFW(cr.width());
1761         if (hfw) {
1762             qGeomCalc(d->hfwLayouts, 0, d->vLayoutCount, cr.y(), cr.height());
1763             d->arrangeWidgets(d->hfwLayouts, cr);
1764         } else {
1765             qGeomCalc(d->vLayouts, 0, d->vLayoutCount, cr.y(), cr.height());
1766             d->arrangeWidgets(d->vLayouts, cr);
1767         }
1768         QLayout::setGeometry(rect);
1769     }
1770 }
1771 
1772 /*!
1773     \reimp
1774 */
sizeHint() const1775 QSize QFormLayout::sizeHint() const
1776 {
1777     Q_D(const QFormLayout);
1778     if (!d->prefSize.isValid()) {
1779         QFormLayoutPrivate *dat = const_cast<QFormLayoutPrivate *>(d);
1780         dat->calcSizeHints();
1781     }
1782     return d->prefSize;
1783 }
1784 
1785 /*!
1786     \reimp
1787 */
minimumSize() const1788 QSize QFormLayout::minimumSize() const
1789 {
1790     // ### fix minimumSize if hfw
1791     Q_D(const QFormLayout);
1792     if (!d->minSize.isValid()) {
1793         QFormLayoutPrivate *dat = const_cast<QFormLayoutPrivate *>(d);
1794         dat->calcSizeHints();
1795     }
1796     return d->minSize;
1797 }
1798 
1799 /*!
1800     \reimp
1801 */
invalidate()1802 void QFormLayout::invalidate()
1803 {
1804     Q_D(QFormLayout);
1805     d->dirty = true;
1806     d->sizesDirty = true;
1807     d->minSize = QSize();
1808     d->prefSize = QSize();
1809     d->formMaxWidth = -1;
1810     d->hfw_width = -1;
1811     d->sh_width = -1;
1812     d->layoutWidth = -1;
1813     d->hfw_sh_height = -1;
1814     QLayout::invalidate();
1815 }
1816 
1817 /*!
1818     Returns the number of rows in the form.
1819 
1820     \sa QLayout::count()
1821 */
rowCount() const1822 int QFormLayout::rowCount() const
1823 {
1824     Q_D(const QFormLayout);
1825     return d->m_matrix.rowCount();
1826 }
1827 
1828 /*!
1829     Returns the layout item in the given \a row with the specified \a
1830     role (column). Returns \nullptr if there is no such item.
1831 
1832     \sa QLayout::itemAt(), setItem()
1833 */
itemAt(int row,ItemRole role) const1834 QLayoutItem *QFormLayout::itemAt(int row, ItemRole role) const
1835 {
1836     Q_D(const QFormLayout);
1837     if (uint(row) >= uint(d->m_matrix.rowCount()))
1838         return nullptr;
1839     switch (role) {
1840     case SpanningRole:
1841         if (QFormLayoutItem *item = d->m_matrix(row, 1))
1842             if (item->fullRow)
1843                 return item->item;
1844         break;
1845     case LabelRole:
1846     case FieldRole:
1847         if (QFormLayoutItem *item = d->m_matrix(row, (role == LabelRole) ? 0 : 1))
1848             return item->item;
1849         break;
1850     }
1851     return nullptr;
1852 }
1853 
1854 /*!
1855     Retrieves the row and role (column) of the item at the specified
1856     \a index. If \a index is out of bounds, *\a rowPtr is set to -1;
1857     otherwise the row is stored in *\a rowPtr and the role is stored
1858     in *\a rolePtr.
1859 
1860     \sa itemAt(), count(), getLayoutPosition(), getWidgetPosition()
1861 */
getItemPosition(int index,int * rowPtr,ItemRole * rolePtr) const1862 void QFormLayout::getItemPosition(int index, int *rowPtr, ItemRole *rolePtr) const
1863 {
1864     Q_D(const QFormLayout);
1865     int col = -1;
1866     int row = -1;
1867 
1868     const int storageIndex = storageIndexFromLayoutItem(d->m_matrix, d->m_things.value(index));
1869     if (storageIndex != -1)
1870         QFormLayoutPrivate::ItemMatrix::storageIndexToPosition(storageIndex, &row, &col);
1871 
1872     if (rowPtr)
1873         *rowPtr = row;
1874     if (rolePtr && row != -1) {
1875         const bool spanning = col == 1 && d->m_matrix(row, col)->fullRow;
1876         if (spanning) {
1877             *rolePtr = SpanningRole;
1878         } else {
1879             *rolePtr = ItemRole(col);
1880         }
1881     }
1882 }
1883 
1884 /*!
1885     Retrieves the row and role (column) of the specified child \a
1886     layout. If \a layout is not in the form layout, *\a rowPtr is set
1887     to -1; otherwise the row is stored in *\a rowPtr and the role is stored
1888     in *\a rolePtr.
1889 */
getLayoutPosition(QLayout * layout,int * rowPtr,ItemRole * rolePtr) const1890 void QFormLayout::getLayoutPosition(QLayout *layout, int *rowPtr, ItemRole *rolePtr) const
1891 {
1892     int n = count();
1893     int index = 0;
1894     while (index < n) {
1895         if (itemAt(index) == layout)
1896             break;
1897         ++index;
1898     }
1899     getItemPosition(index, rowPtr, rolePtr);
1900 }
1901 
1902 /*!
1903     Retrieves the row and role (column) of the specified \a widget in
1904     the layout. If \a widget is not in the layout, *\a rowPtr is set
1905     to -1; otherwise the row is stored in *\a rowPtr and the role is stored
1906     in *\a rolePtr.
1907 
1908     \sa getItemPosition(), itemAt()
1909 */
getWidgetPosition(QWidget * widget,int * rowPtr,ItemRole * rolePtr) const1910 void QFormLayout::getWidgetPosition(QWidget *widget, int *rowPtr, ItemRole *rolePtr) const
1911 {
1912     getItemPosition(indexOf(widget), rowPtr, rolePtr);
1913 }
1914 
1915 // ### eliminate labelForField()
1916 
1917 /*!
1918     Returns the label associated with the given \a field.
1919 
1920     \sa itemAt()
1921 */
labelForField(QWidget * field) const1922 QWidget *QFormLayout::labelForField(QWidget *field) const
1923 {
1924     Q_D(const QFormLayout);
1925 
1926     int row;
1927     ItemRole role = LabelRole;
1928 
1929     getWidgetPosition(field, &row, &role);
1930 
1931     if (row != -1 && role == FieldRole) {
1932         if (QFormLayoutItem *label = d->m_matrix(row, LabelRole))
1933             return label->widget();
1934     }
1935     return nullptr;
1936 }
1937 
1938 /*!
1939     \overload
1940 */
labelForField(QLayout * field) const1941 QWidget *QFormLayout::labelForField(QLayout *field) const
1942 {
1943     Q_D(const QFormLayout);
1944 
1945     int row;
1946     ItemRole role;
1947 
1948     getLayoutPosition(field, &row, &role);
1949 
1950     if (row != -1 && role == FieldRole) {
1951         if (QFormLayoutItem *label = d->m_matrix(row, LabelRole))
1952             return label->widget();
1953     }
1954     return nullptr;
1955 }
1956 
1957 /*!
1958     \property QFormLayout::fieldGrowthPolicy
1959     \brief the way in which the form's fields grow
1960 
1961     The default value depends on the widget or application style. For
1962     QMacStyle, the default is FieldsStayAtSizeHint; for QCommonStyle
1963     derived styles (like Plastique and Windows), the default
1964     is ExpandingFieldsGrow; for Qt Extended styles, the default is
1965     AllNonFixedFieldsGrow.
1966 
1967     If none of the fields can grow and the form is resized, extra
1968     space is distributed according to the current
1969     \l{formAlignment}{form alignment}.
1970 
1971     \sa formAlignment, rowWrapPolicy
1972 */
1973 
setFieldGrowthPolicy(FieldGrowthPolicy policy)1974 void QFormLayout::setFieldGrowthPolicy(FieldGrowthPolicy policy)
1975 {
1976     Q_D(QFormLayout);
1977     if (FieldGrowthPolicy(d->fieldGrowthPolicy) != policy) {
1978         d->fieldGrowthPolicy = policy;
1979         invalidate();
1980     }
1981 }
1982 
fieldGrowthPolicy() const1983 QFormLayout::FieldGrowthPolicy QFormLayout::fieldGrowthPolicy() const
1984 {
1985     Q_D(const QFormLayout);
1986     if (d->fieldGrowthPolicy == DefaultFieldGrowthPolicy) {
1987         return QFormLayout::FieldGrowthPolicy(d->getStyle()->styleHint(QStyle::SH_FormLayoutFieldGrowthPolicy));
1988     } else {
1989         return QFormLayout::FieldGrowthPolicy(d->fieldGrowthPolicy);
1990     }
1991 }
1992 
1993 /*!
1994     \property QFormLayout::rowWrapPolicy
1995     \brief the way in which the form's rows wrap
1996 
1997     The default value depends on the widget or application style. For
1998     Qt Extended styles, the default is WrapLongRows;
1999     for the other styles, the default is DontWrapRows.
2000 
2001     If you want to display each label above its associated field
2002     (instead of next to it), set this property to WrapAllRows.
2003 
2004     \sa fieldGrowthPolicy
2005 */
2006 
setRowWrapPolicy(RowWrapPolicy policy)2007 void QFormLayout::setRowWrapPolicy(RowWrapPolicy policy)
2008 {
2009     Q_D(QFormLayout);
2010     if (RowWrapPolicy(d->rowWrapPolicy) != policy) {
2011         d->rowWrapPolicy = policy;
2012         invalidate();
2013     }
2014 }
2015 
rowWrapPolicy() const2016 QFormLayout::RowWrapPolicy QFormLayout::rowWrapPolicy() const
2017 {
2018     Q_D(const QFormLayout);
2019     if (d->rowWrapPolicy == DefaultRowWrapPolicy) {
2020         return QFormLayout::RowWrapPolicy(d->getStyle()->styleHint(QStyle::SH_FormLayoutWrapPolicy));
2021     } else {
2022         return QFormLayout::RowWrapPolicy(d->rowWrapPolicy);
2023     }
2024 }
2025 
2026 /*!
2027     \property QFormLayout::labelAlignment
2028     \brief the horizontal alignment of the labels
2029 
2030     The default value depends on the widget or application style. For
2031     QCommonStyle derived styles, except for QPlastiqueStyle, the
2032     default is Qt::AlignLeft; for the other styles, the default is
2033     Qt::AlignRight.
2034 
2035     \sa formAlignment
2036 */
2037 
setLabelAlignment(Qt::Alignment alignment)2038 void QFormLayout::setLabelAlignment(Qt::Alignment alignment)
2039 {
2040     Q_D(QFormLayout);
2041     if (d->labelAlignment != alignment) {
2042         d->labelAlignment = alignment;
2043         invalidate();
2044     }
2045 }
2046 
labelAlignment() const2047 Qt::Alignment QFormLayout::labelAlignment() const
2048 {
2049     Q_D(const QFormLayout);
2050     if (!d->labelAlignment) {
2051         return Qt::Alignment(d->getStyle()->styleHint(QStyle::SH_FormLayoutLabelAlignment));
2052     } else {
2053         return d->labelAlignment;
2054     }
2055 }
2056 
2057 /*!
2058     \property QFormLayout::formAlignment
2059     \brief the alignment of the form layout's contents within the layout's geometry
2060 
2061     The default value depends on the widget or application style. For
2062     QMacStyle, the default is Qt::AlignHCenter | Qt::AlignTop; for the
2063     other styles, the default is Qt::AlignLeft | Qt::AlignTop.
2064 
2065     \sa labelAlignment, rowWrapPolicy
2066 */
2067 
setFormAlignment(Qt::Alignment alignment)2068 void QFormLayout::setFormAlignment(Qt::Alignment alignment)
2069 {
2070     Q_D(QFormLayout);
2071     if (d->formAlignment != alignment) {
2072         d->formAlignment = alignment;
2073         invalidate();
2074     }
2075 }
2076 
formAlignment() const2077 Qt::Alignment QFormLayout::formAlignment() const
2078 {
2079     Q_D(const QFormLayout);
2080     if (!d->formAlignment) {
2081         return Qt::Alignment(d->getStyle()->styleHint(QStyle::SH_FormLayoutFormAlignment));
2082     } else {
2083         return d->formAlignment;
2084     }
2085 }
2086 
2087 /*!
2088     \property QFormLayout::horizontalSpacing
2089     \brief the spacing between widgets that are laid out side by side
2090 
2091     By default, if no value is explicitly set, the layout's horizontal
2092     spacing is inherited from the parent layout, or from the style settings
2093     for the parent widget.
2094 
2095     \sa verticalSpacing, QStyle::pixelMetric(), {QStyle::}{PM_LayoutHorizontalSpacing}
2096 */
setHorizontalSpacing(int spacing)2097 void QFormLayout::setHorizontalSpacing(int spacing)
2098 {
2099     Q_D(QFormLayout);
2100     if (spacing != d->hSpacing) {
2101         d->hSpacing = spacing;
2102         invalidate();
2103     }
2104 }
2105 
horizontalSpacing() const2106 int QFormLayout::horizontalSpacing() const
2107 {
2108     Q_D(const QFormLayout);
2109     if (d->hSpacing >= 0) {
2110         return d->hSpacing;
2111     } else {
2112         return qSmartSpacing(this, QStyle::PM_LayoutHorizontalSpacing);
2113     }
2114 }
2115 
2116 /*!
2117     \property QFormLayout::verticalSpacing
2118     \brief the spacing between widgets that are laid out vertically
2119 
2120     By default, if no value is explicitly set, the layout's vertical spacing is
2121     inherited from the parent layout, or from the style settings for the parent
2122     widget.
2123 
2124     \sa horizontalSpacing, QStyle::pixelMetric(), {QStyle::}{PM_LayoutHorizontalSpacing}
2125 */
setVerticalSpacing(int spacing)2126 void QFormLayout::setVerticalSpacing(int spacing)
2127 {
2128     Q_D(QFormLayout);
2129     if (spacing != d->vSpacing) {
2130         d->vSpacing = spacing;
2131         invalidate();
2132     }
2133 }
2134 
verticalSpacing() const2135 int QFormLayout::verticalSpacing() const
2136 {
2137     Q_D(const QFormLayout);
2138     if (d->vSpacing >= 0) {
2139         return d->vSpacing;
2140     } else {
2141         return qSmartSpacing(this, QStyle::PM_LayoutVerticalSpacing);
2142     }
2143 }
2144 
2145 /*!
2146     This function sets both the vertical and horizontal spacing to
2147     \a spacing.
2148 
2149     \sa setVerticalSpacing(), setHorizontalSpacing()
2150 */
setSpacing(int spacing)2151 void QFormLayout::setSpacing(int spacing)
2152 {
2153     Q_D(QFormLayout);
2154     d->vSpacing = d->hSpacing = spacing;
2155     invalidate();
2156 }
2157 
2158 /*!
2159     If the vertical spacing is equal to the horizontal spacing,
2160     this function returns that value; otherwise it returns -1.
2161 
2162     \sa setSpacing(), verticalSpacing(), horizontalSpacing()
2163 */
spacing() const2164 int QFormLayout::spacing() const
2165 {
2166     int hSpacing = horizontalSpacing();
2167     if (hSpacing == verticalSpacing()) {
2168         return hSpacing;
2169     } else {
2170         return -1;
2171     }
2172 }
2173 
arrangeWidgets(const QVector<QLayoutStruct> & layouts,QRect & rect)2174 void QFormLayoutPrivate::arrangeWidgets(const QVector<QLayoutStruct>& layouts, QRect &rect)
2175 {
2176     Q_Q(QFormLayout);
2177 
2178     int i;
2179     const int rr = m_matrix.rowCount();
2180     QWidget *w = q->parentWidget();
2181     Qt::LayoutDirection layoutDirection = w ? w->layoutDirection() : QGuiApplication::layoutDirection();
2182 
2183     Qt::Alignment formAlignment = fixedAlignment(q->formAlignment(), layoutDirection);
2184     int leftOffset = 0;
2185     int delta = rect.width() - formMaxWidth;
2186     if (formAlignment & (Qt::AlignHCenter | Qt::AlignRight) && delta > 0) {
2187         leftOffset = delta;
2188         if (formAlignment & Qt::AlignHCenter)
2189             leftOffset >>= 1;
2190     }
2191 
2192     for (i = 0; i < rr; ++i) {
2193         QFormLayoutItem *label = m_matrix(i, 0);
2194         QFormLayoutItem *field = m_matrix(i, 1);
2195 
2196         if (label) {
2197             int height = layouts.at(label->vLayoutIndex).size;
2198             if ((label->expandingDirections() & Qt::Vertical) == 0) {
2199                 /*
2200                     If the field on the right-hand side is tall,
2201                     we want the label to be top-aligned, but not too
2202                     much. So we introduce a 7 / 4 factor so that it
2203                     gets some extra pixels at the top.
2204                 */
2205                 height = qMin(height,
2206                               qMin(label->sizeHint.height() * 7 / 4,
2207                                    label->maxSize.height()));
2208             }
2209 
2210             QSize sz(qMin(label->layoutWidth, label->sizeHint.width()), height);
2211             int x = leftOffset + rect.x() + label->layoutPos;
2212             const auto fAlign = fixedAlignment(q->labelAlignment(), layoutDirection);
2213             if (fAlign & Qt::AlignRight)
2214                 x += label->layoutWidth - sz.width();
2215             else if (fAlign & Qt::AlignHCenter)
2216                 x += label->layoutWidth / 2 - sz.width() / 2;
2217             QPoint p(x, layouts.at(label->vLayoutIndex).pos);
2218             // ### expansion & sizepolicy stuff
2219 
2220             label->setGeometry(QStyle::visualRect(layoutDirection, rect, QRect(p, sz)));
2221         }
2222 
2223         if (field) {
2224             QSize sz(field->layoutWidth, layouts.at(field->vLayoutIndex).size);
2225             QPoint p(field->layoutPos + leftOffset + rect.x(), layouts.at(field->vLayoutIndex).pos);
2226 /*
2227             if ((field->widget() && field->widget()->sizePolicy().horizontalPolicy() & (QSizePolicy::GrowFlag | QSizePolicy::ExpandFlag | QSizePolicy::IgnoreFlag))
2228                 || (field->layout() && sz.width() < field->maxSize.width())) {
2229                 sz.rwidth() = field->layoutWidth;
2230             }
2231 */
2232             if (field->maxSize.isValid())
2233                 sz = sz.boundedTo(field->maxSize);
2234 
2235             field->setGeometry(QStyle::visualRect(layoutDirection, rect, QRect(p, sz)));
2236         }
2237     }
2238 }
2239 
2240 /*!
2241     Sets the widget in the given \a row for the given \a role to \a widget, extending the
2242     layout with empty rows if necessary.
2243 
2244     If the cell is already occupied, the \a widget is not inserted and an error message is
2245     sent to the console.
2246 
2247     \b{Note:} For most applications, addRow() or insertRow() should be used instead of setWidget().
2248 
2249     \sa setLayout()
2250 */
setWidget(int row,ItemRole role,QWidget * widget)2251 void QFormLayout::setWidget(int row, ItemRole role, QWidget *widget)
2252 {
2253     Q_D(QFormLayout);
2254     int rowCnt = rowCount();
2255     if (row >= rowCnt)
2256         d->insertRows(rowCnt, row - rowCnt + 1);
2257     d->setWidget(row, role, widget);
2258 }
2259 
2260 /*!
2261     Sets the sub-layout in the given \a row for the given \a role to \a layout, extending the
2262     form layout with empty rows if necessary.
2263 
2264     If the cell is already occupied, the \a layout is not inserted and an error message is
2265     sent to the console.
2266 
2267     \b{Note:} For most applications, addRow() or insertRow() should be used instead of setLayout().
2268 
2269     \sa setWidget()
2270 */
setLayout(int row,ItemRole role,QLayout * layout)2271 void QFormLayout::setLayout(int row, ItemRole role, QLayout *layout)
2272 {
2273     Q_D(QFormLayout);
2274     int rowCnt = rowCount();
2275     if (row >= rowCnt)
2276         d->insertRows(rowCnt, row - rowCnt + 1);
2277     d->setLayout(row, role, layout);
2278 }
2279 
2280 /*!
2281     Sets the item in the given \a row for the given \a role to \a item, extending the
2282     layout with empty rows if necessary.
2283 
2284     If the cell is already occupied, the \a item is not inserted and an error message is
2285     sent to the console.
2286     The \a item spans both columns.
2287 
2288     \warning Do not use this function to add child layouts or child
2289     widget items. Use setLayout() or setWidget() instead.
2290 
2291     \sa setLayout()
2292 */
setItem(int row,ItemRole role,QLayoutItem * item)2293 void QFormLayout::setItem(int row, ItemRole role, QLayoutItem *item)
2294 {
2295     Q_D(QFormLayout);
2296     int rowCnt = rowCount();
2297     if (row >= rowCnt)
2298         d->insertRows(rowCnt, row - rowCnt + 1);
2299     d->setItem(row, role, item);
2300 }
2301 
2302 /*!
2303      \internal
2304  */
2305 
resetFieldGrowthPolicy()2306 void QFormLayout::resetFieldGrowthPolicy()
2307 {
2308     Q_D(QFormLayout);
2309     d->fieldGrowthPolicy = DefaultFieldGrowthPolicy;
2310 }
2311 
2312 /*!
2313      \internal
2314  */
2315 
resetRowWrapPolicy()2316 void QFormLayout::resetRowWrapPolicy()
2317 {
2318     Q_D(QFormLayout);
2319     d->rowWrapPolicy = DefaultRowWrapPolicy;
2320 }
2321 
2322 /*!
2323      \internal
2324  */
2325 
resetFormAlignment()2326 void QFormLayout::resetFormAlignment()
2327 {
2328     Q_D(QFormLayout);
2329     d->formAlignment = { };
2330 }
2331 
2332 /*!
2333      \internal
2334  */
2335 
resetLabelAlignment()2336 void QFormLayout::resetLabelAlignment()
2337 {
2338     Q_D(QFormLayout);
2339     d->labelAlignment = { };
2340 }
2341 
2342 #if 0
2343 void QFormLayout::dump() const
2344 {
2345     Q_D(const QFormLayout);
2346     for (int i = 0; i < rowCount(); ++i) {
2347         for (int j = 0; j < 2; ++j) {
2348             qDebug("m_matrix(%d, %d) = %p", i, j, d->m_matrix(i, j));
2349         }
2350     }
2351     for (int i = 0; i < d->m_things.count(); ++i)
2352         qDebug("m_things[%d] = %p", i, d->m_things.at(i));
2353 }
2354 #endif
2355 
2356 QT_END_NAMESPACE
2357 
2358 #include "moc_qformlayout.cpp"
2359