Skip to content

Commit 42d681f

Browse files
tmartsumThe Qt Project
authored and
The Qt Project
committed
Add widget replace function to QLayout
Sometimes it is nice to be able to replace a widget in a layout. Change-Id: I23a6a65e417e94d53bc48639503db1a142bc3f10 Reviewed-by: J-P Nurmi <[email protected]>
1 parent e327ba1 commit 42d681f

File tree

14 files changed

+401
-2
lines changed

14 files changed

+401
-2
lines changed

src/widgets/kernel/qboxlayout.cpp

+16
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ class QBoxLayoutPrivate : public QLayoutPrivate
140140
void calcHfw(int);
141141

142142
void effectiveMargins(int *left, int *top, int *right, int *bottom) const;
143+
QLayoutItem* replaceAt(int index, QLayoutItem*) Q_DECL_OVERRIDE;
143144
};
144145

145146
QBoxLayoutPrivate::~QBoxLayoutPrivate()
@@ -444,6 +445,21 @@ void QBoxLayoutPrivate::calcHfw(int w)
444445
hfwMinHeight = mh;
445446
}
446447

448+
QLayoutItem* QBoxLayoutPrivate::replaceAt(int index, QLayoutItem *item)
449+
{
450+
Q_Q(QBoxLayout);
451+
if (!item)
452+
return 0;
453+
QBoxLayoutItem *b = list.value(index);
454+
if (!b)
455+
return 0;
456+
QLayoutItem *r = b->item;
457+
458+
b->item = item;
459+
q->invalidate();
460+
return r;
461+
}
462+
447463

448464
/*!
449465
\class QBoxLayout

src/widgets/kernel/qformlayout.cpp

+27
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ class QFormLayoutPrivate : public QLayoutPrivate
246246

247247
int hSpacing;
248248
int vSpacing;
249+
QLayoutItem* replaceAt(int index, QLayoutItem*) Q_DECL_OVERRIDE;
249250
};
250251

251252
QFormLayoutPrivate::QFormLayoutPrivate()
@@ -1001,6 +1002,32 @@ QStyle* QFormLayoutPrivate::getStyle() const
10011002
return QApplication::style();
10021003
}
10031004

1005+
QLayoutItem* QFormLayoutPrivate::replaceAt(int index, QLayoutItem *newitem)
1006+
{
1007+
Q_Q(QFormLayout);
1008+
if (!newitem)
1009+
return 0;
1010+
const int storageIndex = storageIndexFromLayoutItem(m_matrix, m_things.value(index));
1011+
if (storageIndex == -1) {
1012+
// ### Qt6 - fix warning too when this class becomes public
1013+
qWarning("QFormLayoutPrivate::replaceAt: Invalid index %d", index);
1014+
return 0;
1015+
}
1016+
1017+
int row, col;
1018+
QFormLayoutPrivate::ItemMatrix::storageIndexToPosition(storageIndex, &row, &col);
1019+
Q_ASSERT(m_matrix(row, col));
1020+
1021+
QFormLayoutItem *item = m_matrix(row, col);
1022+
Q_ASSERT(item);
1023+
1024+
QLayoutItem *olditem = item->item;
1025+
item->item = newitem;
1026+
1027+
q->invalidate();
1028+
return olditem;
1029+
}
1030+
10041031
/*!
10051032
\class QFormLayout
10061033
\since 4.4

src/widgets/kernel/qgridlayout.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ class QGridBox
8585
void setGeometry(const QRect &r) { item_->setGeometry(r); }
8686
Qt::Alignment alignment() const { return item_->alignment(); }
8787
QLayoutItem *item() { return item_; }
88+
void setItem(QLayoutItem *newitem) { item_ = newitem; }
8889
QLayoutItem *takeItem() { QLayoutItem *i = item_; item_ = 0; return i; }
8990

9091
int hStretch() { return item_->widget() ?
@@ -171,6 +172,18 @@ class QGridLayoutPrivate : public QLayoutPrivate
171172
}
172173
return 0;
173174
}
175+
QLayoutItem* replaceAt(int index, QLayoutItem *newitem) Q_DECL_OVERRIDE
176+
{
177+
if (!newitem)
178+
return 0;
179+
QLayoutItem *item = 0;
180+
QGridBox *b = things.value(index);
181+
if (b) {
182+
item = b->takeItem();
183+
b->setItem(newitem);
184+
}
185+
return item;
186+
}
174187

175188
void getItemPosition(int index, int *row, int *column, int *rowSpan, int *columnSpan) const {
176189
if (index < things.count()) {

src/widgets/kernel/qlayout.cpp

+48
Original file line numberDiff line numberDiff line change
@@ -1108,6 +1108,54 @@ bool QLayout::activate()
11081108
return true;
11091109
}
11101110

1111+
/*!
1112+
\since 5.2
1113+
1114+
Searches for widget \a from and replaces it with widget \a to if found.
1115+
Returns the layout item that contains the widget \a from on success. Otherwise \c 0 is returned.
1116+
If \a recursive is \c true, sub-layouts are searched for doing the replacement. Notice that the returned item therefore might not belong to this layout, but to a sub-layout.
1117+
1118+
The returned layout item is no longer owned by the layout and should be either deleted or inserted to another layout. The widget \a from is no longer managed by the layout and may need to be deleted or hidden. The parent of widget \a from is left unchanged.
1119+
1120+
This function works for the built-in Qt layouts, but might not work for custom layouts.
1121+
1122+
\sa indexOf()
1123+
*/
1124+
1125+
//### Qt 6 make this function virtual
1126+
QLayoutItem* QLayout::replaceWidget(QWidget *from, QWidget *to, bool recursive)
1127+
{
1128+
Q_D(QLayout);
1129+
if (!from || !to)
1130+
return 0;
1131+
1132+
int index = -1;
1133+
QLayoutItem *item = 0;
1134+
for (int u = 0; u < count(); ++u) {
1135+
item = itemAt(u);
1136+
if (item->widget() == from) {
1137+
index = u;
1138+
break;
1139+
}
1140+
if (item && item->layout() && recursive) {
1141+
QLayoutItem *r = item->layout()->replaceWidget(from, to, true);
1142+
if (r)
1143+
return r;
1144+
}
1145+
}
1146+
if (index == -1)
1147+
return 0;
1148+
1149+
QLayoutItem *newitem = new QWidgetItem(to);
1150+
newitem->setAlignment(item->alignment());
1151+
QLayoutItem *r = d->replaceAt(index, newitem);
1152+
if (!r)
1153+
delete newitem;
1154+
else
1155+
addChildWidget(to);
1156+
return r;
1157+
}
1158+
11111159
/*!
11121160
\fn QLayoutItem *QLayout::itemAt(int index) const
11131161

src/widgets/kernel/qlayout.h

+1
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ class Q_WIDGETS_EXPORT QLayout : public QObject, public QLayoutItem
130130
virtual int count() const = 0;
131131
bool isEmpty() const;
132132
QSizePolicy::ControlTypes controlTypes() const;
133+
QLayoutItem* replaceWidget(QWidget *from, QWidget *to, bool recursive = true);
133134

134135
int totalHeightForWidth(int w) const;
135136
QSize totalMinimumSize() const;

src/widgets/kernel/qlayout_p.h

+2
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ QT_BEGIN_NAMESPACE
6161

6262
class QWidgetItem;
6363
class QSpacerItem;
64+
class QLayoutItem;
6465

6566
class Q_WIDGETS_EXPORT QLayoutPrivate : public QObjectPrivate
6667
{
@@ -78,6 +79,7 @@ class Q_WIDGETS_EXPORT QLayoutPrivate : public QObjectPrivate
7879

7980
static QWidgetItem *createWidgetItem(const QLayout *layout, QWidget *widget);
8081
static QSpacerItem *createSpacerItem(const QLayout *layout, int w, int h, QSizePolicy::Policy hPolicy = QSizePolicy::Minimum, QSizePolicy::Policy vPolicy = QSizePolicy::Minimum);
82+
virtual QLayoutItem* replaceAt(int index, QLayoutItem *newitem) { Q_UNUSED(index); Q_UNUSED(newitem); return 0; }
8183

8284
static QWidgetItemFactoryMethod widgetItemFactoryMethod;
8385
static QSpacerItemFactoryMethod spacerItemFactoryMethod;

src/widgets/kernel/qstackedlayout.cpp

+18
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,29 @@ class QStackedLayoutPrivate : public QLayoutPrivate
5353
Q_DECLARE_PUBLIC(QStackedLayout)
5454
public:
5555
QStackedLayoutPrivate() : index(-1), stackingMode(QStackedLayout::StackOne) {}
56+
QLayoutItem* replaceAt(int index, QLayoutItem *newitem) Q_DECL_OVERRIDE;
5657
QList<QLayoutItem *> list;
5758
int index;
5859
QStackedLayout::StackingMode stackingMode;
5960
};
6061

62+
QLayoutItem* QStackedLayoutPrivate::replaceAt(int idx, QLayoutItem *newitem)
63+
{
64+
Q_Q(QStackedLayout);
65+
if (idx < 0 || idx >= list.size() || !newitem)
66+
return 0;
67+
QWidget *wdg = newitem->widget();
68+
if (!wdg) {
69+
qWarning("QStackedLayout::replaceAt: Only widgets can be added");
70+
return 0;
71+
}
72+
QLayoutItem *orgitem = list.at(idx);
73+
list.replace(idx, newitem);
74+
if (idx == index)
75+
q->setCurrentIndex(index);
76+
return orgitem;
77+
}
78+
6179
/*!
6280
\class QStackedLayout
6381

tests/auto/widgets/kernel/qboxlayout/tst_qboxlayout.cpp

+20-1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ private slots:
7979

8080
void taskQTBUG_7103_minMaxWidthNotRespected();
8181
void taskQTBUG_27420_takeAtShouldUnparentLayout();
82+
void replaceWidget();
8283
};
8384

8485
class CustomLayoutStyle : public QProxyStyle
@@ -328,7 +329,6 @@ void tst_QBoxLayout::taskQTBUG_27420_takeAtShouldUnparentLayout()
328329
QVERIFY(!inner.isNull());
329330
}
330331

331-
332332
struct Descr
333333
{
334334
Descr(int min, int sh, int max = -1, bool exp= false, int _stretch = 0, bool _empty = false)
@@ -507,5 +507,24 @@ void tst_QBoxLayout::testLayoutEngine()
507507
}
508508
}
509509

510+
void tst_QBoxLayout::replaceWidget()
511+
{
512+
QWidget w;
513+
QBoxLayout *boxLayout = new QVBoxLayout(&w);
514+
515+
QLineEdit *replaceFrom = new QLineEdit;
516+
QLineEdit *replaceTo = new QLineEdit;
517+
boxLayout->addWidget(new QLineEdit());
518+
boxLayout->addWidget(replaceFrom);
519+
boxLayout->addWidget(new QLineEdit());
520+
521+
QCOMPARE(boxLayout->indexOf(replaceFrom), 1);
522+
QCOMPARE(boxLayout->indexOf(replaceTo), -1);
523+
boxLayout->replaceWidget(replaceFrom, replaceTo);
524+
525+
QCOMPARE(boxLayout->indexOf(replaceFrom), -1);
526+
QCOMPARE(boxLayout->indexOf(replaceTo), 1);
527+
}
528+
510529
QTEST_MAIN(tst_QBoxLayout)
511530
#include "tst_qboxlayout.moc"

tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp

+49
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ private slots:
122122
void itemAt();
123123
void takeAt();
124124
void layoutAlone();
125+
void replaceWidget();
125126
/*
126127
void setGeometry(const QRect &rect);
127128
QSize minimumSize() const;
@@ -934,6 +935,54 @@ void tst_QFormLayout::taskQTBUG_27420_takeAtShouldUnparentLayout()
934935
QVERIFY(!inner.isNull());
935936
}
936937

938+
void tst_QFormLayout::replaceWidget()
939+
{
940+
QWidget w;
941+
QFormLayout *layout = new QFormLayout();
942+
w.setLayout(layout);
943+
QLineEdit *edit1 = new QLineEdit();
944+
QLineEdit *edit2 = new QLineEdit();
945+
QLineEdit *edit3 = new QLineEdit();
946+
QLabel *label1 = new QLabel();
947+
QLabel *label2 = new QLabel();
948+
949+
layout->addRow("Label", edit1);
950+
layout->addRow(label1, edit2);
951+
952+
// Verify controls not in layout
953+
QCOMPARE(layout->indexOf(edit3), -1);
954+
QCOMPARE(layout->indexOf(label2), -1);
955+
956+
// Verify controls in layout
957+
int editIndex = layout->indexOf(edit1);
958+
int labelIndex = layout->indexOf(label1);
959+
QVERIFY(editIndex > 0);
960+
QVERIFY(labelIndex > 0);
961+
int rownum;
962+
QFormLayout::ItemRole role;
963+
964+
// replace editor
965+
layout->replaceWidget(edit1, edit3);
966+
edit1->hide(); // Not strictly needed for the test, but for normal usage it is.
967+
QCOMPARE(layout->indexOf(edit1), -1);
968+
QCOMPARE(layout->indexOf(edit3), editIndex);
969+
QCOMPARE(layout->indexOf(label1), labelIndex);
970+
rownum = -1;
971+
role = QFormLayout::SpanningRole;
972+
layout->getWidgetPosition(edit3, &rownum, &role);
973+
QCOMPARE(rownum, 0);
974+
QCOMPARE(role, QFormLayout::FieldRole);
975+
976+
layout->replaceWidget(label1, label2);
977+
label1->hide();
978+
QCOMPARE(layout->indexOf(label1), -1);
979+
QCOMPARE(layout->indexOf(label2), labelIndex);
980+
layout->getWidgetPosition(label2, &rownum, &role);
981+
QCOMPARE(rownum, 1);
982+
QCOMPARE(role, QFormLayout::LabelRole);
983+
984+
}
985+
937986
QTEST_MAIN(tst_QFormLayout)
938987

939988
#include "tst_qformlayout.moc"

tests/auto/widgets/kernel/qgridlayout/tst_qgridlayout.cpp

+45
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ private slots:
101101
void distributeMultiCell();
102102

103103
void taskQTBUG_27420_takeAtShouldUnparentLayout();
104+
void replaceWidget();
104105

105106
private:
106107
QWidget *testWidget;
@@ -1656,5 +1657,49 @@ void tst_QGridLayout::taskQTBUG_27420_takeAtShouldUnparentLayout()
16561657
QVERIFY(!inner.isNull());
16571658
}
16581659

1660+
void tst_QGridLayout::replaceWidget()
1661+
{
1662+
QWidget wdg;
1663+
QGridLayout *l = new QGridLayout();
1664+
const int itemCount = 9;
1665+
QLabel *labels[itemCount];
1666+
1667+
// setup layout
1668+
for (int n = 0; n < itemCount; ++n) {
1669+
int x = n % 3;
1670+
int y = n / 3;
1671+
labels[n] = new QLabel(QString("label %1").arg(n));
1672+
Qt::Alignment align = (n % 3 ? Qt::AlignLeft : Qt::AlignRight);
1673+
l->addWidget(labels[n], x * 3, y * 3, (n % 2) + 1, (n + 1) % 2 + 1, align);
1674+
}
1675+
wdg.setLayout(l);
1676+
1677+
// iterate and replace
1678+
for (int n = 0; n < itemCount; n += 2) {
1679+
int i = l->indexOf(labels[n]);
1680+
int fromRow, fromCol, fromRowSpan, fromColSpan;
1681+
l->getItemPosition(i, &fromRow, &fromCol, &fromRowSpan, &fromColSpan);
1682+
Qt::Alignment fromAlign = l->itemAt(i)->alignment();
1683+
// do replace
1684+
QPushButton *pb = new QPushButton("replaced");
1685+
QLayoutItem *olditem = l->replaceWidget(labels[n], pb);
1686+
// verify
1687+
QCOMPARE(i, l->indexOf(pb));
1688+
QVERIFY(olditem != 0);
1689+
QCOMPARE(l->indexOf(labels[n]), -1);
1690+
int toRow, toCol, toRowSpan, toColSpan;
1691+
l->getItemPosition(i, &toRow, &toCol, &toRowSpan, &toColSpan);
1692+
QCOMPARE(fromRow, toRow);
1693+
QCOMPARE(fromCol, toCol);
1694+
QCOMPARE(fromRowSpan, toRowSpan);
1695+
QCOMPARE(fromColSpan, toColSpan);
1696+
Qt::Alignment toAlign = l->itemAt(i)->alignment();
1697+
QCOMPARE(fromAlign, toAlign);
1698+
// clean up
1699+
olditem->widget()->deleteLater();
1700+
delete olditem;
1701+
}
1702+
}
1703+
16591704
QTEST_MAIN(tst_QGridLayout)
16601705
#include "tst_qgridlayout.moc"

0 commit comments

Comments
 (0)