summaryrefslogtreecommitdiffstats
path: root/tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp')
-rw-r--r--tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp230
1 files changed, 202 insertions, 28 deletions
diff --git a/tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp b/tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp
index 4aa9f8ac2d..9638823538 100644
--- a/tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp
+++ b/tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** 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 General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** 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-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
@@ -135,6 +110,9 @@ private slots:
void takeRow_QLayout();
void setWidget();
void setLayout();
+ void hideShowRow();
+ void showWithHiddenRow();
+ void hiddenRowAndStretch();
/*
QLayoutItem *itemAt(int row, ItemRole role) const;
@@ -274,7 +252,9 @@ void tst_QFormLayout::wrapping()
fl->setRowWrapPolicy(QFormLayout::WrapLongRows);
QLineEdit *le = new QLineEdit;
- QLabel *lbl = new QLabel("A long label");
+ QLabel *lbl = new QLabel("A long label which is actually long enough to trigger wrapping,"
+ " even on Android and even if it is executed on a tiling window"
+ " manager which forces the window into fullscreen mode.");
le->setMinimumWidth(200);
fl->addRow(lbl, le);
@@ -814,6 +794,7 @@ void tst_QFormLayout::removeRow_QWidget()
QCOMPARE(layout->rowCount(), 0);
QWidget *w3 = new QWidget;
+ QTest::ignoreMessage(QtWarningMsg, "QFormLayout::takeRow: Invalid widget");
layout->removeRow(w3);
delete w3;
}
@@ -854,6 +835,7 @@ void tst_QFormLayout::removeRow_QLayout()
QCOMPARE(layout->rowCount(), 0);
QHBoxLayout *l3 = new QHBoxLayout;
+ QTest::ignoreMessage(QtWarningMsg, "QFormLayout::takeRow: Invalid layout");
layout->removeRow(l3);
delete l3;
}
@@ -893,6 +875,7 @@ void tst_QFormLayout::takeRow()
QCOMPARE(layout->rowCount(), 0);
QCOMPARE(result.fieldItem->widget(), w1.data());
+ QTest::ignoreMessage(QtWarningMsg, "QFormLayout::takeRow: Invalid row 0");
result = layout->takeRow(0);
QVERIFY(!result.fieldItem);
@@ -933,6 +916,7 @@ void tst_QFormLayout::takeRow_QWidget()
QCOMPARE(layout->rowCount(), 0);
QWidget *w3 = new QWidget;
+ QTest::ignoreMessage(QtWarningMsg, "QFormLayout::takeRow: Invalid widget");
result = layout->takeRow(w3);
delete w3;
@@ -980,6 +964,7 @@ void tst_QFormLayout::takeRow_QLayout()
QCOMPARE(layout->rowCount(), 0);
QHBoxLayout *l3 = new QHBoxLayout;
+ QTest::ignoreMessage(QtWarningMsg, "QFormLayout::takeRow: Invalid layout");
result = layout->takeRow(l3);
delete l3;
@@ -1009,7 +994,9 @@ void tst_QFormLayout::setWidget()
QCOMPARE(layout.rowCount(), 6);
// should be ignored and generate warnings
+ QTest::ignoreMessage(QtWarningMsg, "QFormLayoutPrivate::setItem: Cell (3, 1) already occupied");
layout.setWidget(3, QFormLayout::FieldRole, &w4);
+ QTest::ignoreMessage(QtWarningMsg, "QFormLayoutPrivate::setItem: Invalid cell (-1, 1)");
layout.setWidget(-1, QFormLayout::FieldRole, &w4);
{
@@ -1077,7 +1064,9 @@ void tst_QFormLayout::setLayout()
QCOMPARE(layout.rowCount(), 6);
// should be ignored and generate warnings
+ QTest::ignoreMessage(QtWarningMsg, "QFormLayoutPrivate::setItem: Cell (3, 1) already occupied");
layout.setLayout(3, QFormLayout::FieldRole, &l4);
+ QTest::ignoreMessage(QtWarningMsg, "QLayout::addChildLayout: layout QHBoxLayout \"\" already has a parent");
layout.setLayout(-1, QFormLayout::FieldRole, &l4);
QCOMPARE(layout.count(), 3);
QCOMPARE(layout.rowCount(), 6);
@@ -1123,6 +1112,191 @@ void tst_QFormLayout::setLayout()
}
}
+void tst_QFormLayout::hideShowRow()
+{
+ QWidget topLevel;
+ QFormLayout layout;
+
+ const auto makeComplex = []{
+ QHBoxLayout *hboxField = new QHBoxLayout;
+ hboxField->addWidget(new QLineEdit("Left"));
+ hboxField->addWidget(new QLineEdit("Right"));
+ return hboxField;
+ };
+
+ layout.addRow("Label", new QLineEdit("one"));
+ layout.addRow("Label", new QLineEdit("two"));
+ layout.addRow("Label", new QLineEdit("three"));
+ layout.addRow("Label", makeComplex());
+ layout.addRow(new QLineEdit("five")); // spanning widget
+ layout.addRow(makeComplex()); // spanning layout
+
+ topLevel.setLayout(&layout);
+ topLevel.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
+
+ // returns the top-left position of the items in a row
+ const auto rowPosition = [&layout](int row) {
+ QRect rect;
+ if (QLayoutItem *spanningItem = layout.itemAt(row, QFormLayout::SpanningRole)) {
+ rect = spanningItem->geometry();
+ } else {
+ if (QLayoutItem *labelItem = layout.itemAt(row, QFormLayout::LabelRole)) {
+ rect = labelItem->geometry();
+ }
+ if (QLayoutItem *fieldItem = layout.itemAt(row, QFormLayout::FieldRole)) {
+ rect |= fieldItem->geometry();
+ }
+ }
+ return rect.topLeft();
+ };
+
+ // returns the first widget in a row, even if that row is taken by a layout
+ const auto rowInputWidget = [&layout](int row) -> QWidget* {
+ auto fieldItem = layout.itemAt(row, QFormLayout::FieldRole);
+ if (!fieldItem)
+ return nullptr;
+ QWidget *fieldWidget = fieldItem->widget();
+ // we happen to know our layout structure
+ if (!fieldWidget)
+ fieldWidget = fieldItem->layout()->itemAt(0)->widget();
+ return fieldWidget;
+ };
+
+ // record the reference positions for all rows
+ QList<QPoint> rowPositions(layout.rowCount());
+ for (int row = 0; row < layout.rowCount(); ++row)
+ rowPositions[row] = rowPosition(row);
+
+ // hide each row in turn, the next row should take the space of the hidden row
+ for (int row = 0; row < layout.rowCount(); ++ row) {
+ layout.setRowVisible(row, false);
+ QVERIFY(!layout.isRowVisible(row));
+ if (row < layout.rowCount() - 1)
+ QTRY_COMPARE(rowPosition(row + 1), rowPositions[row]);
+ layout.setRowVisible(row, true);
+ QVERIFY(layout.isRowVisible(row));
+ }
+
+ // Hiding only the label or only the field doesn't hide the row.
+ for (int row = 0; row < layout.rowCount() - 1; ++row) {
+ const auto labelItem = layout.itemAt(0, QFormLayout::LabelRole);
+ if (labelItem) {
+ labelItem->widget()->hide();
+ QVERIFY(layout.isRowVisible(row));
+ QCOMPARE(rowPosition(row), rowPositions[row]);
+ layout.itemAt(0, QFormLayout::LabelRole)->widget()->show();
+ }
+ const auto fieldItem = layout.itemAt(0, QFormLayout::FieldRole);
+ if (fieldItem) {
+ fieldItem->widget()->hide();
+ QVERIFY(layout.isRowVisible(row));
+ QCOMPARE(rowPosition(row), rowPositions[row]);
+ layout.itemAt(0, QFormLayout::FieldRole)->widget()->show();
+ }
+ }
+
+ // If we hide both label and field, then the row should be considered hidden and the
+ // following row should move up into the space of the hidden row. We can only test
+ // this if both label and field are widgets, or if there is a spanning widget.
+ for (int row = 0; row < layout.rowCount() - 1; ++row) {
+ QWidget *labelWidget = nullptr;
+ if (auto labelItem = layout.itemAt(row, QFormLayout::LabelRole))
+ labelWidget = labelItem->widget();
+ QWidget *fieldWidget = nullptr;
+ if (auto fieldItem = layout.itemAt(row, QFormLayout::FieldRole))
+ fieldWidget = fieldItem->widget();
+
+ if (!fieldWidget)
+ continue;
+ if (labelWidget)
+ labelWidget->hide();
+ fieldWidget->hide();
+ QVERIFY(!layout.isRowVisible(row));
+ QVERIFY(!layout.isRowVisible(fieldWidget));
+ if (labelWidget)
+ QVERIFY(!layout.isRowVisible(labelWidget));
+ QTRY_COMPARE(rowPosition(row + 1), rowPositions[row]);
+ if (labelWidget)
+ labelWidget->show();
+ fieldWidget->show();
+ }
+
+ // hiding a row where a widget has focus must move focus to a widget in the next row
+ for (int row = 0; row < layout.rowCount(); ++row) {
+ QWidget *inputWidget = rowInputWidget(row);
+ QVERIFY(inputWidget);
+ inputWidget->setFocus();
+ layout.setRowVisible(row, false);
+ QVERIFY(!inputWidget->hasFocus());
+ }
+
+ // Now hide all rows, hide the toplevel widget, and show the toplevel widget again.
+ // None of the widgets inside must be visible.
+ for (int row = 0; row < layout.rowCount(); ++row)
+ layout.setRowVisible(row, false);
+ topLevel.hide();
+ topLevel.show();
+ for (int row = 0; row < layout.rowCount(); ++row)
+ QVERIFY(rowInputWidget(row)->isHidden());
+}
+
+void tst_QFormLayout::showWithHiddenRow()
+{
+ QWidget topLevel;
+ QFormLayout layout;
+
+ for (int row = 0; row < 3; ++row)
+ layout.addRow(QString("Label %1").arg(row), new QLineEdit);
+ layout.setRowVisible(1, false);
+
+ topLevel.setLayout(&layout);
+ 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;