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