summaryrefslogtreecommitdiffstats
path: root/tests/auto/widgets/kernel
diff options
context:
space:
mode:
authorVolker Hilsheimer <volker.hilsheimer@qt.io>2022-02-22 21:48:58 +0100
committerVolker Hilsheimer <volker.hilsheimer@qt.io>2022-02-24 17:07:43 +0100
commita74cdf778c0c72fa22d9354207d729b2cbf88128 (patch)
tree47e8846376a28caefd2f1b3cc4c60df0a9cd94b9 /tests/auto/widgets/kernel
parent599c648b77628fe195e78d6e7ac145f258f8880b (diff)
Implement QFormLayout::set/isRowVisible
Hiding a row in a form layout is inconvenient to do as access to the widgets in each row is cumbersome. In addition, a row might include a layout for the label or the field column, and we can't hide layouts and instead need to navigate to the widgets inside the layout. And even if an application developer does all that, the spacing calculation doesn't ignore hidden rows. Add setRowVisible and isRowVisible APIs with the usual overloads. Implement the logic to traverse a layout item to its contained widgets, so that they are explicitly hidden when a row is hidden, and skip hidden rows in the spacing calculation. [ChangeLog][Widgets][QFormLayout] New APIs setRowVisible and isRowVisible to hide and show rows in a form layout. Fixes: QTBUG-6864 Change-Id: I6af98409802f331c4523e91d7dac8a97762c579d Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Axel Spoerl <axel.spoerl@qt.io> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'tests/auto/widgets/kernel')
-rw-r--r--tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp130
1 files changed, 130 insertions, 0 deletions
diff --git a/tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp b/tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp
index 4aa9f8ac2d..a7ac5fa479 100644
--- a/tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp
+++ b/tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp
@@ -135,6 +135,7 @@ private slots:
void takeRow_QLayout();
void setWidget();
void setLayout();
+ void hideShowRow();
/*
QLayoutItem *itemAt(int row, ItemRole role) const;
@@ -1123,6 +1124,135 @@ 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::itemAt()
{
QWidget topLevel;