diff options
-rw-r--r-- | src/widgets/kernel/qformlayout.cpp | 18 | ||||
-rw-r--r-- | tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp | 44 |
2 files changed, 60 insertions, 2 deletions
diff --git a/src/widgets/kernel/qformlayout.cpp b/src/widgets/kernel/qformlayout.cpp index e924c71997..991b1429d7 100644 --- a/src/widgets/kernel/qformlayout.cpp +++ b/src/widgets/kernel/qformlayout.cpp @@ -480,6 +480,7 @@ void QFormLayoutPrivate::recalcHFW(int w) void QFormLayoutPrivate::setupHfwLayoutData() { + Q_Q(QFormLayout); // setupVerticalLayoutData must be called before this // setupHorizontalLayoutData must also be called before this // copies non hfw data into hfw @@ -504,6 +505,10 @@ void QFormLayoutPrivate::setupHfwLayoutData() QFormLayoutItem *label = m_matrix(i, 0); QFormLayoutItem *field = m_matrix(i, 1); + // ignore rows with only hidden items + if (!q->isRowVisible(i)) + continue; + if (label && label->vLayoutIndex > -1) { if (label->isHfw) { // We don't check sideBySide here, since a label is only @@ -681,9 +686,15 @@ void QFormLayoutPrivate::setupVerticalLayoutData(int width) QFormLayoutItem *label = m_matrix(i, 0); QFormLayoutItem *field = m_matrix(i, 1); - // Totally ignore empty rows or rows with only hidden items - if (!q->isRowVisible(i)) + // Ignore empty rows or rows with only hidden items, + // and invalidate their position in the layout. + if (!q->isRowVisible(i)) { + if (label) + label->vLayoutIndex = -1; + if (field) + field->vLayoutIndex = -1; continue; + } QSize min1; QSize min2; @@ -2190,6 +2201,9 @@ void QFormLayoutPrivate::arrangeWidgets(const QList<QLayoutStruct> &layouts, QRe QFormLayoutItem *label = m_matrix(i, 0); QFormLayoutItem *field = m_matrix(i, 1); + if (!q->isRowVisible(i)) + continue; + if (label && label->vLayoutIndex > -1) { int height = layouts.at(label->vLayoutIndex).size; if ((label->expandingDirections() & Qt::Vertical) == 0) { diff --git a/tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp b/tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp index 33e6bd64db..44b716bb34 100644 --- a/tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp +++ b/tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp @@ -112,6 +112,7 @@ private slots: void setLayout(); void hideShowRow(); void showWithHiddenRow(); + void hiddenRowAndStretch(); /* QLayoutItem *itemAt(int row, ItemRole role) const; @@ -1253,6 +1254,49 @@ void tst_QFormLayout::showWithHiddenRow() topLevel.show(); } +/* + Test that hiding rows does not leave outdated layout data behind + in hidden items that results in out-of-bounds array access. See + QTBUG-109237. +*/ +void tst_QFormLayout::hiddenRowAndStretch() +{ + QWidget topLevel; + QFormLayout layout; + layout.setRowWrapPolicy(QFormLayout::WrapAllRows); + + // We need our own stretcher item so that QFormLayout doesn't insert + // it's own, as that would grow the size of the layout data array again. + QSpacerItem *stretch = new QSpacerItem(100, 100, QSizePolicy::Expanding, QSizePolicy::Expanding); + layout.setItem(0, QFormLayout::FieldRole, stretch); + + QLabel *lastLabel = nullptr; + QLineEdit *lastField = nullptr; + for (int row = 1; row < 4; ++row) { + QLabel *label = new QLabel(QString("Label %1").arg(row)); + label->setWordWrap(true); + QLineEdit *field = new QLineEdit; + layout.setWidget(row, QFormLayout::LabelRole, label); + layout.setWidget(row, QFormLayout::FieldRole, field); + if (row == 3) { + lastLabel = label; + lastField = field; + } + } + + Q_ASSERT(lastLabel); + Q_ASSERT(lastField); + + topLevel.setLayout(&layout); + topLevel.sizeHint(); + + lastLabel->setVisible(false); + lastField->setVisible(false); + + // should not assert here + topLevel.show(); +} + void tst_QFormLayout::itemAt() { QWidget topLevel; |