1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui 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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://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 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qapplication.h"
43 #include "qdebug.h"
44 #include "qformlayout.h"
45 #include "qlabel.h"
46 #include "qlayout_p.h"
47 #include "qlayoutengine_p.h"
48 #include "qrect.h"
49 #include "qvector.h"
50 #include "qwidget.h"
51
52 QT_BEGIN_NAMESPACE
53
54 namespace {
55 // Fixed column matrix, stores items as [i11, i12, i21, i22...],
56 // with FORTRAN-style index operator(r, c).
57 template <class T, int NumColumns>
58 class FixedColumnMatrix {
59 public:
60 typedef QVector<T> Storage;
61
FixedColumnMatrix()62 FixedColumnMatrix() { }
63
clear()64 void clear() { m_storage.clear(); }
65
operator ()(int r,int c) const66 const T &operator()(int r, int c) const { return m_storage[r * NumColumns + c]; }
operator ()(int r,int c)67 T &operator()(int r, int c) { return m_storage[r * NumColumns + c]; }
68
rowCount() const69 int rowCount() const { return m_storage.size() / NumColumns; }
70 void addRow(const T &value);
71 void insertRow(int r, const T &value);
72 void removeRow(int r);
73
74 bool find(const T &value, int *rowPtr, int *colPtr) const ;
count(const T & value) const75 int count(const T &value) const { return m_storage.count(value); }
76
77 // Hmmpf.. Some things are faster that way.
storage() const78 const Storage &storage() const { return m_storage; }
79
80 static void storageIndexToPosition(int idx, int *rowPtr, int *colPtr);
81
82 private:
83 Storage m_storage;
84 };
85
86 template <class T, int NumColumns>
addRow(const T & value)87 void FixedColumnMatrix<T, NumColumns>::addRow(const T &value)
88 {
89 for (int i = 0; i < NumColumns; ++i)
90 m_storage.append(value);
91 }
92
93 template <class T, int NumColumns>
insertRow(int r,const T & value)94 void FixedColumnMatrix<T, NumColumns>::insertRow(int r, const T &value)
95 {
96 Q_TYPENAME Storage::iterator it = m_storage.begin();
97 it += r * NumColumns;
98 m_storage.insert(it, NumColumns, value);
99 }
100
101 template <class T, int NumColumns>
removeRow(int r)102 void FixedColumnMatrix<T, NumColumns>::removeRow(int r)
103 {
104 m_storage.remove(r * NumColumns, NumColumns);
105 }
106
107 template <class T, int NumColumns>
find(const T & value,int * rowPtr,int * colPtr) const108 bool FixedColumnMatrix<T, NumColumns>::find(const T &value, int *rowPtr, int *colPtr) const
109 {
110 const int idx = m_storage.indexOf(value);
111 if (idx == -1)
112 return false;
113 storageIndexToPosition(idx, rowPtr, colPtr);
114 return true;
115 }
116
117 template <class T, int NumColumns>
storageIndexToPosition(int idx,int * rowPtr,int * colPtr)118 void FixedColumnMatrix<T, NumColumns>::storageIndexToPosition(int idx, int *rowPtr, int *colPtr)
119 {
120 *rowPtr = idx / NumColumns;
121 *colPtr = idx % NumColumns;
122 }
123 } // namespace
124
125 // special values for unset fields; must not clash with values of FieldGrowthPolicy or
126 // RowWrapPolicy
127 const uint DefaultFieldGrowthPolicy = 255;
128 const uint DefaultRowWrapPolicy = 255;
129
130 enum { ColumnCount = 2 };
131
132 // -- our data structure for our items
133 // This owns the QLayoutItem
134 struct QFormLayoutItem
135 {
QFormLayoutItemQFormLayoutItem136 QFormLayoutItem(QLayoutItem* i) : item(i), fullRow(false), isHfw(false) { }
~QFormLayoutItemQFormLayoutItem137 ~QFormLayoutItem() { delete item; }
138
139 // Wrappers
widgetQFormLayoutItem140 QWidget *widget() const { return item->widget(); }
layoutQFormLayoutItem141 QLayout *layout() const { return item->layout(); }
142
hasHeightForWidthQFormLayoutItem143 bool hasHeightForWidth() const { return item->hasHeightForWidth(); }
heightForWidthQFormLayoutItem144 int heightForWidth(int width) const { return item->heightForWidth(width); }
minimumHeightForWidthQFormLayoutItem145 int minimumHeightForWidth(int width) const { return item->minimumHeightForWidth(width); }
expandingDirectionsQFormLayoutItem146 Qt::Orientations expandingDirections() const { return item->expandingDirections(); }
controlTypesQFormLayoutItem147 QSizePolicy::ControlTypes controlTypes() const { return item->controlTypes(); }
vStretchQFormLayoutItem148 int vStretch() const { return widget() ? widget()->sizePolicy().verticalStretch() : 0; }
149
setGeometryQFormLayoutItem150 void setGeometry(const QRect& r) { item->setGeometry(r); }
geometryQFormLayoutItem151 QRect geometry() const { return item->geometry(); }
152
153 // For use with FixedColumnMatrix
operator ==QFormLayoutItem154 bool operator==(const QFormLayoutItem& other) { return item == other.item; }
155
156 QLayoutItem *item;
157 bool fullRow;
158
159 // set by updateSizes
160 bool isHfw;
161 QSize minSize;
162 QSize sizeHint;
163 QSize maxSize;
164
165 // also set by updateSizes
166 int sbsHSpace; // only used for side by side, for the field item only (not label)
167 int vSpace; // This is the spacing to the item in the row above
168
169 // set by setupVerticalLayoutData
170 bool sideBySide;
171 int vLayoutIndex;
172
173 // set by setupHorizontalLayoutData
174 int layoutPos;
175 int layoutWidth;
176 };
177
178 class QFormLayoutPrivate : public QLayoutPrivate
179 {
180 Q_DECLARE_PUBLIC(QFormLayout)
181
182 public:
183 typedef FixedColumnMatrix<QFormLayoutItem *, ColumnCount> ItemMatrix;
184
185 QFormLayoutPrivate();
~QFormLayoutPrivate()186 ~QFormLayoutPrivate() { }
187
188 int insertRow(int row);
189 void insertRows(int row, int count);
190 void setItem(int row, QFormLayout::ItemRole role, QLayoutItem *item);
191 void setLayout(int row, QFormLayout::ItemRole role, QLayout *layout);
192 void setWidget(int row, QFormLayout::ItemRole role, QWidget *widget);
193
194 void arrangeWidgets(const QVector<QLayoutStruct>& layouts, QRect &rect);
195
196 void updateSizes();
197
198 void setupVerticalLayoutData(int width);
199 void setupHorizontalLayoutData(int width);
200
201 QStyle* getStyle() const;
202
haveHfwCached(int width) const203 inline bool haveHfwCached(int width) const
204 {
205 return (hfw_width == width) || (width == sh_width && hfw_sh_height >= 0);
206 }
207
208 void recalcHFW(int w);
209 void setupHfwLayoutData();
210
211 uint fieldGrowthPolicy : 8;
212 uint rowWrapPolicy : 8;
213 uint has_hfw : 2;
214 uint dirty : 2; // have we laid out yet?
215 uint sizesDirty : 2; // have we (not) gathered layout item sizes?
216 uint expandVertical : 1; // Do we expand vertically?
217 uint expandHorizontal : 1; // Do we expand horizonally?
218 Qt::Alignment labelAlignment;
219 Qt::Alignment formAlignment;
220
221 ItemMatrix m_matrix;
222 QList<QFormLayoutItem *> m_things;
223
224 int layoutWidth; // the last width that we called setupVerticalLayoutData on (for vLayouts)
225
226 int hfw_width; // the last width we calculated HFW for
227 int hfw_height; // what that height was
228 int hfw_minheight; // what that minheight was
229
230 int hfw_sh_height; // the hfw for sh_width
231 int hfw_sh_minheight; // the minhfw for sh_width
232
233 int min_width; // the width that gets turned into minSize (from updateSizes)
234 int sh_width; // the width that gets turned into prefSize (from updateSizes)
235 int thresh_width; // the width that we start splitting label/field pairs at (from updateSizes)
236 QSize minSize;
237 QSize prefSize;
238 int formMaxWidth;
239 void calcSizeHints();
240
241 QVector<QLayoutStruct> vLayouts; // set by setupVerticalLayoutData;
242 int vLayoutCount; // Number of rows we calculated in setupVerticalLayoutData
243 int maxLabelWidth; // the label width we calculated in setupVerticalLayoutData
244
245 QVector<QLayoutStruct> hfwLayouts;
246
247 int hSpacing;
248 int vSpacing;
249 };
250
QFormLayoutPrivate()251 QFormLayoutPrivate::QFormLayoutPrivate()
252 : fieldGrowthPolicy(DefaultFieldGrowthPolicy),
253 rowWrapPolicy(DefaultRowWrapPolicy), has_hfw(false), dirty(true), sizesDirty(true),
254 expandVertical(0), expandHorizontal(0), labelAlignment(0), formAlignment(0),
255 layoutWidth(-1), hfw_width(-1), hfw_sh_height(-1), min_width(-1),
256 sh_width(-1), thresh_width(QLAYOUTSIZE_MAX), hSpacing(-1), vSpacing(-1)
257 {
258 }
259
fixedAlignment(Qt::Alignment alignment,Qt::LayoutDirection layoutDirection)260 static Qt::Alignment fixedAlignment(Qt::Alignment alignment, Qt::LayoutDirection layoutDirection)
261 {
262 if (layoutDirection == Qt::RightToLeft && alignment & Qt::AlignAbsolute) {
263 // swap left and right, and eliminate absolute flag
264 return Qt::Alignment((alignment & ~(Qt::AlignLeft | Qt::AlignRight | Qt::AlignAbsolute))
265 | ((alignment & Qt::AlignRight) ? Qt::AlignLeft : 0)
266 | ((alignment & Qt::AlignLeft) ? Qt::AlignRight : 0));
267 } else {
268 return alignment & ~Qt::AlignAbsolute;
269 }
270 }
271
storageIndexFromLayoutItem(const QFormLayoutPrivate::ItemMatrix & m,QFormLayoutItem * item)272 static int storageIndexFromLayoutItem(const QFormLayoutPrivate::ItemMatrix &m,
273 QFormLayoutItem *item)
274 {
275 if (item) {
276 return m.storage().indexOf(item);
277 } else {
278 return -1;
279 }
280 }
281
updateFormLayoutItem(QFormLayoutItem * item,int userVSpacing,QFormLayout::FieldGrowthPolicy fieldGrowthPolicy,bool fullRow)282 static void updateFormLayoutItem(QFormLayoutItem *item, int userVSpacing,
283 QFormLayout::FieldGrowthPolicy fieldGrowthPolicy,
284 bool fullRow)
285 {
286 item->minSize = item->item->minimumSize();
287 item->sizeHint = item->item->sizeHint();
288 item->maxSize = item->item->maximumSize();
289
290 if (!fullRow && (fieldGrowthPolicy == QFormLayout::FieldsStayAtSizeHint
291 || (fieldGrowthPolicy == QFormLayout::ExpandingFieldsGrow
292 && !(item->item->expandingDirections() & Qt::Horizontal))))
293 item->maxSize.setWidth(item->sizeHint.width());
294
295 item->isHfw = item->item->hasHeightForWidth();
296 item->vSpace = userVSpacing;
297 }
298
299 /*
300 Iterate over all the controls and gather their size information
301 (min, sizeHint and max). Also work out what the spacing between
302 pairs of controls should be, and figure out the min and sizeHint
303 widths.
304 */
updateSizes()305 void QFormLayoutPrivate::updateSizes()
306 {
307 Q_Q(QFormLayout);
308
309 if (sizesDirty) {
310 QFormLayout::RowWrapPolicy wrapPolicy = q->rowWrapPolicy();
311 bool wrapAllRows = (wrapPolicy == QFormLayout::WrapAllRows);
312 bool dontWrapRows = (wrapPolicy == QFormLayout::DontWrapRows);
313 int rr = m_matrix.rowCount();
314
315 has_hfw = false;
316
317 // If any control can expand, so can this layout
318 // Wrapping doesn't affect expansion, though, just the minsize
319 bool expandH = false;
320 bool expandV = false;
321
322 QFormLayoutItem *prevLbl = 0;
323 QFormLayoutItem *prevFld = 0;
324
325 QWidget *parent = q->parentWidget();
326 QStyle *style = parent ? parent->style() : 0;
327
328 int userVSpacing = q->verticalSpacing();
329 int userHSpacing = wrapAllRows ? 0 : q->horizontalSpacing();
330
331 int maxMinLblWidth = 0;
332 int maxMinFldWidth = 0; // field with label
333 int maxMinIfldWidth = 0; // independent field
334
335 int maxShLblWidth = 0;
336 int maxShFldWidth = 0;
337 int maxShIfldWidth = 0;
338
339 for (int i = 0; i < rr; ++i) {
340 QFormLayoutItem *label = m_matrix(i, 0);
341 QFormLayoutItem *field = m_matrix(i, 1);
342
343 // Skip empty rows
344 if (!label && !field)
345 continue;
346
347 if (label) {
348 updateFormLayoutItem(label, userVSpacing, q->fieldGrowthPolicy(), false);
349 if (label->isHfw)
350 has_hfw = true;
351 Qt::Orientations o = label->expandingDirections();
352
353 if (o & Qt::Vertical)
354 expandV = true;
355 if (o & Qt::Horizontal)
356 expandH = true;
357 }
358 if (field) {
359 updateFormLayoutItem(field, userVSpacing, q->fieldGrowthPolicy(), !label && field->fullRow);
360 field->sbsHSpace = (!label && field->fullRow) ? 0 : userHSpacing;
361 if (field->isHfw)
362 has_hfw = true;
363
364 Qt::Orientations o = field->expandingDirections();
365
366 if (o & Qt::Vertical)
367 expandV = true;
368 if (o & Qt::Horizontal)
369 expandH = true;
370 }
371
372 // See if we need to calculate default spacings
373 if ((userHSpacing < 0 || userVSpacing < 0) && style) {
374 QSizePolicy::ControlTypes lbltypes =
375 QSizePolicy::ControlTypes(label ? label->controlTypes() : QSizePolicy::DefaultType);
376 QSizePolicy::ControlTypes fldtypes =
377 QSizePolicy::ControlTypes(field ? field->controlTypes() : QSizePolicy::DefaultType);
378
379 // VSpacing
380 if (userVSpacing < 0) {
381 if (wrapAllRows) {
382 // label spacing is to a previous item
383 QFormLayoutItem *lbltop = prevFld ? prevFld : prevLbl;
384 // field spacing is to the label (or a previous item)
385 QFormLayoutItem *fldtop = label ? label : lbltop;
386 QSizePolicy::ControlTypes lbltoptypes =
387 QSizePolicy::ControlTypes(lbltop ? lbltop->controlTypes() : QSizePolicy::DefaultType);
388 QSizePolicy::ControlTypes fldtoptypes =
389 QSizePolicy::ControlTypes(fldtop ? fldtop->controlTypes() : QSizePolicy::DefaultType);
390 if (label && lbltop)
391 label->vSpace = style->combinedLayoutSpacing(lbltoptypes, lbltypes, Qt::Vertical, 0, parent);
392 if (field && fldtop)
393 field->vSpace = style->combinedLayoutSpacing(fldtoptypes, fldtypes, Qt::Vertical, 0, parent);
394 } else {
395 // Side by side.. we have to also consider the spacings to empty cells, which can strangely be more than
396 // non empty cells..
397 QFormLayoutItem *lbltop = prevLbl ? prevLbl : prevFld;
398 QFormLayoutItem *fldtop = prevFld;
399 QSizePolicy::ControlTypes lbltoptypes =
400 QSizePolicy::ControlTypes(lbltop ? lbltop->controlTypes() : QSizePolicy::DefaultType);
401 QSizePolicy::ControlTypes fldtoptypes =
402 QSizePolicy::ControlTypes(fldtop ? fldtop->controlTypes() : QSizePolicy::DefaultType);
403
404 // To be compatible to QGridLayout, we have to compare solitary labels & fields with both predecessors
405 if (label) {
406 if (!field) {
407 int lblspacing = style->combinedLayoutSpacing(lbltoptypes, lbltypes, Qt::Vertical, 0, parent);
408 int fldspacing = style->combinedLayoutSpacing(fldtoptypes, lbltypes, Qt::Vertical, 0, parent);
409 label->vSpace = qMax(lblspacing, fldspacing);
410 } else
411 label->vSpace = style->combinedLayoutSpacing(lbltoptypes, lbltypes, Qt::Vertical, 0, parent);
412 }
413
414 if (field) {
415 // check spacing against both the previous label and field
416 if (!label) {
417 int lblspacing = style->combinedLayoutSpacing(lbltoptypes, fldtypes, Qt::Vertical, 0, parent);
418 int fldspacing = style->combinedLayoutSpacing(fldtoptypes, fldtypes, Qt::Vertical, 0, parent);
419 field->vSpace = qMax(lblspacing, fldspacing);
420 } else
421 field->vSpace = style->combinedLayoutSpacing(fldtoptypes, fldtypes, Qt::Vertical, 0, parent);
422 }
423 }
424 }
425
426 // HSpacing
427 // hard-coded the left and right control types so that all the rows have the same
428 // inter-column spacing (otherwise the right column isn't always left aligned)
429 if (userHSpacing < 0 && !wrapAllRows && (label || !field->fullRow) && field)
430 field->sbsHSpace = style->combinedLayoutSpacing(QSizePolicy::Label, QSizePolicy::LineEdit, Qt::Horizontal, 0, parent);
431 }
432
433 // Now update our min/sizehint widths
434 // We choose to put the spacing in the field side in sbs, so
435 // the right edge of the labels will align, but fields may
436 // be a little ragged.. since different controls may have
437 // different appearances, a slight raggedness in the left
438 // edges of fields can be tolerated.
439 // (Note - field->sbsHSpace is 0 for WrapAllRows mode)
440 if (label) {
441 maxMinLblWidth = qMax(maxMinLblWidth, label->minSize.width());
442 maxShLblWidth = qMax(maxShLblWidth, label->sizeHint.width());
443 if (field) {
444 maxMinFldWidth = qMax(maxMinFldWidth, field->minSize.width() + field->sbsHSpace);
445 maxShFldWidth = qMax(maxShFldWidth, field->sizeHint.width() + field->sbsHSpace);
446 }
447 } else if (field) {
448 maxMinIfldWidth = qMax(maxMinIfldWidth, field->minSize.width());
449 maxShIfldWidth = qMax(maxShIfldWidth, field->sizeHint.width());
450 }
451
452 prevLbl = label;
453 prevFld = field;
454 }
455
456 // Now, finally update the min/sizeHint widths
457 if (wrapAllRows) {
458 sh_width = qMax(maxShLblWidth, qMax(maxShIfldWidth, maxShFldWidth));
459 min_width = qMax(maxMinLblWidth, qMax(maxMinIfldWidth, maxMinFldWidth));
460 // in two line, we don't care as much about the threshold width
461 thresh_width = 0;
462 } else if (dontWrapRows) {
463 // This is just the max widths glommed together
464 sh_width = qMax(maxShLblWidth + maxShFldWidth, maxShIfldWidth);
465 min_width = qMax(maxMinLblWidth + maxMinFldWidth, maxMinIfldWidth);
466 thresh_width = QWIDGETSIZE_MAX;
467 } else {
468 // This is just the max widths glommed together
469 sh_width = qMax(maxShLblWidth + maxShFldWidth, maxShIfldWidth);
470 // min width needs to be the min when everything is wrapped,
471 // otherwise we'll never get set with a width that causes wrapping
472 min_width = qMax(maxMinLblWidth, qMax(maxMinIfldWidth, maxMinFldWidth));
473 // We split a pair at label sh + field min (### for now..)
474 thresh_width = maxShLblWidth + maxMinFldWidth;
475 }
476
477 // Update the expansions
478 expandVertical = expandV;
479 expandHorizontal = expandH;
480 }
481 sizesDirty = false;
482 }
483
recalcHFW(int w)484 void QFormLayoutPrivate::recalcHFW(int w)
485 {
486 setupHfwLayoutData();
487
488 int h = 0;
489 int mh = 0;
490
491 for (int r = 0; r < vLayoutCount; ++r) {
492 int spacing = hfwLayouts.at(r).spacing;
493 h += hfwLayouts.at(r).sizeHint + spacing;
494 mh += hfwLayouts.at(r).minimumSize + spacing;
495 }
496
497 if (sh_width > 0 && sh_width == w) {
498 hfw_sh_height = qMin(QLAYOUTSIZE_MAX, h);
499 hfw_sh_minheight = qMin(QLAYOUTSIZE_MAX, mh);
500 } else {
501 hfw_width = w;
502 hfw_height = qMin(QLAYOUTSIZE_MAX, h);
503 hfw_minheight = qMin(QLAYOUTSIZE_MAX, mh);
504 }
505 }
506
setupHfwLayoutData()507 void QFormLayoutPrivate::setupHfwLayoutData()
508 {
509 // setupVerticalLayoutData must be called before this
510 // setupHorizontalLayoutData must also be called before this
511 // copies non hfw data into hfw
512 // then updates size and min
513
514
515 // Note: QGridLayout doesn't call minimumHeightForWidth,
516 // but instead uses heightForWidth for both min and sizeHint.
517 // For the common case where minimumHeightForWidth just calls
518 // heightForWidth, we do the calculation twice, which can be
519 // very expensive for word wrapped QLabels/QTextEdits, for example.
520 // So we just use heightForWidth as well.
521 int i;
522 int rr = m_matrix.rowCount();
523
524 hfwLayouts.clear();
525 hfwLayouts.resize(vLayoutCount);
526 for (i = 0; i < vLayoutCount; ++i)
527 hfwLayouts[i] = vLayouts.at(i);
528
529 for (i = 0; i < rr; ++i) {
530 QFormLayoutItem *label = m_matrix(i, 0);
531 QFormLayoutItem *field = m_matrix(i, 1);
532
533 if (label) {
534 if (label->isHfw) {
535 // We don't check sideBySide here, since a label is only
536 // ever side by side with its field
537 int hfw = label->heightForWidth(label->layoutWidth);
538 hfwLayouts[label->vLayoutIndex].minimumSize = hfw;
539 hfwLayouts[label->vLayoutIndex].sizeHint = hfw;
540 } else {
541 // Reset these here, so the field can do a qMax below (the previous value may have
542 // been the fields non-hfw values, which are often larger than hfw)
543 hfwLayouts[label->vLayoutIndex].sizeHint = label->sizeHint.height();
544 hfwLayouts[label->vLayoutIndex].minimumSize = label->minSize.height();
545 }
546 }
547
548 if (field) {
549 int hfw = field->isHfw ? field->heightForWidth(field->layoutWidth) : 0;
550 int h = field->isHfw ? hfw : field->sizeHint.height();
551 int mh = field->isHfw ? hfw : field->minSize.height();
552
553 if (field->sideBySide) {
554 int oh = hfwLayouts.at(field->vLayoutIndex).sizeHint;
555 int omh = hfwLayouts.at(field->vLayoutIndex).minimumSize;
556
557 hfwLayouts[field->vLayoutIndex].sizeHint = qMax(h, oh);
558 hfwLayouts[field->vLayoutIndex].minimumSize = qMax(mh, omh);
559 } else {
560 hfwLayouts[field->vLayoutIndex].sizeHint = h;
561 hfwLayouts[field->vLayoutIndex].minimumSize = mh;
562 }
563 }
564 }
565 }
566
567 /*
568 Given up to four items involved in a vertical spacing calculation
569 (two rows * two columns), return the max vertical spacing for the
570 row containing item1 (which may also include item2)
571 We assume parent and item1 are not null.
572
573 If a particular row is split, then the spacings for that row and
574 the following row are affected, and this function should be
575 called with recalculate = true for both rows (note: only rows with both
576 a label and a field can be split).
577
578 In particular:
579
580 1) the split label's row vspace needs to be changed to qMax(label/prevLabel, label/prevField)
581 [call with item1 = label, item2 = null, prevItem1 & prevItem2 as before]
582 2) the split field's row vspace needs to be changed to the label/field spacing
583 [call with item1 = field, item2 = null, prevItem1 = label, prevItem2 = null]
584
585 [if the next row has one item, 'item']
586 3a) the following row's vspace needs to be changed to item/field spacing (would
587 previously been the qMax(item/label, item/field) spacings)
588 [call with item1 = item, item2 = null, prevItem1 = field, prevItem2 = null]
589
590 [if the next row has two items, 'label2' and 'field2']
591 3b) the following row's vspace needs to be changed to be qMax(field/label2, field/field2) spacing
592 [call with item1 = label2, item2 = field2, prevItem1 = field, prevItem2 = null]
593
594 In the (common) non split case, we can just use the precalculated vspace (possibly qMaxed between
595 label and field).
596
597 If recalculate is true, we expect:
598 - parent != null
599 - item1 != null
600 - item2 can be null
601 - prevItem1 can be null
602 - if item2 is not null, prevItem2 will be null (e.g. steps 1 or 3 above)
603 - if prevItem1 is null, prevItem2 will be null
604 */
spacingHelper(QWidget * parent,QStyle * style,int userVSpacing,bool recalculate,QFormLayoutItem * item1,QFormLayoutItem * item2,QFormLayoutItem * prevItem1,QFormLayoutItem * prevItem2)605 static inline int spacingHelper(QWidget* parent, QStyle *style, int userVSpacing, bool recalculate, QFormLayoutItem* item1, QFormLayoutItem* item2, QFormLayoutItem* prevItem1, QFormLayoutItem *prevItem2)
606 {
607 int spacing = userVSpacing;
608 if (spacing < 0) {
609 if (!recalculate) {
610 if (item1)
611 spacing = item1->vSpace;
612 if (item2)
613 spacing = qMax(spacing, item2->vSpace);
614 } else {
615 if (style && prevItem1) {
616 QSizePolicy::ControlTypes itemtypes =
617 QSizePolicy::ControlTypes(item1 ? item1->controlTypes() : QSizePolicy::DefaultType);
618 int spacing2 = 0;
619
620 spacing = style->combinedLayoutSpacing(itemtypes, prevItem1->controlTypes(), Qt::Vertical, 0, parent);
621
622 // At most of one of item2 and prevItem2 will be nonnull
623 if (item2)
624 spacing2 = style->combinedLayoutSpacing(item2->controlTypes(), prevItem1->controlTypes(), Qt::Vertical, 0, parent);
625 else if (prevItem2)
626 spacing2 = style->combinedLayoutSpacing(itemtypes, prevItem2->controlTypes(), Qt::Vertical, 0, parent);
627
628 spacing = qMax(spacing, spacing2);
629 }
630 }
631 } else {
632 if (prevItem1) {
633 QWidget *wid = prevItem1->item->widget();
634 if (wid)
635 spacing = qMax(spacing, prevItem1->geometry().top() - wid->geometry().top() );
636 }
637 if (prevItem2) {
638 QWidget *wid = prevItem2->item->widget();
639 if (wid)
640 spacing = qMax(spacing, prevItem2->geometry().top() - wid->geometry().top() );
641 }
642 }
643 return spacing;
644 }
645
initLayoutStruct(QLayoutStruct & sl,QFormLayoutItem * item)646 static inline void initLayoutStruct(QLayoutStruct& sl, QFormLayoutItem* item)
647 {
648 sl.init(item->vStretch(), item->minSize.height());
649 sl.sizeHint = item->sizeHint.height();
650 sl.maximumSize = item->maxSize.height();
651 sl.expansive = (item->expandingDirections() & Qt::Vertical);
652 sl.empty = false;
653 }
654
setupVerticalLayoutData(int width)655 void QFormLayoutPrivate::setupVerticalLayoutData(int width)
656 {
657 Q_Q(QFormLayout);
658
659 // Early out if we have no changes that would cause a change in vertical layout
660 if ((width == layoutWidth || (width >= thresh_width && layoutWidth >= thresh_width)) && !dirty && !sizesDirty)
661 return;
662
663 layoutWidth = width;
664
665 int rr = m_matrix.rowCount();
666 int vidx = 1;
667 QFormLayout::RowWrapPolicy rowWrapPolicy = q->rowWrapPolicy();
668 bool wrapAllRows = (rowWrapPolicy == QFormLayout::WrapAllRows);
669 bool addTopBottomStretch = true;
670
671 vLayouts.clear();
672 vLayouts.resize((2 * rr) + 2); // a max, some may be unused
673
674 QStyle *style = 0;
675
676 int userVSpacing = q->verticalSpacing();
677
678 if (userVSpacing < 0) {
679 if (QWidget *widget = q->parentWidget())
680 style = widget->style();
681 }
682
683 // make sure our sizes are up to date
684 updateSizes();
685
686 // Grab the widest label width here
687 // This might be different from the value computed during
688 // sizeHint/minSize, since we don't count label/field pairs that
689 // are split.
690 maxLabelWidth = 0;
691 if (!wrapAllRows) {
692 for (int i = 0; i < rr; ++i) {
693 const QFormLayoutItem *label = m_matrix(i, 0);
694 const QFormLayoutItem *field = m_matrix(i, 1);
695 if (label && (label->sizeHint.width() + (field ? field->minSize.width() : 0) <= width))
696 maxLabelWidth = qMax(maxLabelWidth, label->sizeHint.width());
697 }
698 } else {
699 maxLabelWidth = width;
700 }
701
702 QFormLayoutItem *prevItem1 = 0;
703 QFormLayoutItem *prevItem2 = 0;
704 bool prevRowSplit = false;
705
706 for (int i = 0; i < rr; ++i) {
707 QFormLayoutItem *label = m_matrix(i, 0);
708 QFormLayoutItem *field = m_matrix(i, 1);
709
710 // Totally ignore empty rows...
711 if (!label && !field)
712 continue;
713
714 QSize min1;
715 QSize min2;
716 QSize sh1;
717 QSize sh2;
718 if (label) {
719 min1 = label->minSize;
720 sh1 = label->sizeHint;
721 }
722 if (field) {
723 min2 = field->minSize;
724 sh2 = field->sizeHint;
725 }
726
727 // In separate lines, we make a vLayout for everything that isn't null
728 // in side by side, we only separate label/field if we're going to wrap it
729 bool splitSideBySide = (rowWrapPolicy == QFormLayout::WrapLongRows)
730 && ((maxLabelWidth < sh1.width()) || (width < (maxLabelWidth + min2.width())));
731
732 if (wrapAllRows || splitSideBySide) {
733 if (label) {
734 initLayoutStruct(vLayouts[vidx], label);
735
736 if (vidx > 1)
737 vLayouts[vidx - 1].spacing = spacingHelper(q->parentWidget(), style, userVSpacing, splitSideBySide || prevRowSplit, label, 0, prevItem1, prevItem2);
738
739 label->vLayoutIndex = vidx;
740 label->sideBySide = false;
741
742 prevItem1 = label;
743 prevItem2 = 0;
744
745 if (vLayouts[vidx].stretch > 0)
746 addTopBottomStretch = false;
747
748 ++vidx;
749 }
750
751 if (field) {
752 initLayoutStruct(vLayouts[vidx], field);
753
754 if (vidx > 1)
755 vLayouts[vidx - 1].spacing = spacingHelper(q->parentWidget(), style, userVSpacing, splitSideBySide || prevRowSplit, field, 0, prevItem1, prevItem2);
756
757 field->vLayoutIndex = vidx;
758 field->sideBySide = false;
759
760 prevItem1 = field;
761 prevItem2 = 0;
762
763 if (vLayouts[vidx].stretch > 0)
764 addTopBottomStretch = false;
765
766 ++vidx;
767 }
768
769 prevRowSplit = splitSideBySide;
770 } else {
771 // we're in side by side mode, and we have enough space to do that
772 QSize max1(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
773 QSize max2(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
774
775 int stretch1 = 0;
776 int stretch2 = 0;
777 bool expanding = false;
778
779 if (label) {
780 max1 = label->maxSize;
781 if (label->expandingDirections() & Qt::Vertical)
782 expanding = true;
783
784 label->sideBySide = (field != 0);
785 label->vLayoutIndex = vidx;
786 stretch1 = label->vStretch();
787 }
788
789 if (field) {
790 max2 = field->maxSize;
791 if (field->expandingDirections() & Qt::Vertical)
792 expanding = true;
793
794 field->sideBySide = (label || !field->fullRow);
795 field->vLayoutIndex = vidx;
796 stretch2 = field->vStretch();
797 }
798
799 vLayouts[vidx].init(qMax(stretch1, stretch2), qMax(min1.height(), min2.height()));
800 vLayouts[vidx].sizeHint = qMax(sh1.height(), sh2.height());
801 vLayouts[vidx].maximumSize = qMin(max1.height(), max2.height());
802 vLayouts[vidx].expansive = expanding || (vLayouts[vidx].stretch > 0);
803 vLayouts[vidx].empty = false;
804
805 if (vLayouts[vidx].stretch > 0)
806 addTopBottomStretch = false;
807
808 if (vidx > 1)
809 vLayouts[vidx - 1].spacing = spacingHelper(q->parentWidget(), style, userVSpacing, prevRowSplit, label, field, prevItem1, prevItem2);
810
811 if (label) {
812 prevItem1 = label;
813 prevItem2 = field;
814 } else {
815 prevItem1 = field;
816 prevItem2 = 0;
817 }
818
819 prevRowSplit = false;
820 ++vidx;
821 }
822 }
823
824 if (addTopBottomStretch) {
825 Qt::Alignment formAlignment = q->formAlignment();
826
827 if (!(formAlignment & Qt::AlignBottom)) {
828 // AlignTop (default if unspecified) or AlignVCenter: We add a stretch at the bottom
829 vLayouts[vidx].init(1, 0);
830 vLayouts[vidx].expansive = true;
831 ++vidx;
832 }
833
834 if (formAlignment & (Qt::AlignVCenter | Qt::AlignBottom)) {
835 // AlignVCenter or AlignBottom: We add a stretch at the top
836 vLayouts[0].init(1, 0);
837 vLayouts[0].expansive = true;
838 } else {
839 vLayouts[0].init(0, 0);
840 }
841 } else {
842 vLayouts[0].init(0, 0);
843 }
844
845 vLayoutCount = vidx;
846 dirty = false;
847 }
848
setupHorizontalLayoutData(int width)849 void QFormLayoutPrivate::setupHorizontalLayoutData(int width)
850 {
851 Q_Q(QFormLayout);
852
853 // requires setupVerticalLayoutData to be called first
854
855 int fieldMaxWidth = 0;
856
857 int rr = m_matrix.rowCount();
858 bool wrapAllRows = (q->rowWrapPolicy() == QFormLayout::WrapAllRows);
859
860 for (int i = 0; i < rr; ++i) {
861 QFormLayoutItem *label = m_matrix(i, 0);
862 QFormLayoutItem *field = m_matrix(i, 1);
863
864 // Totally ignore empty rows...
865 if (!label && !field)
866 continue;
867
868 if (label) {
869 // if there is a field, and we're side by side, we use maxLabelWidth
870 // otherwise we just use the sizehint
871 label->layoutWidth = (field && label->sideBySide) ? maxLabelWidth : label->sizeHint.width();
872 label->layoutPos = 0;
873 }
874
875 if (field) {
876 // This is the default amount allotted to fields in sbs
877 int fldwidth = width - maxLabelWidth - field->sbsHSpace;
878
879 // If we've split a row, we still decide to align
880 // the field with all the other field if it will fit
881 // Fields in sbs mode get the remnants of the maxLabelWidth
882 if (!field->sideBySide) {
883 if (wrapAllRows || (!label && field->fullRow) || field->sizeHint.width() > fldwidth) {
884 field->layoutWidth = width;
885 field->layoutPos = 0;
886 } else {
887 field->layoutWidth = fldwidth;
888 field->layoutPos = width - fldwidth;
889 }
890 } else {
891 // We're sbs, so we should have a label
892 field->layoutWidth = fldwidth;
893 field->layoutPos = width - fldwidth;
894 }
895
896 fieldMaxWidth = qMax(fieldMaxWidth, field->maxSize.width());
897 }
898 }
899
900 formMaxWidth = maxLabelWidth + fieldMaxWidth;
901 }
902
calcSizeHints()903 void QFormLayoutPrivate::calcSizeHints()
904 {
905 Q_Q(QFormLayout);
906
907 int leftMargin, topMargin, rightMargin, bottomMargin;
908 q->getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
909
910 updateSizes();
911 setupVerticalLayoutData(QLAYOUTSIZE_MAX);
912 // Don't need to call setupHorizontal here
913
914 int h = topMargin + bottomMargin;
915 int mh = topMargin + bottomMargin;
916
917 // The following are set in updateSizes
918 int w = sh_width + leftMargin + rightMargin;
919 int mw = min_width + leftMargin + rightMargin;
920
921 for (int i = 0; i < vLayoutCount; ++i) {
922 int spacing = vLayouts.at(i).spacing;
923 h += vLayouts.at(i).sizeHint + spacing;
924 mh += vLayouts.at(i).minimumSize + spacing;
925 }
926
927 minSize.rwidth() = qMin(mw, QLAYOUTSIZE_MAX);
928 minSize.rheight() = qMin(mh, QLAYOUTSIZE_MAX);
929 prefSize.rwidth() = qMin(w, QLAYOUTSIZE_MAX);
930 prefSize.rheight() = qMin(h, QLAYOUTSIZE_MAX);
931 }
932
insertRow(int row)933 int QFormLayoutPrivate::insertRow(int row)
934 {
935 int rowCnt = m_matrix.rowCount();
936 if (uint(row) > uint(rowCnt))
937 row = rowCnt;
938
939 insertRows(row, 1);
940 return row;
941 }
942
insertRows(int row,int count)943 void QFormLayoutPrivate::insertRows(int row, int count)
944 {
945 while (count > 0) {
946 m_matrix.insertRow(row, 0);
947 --count;
948 }
949 }
950
setItem(int row,QFormLayout::ItemRole role,QLayoutItem * item)951 void QFormLayoutPrivate::setItem(int row, QFormLayout::ItemRole role, QLayoutItem *item)
952 {
953 const bool fullRow = role == QFormLayout::SpanningRole;
954 const int column = role == QFormLayout::SpanningRole ? 1 : static_cast<int>(role);
955 if (uint(row) >= uint(m_matrix.rowCount()) || uint(column) > 1U) {
956 qWarning("QFormLayoutPrivate::setItem: Invalid cell (%d, %d)", row, column);
957 return;
958 }
959
960 if (!item)
961 return;
962
963 if (m_matrix(row, column)) {
964 qWarning("QFormLayoutPrivate::setItem: Cell (%d, %d) already occupied", row, column);
965 return;
966 }
967
968 QFormLayoutItem *i = new QFormLayoutItem(item);
969 i->fullRow = fullRow;
970 m_matrix(row, column) = i;
971
972 m_things.append(i);
973 }
974
setLayout(int row,QFormLayout::ItemRole role,QLayout * layout)975 void QFormLayoutPrivate::setLayout(int row, QFormLayout::ItemRole role, QLayout *layout)
976 {
977 if (layout) {
978 Q_Q(QFormLayout);
979 if (q->adoptLayout(layout))
980 setItem(row, role, layout);
981 }
982 }
983
setWidget(int row,QFormLayout::ItemRole role,QWidget * widget)984 void QFormLayoutPrivate::setWidget(int row, QFormLayout::ItemRole role, QWidget *widget)
985 {
986 if (widget) {
987 Q_Q(QFormLayout);
988 q->addChildWidget(widget);
989 setItem(row, role, QLayoutPrivate::createWidgetItem(q, widget));
990 }
991 }
992
getStyle() const993 QStyle* QFormLayoutPrivate::getStyle() const
994 {
995 Q_Q(const QFormLayout);
996
997 // ### cache
998 if (QWidget *parentWidget = q->parentWidget())
999 return parentWidget->style();
1000 else
1001 return QApplication::style();
1002 }
1003
1004 /*!
1005 \class QFormLayout
1006 \since 4.4
1007 \brief The QFormLayout class manages forms of input widgets and their associated labels.
1008
1009 \ingroup geomanagement
1010
1011
1012 QFormLayout is a convenience layout class that lays out its
1013 children in a two-column form. The left column consists of labels
1014 and the right column consists of "field" widgets (line editors,
1015 spin boxes, etc.).
1016
1017 Traditionally, such two-column form layouts were achieved using
1018 QGridLayout. QFormLayout is a higher-level alternative that
1019 provides the following advantages:
1020
1021 \list
1022 \o \bold{Adherence to the different platform's look and feel guidelines.}
1023
1024 For example, the
1025 \l{Mac OS X Aqua} and KDE guidelines specify that the
1026 labels should be right-aligned, whereas Windows and GNOME
1027 applications normally use left-alignment.
1028
1029 \o \bold{Support for wrapping long rows.}
1030
1031 For devices with small displays, QFormLayout can be set to
1032 \l{WrapLongRows}{wrap long rows}, or even to
1033 \l{WrapAllRows}{wrap all rows}.
1034
1035 \o \bold{Convenient API for creating label--field pairs.}
1036
1037 The addRow() overload that takes a QString and a QWidget *
1038 creates a QLabel behind the scenes and automatically set up
1039 its buddy. We can then write code like this:
1040
1041 \snippet doc/src/snippets/code/src_gui_kernel_qformlayout.cpp 0
1042
1043 Compare this with the following code, written using QGridLayout:
1044
1045 \snippet doc/src/snippets/code/src_gui_kernel_qformlayout.cpp 1
1046 \endlist
1047
1048 The table below shows the default appearance in different styles.
1049
1050 \table
1051 \header
1052 \o QCommonStyle derived styles (except QPlastiqueStyle)
1053 \o QMacStyle
1054 \o QPlastiqueStyle
1055 \o Qt Extended styles
1056 \row
1057 \o \inlineimage qformlayout-win.png
1058 \o \inlineimage qformlayout-mac.png
1059 \o \inlineimage qformlayout-kde.png
1060 \o \inlineimage qformlayout-qpe.png
1061 \row
1062 \o Traditional style used for Windows, GNOME, and earlier
1063 versions of KDE. Labels are left aligned, and expanding
1064 fields grow to fill the available space. (This normally
1065 corresponds to what we would get using a two-column
1066 QGridLayout.)
1067 \o Style based on the
1068 \l{Mac OS X Aqua} guidelines. Labels are right-aligned,
1069 the fields don't grow beyond their size hint, and the
1070 form is horizontally centered.
1071 \o Recommended style for
1072 \l{KDE applications}. Similar to MacStyle, except that the form
1073 is left-aligned and all fields grow to fill the available
1074 space.
1075 \o Default style for Qt Extended styles. Labels are right-aligned,
1076 expanding fields grow to fill the available space, and row
1077 wrapping is enabled for long lines.
1078 \endtable
1079
1080 The form styles can be also be overridden individually by calling
1081 setLabelAlignment(), setFormAlignment(), setFieldGrowthPolicy(),
1082 and setRowWrapPolicy(). For example, to simulate the form layout
1083 appearance of QMacStyle on all platforms, but with left-aligned
1084 labels, you could write:
1085
1086 \snippet doc/src/snippets/code/src_gui_kernel_qformlayout.cpp 2
1087
1088 \sa QGridLayout, QBoxLayout, QStackedLayout
1089 */
1090
1091
1092 /*!
1093 \enum QFormLayout::FieldGrowthPolicy
1094
1095 This enum specifies the different policies that can be used to
1096 control the way in which the form's fields grow.
1097
1098 \value FieldsStayAtSizeHint
1099 The fields never grow beyond their
1100 \l{QWidgetItem::sizeHint()}{effective size hint}. This is
1101 the default for QMacStyle.
1102
1103 \value ExpandingFieldsGrow
1104 Fields with an horizontal \l{QSizePolicy}{size policy} of
1105 \l{QSizePolicy::}{Expanding} or
1106 \l{QSizePolicy::}{MinimumExpanding} will grow to fill the
1107 available space. The other fields will not grow beyond
1108 their effective size hint. This is the default policy for
1109 Plastique.
1110
1111 \value AllNonFixedFieldsGrow
1112 All fields with a size policy that allows them to grow
1113 will grow to fill the available space. This is the default
1114 policy for most styles.
1115
1116 \sa fieldGrowthPolicy
1117 */
1118
1119 /*!
1120 \enum QFormLayout::RowWrapPolicy
1121
1122 This enum specifies the different policies that can be used to
1123 control the way in which the form's rows wrap.
1124
1125 \value DontWrapRows
1126 Fields are always laid out next to their label. This is
1127 the default policy for all styles except Qt Extended styles
1128 and QS60Style.
1129
1130 \value WrapLongRows
1131 Labels are given enough horizontal space to fit the widest label,
1132 and the rest of the space is given to the fields. If the minimum
1133 size of a field pair is wider than the available space, the field
1134 is wrapped to the next line. This is the default policy for
1135 Qt Extended styles and and QS60Style.
1136
1137 \value WrapAllRows
1138 Fields are always laid out below their label.
1139
1140 \sa rowWrapPolicy
1141 */
1142
1143 /*!
1144 \enum QFormLayout::ItemRole
1145
1146 This enum specifies the types of widgets (or other layout items)
1147 that may appear in a row.
1148
1149 \value LabelRole A label widget.
1150 \value FieldRole A field widget.
1151 \value SpanningRole A widget that spans label and field columns.
1152
1153 \sa itemAt(), getItemPosition()
1154 */
1155
1156 /*!
1157 Constructs a new form layout with the given \a parent widget.
1158
1159 \sa QWidget::setLayout()
1160 */
QFormLayout(QWidget * parent)1161 QFormLayout::QFormLayout(QWidget *parent)
1162 : QLayout(*new QFormLayoutPrivate, 0, parent)
1163 {
1164 }
1165
1166 /*!
1167 Destroys the form layout.
1168 */
~QFormLayout()1169 QFormLayout::~QFormLayout()
1170 {
1171 Q_D(QFormLayout);
1172
1173 /*
1174 The clearing and destruction order here is important. We start by clearing
1175 m_things so that QLayout and the rest of the world know that we don't babysit
1176 the layout items anymore and don't care if they are destroyed.
1177 */
1178 d->m_things.clear();
1179 qDeleteAll(d->m_matrix.storage());
1180 d->m_matrix.clear();
1181 }
1182
1183 /*!
1184 Adds a new row to the bottom of this form layout, with the given
1185 \a label and \a field.
1186
1187 \sa insertRow()
1188 */
addRow(QWidget * label,QWidget * field)1189 void QFormLayout::addRow(QWidget *label, QWidget *field)
1190 {
1191 insertRow(-1, label, field);
1192 }
1193
1194 /*!
1195 \overload
1196 */
addRow(QWidget * label,QLayout * field)1197 void QFormLayout::addRow(QWidget *label, QLayout *field)
1198 {
1199 insertRow(-1, label, field);
1200 }
1201
1202 /*!
1203 \overload
1204
1205 This overload automatically creates a QLabel behind the scenes
1206 with \a labelText as its text. The \a field is set as the new
1207 QLabel's \l{QLabel::setBuddy()}{buddy}.
1208 */
addRow(const QString & labelText,QWidget * field)1209 void QFormLayout::addRow(const QString &labelText, QWidget *field)
1210 {
1211 insertRow(-1, labelText, field);
1212 }
1213
1214 /*!
1215 \overload
1216
1217 This overload automatically creates a QLabel behind the scenes
1218 with \a labelText as its text.
1219 */
addRow(const QString & labelText,QLayout * field)1220 void QFormLayout::addRow(const QString &labelText, QLayout *field)
1221 {
1222 insertRow(-1, labelText, field);
1223 }
1224
1225 /*!
1226 \overload
1227
1228 Adds the specified \a widget at the end of this form layout. The
1229 \a widget spans both columns.
1230 */
addRow(QWidget * widget)1231 void QFormLayout::addRow(QWidget *widget)
1232 {
1233 insertRow(-1, widget);
1234 }
1235
1236 /*!
1237 \overload
1238
1239 Adds the specified \a layout at the end of this form layout. The
1240 \a layout spans both columns.
1241 */
addRow(QLayout * layout)1242 void QFormLayout::addRow(QLayout *layout)
1243 {
1244 insertRow(-1, layout);
1245 }
1246
1247 /*!
1248 Inserts a new row at position \a row in this form layout, with
1249 the given \a label and \a field. If \a row is out of bounds, the
1250 new row is added at the end.
1251
1252 \sa addRow()
1253 */
insertRow(int row,QWidget * label,QWidget * field)1254 void QFormLayout::insertRow(int row, QWidget *label, QWidget *field)
1255 {
1256 Q_D(QFormLayout);
1257
1258 row = d->insertRow(row);
1259 if (label)
1260 d->setWidget(row, LabelRole, label);
1261 if (field)
1262 d->setWidget(row, FieldRole, field);
1263 invalidate();
1264 }
1265
1266 /*!
1267 \overload
1268 */
insertRow(int row,QWidget * label,QLayout * field)1269 void QFormLayout::insertRow(int row, QWidget *label, QLayout *field)
1270 {
1271 Q_D(QFormLayout);
1272
1273 row = d->insertRow(row);
1274 if (label)
1275 d->setWidget(row, LabelRole, label);
1276 if (field)
1277 d->setLayout(row, FieldRole, field);
1278 invalidate();
1279 }
1280
1281 /*!
1282 \overload
1283
1284 This overload automatically creates a QLabel behind the scenes
1285 with \a labelText as its text. The \a field is set as the new
1286 QLabel's \l{QLabel::setBuddy()}{buddy}.
1287 */
insertRow(int row,const QString & labelText,QWidget * field)1288 void QFormLayout::insertRow(int row, const QString &labelText, QWidget *field)
1289 {
1290 QLabel *label = 0;
1291 if (!labelText.isEmpty()) {
1292 label = new QLabel(labelText);
1293 #ifndef QT_NO_SHORTCUT
1294 label->setBuddy(field);
1295 #endif
1296 }
1297 insertRow(row, label, field);
1298 }
1299
1300 /*!
1301 \overload
1302
1303 This overload automatically creates a QLabel behind the scenes
1304 with \a labelText as its text.
1305 */
insertRow(int row,const QString & labelText,QLayout * field)1306 void QFormLayout::insertRow(int row, const QString &labelText, QLayout *field)
1307 {
1308 insertRow(row, labelText.isEmpty() ? 0 : new QLabel(labelText), field);
1309 }
1310
1311 /*!
1312 \overload
1313
1314 Inserts the specified \a widget at position \a row in this form
1315 layout. The \a widget spans both columns. If \a row is out of
1316 bounds, the widget is added at the end.
1317 */
insertRow(int row,QWidget * widget)1318 void QFormLayout::insertRow(int row, QWidget *widget)
1319 {
1320 Q_D(QFormLayout);
1321
1322 if (!widget) {
1323 qWarning("QFormLayout: Cannot add null field to %s", qPrintable(objectName()));
1324 return;
1325 }
1326
1327 row = d->insertRow(row);
1328 d->setWidget(row, SpanningRole, widget);
1329 invalidate();
1330 }
1331
1332 /*!
1333 \overload
1334
1335 Inserts the specified \a layout at position \a row in this form
1336 layout. The \a layout spans both columns. If \a row is out of
1337 bounds, the widget is added at the end.
1338 */
insertRow(int row,QLayout * layout)1339 void QFormLayout::insertRow(int row, QLayout *layout)
1340 {
1341 Q_D(QFormLayout);
1342
1343 if (!layout) {
1344 qWarning("QFormLayout: Cannot add null field to %s", qPrintable(objectName()));
1345 return;
1346 }
1347
1348 row = d->insertRow(row);
1349 d->setLayout(row, SpanningRole, layout);
1350 invalidate();
1351 }
1352
1353 /*!
1354 \reimp
1355 */
addItem(QLayoutItem * item)1356 void QFormLayout::addItem(QLayoutItem *item)
1357 {
1358 Q_D(QFormLayout);
1359
1360 int row = d->insertRow(d->m_matrix.rowCount());
1361 d->setItem(row, FieldRole, item);
1362 invalidate();
1363 }
1364
1365 /*!
1366 \reimp
1367 */
count() const1368 int QFormLayout::count() const
1369 {
1370 Q_D(const QFormLayout);
1371 return d->m_things.count();
1372 }
1373
1374 /*!
1375 \reimp
1376 */
itemAt(int index) const1377 QLayoutItem *QFormLayout::itemAt(int index) const
1378 {
1379 Q_D(const QFormLayout);
1380 if (QFormLayoutItem *formItem = d->m_things.value(index))
1381 return formItem->item;
1382 return 0;
1383 }
1384
1385 /*!
1386 \reimp
1387 */
takeAt(int index)1388 QLayoutItem *QFormLayout::takeAt(int index)
1389 {
1390 Q_D(QFormLayout);
1391
1392 const int storageIndex = storageIndexFromLayoutItem(d->m_matrix, d->m_things.value(index));
1393 if (storageIndex == -1) {
1394 qWarning("QFormLayout::takeAt: Invalid index %d", index);
1395 return 0;
1396 }
1397
1398 int row, col;
1399 QFormLayoutPrivate::ItemMatrix::storageIndexToPosition(storageIndex, &row, &col);
1400 Q_ASSERT(d->m_matrix(row, col));
1401
1402 QFormLayoutItem *item = d->m_matrix(row, col);
1403 Q_ASSERT(item);
1404 d->m_things.removeAt(index);
1405 d->m_matrix(row, col) = 0;
1406
1407 invalidate();
1408
1409 // grab ownership back from the QFormLayoutItem
1410 QLayoutItem *i = item->item;
1411 item->item = 0;
1412 delete item;
1413
1414 if (QLayout *l = i->layout()) {
1415 // sanity check in case the user passed something weird to QObject::setParent()
1416 if (l->parent() == this)
1417 l->setParent(0);
1418 }
1419
1420 return i;
1421 }
1422
1423 /*!
1424 \reimp
1425 */
expandingDirections() const1426 Qt::Orientations QFormLayout::expandingDirections() const
1427 {
1428 Q_D(const QFormLayout);
1429 QFormLayoutPrivate *e = const_cast<QFormLayoutPrivate *>(d);
1430 e->updateSizes();
1431
1432 Qt::Orientations o = 0;
1433 if (e->expandHorizontal)
1434 o = Qt::Horizontal;
1435 if (e->expandVertical)
1436 o |= Qt::Vertical;
1437 return o;
1438 }
1439
1440 /*!
1441 \reimp
1442 */
hasHeightForWidth() const1443 bool QFormLayout::hasHeightForWidth() const
1444 {
1445 Q_D(const QFormLayout);
1446 QFormLayoutPrivate *e = const_cast<QFormLayoutPrivate *>(d);
1447 e->updateSizes();
1448 return (d->has_hfw || rowWrapPolicy() == WrapLongRows);
1449 }
1450
1451 /*!
1452 \reimp
1453 */
heightForWidth(int width) const1454 int QFormLayout::heightForWidth(int width) const
1455 {
1456 Q_D(const QFormLayout);
1457 if (!hasHeightForWidth())
1458 return -1;
1459
1460 int leftMargin, topMargin, rightMargin, bottomMargin;
1461 getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
1462
1463 int targetWidth = width - leftMargin - rightMargin;
1464
1465 if (!d->haveHfwCached(targetWidth)) {
1466 QFormLayoutPrivate *dat = const_cast<QFormLayoutPrivate *>(d);
1467 dat->setupVerticalLayoutData(targetWidth);
1468 dat->setupHorizontalLayoutData(targetWidth);
1469 dat->recalcHFW(targetWidth);
1470 }
1471 if (targetWidth == d->sh_width)
1472 return d->hfw_sh_height + topMargin + bottomMargin;
1473 else
1474 return d->hfw_height + topMargin + bottomMargin;
1475 }
1476
1477 /*!
1478 \reimp
1479 */
setGeometry(const QRect & rect)1480 void QFormLayout::setGeometry(const QRect &rect)
1481 {
1482 Q_D(QFormLayout);
1483 if (d->dirty || rect != geometry()) {
1484 QRect cr = rect;
1485 int leftMargin, topMargin, rightMargin, bottomMargin;
1486 getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
1487 cr.adjust(+leftMargin, +topMargin, -rightMargin, -bottomMargin);
1488
1489 bool hfw = hasHeightForWidth();
1490 d->setupVerticalLayoutData(cr.width());
1491 d->setupHorizontalLayoutData(cr.width());
1492 if (hfw && (!d->haveHfwCached(cr.width()) || d->hfwLayouts.size() != d->vLayoutCount))
1493 d->recalcHFW(cr.width());
1494 if (hfw) {
1495 qGeomCalc(d->hfwLayouts, 0, d->vLayoutCount, cr.y(), cr.height());
1496 d->arrangeWidgets(d->hfwLayouts, cr);
1497 } else {
1498 qGeomCalc(d->vLayouts, 0, d->vLayoutCount, cr.y(), cr.height());
1499 d->arrangeWidgets(d->vLayouts, cr);
1500 }
1501 QLayout::setGeometry(rect);
1502 }
1503 }
1504
1505 /*!
1506 \reimp
1507 */
sizeHint() const1508 QSize QFormLayout::sizeHint() const
1509 {
1510 Q_D(const QFormLayout);
1511 if (!d->prefSize.isValid()) {
1512 QFormLayoutPrivate *dat = const_cast<QFormLayoutPrivate *>(d);
1513 dat->calcSizeHints();
1514 }
1515 return d->prefSize;
1516 }
1517
1518 /*!
1519 \reimp
1520 */
minimumSize() const1521 QSize QFormLayout::minimumSize() const
1522 {
1523 // ### fix minimumSize if hfw
1524 Q_D(const QFormLayout);
1525 if (!d->minSize.isValid()) {
1526 QFormLayoutPrivate *dat = const_cast<QFormLayoutPrivate *>(d);
1527 dat->calcSizeHints();
1528 }
1529 return d->minSize;
1530 }
1531
1532 /*!
1533 \reimp
1534 */
invalidate()1535 void QFormLayout::invalidate()
1536 {
1537 Q_D(QFormLayout);
1538 d->dirty = true;
1539 d->sizesDirty = true;
1540 d->minSize = QSize();
1541 d->prefSize = QSize();
1542 d->formMaxWidth = -1;
1543 d->hfw_width = -1;
1544 d->sh_width = -1;
1545 d->layoutWidth = -1;
1546 d->hfw_sh_height = -1;
1547 QLayout::invalidate();
1548 }
1549
1550 /*!
1551 Returns the number of rows in the form.
1552
1553 \sa QLayout::count()
1554 */
rowCount() const1555 int QFormLayout::rowCount() const
1556 {
1557 Q_D(const QFormLayout);
1558 return d->m_matrix.rowCount();
1559 }
1560
1561 /*!
1562 Returns the layout item in the given \a row with the specified \a
1563 role (column). Returns 0 if there is no such item.
1564
1565 \sa QLayout::itemAt(), setItem()
1566 */
itemAt(int row,ItemRole role) const1567 QLayoutItem *QFormLayout::itemAt(int row, ItemRole role) const
1568 {
1569 Q_D(const QFormLayout);
1570 if (uint(row) >= uint(d->m_matrix.rowCount()))
1571 return 0;
1572 switch (role) {
1573 case SpanningRole:
1574 if (QFormLayoutItem *item = d->m_matrix(row, 1))
1575 if (item->fullRow)
1576 return item->item;
1577 break;
1578 case LabelRole:
1579 case FieldRole:
1580 if (QFormLayoutItem *item = d->m_matrix(row, (role == LabelRole) ? 0 : 1))
1581 return item->item;
1582 break;
1583 }
1584 return 0;
1585 }
1586
1587 /*!
1588 Retrieves the row and role (column) of the item at the specified
1589 \a index. If \a index is out of bounds, *\a rowPtr is set to -1;
1590 otherwise the row is stored in *\a rowPtr and the role is stored
1591 in *\a rolePtr.
1592
1593 \sa itemAt(), count(), getLayoutPosition(), getWidgetPosition()
1594 */
getItemPosition(int index,int * rowPtr,ItemRole * rolePtr) const1595 void QFormLayout::getItemPosition(int index, int *rowPtr, ItemRole *rolePtr) const
1596 {
1597 Q_D(const QFormLayout);
1598 int col = -1;
1599 int row = -1;
1600
1601 const int storageIndex = storageIndexFromLayoutItem(d->m_matrix, d->m_things.value(index));
1602 if (storageIndex != -1)
1603 QFormLayoutPrivate::ItemMatrix::storageIndexToPosition(storageIndex, &row, &col);
1604
1605 if (rowPtr)
1606 *rowPtr = row;
1607 if (rolePtr && col != -1) {
1608 const bool spanning = col == 1 && d->m_matrix(row, col)->fullRow;
1609 if (spanning) {
1610 *rolePtr = SpanningRole;
1611 } else {
1612 *rolePtr = ItemRole(col);
1613 }
1614 }
1615 }
1616
1617 /*!
1618 Retrieves the row and role (column) of the specified child \a
1619 layout. If \a layout is not in the form layout, *\a rowPtr is set
1620 to -1; otherwise the row is stored in *\a rowPtr and the role is stored
1621 in *\a rolePtr.
1622 */
getLayoutPosition(QLayout * layout,int * rowPtr,ItemRole * rolePtr) const1623 void QFormLayout::getLayoutPosition(QLayout *layout, int *rowPtr, ItemRole *rolePtr) const
1624 {
1625 int n = count();
1626 int index = 0;
1627 while (index < n) {
1628 if (itemAt(index) == layout)
1629 break;
1630 ++index;
1631 }
1632 getItemPosition(index, rowPtr, rolePtr);
1633 }
1634
1635 /*!
1636 Retrieves the row and role (column) of the specified \a widget in
1637 the layout. If \a widget is not in the layout, *\a rowPtr is set
1638 to -1; otherwise the row is stored in *\a rowPtr and the role is stored
1639 in *\a rolePtr.
1640
1641 \sa getItemPosition(), itemAt()
1642 */
getWidgetPosition(QWidget * widget,int * rowPtr,ItemRole * rolePtr) const1643 void QFormLayout::getWidgetPosition(QWidget *widget, int *rowPtr, ItemRole *rolePtr) const
1644 {
1645 getItemPosition(indexOf(widget), rowPtr, rolePtr);
1646 }
1647
1648 // ### eliminate labelForField()
1649
1650 /*!
1651 Returns the label associated with the given \a field.
1652
1653 \sa itemAt()
1654 */
labelForField(QWidget * field) const1655 QWidget *QFormLayout::labelForField(QWidget *field) const
1656 {
1657 Q_D(const QFormLayout);
1658
1659 int row;
1660 ItemRole role;
1661
1662 getWidgetPosition(field, &row, &role);
1663
1664 if (row != -1 && role == FieldRole) {
1665 if (QFormLayoutItem *label = d->m_matrix(row, LabelRole))
1666 return label->widget();
1667 }
1668 return 0;
1669 }
1670
1671 /*!
1672 \overload
1673 */
labelForField(QLayout * field) const1674 QWidget *QFormLayout::labelForField(QLayout *field) const
1675 {
1676 Q_D(const QFormLayout);
1677
1678 int row;
1679 ItemRole role;
1680
1681 getLayoutPosition(field, &row, &role);
1682
1683 if (row != -1 && role == FieldRole) {
1684 if (QFormLayoutItem *label = d->m_matrix(row, LabelRole))
1685 return label->widget();
1686 }
1687 return 0;
1688 }
1689
1690 /*!
1691 \property QFormLayout::fieldGrowthPolicy
1692 \brief the way in which the form's fields grow
1693
1694 The default value depends on the widget or application style. For
1695 QMacStyle, the default is FieldsStayAtSizeHint; for QCommonStyle
1696 derived styles (like Plastique and Windows), the default
1697 is ExpandingFieldsGrow; for Qt Extended styles, the default is
1698 AllNonFixedFieldsGrow.
1699
1700 If none of the fields can grow and the form is resized, extra
1701 space is distributed according to the current
1702 \l{formAlignment}{form alignment}.
1703
1704 \sa formAlignment, rowWrapPolicy
1705 */
1706
setFieldGrowthPolicy(FieldGrowthPolicy policy)1707 void QFormLayout::setFieldGrowthPolicy(FieldGrowthPolicy policy)
1708 {
1709 Q_D(QFormLayout);
1710 if (FieldGrowthPolicy(d->fieldGrowthPolicy) != policy) {
1711 d->fieldGrowthPolicy = policy;
1712 invalidate();
1713 }
1714 }
1715
fieldGrowthPolicy() const1716 QFormLayout::FieldGrowthPolicy QFormLayout::fieldGrowthPolicy() const
1717 {
1718 Q_D(const QFormLayout);
1719 if (d->fieldGrowthPolicy == DefaultFieldGrowthPolicy) {
1720 return QFormLayout::FieldGrowthPolicy(d->getStyle()->styleHint(QStyle::SH_FormLayoutFieldGrowthPolicy));
1721 } else {
1722 return QFormLayout::FieldGrowthPolicy(d->fieldGrowthPolicy);
1723 }
1724 }
1725
1726 /*!
1727 \property QFormLayout::rowWrapPolicy
1728 \brief the way in which the form's rows wrap
1729
1730 The default value depends on the widget or application style. For
1731 Qt Extended styles and QS60Style, the default is WrapLongRows;
1732 for the other styles, the default is DontWrapRows.
1733
1734 If you want to display each label above its associated field
1735 (instead of next to it), set this property to WrapAllRows.
1736
1737 \sa fieldGrowthPolicy
1738 */
1739
setRowWrapPolicy(RowWrapPolicy policy)1740 void QFormLayout::setRowWrapPolicy(RowWrapPolicy policy)
1741 {
1742 Q_D(QFormLayout);
1743 if (RowWrapPolicy(d->rowWrapPolicy) != policy) {
1744 d->rowWrapPolicy = policy;
1745 invalidate();
1746 }
1747 }
1748
rowWrapPolicy() const1749 QFormLayout::RowWrapPolicy QFormLayout::rowWrapPolicy() const
1750 {
1751 Q_D(const QFormLayout);
1752 if (d->rowWrapPolicy == DefaultRowWrapPolicy) {
1753 return QFormLayout::RowWrapPolicy(d->getStyle()->styleHint(QStyle::SH_FormLayoutWrapPolicy));
1754 } else {
1755 return QFormLayout::RowWrapPolicy(d->rowWrapPolicy);
1756 }
1757 }
1758
1759 /*!
1760 \property QFormLayout::labelAlignment
1761 \brief the horizontal alignment of the labels
1762
1763 The default value depends on the widget or application style. For
1764 QCommonStyle derived styles, except for QPlastiqueStyle, the
1765 default is Qt::AlignLeft; for the other styles, the default is
1766 Qt::AlignRight.
1767
1768 \sa formAlignment
1769 */
1770
setLabelAlignment(Qt::Alignment alignment)1771 void QFormLayout::setLabelAlignment(Qt::Alignment alignment)
1772 {
1773 Q_D(QFormLayout);
1774 if (d->labelAlignment != alignment) {
1775 d->labelAlignment = alignment;
1776 invalidate();
1777 }
1778 }
1779
labelAlignment() const1780 Qt::Alignment QFormLayout::labelAlignment() const
1781 {
1782 Q_D(const QFormLayout);
1783 if (!d->labelAlignment) {
1784 return Qt::Alignment(d->getStyle()->styleHint(QStyle::SH_FormLayoutLabelAlignment));
1785 } else {
1786 return d->labelAlignment;
1787 }
1788 }
1789
1790 /*!
1791 \property QFormLayout::formAlignment
1792 \brief the alignment of the form layout's contents within the layout's geometry
1793
1794 The default value depends on the widget or application style. For
1795 QMacStyle, the default is Qt::AlignHCenter | Qt::AlignTop; for the
1796 other styles, the default is Qt::AlignLeft | Qt::AlignTop.
1797
1798 \sa labelAlignment, rowWrapPolicy
1799 */
1800
setFormAlignment(Qt::Alignment alignment)1801 void QFormLayout::setFormAlignment(Qt::Alignment alignment)
1802 {
1803 Q_D(QFormLayout);
1804 if (d->formAlignment != alignment) {
1805 d->formAlignment = alignment;
1806 invalidate();
1807 }
1808 }
1809
formAlignment() const1810 Qt::Alignment QFormLayout::formAlignment() const
1811 {
1812 Q_D(const QFormLayout);
1813 if (!d->formAlignment) {
1814 return Qt::Alignment(d->getStyle()->styleHint(QStyle::SH_FormLayoutFormAlignment));
1815 } else {
1816 return d->formAlignment;
1817 }
1818 }
1819
1820 /*!
1821 \property QFormLayout::horizontalSpacing
1822 \brief the spacing between widgets that are laid out side by side
1823
1824 By default, if no value is explicitly set, the layout's horizontal
1825 spacing is inherited from the parent layout, or from the style settings
1826 for the parent widget.
1827
1828 \sa verticalSpacing, QStyle::pixelMetric(), {QStyle::}{PM_LayoutHorizontalSpacing}
1829 */
setHorizontalSpacing(int spacing)1830 void QFormLayout::setHorizontalSpacing(int spacing)
1831 {
1832 Q_D(QFormLayout);
1833 if (spacing != d->hSpacing) {
1834 d->hSpacing = spacing;
1835 invalidate();
1836 }
1837 }
1838
horizontalSpacing() const1839 int QFormLayout::horizontalSpacing() const
1840 {
1841 Q_D(const QFormLayout);
1842 if (d->hSpacing >= 0) {
1843 return d->hSpacing;
1844 } else {
1845 return qSmartSpacing(this, QStyle::PM_LayoutHorizontalSpacing);
1846 }
1847 }
1848
1849 /*!
1850 \property QFormLayout::verticalSpacing
1851 \brief the spacing between widgets that are laid out vertically
1852
1853 By default, if no value is explicitly set, the layout's vertical spacing is
1854 inherited from the parent layout, or from the style settings for the parent
1855 widget.
1856
1857 \sa horizontalSpacing, QStyle::pixelMetric(), {QStyle::}{PM_LayoutHorizontalSpacing}
1858 */
setVerticalSpacing(int spacing)1859 void QFormLayout::setVerticalSpacing(int spacing)
1860 {
1861 Q_D(QFormLayout);
1862 if (spacing != d->vSpacing) {
1863 d->vSpacing = spacing;
1864 invalidate();
1865 }
1866 }
1867
verticalSpacing() const1868 int QFormLayout::verticalSpacing() const
1869 {
1870 Q_D(const QFormLayout);
1871 if (d->vSpacing >= 0) {
1872 return d->vSpacing;
1873 } else {
1874 return qSmartSpacing(this, QStyle::PM_LayoutVerticalSpacing);
1875 }
1876 }
1877
1878 /*!
1879 This function sets both the vertical and horizontal spacing to
1880 \a spacing.
1881
1882 \sa setVerticalSpacing(), setHorizontalSpacing()
1883 */
setSpacing(int spacing)1884 void QFormLayout::setSpacing(int spacing)
1885 {
1886 Q_D(QFormLayout);
1887 d->vSpacing = d->hSpacing = spacing;
1888 invalidate();
1889 }
1890
1891 /*!
1892 If the vertical spacing is equal to the horizontal spacing,
1893 this function returns that value; otherwise it returns -1.
1894
1895 \sa setSpacing(), verticalSpacing(), horizontalSpacing()
1896 */
spacing() const1897 int QFormLayout::spacing() const
1898 {
1899 int hSpacing = horizontalSpacing();
1900 if (hSpacing == verticalSpacing()) {
1901 return hSpacing;
1902 } else {
1903 return -1;
1904 }
1905 }
1906
arrangeWidgets(const QVector<QLayoutStruct> & layouts,QRect & rect)1907 void QFormLayoutPrivate::arrangeWidgets(const QVector<QLayoutStruct>& layouts, QRect &rect)
1908 {
1909 Q_Q(QFormLayout);
1910
1911 int i;
1912 const int rr = m_matrix.rowCount();
1913 QWidget *w = q->parentWidget();
1914 Qt::LayoutDirection layoutDirection = w ? w->layoutDirection() : QApplication::layoutDirection();
1915
1916 Qt::Alignment formAlignment = fixedAlignment(q->formAlignment(), layoutDirection);
1917 int leftOffset = 0;
1918 int delta = rect.width() - formMaxWidth;
1919 if (formAlignment & (Qt::AlignHCenter | Qt::AlignRight) && delta > 0) {
1920 leftOffset = delta;
1921 if (formAlignment & Qt::AlignHCenter)
1922 leftOffset >>= 1;
1923 }
1924
1925 for (i = 0; i < rr; ++i) {
1926 QFormLayoutItem *label = m_matrix(i, 0);
1927 QFormLayoutItem *field = m_matrix(i, 1);
1928
1929 if (label) {
1930 int height = layouts.at(label->vLayoutIndex).size;
1931 if ((label->expandingDirections() & Qt::Vertical) == 0) {
1932 /*
1933 If the field on the right-hand side is tall,
1934 we want the label to be top-aligned, but not too
1935 much. So we introduce a 7 / 4 factor so that it
1936 gets some extra pixels at the top.
1937 */
1938 height = qMin(height,
1939 qMin(label->sizeHint.height() * 7 / 4,
1940 label->maxSize.height()));
1941 }
1942
1943 QSize sz(qMin(label->layoutWidth, label->sizeHint.width()), height);
1944 int x = leftOffset + rect.x() + label->layoutPos;
1945 if (fixedAlignment(q->labelAlignment(), layoutDirection) & Qt::AlignRight)
1946 x += label->layoutWidth - sz.width();
1947 QPoint p(x, layouts.at(label->vLayoutIndex).pos);
1948 // ### expansion & sizepolicy stuff
1949
1950 label->setGeometry(QStyle::visualRect(layoutDirection, rect, QRect(p, sz)));
1951 }
1952
1953 if (field) {
1954 QSize sz(field->layoutWidth, layouts.at(field->vLayoutIndex).size);
1955 QPoint p(field->layoutPos + leftOffset + rect.x(), layouts.at(field->vLayoutIndex).pos);
1956 /*
1957 if ((field->widget() && field->widget()->sizePolicy().horizontalPolicy() & (QSizePolicy::GrowFlag | QSizePolicy::ExpandFlag | QSizePolicy::IgnoreFlag))
1958 || (field->layout() && sz.width() < field->maxSize.width())) {
1959 sz.rwidth() = field->layoutWidth;
1960 }
1961 */
1962 if (field->maxSize.isValid())
1963 sz = sz.boundedTo(field->maxSize);
1964
1965 field->setGeometry(QStyle::visualRect(layoutDirection, rect, QRect(p, sz)));
1966 }
1967 }
1968 }
1969
1970 /*!
1971 Sets the widget in the given \a row for the given \a role to \a widget, extending the
1972 layout with empty rows if necessary.
1973
1974 If the cell is already occupied, the \a widget is not inserted and an error message is
1975 sent to the console.
1976
1977 \bold{Note:} For most applications, addRow() or insertRow() should be used instead of setWidget().
1978
1979 \sa setLayout()
1980 */
setWidget(int row,ItemRole role,QWidget * widget)1981 void QFormLayout::setWidget(int row, ItemRole role, QWidget *widget)
1982 {
1983 Q_D(QFormLayout);
1984 int rowCnt = rowCount();
1985 if (row >= rowCnt)
1986 d->insertRows(rowCnt, row - rowCnt + 1);
1987 d->setWidget(row, role, widget);
1988 }
1989
1990 /*!
1991 Sets the sub-layout in the given \a row for the given \a role to \a layout, extending the
1992 form layout with empty rows if necessary.
1993
1994 If the cell is already occupied, the \a layout is not inserted and an error message is
1995 sent to the console.
1996
1997 \bold{Note:} For most applications, addRow() or insertRow() should be used instead of setLayout().
1998
1999 \sa setWidget()
2000 */
setLayout(int row,ItemRole role,QLayout * layout)2001 void QFormLayout::setLayout(int row, ItemRole role, QLayout *layout)
2002 {
2003 Q_D(QFormLayout);
2004 int rowCnt = rowCount();
2005 if (row >= rowCnt)
2006 d->insertRows(rowCnt, row - rowCnt + 1);
2007 d->setLayout(row, role, layout);
2008 }
2009
2010 /*!
2011 Sets the item in the given \a row for the given \a role to \a item, extending the
2012 layout with empty rows if necessary.
2013
2014 If the cell is already occupied, the \a item is not inserted and an error message is
2015 sent to the console.
2016 The \a item spans both columns.
2017
2018 \warning Do not use this function to add child layouts or child
2019 widget items. Use setLayout() or setWidget() instead.
2020
2021 \sa setLayout()
2022 */
setItem(int row,ItemRole role,QLayoutItem * item)2023 void QFormLayout::setItem(int row, ItemRole role, QLayoutItem *item)
2024 {
2025 Q_D(QFormLayout);
2026 int rowCnt = rowCount();
2027 if (row >= rowCnt)
2028 d->insertRows(rowCnt, row - rowCnt + 1);
2029 d->setItem(row, role, item);
2030 }
2031
2032 /*!
2033 \internal
2034 */
2035
resetFieldGrowthPolicy()2036 void QFormLayout::resetFieldGrowthPolicy()
2037 {
2038 Q_D(QFormLayout);
2039 d->fieldGrowthPolicy = DefaultFieldGrowthPolicy;
2040 }
2041
2042 /*!
2043 \internal
2044 */
2045
resetRowWrapPolicy()2046 void QFormLayout::resetRowWrapPolicy()
2047 {
2048 Q_D(QFormLayout);
2049 d->rowWrapPolicy = DefaultRowWrapPolicy;
2050 }
2051
2052 /*!
2053 \internal
2054 */
2055
resetFormAlignment()2056 void QFormLayout::resetFormAlignment()
2057 {
2058 Q_D(QFormLayout);
2059 d->formAlignment = 0;
2060 }
2061
2062 /*!
2063 \internal
2064 */
2065
resetLabelAlignment()2066 void QFormLayout::resetLabelAlignment()
2067 {
2068 Q_D(QFormLayout);
2069 d->labelAlignment = 0;
2070 }
2071
2072 #if 0
2073 void QFormLayout::dump() const
2074 {
2075 Q_D(const QFormLayout);
2076 for (int i = 0; i < rowCount(); ++i) {
2077 for (int j = 0; j < 2; ++j) {
2078 qDebug("m_matrix(%d, %d) = %p", i, j, d->m_matrix(i, j));
2079 }
2080 }
2081 for (int i = 0; i < d->m_things.count(); ++i)
2082 qDebug("m_things[%d] = %p", i, d->m_things.at(i));
2083 }
2084 #endif
2085
2086 QT_END_NAMESPACE
2087