1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QCompleter>
30#include <QHBoxLayout>
31#include <QLineEdit>
32#include <QListWidget>
33#include <QSignalSpy>
34#include <QStyledItemDelegate>
35#include <QTest>
36#include <private/qlistwidget_p.h>
37
38using IntList = QVector<int>;
39
40class tst_QListWidget : public QObject
41{
42 Q_OBJECT
43
44public:
45 tst_QListWidget() = default;
46
47 enum ModelChanged {
48 RowsAboutToBeInserted,
49 RowsInserted,
50 RowsAboutToBeRemoved,
51 RowsRemoved,
52 ColumnsAboutToBeInserted,
53 ColumnsInserted,
54 ColumnsAboutToBeRemoved,
55 ColumnsRemoved
56 };
57
58private slots:
59 void initTestCase();
60 void cleanupTestCase();
61 void init();
62 void addItem();
63 void addItem2();
64 void addItems();
65 void openPersistentEditor();
66 void closePersistentEditor();
67 void count();
68 void currentItem();
69 void setCurrentItem_data();
70 void setCurrentItem();
71 void currentRow();
72 void setCurrentRow_data();
73 void setCurrentRow();
74 void editItem_data();
75 void editItem();
76 void findItems();
77 void insertItem_data();
78 void insertItem();
79 void insertItems_data();
80 void insertItems();
81 void moveItemsPriv_data();
82 void moveItemsPriv();
83
84 void itemAssignment();
85 void item_data();
86 void item();
87 void takeItem_data();
88 void takeItem();
89 void setItemHidden();
90 void selectedItems_data();
91 void selectedItems();
92 void removeItems_data();
93 void removeItems();
94 void itemStreaming_data();
95 void itemStreaming();
96 void sortItems_data();
97 void sortItems();
98 void sortHiddenItems();
99 void sortHiddenItems_data();
100 void closeEditor();
101 void setData_data();
102 void setData();
103 void insertItemsWithSorting_data();
104 void insertItemsWithSorting();
105 void changeDataWithSorting_data();
106 void changeDataWithSorting();
107 void itemData();
108 void itemWidget();
109#ifndef Q_OS_MAC
110 void fastScroll();
111#endif
112 void insertUnchanged();
113 void setSortingEnabled();
114 void task199503_crashWhenCleared();
115 void task217070_scrollbarsAdjusted();
116 void task258949_keypressHangup();
117 void QTBUG8086_currentItemChangedOnClick();
118 void QTBUG14363_completerWithAnyKeyPressedEditTriggers();
119 void mimeData();
120 void QTBUG50891_ensureSelectionModelSignalConnectionsAreSet();
121#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
122 void clearItemData();
123#endif
124
125 void moveRows_data();
126 void moveRows();
127 void moveRowsInvalid_data();
128 void moveRowsInvalid();
129
130protected slots:
131 void rowsAboutToBeInserted(const QModelIndex &parent, int first, int last)
132 { modelChanged(change: RowsAboutToBeInserted, parent, first, last); }
133 void rowsInserted(const QModelIndex &parent, int first, int last)
134 { modelChanged(change: RowsInserted, parent, first, last); }
135 void rowsAboutToBeRemoved(const QModelIndex &parent, int first, int last)
136 { modelChanged(change: RowsAboutToBeRemoved, parent, first, last); }
137 void rowsRemoved(const QModelIndex &parent, int first, int last)
138 { modelChanged(change: RowsRemoved, parent, first, last); }
139 void columnsAboutToBeInserted(const QModelIndex &parent, int first, int last)
140 { modelChanged(change: ColumnsAboutToBeInserted, parent, first, last); }
141 void columnsInserted(const QModelIndex &parent, int first, int last)
142 { modelChanged(change: ColumnsInserted, parent, first, last); }
143 void columnsAboutToBeRemoved(const QModelIndex &parent, int first, int last)
144 { modelChanged(change: ColumnsAboutToBeRemoved, parent, first, last); }
145 void columnsRemoved(const QModelIndex &parent, int first, int last)
146 { modelChanged(change: ColumnsRemoved, parent, first, last); }
147
148 void modelChanged(ModelChanged change, const QModelIndex &parent, int first, int last)
149 {
150 rcParent[change] = parent;
151 rcFirst[change] = first;
152 rcLast[change] = last;
153 }
154
155private:
156 QListWidget *testWidget = nullptr;
157 QVector<QModelIndex> rcParent{8};
158 QVector<int> rcFirst = QVector<int>(8, 0);
159 QVector<int> rcLast = QVector<int>(8, 0);
160
161 void populate();
162 void checkDefaultValues();
163};
164
165
166void tst_QListWidget::moveRowsInvalid_data()
167{
168 QTest::addColumn<QListWidget*>(name: "baseWidget");
169 QTest::addColumn<QModelIndex>(name: "startParent");
170 QTest::addColumn<int>(name: "startRow");
171 QTest::addColumn<int>(name: "count");
172 QTest::addColumn<QModelIndex>(name: "destinationParent");
173 QTest::addColumn<int>(name: "destination");
174
175 const auto createWidget = []() -> QListWidget* {
176 QListWidget* result = new QListWidget;
177 result->addItems(labels: {"A", "B", "C", "D", "E", "F"});
178 return result;
179 };
180 constexpr int rowCount = 6;
181
182 QTest::addRow(format: "destination_equal_source") << createWidget() << QModelIndex() << 0 << 1 << QModelIndex() << 0;
183 QTest::addRow(format: "count_equal_0") << createWidget() << QModelIndex() << 0 << 0 << QModelIndex() << 2;
184 QListWidget* tempWidget = createWidget();
185 QTest::addRow(format: "move_child") << tempWidget << tempWidget->model()->index(row: 0, column: 0) << 0 << 1 << QModelIndex() << 2;
186 tempWidget = createWidget();
187 QTest::addRow(format: "move_to_child") << tempWidget << QModelIndex() << 0 << 1 << tempWidget->model()->index(row: 0, column: 0) << 2;
188 QTest::addRow(format: "negative_count") << createWidget() << QModelIndex() << 0 << -1 << QModelIndex() << 2;
189 QTest::addRow(format: "negative_source_row") << createWidget() << QModelIndex() << -1 << 1 << QModelIndex() << 2;
190 QTest::addRow(format: "negative_destination_row") << createWidget() << QModelIndex() << 0 << 1 << QModelIndex() << -1;
191 QTest::addRow(format: "source_row_equal_rowCount") << createWidget() << QModelIndex() << rowCount << 1 << QModelIndex() << 1;
192 QTest::addRow(format: "source_row_equal_destination_row") << createWidget() << QModelIndex() << 2 << 1 << QModelIndex() << 2;
193 QTest::addRow(format: "source_row_equal_destination_row_plus1") << createWidget() << QModelIndex() << 2 << 1 << QModelIndex() << 3;
194 QTest::addRow(format: "destination_row_greater_rowCount") << createWidget() << QModelIndex() << 0 << 1 << QModelIndex() << rowCount + 1;
195 QTest::addRow(format: "move_row_within_source_range") << createWidget() << QModelIndex() << 0 << 3 << QModelIndex() << 2;
196}
197
198void tst_QListWidget::moveRowsInvalid()
199{
200 QFETCH(QListWidget* const, baseWidget);
201 QFETCH(const QModelIndex, startParent);
202 QFETCH(const int, startRow);
203 QFETCH(const int, count);
204 QFETCH(const QModelIndex, destinationParent);
205 QFETCH(const int, destination);
206 QAbstractItemModel *baseModel = baseWidget->model();
207 QSignalSpy rowMovedSpy(baseModel, &QAbstractItemModel::rowsMoved);
208 QSignalSpy rowAboutMovedSpy(baseModel, &QAbstractItemModel::rowsAboutToBeMoved);
209 QVERIFY(rowMovedSpy.isValid());
210 QVERIFY(rowAboutMovedSpy.isValid());
211 QVERIFY(!baseModel->moveRows(startParent, startRow, count, destinationParent, destination));
212 QCOMPARE(rowMovedSpy.size(), 0);
213 QCOMPARE(rowAboutMovedSpy.size(), 0);
214 delete baseWidget;
215}
216
217void tst_QListWidget::moveRows_data()
218{
219 QTest::addColumn<int>(name: "startRow");
220 QTest::addColumn<int>(name: "count");
221 QTest::addColumn<int>(name: "destination");
222 QTest::addColumn<QStringList>(name: "expected");
223
224 QTest::newRow(dataTag: "1_Item_from_top_to_middle") << 0 << 1 << 3 << QStringList{"B", "C", "A", "D", "E", "F"};
225 QTest::newRow(dataTag: "1_Item_from_top_to_bottom") << 0 << 1 << 6 << QStringList{"B", "C", "D", "E", "F", "A"};
226 QTest::newRow(dataTag: "1_Item_from_middle_to_top") << 2 << 1 << 0 << QStringList{"C", "A", "B", "D", "E", "F"};
227 QTest::newRow(dataTag: "1_Item_from_bottom_to_middle") << 5 << 1 << 2 << QStringList{"A", "B", "F", "C", "D", "E"};
228 QTest::newRow(dataTag: "1_Item_from_bottom to_top") << 5 << 1 << 0 << QStringList{"F", "A", "B", "C", "D", "E"};
229 QTest::newRow(dataTag: "1_Item_from_middle_to_bottom") << 2 << 1 << 6 << QStringList{"A", "B", "D", "E", "F", "C"};
230 QTest::newRow(dataTag: "1_Item_from_middle_to_middle_before") << 2 << 1 << 1 << QStringList{"A", "C", "B", "D", "E", "F"};
231 QTest::newRow(dataTag: "1_Item_from_middle_to_middle_after") << 2 << 1 << 4 << QStringList{"A", "B", "D", "C", "E", "F"};
232
233 QTest::newRow(dataTag: "2_Items_from_top_to_middle") << 0 << 2 << 3 << QStringList{"C", "A", "B", "D", "E", "F"};
234 QTest::newRow(dataTag: "2_Items_from_top_to_bottom") << 0 << 2 << 6 << QStringList{"C", "D", "E", "F", "A", "B"};
235 QTest::newRow(dataTag: "2_Items_from_middle_to_top") << 2 << 2 << 0 << QStringList{"C", "D", "A", "B", "E", "F"};
236 QTest::newRow(dataTag: "2_Items_from_bottom_to_middle") << 4 << 2 << 2 << QStringList{"A", "B", "E", "F", "C", "D"};
237 QTest::newRow(dataTag: "2_Items_from_bottom_to_top") << 4 << 2 << 0 << QStringList{"E", "F", "A", "B", "C", "D"};
238 QTest::newRow(dataTag: "2_Items_from_middle_to_bottom") << 2 << 2 << 6 << QStringList{"A", "B", "E", "F", "C", "D"};
239 QTest::newRow(dataTag: "2_Items_from_middle_to_middle_before") << 3 << 2 << 1 << QStringList{"A", "D", "E", "B", "C", "F"};
240 QTest::newRow(dataTag: "2_Items_from_middle_to_middle_after") << 1 << 2 << 5 << QStringList{"A", "D", "E", "B", "C", "F"};
241}
242
243void tst_QListWidget::moveRows()
244{
245 QFETCH(const int, startRow);
246 QFETCH(const int, count);
247 QFETCH(const int, destination);
248 QFETCH(const QStringList, expected);
249 QListWidget baseWidget;
250 baseWidget.addItems(labels: QStringList{"A", "B", "C", "D", "E", "F"});
251 QAbstractItemModel *baseModel = baseWidget.model();
252 QSignalSpy rowMovedSpy(baseModel, &QAbstractItemModel::rowsMoved);
253 QSignalSpy rowAboutMovedSpy(baseModel, &QAbstractItemModel::rowsAboutToBeMoved);
254 QVERIFY(baseModel->moveRows(QModelIndex(), startRow, count, QModelIndex(), destination));
255 QCOMPARE(baseModel->rowCount(), expected.size());
256 for (int i = 0; i < expected.size(); ++i)
257 QCOMPARE(baseModel->index(i, 0).data().toString(), expected.at(i));
258 QCOMPARE(rowMovedSpy.size(), 1);
259 QCOMPARE(rowAboutMovedSpy.size(), 1);
260 for (const QList<QVariant> &signalArgs : {rowMovedSpy.first(), rowAboutMovedSpy.first()}){
261 QVERIFY(!signalArgs.at(0).value<QModelIndex>().isValid());
262 QCOMPARE(signalArgs.at(1).toInt(), startRow);
263 QCOMPARE(signalArgs.at(2).toInt(), startRow + count - 1);
264 QVERIFY(!signalArgs.at(3).value<QModelIndex>().isValid());
265 QCOMPARE(signalArgs.at(4).toInt(), destination);
266 }
267}
268
269
270void tst_QListWidget::initTestCase()
271{
272 qRegisterMetaType<QListWidgetItem*>(typeName: "QListWidgetItem*");
273 qRegisterMetaType<QList<QPersistentModelIndex>>(typeName: "QList<QPersistentModelIndex>");
274 qRegisterMetaType<QAbstractItemModel::LayoutChangeHint>(typeName: "QAbstractItemModel::LayoutChangeHint");
275
276 testWidget = new QListWidget;
277 testWidget->show();
278
279 connect(sender: testWidget->model(), signal: &QAbstractItemModel::rowsAboutToBeInserted,
280 receiver: this, slot: &tst_QListWidget::rowsAboutToBeInserted);
281 connect(sender: testWidget->model(), signal: &QAbstractItemModel::rowsInserted,
282 receiver: this, slot: &tst_QListWidget::rowsInserted);
283 connect(sender: testWidget->model(), signal: &QAbstractItemModel::rowsAboutToBeRemoved,
284 receiver: this, slot: &tst_QListWidget::rowsAboutToBeRemoved);
285 connect(sender: testWidget->model(), signal: &QAbstractItemModel::rowsRemoved,
286 receiver: this, slot: &tst_QListWidget::rowsRemoved);
287
288 connect(sender: testWidget->model(), signal: &QAbstractItemModel::columnsAboutToBeInserted,
289 receiver: this, slot: &tst_QListWidget::columnsAboutToBeInserted);
290 connect(sender: testWidget->model(), signal: &QAbstractItemModel::columnsInserted,
291 receiver: this, slot: &tst_QListWidget::columnsInserted);
292 connect(sender: testWidget->model(), signal: &QAbstractItemModel::columnsAboutToBeRemoved,
293 receiver: this, slot: &tst_QListWidget::columnsAboutToBeRemoved);
294 connect(sender: testWidget->model(), signal: &QAbstractItemModel::columnsRemoved,
295 receiver: this, slot: &tst_QListWidget::columnsRemoved);
296
297 checkDefaultValues();
298}
299
300void tst_QListWidget::cleanupTestCase()
301{
302 delete testWidget;
303}
304
305void tst_QListWidget::init()
306{
307 testWidget->clear();
308 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
309}
310
311void tst_QListWidget::checkDefaultValues()
312{
313 QCOMPARE(testWidget->currentItem(), nullptr);
314 QCOMPARE(testWidget->currentRow(), -1);
315 QCOMPARE(testWidget->count(), 0);
316}
317
318void tst_QListWidget::populate()
319{
320 addItem();
321 addItem2();
322 addItems();
323 setItemHidden();
324
325 testWidget->setCurrentIndex(testWidget->model()->index(row: 0, column: 0));
326
327 // setCurrentItem();
328 // setCurrentRow();
329}
330
331void tst_QListWidget::addItem()
332{
333 int count = testWidget->count();
334 const QString label = QString::number(count);
335 testWidget->addItem(label);
336 QCOMPARE(testWidget->count(), ++count);
337 QCOMPARE(testWidget->item(testWidget->count() - 1)->text(), label);
338}
339
340void tst_QListWidget::addItem2()
341{
342 int count = testWidget->count();
343
344 // Boundary Checking
345 testWidget->addItem(aitem: nullptr);
346 QCOMPARE(testWidget->count(), count);
347
348 QListWidgetItem *item = new QListWidgetItem(QString::number(count));
349 item->setFlags(item->flags() | Qt::ItemIsEditable);
350 testWidget->addItem(aitem: item);
351 QCOMPARE(testWidget->count(), ++count);
352 QCOMPARE(testWidget->item(testWidget->count()-1), item);
353 QCOMPARE(item->isHidden(), false);
354}
355
356void tst_QListWidget::addItems()
357{
358 int count = testWidget->count();
359
360 // Boundary Checking
361 testWidget->addItems(labels: QStringList());
362 QCOMPARE(testWidget->count(), count);
363
364 QString label = QString::number(count);
365 const QStringList stringList{QString::number(testWidget->count() + 1),
366 QString::number(testWidget->count() + 2),
367 QString::number(testWidget->count() + 3),
368 label};
369 testWidget->addItems(labels: stringList);
370 QCOMPARE(testWidget->count(), count + stringList.count());
371 QCOMPARE(testWidget->item(testWidget->count()-1)->text(), label);
372}
373
374
375void tst_QListWidget::openPersistentEditor()
376{
377 // Boundary checking
378 testWidget->openPersistentEditor(item: nullptr);
379 QListWidgetItem *item = new QListWidgetItem(QString::number(testWidget->count()));
380 testWidget->openPersistentEditor(item);
381
382 int childCount = testWidget->viewport()->children().count();
383 testWidget->addItem(aitem: item);
384 testWidget->openPersistentEditor(item);
385 QCOMPARE(childCount + 1, testWidget->viewport()->children().count());
386}
387
388void tst_QListWidget::closePersistentEditor()
389{
390 // Boundary checking
391 int childCount = testWidget->viewport()->children().count();
392 testWidget->closePersistentEditor(item: nullptr);
393 QListWidgetItem *item = new QListWidgetItem(QString::number(testWidget->count()));
394 testWidget->closePersistentEditor(item);
395 QCOMPARE(childCount, testWidget->viewport()->children().count());
396
397 // Create something
398 testWidget->addItem(aitem: item);
399 testWidget->openPersistentEditor(item);
400
401 // actual test
402 childCount = testWidget->viewport()->children().count();
403 testWidget->closePersistentEditor(item);
404 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
405 QCOMPARE(testWidget->viewport()->children().count(), childCount - 1);
406}
407
408void tst_QListWidget::setItemHidden()
409{
410#if QT_DEPRECATED_SINCE(5, 13)
411QT_WARNING_PUSH
412QT_WARNING_DISABLE_DEPRECATED
413 // Boundary checking
414 testWidget->setItemHidden(item: nullptr, hide: true);
415 testWidget->setItemHidden(item: nullptr, hide: false);
416QT_WARNING_POP
417#endif
418
419 auto countHidden = [](QListWidget *testWidget)
420 {
421 int totalHidden = 0;
422 for (int i = 0; i < testWidget->model()->rowCount(); ++i) {
423 if (testWidget->item(row: i)->isHidden())
424 totalHidden++;
425 }
426 return totalHidden;
427 };
428 const int totalHidden = countHidden(testWidget);
429 QListWidgetItem *item = new QListWidgetItem(QString::number(testWidget->count()));
430 testWidget->addItem(aitem: item);
431
432 // Check that nothing else changed
433 QCOMPARE(countHidden(testWidget), totalHidden);
434
435 item->setHidden(true);
436 QCOMPARE(item->isHidden(), true);
437
438 // Check that nothing else changed
439 QCOMPARE(countHidden(testWidget), totalHidden + 1);
440
441 item->setHidden(false);
442 QCOMPARE(item->isHidden(), false);
443
444 // Check that nothing else changed
445 QCOMPARE(countHidden(testWidget), totalHidden);
446
447 item->setHidden(true);
448}
449
450void tst_QListWidget::setCurrentItem_data()
451{
452 QTest::addColumn<int>(name: "fill");
453 QTest::newRow(dataTag: "HasItems: 0") << 0;
454 QTest::newRow(dataTag: "HasItems: 1") << 1;
455 QTest::newRow(dataTag: "HasItems: 2") << 2;
456 QTest::newRow(dataTag: "HasItems: 3") << 3;
457}
458
459void tst_QListWidget::setCurrentItem()
460{
461 QFETCH(int, fill);
462 for (int i = 0; i < fill; ++i)
463 testWidget->addItem(label: QString::number(i));
464
465 // Boundary checking
466 testWidget->setCurrentItem(nullptr);
467 QVERIFY(!testWidget->currentItem());
468 QListWidgetItem item;
469 testWidget->setCurrentItem(&item);
470 QVERIFY(!testWidget->currentItem());
471
472 // Make sure that currentItem changes to what is passed into setCurrentItem
473 for (int i = 0; i < testWidget->count(); ++i) {
474 testWidget->setCurrentItem(testWidget->item(row: i));
475 for (int j = 0; j < testWidget->count(); ++j) {
476 testWidget->setCurrentItem(testWidget->item(row: j));
477 QCOMPARE(testWidget->item(j), testWidget->currentItem());
478 }
479 }
480}
481
482void tst_QListWidget::setCurrentRow_data()
483{
484 QTest::addColumn<int>(name: "fill");
485 QTest::newRow(dataTag: "HasItems: 0") << 0;
486 QTest::newRow(dataTag: "HasItems: 1") << 1;
487 QTest::newRow(dataTag: "HasItems: 2") << 2;
488 QTest::newRow(dataTag: "HasItems: 3") << 3;
489}
490
491void tst_QListWidget::setCurrentRow()
492{
493 QFETCH(int, fill);
494 for (int i = 0; i < fill; ++i)
495 testWidget->addItem(label: QString::number(i));
496
497 // Boundary checking
498 testWidget->setCurrentRow(-1);
499 QCOMPARE(-1, testWidget->currentRow());
500 testWidget->setCurrentRow(testWidget->count());
501 QCOMPARE(-1, testWidget->currentRow());
502
503 // Make sure that currentRow changes to what is passed into setCurrentRow
504 for (int i = 0; i < testWidget->count(); ++i) {
505 testWidget->setCurrentRow(i);
506 for (int j = 0; j < testWidget->count(); ++j) {
507 testWidget->setCurrentRow(j);
508 QCOMPARE(j, testWidget->currentRow());
509 }
510 }
511}
512
513void tst_QListWidget::count()
514{
515 populate();
516
517 // actual test
518 QCOMPARE(testWidget->model()->rowCount(), testWidget->count());
519}
520
521void tst_QListWidget::currentItem()
522{
523 populate();
524
525 // actual test
526 QModelIndex currentIndex = testWidget->selectionModel()->currentIndex();
527 if (currentIndex.isValid())
528 QCOMPARE(testWidget->currentItem(), testWidget->item(currentIndex.row()));
529 else
530 QCOMPARE(testWidget->currentItem(), nullptr);
531}
532
533void tst_QListWidget::currentRow()
534{
535 populate();
536
537 // actual test
538 QModelIndex currentIndex = testWidget->selectionModel()->currentIndex();
539 if (currentIndex.isValid())
540 QCOMPARE(testWidget->currentRow(), currentIndex.row());
541 else
542 QCOMPARE(testWidget->currentRow(), -1);
543}
544
545void tst_QListWidget::editItem_data()
546{
547 QTest::addColumn<bool>(name: "editable");
548 QTest::newRow(dataTag: "editable") << true;
549 QTest::newRow(dataTag: "not editable") << false;
550}
551
552void tst_QListWidget::editItem()
553{
554 // Boundary checking
555 testWidget->editItem(item: nullptr);
556 QListWidgetItem *item = new QListWidgetItem(QString::number(testWidget->count()));
557 testWidget->editItem(item);
558
559 QFETCH(bool, editable);
560 if (editable)
561 item->setFlags(item->flags() | Qt::ItemIsEditable);
562 testWidget->addItem(aitem: item);
563
564 int childCount = testWidget->viewport()->children().count();
565 QWidget *existsAlready = testWidget->indexWidget(index: testWidget->model()->index(row: testWidget->row(item), column: 0));
566 testWidget->editItem(item);
567 Qt::ItemFlags flags = item->flags();
568
569 // There doesn't seem to be a way to detect if the item has already been edited...
570 if (!existsAlready && flags & Qt::ItemIsEditable && flags & Qt::ItemIsEnabled) {
571 QList<QObject *> children = testWidget->viewport()->children();
572 QVERIFY(children.count() > childCount);
573 bool found = false;
574 for (int i = 0; i < children.size(); ++i) {
575 if (children.at(i)->inherits(classname: "QExpandingLineEdit"))
576 found = true;
577 }
578 QVERIFY(found);
579 } else {
580 QCOMPARE(testWidget->viewport()->children().count(), childCount);
581 }
582}
583
584void tst_QListWidget::findItems()
585{
586 // This really just tests that the items that are returned are converted from index's to items correctly.
587
588 // Boundary checking
589 QCOMPARE(testWidget->findItems("GirlsCanWearJeansAndCutTheirHairShort", Qt::MatchExactly).count(), 0);
590
591 populate();
592
593 for (int i = 0; i < testWidget->count(); ++i)
594 QCOMPARE(testWidget->findItems(testWidget->item(i)->text(), Qt::MatchExactly).count(), 1);
595}
596
597
598void tst_QListWidget::insertItem_data()
599{
600 QTest::addColumn<QStringList>(name: "initialItems");
601 QTest::addColumn<int>(name: "insertIndex");
602 QTest::addColumn<QString>(name: "itemLabel");
603 QTest::addColumn<int>(name: "expectedIndex");
604
605 const QStringList initialItems{"foo", "bar"};
606
607 QTest::newRow(dataTag: "Insert less then 0") << initialItems << -1 << "inserted" << 0;
608 QTest::newRow(dataTag: "Insert at 0") << initialItems << 0 << "inserted" << 0;
609 QTest::newRow(dataTag: "Insert beyond count") << initialItems << initialItems.count()+1 << "inserted" << initialItems.count();
610 QTest::newRow(dataTag: "Insert at count") << initialItems << initialItems.count() << "inserted" << initialItems.count();
611 QTest::newRow(dataTag: "Insert in the middle") << initialItems << 1 << "inserted" << 1;
612}
613
614void tst_QListWidget::insertItem()
615{
616 QFETCH(QStringList, initialItems);
617 QFETCH(int, insertIndex);
618 QFETCH(QString, itemLabel);
619 QFETCH(int, expectedIndex);
620
621 testWidget->insertItems(row: 0, labels: initialItems);
622 QCOMPARE(testWidget->count(), initialItems.count());
623
624 testWidget->insertItem(row: insertIndex, label: itemLabel);
625
626 QCOMPARE(rcFirst[RowsAboutToBeInserted], expectedIndex);
627 QCOMPARE(rcLast[RowsAboutToBeInserted], expectedIndex);
628 QCOMPARE(rcFirst[RowsInserted], expectedIndex);
629 QCOMPARE(rcLast[RowsInserted], expectedIndex);
630
631 QCOMPARE(testWidget->count(), initialItems.count() + 1);
632 QCOMPARE(testWidget->item(expectedIndex)->text(), itemLabel);
633}
634
635void tst_QListWidget::insertItems_data()
636{
637 QTest::addColumn<int>(name: "rowCount");
638 QTest::addColumn<int>(name: "insertType");
639
640 QTest::newRow(dataTag: "Insert 1 item using constructor") << 1 << 0;
641 QTest::newRow(dataTag: "Insert 10 items using constructor") << 10 << 0;
642 QTest::newRow(dataTag: "Insert 100 items using constructor") << 100 << 0;
643
644 QTest::newRow(dataTag: "Insert 1 item with insertItem") << 1 << 1;
645 QTest::newRow(dataTag: "Insert 10 items with insertItem") << 10 << 1;
646 QTest::newRow(dataTag: "Insert 100 items with insertItem") << 100 << 1;
647
648 QTest::newRow(dataTag: "Insert/Create 1 item using insertItem") << 1 << 2;
649 QTest::newRow(dataTag: "Insert/Create 10 items using insertItem") << 10 << 2;
650 QTest::newRow(dataTag: "Insert/Create 100 items using insertItem") << 100 << 2;
651
652 QTest::newRow(dataTag: "Insert 0 items with insertItems") << 0 << 3;
653 QTest::newRow(dataTag: "Insert 1 item with insertItems") << 1 << 3;
654 QTest::newRow(dataTag: "Insert 10 items with insertItems") << 10 << 3;
655 QTest::newRow(dataTag: "Insert 100 items with insertItems") << 100 << 3;
656}
657
658void tst_QListWidget::insertItems()
659{
660 QFETCH(int, rowCount);
661 QFETCH(int, insertType);
662
663 QSignalSpy itemChangedSpy(testWidget, &QListWidget::itemChanged);
664 QSignalSpy dataChangedSpy(testWidget->model(), &QAbstractItemModel::dataChanged);
665
666 if (insertType == 3) {
667 QStringList strings;
668 for (int i = 0; i < rowCount; ++i)
669 strings << QString::number(i);
670 testWidget->insertItems(row: 0, labels: strings);
671 } else {
672 for (int r = 0; r < rowCount; ++r) {
673 if (insertType == 0) {
674 // insert with QListWidgetItem constructor
675 new QListWidgetItem(QString::number(r), testWidget);
676 } else if (insertType == 1) {
677 // insert actual item
678 testWidget->insertItem(row: r, item: new QListWidgetItem(QString::number(r)));
679 } else if (insertType == 2) {
680 // insert/creating with string
681 testWidget->insertItem(row: r, label: QString::number(r));
682 } else if (insertType == 3) {
683 QStringList strings;
684 for (int i = 0; i < rowCount; ++i)
685 strings << QString::number(i);
686 testWidget->insertItems(row: 0, labels: strings);
687 break;
688 } else {
689 QVERIFY(0);
690 }
691 }
692 }
693 // compare the results
694 QCOMPARE(testWidget->count(), rowCount);
695
696 // check if the text
697 for (int r = 0; r < rowCount; ++r)
698 QCOMPARE(testWidget->item(r)->text(), QString::number(r));
699
700 // make sure all items have view set correctly
701 for (int i = 0; i < testWidget->count(); ++i)
702 QCOMPARE(testWidget->item(i)->listWidget(), testWidget);
703
704 QCOMPARE(itemChangedSpy.count(), 0);
705 QCOMPARE(dataChangedSpy.count(), 0);
706}
707
708void tst_QListWidget::itemAssignment()
709{
710 QListWidgetItem itemInWidget("inWidget", testWidget);
711 itemInWidget.setFlags(itemInWidget.flags() | Qt::ItemIsUserTristate);
712 QListWidgetItem itemOutsideWidget("outsideWidget");
713
714 QVERIFY(itemInWidget.listWidget());
715 QCOMPARE(itemInWidget.text(), QString("inWidget"));
716 QVERIFY(itemInWidget.flags() & Qt::ItemIsUserTristate);
717
718 QVERIFY(!itemOutsideWidget.listWidget());
719 QCOMPARE(itemOutsideWidget.text(), QString("outsideWidget"));
720 QVERIFY(!(itemOutsideWidget.flags() & Qt::ItemIsUserTristate));
721
722 itemOutsideWidget = itemInWidget;
723 QVERIFY(!itemOutsideWidget.listWidget());
724 QCOMPARE(itemOutsideWidget.text(), QString("inWidget"));
725 QVERIFY(itemOutsideWidget.flags() & Qt::ItemIsUserTristate);
726}
727
728void tst_QListWidget::item_data()
729{
730 QTest::addColumn<int>(name: "row");
731 QTest::addColumn<bool>(name: "outOfBounds");
732
733 QTest::newRow(dataTag: "First item, row: 0") << 0 << false;
734 QTest::newRow(dataTag: "Middle item, row: 1") << 1 << false;
735 QTest::newRow(dataTag: "Last item, row: 2") << 2 << false;
736 QTest::newRow(dataTag: "Out of bounds, row: -1") << -1 << true;
737 QTest::newRow(dataTag: "Out of bounds, row: 3") << 3 << true;
738}
739
740void tst_QListWidget::item()
741{
742 QFETCH(int, row);
743 QFETCH(bool, outOfBounds);
744
745 (new QListWidgetItem(testWidget))->setText("item0");
746 (new QListWidgetItem(testWidget))->setText("item1");
747 (new QListWidgetItem(testWidget))->setText("item2");
748
749 QCOMPARE(testWidget->count(), 3);
750
751 QListWidgetItem *item = testWidget->item(row);
752 if (outOfBounds) {
753 QCOMPARE(item, nullptr);
754 QCOMPARE(testWidget->count(), 3);
755 } else {
756 QCOMPARE(item->text(), QStringLiteral("item") + QString::number(row));
757 QCOMPARE(testWidget->count(), 3);
758 }
759}
760
761void tst_QListWidget::takeItem_data()
762{
763 QTest::addColumn<int>(name: "row");
764 QTest::addColumn<bool>(name: "outOfBounds");
765
766 QTest::newRow(dataTag: "First item, row: 0") << 0 << false;
767 QTest::newRow(dataTag: "Middle item, row: 1") << 1 << false;
768 QTest::newRow(dataTag: "Last item, row: 2") << 2 << false;
769 QTest::newRow(dataTag: "Out of bounds, row: -1") << -1 << true;
770 QTest::newRow(dataTag: "Out of bounds, row: 3") << 3 << true;
771}
772
773void tst_QListWidget::takeItem()
774{
775 QFETCH(int, row);
776 QFETCH(bool, outOfBounds);
777
778 (new QListWidgetItem(testWidget))->setText("item0");
779 (new QListWidgetItem(testWidget))->setText("item1");
780 (new QListWidgetItem(testWidget))->setText("item2");
781
782 QCOMPARE(testWidget->count(), 3);
783
784 QListWidgetItem *item = testWidget->takeItem(row);
785 if (outOfBounds) {
786 QCOMPARE(item, nullptr);
787 QCOMPARE(testWidget->count(), 3);
788 } else {
789 QCOMPARE(item->text(), QStringLiteral("item") + QString::number(row));
790 QCOMPARE(testWidget->count(), 2);
791 }
792
793 delete item;
794}
795
796void tst_QListWidget::selectedItems_data()
797{
798 QTest::addColumn<int>(name: "itemCount");
799 QTest::addColumn<IntList>(name: "hiddenRows");
800 QTest::addColumn<IntList>(name: "selectedRows");
801 QTest::addColumn<IntList>(name: "expectedRows");
802
803
804 QTest::newRow(dataTag: "none hidden, none selected")
805 << 3
806 << IntList()
807 << IntList()
808 << IntList();
809
810 QTest::newRow(dataTag: "none hidden, all selected")
811 << 3
812 << IntList()
813 << (IntList() << 0 << 1 << 2)
814 << (IntList() << 0 << 1 << 2);
815
816 QTest::newRow(dataTag: "first hidden, all selected")
817 << 3
818 << (IntList() << 0)
819 << (IntList() << 0 << 1 << 2)
820 << (IntList() << 0 << 1 << 2);
821
822 QTest::newRow(dataTag: "last hidden, all selected")
823 << 3
824 << (IntList() << 2)
825 << (IntList() << 0 << 1 << 2)
826 << (IntList() << 0 << 1 << 2);
827
828 QTest::newRow(dataTag: "middle hidden, all selected")
829 << 3
830 << (IntList() << 1)
831 << (IntList() << 0 << 1 << 2)
832 << (IntList() << 0 << 1 << 2);
833
834 QTest::newRow(dataTag: "all hidden, all selected")
835 << 3
836 << (IntList() << 0 << 1 << 2)
837 << (IntList() << 0 << 1 << 2)
838 << (IntList() << 0 << 1 << 2);
839}
840
841void tst_QListWidget::selectedItems()
842{
843 QFETCH(int, itemCount);
844 QFETCH(const IntList, hiddenRows);
845 QFETCH(const IntList, selectedRows);
846 QFETCH(const IntList, expectedRows);
847
848 QCOMPARE(testWidget->count(), 0);
849
850 //insert items
851 for (int i = 0; i < itemCount; ++i)
852 new QListWidgetItem(QStringLiteral("Item") + QString::number(i), testWidget);
853
854 //test the selection
855 testWidget->setSelectionMode(QListWidget::SingleSelection);
856 for (int i = 0; i < itemCount; ++i) {
857 QListWidgetItem *item = testWidget->item(row: i);
858 item->setSelected(true);
859 QVERIFY(item->isSelected());
860 QCOMPARE(testWidget->selectedItems().count(), 1);
861 }
862 //let's clear the selection
863 testWidget->clearSelection();
864 //... and set the selection mode to allow more than 1 item to be selected
865 testWidget->setSelectionMode(QAbstractItemView::MultiSelection);
866
867 //verify items are inserted
868 QCOMPARE(testWidget->count(), itemCount);
869 // hide items
870 for (int row : hiddenRows)
871 testWidget->item(row)->setHidden(true);
872 // select items
873 for (int row : selectedRows)
874 testWidget->item(row)->setSelected(true);
875
876 // check that the correct number of items and the expected items are there
877 QList<QListWidgetItem *> selectedItems = testWidget->selectedItems();
878 QCOMPARE(selectedItems.count(), expectedRows.count());
879 for (int row : expectedRows)
880 QVERIFY(selectedItems.contains(testWidget->item(row)));
881
882 //check that isSelected agrees with selectedItems
883 for (int i = 0; i < itemCount; ++i) {
884 QListWidgetItem *item = testWidget->item(row: i);
885 if (item->isSelected())
886 QVERIFY(selectedItems.contains(item));
887 }
888}
889
890void tst_QListWidget::removeItems_data()
891{
892 QTest::addColumn<int>(name: "rowCount");
893 QTest::addColumn<int>(name: "removeRows");
894 QTest::addColumn<int>(name: "row");
895 QTest::addColumn<int>(name: "expectedRowCount");
896
897 QTest::newRow(dataTag: "Empty") << 0 << 1 << 0 << 0;
898 QTest::newRow(dataTag: "1:1") << 1 << 1 << 0 << 0;
899 QTest::newRow(dataTag: "3:1") << 3 << 1 << 0 << 2;
900 QTest::newRow(dataTag: "3:2") << 3 << 2 << 0 << 1;
901 QTest::newRow(dataTag: "100:10") << 100 << 10 << 0 << 90;
902}
903
904void tst_QListWidget::removeItems()
905{
906 QFETCH(int, rowCount);
907 QFETCH(int, removeRows);
908 QFETCH(int, row);
909 QFETCH(int, expectedRowCount);
910
911 //insert items
912 for (int r = 0; r < rowCount; ++r)
913 new QListWidgetItem(QString::number(r), testWidget);
914
915 // remove and compare the results
916 for (int r = 0; r < removeRows; ++r)
917 delete testWidget->item(row);
918 QCOMPARE(testWidget->count(), expectedRowCount);
919
920 // check if the correct items were removed
921 for (int r = 0; r < expectedRowCount; ++r)
922 if (r < row)
923 QCOMPARE(testWidget->item(r)->text(), QString::number(r));
924 else
925 QCOMPARE(testWidget->item(r)->text(), QString::number(r + removeRows));
926}
927
928void tst_QListWidget::moveItemsPriv_data()
929{
930 QTest::addColumn<int>(name: "rowCount");
931 QTest::addColumn<int>(name: "srcRow");
932 QTest::addColumn<int>(name: "dstRow");
933 QTest::addColumn<bool>(name: "shouldHaveSignaled");
934
935 QTest::newRow(dataTag: "Empty") << 0 << 0 << 0 << false;
936 QTest::newRow(dataTag: "Overflow src") << 5 << 5 << 2 << false;
937 QTest::newRow(dataTag: "Underflow src") << 5 << -1 << 2 << false;
938 QTest::newRow(dataTag: "Overflow dst") << 5 << 2 << 6 << false;
939 QTest::newRow(dataTag: "Underflow dst") << 5 << 2 << -1 << false;
940 QTest::newRow(dataTag: "Same place") << 5 << 2 << 2 << false;
941 QTest::newRow(dataTag: "Up") << 5 << 4 << 2 << true;
942 QTest::newRow(dataTag: "Down") << 5 << 2 << 4 << true;
943 QTest::newRow(dataTag: "QTBUG-6532 assert") << 5 << 0 << 1 << false;
944 QTest::newRow(dataTag: "QTBUG-6565 to the end") << 5 << 3 << 5 << true;
945 QTest::newRow(dataTag: "Same place 2") << 2 << 0 << 1 << false;
946 QTest::newRow(dataTag: "swap") << 2 << 0 << 2 << true;
947 QTest::newRow(dataTag: "swap2") << 4 << 1 << 3 << true;
948 QTest::newRow(dataTag: "swap3") << 4 << 3 << 2 << true;
949 QTest::newRow(dataTag: "swap4") << 2 << 1 << 0 << true;
950}
951
952void tst_QListWidget::moveItemsPriv()
953{
954 QFETCH(int, rowCount);
955 QFETCH(int, srcRow);
956 QFETCH(int, dstRow);
957 QFETCH(bool, shouldHaveSignaled);
958
959 for (int r = 0; r < rowCount; ++r)
960 new QListWidgetItem(QString::number(r), testWidget);
961
962 QListModel *model = qobject_cast<QListModel *>(object: testWidget->model());
963 QVERIFY(model);
964 QSignalSpy beginMoveSpy(model, &QAbstractItemModel::rowsAboutToBeMoved);
965 QSignalSpy movedSpy(model, &QAbstractItemModel::rowsMoved);
966 model->move(srcRow, dstRow);
967
968 if (shouldHaveSignaled) {
969 if (srcRow < dstRow)
970 QCOMPARE(testWidget->item(dstRow - 1)->text(), QString::number(srcRow));
971 else
972 QCOMPARE(testWidget->item(dstRow)->text(), QString::number(srcRow));
973
974 QCOMPARE(beginMoveSpy.count(), 1);
975 const QList<QVariant> &beginMoveArgs = beginMoveSpy.takeFirst();
976 QCOMPARE(beginMoveArgs.at(1).toInt(), srcRow);
977 QCOMPARE(beginMoveArgs.at(2).toInt(), srcRow);
978 QCOMPARE(beginMoveArgs.at(4).toInt(), dstRow);
979
980 QCOMPARE(movedSpy.count(), 1);
981 const QList<QVariant> &movedArgs = movedSpy.takeFirst();
982 QCOMPARE(movedArgs.at(1).toInt(), srcRow);
983 QCOMPARE(movedArgs.at(2).toInt(), srcRow);
984 QCOMPARE(movedArgs.at(4).toInt(), dstRow);
985 } else {
986 QCOMPARE(beginMoveSpy.count(), 0);
987 QCOMPARE(movedSpy.count(), 0);
988 }
989}
990
991void tst_QListWidget::itemStreaming_data()
992{
993 QTest::addColumn<QString>(name: "text");
994 QTest::addColumn<QString>(name: "toolTip");
995
996 QTest::newRow(dataTag: "Data") << "item text" << "tool tip text";
997}
998
999void tst_QListWidget::itemStreaming()
1000{
1001 QFETCH(QString, text);
1002 QFETCH(QString, toolTip);
1003
1004 QListWidgetItem item;
1005 QCOMPARE(item.text(), QString());
1006 QCOMPARE(item.toolTip(), QString());
1007
1008 item.setText(text);
1009 item.setToolTip(toolTip);
1010 QCOMPARE(item.text(), text);
1011 QCOMPARE(item.toolTip(), toolTip);
1012
1013 QByteArray buffer;
1014 QDataStream out(&buffer, QIODevice::WriteOnly);
1015 out << item;
1016
1017 QListWidgetItem item2;
1018 QCOMPARE(item2.text(), QString());
1019 QCOMPARE(item2.toolTip(), QString());
1020
1021 QVERIFY(!buffer.isEmpty());
1022
1023 QDataStream in(&buffer, QIODevice::ReadOnly);
1024 in >> item2;
1025 QCOMPARE(item2.text(), text);
1026 QCOMPARE(item2.toolTip(), toolTip);
1027}
1028
1029void tst_QListWidget::sortItems_data()
1030{
1031 QTest::addColumn<Qt::SortOrder>(name: "order");
1032 QTest::addColumn<QVariantList>(name: "initialList");
1033 QTest::addColumn<QVariantList>(name: "expectedList");
1034 QTest::addColumn<IntList>(name: "expectedRows");
1035
1036 QTest::newRow(dataTag: "ascending strings")
1037 << Qt::AscendingOrder
1038 << (QVariantList() << QString("c") << QString("d") << QString("a") << QString("b"))
1039 << (QVariantList() << QString("a") << QString("b") << QString("c") << QString("d"))
1040 << (IntList() << 2 << 3 << 0 << 1);
1041
1042 QTest::newRow(dataTag: "descending strings")
1043 << Qt::DescendingOrder
1044 << (QVariantList() << QString("c") << QString("d") << QString("a") << QString("b"))
1045 << (QVariantList() << QString("d") << QString("c") << QString("b") << QString("a"))
1046 << (IntList() << 1 << 0 << 3 << 2);
1047
1048 QTest::newRow(dataTag: "ascending numbers")
1049 << Qt::AscendingOrder
1050 << (QVariantList() << 1 << 11 << 2 << 22)
1051 << (QVariantList() << 1 << 2 << 11 << 22)
1052 << (IntList() << 0 << 2 << 1 << 3);
1053
1054 QTest::newRow(dataTag: "descending numbers")
1055 << Qt::DescendingOrder
1056 << (QVariantList() << 1 << 11 << 2 << 22)
1057 << (QVariantList() << 22 << 11 << 2 << 1)
1058 << (IntList() << 3 << 1 << 2 << 0);
1059}
1060
1061void tst_QListWidget::sortItems()
1062{
1063 QFETCH(Qt::SortOrder, order);
1064 QFETCH(const QVariantList, initialList);
1065 QFETCH(const QVariantList, expectedList);
1066 QFETCH(const IntList, expectedRows);
1067
1068 for (const QVariant &data : initialList) {
1069 QListWidgetItem *item = new QListWidgetItem(testWidget);
1070 item->setData(role: Qt::DisplayRole, value: data);
1071 }
1072
1073 QAbstractItemModel *model = testWidget->model();
1074 QVector<QPersistentModelIndex> persistent;
1075 for (int j = 0; j < model->rowCount(parent: QModelIndex()); ++j)
1076 persistent << model->index(row: j, column: 0, parent: QModelIndex());
1077
1078 testWidget->sortItems(order);
1079
1080 QCOMPARE(testWidget->count(), expectedList.count());
1081 for (int i = 0; i < testWidget->count(); ++i)
1082 QCOMPARE(testWidget->item(i)->text(), expectedList.at(i).toString());
1083
1084 for (int k = 0; k < testWidget->count(); ++k)
1085 QCOMPARE(persistent.at(k).row(), expectedRows.at(k));
1086}
1087
1088void tst_QListWidget::sortHiddenItems_data()
1089{
1090 QTest::addColumn<Qt::SortOrder>(name: "order");
1091 QTest::addColumn<QStringList>(name: "initialList");
1092 QTest::addColumn<QStringList>(name: "expectedList");
1093 QTest::addColumn<IntList>(name: "expectedRows");
1094 QTest::addColumn<IntList>(name: "expectedVisibility");
1095
1096 QStringList initial, expected;
1097 IntList rowOrder;
1098 IntList visible;
1099 for (int i = 0; i < 20; ++i) {
1100 initial << QString(QChar(0x41 + i));
1101 expected << QString(QChar(0x54 - i));
1102 rowOrder << 19 - i;
1103 visible << (i % 2);
1104
1105 }
1106 QTest::newRow(dataTag: "descending order, 20 items")
1107 << Qt::DescendingOrder
1108 << initial
1109 << expected
1110 << rowOrder
1111 << visible;
1112
1113 QTest::newRow(dataTag: "ascending order")
1114 << Qt::AscendingOrder
1115 << (QStringList() << "c" << "d" << "a" << "b")
1116 << (QStringList() << "a" << "b" << "c" << "d")
1117 << (IntList() << 2 << 3 << 0 << 1)
1118 << (IntList() << 1 << 0 << 1 << 0);
1119
1120 QTest::newRow(dataTag: "descending order")
1121 << Qt::DescendingOrder
1122 << (QStringList() << "c" << "d" << "a" << "b")
1123 << (QStringList() << "d" << "c" << "b" << "a")
1124 << (IntList() << 1 << 0 << 3 << 2)
1125 << (IntList() << 0 << 1 << 0 << 1);
1126}
1127
1128void tst_QListWidget::sortHiddenItems()
1129{
1130 QFETCH(Qt::SortOrder, order);
1131 QFETCH(QStringList, initialList);
1132 QFETCH(QStringList, expectedList);
1133 QFETCH(IntList, expectedRows);
1134 QFETCH(IntList, expectedVisibility);
1135
1136 // init() won't clear hidden items...
1137 QListWidget *tw = new QListWidget();
1138 tw->addItems(labels: initialList);
1139
1140 QAbstractItemModel *model = tw->model();
1141 QVector<QPersistentModelIndex> persistent;
1142 for (int j = 0; j < model->rowCount(parent: QModelIndex()); ++j) {
1143 persistent << model->index(row: j, column: 0, parent: QModelIndex());
1144 tw->setRowHidden(row: j, hide: j & 1); // every odd is hidden
1145 }
1146
1147 tw->setSortingEnabled(true);
1148 tw->sortItems(order);
1149
1150 QCOMPARE(tw->count(), expectedList.count());
1151 for (int i = 0; i < tw->count(); ++i) {
1152 QCOMPARE(tw->item(i)->text(), expectedList.at(i));
1153 QCOMPARE(tw->item(i)->isHidden(), !expectedVisibility.at(i));
1154 }
1155
1156 for (int k = 0; k < tw->count(); ++k)
1157 QCOMPARE(persistent.at(k).row(), expectedRows.at(k));
1158
1159 delete tw;
1160}
1161
1162class TestListWidget : public QListWidget
1163{
1164 Q_OBJECT
1165public:
1166 using QListWidget::QListWidget;
1167 using QListWidget::state;
1168 using QListWidget::closeEditor;
1169 using QListWidget::mimeData;
1170 using QListWidget::indexFromItem;
1171
1172 bool isEditingState() const {
1173 return QListWidget::state() == QListWidget::EditingState;
1174 }
1175};
1176
1177void tst_QListWidget::closeEditor()
1178{
1179 TestListWidget w;
1180 w.addItems(labels: {"a", "b", "c", "d"});
1181 QListWidgetItem *item = w.item(row: 0);
1182 item->setFlags(item->flags() | Qt::ItemIsEditable);
1183 QVERIFY(item);
1184 w.editItem(item);
1185
1186 QVERIFY(w.isEditingState());
1187
1188 w.reset();
1189
1190 QVERIFY(!w.isEditingState());
1191}
1192
1193void tst_QListWidget::setData_data()
1194{
1195 QTest::addColumn<QStringList>(name: "initialItems");
1196 QTest::addColumn<int>(name: "itemIndex");
1197 QTest::addColumn<IntList>(name: "roles");
1198 QTest::addColumn<QVariantList>(name: "values");
1199 QTest::addColumn<int>(name: "expectedSignalCount");
1200
1201 QStringList initialItems;
1202 IntList roles;
1203 QVariantList values;
1204
1205 {
1206 initialItems.clear(); roles.clear(); values.clear();
1207 initialItems << "foo";
1208 roles << Qt::DisplayRole;
1209 values << "xxx";
1210 QTest::newRow(dataTag: "changing a role should emit")
1211 << initialItems << 0 << roles << values << 1;
1212 }
1213 {
1214 initialItems.clear(); roles.clear(); values.clear();
1215 initialItems << "foo";
1216 roles << Qt::DisplayRole;
1217 values << "foo";
1218 QTest::newRow(dataTag: "setting the same value should not emit")
1219 << initialItems << 0 << roles << values << 0;
1220 }
1221 {
1222 initialItems.clear(); roles.clear(); values.clear();
1223 initialItems << "foo";
1224 roles << Qt::DisplayRole << Qt::DisplayRole;
1225 values << "bar" << "bar";
1226 QTest::newRow(dataTag: "setting the same value twice should only emit once")
1227 << initialItems << 0 << roles << values << 1;
1228 }
1229 {
1230 initialItems.clear(); roles.clear(); values.clear();
1231 initialItems << "foo";
1232 roles << Qt::DisplayRole << Qt::ToolTipRole << Qt::WhatsThisRole;
1233 values << "bar" << "bartooltip" << "barwhatsthis";
1234 QTest::newRow(dataTag: "changing three roles should emit three times")
1235 << initialItems << 0 << roles << values << 3;
1236 }
1237}
1238
1239void tst_QListWidget::setData()
1240{
1241 QFETCH(QStringList, initialItems);
1242 QFETCH(int, itemIndex);
1243 QFETCH(IntList, roles);
1244 QFETCH(QVariantList, values);
1245 QFETCH(int, expectedSignalCount);
1246
1247 QCOMPARE(roles.count(), values.count());
1248
1249 for (int manipulateModel = 0; manipulateModel < 2; ++manipulateModel) {
1250 testWidget->clear();
1251 testWidget->insertItems(row: 0, labels: initialItems);
1252 QCOMPARE(testWidget->count(), initialItems.count());
1253
1254 QSignalSpy itemChanged(testWidget, &QListWidget::itemChanged);
1255 QSignalSpy dataChanged(testWidget->model(), &QAbstractItemModel::dataChanged);
1256
1257 for (int i = 0; i < roles.count(); ++i) {
1258 if (manipulateModel)
1259 testWidget->model()->setData(
1260 index: testWidget->model()->index(row: itemIndex, column: 0, parent: testWidget->rootIndex()),
1261 value: values.at(i),
1262 role: roles.at(i));
1263 else
1264 testWidget->item(row: itemIndex)->setData(role: roles.at(i), value: values.at(i));
1265 }
1266
1267 // make sure the data is actually set
1268 for (int i = 0; i < roles.count(); ++i)
1269 QCOMPARE(testWidget->item(itemIndex)->data(roles.at(i)), values.at(i));
1270
1271 // make sure we get the right number of emits
1272 QCOMPARE(itemChanged.count(), expectedSignalCount);
1273 QCOMPARE(dataChanged.count(), expectedSignalCount);
1274 }
1275}
1276
1277void tst_QListWidget::insertItemsWithSorting_data()
1278{
1279 QTest::addColumn<Qt::SortOrder>(name: "sortOrder");
1280 QTest::addColumn<QStringList>(name: "initialItems");
1281 QTest::addColumn<QStringList>(name: "insertItems");
1282 QTest::addColumn<QStringList>(name: "expectedItems");
1283 QTest::addColumn<IntList>(name: "expectedRows");
1284
1285 QTest::newRow(dataTag: "() + (a) = (a)")
1286 << Qt::AscendingOrder
1287 << QStringList()
1288 << (QStringList() << "a")
1289 << (QStringList() << "a")
1290 << IntList();
1291 QTest::newRow(dataTag: "() + (c, b, a) = (a, b, c)")
1292 << Qt::AscendingOrder
1293 << QStringList()
1294 << (QStringList() << "c" << "b" << "a")
1295 << (QStringList() << "a" << "b" << "c")
1296 << IntList();
1297 QTest::newRow(dataTag: "() + (a, b, c) = (c, b, a)")
1298 << Qt::DescendingOrder
1299 << QStringList()
1300 << (QStringList() << "a" << "b" << "c")
1301 << (QStringList() << "c" << "b" << "a")
1302 << IntList();
1303 QTest::newRow(dataTag: "(a) + (b) = (a, b)")
1304 << Qt::AscendingOrder
1305 << QStringList("a")
1306 << (QStringList() << "b")
1307 << (QStringList() << "a" << "b")
1308 << (IntList() << 0);
1309 QTest::newRow(dataTag: "(a) + (b) = (b, a)")
1310 << Qt::DescendingOrder
1311 << QStringList("a")
1312 << (QStringList() << "b")
1313 << (QStringList() << "b" << "a")
1314 << (IntList() << 1);
1315 QTest::newRow(dataTag: "(a, c, b) + (d) = (a, b, c, d)")
1316 << Qt::AscendingOrder
1317 << (QStringList() << "a" << "c" << "b")
1318 << (QStringList() << "d")
1319 << (QStringList() << "a" << "b" << "c" << "d")
1320 << (IntList() << 0 << 1 << 2);
1321 QTest::newRow(dataTag: "(b, c, a) + (d) = (d, c, b, a)")
1322 << Qt::DescendingOrder
1323 << (QStringList() << "b" << "c" << "a")
1324 << (QStringList() << "d")
1325 << (QStringList() << "d" << "c" << "b" << "a")
1326 << (IntList() << 1 << 2 << 3);
1327 {
1328 IntList ascendingRows;
1329 IntList reverseRows;
1330 QStringList ascendingItems;
1331 QStringList reverseItems;
1332 for (char i = 'a'; i <= 'z'; ++i) {
1333 ascendingItems << QString(1, QLatin1Char(i));
1334 reverseItems << QString(1, QLatin1Char('z' - i + 'a'));
1335 ascendingRows << i - 'a';
1336 reverseRows << 'z' - i + 'a';
1337 }
1338 QTest::newRow(dataTag: "() + (sorted items) = (sorted items)")
1339 << Qt::AscendingOrder
1340 << QStringList()
1341 << ascendingItems
1342 << ascendingItems
1343 << IntList();
1344 QTest::newRow(dataTag: "(sorted items) + () = (sorted items)")
1345 << Qt::AscendingOrder
1346 << ascendingItems
1347 << QStringList()
1348 << ascendingItems
1349 << ascendingRows;
1350 QTest::newRow(dataTag: "() + (ascending items) = (reverse items)")
1351 << Qt::DescendingOrder
1352 << QStringList()
1353 << ascendingItems
1354 << reverseItems
1355 << IntList();
1356 QTest::newRow(dataTag: "(reverse items) + () = (ascending items)")
1357 << Qt::AscendingOrder
1358 << reverseItems
1359 << QStringList()
1360 << ascendingItems
1361 << ascendingRows;
1362 QTest::newRow(dataTag: "(reverse items) + () = (reverse items)")
1363 << Qt::DescendingOrder
1364 << reverseItems
1365 << QStringList()
1366 << reverseItems
1367 << ascendingRows;
1368 }
1369}
1370
1371void tst_QListWidget::insertItemsWithSorting()
1372{
1373 QFETCH(Qt::SortOrder, sortOrder);
1374 QFETCH(const QStringList, initialItems);
1375 QFETCH(const QStringList, insertItems);
1376 QFETCH(const QStringList, expectedItems);
1377 QFETCH(const IntList, expectedRows);
1378
1379 for (int method = 0; method < 5; ++method) {
1380 QListWidget w;
1381 w.setSortingEnabled(true);
1382 w.sortItems(order: sortOrder);
1383 w.addItems(labels: initialItems);
1384
1385 QAbstractItemModel *model = w.model();
1386 QList<QPersistentModelIndex> persistent;
1387 for (int j = 0; j < model->rowCount(parent: QModelIndex()); ++j)
1388 persistent << model->index(row: j, column: 0, parent: QModelIndex());
1389
1390 switch (method) {
1391 case 0:
1392 // insert using item constructor
1393 for (const QString &str : insertItems)
1394 new QListWidgetItem(str, &w);
1395 break;
1396 case 1:
1397 // insert using insertItems()
1398 w.insertItems(row: 0, labels: insertItems);
1399 break;
1400 case 2:
1401 // insert using insertItem()
1402 for (const QString &str : insertItems)
1403 w.insertItem(row: 0, label: str);
1404 break;
1405 case 3:
1406 // insert using addItems()
1407 w.addItems(labels: insertItems);
1408 break;
1409 case 4:
1410 // insert using addItem()
1411 for (const QString &str : insertItems)
1412 w.addItem(label: str);
1413 break;
1414 }
1415 QCOMPARE(w.count(), expectedItems.count());
1416 for (int i = 0; i < w.count(); ++i)
1417 QCOMPARE(w.item(i)->text(), expectedItems.at(i));
1418
1419 for (int k = 0; k < persistent.count(); ++k)
1420 QCOMPARE(persistent.at(k).row(), expectedRows.at(k));
1421 }
1422}
1423
1424void tst_QListWidget::changeDataWithSorting_data()
1425{
1426 QTest::addColumn<Qt::SortOrder>(name: "sortOrder");
1427 QTest::addColumn<QStringList>(name: "initialItems");
1428 QTest::addColumn<int>(name: "itemIndex");
1429 QTest::addColumn<QString>(name: "newValue");
1430 QTest::addColumn<QStringList>(name: "expectedItems");
1431 QTest::addColumn<IntList>(name: "expectedRows");
1432 QTest::addColumn<bool>(name: "reorderingExpected");
1433
1434 QTest::newRow(dataTag: "change a to b in (a)")
1435 << Qt::AscendingOrder
1436 << (QStringList() << "a")
1437 << 0 << "b"
1438 << (QStringList() << "b")
1439 << (IntList() << 0)
1440 << false;
1441 QTest::newRow(dataTag: "change a to b in (a, c)")
1442 << Qt::AscendingOrder
1443 << (QStringList() << "a" << "c")
1444 << 0 << "b"
1445 << (QStringList() << "b" << "c")
1446 << (IntList() << 0 << 1)
1447 << false;
1448 QTest::newRow(dataTag: "change a to c in (a, b)")
1449 << Qt::AscendingOrder
1450 << (QStringList() << "a" << "b")
1451 << 0 << "c"
1452 << (QStringList() << "b" << "c")
1453 << (IntList() << 1 << 0)
1454 << true;
1455 QTest::newRow(dataTag: "change c to a in (c, b)")
1456 << Qt::DescendingOrder
1457 << (QStringList() << "c" << "b")
1458 << 0 << "a"
1459 << (QStringList() << "b" << "a")
1460 << (IntList() << 1 << 0)
1461 << true;
1462 QTest::newRow(dataTag: "change e to i in (a, c, e, g)")
1463 << Qt::AscendingOrder
1464 << (QStringList() << "a" << "c" << "e" << "g")
1465 << 2 << "i"
1466 << (QStringList() << "a" << "c" << "g" << "i")
1467 << (IntList() << 0 << 1 << 3 << 2)
1468 << true;
1469 QTest::newRow(dataTag: "change e to a in (c, e, g, i)")
1470 << Qt::AscendingOrder
1471 << (QStringList() << "c" << "e" << "g" << "i")
1472 << 1 << "a"
1473 << (QStringList() << "a" << "c" << "g" << "i")
1474 << (IntList() << 1 << 0 << 2 << 3)
1475 << true;
1476 QTest::newRow(dataTag: "change e to f in (c, e, g, i)")
1477 << Qt::AscendingOrder
1478 << (QStringList() << "c" << "e" << "g" << "i")
1479 << 1 << "f"
1480 << (QStringList() << "c" << "f" << "g" << "i")
1481 << (IntList() << 0 << 1 << 2 << 3)
1482 << false;
1483}
1484
1485class QListWidgetDataChanged : public QListWidget
1486{
1487 Q_OBJECT
1488public:
1489 using QListWidget::QListWidget;
1490
1491 void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles) override
1492 {
1493 QListWidget::dataChanged(topLeft, bottomRight, roles);
1494 currentRoles = roles;
1495 }
1496 QVector<int> currentRoles;
1497};
1498
1499void tst_QListWidget::itemData()
1500{
1501 QListWidgetDataChanged widget;
1502 QListWidgetItem item(&widget);
1503 item.setFlags(item.flags() | Qt::ItemIsEditable);
1504 item.setData(role: Qt::DisplayRole, value: QString("0"));
1505 QCOMPARE(widget.currentRoles, QVector<int>({Qt::DisplayRole, Qt::EditRole}));
1506 item.setData(role: Qt::CheckStateRole, value: Qt::PartiallyChecked);
1507 QCOMPARE(widget.currentRoles, QVector<int>{Qt::CheckStateRole});
1508 for (int i = 0; i < 4; ++i)
1509 {
1510 item.setData(role: Qt::UserRole + i, value: QString::number(i + 1));
1511 QCOMPARE(widget.currentRoles, QVector<int>{Qt::UserRole + i});
1512 }
1513 QMap<int, QVariant> flags = widget.model()->itemData(index: widget.model()->index(row: 0, column: 0));
1514 QCOMPARE(flags.count(), 6);
1515 for (int i = 0; i < 4; ++i)
1516 QCOMPARE(flags[Qt::UserRole + i].toString(), QString::number(i + 1));
1517
1518 item.setBackground(QBrush(Qt::red));
1519 item.setForeground(QBrush(Qt::red));
1520 item.setSizeHint(QSize(10, 10));
1521 QCOMPARE(item.data(Qt::BackgroundRole), QVariant(QBrush(Qt::red)));
1522 QCOMPARE(item.data(Qt::ForegroundRole), QVariant(QBrush(Qt::red)));
1523 QCOMPARE(item.data(Qt::SizeHintRole), QVariant(QSize(10, 10)));
1524 // an empty brush should result in a QVariant()
1525 item.setBackground(QBrush());
1526 item.setForeground(QBrush());
1527 item.setSizeHint(QSize());
1528 QCOMPARE(item.data(Qt::BackgroundRole), QVariant());
1529 QCOMPARE(item.data(Qt::ForegroundRole), QVariant());
1530 QCOMPARE(item.data(Qt::SizeHintRole), QVariant());
1531}
1532
1533void tst_QListWidget::changeDataWithSorting()
1534{
1535 QFETCH(Qt::SortOrder, sortOrder);
1536 QFETCH(QStringList, initialItems);
1537 QFETCH(int, itemIndex);
1538 QFETCH(QString, newValue);
1539 QFETCH(QStringList, expectedItems);
1540 QFETCH(IntList, expectedRows);
1541 QFETCH(bool, reorderingExpected);
1542
1543 QListWidget w;
1544 w.setSortingEnabled(true);
1545 w.sortItems(order: sortOrder);
1546 w.addItems(labels: initialItems);
1547
1548 QAbstractItemModel *model = w.model();
1549 QVector<QPersistentModelIndex> persistent;
1550 for (int j = 0; j < model->rowCount(parent: QModelIndex()); ++j)
1551 persistent << model->index(row: j, column: 0, parent: QModelIndex());
1552
1553 QSignalSpy dataChangedSpy(model, &QAbstractItemModel::dataChanged);
1554 QSignalSpy layoutChangedSpy(model, &QAbstractItemModel::layoutChanged);
1555
1556 QListWidgetItem *item = w.item(row: itemIndex);
1557 item->setText(newValue);
1558 for (int i = 0; i < expectedItems.count(); ++i) {
1559 QCOMPARE(w.item(i)->text(), expectedItems.at(i));
1560 for (int j = 0; j < persistent.count(); ++j) {
1561 if (persistent.at(i: j).row() == i) // the same toplevel row
1562 QCOMPARE(persistent.at(j).internalPointer(), static_cast<void *>(w.item(i)));
1563 }
1564 }
1565
1566 for (int k = 0; k < persistent.count(); ++k)
1567 QCOMPARE(persistent.at(k).row(), expectedRows.at(k));
1568
1569 QCOMPARE(dataChangedSpy.count(), 1);
1570 QCOMPARE(layoutChangedSpy.count(), reorderingExpected ? 1 : 0);
1571}
1572
1573void tst_QListWidget::itemWidget()
1574{
1575 QListWidget list;
1576 QWidget widget;
1577
1578 QListWidgetItem *item = new QListWidgetItem(&list);
1579
1580
1581 QCOMPARE(list.itemWidget(item), nullptr);
1582 list.setItemWidget(item, widget: &widget);
1583 QCOMPARE(list.itemWidget(item), &widget);
1584 list.removeItemWidget(aItem: item);
1585 QCOMPARE(list.itemWidget(item), nullptr);
1586}
1587
1588#ifndef Q_OS_MAC
1589class MyListWidget : public QListWidget
1590{
1591 Q_OBJECT
1592public:
1593 using QListWidget::QListWidget;
1594
1595 void paintEvent(QPaintEvent *e) override
1596 {
1597 painted += e->region();
1598 QListWidget::paintEvent(e);
1599 }
1600
1601 QRegion painted;
1602};
1603
1604void tst_QListWidget::fastScroll()
1605{
1606 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1607 QSKIP("Wayland: This fails. Figure out why.");
1608
1609 QWidget topLevel;
1610 MyListWidget widget(&topLevel);
1611 for (int i = 0; i < 50; ++i)
1612 widget.addItem(QStringLiteral("Item ") + QString::number(i));
1613
1614 topLevel.resize(w: 300, h: 300); // toplevel needs to be wide enough for the item
1615 topLevel.show();
1616
1617 // Force the mouse cursor off the widget as it causes item it is over to highlight,
1618 // which causes unexpected paint region.
1619 QTest::mouseMove(widget: &widget, pos: QPoint(-10, -10));
1620
1621 // Make sure the widget gets the first full repaint. On
1622 // some WMs, we'll get two (first inactive exposure, then
1623 // active exposure.
1624 QVERIFY(QTest::qWaitForWindowActive(&topLevel));
1625
1626 QSize itemSize = widget.visualItemRect(item: widget.item(row: 0)).size();
1627 QVERIFY(!itemSize.isEmpty());
1628
1629 QScrollBar *sbar = widget.verticalScrollBar();
1630 widget.setVerticalScrollMode(QAbstractItemView::ScrollPerItem);
1631 widget.painted = QRegion();
1632 sbar->setValue(sbar->value() + sbar->singleStep());
1633 QApplication::processEvents();
1634
1635 const QSize actualItemSize = widget.painted.boundingRect().size();
1636 if (actualItemSize != itemSize)
1637 QEXPECT_FAIL("", "QTBUG-21098", Continue);
1638
1639 // only one item should be repainted, the rest should be scrolled in memory
1640 QCOMPARE(actualItemSize, itemSize);
1641}
1642#endif // Q_OS_MAC
1643
1644void tst_QListWidget::insertUnchanged()
1645{
1646 QListWidget w;
1647 QSignalSpy itemChangedSpy(&w, &QListWidget::itemChanged);
1648 QListWidgetItem item("foo", &w);
1649 QCOMPARE(itemChangedSpy.count(), 0);
1650}
1651
1652void tst_QListWidget::setSortingEnabled()
1653{
1654 QListWidget w;
1655 QListWidgetItem *item1 = new QListWidgetItem(&w);
1656 QListWidgetItem *item2 = new QListWidgetItem(&w);
1657
1658 w.setSortingEnabled(true);
1659 QCOMPARE(w.isSortingEnabled(), true);
1660 QCOMPARE(w.item(0), item1);
1661 QCOMPARE(w.item(1), item2);
1662}
1663
1664void tst_QListWidget::task199503_crashWhenCleared()
1665{
1666 //we test here for a crash that would occur if you clear the items in the currentItemChanged signal
1667 QListWidget w;
1668 w.addItems(labels: {"item1", "item2", "item3"});
1669 w.setCurrentRow(0);
1670 w.connect(sender: &w, signal: &QListWidget::currentItemChanged, receiver: &w, slot: &QListWidget::clear);
1671 w.setCurrentRow(1);
1672}
1673
1674void tst_QListWidget::task217070_scrollbarsAdjusted()
1675{
1676 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1677 QSKIP("Wayland: This fails. Figure out why.");
1678
1679 //This task was mailing for style using SH_ScrollView_FrameOnlyAroundContents such as QMotifStyle
1680 QListWidget v;
1681 for (int i = 0; i < 200;i++)
1682 v.addItem(label: QString::number(i));
1683 v.show();
1684 v.setViewMode(QListView::IconMode);
1685 v.setResizeMode(QListView::Adjust);
1686 v.setUniformItemSizes(true);
1687 v.resize(w: 160, h: 100);
1688 QVERIFY(QTest::qWaitForWindowActive(&v));
1689 QScrollBar *hbar = v.horizontalScrollBar();
1690 QScrollBar *vbar = v.verticalScrollBar();
1691 QVERIFY(hbar && vbar);
1692 const auto style = vbar->style();
1693 for (int f = 150; f > 90 ; f--) {
1694 v.resize(w: f, h: 100);
1695 QTRY_VERIFY(style->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, vbar) ||
1696 vbar->isVisible());
1697 //the horizontal scrollbar must not be visible.
1698 QVERIFY(!hbar->isVisible());
1699 }
1700}
1701
1702void tst_QListWidget::task258949_keypressHangup()
1703{
1704 QListWidget lw;
1705 for (int y = 0; y < 5; y++) {
1706 QListWidgetItem *lwi = new QListWidgetItem(&lw);
1707 lwi->setText(y ? "1" : "0");
1708 if (y)
1709 lwi->setFlags(Qt::ItemIsSelectable);
1710 }
1711
1712 lw.show();
1713 lw.setCurrentIndex(lw.model()->index(row: 0, column: 0));
1714 QCOMPARE(lw.currentIndex(), lw.model()->index(0, 0));
1715 QTest::qWait(ms: 30);
1716 QTest::keyPress(widget: &lw, key: '1'); //this used to freeze
1717 QTRY_COMPARE(lw.currentIndex(), lw.model()->index(0, 0));
1718}
1719
1720void tst_QListWidget::QTBUG8086_currentItemChangedOnClick()
1721{
1722 QWidget win;
1723 QHBoxLayout layout(&win);
1724 QListWidget list;
1725 for (int i = 0 ; i < 4; ++i)
1726 new QListWidgetItem(QString::number(i), &list);
1727
1728 layout.addWidget(&list);
1729
1730 QLineEdit edit;
1731 layout.addWidget(&edit);
1732
1733 edit.setFocus();
1734 win.show();
1735
1736 QSignalSpy spy(&list, &QListWidget::currentItemChanged);
1737
1738 QVERIFY(QTest::qWaitForWindowExposed(&win));
1739
1740 QCOMPARE(spy.count(), 0);
1741
1742 QTest::mouseClick(widget: list.viewport(), button: Qt::LeftButton, stateKey: {},
1743 pos: list.visualItemRect(item: list.item(row: 2)).center());
1744
1745 QCOMPARE(spy.count(), 1);
1746}
1747
1748
1749class ItemDelegate : public QStyledItemDelegate
1750{
1751 Q_OBJECT
1752public:
1753 using QStyledItemDelegate::QStyledItemDelegate;
1754 QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &,
1755 const QModelIndex &) const override
1756 {
1757 QLineEdit *lineEdit = new QLineEdit(parent);
1758 lineEdit->setFrame(false);
1759 QCompleter *completer = new QCompleter(QStringList() << "completer", lineEdit);
1760 completer->setCompletionMode(QCompleter::InlineCompletion);
1761 lineEdit->setCompleter(completer);
1762 return lineEdit;
1763 }
1764};
1765
1766void tst_QListWidget::QTBUG14363_completerWithAnyKeyPressedEditTriggers()
1767{
1768 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1769 QSKIP("Wayland: This fails. Figure out why.");
1770
1771 QListWidget listWidget;
1772 listWidget.setEditTriggers(QAbstractItemView::AnyKeyPressed);
1773 listWidget.setItemDelegate(new ItemDelegate(&listWidget));
1774 QListWidgetItem *item = new QListWidgetItem(QLatin1String("select an item (don't start editing)"), &listWidget);
1775 item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable|Qt::ItemIsEditable);
1776 new QListWidgetItem(QLatin1String("try to type the letter 'c'"), &listWidget);
1777 new QListWidgetItem(QLatin1String("completer"), &listWidget);
1778 listWidget.show();
1779 listWidget.setCurrentItem(item);
1780 QApplication::setActiveWindow(&listWidget);
1781 QVERIFY(QTest::qWaitForWindowActive(&listWidget));
1782 listWidget.setFocus();
1783 QCOMPARE(QApplication::focusWidget(), &listWidget);
1784
1785 QTest::keyClick(widget: listWidget.viewport(), key: Qt::Key_C);
1786
1787 QLineEdit *le = qobject_cast<QLineEdit*>(object: listWidget.itemWidget(item));
1788 QVERIFY(le);
1789 QCOMPARE(le->text(), QString("completer"));
1790 QCOMPARE(le->completer()->currentCompletion(), QString("completer"));
1791}
1792
1793void tst_QListWidget::mimeData()
1794{
1795 TestListWidget list;
1796
1797 for (int x = 0; x < 10; ++x)
1798 list.addItem(aitem: new QListWidgetItem(QStringLiteral("123")));
1799
1800 const QList<QListWidgetItem *> tableWidgetItemList{list.item(row: 1)};
1801 const QModelIndexList modelIndexList{list.indexFromItem(item: list.item(row: 1))};
1802
1803 // do these checks more than once to ensure that the "cached indexes" work as expected
1804 QMimeData *data;
1805 for (int i = 0; i < 2; ++i) {
1806 QVERIFY(!list.mimeData({}));
1807 QVERIFY(!list.model()->mimeData({}));
1808
1809 QVERIFY((data = list.mimeData(tableWidgetItemList)));
1810 delete data;
1811
1812 QVERIFY((data = list.model()->mimeData(modelIndexList)));
1813 delete data;
1814 }
1815
1816 // check the saved data is actually the same
1817 QMimeData *data2;
1818 data = list.mimeData(items: tableWidgetItemList);
1819 data2 = list.model()->mimeData(indexes: modelIndexList);
1820
1821 const QString format = QStringLiteral("application/x-qabstractitemmodeldatalist");
1822
1823 QVERIFY(data->hasFormat(format));
1824 QVERIFY(data2->hasFormat(format));
1825 QCOMPARE(data->data(format), data2->data(format));
1826
1827 delete data;
1828 delete data2;
1829}
1830
1831void tst_QListWidget::QTBUG50891_ensureSelectionModelSignalConnectionsAreSet()
1832{
1833 QListWidget list;
1834 for (int i = 0 ; i < 4; ++i)
1835 new QListWidgetItem(QString::number(i), &list);
1836
1837 list.setSelectionModel(new QItemSelectionModel(list.model()));
1838 list.show();
1839 QVERIFY(QTest::qWaitForWindowExposed(&list));
1840
1841 QSignalSpy currentItemChangedSpy(&list, &QListWidget::currentItemChanged);
1842 QSignalSpy itemSelectionChangedSpy(&list, &QListWidget::itemSelectionChanged);
1843
1844 QCOMPARE(currentItemChangedSpy.count(), 0);
1845 QCOMPARE(itemSelectionChangedSpy.count(), 0);
1846
1847 QTest::mouseClick(widget: list.viewport(), button: Qt::LeftButton, stateKey: {},
1848 pos: list.visualItemRect(item: list.item(row: 2)).center());
1849
1850 QCOMPARE(currentItemChangedSpy.count(), 1);
1851 QCOMPARE(itemSelectionChangedSpy.count(), 1);
1852
1853}
1854
1855#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
1856void tst_QListWidget::clearItemData()
1857{
1858 QListWidget list;
1859 for (int i = 0 ; i < 4; ++i)
1860 new QListWidgetItem(QString::number(i), &list);
1861 QSignalSpy dataChangeSpy(list.model(), &QAbstractItemModel::dataChanged);
1862 QVERIFY(dataChangeSpy.isValid());
1863 QVERIFY(!list.model()->clearItemData(QModelIndex()));
1864 QCOMPARE(dataChangeSpy.size(), 0);
1865 QVERIFY(list.model()->clearItemData(list.model()->index(0, 0)));
1866 QVERIFY(!list.model()->index(0, 0).data().isValid());
1867 QCOMPARE(dataChangeSpy.size(), 1);
1868 const QList<QVariant> dataChangeArgs = dataChangeSpy.takeFirst();
1869 QCOMPARE(dataChangeArgs.at(0).value<QModelIndex>(), list.model()->index(0, 0));
1870 QCOMPARE(dataChangeArgs.at(1).value<QModelIndex>(), list.model()->index(0, 0));
1871 QVERIFY(dataChangeArgs.at(2).value<QVector<int>>().isEmpty());
1872 QVERIFY(list.model()->clearItemData(list.model()->index(0, 0)));
1873 QCOMPARE(dataChangeSpy.size(), 0);
1874}
1875#endif
1876
1877QTEST_MAIN(tst_QListWidget)
1878#include "tst_qlistwidget.moc"
1879

source code of qtbase/tests/auto/widgets/itemviews/qlistwidget/tst_qlistwidget.cpp