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