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