| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtWidgets module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qapplication.h" |
| #include "qdebug.h" |
| #include "qformlayout.h" |
| #include "qlabel.h" |
| #include "qlayout_p.h" |
| #include "qlayoutengine_p.h" |
| #include "qrect.h" |
| #include "qvector.h" |
| #include "qwidget.h" |
| |
| QT_BEGIN_NAMESPACE |
| |
| namespace { |
| // Fixed column matrix, stores items as [i11, i12, i21, i22...], |
| // with FORTRAN-style index operator(r, c). |
| template <class T, int NumColumns> |
| class FixedColumnMatrix { |
| public: |
| typedef QVector<T> Storage; |
| |
| FixedColumnMatrix() { } |
| |
| void clear() { m_storage.clear(); } |
| |
| const T &operator()(int r, int c) const { return m_storage[r * NumColumns + c]; } |
| T &operator()(int r, int c) { return m_storage[r * NumColumns + c]; } |
| |
| int rowCount() const { return m_storage.size() / NumColumns; } |
| void insertRow(int r, const T &value); |
| void removeRow(int r); |
| |
| // Hmmpf.. Some things are faster that way. |
| const Storage &storage() const { return m_storage; } |
| |
| static void storageIndexToPosition(int idx, int *rowPtr, int *colPtr); |
| |
| private: |
| Storage m_storage; |
| }; |
| |
| template <class T, int NumColumns> |
| void FixedColumnMatrix<T, NumColumns>::insertRow(int r, const T &value) |
| { |
| typename Storage::iterator it = m_storage.begin(); |
| it += r * NumColumns; |
| m_storage.insert(it, NumColumns, value); |
| } |
| |
| template <class T, int NumColumns> |
| void FixedColumnMatrix<T, NumColumns>::removeRow(int r) |
| { |
| m_storage.remove(r * NumColumns, NumColumns); |
| } |
| |
| template <class T, int NumColumns> |
| void FixedColumnMatrix<T, NumColumns>::storageIndexToPosition(int idx, int *rowPtr, int *colPtr) |
| { |
| *rowPtr = idx / NumColumns; |
| *colPtr = idx % NumColumns; |
| } |
| } // namespace |
| |
| // special values for unset fields; must not clash with values of FieldGrowthPolicy or |
| // RowWrapPolicy |
| const uint DefaultFieldGrowthPolicy = 255; |
| const uint DefaultRowWrapPolicy = 255; |
| |
| enum { ColumnCount = 2 }; |
| |
| // -- our data structure for our items |
| // This owns the QLayoutItem |
| struct QFormLayoutItem |
| { |
| QFormLayoutItem(QLayoutItem* i) : item(i), fullRow(false), isHfw(false) { } |
| ~QFormLayoutItem() { delete item; } |
| |
| // Wrappers |
| QWidget *widget() const { return item->widget(); } |
| QLayout *layout() const { return item->layout(); } |
| |
| bool hasHeightForWidth() const { return item->hasHeightForWidth(); } |
| int heightForWidth(int width) const { return item->heightForWidth(width); } |
| int minimumHeightForWidth(int width) const { return item->minimumHeightForWidth(width); } |
| Qt::Orientations expandingDirections() const { return item->expandingDirections(); } |
| QSizePolicy::ControlTypes controlTypes() const { return item->controlTypes(); } |
| int vStretch() const { return widget() ? widget()->sizePolicy().verticalStretch() : 0; } |
| |
| void setGeometry(const QRect& r) { item->setGeometry(r); } |
| QRect geometry() const { return item->geometry(); } |
| |
| // For use with FixedColumnMatrix |
| bool operator==(const QFormLayoutItem& other) { return item == other.item; } |
| |
| QLayoutItem *item; |
| bool fullRow; |
| |
| // set by updateSizes |
| bool isHfw; |
| QSize minSize; |
| QSize sizeHint; |
| QSize maxSize; |
| |
| // also set by updateSizes |
| int sbsHSpace; // only used for side by side, for the field item only (not label) |
| int vSpace; // This is the spacing to the item in the row above |
| |
| // set by setupVerticalLayoutData |
| bool sideBySide; |
| int vLayoutIndex; |
| |
| // set by setupHorizontalLayoutData |
| int layoutPos; |
| int layoutWidth; |
| }; |
| |
| class QFormLayoutPrivate : public QLayoutPrivate |
| { |
| Q_DECLARE_PUBLIC(QFormLayout) |
| |
| public: |
| typedef FixedColumnMatrix<QFormLayoutItem *, ColumnCount> ItemMatrix; |
| |
| QFormLayoutPrivate(); |
| ~QFormLayoutPrivate() { } |
| |
| int insertRow(int row); |
| void insertRows(int row, int count); |
| void removeRow(int row); |
| bool setItem(int row, QFormLayout::ItemRole role, QLayoutItem *item); |
| void setLayout(int row, QFormLayout::ItemRole role, QLayout *layout); |
| void setWidget(int row, QFormLayout::ItemRole role, QWidget *widget); |
| |
| void arrangeWidgets(const QVector<QLayoutStruct>& layouts, QRect &rect); |
| |
| void updateSizes(); |
| |
| void setupVerticalLayoutData(int width); |
| void setupHorizontalLayoutData(int width); |
| |
| QStyle* getStyle() const; |
| |
| inline bool haveHfwCached(int width) const |
| { |
| return (hfw_width == width) || (width == sh_width && hfw_sh_height >= 0); |
| } |
| |
| void recalcHFW(int w); |
| void setupHfwLayoutData(); |
| |
| uint fieldGrowthPolicy : 8; |
| uint rowWrapPolicy : 8; |
| uint has_hfw : 2; |
| uint dirty : 2; // have we laid out yet? |
| uint sizesDirty : 2; // have we (not) gathered layout item sizes? |
| uint expandVertical : 1; // Do we expand vertically? |
| uint expandHorizontal : 1; // Do we expand horizonally? |
| Qt::Alignment labelAlignment; |
| Qt::Alignment formAlignment; |
| |
| ItemMatrix m_matrix; |
| QList<QFormLayoutItem *> m_things; |
| |
| int layoutWidth = -1; // the last width that we called setupVerticalLayoutData on (for vLayouts) |
| |
| int hfw_width = -1; // the last width we calculated HFW for |
| int hfw_height = -1; // what that height was |
| |
| int hfw_sh_height = -1; // the hfw for sh_width |
| int hfw_sh_minheight = -1; // the minhfw for sh_width |
| |
| int min_width = -1; // the width that gets turned into minSize (from updateSizes) |
| int sh_width = -1; // the width that gets turned into prefSize (from updateSizes) |
| int thresh_width = QLAYOUTSIZE_MAX; // the width that we start splitting label/field pairs at (from updateSizes) |
| QSize minSize; |
| QSize prefSize; |
| int formMaxWidth; |
| void calcSizeHints(); |
| |
| QVector<QLayoutStruct> vLayouts; // set by setupVerticalLayoutData; |
| int vLayoutCount; // Number of rows we calculated in setupVerticalLayoutData |
| int maxLabelWidth; // the label width we calculated in setupVerticalLayoutData |
| |
| QVector<QLayoutStruct> hfwLayouts; |
| |
| int hSpacing = -1; |
| int vSpacing = -1; |
| QLayoutItem* replaceAt(int index, QLayoutItem*) override; |
| }; |
| |
| QFormLayoutPrivate::QFormLayoutPrivate() |
| : fieldGrowthPolicy(DefaultFieldGrowthPolicy), |
| rowWrapPolicy(DefaultRowWrapPolicy), has_hfw(false), dirty(true), sizesDirty(true), |
| expandVertical(0), expandHorizontal(0) |
| { |
| } |
| |
| static Qt::Alignment fixedAlignment(Qt::Alignment alignment, Qt::LayoutDirection layoutDirection) |
| { |
| if (layoutDirection == Qt::RightToLeft && alignment & Qt::AlignAbsolute) { |
| // swap left and right, and eliminate absolute flag |
| return Qt::Alignment((alignment & ~(Qt::AlignLeft | Qt::AlignRight | Qt::AlignAbsolute)) |
| | ((alignment & Qt::AlignRight) ? Qt::AlignLeft : 0) |
| | ((alignment & Qt::AlignLeft) ? Qt::AlignRight : 0)); |
| } else { |
| return alignment & ~Qt::AlignAbsolute; |
| } |
| } |
| |
| static int storageIndexFromLayoutItem(const QFormLayoutPrivate::ItemMatrix &m, |
| QFormLayoutItem *item) |
| { |
| if (item) { |
| return m.storage().indexOf(item); |
| } else { |
| return -1; |
| } |
| } |
| |
| static void updateFormLayoutItem(QFormLayoutItem *item, int userVSpacing, |
| QFormLayout::FieldGrowthPolicy fieldGrowthPolicy, |
| bool fullRow) |
| { |
| item->minSize = item->item->minimumSize(); |
| item->sizeHint = item->item->sizeHint(); |
| item->maxSize = item->item->maximumSize(); |
| |
| if (!fullRow && (fieldGrowthPolicy == QFormLayout::FieldsStayAtSizeHint |
| || (fieldGrowthPolicy == QFormLayout::ExpandingFieldsGrow |
| && !(item->item->expandingDirections() & Qt::Horizontal)))) |
| item->maxSize.setWidth(item->sizeHint.width()); |
| |
| item->isHfw = item->item->hasHeightForWidth(); |
| item->vSpace = userVSpacing; |
| } |
| |
| /* |
| Iterate over all the controls and gather their size information |
| (min, sizeHint and max). Also work out what the spacing between |
| pairs of controls should be, and figure out the min and sizeHint |
| widths. |
| */ |
| void QFormLayoutPrivate::updateSizes() |
| { |
| Q_Q(QFormLayout); |
| |
| if (sizesDirty) { |
| QFormLayout::RowWrapPolicy wrapPolicy = q->rowWrapPolicy(); |
| bool wrapAllRows = (wrapPolicy == QFormLayout::WrapAllRows); |
| bool dontWrapRows = (wrapPolicy == QFormLayout::DontWrapRows); |
| int rr = m_matrix.rowCount(); |
| |
| has_hfw = false; |
| |
| // If any control can expand, so can this layout |
| // Wrapping doesn't affect expansion, though, just the minsize |
| bool expandH = false; |
| bool expandV = false; |
| |
| QFormLayoutItem *prevLbl = nullptr; |
| QFormLayoutItem *prevFld = nullptr; |
| |
| QWidget *parent = q->parentWidget(); |
| QStyle *style = parent ? parent->style() : nullptr; |
| |
| int userVSpacing = q->verticalSpacing(); |
| int userHSpacing = wrapAllRows ? 0 : q->horizontalSpacing(); |
| |
| int maxMinLblWidth = 0; |
| int maxMinFldWidth = 0; // field with label |
| int maxMinIfldWidth = 0; // independent field |
| |
| int maxShLblWidth = 0; |
| int maxShFldWidth = 0; |
| int maxShIfldWidth = 0; |
| |
| for (int i = 0; i < rr; ++i) { |
| QFormLayoutItem *label = m_matrix(i, 0); |
| QFormLayoutItem *field = m_matrix(i, 1); |
| |
| // Skip empty rows |
| if (!label && !field) |
| continue; |
| |
| if (label) { |
| updateFormLayoutItem(label, userVSpacing, q->fieldGrowthPolicy(), false); |
| if (label->isHfw) |
| has_hfw = true; |
| Qt::Orientations o = label->expandingDirections(); |
| |
| if (o & Qt::Vertical) |
| expandV = true; |
| if (o & Qt::Horizontal) |
| expandH = true; |
| } |
| if (field) { |
| updateFormLayoutItem(field, userVSpacing, q->fieldGrowthPolicy(), !label && field->fullRow); |
| field->sbsHSpace = (!label && field->fullRow) ? 0 : userHSpacing; |
| if (field->isHfw) |
| has_hfw = true; |
| |
| Qt::Orientations o = field->expandingDirections(); |
| |
| if (o & Qt::Vertical) |
| expandV = true; |
| if (o & Qt::Horizontal) |
| expandH = true; |
| } |
| |
| // See if we need to calculate default spacings |
| if ((userHSpacing < 0 || userVSpacing < 0) && style) { |
| QSizePolicy::ControlTypes lbltypes = |
| QSizePolicy::ControlTypes(label ? label->controlTypes() : QSizePolicy::DefaultType); |
| QSizePolicy::ControlTypes fldtypes = |
| QSizePolicy::ControlTypes(field ? field->controlTypes() : QSizePolicy::DefaultType); |
| |
| // VSpacing |
| if (userVSpacing < 0) { |
| if (wrapAllRows) { |
| // label spacing is to a previous item |
| QFormLayoutItem *lbltop = prevFld ? prevFld : prevLbl; |
| // field spacing is to the label (or a previous item) |
| QFormLayoutItem *fldtop = label ? label : lbltop; |
| QSizePolicy::ControlTypes lbltoptypes = |
| QSizePolicy::ControlTypes(lbltop ? lbltop->controlTypes() : QSizePolicy::DefaultType); |
| QSizePolicy::ControlTypes fldtoptypes = |
| QSizePolicy::ControlTypes(fldtop ? fldtop->controlTypes() : QSizePolicy::DefaultType); |
| if (label && lbltop) |
| label->vSpace = style->combinedLayoutSpacing(lbltoptypes, lbltypes, Qt::Vertical, nullptr, parent); |
| if (field && fldtop) |
| field->vSpace = style->combinedLayoutSpacing(fldtoptypes, fldtypes, Qt::Vertical, nullptr, parent); |
| } else { |
| // Side by side.. we have to also consider the spacings to empty cells, which can strangely be more than |
| // non empty cells.. |
| QFormLayoutItem *lbltop = prevLbl ? prevLbl : prevFld; |
| QFormLayoutItem *fldtop = prevFld; |
| QSizePolicy::ControlTypes lbltoptypes = |
| QSizePolicy::ControlTypes(lbltop ? lbltop->controlTypes() : QSizePolicy::DefaultType); |
| QSizePolicy::ControlTypes fldtoptypes = |
| QSizePolicy::ControlTypes(fldtop ? fldtop->controlTypes() : QSizePolicy::DefaultType); |
| |
| // To be compatible to QGridLayout, we have to compare solitary labels & fields with both predecessors |
| if (label) { |
| if (!field) { |
| int lblspacing = style->combinedLayoutSpacing(lbltoptypes, lbltypes, Qt::Vertical, nullptr, parent); |
| int fldspacing = style->combinedLayoutSpacing(fldtoptypes, lbltypes, Qt::Vertical, nullptr, parent); |
| label->vSpace = qMax(lblspacing, fldspacing); |
| } else |
| label->vSpace = style->combinedLayoutSpacing(lbltoptypes, lbltypes, Qt::Vertical, nullptr, parent); |
| } |
| |
| if (field) { |
| // check spacing against both the previous label and field |
| if (!label) { |
| int lblspacing = style->combinedLayoutSpacing(lbltoptypes, fldtypes, Qt::Vertical, nullptr, parent); |
| int fldspacing = style->combinedLayoutSpacing(fldtoptypes, fldtypes, Qt::Vertical, nullptr, parent); |
| field->vSpace = qMax(lblspacing, fldspacing); |
| } else |
| field->vSpace = style->combinedLayoutSpacing(fldtoptypes, fldtypes, Qt::Vertical, nullptr, parent); |
| } |
| } |
| } |
| |
| // HSpacing |
| // hard-coded the left and right control types so that all the rows have the same |
| // inter-column spacing (otherwise the right column isn't always left aligned) |
| if (userHSpacing < 0 && !wrapAllRows && (label || !field->fullRow) && field) |
| field->sbsHSpace = style->combinedLayoutSpacing(QSizePolicy::Label, QSizePolicy::LineEdit, Qt::Horizontal, nullptr, parent); |
| } |
| |
| // Now update our min/sizehint widths |
| // We choose to put the spacing in the field side in sbs, so |
| // the right edge of the labels will align, but fields may |
| // be a little ragged.. since different controls may have |
| // different appearances, a slight raggedness in the left |
| // edges of fields can be tolerated. |
| // (Note - field->sbsHSpace is 0 for WrapAllRows mode) |
| if (label) { |
| maxMinLblWidth = qMax(maxMinLblWidth, label->minSize.width()); |
| maxShLblWidth = qMax(maxShLblWidth, label->sizeHint.width()); |
| } |
| if (field) { |
| if (field->fullRow) { |
| maxMinIfldWidth = qMax(maxMinIfldWidth, field->minSize.width()); |
| maxShIfldWidth = qMax(maxShIfldWidth, field->sizeHint.width()); |
| } else { |
| maxMinFldWidth = qMax(maxMinFldWidth, field->minSize.width() + field->sbsHSpace); |
| maxShFldWidth = qMax(maxShFldWidth, field->sizeHint.width() + field->sbsHSpace); |
| } |
| } |
| |
| prevLbl = label; |
| prevFld = field; |
| } |
| |
| // Now, finally update the min/sizeHint widths |
| if (wrapAllRows) { |
| sh_width = qMax(maxShLblWidth, qMax(maxShIfldWidth, maxShFldWidth)); |
| min_width = qMax(maxMinLblWidth, qMax(maxMinIfldWidth, maxMinFldWidth)); |
| // in two line, we don't care as much about the threshold width |
| thresh_width = 0; |
| } else if (dontWrapRows) { |
| // This is just the max widths glommed together |
| sh_width = qMax(maxShLblWidth + maxShFldWidth, maxShIfldWidth); |
| min_width = qMax(maxMinLblWidth + maxMinFldWidth, maxMinIfldWidth); |
| thresh_width = QWIDGETSIZE_MAX; |
| } else { |
| // This is just the max widths glommed together |
| sh_width = qMax(maxShLblWidth + maxShFldWidth, maxShIfldWidth); |
| // min width needs to be the min when everything is wrapped, |
| // otherwise we'll never get set with a width that causes wrapping |
| min_width = qMax(maxMinLblWidth, qMax(maxMinIfldWidth, maxMinFldWidth)); |
| // We split a pair at label sh + field min (### for now..) |
| thresh_width = maxShLblWidth + maxMinFldWidth; |
| } |
| |
| // Update the expansions |
| expandVertical = expandV; |
| expandHorizontal = expandH; |
| } |
| sizesDirty = false; |
| } |
| |
| void QFormLayoutPrivate::recalcHFW(int w) |
| { |
| setupHfwLayoutData(); |
| |
| int h = 0; |
| int mh = 0; |
| |
| for (int r = 0; r < vLayoutCount; ++r) { |
| int spacing = hfwLayouts.at(r).spacing; |
| h += hfwLayouts.at(r).sizeHint + spacing; |
| mh += hfwLayouts.at(r).minimumSize + spacing; |
| } |
| |
| if (sh_width > 0 && sh_width == w) { |
| hfw_sh_height = qMin(QLAYOUTSIZE_MAX, h); |
| hfw_sh_minheight = qMin(QLAYOUTSIZE_MAX, mh); |
| } else { |
| hfw_width = w; |
| hfw_height = qMin(QLAYOUTSIZE_MAX, h); |
| } |
| } |
| |
| void QFormLayoutPrivate::setupHfwLayoutData() |
| { |
| // setupVerticalLayoutData must be called before this |
| // setupHorizontalLayoutData must also be called before this |
| // copies non hfw data into hfw |
| // then updates size and min |
| |
| |
| // Note: QGridLayout doesn't call minimumHeightForWidth, |
| // but instead uses heightForWidth for both min and sizeHint. |
| // For the common case where minimumHeightForWidth just calls |
| // heightForWidth, we do the calculation twice, which can be |
| // very expensive for word wrapped QLabels/QTextEdits, for example. |
| // So we just use heightForWidth as well. |
| int i; |
| int rr = m_matrix.rowCount(); |
| |
| hfwLayouts.clear(); |
| hfwLayouts.resize(vLayoutCount); |
| for (i = 0; i < vLayoutCount; ++i) |
| hfwLayouts[i] = vLayouts.at(i); |
| |
| for (i = 0; i < rr; ++i) { |
| QFormLayoutItem *label = m_matrix(i, 0); |
| QFormLayoutItem *field = m_matrix(i, 1); |
| |
| if (label) { |
| if (label->isHfw) { |
| // We don't check sideBySide here, since a label is only |
| // ever side by side with its field |
| int hfw = label->heightForWidth(label->layoutWidth); |
| hfwLayouts[label->vLayoutIndex].minimumSize = hfw; |
| hfwLayouts[label->vLayoutIndex].sizeHint = hfw; |
| } else { |
| // Reset these here, so the field can do a qMax below (the previous value may have |
| // been the fields non-hfw values, which are often larger than hfw) |
| hfwLayouts[label->vLayoutIndex].sizeHint = label->sizeHint.height(); |
| hfwLayouts[label->vLayoutIndex].minimumSize = label->minSize.height(); |
| } |
| } |
| |
| if (field) { |
| int hfw = field->isHfw ? field->heightForWidth(field->layoutWidth) : 0; |
| int h = field->isHfw ? hfw : field->sizeHint.height(); |
| int mh = field->isHfw ? hfw : field->minSize.height(); |
| |
| if (field->sideBySide) { |
| int oh = hfwLayouts.at(field->vLayoutIndex).sizeHint; |
| int omh = hfwLayouts.at(field->vLayoutIndex).minimumSize; |
| |
| hfwLayouts[field->vLayoutIndex].sizeHint = qMax(h, oh); |
| hfwLayouts[field->vLayoutIndex].minimumSize = qMax(mh, omh); |
| } else { |
| hfwLayouts[field->vLayoutIndex].sizeHint = h; |
| hfwLayouts[field->vLayoutIndex].minimumSize = mh; |
| } |
| } |
| } |
| } |
| |
| /* |
| Given up to four items involved in a vertical spacing calculation |
| (two rows * two columns), return the max vertical spacing for the |
| row containing item1 (which may also include item2) |
| We assume parent and item1 are not null. |
| |
| If a particular row is split, then the spacings for that row and |
| the following row are affected, and this function should be |
| called with recalculate = true for both rows (note: only rows with both |
| a label and a field can be split). |
| |
| In particular: |
| |
| 1) the split label's row vspace needs to be changed to qMax(label/prevLabel, label/prevField) |
| [call with item1 = label, item2 = null, prevItem1 & prevItem2 as before] |
| 2) the split field's row vspace needs to be changed to the label/field spacing |
| [call with item1 = field, item2 = null, prevItem1 = label, prevItem2 = null] |
| |
| [if the next row has one item, 'item'] |
| 3a) the following row's vspace needs to be changed to item/field spacing (would |
| previously been the qMax(item/label, item/field) spacings) |
| [call with item1 = item, item2 = null, prevItem1 = field, prevItem2 = null] |
| |
| [if the next row has two items, 'label2' and 'field2'] |
| 3b) the following row's vspace needs to be changed to be qMax(field/label2, field/field2) spacing |
| [call with item1 = label2, item2 = field2, prevItem1 = field, prevItem2 = null] |
| |
| In the (common) non split case, we can just use the precalculated vspace (possibly qMaxed between |
| label and field). |
| |
| If recalculate is true, we expect: |
| - parent != null |
| - item1 != null |
| - item2 can be null |
| - prevItem1 can be null |
| - if item2 is not null, prevItem2 will be null (e.g. steps 1 or 3 above) |
| - if prevItem1 is null, prevItem2 will be null |
| */ |
| static inline int spacingHelper(QWidget* parent, QStyle *style, int userVSpacing, bool recalculate, QFormLayoutItem* item1, QFormLayoutItem* item2, QFormLayoutItem* prevItem1, QFormLayoutItem *prevItem2) |
| { |
| int spacing = userVSpacing; |
| if (spacing < 0) { |
| if (!recalculate) { |
| if (item1) |
| spacing = item1->vSpace; |
| if (item2) |
| spacing = qMax(spacing, item2->vSpace); |
| } else { |
| if (style && prevItem1) { |
| QSizePolicy::ControlTypes itemtypes = |
| QSizePolicy::ControlTypes(item1 ? item1->controlTypes() : QSizePolicy::DefaultType); |
| int spacing2 = 0; |
| |
| spacing = style->combinedLayoutSpacing(itemtypes, prevItem1->controlTypes(), Qt::Vertical, nullptr, parent); |
| |
| // At most of one of item2 and prevItem2 will be nonnull |
| if (item2) |
| spacing2 = style->combinedLayoutSpacing(item2->controlTypes(), prevItem1->controlTypes(), Qt::Vertical, nullptr, parent); |
| else if (prevItem2) |
| spacing2 = style->combinedLayoutSpacing(itemtypes, prevItem2->controlTypes(), Qt::Vertical, nullptr, parent); |
| |
| spacing = qMax(spacing, spacing2); |
| } |
| } |
| } else { |
| if (prevItem1) { |
| QWidget *wid = prevItem1->item->widget(); |
| if (wid) |
| spacing = qMax(spacing, prevItem1->geometry().top() - wid->geometry().top() ); |
| } |
| if (prevItem2) { |
| QWidget *wid = prevItem2->item->widget(); |
| if (wid) |
| spacing = qMax(spacing, prevItem2->geometry().top() - wid->geometry().top() ); |
| } |
| } |
| return qMax(spacing, 0); |
| } |
| |
| static inline void initLayoutStruct(QLayoutStruct& sl, QFormLayoutItem* item) |
| { |
| sl.init(item->vStretch(), item->minSize.height()); |
| sl.sizeHint = item->sizeHint.height(); |
| sl.maximumSize = item->maxSize.height(); |
| sl.expansive = (item->expandingDirections() & Qt::Vertical); |
| sl.empty = false; |
| } |
| |
| void QFormLayoutPrivate::setupVerticalLayoutData(int width) |
| { |
| Q_Q(QFormLayout); |
| |
| // Early out if we have no changes that would cause a change in vertical layout |
| if ((width == layoutWidth || (width >= thresh_width && layoutWidth >= thresh_width)) && !dirty && !sizesDirty) |
| return; |
| |
| layoutWidth = width; |
| |
| int rr = m_matrix.rowCount(); |
| int vidx = 1; |
| QFormLayout::RowWrapPolicy rowWrapPolicy = q->rowWrapPolicy(); |
| bool wrapAllRows = (rowWrapPolicy == QFormLayout::WrapAllRows); |
| bool addTopBottomStretch = true; |
| |
| vLayouts.clear(); |
| vLayouts.resize((2 * rr) + 2); // a max, some may be unused |
| |
| QStyle *style = nullptr; |
| |
| int userVSpacing = q->verticalSpacing(); |
| |
| if (userVSpacing < 0) { |
| if (QWidget *widget = q->parentWidget()) |
| style = widget->style(); |
| } |
| |
| // make sure our sizes are up to date |
| updateSizes(); |
| |
| // Grab the widest label width here |
| // This might be different from the value computed during |
| // sizeHint/minSize, since we don't count label/field pairs that |
| // are split. |
| maxLabelWidth = 0; |
| if (!wrapAllRows) { |
| for (int i = 0; i < rr; ++i) { |
| const QFormLayoutItem *label = m_matrix(i, 0); |
| const QFormLayoutItem *field = m_matrix(i, 1); |
| if (label && (label->sizeHint.width() + (field ? field->minSize.width() : 0) <= width)) |
| maxLabelWidth = qMax(maxLabelWidth, label->sizeHint.width()); |
| } |
| } else { |
| maxLabelWidth = width; |
| } |
| |
| QFormLayoutItem *prevItem1 = nullptr; |
| QFormLayoutItem *prevItem2 = nullptr; |
| bool prevRowSplit = false; |
| |
| for (int i = 0; i < rr; ++i) { |
| QFormLayoutItem *label = m_matrix(i, 0); |
| QFormLayoutItem *field = m_matrix(i, 1); |
| |
| // Totally ignore empty rows... |
| if (!label && !field) |
| continue; |
| |
| QSize min1; |
| QSize min2; |
| QSize sh1; |
| QSize sh2; |
| if (label) { |
| min1 = label->minSize; |
| sh1 = label->sizeHint; |
| } |
| if (field) { |
| min2 = field->minSize; |
| sh2 = field->sizeHint; |
| } |
| |
| // In separate lines, we make a vLayout for everything that isn't null |
| // in side by side, we only separate label/field if we're going to wrap it |
| bool splitSideBySide = (rowWrapPolicy == QFormLayout::WrapLongRows) |
| && ((maxLabelWidth < sh1.width()) || (width < (maxLabelWidth + min2.width()))); |
| |
| if (wrapAllRows || splitSideBySide) { |
| if (label) { |
| initLayoutStruct(vLayouts[vidx], label); |
| |
| if (vidx > 1) |
| vLayouts[vidx - 1].spacing = spacingHelper(q->parentWidget(), style, userVSpacing, splitSideBySide || prevRowSplit, label, nullptr, prevItem1, prevItem2); |
| |
| label->vLayoutIndex = vidx; |
| label->sideBySide = false; |
| |
| prevItem1 = label; |
| prevItem2 = nullptr; |
| |
| if (vLayouts[vidx].stretch > 0) |
| addTopBottomStretch = false; |
| |
| ++vidx; |
| } |
| |
| if (field) { |
| initLayoutStruct(vLayouts[vidx], field); |
| |
| if (vidx > 1) |
| vLayouts[vidx - 1].spacing = spacingHelper(q->parentWidget(), style, userVSpacing, splitSideBySide || prevRowSplit, field, nullptr, prevItem1, prevItem2); |
| |
| field->vLayoutIndex = vidx; |
| field->sideBySide = false; |
| |
| prevItem1 = field; |
| prevItem2 = nullptr; |
| |
| if (vLayouts[vidx].stretch > 0) |
| addTopBottomStretch = false; |
| |
| ++vidx; |
| } |
| |
| prevRowSplit = splitSideBySide; |
| } else { |
| // we're in side by side mode, and we have enough space to do that |
| QSize max1(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); |
| QSize max2(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); |
| |
| int stretch1 = 0; |
| int stretch2 = 0; |
| bool expanding = false; |
| |
| if (label) { |
| max1 = label->maxSize; |
| if (label->expandingDirections() & Qt::Vertical) |
| expanding = true; |
| |
| label->sideBySide = (field != nullptr); |
| label->vLayoutIndex = vidx; |
| stretch1 = label->vStretch(); |
| } |
| |
| if (field) { |
| max2 = field->maxSize; |
| if (field->expandingDirections() & Qt::Vertical) |
| expanding = true; |
| |
| field->sideBySide = (label || !field->fullRow); |
| field->vLayoutIndex = vidx; |
| stretch2 = field->vStretch(); |
| } |
| |
| vLayouts[vidx].init(qMax(stretch1, stretch2), qMax(min1.height(), min2.height())); |
| vLayouts[vidx].sizeHint = qMax(sh1.height(), sh2.height()); |
| vLayouts[vidx].maximumSize = qMin(max1.height(), max2.height()); |
| vLayouts[vidx].expansive = expanding || (vLayouts[vidx].stretch > 0); |
| vLayouts[vidx].empty = false; |
| |
| if (vLayouts[vidx].expansive) |
| addTopBottomStretch = false; |
| |
| if (vidx > 1) |
| vLayouts[vidx - 1].spacing = spacingHelper(q->parentWidget(), style, userVSpacing, prevRowSplit, label, field, prevItem1, prevItem2); |
| |
| if (label) { |
| prevItem1 = label; |
| prevItem2 = field; |
| } else { |
| prevItem1 = field; |
| prevItem2 = nullptr; |
| } |
| |
| prevRowSplit = false; |
| ++vidx; |
| } |
| } |
| |
| if (addTopBottomStretch) { |
| Qt::Alignment formAlignment = q->formAlignment(); |
| |
| if (!(formAlignment & Qt::AlignBottom)) { |
| // AlignTop (default if unspecified) or AlignVCenter: We add a stretch at the bottom |
| vLayouts[vidx].init(1, 0); |
| vLayouts[vidx].expansive = true; |
| ++vidx; |
| } |
| |
| if (formAlignment & (Qt::AlignVCenter | Qt::AlignBottom)) { |
| // AlignVCenter or AlignBottom: We add a stretch at the top |
| vLayouts[0].init(1, 0); |
| vLayouts[0].expansive = true; |
| } else { |
| vLayouts[0].init(0, 0); |
| } |
| } else { |
| vLayouts[0].init(0, 0); |
| } |
| |
| vLayoutCount = vidx; |
| dirty = false; |
| } |
| |
| void QFormLayoutPrivate::setupHorizontalLayoutData(int width) |
| { |
| Q_Q(QFormLayout); |
| |
| // requires setupVerticalLayoutData to be called first |
| |
| int fieldMaxWidth = 0; |
| |
| int rr = m_matrix.rowCount(); |
| bool wrapAllRows = (q->rowWrapPolicy() == QFormLayout::WrapAllRows); |
| |
| for (int i = 0; i < rr; ++i) { |
| QFormLayoutItem *label = m_matrix(i, 0); |
| QFormLayoutItem *field = m_matrix(i, 1); |
| |
| // Totally ignore empty rows... |
| if (!label && !field) |
| continue; |
| |
| if (label) { |
| // if there is a field, and we're side by side, we use maxLabelWidth |
| // otherwise we just use the sizehint |
| label->layoutWidth = (field && label->sideBySide) ? maxLabelWidth : label->sizeHint.width(); |
| label->layoutPos = 0; |
| } |
| |
| if (field) { |
| // This is the default amount allotted to fields in sbs |
| int fldwidth = width - maxLabelWidth - field->sbsHSpace; |
| |
| // If we've split a row, we still decide to align |
| // the field with all the other field if it will fit |
| // Fields in sbs mode get the remnants of the maxLabelWidth |
| if (!field->sideBySide) { |
| if (wrapAllRows || (!label && field->fullRow) || field->sizeHint.width() > fldwidth) { |
| field->layoutWidth = width; |
| field->layoutPos = 0; |
| } else { |
| field->layoutWidth = fldwidth; |
| field->layoutPos = width - fldwidth; |
| } |
| } else { |
| // We're sbs, so we should have a label |
| field->layoutWidth = fldwidth; |
| field->layoutPos = width - fldwidth; |
| } |
| |
| fieldMaxWidth = qMax(fieldMaxWidth, field->maxSize.width()); |
| } |
| } |
| |
| formMaxWidth = maxLabelWidth + fieldMaxWidth; |
| } |
| |
| void QFormLayoutPrivate::calcSizeHints() |
| { |
| Q_Q(QFormLayout); |
| |
| int leftMargin, topMargin, rightMargin, bottomMargin; |
| q->getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin); |
| |
| updateSizes(); |
| setupVerticalLayoutData(QLAYOUTSIZE_MAX); |
| // Don't need to call setupHorizontal here |
| |
| int h = topMargin + bottomMargin; |
| int mh = topMargin + bottomMargin; |
| |
| // The following are set in updateSizes |
| int w = sh_width + leftMargin + rightMargin; |
| int mw = min_width + leftMargin + rightMargin; |
| |
| for (int i = 0; i < vLayoutCount; ++i) { |
| int spacing = vLayouts.at(i).spacing; |
| h += vLayouts.at(i).sizeHint + spacing; |
| mh += vLayouts.at(i).minimumSize + spacing; |
| } |
| |
| minSize.rwidth() = qMin(mw, QLAYOUTSIZE_MAX); |
| minSize.rheight() = qMin(mh, QLAYOUTSIZE_MAX); |
| prefSize.rwidth() = qMin(w, QLAYOUTSIZE_MAX); |
| prefSize.rheight() = qMin(h, QLAYOUTSIZE_MAX); |
| } |
| |
| int QFormLayoutPrivate::insertRow(int row) |
| { |
| int rowCnt = m_matrix.rowCount(); |
| if (uint(row) > uint(rowCnt)) |
| row = rowCnt; |
| |
| insertRows(row, 1); |
| return row; |
| } |
| |
| void QFormLayoutPrivate::insertRows(int row, int count) |
| { |
| while (count > 0) { |
| m_matrix.insertRow(row, 0); |
| --count; |
| } |
| } |
| |
| void QFormLayoutPrivate::removeRow(int row) |
| { |
| if (uint(row) < uint(m_matrix.rowCount())) |
| m_matrix.removeRow(row); |
| } |
| |
| bool QFormLayoutPrivate::setItem(int row, QFormLayout::ItemRole role, QLayoutItem *item) |
| { |
| const bool fullRow = role == QFormLayout::SpanningRole; |
| const int column = role == QFormLayout::SpanningRole ? 1 : static_cast<int>(role); |
| if (Q_UNLIKELY(uint(row) >= uint(m_matrix.rowCount()) || uint(column) > 1U)) { |
| qWarning("QFormLayoutPrivate::setItem: Invalid cell (%d, %d)", row, column); |
| return false; |
| } |
| |
| if (!item) |
| return false; |
| |
| if (Q_UNLIKELY(m_matrix(row, column))) { |
| qWarning("QFormLayoutPrivate::setItem: Cell (%d, %d) already occupied", row, column); |
| return false; |
| } |
| |
| QFormLayoutItem *i = new QFormLayoutItem(item); |
| i->fullRow = fullRow; |
| m_matrix(row, column) = i; |
| |
| m_things.append(i); |
| return true; |
| } |
| |
| void QFormLayoutPrivate::setLayout(int row, QFormLayout::ItemRole role, QLayout *layout) |
| { |
| if (layout) { |
| Q_Q(QFormLayout); |
| if (q->adoptLayout(layout)) |
| setItem(row, role, layout); |
| } |
| } |
| |
| void QFormLayoutPrivate::setWidget(int row, QFormLayout::ItemRole role, QWidget *widget) |
| { |
| if (widget) { |
| Q_Q(QFormLayout); |
| q->addChildWidget(widget); |
| QWidgetItem *item = QLayoutPrivate::createWidgetItem(q, widget); |
| if (!setItem(row, role, item)) |
| delete item; |
| } |
| } |
| |
| QStyle* QFormLayoutPrivate::getStyle() const |
| { |
| Q_Q(const QFormLayout); |
| |
| // ### cache |
| if (QWidget *parentWidget = q->parentWidget()) |
| return parentWidget->style(); |
| else |
| return QApplication::style(); |
| } |
| |
| QLayoutItem* QFormLayoutPrivate::replaceAt(int index, QLayoutItem *newitem) |
| { |
| Q_Q(QFormLayout); |
| if (!newitem) |
| return nullptr; |
| const int storageIndex = storageIndexFromLayoutItem(m_matrix, m_things.value(index)); |
| if (Q_UNLIKELY(storageIndex == -1)) { |
| // ### Qt6 - fix warning too when this class becomes public |
| qWarning("QFormLayoutPrivate::replaceAt: Invalid index %d", index); |
| return nullptr; |
| } |
| |
| int row, col; |
| QFormLayoutPrivate::ItemMatrix::storageIndexToPosition(storageIndex, &row, &col); |
| Q_ASSERT(m_matrix(row, col)); |
| |
| QFormLayoutItem *item = m_matrix(row, col); |
| Q_ASSERT(item); |
| |
| QLayoutItem *olditem = item->item; |
| item->item = newitem; |
| |
| q->invalidate(); |
| return olditem; |
| } |
| |
| /*! |
| \class QFormLayout |
| \since 4.4 |
| \brief The QFormLayout class manages forms of input widgets and their associated labels. |
| |
| \ingroup geomanagement |
| \inmodule QtWidgets |
| |
| QFormLayout is a convenience layout class that lays out its |
| children in a two-column form. The left column consists of labels |
| and the right column consists of "field" widgets (line editors, |
| spin boxes, etc.). |
| |
| Traditionally, such two-column form layouts were achieved using |
| QGridLayout. QFormLayout is a higher-level alternative that |
| provides the following advantages: |
| |
| \list |
| \li \b{Adherence to the different platform's look and feel guidelines.} |
| |
| For example, the |
| \l{http://developer.apple.com/library/mac/#documentation/UserExperience/Conceptual/AppleHIGuidelines/Intro/Intro.html}{\macos Aqua} and KDE guidelines specify that the |
| labels should be right-aligned, whereas Windows and GNOME |
| applications normally use left-alignment. |
| |
| \li \b{Support for wrapping long rows.} |
| |
| For devices with small displays, QFormLayout can be set to |
| \l{WrapLongRows}{wrap long rows}, or even to |
| \l{WrapAllRows}{wrap all rows}. |
| |
| \li \b{Convenient API for creating label--field pairs.} |
| |
| The addRow() overload that takes a QString and a QWidget * |
| creates a QLabel behind the scenes and automatically set up |
| its buddy. We can then write code like this: |
| |
| \snippet code/src_gui_kernel_qformlayout.cpp 0 |
| |
| Compare this with the following code, written using QGridLayout: |
| |
| \snippet code/src_gui_kernel_qformlayout.cpp 1 |
| \endlist |
| |
| The table below shows the default appearance in different styles. |
| |
| \table |
| \header |
| \li QCommonStyle derived styles (except QPlastiqueStyle) |
| \li QMacStyle |
| \li QPlastiqueStyle |
| \li Qt Extended styles |
| \row |
| \li \inlineimage qformlayout-win.png |
| \li \inlineimage qformlayout-mac.png |
| \li \inlineimage qformlayout-kde.png |
| \li \inlineimage qformlayout-qpe.png |
| \row |
| \li Traditional style used for Windows, GNOME, and earlier |
| versions of KDE. Labels are left aligned, and expanding |
| fields grow to fill the available space. (This normally |
| corresponds to what we would get using a two-column |
| QGridLayout.) |
| \li Style based on the |
| \l{http://developer.apple.com/library/mac/#documentation/UserExperience/Conceptual/AppleHIGuidelines/Intro/Intro.html}{\macos Aqua} guidelines. Labels are right-aligned, |
| the fields don't grow beyond their size hint, and the |
| form is horizontally centered. |
| \li Recommended style for |
| \l{KDE applications}. Similar to MacStyle, except that the form |
| is left-aligned and all fields grow to fill the available |
| space. |
| \li Default style for Qt Extended styles. Labels are right-aligned, |
| expanding fields grow to fill the available space, and row |
| wrapping is enabled for long lines. |
| \endtable |
| |
| The form styles can be also be overridden individually by calling |
| setLabelAlignment(), setFormAlignment(), setFieldGrowthPolicy(), |
| and setRowWrapPolicy(). For example, to simulate the form layout |
| appearance of QMacStyle on all platforms, but with left-aligned |
| labels, you could write: |
| |
| \snippet code/src_gui_kernel_qformlayout.cpp 2 |
| |
| \sa QGridLayout, QBoxLayout, QStackedLayout |
| */ |
| |
| |
| /*! |
| \enum QFormLayout::FieldGrowthPolicy |
| |
| This enum specifies the different policies that can be used to |
| control the way in which the form's fields grow. |
| |
| \value FieldsStayAtSizeHint |
| The fields never grow beyond their |
| \l{QWidgetItem::sizeHint()}{effective size hint}. This is |
| the default for QMacStyle. |
| |
| \value ExpandingFieldsGrow |
| Fields with an horizontal \l{QSizePolicy}{size policy} of |
| \l{QSizePolicy::}{Expanding} or |
| \l{QSizePolicy::}{MinimumExpanding} will grow to fill the |
| available space. The other fields will not grow beyond |
| their effective size hint. This is the default policy for |
| Plastique. |
| |
| \value AllNonFixedFieldsGrow |
| All fields with a size policy that allows them to grow |
| will grow to fill the available space. This is the default |
| policy for most styles. |
| |
| \sa fieldGrowthPolicy |
| */ |
| |
| /*! |
| \enum QFormLayout::RowWrapPolicy |
| |
| This enum specifies the different policies that can be used to |
| control the way in which the form's rows wrap. |
| |
| \value DontWrapRows |
| Fields are always laid out next to their label. This is |
| the default policy for all styles except Qt Extended styles. |
| |
| \value WrapLongRows |
| Labels are given enough horizontal space to fit the widest label, |
| and the rest of the space is given to the fields. If the minimum |
| size of a field pair is wider than the available space, the field |
| is wrapped to the next line. This is the default policy for |
| Qt Extended styles. |
| |
| \value WrapAllRows |
| Fields are always laid out below their label. |
| |
| \sa rowWrapPolicy |
| */ |
| |
| /*! |
| \enum QFormLayout::ItemRole |
| |
| This enum specifies the types of widgets (or other layout items) |
| that may appear in a row. |
| |
| \value LabelRole A label widget. |
| \value FieldRole A field widget. |
| \value SpanningRole A widget that spans label and field columns. |
| |
| \sa itemAt(), getItemPosition() |
| */ |
| |
| /*! |
| |
| \class QFormLayout::TakeRowResult |
| |
| \brief Contains the result of a QFormLayout::takeRow() call. |
| \inmodule QtWidgets |
| \since 5.8 |
| \sa QFormLayout::takeRow() |
| */ |
| |
| /*! |
| \variable QFormLayout::TakeRowResult::labelItem |
| |
| Contains the layout item corresponding to the label of the row. |
| */ |
| |
| /*! |
| \variable QFormLayout::TakeRowResult::fieldItem |
| |
| Contains the layout item corresponding to the field of the row. |
| */ |
| |
| /*! |
| Constructs a new form layout with the given \a parent widget. |
| |
| \sa QWidget::setLayout() |
| */ |
| QFormLayout::QFormLayout(QWidget *parent) |
| : QLayout(*new QFormLayoutPrivate, nullptr, parent) |
| { |
| } |
| |
| /*! |
| Destroys the form layout. |
| */ |
| QFormLayout::~QFormLayout() |
| { |
| Q_D(QFormLayout); |
| |
| /* |
| The clearing and destruction order here is important. We start by clearing |
| m_things so that QLayout and the rest of the world know that we don't babysit |
| the layout items anymore and don't care if they are destroyed. |
| */ |
| d->m_things.clear(); |
| qDeleteAll(d->m_matrix.storage()); |
| d->m_matrix.clear(); |
| } |
| |
| /*! |
| Adds a new row to the bottom of this form layout, with the given |
| \a label and \a field. |
| |
| \sa insertRow() |
| */ |
| void QFormLayout::addRow(QWidget *label, QWidget *field) |
| { |
| insertRow(-1, label, field); |
| } |
| |
| /*! |
| \overload |
| */ |
| void QFormLayout::addRow(QWidget *label, QLayout *field) |
| { |
| insertRow(-1, label, field); |
| } |
| |
| /*! |
| \overload |
| |
| This overload automatically creates a QLabel behind the scenes |
| with \a labelText as its text. The \a field is set as the new |
| QLabel's \l{QLabel::setBuddy()}{buddy}. |
| */ |
| void QFormLayout::addRow(const QString &labelText, QWidget *field) |
| { |
| insertRow(-1, labelText, field); |
| } |
| |
| /*! |
| \overload |
| |
| This overload automatically creates a QLabel behind the scenes |
| with \a labelText as its text. |
| */ |
| void QFormLayout::addRow(const QString &labelText, QLayout *field) |
| { |
| insertRow(-1, labelText, field); |
| } |
| |
| /*! |
| \overload |
| |
| Adds the specified \a widget at the end of this form layout. The |
| \a widget spans both columns. |
| */ |
| void QFormLayout::addRow(QWidget *widget) |
| { |
| insertRow(-1, widget); |
| } |
| |
| /*! |
| \overload |
| |
| Adds the specified \a layout at the end of this form layout. The |
| \a layout spans both columns. |
| */ |
| void QFormLayout::addRow(QLayout *layout) |
| { |
| insertRow(-1, layout); |
| } |
| |
| /*! |
| Inserts a new row at position \a row in this form layout, with |
| the given \a label and \a field. If \a row is out of bounds, the |
| new row is added at the end. |
| |
| \sa addRow() |
| */ |
| void QFormLayout::insertRow(int row, QWidget *label, QWidget *field) |
| { |
| Q_D(QFormLayout); |
| if ((label && !d->checkWidget(label)) || (field && !d->checkWidget(field))) |
| return; |
| |
| row = d->insertRow(row); |
| if (label) |
| d->setWidget(row, LabelRole, label); |
| if (field) |
| d->setWidget(row, FieldRole, field); |
| invalidate(); |
| } |
| |
| /*! |
| \overload |
| */ |
| void QFormLayout::insertRow(int row, QWidget *label, QLayout *field) |
| { |
| Q_D(QFormLayout); |
| if ((label && !d->checkWidget(label)) || (field && !d->checkLayout(field))) |
| return; |
| |
| row = d->insertRow(row); |
| if (label) |
| d->setWidget(row, LabelRole, label); |
| if (field) |
| d->setLayout(row, FieldRole, field); |
| invalidate(); |
| } |
| |
| /*! |
| \overload |
| |
| This overload automatically creates a QLabel behind the scenes |
| with \a labelText as its text. The \a field is set as the new |
| QLabel's \l{QLabel::setBuddy()}{buddy}. |
| */ |
| void QFormLayout::insertRow(int row, const QString &labelText, QWidget *field) |
| { |
| Q_D(QFormLayout); |
| if (field && !d->checkWidget(field)) |
| return; |
| |
| QLabel *label = nullptr; |
| if (!labelText.isEmpty()) { |
| label = new QLabel(labelText); |
| #ifndef QT_NO_SHORTCUT |
| label->setBuddy(field); |
| #endif |
| } |
| insertRow(row, label, field); |
| } |
| |
| /*! |
| \overload |
| |
| This overload automatically creates a QLabel behind the scenes |
| with \a labelText as its text. |
| */ |
| void QFormLayout::insertRow(int row, const QString &labelText, QLayout *field) |
| { |
| Q_D(QFormLayout); |
| if (field && !d->checkLayout(field)) |
| return; |
| |
| insertRow(row, labelText.isEmpty() ? nullptr : new QLabel(labelText), field); |
| } |
| |
| /*! |
| \overload |
| |
| Inserts the specified \a widget at position \a row in this form |
| layout. The \a widget spans both columns. If \a row is out of |
| bounds, the widget is added at the end. |
| */ |
| void QFormLayout::insertRow(int row, QWidget *widget) |
| { |
| Q_D(QFormLayout); |
| if (!d->checkWidget(widget)) |
| return; |
| |
| row = d->insertRow(row); |
| d->setWidget(row, SpanningRole, widget); |
| invalidate(); |
| } |
| |
| /*! |
| \overload |
| |
| Inserts the specified \a layout at position \a row in this form |
| layout. The \a layout spans both columns. If \a row is out of |
| bounds, the widget is added at the end. |
| */ |
| void QFormLayout::insertRow(int row, QLayout *layout) |
| { |
| Q_D(QFormLayout); |
| if (!d->checkLayout(layout)) |
| return; |
| |
| row = d->insertRow(row); |
| d->setLayout(row, SpanningRole, layout); |
| invalidate(); |
| } |
| |
| static QLayoutItem *ownershipCleanedItem(QFormLayoutItem *item, QFormLayout *layout) |
| { |
| if (!item) |
| return nullptr; |
| |
| // grab ownership back from the QFormLayoutItem |
| QLayoutItem *i = item->item; |
| item->item = nullptr; |
| delete item; |
| |
| if (QLayout *l = i->layout()) { |
| // sanity check in case the user passed something weird to QObject::setParent() |
| if (l->parent() == layout) |
| l->setParent(nullptr); |
| } |
| |
| return i; |
| } |
| |
| static void clearAndDestroyQLayoutItem(QLayoutItem *item) |
| { |
| if (Q_LIKELY(item)) { |
| delete item->widget(); |
| if (QLayout *layout = item->layout()) { |
| while (QLayoutItem *child = layout->takeAt(0)) |
| clearAndDestroyQLayoutItem(child); |
| } |
| delete item; |
| } |
| } |
| |
| /*! |
| \since 5.8 |
| |
| Deletes row \a row from this form layout. |
| |
| \a row must be non-negative and less than rowCount(). |
| |
| After this call, rowCount() is decremented by one. All widgets and |
| nested layouts that occupied this row are deleted. That includes both |
| the field widget(s) and the label, if any. All following rows are shifted |
| up one row and the freed vertical space is redistributed amongst the remaining rows. |
| |
| You can use this function to undo a previous addRow() or insertRow(): |
| \snippet code/src_gui_kernel_qformlayout.cpp 3 |
| |
| If you want to remove the row from the layout without deleting the widgets, use takeRow() instead. |
| |
| \sa takeRow() |
| */ |
| void QFormLayout::removeRow(int row) |
| { |
| TakeRowResult result = takeRow(row); |
| clearAndDestroyQLayoutItem(result.labelItem); |
| clearAndDestroyQLayoutItem(result.fieldItem); |
| } |
| |
| /*! |
| \since 5.8 |
| |
| \overload |
| |
| Deletes the row corresponding to \a widget from this form layout. |
| |
| After this call, rowCount() is decremented by one. All widgets and |
| nested layouts that occupied this row are deleted. That includes both |
| the field widget(s) and the label, if any. All following rows are shifted |
| up one row and the freed vertical space is redistributed amongst the remaining rows. |
| |
| You can use this function to undo a previous addRow() or insertRow(): |
| \snippet code/src_gui_kernel_qformlayout.cpp 4 |
| |
| If you want to remove the row from the layout without deleting the widgets, use takeRow() instead. |
| |
| \sa takeRow() |
| */ |
| void QFormLayout::removeRow(QWidget *widget) |
| { |
| TakeRowResult result = takeRow(widget); |
| clearAndDestroyQLayoutItem(result.labelItem); |
| clearAndDestroyQLayoutItem(result.fieldItem); |
| } |
| |
| /*! |
| \since 5.8 |
| |
| \overload |
| |
| Deletes the row corresponding to \a layout from this form layout. |
| |
| After this call, rowCount() is decremented by one. All widgets and |
| nested layouts that occupied this row are deleted. That includes both |
| the field widget(s) and the label, if any. All following rows are shifted |
| up one row and the freed vertical space is redistributed amongst the remaining rows. |
| |
| You can use this function to undo a previous addRow() or insertRow(): |
| \snippet code/src_gui_kernel_qformlayout.cpp 5 |
| |
| If you want to remove the row from the form layout without deleting the inserted layout, |
| use takeRow() instead. |
| |
| \sa takeRow() |
| */ |
| void QFormLayout::removeRow(QLayout *layout) |
| { |
| TakeRowResult result = takeRow(layout); |
| clearAndDestroyQLayoutItem(result.labelItem); |
| clearAndDestroyQLayoutItem(result.fieldItem); |
| } |
| |
| /*! |
| \since 5.8 |
| |
| Removes the specified \a row from this form layout. |
| |
| \a row must be non-negative and less than rowCount(). |
| |
| \note This function doesn't delete anything. |
| |
| After this call, rowCount() is decremented by one. All following rows are shifted |
| up one row and the freed vertical space is redistributed amongst the remaining rows. |
| |
| You can use this function to undo a previous addRow() or insertRow(): |
| \snippet code/src_gui_kernel_qformlayout.cpp 6 |
| |
| If you want to remove the row from the layout and delete the widgets, use removeRow() instead. |
| |
| \return A structure containing both the widget and |
| corresponding label layout items |
| |
| \sa removeRow() |
| */ |
| QFormLayout::TakeRowResult QFormLayout::takeRow(int row) |
| { |
| Q_D(QFormLayout); |
| |
| if (Q_UNLIKELY(!(uint(row) < uint(d->m_matrix.rowCount())))) { |
| qWarning("QFormLayout::takeRow: Invalid row %d", row); |
| return TakeRowResult(); |
| } |
| |
| QFormLayoutItem *label = d->m_matrix(row, 0); |
| QFormLayoutItem *field = d->m_matrix(row, 1); |
| |
| d->m_things.removeOne(label); |
| d->m_things.removeOne(field); |
| d->m_matrix.removeRow(row); |
| |
| invalidate(); |
| |
| TakeRowResult result; |
| result.labelItem = ownershipCleanedItem(label, this); |
| result.fieldItem = ownershipCleanedItem(field, this); |
| return result; |
| } |
| |
| /*! |
| \since 5.8 |
| |
| \overload |
| |
| Removes the specified \a widget from this form layout. |
| |
| \note This function doesn't delete anything. |
| |
| After this call, rowCount() is decremented by one. All following rows are shifted |
| up one row and the freed vertical space is redistributed amongst the remaining rows. |
| |
| \snippet code/src_gui_kernel_qformlayout.cpp 7 |
| |
| If you want to remove the row from the layout and delete the widgets, use removeRow() instead. |
| |
| \return A structure containing both the widget and |
| corresponding label layout items |
| |
| \sa removeRow() |
| */ |
| QFormLayout::TakeRowResult QFormLayout::takeRow(QWidget *widget) |
| { |
| Q_D(QFormLayout); |
| if (Q_UNLIKELY(!d->checkWidget(widget))) |
| return TakeRowResult(); |
| |
| int row; |
| ItemRole role; |
| getWidgetPosition(widget, &row, &role); |
| |
| if (Q_UNLIKELY(row < 0)) { |
| qWarning("QFormLayout::takeRow: Invalid widget"); |
| return TakeRowResult(); |
| } |
| |
| return takeRow(row); |
| } |
| |
| /*! |
| \since 5.8 |
| |
| \overload |
| |
| Removes the specified \a layout from this form layout. |
| |
| \note This function doesn't delete anything. |
| |
| After this call, rowCount() is decremented by one. All following rows are shifted |
| up one row and the freed vertical space is redistributed amongst the remaining rows. |
| |
| \snippet code/src_gui_kernel_qformlayout.cpp 8 |
| |
| If you want to remove the row from the form layout and delete the inserted layout, |
| use removeRow() instead. |
| |
| \return A structure containing both the widget and |
| corresponding label layout items |
| |
| \sa removeRow() |
| */ |
| QFormLayout::TakeRowResult QFormLayout::takeRow(QLayout *layout) |
| { |
| Q_D(QFormLayout); |
| if (Q_UNLIKELY(!d->checkLayout(layout))) |
| return TakeRowResult(); |
| |
| int row; |
| ItemRole role; |
| getLayoutPosition(layout, &row, &role); |
| |
| if (Q_UNLIKELY(row < 0)) { |
| qWarning("QFormLayout::takeRow: Invalid layout"); |
| return TakeRowResult(); |
| } |
| |
| return takeRow(row); |
| } |
| |
| /*! |
| \reimp |
| */ |
| void QFormLayout::addItem(QLayoutItem *item) |
| { |
| Q_D(QFormLayout); |
| |
| int row = d->insertRow(d->m_matrix.rowCount()); |
| d->setItem(row, FieldRole, item); |
| invalidate(); |
| } |
| |
| /*! |
| \reimp |
| */ |
| int QFormLayout::count() const |
| { |
| Q_D(const QFormLayout); |
| return d->m_things.count(); |
| } |
| |
| /*! |
| \reimp |
| */ |
| QLayoutItem *QFormLayout::itemAt(int index) const |
| { |
| Q_D(const QFormLayout); |
| if (QFormLayoutItem *formItem = d->m_things.value(index)) |
| return formItem->item; |
| return nullptr; |
| } |
| |
| /*! |
| \reimp |
| */ |
| QLayoutItem *QFormLayout::takeAt(int index) |
| { |
| Q_D(QFormLayout); |
| |
| const int storageIndex = storageIndexFromLayoutItem(d->m_matrix, d->m_things.value(index)); |
| if (Q_UNLIKELY(storageIndex == -1)) { |
| qWarning("QFormLayout::takeAt: Invalid index %d", index); |
| return nullptr; |
| } |
| |
| int row, col; |
| QFormLayoutPrivate::ItemMatrix::storageIndexToPosition(storageIndex, &row, &col); |
| Q_ASSERT(d->m_matrix(row, col)); |
| |
| QFormLayoutItem *item = d->m_matrix(row, col); |
| Q_ASSERT(item); |
| d->m_things.removeAt(index); |
| d->m_matrix(row, col) = 0; |
| |
| invalidate(); |
| |
| return ownershipCleanedItem(item, this); |
| } |
| |
| /*! |
| \reimp |
| */ |
| Qt::Orientations QFormLayout::expandingDirections() const |
| { |
| Q_D(const QFormLayout); |
| QFormLayoutPrivate *e = const_cast<QFormLayoutPrivate *>(d); |
| e->updateSizes(); |
| |
| Qt::Orientations o; |
| if (e->expandHorizontal) |
| o = Qt::Horizontal; |
| if (e->expandVertical) |
| o |= Qt::Vertical; |
| return o; |
| } |
| |
| /*! |
| \reimp |
| */ |
| bool QFormLayout::hasHeightForWidth() const |
| { |
| Q_D(const QFormLayout); |
| QFormLayoutPrivate *e = const_cast<QFormLayoutPrivate *>(d); |
| e->updateSizes(); |
| return (d->has_hfw || rowWrapPolicy() == WrapLongRows); |
| } |
| |
| /*! |
| \reimp |
| */ |
| int QFormLayout::heightForWidth(int width) const |
| { |
| Q_D(const QFormLayout); |
| if (!hasHeightForWidth()) |
| return -1; |
| |
| int leftMargin, topMargin, rightMargin, bottomMargin; |
| getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin); |
| |
| int targetWidth = width - leftMargin - rightMargin; |
| |
| if (!d->haveHfwCached(targetWidth)) { |
| QFormLayoutPrivate *dat = const_cast<QFormLayoutPrivate *>(d); |
| dat->setupVerticalLayoutData(targetWidth); |
| dat->setupHorizontalLayoutData(targetWidth); |
| dat->recalcHFW(targetWidth); |
| } |
| if (targetWidth == d->sh_width) |
| return d->hfw_sh_height + topMargin + bottomMargin; |
| else |
| return d->hfw_height + topMargin + bottomMargin; |
| } |
| |
| /*! |
| \reimp |
| */ |
| void QFormLayout::setGeometry(const QRect &rect) |
| { |
| Q_D(QFormLayout); |
| if (d->dirty || rect != geometry()) { |
| QRect cr = rect; |
| int leftMargin, topMargin, rightMargin, bottomMargin; |
| getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin); |
| cr.adjust(+leftMargin, +topMargin, -rightMargin, -bottomMargin); |
| |
| bool hfw = hasHeightForWidth(); |
| d->setupVerticalLayoutData(cr.width()); |
| d->setupHorizontalLayoutData(cr.width()); |
| if (hfw && (!d->haveHfwCached(cr.width()) || d->hfwLayouts.size() != d->vLayoutCount)) |
| d->recalcHFW(cr.width()); |
| if (hfw) { |
| qGeomCalc(d->hfwLayouts, 0, d->vLayoutCount, cr.y(), cr.height()); |
| d->arrangeWidgets(d->hfwLayouts, cr); |
| } else { |
| qGeomCalc(d->vLayouts, 0, d->vLayoutCount, cr.y(), cr.height()); |
| d->arrangeWidgets(d->vLayouts, cr); |
| } |
| QLayout::setGeometry(rect); |
| } |
| } |
| |
| /*! |
| \reimp |
| */ |
| QSize QFormLayout::sizeHint() const |
| { |
| Q_D(const QFormLayout); |
| if (!d->prefSize.isValid()) { |
| QFormLayoutPrivate *dat = const_cast<QFormLayoutPrivate *>(d); |
| dat->calcSizeHints(); |
| } |
| return d->prefSize; |
| } |
| |
| /*! |
| \reimp |
| */ |
| QSize QFormLayout::minimumSize() const |
| { |
| // ### fix minimumSize if hfw |
| Q_D(const QFormLayout); |
| if (!d->minSize.isValid()) { |
| QFormLayoutPrivate *dat = const_cast<QFormLayoutPrivate *>(d); |
| dat->calcSizeHints(); |
| } |
| return d->minSize; |
| } |
| |
| /*! |
| \reimp |
| */ |
| void QFormLayout::invalidate() |
| { |
| Q_D(QFormLayout); |
| d->dirty = true; |
| d->sizesDirty = true; |
| d->minSize = QSize(); |
| d->prefSize = QSize(); |
| d->formMaxWidth = -1; |
| d->hfw_width = -1; |
| d->sh_width = -1; |
| d->layoutWidth = -1; |
| d->hfw_sh_height = -1; |
| QLayout::invalidate(); |
| } |
| |
| /*! |
| Returns the number of rows in the form. |
| |
| \sa QLayout::count() |
| */ |
| int QFormLayout::rowCount() const |
| { |
| Q_D(const QFormLayout); |
| return d->m_matrix.rowCount(); |
| } |
| |
| /*! |
| Returns the layout item in the given \a row with the specified \a |
| role (column). Returns \nullptr if there is no such item. |
| |
| \sa QLayout::itemAt(), setItem() |
| */ |
| QLayoutItem *QFormLayout::itemAt(int row, ItemRole role) const |
| { |
| Q_D(const QFormLayout); |
| if (uint(row) >= uint(d->m_matrix.rowCount())) |
| return nullptr; |
| switch (role) { |
| case SpanningRole: |
| if (QFormLayoutItem *item = d->m_matrix(row, 1)) |
| if (item->fullRow) |
| return item->item; |
| break; |
| case LabelRole: |
| case FieldRole: |
| if (QFormLayoutItem *item = d->m_matrix(row, (role == LabelRole) ? 0 : 1)) |
| return item->item; |
| break; |
| } |
| return nullptr; |
| } |
| |
| /*! |
| Retrieves the row and role (column) of the item at the specified |
| \a index. If \a index is out of bounds, *\a rowPtr is set to -1; |
| otherwise the row is stored in *\a rowPtr and the role is stored |
| in *\a rolePtr. |
| |
| \sa itemAt(), count(), getLayoutPosition(), getWidgetPosition() |
| */ |
| void QFormLayout::getItemPosition(int index, int *rowPtr, ItemRole *rolePtr) const |
| { |
| Q_D(const QFormLayout); |
| int col = -1; |
| int row = -1; |
| |
| const int storageIndex = storageIndexFromLayoutItem(d->m_matrix, d->m_things.value(index)); |
| if (storageIndex != -1) |
| QFormLayoutPrivate::ItemMatrix::storageIndexToPosition(storageIndex, &row, &col); |
| |
| if (rowPtr) |
| *rowPtr = row; |
| if (rolePtr && row != -1) { |
| const bool spanning = col == 1 && d->m_matrix(row, col)->fullRow; |
| if (spanning) { |
| *rolePtr = SpanningRole; |
| } else { |
| *rolePtr = ItemRole(col); |
| } |
| } |
| } |
| |
| /*! |
| Retrieves the row and role (column) of the specified child \a |
| layout. If \a layout is not in the form layout, *\a rowPtr is set |
| to -1; otherwise the row is stored in *\a rowPtr and the role is stored |
| in *\a rolePtr. |
| */ |
| void QFormLayout::getLayoutPosition(QLayout *layout, int *rowPtr, ItemRole *rolePtr) const |
| { |
| int n = count(); |
| int index = 0; |
| while (index < n) { |
| if (itemAt(index) == layout) |
| break; |
| ++index; |
| } |
| getItemPosition(index, rowPtr, rolePtr); |
| } |
| |
| /*! |
| Retrieves the row and role (column) of the specified \a widget in |
| the layout. If \a widget is not in the layout, *\a rowPtr is set |
| to -1; otherwise the row is stored in *\a rowPtr and the role is stored |
| in *\a rolePtr. |
| |
| \sa getItemPosition(), itemAt() |
| */ |
| void QFormLayout::getWidgetPosition(QWidget *widget, int *rowPtr, ItemRole *rolePtr) const |
| { |
| getItemPosition(indexOf(widget), rowPtr, rolePtr); |
| } |
| |
| // ### eliminate labelForField() |
| |
| /*! |
| Returns the label associated with the given \a field. |
| |
| \sa itemAt() |
| */ |
| QWidget *QFormLayout::labelForField(QWidget *field) const |
| { |
| Q_D(const QFormLayout); |
| |
| int row; |
| ItemRole role = LabelRole; |
| |
| getWidgetPosition(field, &row, &role); |
| |
| if (row != -1 && role == FieldRole) { |
| if (QFormLayoutItem *label = d->m_matrix(row, LabelRole)) |
| return label->widget(); |
| } |
| return nullptr; |
| } |
| |
| /*! |
| \overload |
| */ |
| QWidget *QFormLayout::labelForField(QLayout *field) const |
| { |
| Q_D(const QFormLayout); |
| |
| int row; |
| ItemRole role; |
| |
| getLayoutPosition(field, &row, &role); |
| |
| if (row != -1 && role == FieldRole) { |
| if (QFormLayoutItem *label = d->m_matrix(row, LabelRole)) |
| return label->widget(); |
| } |
| return nullptr; |
| } |
| |
| /*! |
| \property QFormLayout::fieldGrowthPolicy |
| \brief the way in which the form's fields grow |
| |
| The default value depends on the widget or application style. For |
| QMacStyle, the default is FieldsStayAtSizeHint; for QCommonStyle |
| derived styles (like Plastique and Windows), the default |
| is ExpandingFieldsGrow; for Qt Extended styles, the default is |
| AllNonFixedFieldsGrow. |
| |
| If none of the fields can grow and the form is resized, extra |
| space is distributed according to the current |
| \l{formAlignment}{form alignment}. |
| |
| \sa formAlignment, rowWrapPolicy |
| */ |
| |
| void QFormLayout::setFieldGrowthPolicy(FieldGrowthPolicy policy) |
| { |
| Q_D(QFormLayout); |
| if (FieldGrowthPolicy(d->fieldGrowthPolicy) != policy) { |
| d->fieldGrowthPolicy = policy; |
| invalidate(); |
| } |
| } |
| |
| QFormLayout::FieldGrowthPolicy QFormLayout::fieldGrowthPolicy() const |
| { |
| Q_D(const QFormLayout); |
| if (d->fieldGrowthPolicy == DefaultFieldGrowthPolicy) { |
| return QFormLayout::FieldGrowthPolicy(d->getStyle()->styleHint(QStyle::SH_FormLayoutFieldGrowthPolicy)); |
| } else { |
| return QFormLayout::FieldGrowthPolicy(d->fieldGrowthPolicy); |
| } |
| } |
| |
| /*! |
| \property QFormLayout::rowWrapPolicy |
| \brief the way in which the form's rows wrap |
| |
| The default value depends on the widget or application style. For |
| Qt Extended styles, the default is WrapLongRows; |
| for the other styles, the default is DontWrapRows. |
| |
| If you want to display each label above its associated field |
| (instead of next to it), set this property to WrapAllRows. |
| |
| \sa fieldGrowthPolicy |
| */ |
| |
| void QFormLayout::setRowWrapPolicy(RowWrapPolicy policy) |
| { |
| Q_D(QFormLayout); |
| if (RowWrapPolicy(d->rowWrapPolicy) != policy) { |
| d->rowWrapPolicy = policy; |
| invalidate(); |
| } |
| } |
| |
| QFormLayout::RowWrapPolicy QFormLayout::rowWrapPolicy() const |
| { |
| Q_D(const QFormLayout); |
| if (d->rowWrapPolicy == DefaultRowWrapPolicy) { |
| return QFormLayout::RowWrapPolicy(d->getStyle()->styleHint(QStyle::SH_FormLayoutWrapPolicy)); |
| } else { |
| return QFormLayout::RowWrapPolicy(d->rowWrapPolicy); |
| } |
| } |
| |
| /*! |
| \property QFormLayout::labelAlignment |
| \brief the horizontal alignment of the labels |
| |
| The default value depends on the widget or application style. For |
| QCommonStyle derived styles, except for QPlastiqueStyle, the |
| default is Qt::AlignLeft; for the other styles, the default is |
| Qt::AlignRight. |
| |
| \sa formAlignment |
| */ |
| |
| void QFormLayout::setLabelAlignment(Qt::Alignment alignment) |
| { |
| Q_D(QFormLayout); |
| if (d->labelAlignment != alignment) { |
| d->labelAlignment = alignment; |
| invalidate(); |
| } |
| } |
| |
| Qt::Alignment QFormLayout::labelAlignment() const |
| { |
| Q_D(const QFormLayout); |
| if (!d->labelAlignment) { |
| return Qt::Alignment(d->getStyle()->styleHint(QStyle::SH_FormLayoutLabelAlignment)); |
| } else { |
| return d->labelAlignment; |
| } |
| } |
| |
| /*! |
| \property QFormLayout::formAlignment |
| \brief the alignment of the form layout's contents within the layout's geometry |
| |
| The default value depends on the widget or application style. For |
| QMacStyle, the default is Qt::AlignHCenter | Qt::AlignTop; for the |
| other styles, the default is Qt::AlignLeft | Qt::AlignTop. |
| |
| \sa labelAlignment, rowWrapPolicy |
| */ |
| |
| void QFormLayout::setFormAlignment(Qt::Alignment alignment) |
| { |
| Q_D(QFormLayout); |
| if (d->formAlignment != alignment) { |
| d->formAlignment = alignment; |
| invalidate(); |
| } |
| } |
| |
| Qt::Alignment QFormLayout::formAlignment() const |
| { |
| Q_D(const QFormLayout); |
| if (!d->formAlignment) { |
| return Qt::Alignment(d->getStyle()->styleHint(QStyle::SH_FormLayoutFormAlignment)); |
| } else { |
| return d->formAlignment; |
| } |
| } |
| |
| /*! |
| \property QFormLayout::horizontalSpacing |
| \brief the spacing between widgets that are laid out side by side |
| |
| By default, if no value is explicitly set, the layout's horizontal |
| spacing is inherited from the parent layout, or from the style settings |
| for the parent widget. |
| |
| \sa verticalSpacing, QStyle::pixelMetric(), {QStyle::}{PM_LayoutHorizontalSpacing} |
| */ |
| void QFormLayout::setHorizontalSpacing(int spacing) |
| { |
| Q_D(QFormLayout); |
| if (spacing != d->hSpacing) { |
| d->hSpacing = spacing; |
| invalidate(); |
| } |
| } |
| |
| int QFormLayout::horizontalSpacing() const |
| { |
| Q_D(const QFormLayout); |
| if (d->hSpacing >= 0) { |
| return d->hSpacing; |
| } else { |
| return qSmartSpacing(this, QStyle::PM_LayoutHorizontalSpacing); |
| } |
| } |
| |
| /*! |
| \property QFormLayout::verticalSpacing |
| \brief the spacing between widgets that are laid out vertically |
| |
| By default, if no value is explicitly set, the layout's vertical spacing is |
| inherited from the parent layout, or from the style settings for the parent |
| widget. |
| |
| \sa horizontalSpacing, QStyle::pixelMetric(), {QStyle::}{PM_LayoutHorizontalSpacing} |
| */ |
| void QFormLayout::setVerticalSpacing(int spacing) |
| { |
| Q_D(QFormLayout); |
| if (spacing != d->vSpacing) { |
| d->vSpacing = spacing; |
| invalidate(); |
| } |
| } |
| |
| int QFormLayout::verticalSpacing() const |
| { |
| Q_D(const QFormLayout); |
| if (d->vSpacing >= 0) { |
| return d->vSpacing; |
| } else { |
| return qSmartSpacing(this, QStyle::PM_LayoutVerticalSpacing); |
| } |
| } |
| |
| /*! |
| This function sets both the vertical and horizontal spacing to |
| \a spacing. |
| |
| \sa setVerticalSpacing(), setHorizontalSpacing() |
| */ |
| void QFormLayout::setSpacing(int spacing) |
| { |
| Q_D(QFormLayout); |
| d->vSpacing = d->hSpacing = spacing; |
| invalidate(); |
| } |
| |
| /*! |
| If the vertical spacing is equal to the horizontal spacing, |
| this function returns that value; otherwise it returns -1. |
| |
| \sa setSpacing(), verticalSpacing(), horizontalSpacing() |
| */ |
| int QFormLayout::spacing() const |
| { |
| int hSpacing = horizontalSpacing(); |
| if (hSpacing == verticalSpacing()) { |
| return hSpacing; |
| } else { |
| return -1; |
| } |
| } |
| |
| void QFormLayoutPrivate::arrangeWidgets(const QVector<QLayoutStruct>& layouts, QRect &rect) |
| { |
| Q_Q(QFormLayout); |
| |
| int i; |
| const int rr = m_matrix.rowCount(); |
| QWidget *w = q->parentWidget(); |
| Qt::LayoutDirection layoutDirection = w ? w->layoutDirection() : QGuiApplication::layoutDirection(); |
| |
| Qt::Alignment formAlignment = fixedAlignment(q->formAlignment(), layoutDirection); |
| int leftOffset = 0; |
| int delta = rect.width() - formMaxWidth; |
| if (formAlignment & (Qt::AlignHCenter | Qt::AlignRight) && delta > 0) { |
| leftOffset = delta; |
| if (formAlignment & Qt::AlignHCenter) |
| leftOffset >>= 1; |
| } |
| |
| for (i = 0; i < rr; ++i) { |
| QFormLayoutItem *label = m_matrix(i, 0); |
| QFormLayoutItem *field = m_matrix(i, 1); |
| |
| if (label) { |
| int height = layouts.at(label->vLayoutIndex).size; |
| if ((label->expandingDirections() & Qt::Vertical) == 0) { |
| /* |
| If the field on the right-hand side is tall, |
| we want the label to be top-aligned, but not too |
| much. So we introduce a 7 / 4 factor so that it |
| gets some extra pixels at the top. |
| */ |
| height = qMin(height, |
| qMin(label->sizeHint.height() * 7 / 4, |
| label->maxSize.height())); |
| } |
| |
| QSize sz(qMin(label->layoutWidth, label->sizeHint.width()), height); |
| int x = leftOffset + rect.x() + label->layoutPos; |
| const auto fAlign = fixedAlignment(q->labelAlignment(), layoutDirection); |
| if (fAlign & Qt::AlignRight) |
| x += label->layoutWidth - sz.width(); |
| else if (fAlign & Qt::AlignHCenter) |
| x += label->layoutWidth / 2 - sz.width() / 2; |
| QPoint p(x, layouts.at(label->vLayoutIndex).pos); |
| // ### expansion & sizepolicy stuff |
| |
| label->setGeometry(QStyle::visualRect(layoutDirection, rect, QRect(p, sz))); |
| } |
| |
| if (field) { |
| QSize sz(field->layoutWidth, layouts.at(field->vLayoutIndex).size); |
| QPoint p(field->layoutPos + leftOffset + rect.x(), layouts.at(field->vLayoutIndex).pos); |
| /* |
| if ((field->widget() && field->widget()->sizePolicy().horizontalPolicy() & (QSizePolicy::GrowFlag | QSizePolicy::ExpandFlag | QSizePolicy::IgnoreFlag)) |
| || (field->layout() && sz.width() < field->maxSize.width())) { |
| sz.rwidth() = field->layoutWidth; |
| } |
| */ |
| if (field->maxSize.isValid()) |
| sz = sz.boundedTo(field->maxSize); |
| |
| field->setGeometry(QStyle::visualRect(layoutDirection, rect, QRect(p, sz))); |
| } |
| } |
| } |
| |
| /*! |
| Sets the widget in the given \a row for the given \a role to \a widget, extending the |
| layout with empty rows if necessary. |
| |
| If the cell is already occupied, the \a widget is not inserted and an error message is |
| sent to the console. |
| |
| \b{Note:} For most applications, addRow() or insertRow() should be used instead of setWidget(). |
| |
| \sa setLayout() |
| */ |
| void QFormLayout::setWidget(int row, ItemRole role, QWidget *widget) |
| { |
| Q_D(QFormLayout); |
| int rowCnt = rowCount(); |
| if (row >= rowCnt) |
| d->insertRows(rowCnt, row - rowCnt + 1); |
| d->setWidget(row, role, widget); |
| } |
| |
| /*! |
| Sets the sub-layout in the given \a row for the given \a role to \a layout, extending the |
| form layout with empty rows if necessary. |
| |
| If the cell is already occupied, the \a layout is not inserted and an error message is |
| sent to the console. |
| |
| \b{Note:} For most applications, addRow() or insertRow() should be used instead of setLayout(). |
| |
| \sa setWidget() |
| */ |
| void QFormLayout::setLayout(int row, ItemRole role, QLayout *layout) |
| { |
| Q_D(QFormLayout); |
| int rowCnt = rowCount(); |
| if (row >= rowCnt) |
| d->insertRows(rowCnt, row - rowCnt + 1); |
| d->setLayout(row, role, layout); |
| } |
| |
| /*! |
| Sets the item in the given \a row for the given \a role to \a item, extending the |
| layout with empty rows if necessary. |
| |
| If the cell is already occupied, the \a item is not inserted and an error message is |
| sent to the console. |
| The \a item spans both columns. |
| |
| \warning Do not use this function to add child layouts or child |
| widget items. Use setLayout() or setWidget() instead. |
| |
| \sa setLayout() |
| */ |
| void QFormLayout::setItem(int row, ItemRole role, QLayoutItem *item) |
| { |
| Q_D(QFormLayout); |
| int rowCnt = rowCount(); |
| if (row >= rowCnt) |
| d->insertRows(rowCnt, row - rowCnt + 1); |
| d->setItem(row, role, item); |
| } |
| |
| /*! |
| \internal |
| */ |
| |
| void QFormLayout::resetFieldGrowthPolicy() |
| { |
| Q_D(QFormLayout); |
| d->fieldGrowthPolicy = DefaultFieldGrowthPolicy; |
| } |
| |
| /*! |
| \internal |
| */ |
| |
| void QFormLayout::resetRowWrapPolicy() |
| { |
| Q_D(QFormLayout); |
| d->rowWrapPolicy = DefaultRowWrapPolicy; |
| } |
| |
| /*! |
| \internal |
| */ |
| |
| void QFormLayout::resetFormAlignment() |
| { |
| Q_D(QFormLayout); |
| d->formAlignment = { }; |
| } |
| |
| /*! |
| \internal |
| */ |
| |
| void QFormLayout::resetLabelAlignment() |
| { |
| Q_D(QFormLayout); |
| d->labelAlignment = { }; |
| } |
| |
| #if 0 |
| void QFormLayout::dump() const |
| { |
| Q_D(const QFormLayout); |
| for (int i = 0; i < rowCount(); ++i) { |
| for (int j = 0; j < 2; ++j) { |
| qDebug("m_matrix(%d, %d) = %p", i, j, d->m_matrix(i, j)); |
| } |
| } |
| for (int i = 0; i < d->m_things.count(); ++i) |
| qDebug("m_things[%d] = %p", i, d->m_things.at(i)); |
| } |
| #endif |
| |
| QT_END_NAMESPACE |
| |
| #include "moc_qformlayout.cpp" |