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 | |
38 | using IntList = QVector<int>; |
39 | |
40 | class tst_QListWidget : public QObject |
41 | { |
42 | Q_OBJECT |
43 | |
44 | public: |
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 | |
58 | private 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 | |
130 | protected 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 | |
155 | private: |
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 | |
166 | void 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 | |
198 | void 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 | |
217 | void 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 | |
243 | void 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 | |
270 | void 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 | |
300 | void tst_QListWidget::cleanupTestCase() |
301 | { |
302 | delete testWidget; |
303 | } |
304 | |
305 | void tst_QListWidget::init() |
306 | { |
307 | testWidget->clear(); |
308 | QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete); |
309 | } |
310 | |
311 | void tst_QListWidget::checkDefaultValues() |
312 | { |
313 | QCOMPARE(testWidget->currentItem(), nullptr); |
314 | QCOMPARE(testWidget->currentRow(), -1); |
315 | QCOMPARE(testWidget->count(), 0); |
316 | } |
317 | |
318 | void 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 | |
331 | void 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 | |
340 | void 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 | |
356 | void 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 | |
375 | void 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 | |
388 | void 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 | |
408 | void tst_QListWidget::setItemHidden() |
409 | { |
410 | #if QT_DEPRECATED_SINCE(5, 13) |
411 | QT_WARNING_PUSH |
412 | QT_WARNING_DISABLE_DEPRECATED |
413 | // Boundary checking |
414 | testWidget->setItemHidden(item: nullptr, hide: true); |
415 | testWidget->setItemHidden(item: nullptr, hide: false); |
416 | QT_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 | |
450 | void 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 | |
459 | void 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 | |
482 | void 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 | |
491 | void 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 | |
513 | void tst_QListWidget::count() |
514 | { |
515 | populate(); |
516 | |
517 | // actual test |
518 | QCOMPARE(testWidget->model()->rowCount(), testWidget->count()); |
519 | } |
520 | |
521 | void 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 | |
533 | void 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 | |
545 | void 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 | |
552 | void 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 | |
584 | void 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 | |
598 | void 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 | |
614 | void 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 | |
635 | void 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 | |
658 | void 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 | |
708 | void 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 | |
728 | void 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 | |
740 | void 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 | |
761 | void 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 | |
773 | void 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 | |
796 | void 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 | |
841 | void 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 | |
890 | void 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 | |
904 | void 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 | |
928 | void 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 | |
952 | void 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 | |
991 | void 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 | |
999 | void 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 | |
1029 | void 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 | |
1061 | void 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 | |
1088 | void 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 | |
1128 | void 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 | |
1162 | class TestListWidget : public QListWidget |
1163 | { |
1164 | Q_OBJECT |
1165 | public: |
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 | |
1177 | void 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 | |
1193 | void 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 | |
1239 | void 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 | |
1277 | void 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 | |
1371 | void 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 | |
1424 | void 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 | |
1485 | class QListWidgetDataChanged : public QListWidget |
1486 | { |
1487 | Q_OBJECT |
1488 | public: |
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 | |
1499 | void 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 | |
1533 | void 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 | |
1573 | void 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 |
1589 | class MyListWidget : public QListWidget |
1590 | { |
1591 | Q_OBJECT |
1592 | public: |
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 | |
1604 | void 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 | |
1644 | void 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 | |
1652 | void 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 | |
1664 | void 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 | |
1674 | void 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 | |
1702 | void 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 | |
1720 | void 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 | |
1749 | class ItemDelegate : public QStyledItemDelegate |
1750 | { |
1751 | Q_OBJECT |
1752 | public: |
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 | |
1766 | void 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 | |
1793 | void 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 | |
1831 | void 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) |
1856 | void 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 | |
1877 | QTEST_MAIN(tst_QListWidget) |
1878 | #include "tst_qlistwidget.moc" |
1879 | |