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*) Q_DECL_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 Q_ASSERT(field);
1563
1564 d->m_things.removeOne(label);
1565 d->m_things.removeOne(field);
1566 d->m_matrix.removeRow(row);
1567
1568 invalidate();
1569
1570 TakeRowResult result;
1571 result.labelItem = ownershipCleanedItem(label, this);
1572 result.fieldItem = ownershipCleanedItem(field, this);
1573 return result;
1574}
1575
1576/*!
1577 \since 5.8
1578
1579 \overload
1580
1581 Removes the specified \a widget from this form layout.
1582
1583 \note This function doesn't delete anything.
1584
1585 After this call, rowCount() is decremented by one. All following rows are shifted
1586 up one row and the freed vertical space is redistributed amongst the remaining rows.
1587
1588 \code
1589 QFormLayout *flay = ...;
1590 QPointer<QLineEdit> le = new QLineEdit;
1591 flay->insertRow(2, "User:", le);
1592 // later:
1593 QFormLayout::TakeRowResult result = flay->takeRow(widget);
1594 \endcode
1595
1596 If you want to remove the row from the layout and delete the widgets, use removeRow() instead.
1597
1598 \return A structure containing both the widget and
1599 corresponding label layout items
1600
1601 \sa removeRow()
1602*/
1603QFormLayout::TakeRowResult QFormLayout::takeRow(QWidget *widget)
1604{
1605 Q_D(QFormLayout);
1606 if (Q_UNLIKELY(!d->checkWidget(widget)))
1607 return TakeRowResult();
1608
1609 int row;
1610 ItemRole role;
1611 getWidgetPosition(widget, &row, &role);
1612
1613 if (Q_UNLIKELY(row < 0)) {
1614 qWarning("QFormLayout::takeRow: Invalid widget");
1615 return TakeRowResult();
1616 }
1617
1618 return takeRow(row);
1619}
1620
1621/*!
1622 \since 5.8
1623
1624 \overload
1625
1626 Removes the specified \a layout from this form layout.
1627
1628 \note This function doesn't delete anything.
1629
1630 After this call, rowCount() is decremented by one. All following rows are shifted
1631 up one row and the freed vertical space is redistributed amongst the remaining rows.
1632
1633 \code
1634 QFormLayout *flay = ...;
1635 QPointer<QVBoxLayout> vbl = new QVBoxLayout;
1636 flay->insertRow(2, "User:", vbl);
1637 // later:
1638 QFormLayout::TakeRowResult result = flay->takeRow(widget);
1639 \endcode
1640
1641 If you want to remove the row from the form layout and delete the inserted layout,
1642 use removeRow() instead.
1643
1644 \return A structure containing both the widget and
1645 corresponding label layout items
1646
1647 \sa removeRow()
1648*/
1649QFormLayout::TakeRowResult QFormLayout::takeRow(QLayout *layout)
1650{
1651 Q_D(QFormLayout);
1652 if (Q_UNLIKELY(!d->checkLayout(layout)))
1653 return TakeRowResult();
1654
1655 int row;
1656 ItemRole role;
1657 getLayoutPosition(layout, &row, &role);
1658
1659 if (Q_UNLIKELY(row < 0)) {
1660 qWarning("QFormLayout::takeRow: Invalid layout");
1661 return TakeRowResult();
1662 }
1663
1664 return takeRow(row);
1665}
1666
1667/*!
1668 \reimp
1669*/
1670void QFormLayout::addItem(QLayoutItem *item)
1671{
1672 Q_D(QFormLayout);
1673
1674 int row = d->insertRow(d->m_matrix.rowCount());
1675 d->setItem(row, FieldRole, item);
1676 invalidate();
1677}
1678
1679/*!
1680 \reimp
1681*/
1682int QFormLayout::count() const
1683{
1684 Q_D(const QFormLayout);
1685 return d->m_things.count();
1686}
1687
1688/*!
1689 \reimp
1690*/
1691QLayoutItem *QFormLayout::itemAt(int index) const
1692{
1693 Q_D(const QFormLayout);
1694 if (QFormLayoutItem *formItem = d->m_things.value(index))
1695 return formItem->item;
1696 return 0;
1697}
1698
1699/*!
1700 \reimp
1701*/
1702QLayoutItem *QFormLayout::takeAt(int index)
1703{
1704 Q_D(QFormLayout);
1705
1706 const int storageIndex = storageIndexFromLayoutItem(d->m_matrix, d->m_things.value(index));
1707 if (Q_UNLIKELY(storageIndex == -1)) {
1708 qWarning("QFormLayout::takeAt: Invalid index %d", index);
1709 return 0;
1710 }
1711
1712 int row, col;
1713 QFormLayoutPrivate::ItemMatrix::storageIndexToPosition(storageIndex, &row, &col);
1714 Q_ASSERT(d->m_matrix(row, col));
1715
1716 QFormLayoutItem *item = d->m_matrix(row, col);
1717 Q_ASSERT(item);
1718 d->m_things.removeAt(index);
1719 d->m_matrix(row, col) = 0;
1720
1721 invalidate();
1722
1723 return ownershipCleanedItem(item, this);
1724}
1725
1726/*!
1727 \reimp
1728*/
1729Qt::Orientations QFormLayout::expandingDirections() const
1730{
1731 Q_D(const QFormLayout);
1732 QFormLayoutPrivate *e = const_cast<QFormLayoutPrivate *>(d);
1733 e->updateSizes();
1734
1735 Qt::Orientations o = 0;
1736 if (e->expandHorizontal)
1737 o = Qt::Horizontal;
1738 if (e->expandVertical)
1739 o |= Qt::Vertical;
1740 return o;
1741}
1742
1743/*!
1744 \reimp
1745*/
1746bool QFormLayout::hasHeightForWidth() const
1747{
1748 Q_D(const QFormLayout);
1749 QFormLayoutPrivate *e = const_cast<QFormLayoutPrivate *>(d);
1750 e->updateSizes();
1751 return (d->has_hfw || rowWrapPolicy() == WrapLongRows);
1752}
1753
1754/*!
1755 \reimp
1756*/
1757int QFormLayout::heightForWidth(int width) const
1758{
1759 Q_D(const QFormLayout);
1760 if (!hasHeightForWidth())
1761 return -1;
1762
1763 int leftMargin, topMargin, rightMargin, bottomMargin;
1764 getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
1765
1766 int targetWidth = width - leftMargin - rightMargin;
1767
1768 if (!d->haveHfwCached(targetWidth)) {
1769 QFormLayoutPrivate *dat = const_cast<QFormLayoutPrivate *>(d);
1770 dat->setupVerticalLayoutData(targetWidth);
1771 dat->setupHorizontalLayoutData(targetWidth);
1772 dat->recalcHFW(targetWidth);
1773 }
1774 if (targetWidth == d->sh_width)
1775 return d->hfw_sh_height + topMargin + bottomMargin;
1776 else
1777 return d->hfw_height + topMargin + bottomMargin;
1778}
1779
1780/*!
1781 \reimp
1782*/
1783void QFormLayout::setGeometry(const QRect &rect)
1784{
1785 Q_D(QFormLayout);
1786 if (d->dirty || rect != geometry()) {
1787 QRect cr = rect;
1788 int leftMargin, topMargin, rightMargin, bottomMargin;
1789 getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
1790 cr.adjust(+leftMargin, +topMargin, -rightMargin, -bottomMargin);
1791
1792 bool hfw = hasHeightForWidth();
1793 d->setupVerticalLayoutData(cr.width());
1794 d->setupHorizontalLayoutData(cr.width());
1795 if (hfw && (!d->haveHfwCached(cr.width()) || d->hfwLayouts.size() != d->vLayoutCount))
1796 d->recalcHFW(cr.width());
1797 if (hfw) {
1798 qGeomCalc(d->hfwLayouts, 0, d->vLayoutCount, cr.y(), cr.height());
1799 d->arrangeWidgets(d->hfwLayouts, cr);
1800 } else {
1801 qGeomCalc(d->vLayouts, 0, d->vLayoutCount, cr.y(), cr.height());
1802 d->arrangeWidgets(d->vLayouts, cr);
1803 }
1804 QLayout::setGeometry(rect);
1805 }
1806}
1807
1808/*!
1809 \reimp
1810*/
1811QSize QFormLayout::sizeHint() const
1812{
1813 Q_D(const QFormLayout);
1814 if (!d->prefSize.isValid()) {
1815 QFormLayoutPrivate *dat = const_cast<QFormLayoutPrivate *>(d);
1816 dat->calcSizeHints();
1817 }
1818 return d->prefSize;
1819}
1820
1821/*!
1822 \reimp
1823*/
1824QSize QFormLayout::minimumSize() const
1825{
1826 // ### fix minimumSize if hfw
1827 Q_D(const QFormLayout);
1828 if (!d->minSize.isValid()) {
1829 QFormLayoutPrivate *dat = const_cast<QFormLayoutPrivate *>(d);
1830 dat->calcSizeHints();
1831 }
1832 return d->minSize;
1833}
1834
1835/*!
1836 \reimp
1837*/
1838void QFormLayout::invalidate()
1839{
1840 Q_D(QFormLayout);
1841 d->dirty = true;
1842 d->sizesDirty = true;
1843 d->minSize = QSize();
1844 d->prefSize = QSize();
1845 d->formMaxWidth = -1;
1846 d->hfw_width = -1;
1847 d->sh_width = -1;
1848 d->layoutWidth = -1;
1849 d->hfw_sh_height = -1;
1850 QLayout::invalidate();
1851}
1852
1853/*!
1854 Returns the number of rows in the form.
1855
1856 \sa QLayout::count()
1857*/
1858int QFormLayout::rowCount() const
1859{
1860 Q_D(const QFormLayout);
1861 return d->m_matrix.rowCount();
1862}
1863
1864/*!
1865 Returns the layout item in the given \a row with the specified \a
1866 role (column). Returns 0 if there is no such item.
1867
1868 \sa QLayout::itemAt(), setItem()
1869*/
1870QLayoutItem *QFormLayout::itemAt(int row, ItemRole role) const
1871{
1872 Q_D(const QFormLayout);
1873 if (uint(row) >= uint(d->m_matrix.rowCount()))
1874 return 0;
1875 switch (role) {
1876 case SpanningRole:
1877 if (QFormLayoutItem *item = d->m_matrix(row, 1))
1878 if (item->fullRow)
1879 return item->item;
1880 break;
1881 case LabelRole:
1882 case FieldRole:
1883 if (QFormLayoutItem *item = d->m_matrix(row, (role == LabelRole) ? 0 : 1))
1884 return item->item;
1885 break;
1886 }
1887 return 0;
1888}
1889
1890/*!
1891 Retrieves the row and role (column) of the item at the specified
1892 \a index. If \a index is out of bounds, *\a rowPtr is set to -1;
1893 otherwise the row is stored in *\a rowPtr and the role is stored
1894 in *\a rolePtr.
1895
1896 \sa itemAt(), count(), getLayoutPosition(), getWidgetPosition()
1897*/
1898void QFormLayout::getItemPosition(int index, int *rowPtr, ItemRole *rolePtr) const
1899{
1900 Q_D(const QFormLayout);
1901 int col = -1;
1902 int row = -1;
1903
1904 const int storageIndex = storageIndexFromLayoutItem(d->m_matrix, d->m_things.value(index));
1905 if (storageIndex != -1)
1906 QFormLayoutPrivate::ItemMatrix::storageIndexToPosition(storageIndex, &row, &col);
1907
1908 if (rowPtr)
1909 *rowPtr = row;
1910 if (rolePtr && col != -1) {
1911 const bool spanning = col == 1 && d->m_matrix(row, col)->fullRow;
1912 if (spanning) {
1913 *rolePtr = SpanningRole;
1914 } else {
1915 *rolePtr = ItemRole(col);
1916 }
1917 }
1918}
1919
1920/*!
1921 Retrieves the row and role (column) of the specified child \a
1922 layout. If \a layout is not in the form layout, *\a rowPtr is set
1923 to -1; otherwise the row is stored in *\a rowPtr and the role is stored
1924 in *\a rolePtr.
1925*/
1926void QFormLayout::getLayoutPosition(QLayout *layout, int *rowPtr, ItemRole *rolePtr) const
1927{
1928 int n = count();
1929 int index = 0;
1930 while (index < n) {
1931 if (itemAt(index) == layout)
1932 break;
1933 ++index;
1934 }
1935 getItemPosition(index, rowPtr, rolePtr);
1936}
1937
1938/*!
1939 Retrieves the row and role (column) of the specified \a widget in
1940 the layout. If \a widget is not in the layout, *\a rowPtr is set
1941 to -1; otherwise the row is stored in *\a rowPtr and the role is stored
1942 in *\a rolePtr.
1943
1944 \sa getItemPosition(), itemAt()
1945*/
1946void QFormLayout::getWidgetPosition(QWidget *widget, int *rowPtr, ItemRole *rolePtr) const
1947{
1948 getItemPosition(indexOf(widget), rowPtr, rolePtr);
1949}
1950
1951// ### eliminate labelForField()
1952
1953/*!
1954 Returns the label associated with the given \a field.
1955
1956 \sa itemAt()
1957*/
1958QWidget *QFormLayout::labelForField(QWidget *field) const
1959{
1960 Q_D(const QFormLayout);
1961
1962 int row;
1963 ItemRole role = LabelRole;
1964
1965 getWidgetPosition(field, &row, &role);
1966
1967 if (row != -1 && role == FieldRole) {
1968 if (QFormLayoutItem *label = d->m_matrix(row, LabelRole))
1969 return label->widget();
1970 }
1971 return 0;
1972}
1973
1974/*!
1975 \overload
1976*/
1977QWidget *QFormLayout::labelForField(QLayout *field) const
1978{
1979 Q_D(const QFormLayout);
1980
1981 int row;
1982 ItemRole role;
1983
1984 getLayoutPosition(field, &row, &role);
1985
1986 if (row != -1 && role == FieldRole) {
1987 if (QFormLayoutItem *label = d->m_matrix(row, LabelRole))
1988 return label->widget();
1989 }
1990 return 0;
1991}
1992
1993/*!
1994 \property QFormLayout::fieldGrowthPolicy
1995 \brief the way in which the form's fields grow
1996
1997 The default value depends on the widget or application style. For
1998 QMacStyle, the default is FieldsStayAtSizeHint; for QCommonStyle
1999 derived styles (like Plastique and Windows), the default
2000 is ExpandingFieldsGrow; for Qt Extended styles, the default is
2001 AllNonFixedFieldsGrow.
2002
2003 If none of the fields can grow and the form is resized, extra
2004 space is distributed according to the current
2005 \l{formAlignment}{form alignment}.
2006
2007 \sa formAlignment, rowWrapPolicy
2008*/
2009
2010void QFormLayout::setFieldGrowthPolicy(FieldGrowthPolicy policy)
2011{
2012 Q_D(QFormLayout);
2013 if (FieldGrowthPolicy(d->fieldGrowthPolicy) != policy) {
2014 d->fieldGrowthPolicy = policy;
2015 invalidate();
2016 }
2017}
2018
2019QFormLayout::FieldGrowthPolicy QFormLayout::fieldGrowthPolicy() const
2020{
2021 Q_D(const QFormLayout);
2022 if (d->fieldGrowthPolicy == DefaultFieldGrowthPolicy) {
2023 return QFormLayout::FieldGrowthPolicy(d->getStyle()->styleHint(QStyle::SH_FormLayoutFieldGrowthPolicy));
2024 } else {
2025 return QFormLayout::FieldGrowthPolicy(d->fieldGrowthPolicy);
2026 }
2027}
2028
2029/*!
2030 \property QFormLayout::rowWrapPolicy
2031 \brief the way in which the form's rows wrap
2032
2033 The default value depends on the widget or application style. For
2034 Qt Extended styles, the default is WrapLongRows;
2035 for the other styles, the default is DontWrapRows.
2036
2037 If you want to display each label above its associated field
2038 (instead of next to it), set this property to WrapAllRows.
2039
2040 \sa fieldGrowthPolicy
2041*/
2042
2043void QFormLayout::setRowWrapPolicy(RowWrapPolicy policy)
2044{
2045 Q_D(QFormLayout);
2046 if (RowWrapPolicy(d->rowWrapPolicy) != policy) {
2047 d->rowWrapPolicy = policy;
2048 invalidate();
2049 }
2050}
2051
2052QFormLayout::RowWrapPolicy QFormLayout::rowWrapPolicy() const
2053{
2054 Q_D(const QFormLayout);
2055 if (d->rowWrapPolicy == DefaultRowWrapPolicy) {
2056 return QFormLayout::RowWrapPolicy(d->getStyle()->styleHint(QStyle::SH_FormLayoutWrapPolicy));
2057 } else {
2058 return QFormLayout::RowWrapPolicy(d->rowWrapPolicy);
2059 }
2060}
2061
2062/*!
2063 \property QFormLayout::labelAlignment
2064 \brief the horizontal alignment of the labels
2065
2066 The default value depends on the widget or application style. For
2067 QCommonStyle derived styles, except for QPlastiqueStyle, the
2068 default is Qt::AlignLeft; for the other styles, the default is
2069 Qt::AlignRight.
2070
2071 \sa formAlignment
2072*/
2073
2074void QFormLayout::setLabelAlignment(Qt::Alignment alignment)
2075{
2076 Q_D(QFormLayout);
2077 if (d->labelAlignment != alignment) {
2078 d->labelAlignment = alignment;
2079 invalidate();
2080 }
2081}
2082
2083Qt::Alignment QFormLayout::labelAlignment() const
2084{
2085 Q_D(const QFormLayout);
2086 if (!d->labelAlignment) {
2087 return Qt::Alignment(d->getStyle()->styleHint(QStyle::SH_FormLayoutLabelAlignment));
2088 } else {
2089 return d->labelAlignment;
2090 }
2091}
2092
2093/*!
2094 \property QFormLayout::formAlignment
2095 \brief the alignment of the form layout's contents within the layout's geometry
2096
2097 The default value depends on the widget or application style. For
2098 QMacStyle, the default is Qt::AlignHCenter | Qt::AlignTop; for the
2099 other styles, the default is Qt::AlignLeft | Qt::AlignTop.
2100
2101 \sa labelAlignment, rowWrapPolicy
2102*/
2103
2104void QFormLayout::setFormAlignment(Qt::Alignment alignment)
2105{
2106 Q_D(QFormLayout);
2107 if (d->formAlignment != alignment) {
2108 d->formAlignment = alignment;
2109 invalidate();
2110 }
2111}
2112
2113Qt::Alignment QFormLayout::formAlignment() const
2114{
2115 Q_D(const QFormLayout);
2116 if (!d->formAlignment) {
2117 return Qt::Alignment(d->getStyle()->styleHint(QStyle::SH_FormLayoutFormAlignment));
2118 } else {
2119 return d->formAlignment;
2120 }
2121}
2122
2123/*!
2124 \property QFormLayout::horizontalSpacing
2125 \brief the spacing between widgets that are laid out side by side
2126
2127 By default, if no value is explicitly set, the layout's horizontal
2128 spacing is inherited from the parent layout, or from the style settings
2129 for the parent widget.
2130
2131 \sa verticalSpacing, QStyle::pixelMetric(), {QStyle::}{PM_LayoutHorizontalSpacing}
2132*/
2133void QFormLayout::setHorizontalSpacing(int spacing)
2134{
2135 Q_D(QFormLayout);
2136 if (spacing != d->hSpacing) {
2137 d->hSpacing = spacing;
2138 invalidate();
2139 }
2140}
2141
2142int QFormLayout::horizontalSpacing() const
2143{
2144 Q_D(const QFormLayout);
2145 if (d->hSpacing >= 0) {
2146 return d->hSpacing;
2147 } else {
2148 return qSmartSpacing(this, QStyle::PM_LayoutHorizontalSpacing);
2149 }
2150}
2151
2152/*!
2153 \property QFormLayout::verticalSpacing
2154 \brief the spacing between widgets that are laid out vertically
2155
2156 By default, if no value is explicitly set, the layout's vertical spacing is
2157 inherited from the parent layout, or from the style settings for the parent
2158 widget.
2159
2160 \sa horizontalSpacing, QStyle::pixelMetric(), {QStyle::}{PM_LayoutHorizontalSpacing}
2161*/
2162void QFormLayout::setVerticalSpacing(int spacing)
2163{
2164 Q_D(QFormLayout);
2165 if (spacing != d->vSpacing) {
2166 d->vSpacing = spacing;
2167 invalidate();
2168 }
2169}
2170
2171int QFormLayout::verticalSpacing() const
2172{
2173 Q_D(const QFormLayout);
2174 if (d->vSpacing >= 0) {
2175 return d->vSpacing;
2176 } else {
2177 return qSmartSpacing(this, QStyle::PM_LayoutVerticalSpacing);
2178 }
2179}
2180
2181/*!
2182 This function sets both the vertical and horizontal spacing to
2183 \a spacing.
2184
2185 \sa setVerticalSpacing(), setHorizontalSpacing()
2186*/
2187void QFormLayout::setSpacing(int spacing)
2188{
2189 Q_D(QFormLayout);
2190 d->vSpacing = d->hSpacing = spacing;
2191 invalidate();
2192}
2193
2194/*!
2195 If the vertical spacing is equal to the horizontal spacing,
2196 this function returns that value; otherwise it returns -1.
2197
2198 \sa setSpacing(), verticalSpacing(), horizontalSpacing()
2199*/
2200int QFormLayout::spacing() const
2201{
2202 int hSpacing = horizontalSpacing();
2203 if (hSpacing == verticalSpacing()) {
2204 return hSpacing;
2205 } else {
2206 return -1;
2207 }
2208}
2209
2210void QFormLayoutPrivate::arrangeWidgets(const QVector<QLayoutStruct>& layouts, QRect &rect)
2211{
2212 Q_Q(QFormLayout);
2213
2214 int i;
2215 const int rr = m_matrix.rowCount();
2216 QWidget *w = q->parentWidget();
2217 Qt::LayoutDirection layoutDirection = w ? w->layoutDirection() : QApplication::layoutDirection();
2218
2219 Qt::Alignment formAlignment = fixedAlignment(q->formAlignment(), layoutDirection);
2220 int leftOffset = 0;
2221 int delta = rect.width() - formMaxWidth;
2222 if (formAlignment & (Qt::AlignHCenter | Qt::AlignRight) && delta > 0) {
2223 leftOffset = delta;
2224 if (formAlignment & Qt::AlignHCenter)
2225 leftOffset >>= 1;
2226 }
2227
2228 for (i = 0; i < rr; ++i) {
2229 QFormLayoutItem *label = m_matrix(i, 0);
2230 QFormLayoutItem *field = m_matrix(i, 1);
2231
2232 if (label) {
2233 int height = layouts.at(label->vLayoutIndex).size;
2234 if ((label->expandingDirections() & Qt::Vertical) == 0) {
2235 /*
2236 If the field on the right-hand side is tall,
2237 we want the label to be top-aligned, but not too
2238 much. So we introduce a 7 / 4 factor so that it
2239 gets some extra pixels at the top.
2240 */
2241 height = qMin(height,
2242 qMin(label->sizeHint.height() * 7 / 4,
2243 label->maxSize.height()));
2244 }
2245
2246 QSize sz(qMin(label->layoutWidth, label->sizeHint.width()), height);
2247 int x = leftOffset + rect.x() + label->layoutPos;
2248 if (fixedAlignment(q->labelAlignment(), layoutDirection) & Qt::AlignRight)
2249 x += label->layoutWidth - sz.width();
2250 QPoint p(x, layouts.at(label->vLayoutIndex).pos);
2251 // ### expansion & sizepolicy stuff
2252
2253 label->setGeometry(QStyle::visualRect(layoutDirection, rect, QRect(p, sz)));
2254 }
2255
2256 if (field) {
2257 QSize sz(field->layoutWidth, layouts.at(field->vLayoutIndex).size);
2258 QPoint p(field->layoutPos + leftOffset + rect.x(), layouts.at(field->vLayoutIndex).pos);
2259/*
2260 if ((field->widget() && field->widget()->sizePolicy().horizontalPolicy() & (QSizePolicy::GrowFlag | QSizePolicy::ExpandFlag | QSizePolicy::IgnoreFlag))
2261 || (field->layout() && sz.width() < field->maxSize.width())) {
2262 sz.rwidth() = field->layoutWidth;
2263 }
2264*/
2265 if (field->maxSize.isValid())
2266 sz = sz.boundedTo(field->maxSize);
2267
2268 field->setGeometry(QStyle::visualRect(layoutDirection, rect, QRect(p, sz)));
2269 }
2270 }
2271}
2272
2273/*!
2274 Sets the widget in the given \a row for the given \a role to \a widget, extending the
2275 layout with empty rows if necessary.
2276
2277 If the cell is already occupied, the \a widget is not inserted and an error message is
2278 sent to the console.
2279
2280 \b{Note:} For most applications, addRow() or insertRow() should be used instead of setWidget().
2281
2282 \sa setLayout()
2283*/
2284void QFormLayout::setWidget(int row, ItemRole role, QWidget *widget)
2285{
2286 Q_D(QFormLayout);
2287 int rowCnt = rowCount();
2288 if (row >= rowCnt)
2289 d->insertRows(rowCnt, row - rowCnt + 1);
2290 d->setWidget(row, role, widget);
2291}
2292
2293/*!
2294 Sets the sub-layout in the given \a row for the given \a role to \a layout, extending the
2295 form layout with empty rows if necessary.
2296
2297 If the cell is already occupied, the \a layout is not inserted and an error message is
2298 sent to the console.
2299
2300 \b{Note:} For most applications, addRow() or insertRow() should be used instead of setLayout().
2301
2302 \sa setWidget()
2303*/
2304void QFormLayout::setLayout(int row, ItemRole role, QLayout *layout)
2305{
2306 Q_D(QFormLayout);
2307 int rowCnt = rowCount();
2308 if (row >= rowCnt)
2309 d->insertRows(rowCnt, row - rowCnt + 1);
2310 d->setLayout(row, role, layout);
2311}
2312
2313/*!
2314 Sets the item in the given \a row for the given \a role to \a item, extending the
2315 layout with empty rows if necessary.
2316
2317 If the cell is already occupied, the \a item is not inserted and an error message is
2318 sent to the console.
2319 The \a item spans both columns.
2320
2321 \warning Do not use this function to add child layouts or child
2322 widget items. Use setLayout() or setWidget() instead.
2323
2324 \sa setLayout()
2325*/
2326void QFormLayout::setItem(int row, ItemRole role, QLayoutItem *item)
2327{
2328 Q_D(QFormLayout);
2329 int rowCnt = rowCount();
2330 if (row >= rowCnt)
2331 d->insertRows(rowCnt, row - rowCnt + 1);
2332 d->setItem(row, role, item);
2333}
2334
2335/*!
2336 \internal
2337 */
2338
2339void QFormLayout::resetFieldGrowthPolicy()
2340{
2341 Q_D(QFormLayout);
2342 d->fieldGrowthPolicy = DefaultFieldGrowthPolicy;
2343}
2344
2345/*!
2346 \internal
2347 */
2348
2349void QFormLayout::resetRowWrapPolicy()
2350{
2351 Q_D(QFormLayout);
2352 d->rowWrapPolicy = DefaultRowWrapPolicy;
2353}
2354
2355/*!
2356 \internal
2357 */
2358
2359void QFormLayout::resetFormAlignment()
2360{
2361 Q_D(QFormLayout);
2362 d->formAlignment = 0;
2363}
2364
2365/*!
2366 \internal
2367 */
2368
2369void QFormLayout::resetLabelAlignment()
2370{
2371 Q_D(QFormLayout);
2372 d->labelAlignment = 0;
2373}
2374
2375#if 0
2376void QFormLayout::dump() const
2377{
2378 Q_D(const QFormLayout);
2379 for (int i = 0; i < rowCount(); ++i) {
2380 for (int j = 0; j < 2; ++j) {
2381 qDebug("m_matrix(%d, %d) = %p", i, j, d->m_matrix(i, j));
2382 }
2383 }
2384 for (int i = 0; i < d->m_things.count(); ++i)
2385 qDebug("m_things[%d] = %p", i, d->m_things.at(i));
2386}
2387#endif
2388
2389QT_END_NAMESPACE
2390
2391#include "moc_qformlayout.cpp"
2392