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

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