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