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
50QT_BEGIN_NAMESPACE
51
52namespace {
53// Fixed column matrix, stores items as [i11, i12, i21, i22...],
54// with FORTRAN-style index operator(r, c).
55template <class T, int NumColumns>
56class FixedColumnMatrix {
57public:
58 typedef QVector<T> Storage;
59
60 FixedColumnMatrix() { }
61
62 void clear() { m_storage.clear(); }
63
64 const T &operator()(int r, int c) const { return m_storage[r * NumColumns + c]; }
65 T &operator()(int r, int c) { return m_storage[r * NumColumns + c]; }
66
67 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.
72 const Storage &storage() const { return m_storage; }
73
74 static void storageIndexToPosition(int idx, int *rowPtr, int *colPtr);
75
76private:
77 Storage m_storage;
78};
79
80template <class T, int NumColumns>
81void 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
88template <class T, int NumColumns>
89void FixedColumnMatrix<T, NumColumns>::removeRow(int r)
90{
91 m_storage.remove(r * NumColumns, NumColumns);
92}
93
94template <class T, int NumColumns>
95void 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
104const uint DefaultFieldGrowthPolicy = 255;
105const uint DefaultRowWrapPolicy = 255;
106
107enum { ColumnCount = 2 };
108
109// -- our data structure for our items
110// This owns the QLayoutItem
111struct QFormLayoutItem
112{
113 QFormLayoutItem(QLayoutItem* i) : item(i), fullRow(false), isHfw(false) { }
114 ~QFormLayoutItem() { delete item; }
115
116 // Wrappers
117 QWidget *widget() const { return item->widget(); }
118 QLayout *layout() const { return item->layout(); }
119
120 bool hasHeightForWidth() const { return item->hasHeightForWidth(); }
121 int heightForWidth(int width) const { return item->heightForWidth(width); }
122 int minimumHeightForWidth(int width) const { return item->minimumHeightForWidth(width); }
123 Qt::Orientations expandingDirections() const { return item->expandingDirections(); }
124 QSizePolicy::ControlTypes controlTypes() const { return item->controlTypes(); }
125 int vStretch() const { return widget() ? widget()->sizePolicy().verticalStretch() : 0; }
126
127 void setGeometry(const QRect& r) { item->setGeometry(r); }
128 QRect geometry() const { return item->geometry(); }
129
130 // For use with FixedColumnMatrix
131 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
155class QFormLayoutPrivate : public QLayoutPrivate
156{
157 Q_DECLARE_PUBLIC(QFormLayout)
158
159public:
160 typedef FixedColumnMatrix<QFormLayoutItem *, ColumnCount> ItemMatrix;
161
162 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
181 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
229QFormLayoutPrivate::QFormLayoutPrivate()
230 : fieldGrowthPolicy(DefaultFieldGrowthPolicy),
231 rowWrapPolicy(DefaultRowWrapPolicy), has_hfw(false), dirty(true), sizesDirty(true),
232 expandVertical(0), expandHorizontal(0)
233{
234}
235
236static 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
248static int storageIndexFromLayoutItem(const QFormLayoutPrivate::ItemMatrix &m,
249 QFormLayoutItem *item)
250{
251 if (item) {
252 return m.storage().indexOf(t: item);
253 } else {
254 return -1;
255 }
256}
257
258static 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*/
281void 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(item: label, userVSpacing, fieldGrowthPolicy: q->fieldGrowthPolicy(), fullRow: 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(item: field, userVSpacing, fieldGrowthPolicy: q->fieldGrowthPolicy(), fullRow: !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(controls1: lbltoptypes, controls2: lbltypes, orientation: Qt::Vertical, option: nullptr, widget: parent);
368 if (field && fldtop)
369 field->vSpace = style->combinedLayoutSpacing(controls1: fldtoptypes, controls2: fldtypes, orientation: Qt::Vertical, option: nullptr, widget: 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(controls1: lbltoptypes, controls2: lbltypes, orientation: Qt::Vertical, option: nullptr, widget: parent);
384 int fldspacing = style->combinedLayoutSpacing(controls1: fldtoptypes, controls2: lbltypes, orientation: Qt::Vertical, option: nullptr, widget: parent);
385 label->vSpace = qMax(a: lblspacing, b: fldspacing);
386 } else
387 label->vSpace = style->combinedLayoutSpacing(controls1: lbltoptypes, controls2: lbltypes, orientation: Qt::Vertical, option: nullptr, widget: parent);
388 }
389
390 if (field) {
391 // check spacing against both the previous label and field
392 if (!label) {
393 int lblspacing = style->combinedLayoutSpacing(controls1: lbltoptypes, controls2: fldtypes, orientation: Qt::Vertical, option: nullptr, widget: parent);
394 int fldspacing = style->combinedLayoutSpacing(controls1: fldtoptypes, controls2: fldtypes, orientation: Qt::Vertical, option: nullptr, widget: parent);
395 field->vSpace = qMax(a: lblspacing, b: fldspacing);
396 } else
397 field->vSpace = style->combinedLayoutSpacing(controls1: fldtoptypes, controls2: fldtypes, orientation: Qt::Vertical, option: nullptr, widget: 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(controls1: QSizePolicy::Label, controls2: QSizePolicy::LineEdit, orientation: Qt::Horizontal, option: nullptr, widget: 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(a: maxMinLblWidth, b: label->minSize.width());
418 maxShLblWidth = qMax(a: maxShLblWidth, b: label->sizeHint.width());
419 }
420 if (field) {
421 if (field->fullRow) {
422 maxMinIfldWidth = qMax(a: maxMinIfldWidth, b: field->minSize.width());
423 maxShIfldWidth = qMax(a: maxShIfldWidth, b: field->sizeHint.width());
424 } else {
425 maxMinFldWidth = qMax(a: maxMinFldWidth, b: field->minSize.width() + field->sbsHSpace);
426 maxShFldWidth = qMax(a: maxShFldWidth, b: 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(a: maxShLblWidth, b: qMax(a: maxShIfldWidth, b: maxShFldWidth));
437 min_width = qMax(a: maxMinLblWidth, b: qMax(a: maxMinIfldWidth, b: 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(a: maxShLblWidth + maxShFldWidth, b: maxShIfldWidth);
443 min_width = qMax(a: maxMinLblWidth + maxMinFldWidth, b: maxMinIfldWidth);
444 thresh_width = QWIDGETSIZE_MAX;
445 } else {
446 // This is just the max widths glommed together
447 sh_width = qMax(a: maxShLblWidth + maxShFldWidth, b: 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(a: maxMinLblWidth, b: qMax(a: maxMinIfldWidth, b: 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
462void 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(i: r).spacing;
471 h += hfwLayouts.at(i: r).sizeHint + spacing;
472 mh += hfwLayouts.at(i: r).minimumSize + spacing;
473 }
474
475 if (sh_width > 0 && sh_width == w) {
476 hfw_sh_height = qMin(a: QLAYOUTSIZE_MAX, b: h);
477 hfw_sh_minheight = qMin(a: QLAYOUTSIZE_MAX, b: mh);
478 } else {
479 hfw_width = w;
480 hfw_height = qMin(a: QLAYOUTSIZE_MAX, b: h);
481 }
482}
483
484void 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(asize: 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(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) {
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(asize: (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 // 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(sl&: vLayouts[vidx], item: label);
712
713 if (vidx > 1)
714 vLayouts[vidx - 1].spacing = spacingHelper(parent: q->parentWidget(), style, userVSpacing, recalculate: splitSideBySide || prevRowSplit, item1: label, item2: 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(sl&: vLayouts[vidx], item: field);
730
731 if (vidx > 1)
732 vLayouts[vidx - 1].spacing = spacingHelper(parent: q->parentWidget(), style, userVSpacing, recalculate: splitSideBySide || prevRowSplit, item1: field, item2: 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(stretchFactor: qMax(a: stretch1, b: stretch2), minSize: qMax(a: min1.height(), b: min2.height()));
777 vLayouts[vidx].sizeHint = qMax(a: sh1.height(), b: sh2.height());
778 vLayouts[vidx].maximumSize = qMin(a: max1.height(), b: 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(parent: q->parentWidget(), style, userVSpacing, recalculate: prevRowSplit, item1: label, item2: 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(stretchFactor: 1, minSize: 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(stretchFactor: 1, minSize: 0);
814 vLayouts[0].expansive = true;
815 } else {
816 vLayouts[0].init(stretchFactor: 0, minSize: 0);
817 }
818 } else {
819 vLayouts[0].init(stretchFactor: 0, minSize: 0);
820 }
821
822 vLayoutCount = vidx;
823 dirty = false;
824}
825
826void 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(a: fieldMaxWidth, b: field->maxSize.width());
874 }
875 }
876
877 formMaxWidth = maxLabelWidth + fieldMaxWidth;
878}
879
880void QFormLayoutPrivate::calcSizeHints()
881{
882 Q_Q(QFormLayout);
883
884 int leftMargin, topMargin, rightMargin, bottomMargin;
885 q->getContentsMargins(left: &leftMargin, top: &topMargin, right: &rightMargin, bottom: &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(a: mw, b: QLAYOUTSIZE_MAX);
905 minSize.rheight() = qMin(a: mh, b: QLAYOUTSIZE_MAX);
906 prefSize.rwidth() = qMin(a: w, b: QLAYOUTSIZE_MAX);
907 prefSize.rheight() = qMin(a: h, b: QLAYOUTSIZE_MAX);
908}
909
910int QFormLayoutPrivate::insertRow(int row)
911{
912 int rowCnt = m_matrix.rowCount();
913 if (uint(row) > uint(rowCnt))
914 row = rowCnt;
915
916 insertRows(row, count: 1);
917 return row;
918}
919
920void QFormLayoutPrivate::insertRows(int row, int count)
921{
922 while (count > 0) {
923 m_matrix.insertRow(r: row, value: 0);
924 --count;
925 }
926}
927
928void QFormLayoutPrivate::removeRow(int row)
929{
930 if (uint(row) < uint(m_matrix.rowCount()))
931 m_matrix.removeRow(r: row);
932}
933
934bool 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(msg: "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(msg: "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(t: i);
956 return true;
957}
958
959void 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, item: layout);
965 }
966}
967
968void QFormLayoutPrivate::setWidget(int row, QFormLayout::ItemRole role, QWidget *widget)
969{
970 if (widget) {
971 Q_Q(QFormLayout);
972 q->addChildWidget(w: widget);
973 QWidgetItem *item = QLayoutPrivate::createWidgetItem(layout: q, widget);
974 if (!setItem(row, role, item))
975 delete item;
976 }
977}
978
979QStyle* 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
990QLayoutItem* QFormLayoutPrivate::replaceAt(int index, QLayoutItem *newitem)
991{
992 Q_Q(QFormLayout);
993 if (!newitem)
994 return nullptr;
995 const int storageIndex = storageIndexFromLayoutItem(m: m_matrix, item: m_things.value(i: index));
996 if (Q_UNLIKELY(storageIndex == -1)) {
997 // ### Qt6 - fix warning too when this class becomes public
998 qWarning(msg: "QFormLayoutPrivate::replaceAt: Invalid index %d", index);
999 return nullptr;
1000 }
1001
1002 int row, col;
1003 QFormLayoutPrivate::ItemMatrix::storageIndexToPosition(idx: storageIndex, rowPtr: &row, colPtr: &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*/
1198QFormLayout::QFormLayout(QWidget *parent)
1199 : QLayout(*new QFormLayoutPrivate, nullptr, parent)
1200{
1201}
1202
1203/*!
1204 Destroys the form layout.
1205*/
1206QFormLayout::~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(c: 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*/
1226void QFormLayout::addRow(QWidget *label, QWidget *field)
1227{
1228 insertRow(row: -1, label, field);
1229}
1230
1231/*!
1232 \overload
1233*/
1234void QFormLayout::addRow(QWidget *label, QLayout *field)
1235{
1236 insertRow(row: -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*/
1246void QFormLayout::addRow(const QString &labelText, QWidget *field)
1247{
1248 insertRow(row: -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*/
1257void QFormLayout::addRow(const QString &labelText, QLayout *field)
1258{
1259 insertRow(row: -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*/
1268void QFormLayout::addRow(QWidget *widget)
1269{
1270 insertRow(row: -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*/
1279void QFormLayout::addRow(QLayout *layout)
1280{
1281 insertRow(row: -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*/
1291void QFormLayout::insertRow(int row, QWidget *label, QWidget *field)
1292{
1293 Q_D(QFormLayout);
1294 if ((label && !d->checkWidget(widget: label)) || (field && !d->checkWidget(widget: field)))
1295 return;
1296
1297 row = d->insertRow(row);
1298 if (label)
1299 d->setWidget(row, role: LabelRole, widget: label);
1300 if (field)
1301 d->setWidget(row, role: FieldRole, widget: field);
1302 invalidate();
1303}
1304
1305/*!
1306 \overload
1307*/
1308void QFormLayout::insertRow(int row, QWidget *label, QLayout *field)
1309{
1310 Q_D(QFormLayout);
1311 if ((label && !d->checkWidget(widget: label)) || (field && !d->checkLayout(otherLayout: field)))
1312 return;
1313
1314 row = d->insertRow(row);
1315 if (label)
1316 d->setWidget(row, role: LabelRole, widget: label);
1317 if (field)
1318 d->setLayout(row, role: FieldRole, layout: 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*/
1329void QFormLayout::insertRow(int row, const QString &labelText, QWidget *field)
1330{
1331 Q_D(QFormLayout);
1332 if (field && !d->checkWidget(widget: 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*/
1351void QFormLayout::insertRow(int row, const QString &labelText, QLayout *field)
1352{
1353 Q_D(QFormLayout);
1354 if (field && !d->checkLayout(otherLayout: field))
1355 return;
1356
1357 insertRow(row, label: 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*/
1367void 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, role: 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*/
1385void QFormLayout::insertRow(int row, QLayout *layout)
1386{
1387 Q_D(QFormLayout);
1388 if (!d->checkLayout(otherLayout: layout))
1389 return;
1390
1391 row = d->insertRow(row);
1392 d->setLayout(row, role: SpanningRole, layout);
1393 invalidate();
1394}
1395
1396static 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
1415static 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(index: 0))
1421 clearAndDestroyQLayoutItem(item: 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*/
1446void QFormLayout::removeRow(int row)
1447{
1448 TakeRowResult result = takeRow(row);
1449 clearAndDestroyQLayoutItem(item: result.labelItem);
1450 clearAndDestroyQLayoutItem(item: 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*/
1472void QFormLayout::removeRow(QWidget *widget)
1473{
1474 TakeRowResult result = takeRow(widget);
1475 clearAndDestroyQLayoutItem(item: result.labelItem);
1476 clearAndDestroyQLayoutItem(item: 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*/
1499void QFormLayout::removeRow(QLayout *layout)
1500{
1501 TakeRowResult result = takeRow(layout);
1502 clearAndDestroyQLayoutItem(item: result.labelItem);
1503 clearAndDestroyQLayoutItem(item: 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*/
1528QFormLayout::TakeRowResult QFormLayout::takeRow(int row)
1529{
1530 Q_D(QFormLayout);
1531
1532 if (Q_UNLIKELY(!(uint(row) < uint(d->m_matrix.rowCount())))) {
1533 qWarning(msg: "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(t: label);
1541 d->m_things.removeOne(t: field);
1542 d->m_matrix.removeRow(r: row);
1543
1544 invalidate();
1545
1546 TakeRowResult result;
1547 result.labelItem = ownershipCleanedItem(item: label, layout: this);
1548 result.fieldItem = ownershipCleanedItem(item: field, layout: 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*/
1573QFormLayout::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, rowPtr: &row, rolePtr: &role);
1582
1583 if (Q_UNLIKELY(row < 0)) {
1584 qWarning(msg: "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*/
1613QFormLayout::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, rowPtr: &row, rolePtr: &role);
1622
1623 if (Q_UNLIKELY(row < 0)) {
1624 qWarning(msg: "QFormLayout::takeRow: Invalid layout");
1625 return TakeRowResult();
1626 }
1627
1628 return takeRow(row);
1629}
1630
1631/*!
1632 \reimp
1633*/
1634void QFormLayout::addItem(QLayoutItem *item)
1635{
1636 Q_D(QFormLayout);
1637
1638 int row = d->insertRow(row: d->m_matrix.rowCount());
1639 d->setItem(row, role: FieldRole, item);
1640 invalidate();
1641}
1642
1643/*!
1644 \reimp
1645*/
1646int QFormLayout::count() const
1647{
1648 Q_D(const QFormLayout);
1649 return d->m_things.count();
1650}
1651
1652/*!
1653 \reimp
1654*/
1655QLayoutItem *QFormLayout::itemAt(int index) const
1656{
1657 Q_D(const QFormLayout);
1658 if (QFormLayoutItem *formItem = d->m_things.value(i: index))
1659 return formItem->item;
1660 return nullptr;
1661}
1662
1663/*!
1664 \reimp
1665*/
1666QLayoutItem *QFormLayout::takeAt(int index)
1667{
1668 Q_D(QFormLayout);
1669
1670 const int storageIndex = storageIndexFromLayoutItem(m: d->m_matrix, item: d->m_things.value(i: index));
1671 if (Q_UNLIKELY(storageIndex == -1)) {
1672 qWarning(msg: "QFormLayout::takeAt: Invalid index %d", index);
1673 return nullptr;
1674 }
1675
1676 int row, col;
1677 QFormLayoutPrivate::ItemMatrix::storageIndexToPosition(idx: storageIndex, rowPtr: &row, colPtr: &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(i: index);
1683 d->m_matrix(row, col) = 0;
1684
1685 invalidate();
1686
1687 return ownershipCleanedItem(item, layout: this);
1688}
1689
1690/*!
1691 \reimp
1692*/
1693Qt::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*/
1710bool 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*/
1721int 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(left: &leftMargin, top: &topMargin, right: &rightMargin, bottom: &bottomMargin);
1729
1730 int targetWidth = width - leftMargin - rightMargin;
1731
1732 if (!d->haveHfwCached(width: targetWidth)) {
1733 QFormLayoutPrivate *dat = const_cast<QFormLayoutPrivate *>(d);
1734 dat->setupVerticalLayoutData(targetWidth);
1735 dat->setupHorizontalLayoutData(targetWidth);
1736 dat->recalcHFW(w: 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*/
1747void 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(left: &leftMargin, top: &topMargin, right: &rightMargin, bottom: &bottomMargin);
1754 cr.adjust(dx1: +leftMargin, dy1: +topMargin, dx2: -rightMargin, dy2: -bottomMargin);
1755
1756 bool hfw = hasHeightForWidth();
1757 d->setupVerticalLayoutData(cr.width());
1758 d->setupHorizontalLayoutData(cr.width());
1759 if (hfw && (!d->haveHfwCached(width: cr.width()) || d->hfwLayouts.size() != d->vLayoutCount))
1760 d->recalcHFW(w: cr.width());
1761 if (hfw) {
1762 qGeomCalc(chain&: d->hfwLayouts, start: 0, count: d->vLayoutCount, pos: cr.y(), space: cr.height());
1763 d->arrangeWidgets(layouts: d->hfwLayouts, rect&: cr);
1764 } else {
1765 qGeomCalc(chain&: d->vLayouts, start: 0, count: d->vLayoutCount, pos: cr.y(), space: cr.height());
1766 d->arrangeWidgets(layouts: d->vLayouts, rect&: cr);
1767 }
1768 QLayout::setGeometry(rect);
1769 }
1770}
1771
1772/*!
1773 \reimp
1774*/
1775QSize 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*/
1788QSize 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*/
1802void 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*/
1822int 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*/
1834QLayoutItem *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*/
1862void 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(m: d->m_matrix, item: d->m_things.value(i: index));
1869 if (storageIndex != -1)
1870 QFormLayoutPrivate::ItemMatrix::storageIndexToPosition(idx: storageIndex, rowPtr: &row, colPtr: &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*/
1890void 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*/
1910void QFormLayout::getWidgetPosition(QWidget *widget, int *rowPtr, ItemRole *rolePtr) const
1911{
1912 getItemPosition(index: 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*/
1922QWidget *QFormLayout::labelForField(QWidget *field) const
1923{
1924 Q_D(const QFormLayout);
1925
1926 int row;
1927 ItemRole role = LabelRole;
1928
1929 getWidgetPosition(widget: field, rowPtr: &row, rolePtr: &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*/
1941QWidget *QFormLayout::labelForField(QLayout *field) const
1942{
1943 Q_D(const QFormLayout);
1944
1945 int row;
1946 ItemRole role;
1947
1948 getLayoutPosition(layout: field, rowPtr: &row, rolePtr: &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
1974void QFormLayout::setFieldGrowthPolicy(FieldGrowthPolicy policy)
1975{
1976 Q_D(QFormLayout);
1977 if (FieldGrowthPolicy(d->fieldGrowthPolicy) != policy) {
1978 d->fieldGrowthPolicy = policy;
1979 invalidate();
1980 }
1981}
1982
1983QFormLayout::FieldGrowthPolicy QFormLayout::fieldGrowthPolicy() const
1984{
1985 Q_D(const QFormLayout);
1986 if (d->fieldGrowthPolicy == DefaultFieldGrowthPolicy) {
1987 return QFormLayout::FieldGrowthPolicy(d->getStyle()->styleHint(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
2007void QFormLayout::setRowWrapPolicy(RowWrapPolicy policy)
2008{
2009 Q_D(QFormLayout);
2010 if (RowWrapPolicy(d->rowWrapPolicy) != policy) {
2011 d->rowWrapPolicy = policy;
2012 invalidate();
2013 }
2014}
2015
2016QFormLayout::RowWrapPolicy QFormLayout::rowWrapPolicy() const
2017{
2018 Q_D(const QFormLayout);
2019 if (d->rowWrapPolicy == DefaultRowWrapPolicy) {
2020 return QFormLayout::RowWrapPolicy(d->getStyle()->styleHint(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
2038void QFormLayout::setLabelAlignment(Qt::Alignment alignment)
2039{
2040 Q_D(QFormLayout);
2041 if (d->labelAlignment != alignment) {
2042 d->labelAlignment = alignment;
2043 invalidate();
2044 }
2045}
2046
2047Qt::Alignment QFormLayout::labelAlignment() const
2048{
2049 Q_D(const QFormLayout);
2050 if (!d->labelAlignment) {
2051 return Qt::Alignment(d->getStyle()->styleHint(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
2068void QFormLayout::setFormAlignment(Qt::Alignment alignment)
2069{
2070 Q_D(QFormLayout);
2071 if (d->formAlignment != alignment) {
2072 d->formAlignment = alignment;
2073 invalidate();
2074 }
2075}
2076
2077Qt::Alignment QFormLayout::formAlignment() const
2078{
2079 Q_D(const QFormLayout);
2080 if (!d->formAlignment) {
2081 return Qt::Alignment(d->getStyle()->styleHint(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*/
2097void QFormLayout::setHorizontalSpacing(int spacing)
2098{
2099 Q_D(QFormLayout);
2100 if (spacing != d->hSpacing) {
2101 d->hSpacing = spacing;
2102 invalidate();
2103 }
2104}
2105
2106int QFormLayout::horizontalSpacing() const
2107{
2108 Q_D(const QFormLayout);
2109 if (d->hSpacing >= 0) {
2110 return d->hSpacing;
2111 } else {
2112 return qSmartSpacing(layout: this, pm: 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*/
2126void QFormLayout::setVerticalSpacing(int spacing)
2127{
2128 Q_D(QFormLayout);
2129 if (spacing != d->vSpacing) {
2130 d->vSpacing = spacing;
2131 invalidate();
2132 }
2133}
2134
2135int QFormLayout::verticalSpacing() const
2136{
2137 Q_D(const QFormLayout);
2138 if (d->vSpacing >= 0) {
2139 return d->vSpacing;
2140 } else {
2141 return qSmartSpacing(layout: this, pm: 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*/
2151void 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*/
2164int QFormLayout::spacing() const
2165{
2166 int hSpacing = horizontalSpacing();
2167 if (hSpacing == verticalSpacing()) {
2168 return hSpacing;
2169 } else {
2170 return -1;
2171 }
2172}
2173
2174void 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(alignment: 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(i: 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(a: height,
2206 b: qMin(a: label->sizeHint.height() * 7 / 4,
2207 b: label->maxSize.height()));
2208 }
2209
2210 QSize sz(qMin(a: label->layoutWidth, b: label->sizeHint.width()), height);
2211 int x = leftOffset + rect.x() + label->layoutPos;
2212 const auto fAlign = fixedAlignment(alignment: 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(i: label->vLayoutIndex).pos);
2218 // ### expansion & sizepolicy stuff
2219
2220 label->setGeometry(QStyle::visualRect(direction: layoutDirection, boundingRect: rect, logicalRect: QRect(p, sz)));
2221 }
2222
2223 if (field) {
2224 QSize sz(field->layoutWidth, layouts.at(i: field->vLayoutIndex).size);
2225 QPoint p(field->layoutPos + leftOffset + rect.x(), layouts.at(i: 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(otherSize: field->maxSize);
2234
2235 field->setGeometry(QStyle::visualRect(direction: layoutDirection, boundingRect: rect, logicalRect: 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*/
2251void QFormLayout::setWidget(int row, ItemRole role, QWidget *widget)
2252{
2253 Q_D(QFormLayout);
2254 int rowCnt = rowCount();
2255 if (row >= rowCnt)
2256 d->insertRows(row: rowCnt, count: 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*/
2271void QFormLayout::setLayout(int row, ItemRole role, QLayout *layout)
2272{
2273 Q_D(QFormLayout);
2274 int rowCnt = rowCount();
2275 if (row >= rowCnt)
2276 d->insertRows(row: rowCnt, count: 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*/
2293void QFormLayout::setItem(int row, ItemRole role, QLayoutItem *item)
2294{
2295 Q_D(QFormLayout);
2296 int rowCnt = rowCount();
2297 if (row >= rowCnt)
2298 d->insertRows(row: rowCnt, count: row - rowCnt + 1);
2299 d->setItem(row, role, item);
2300}
2301
2302/*!
2303 \internal
2304 */
2305
2306void QFormLayout::resetFieldGrowthPolicy()
2307{
2308 Q_D(QFormLayout);
2309 d->fieldGrowthPolicy = DefaultFieldGrowthPolicy;
2310}
2311
2312/*!
2313 \internal
2314 */
2315
2316void QFormLayout::resetRowWrapPolicy()
2317{
2318 Q_D(QFormLayout);
2319 d->rowWrapPolicy = DefaultRowWrapPolicy;
2320}
2321
2322/*!
2323 \internal
2324 */
2325
2326void QFormLayout::resetFormAlignment()
2327{
2328 Q_D(QFormLayout);
2329 d->formAlignment = { };
2330}
2331
2332/*!
2333 \internal
2334 */
2335
2336void QFormLayout::resetLabelAlignment()
2337{
2338 Q_D(QFormLayout);
2339 d->labelAlignment = { };
2340}
2341
2342#if 0
2343void 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
2356QT_END_NAMESPACE
2357
2358#include "moc_qformlayout.cpp"
2359

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