summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/widgets/widgets/qsplitter.cpp71
-rw-r--r--src/widgets/widgets/qsplitter.h1
-rw-r--r--src/widgets/widgets/qsplitter_p.h1
-rw-r--r--tests/auto/widgets/widgets/qsplitter/tst_qsplitter.cpp172
4 files changed, 242 insertions, 3 deletions
diff --git a/src/widgets/widgets/qsplitter.cpp b/src/widgets/widgets/qsplitter.cpp
index 910904e96e..77a793e951 100644
--- a/src/widgets/widgets/qsplitter.cpp
+++ b/src/widgets/widgets/qsplitter.cpp
@@ -731,6 +731,12 @@ void QSplitterPrivate::setSizes_helper(const QList<int> &sizes, bool clampNegati
doResize();
}
+bool QSplitterPrivate::shouldShowWidget(const QWidget *w) const
+{
+ Q_Q(const QSplitter);
+ return q->isVisible() && !(w->isHidden() && w->testAttribute(Qt::WA_WState_ExplicitShowHide));
+}
+
void QSplitterPrivate::setGeo(QSplitterLayoutStruct *sls, int p, int s, bool allowCollapse)
{
Q_Q(QSplitter);
@@ -827,8 +833,7 @@ void QSplitterPrivate::insertWidget_helper(int index, QWidget *widget, bool show
{
Q_Q(QSplitter);
QBoolBlocker b(blockChildAdd);
- bool needShow = show && q->isVisible() &&
- !(widget->isHidden() && widget->testAttribute(Qt::WA_WState_ExplicitShowHide));
+ const bool needShow = show && shouldShowWidget(widget);
if (widget->parentWidget() != q)
widget->setParent(q);
if (needShow)
@@ -1125,6 +1130,66 @@ void QSplitter::insertWidget(int index, QWidget *widget)
}
/*!
+ \since 5.9
+
+ Replaces the widget in the splitter's layout at the given \a index by \a widget.
+
+ Returns the widget that has just been replaced if \a index is valid and \a widget
+ is not already a child of the splitter. Otherwise, it returns null and no replacement
+ or addition is made.
+
+ The geometry of the newly inserted widget will be the same as the widget it replaces.
+ Its visible and collapsed states are also inherited.
+
+ \note The splitter takes ownership of \a widget and sets the parent of the
+ replaced widget to null.
+
+ \sa insertWidget(), indexOf()
+*/
+QWidget *QSplitter::replaceWidget(int index, QWidget *widget)
+{
+ Q_D(QSplitter);
+ if (!widget) {
+ qWarning("QSplitter::replaceWidget: Widget can't be null");
+ return nullptr;
+ }
+
+ if (index < 0 || index >= d->list.count()) {
+ qWarning("QSplitter::replaceWidget: Index %d out of range", index);
+ return nullptr;
+ }
+
+ QSplitterLayoutStruct *s = d->list.at(index);
+ QWidget *current = s->widget;
+ if (current == widget) {
+ qWarning("QSplitter::replaceWidget: Trying to replace a widget with itself");
+ return nullptr;
+ }
+
+ if (widget->parentWidget() == this) {
+ qWarning("QSplitter::replaceWidget: Trying to replace a widget with one of its siblings");
+ return nullptr;
+ }
+
+ QBoolBlocker b(d->blockChildAdd);
+
+ const QRect geom = current->geometry();
+ const bool shouldShow = d->shouldShowWidget(current);
+
+ s->widget = widget;
+ current->setParent(nullptr);
+ widget->setParent(this);
+
+ // The splitter layout struct's geometry is already set and
+ // should not change. Only set the geometry on the new widget
+ widget->setGeometry(geom);
+ widget->lower();
+ widget->setVisible(shouldShow);
+
+ return current;
+}
+
+/*!
\fn int QSplitter::indexOf(QWidget *widget) const
Returns the index in the splitter's layout of the specified \a widget. This
@@ -1232,7 +1297,7 @@ void QSplitter::childEvent(QChildEvent *c)
if (c->added() && !d->blockChildAdd && !d->findWidget(w)) {
d->insertWidget_helper(d->list.count(), w, false);
} else if (c->polished() && !d->blockChildAdd) {
- if (isVisible() && !(w->isHidden() && w->testAttribute(Qt::WA_WState_ExplicitShowHide)))
+ if (d->shouldShowWidget(w))
w->show();
} else if (c->type() == QEvent::ChildRemoved) {
for (int i = 0; i < d->list.size(); ++i) {
diff --git a/src/widgets/widgets/qsplitter.h b/src/widgets/widgets/qsplitter.h
index 9cfde9fed3..e9ef3c3f2d 100644
--- a/src/widgets/widgets/qsplitter.h
+++ b/src/widgets/widgets/qsplitter.h
@@ -71,6 +71,7 @@ public:
void addWidget(QWidget *widget);
void insertWidget(int index, QWidget *widget);
+ QWidget *replaceWidget(int index, QWidget *widget);
void setOrientation(Qt::Orientation);
Qt::Orientation orientation() const;
diff --git a/src/widgets/widgets/qsplitter_p.h b/src/widgets/widgets/qsplitter_p.h
index 4422d9a8a4..07b43e56b8 100644
--- a/src/widgets/widgets/qsplitter_p.h
+++ b/src/widgets/widgets/qsplitter_p.h
@@ -125,6 +125,7 @@ public:
int findWidgetJustBeforeOrJustAfter(int index, int delta, int &collapsibleSize) const;
void updateHandles();
void setSizes_helper(const QList<int> &sizes, bool clampNegativeSize = false);
+ bool shouldShowWidget(const QWidget *w) const;
};
diff --git a/tests/auto/widgets/widgets/qsplitter/tst_qsplitter.cpp b/tests/auto/widgets/widgets/qsplitter/tst_qsplitter.cpp
index f490446c8a..c4cd771f92 100644
--- a/tests/auto/widgets/widgets/qsplitter/tst_qsplitter.cpp
+++ b/tests/auto/widgets/widgets/qsplitter/tst_qsplitter.cpp
@@ -77,6 +77,10 @@ private slots:
void rubberBandNotInSplitter();
void saveAndRestoreStateOfNotYetShownSplitter();
void saveAndRestoreHandleWidth();
+ void replaceWidget_data();
+ void replaceWidget();
+ void replaceWidgetWithSplitterChild_data();
+ void replaceWidgetWithSplitterChild();
// task-specific tests below me:
void task187373_addAbstractScrollAreas();
@@ -645,9 +649,177 @@ public:
MyFriendlySplitter(QWidget *parent = 0) : QSplitter(parent) {}
void setRubberBand(int pos) { QSplitter::setRubberBand(pos); }
+ void moveSplitter(int pos, int index) { QSplitter::moveSplitter(pos, index); }
+
friend class tst_QSplitter;
};
+class EventCounterSpy : public QObject
+{
+public:
+ EventCounterSpy(QWidget *parentWidget) : QObject(parentWidget)
+ { }
+
+ bool eventFilter(QObject *watched, QEvent *event) override
+ {
+ // Watch for events in the parent widget and all its children
+ if (watched == parent() || watched->parent() == parent()) {
+ if (event->type() == QEvent::Resize)
+ resizeCount++;
+ else if (event->type() == QEvent::Paint)
+ paintCount++;
+ }
+
+ return QObject::eventFilter(watched, event);
+ }
+
+ int resizeCount = 0;
+ int paintCount = 0;
+};
+
+void tst_QSplitter::replaceWidget_data()
+{
+ QTest::addColumn<int>("index");
+ QTest::addColumn<bool>("visible");
+ QTest::addColumn<bool>("collapsed");
+
+ QTest::newRow("negative index") << -1 << true << false;
+ QTest::newRow("index too large") << 80 << true << false;
+ QTest::newRow("visible, not collapsed") << 3 << true << false;
+ QTest::newRow("visible, collapsed") << 3 << true << true;
+ QTest::newRow("not visible, not collapsed") << 3 << false << false;
+ QTest::newRow("not visible, collapsed") << 3 << false << true;
+}
+
+void tst_QSplitter::replaceWidget()
+{
+ QFETCH(int, index);
+ QFETCH(bool, visible);
+ QFETCH(bool, collapsed);
+
+ // Setup
+ MyFriendlySplitter sp;
+ const int count = 7;
+ for (int i = 0; i < count; i++) {
+ // We use labels instead of plain widgets to
+ // make it easier to fix eventual regressions.
+ QLabel *w = new QLabel(QString::asprintf("WIDGET #%d", i));
+ sp.addWidget(w);
+ }
+ sp.setWindowTitle(QString::asprintf("index %d, visible %d, collapsed %d", index, visible, collapsed));
+ sp.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&sp));
+
+ // Configure splitter
+ QWidget *oldWidget = sp.widget(index);
+ const QRect oldGeom = oldWidget ? oldWidget->geometry() : QRect();
+ if (oldWidget) {
+ // Collapse first, then hide, if necessary
+ if (collapsed) {
+ sp.setCollapsible(index, true);
+ sp.moveSplitter(oldWidget->x() + 1, index + 1);
+ }
+ if (!visible)
+ oldWidget->hide();
+ }
+
+ // Replace widget
+ QTest::qWait(100); // Flush event queue
+ const QList<int> sizes = sp.sizes();
+ // Shorter label: The important thing is to ensure we can set
+ // the same size on the new widget. Because of QLabel's sizing
+ // constraints (they can expand but not shrink) the easiest is
+ // to set a shorter label.
+ QLabel *newWidget = new QLabel(QLatin1String("<b>NEW</b>"));
+
+ EventCounterSpy *ef = new EventCounterSpy(&sp);
+ qApp->installEventFilter(ef);
+ const QWidget *res = sp.replaceWidget(index, newWidget);
+ QTest::qWait(100); // Give visibility and resizing some time
+ qApp->removeEventFilter(ef);
+
+ // Check
+ if (index < 0 || index >= count) {
+ QVERIFY(!res);
+ QVERIFY(!newWidget->parentWidget());
+ QCOMPARE(ef->resizeCount, 0);
+ QCOMPARE(ef->paintCount, 0);
+ } else {
+ QCOMPARE(res, oldWidget);
+ QVERIFY(!res->parentWidget());
+ QVERIFY(!res->isVisible());
+ QCOMPARE(newWidget->parentWidget(), &sp);
+ QCOMPARE(newWidget->isVisible(), visible);
+ if (visible && !collapsed)
+ QCOMPARE(newWidget->geometry(), oldGeom);
+ QCOMPARE(newWidget->size().isEmpty(), !visible || collapsed);
+ const int expectedResizeCount = visible ? 1 : 0; // new widget only
+ const int expectedPaintCount = visible && !collapsed ? 2 : 0; // splitter and new widget
+ QCOMPARE(ef->resizeCount, expectedResizeCount);
+ QCOMPARE(ef->paintCount, expectedPaintCount);
+ delete res;
+ }
+ QCOMPARE(sp.count(), count);
+ QCOMPARE(sp.sizes(), sizes);
+}
+
+void tst_QSplitter::replaceWidgetWithSplitterChild_data()
+{
+ QTest::addColumn<int>("srcIndex");
+ QTest::addColumn<int>("dstIndex");
+
+ QTest::newRow("replace with null widget") << -2 << 3;
+ QTest::newRow("replace with itself") << 3 << 3;
+ QTest::newRow("replace with sibling, after recalc") << 1 << 4;
+ QTest::newRow("replace with sibling, before recalc") << -1 << 4;
+}
+
+void tst_QSplitter::replaceWidgetWithSplitterChild()
+{
+ QFETCH(int, srcIndex);
+ QFETCH(int, dstIndex);
+
+ // Setup
+ MyFriendlySplitter sp;
+ const int count = 7;
+ for (int i = 0; i < count; i++) {
+ // We use labels instead of plain widgets to
+ // make it easier to fix eventual regressions.
+ QLabel *w = new QLabel(QString::asprintf("WIDGET #%d", i));
+ sp.addWidget(w);
+ }
+ sp.setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1Char(' ') + QLatin1String(QTest::currentDataTag()));
+ sp.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&sp));
+
+ QTest::qWait(100); // Flush event queue before new widget creation
+ const QList<int> sizes = sp.sizes();
+ QWidget *sibling = srcIndex == -1 ? (new QLabel("<b>NEW</b>", &sp)) : sp.widget(srcIndex);
+
+ EventCounterSpy *ef = new EventCounterSpy(&sp);
+ qApp->installEventFilter(ef);
+ const QWidget *res = sp.replaceWidget(dstIndex, sibling);
+ QTest::qWait(100); // Give visibility and resizing some time
+ qApp->removeEventFilter(ef);
+
+ QVERIFY(!res);
+ if (srcIndex == -1) {
+ // Create and replace before recalc. The sibling is scheduled to be
+ // added after replaceWidget(), when QSplitter receives a child event.
+ QVERIFY(ef->resizeCount > 0);
+ QVERIFY(ef->paintCount > 0);
+ QCOMPARE(sp.count(), count + 1);
+ QCOMPARE(sp.sizes().mid(0, count), sizes);
+ QCOMPARE(sp.sizes().last(), sibling->width());
+ } else {
+ // No-op for the rest
+ QCOMPARE(ef->resizeCount, 0);
+ QCOMPARE(ef->paintCount, 0);
+ QCOMPARE(sp.count(), count);
+ QCOMPARE(sp.sizes(), sizes);
+ }
+}
+
void tst_QSplitter::rubberBandNotInSplitter()
{
MyFriendlySplitter split;