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