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 <QRandomGenerator>
30#include <QStack>
31#include <QStandardItemModel>
32#include <QTest>
33#include "viewstotest.cpp"
34
35/*!
36 See viewstotest.cpp for instructions on how to have your view tested with these tests.
37
38 Each test such as visualRect have a _data() function which populate the QTest data with
39 tests specified by viewstotest.cpp and any extra data needed for that particular test.
40
41 setupWithNoTestData() fills QTest data with only the tests it is used by most tests.
42
43 There are some basic qDebug statements sprikled about that might be helpfull for
44 fixing your issues.
45 */
46class tst_QItemView : public QObject
47{
48 Q_OBJECT
49
50private slots:
51 void init();
52 void cleanup();
53
54 void nonDestructiveBasicTest_data();
55 void nonDestructiveBasicTest();
56
57 void spider_data();
58 void spider();
59
60 void resize_data();
61 void resize();
62
63 void visualRect_data();
64 void visualRect();
65
66 void indexAt_data();
67 void indexAt();
68
69 void scrollTo_data();
70 void scrollTo();
71
72 void moveCursor_data();
73 void moveCursor();
74
75private:
76 void setupWithNoTestData();
77 void populate();
78 void walkScreen(QAbstractItemView *view);
79
80 QAbstractItemView *view;
81 QAbstractItemModel *treeModel;
82 ViewsToTest *testViews;
83};
84
85/*!
86 * Views should not make invalid requests, sense a model might not check all the bad cases.
87 */
88class CheckerModel : public QStandardItemModel
89{
90 Q_OBJECT
91
92public:
93 using QStandardItemModel::QStandardItemModel;
94
95 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
96 {
97 if (!index.isValid()) {
98 qWarning(msg: "%s: index is not valid", Q_FUNC_INFO);
99 return QVariant();
100 }
101 return QStandardItemModel::data(index, role);
102 }
103
104 Qt::ItemFlags flags(const QModelIndex &index) const override
105 {
106 if (!index.isValid()) {
107 qWarning(msg: "%s: index is not valid", Q_FUNC_INFO);
108 return Qt::ItemFlags();
109 }
110 if (index.row() == 2 || index.row() == rowCount() - 3
111 || index.column() == 2 || index.column() == columnCount() - 3) {
112 Qt::ItemFlags f = QStandardItemModel::flags(index);
113 f.setFlag(flag: Qt::ItemIsEnabled, on: false);
114 return f;
115 }
116 return QStandardItemModel::flags(index);
117 }
118
119 QModelIndex parent(const QModelIndex &child) const override
120 {
121 if (!child.isValid()) {
122 qWarning(msg: "%s: child index is not valid", Q_FUNC_INFO);
123 return QModelIndex();
124 }
125 return QStandardItemModel::parent(child);
126 }
127
128 QVariant headerData(int section, Qt::Orientation orientation,
129 int role = Qt::DisplayRole) const override
130 {
131 if (orientation == Qt::Horizontal
132 && (section < 0 || section > columnCount())) {
133 qWarning(msg: "%s: invalid section %d, must be in range 0..%d",
134 Q_FUNC_INFO, section, columnCount());
135 return QVariant();
136 }
137 if (orientation == Qt::Vertical
138 && (section < 0 || section > rowCount())) {
139 qWarning(msg: "%s: invalid section %d, must be in range 0..%d",
140 Q_FUNC_INFO, section, rowCount());
141 return QVariant();
142 }
143 return QStandardItemModel::headerData(section, orientation, role);
144 }
145
146 bool setData(const QModelIndex &index, const QVariant &value,
147 int role = Qt::EditRole) override
148 {
149 if (!index.isValid()) {
150 qWarning(msg: "%s: index is not valid", Q_FUNC_INFO);
151 return false;
152 }
153 return QStandardItemModel::setData(index, value, role);
154 }
155
156 void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override
157 {
158 if (column < 0 || column > columnCount())
159 qWarning(msg: "%s: invalid column %d, must be in range 0..%d",
160 Q_FUNC_INFO, column, columnCount());
161 else
162 QStandardItemModel::sort(column, order);
163 }
164
165 QModelIndexList match(const QModelIndex &start, int role,
166 const QVariant &value, int hits = 1,
167 Qt::MatchFlags flags = Qt::MatchFlags(Qt::MatchStartsWith | Qt::MatchWrap)) const override
168 {
169 if (hits <= 0) {
170 qWarning(msg: "%s: hits must be greater than zero", Q_FUNC_INFO);
171 return QModelIndexList();
172 }
173 if (!value.isValid()) {
174 qWarning(msg: "%s: value is not valid", Q_FUNC_INFO);
175 return QModelIndexList();
176 }
177 return QAbstractItemModel::match(start, role, value, hits, flags);
178 }
179
180 bool setHeaderData(int section, Qt::Orientation orientation,
181 const QVariant &value, int role = Qt::EditRole) override
182 {
183 if (orientation == Qt::Horizontal
184 && (section < 0 || section > columnCount())) {
185 qWarning(msg: "%s: invalid section %d, must be in range 0..%d",
186 Q_FUNC_INFO, section, columnCount());
187 return false;
188 }
189 if (orientation == Qt::Vertical
190 && (section < 0 || section > rowCount())) {
191 qWarning(msg: "%s: invalid section %d, must be in range 0..%d",
192 Q_FUNC_INFO, section, rowCount());
193 return false;
194 }
195 return QAbstractItemModel::setHeaderData(section, orientation, value, role);
196 }
197};
198
199void tst_QItemView::init()
200{
201 testViews = new ViewsToTest();
202 populate();
203}
204
205void tst_QItemView::cleanup()
206{
207 delete testViews;
208 delete view;
209 delete treeModel;
210 view = nullptr;
211 testViews = nullptr;
212 treeModel = nullptr;
213}
214
215void tst_QItemView::setupWithNoTestData()
216{
217 ViewsToTest testViews;
218 QTest::addColumn<QString>(name: "viewType");
219 QTest::addColumn<bool>(name: "displays");
220 QTest::addColumn<QAbstractItemView::ScrollMode>(name: "vscroll");
221 QTest::addColumn<QAbstractItemView::ScrollMode>(name: "hscroll");
222 for (int i = 0; i < testViews.tests.size(); ++i) {
223 QString view = testViews.tests.at(i).viewType;
224 QString test = view + " ScrollPerPixel";
225 bool displayIndexes = (testViews.tests.at(i).display == ViewsToTest::DisplayRoot);
226 QTest::newRow(dataTag: test.toLatin1().data()) << view << displayIndexes
227 << QAbstractItemView::ScrollPerPixel
228 << QAbstractItemView::ScrollPerPixel
229 ;
230 }
231 for (int i = 0; i < testViews.tests.size(); ++i) {
232 QString view = testViews.tests.at(i).viewType;
233 QString test = view + " ScrollPerItem";
234 bool displayIndexes = (testViews.tests.at(i).display == ViewsToTest::DisplayRoot);
235 QTest::newRow(dataTag: test.toLatin1().data()) << view << displayIndexes
236 << QAbstractItemView::ScrollPerItem
237 << QAbstractItemView::ScrollPerItem
238 ;
239 }
240}
241
242void tst_QItemView::populate()
243{
244 treeModel = new CheckerModel;
245 QModelIndex parent;
246#if defined(Q_PROCESSOR_ARM)
247 const int baseInsert = 4;
248#else
249 const int baseInsert = 26;
250#endif
251 for (int i = 0; i < 40; ++i) {
252 const QString iS = QString::number(i);
253 parent = treeModel->index(row: 0, column: 0, parent);
254 treeModel->insertRows(row: 0, count: baseInsert + i, parent);
255 treeModel->insertColumns(column: 0, count: baseInsert + i, parent);
256 // Fill in some values to make it easier to debug
257 for (int x = 0; x < treeModel->rowCount(); ++x) {
258 const QString xS = QString::number(x);
259 for (int y = 0; y < treeModel->columnCount(); ++y) {
260 QModelIndex index = treeModel->index(row: x, column: y, parent);
261 treeModel->setData(index, value: xS + QLatin1Char('_') + QString::number(y) + QLatin1Char('_') + iS);
262 treeModel->setData(index, value: QVariant(QColor(Qt::blue)), role: Qt::ForegroundRole);
263 }
264 }
265 }
266}
267
268void tst_QItemView::nonDestructiveBasicTest_data()
269{
270 setupWithNoTestData();
271}
272
273/*!
274 nonDestructiveBasicTest tries to call a number of the basic functions (not all)
275 to make sure the view doesn't segfault, testing the functions that makes sense.
276 */
277void tst_QItemView::nonDestructiveBasicTest()
278{
279 QFETCH(QString, viewType);
280 QFETCH(QAbstractItemView::ScrollMode, vscroll);
281 QFETCH(QAbstractItemView::ScrollMode, hscroll);
282
283 view = testViews->createView(viewType);
284 QVERIFY(view);
285 view->setVerticalScrollMode(vscroll);
286 view->setHorizontalScrollMode(hscroll);
287
288 // setSelectionModel() will assert
289 //view->setSelectionModel(0);
290 // setItemDelegate() will assert
291 //view->setItemDelegate(0);
292
293 // setSelectionMode
294 view->setSelectionMode(QAbstractItemView::SingleSelection);
295 QCOMPARE(view->selectionMode(), QAbstractItemView::SingleSelection);
296 view->setSelectionMode(QAbstractItemView::ContiguousSelection);
297 QCOMPARE(view->selectionMode(), QAbstractItemView::ContiguousSelection);
298 view->setSelectionMode(QAbstractItemView::ExtendedSelection);
299 QCOMPARE(view->selectionMode(), QAbstractItemView::ExtendedSelection);
300 view->setSelectionMode(QAbstractItemView::MultiSelection);
301 QCOMPARE(view->selectionMode(), QAbstractItemView::MultiSelection);
302 view->setSelectionMode(QAbstractItemView::NoSelection);
303 QCOMPARE(view->selectionMode(), QAbstractItemView::NoSelection);
304
305 // setSelectionBehavior
306 view->setSelectionBehavior(QAbstractItemView::SelectItems);
307 QCOMPARE(view->selectionBehavior(), QAbstractItemView::SelectItems);
308 view->setSelectionBehavior(QAbstractItemView::SelectRows);
309 QCOMPARE(view->selectionBehavior(), QAbstractItemView::SelectRows);
310 view->setSelectionBehavior(QAbstractItemView::SelectColumns);
311 QCOMPARE(view->selectionBehavior(), QAbstractItemView::SelectColumns);
312
313 // setEditTriggers
314 view->setEditTriggers(QAbstractItemView::EditKeyPressed);
315 QCOMPARE(view->editTriggers(), QAbstractItemView::EditKeyPressed);
316 view->setEditTriggers(QAbstractItemView::NoEditTriggers);
317 QCOMPARE(view->editTriggers(), QAbstractItemView::NoEditTriggers);
318 view->setEditTriggers(QAbstractItemView::CurrentChanged);
319 QCOMPARE(view->editTriggers(), QAbstractItemView::CurrentChanged);
320 view->setEditTriggers(QAbstractItemView::DoubleClicked);
321 QCOMPARE(view->editTriggers(), QAbstractItemView::DoubleClicked);
322 view->setEditTriggers(QAbstractItemView::SelectedClicked);
323 QCOMPARE(view->editTriggers(), QAbstractItemView::SelectedClicked);
324 view->setEditTriggers(QAbstractItemView::AnyKeyPressed);
325 QCOMPARE(view->editTriggers(), QAbstractItemView::AnyKeyPressed);
326 view->setEditTriggers(QAbstractItemView::AllEditTriggers);
327 QCOMPARE(view->editTriggers(), QAbstractItemView::AllEditTriggers);
328
329 // setAutoScroll
330 view->setAutoScroll(false);
331 QCOMPARE(view->hasAutoScroll(), false);
332 view->setAutoScroll(true);
333 QCOMPARE(view->hasAutoScroll(), true);
334
335 // setTabKeyNavigation
336 view->setTabKeyNavigation(false);
337 QCOMPARE(view->tabKeyNavigation(), false);
338 view->setTabKeyNavigation(true);
339 QCOMPARE(view->tabKeyNavigation(), true);
340#if QT_CONFIG(draganddrop)
341 // setDropIndicatorShown
342 view->setDropIndicatorShown(false);
343 QCOMPARE(view->showDropIndicator(), false);
344 view->setDropIndicatorShown(true);
345 QCOMPARE(view->showDropIndicator(), true);
346
347 // setDragEnabled
348 view->setDragEnabled(false);
349 QCOMPARE(view->dragEnabled(), false);
350 view->setDragEnabled(true);
351 QCOMPARE(view->dragEnabled(), true);
352#endif
353
354 // setAlternatingRowColors
355 view->setAlternatingRowColors(false);
356 QCOMPARE(view->alternatingRowColors(), false);
357 view->setAlternatingRowColors(true);
358 QCOMPARE(view->alternatingRowColors(), true);
359
360 // setIconSize
361 view->setIconSize(QSize(16, 16));
362 QCOMPARE(view->iconSize(), QSize(16, 16));
363 view->setIconSize(QSize(32, 32));
364 QCOMPARE(view->iconSize(), QSize(32, 32));
365 // Should this happen?
366 view->setIconSize(QSize(-1, -1));
367 QCOMPARE(view->iconSize(), QSize(-1, -1));
368
369 QCOMPARE(view->currentIndex(), QModelIndex());
370 QCOMPARE(view->rootIndex(), QModelIndex());
371
372 view->keyboardSearch(search: "");
373 view->keyboardSearch(search: "foo");
374 view->keyboardSearch(search: "1");
375
376 QCOMPARE(view->visualRect(QModelIndex()), QRect());
377
378 view->scrollTo(index: QModelIndex());
379
380 QCOMPARE(view->sizeHintForIndex(QModelIndex()), QSize());
381 QCOMPARE(view->indexAt(QPoint(-1, -1)), QModelIndex());
382
383 if (!view->model()){
384 QCOMPARE(view->indexAt(QPoint(10, 10)), QModelIndex());
385 QCOMPARE(view->sizeHintForRow(0), -1);
386 QCOMPARE(view->sizeHintForColumn(0), -1);
387 } else if (view->itemDelegate()){
388 view->sizeHintForRow(row: 0);
389 view->sizeHintForColumn(column: 0);
390 }
391 view->openPersistentEditor(index: QModelIndex());
392 view->closePersistentEditor(index: QModelIndex());
393
394 view->reset();
395 view->setRootIndex(QModelIndex());
396 view->doItemsLayout();
397 view->selectAll();
398 // edit() causes warning by default
399 //view->edit(QModelIndex());
400 view->clearSelection();
401 view->setCurrentIndex(QModelIndex());
402}
403
404void tst_QItemView::spider_data()
405{
406 setupWithNoTestData();
407}
408
409void touch(QWidget *widget, Qt::KeyboardModifier modifier, Qt::Key keyPress)
410{
411 int width = widget->width();
412 int height = widget->height();
413 for (int i = 0; i < 5; ++i) {
414 QTest::mouseClick(widget, button: Qt::LeftButton, stateKey: modifier,
415 pos: QPoint(QRandomGenerator::global()->bounded(highest: width), QRandomGenerator::global()->bounded(highest: height)));
416 QTest::mouseDClick(widget, button: Qt::LeftButton, stateKey: modifier,
417 pos: QPoint(QRandomGenerator::global()->bounded(highest: width), QRandomGenerator::global()->bounded(highest: height)));
418 QPoint press(QRandomGenerator::global()->bounded(highest: width), QRandomGenerator::global()->bounded(highest: height));
419 QPoint releasePoint(QRandomGenerator::global()->bounded(highest: width), QRandomGenerator::global()->bounded(highest: height));
420 QTest::mousePress(widget, button: Qt::LeftButton, stateKey: modifier, pos: press);
421 QTest::mouseMove(widget, pos: releasePoint);
422 if (QRandomGenerator::global()->bounded(highest: 1) == 0)
423 QTest::mouseRelease(widget, button: Qt::LeftButton, stateKey: {}, pos: releasePoint);
424 else
425 QTest::mouseRelease(widget, button: Qt::LeftButton, stateKey: modifier, pos: releasePoint);
426 QTest::keyClick(widget, key: keyPress);
427 }
428}
429
430/*!
431 This is a basic stress testing application that tries a few basics such as clicking around
432 the screen, and key presses.
433
434 The main goal is to catch any easy segfaults, not to test every case.
435 */
436void tst_QItemView::spider()
437{
438 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
439 QSKIP("Wayland: This fails. Figure out why.");
440
441 QFETCH(QString, viewType);
442 QFETCH(QAbstractItemView::ScrollMode, vscroll);
443 QFETCH(QAbstractItemView::ScrollMode, hscroll);
444
445 view = testViews->createView(viewType);
446 QVERIFY(view);
447 view->setVerticalScrollMode(vscroll);
448 view->setHorizontalScrollMode(hscroll);
449 view->setModel(treeModel);
450 view->show();
451 QVERIFY(QTest::qWaitForWindowActive(view));
452 touch(widget: view->viewport(), modifier: Qt::NoModifier, keyPress: Qt::Key_Left);
453 touch(widget: view->viewport(), modifier: Qt::ShiftModifier, keyPress: Qt::Key_Enter);
454 touch(widget: view->viewport(), modifier: Qt::ControlModifier, keyPress: Qt::Key_Backspace);
455 touch(widget: view->viewport(), modifier: Qt::AltModifier, keyPress: Qt::Key_Up);
456}
457
458void tst_QItemView::resize_data()
459{
460 setupWithNoTestData();
461}
462
463/*!
464 The main goal is to catch any infinite loops from layouting
465 */
466void tst_QItemView::resize()
467{
468 QSKIP("This test needs to be re-thought out, it takes too long and doesn't really catch the problem.");
469
470 QFETCH(QString, viewType);
471 QFETCH(QAbstractItemView::ScrollMode, vscroll);
472 QFETCH(QAbstractItemView::ScrollMode, hscroll);
473
474 view = testViews->createView(viewType);
475 QVERIFY(view);
476 view->setVerticalScrollMode(vscroll);
477 view->setHorizontalScrollMode(hscroll);
478 view->setModel(treeModel);
479 view->show();
480
481 for (int w = 100; w < 400; w += 10) {
482 for (int h = 100; h < 400; h += 10) {
483 view->resize(w, h);
484 QTest::qWait(ms: 1);
485 QCoreApplication::processEvents();
486 }
487 }
488}
489
490void tst_QItemView::visualRect_data()
491{
492 setupWithNoTestData();
493}
494
495void tst_QItemView::visualRect()
496{
497 QFETCH(QString, viewType);
498 QFETCH(QAbstractItemView::ScrollMode, vscroll);
499 QFETCH(QAbstractItemView::ScrollMode, hscroll);
500
501 view = testViews->createView(viewType);
502 QVERIFY(view);
503 view->setVerticalScrollMode(vscroll);
504 view->setHorizontalScrollMode(hscroll);
505 QCOMPARE(view->visualRect(QModelIndex()), QRect());
506
507 // Add model
508 view->setModel(treeModel);
509 QCOMPARE(view->visualRect(QModelIndex()), QRect());
510
511 QModelIndex topIndex = treeModel->index(row: 0,column: 0);
512
513 QFETCH(bool, displays);
514 if (!displays){
515 QCOMPARE(view->visualRect(topIndex), QRect());
516 return;
517 }
518
519 QVERIFY(view->visualRect(topIndex) != QRect());
520 view->show();
521 QVERIFY(view->visualRect(topIndex) != QRect());
522
523 QCOMPARE(topIndex, view->indexAt(view->visualRect(topIndex).center()));
524 QCOMPARE(topIndex, view->indexAt(view->visualRect(topIndex).bottomLeft()));
525 QCOMPARE(topIndex, view->indexAt(view->visualRect(topIndex).bottomRight()));
526 QCOMPARE(topIndex, view->indexAt(view->visualRect(topIndex).topLeft()));
527 QCOMPARE(topIndex, view->indexAt(view->visualRect(topIndex).topRight()));
528
529 testViews->hideIndexes(view);
530 QModelIndex hiddenIndex = treeModel->index(row: 1, column: 0);
531 QCOMPARE(view->visualRect(hiddenIndex), QRect());
532}
533
534void tst_QItemView::walkScreen(QAbstractItemView *view)
535{
536 QModelIndex hiddenIndex = view->model() ? view->model()->index(row: 1, column: 0) : QModelIndex();
537 int width = view->width();
538 int height = view->height();
539 for (int w = 0; w < width; ++w)
540 {
541 for (int h = 0; h < height; ++h)
542 {
543 QPoint point(w, h);
544 QModelIndex index = view->indexAt(point);
545
546 // If we have no model then we should *never* get a valid index
547 if (!view->model() || !view->isVisible())
548 QVERIFY(!index.isValid());
549 // index should not be the hidden one
550 if (hiddenIndex.isValid())
551 QVERIFY(hiddenIndex != index);
552 // If we are valid then check the visualRect for that index
553 if (index.isValid()){
554 QRect visualRect = view->visualRect(index);
555 if (!visualRect.contains(p: point))
556 qDebug() << point << visualRect;
557 QVERIFY(visualRect.contains(point));
558 }
559 }
560 }
561}
562
563void walkIndex(const QModelIndex &index, const QAbstractItemView *view)
564{
565 const QRect visualRect = view->visualRect(index);
566 const int width = visualRect.width();
567 const int height = visualRect.height();
568
569 if (width == 0 || height == 0)
570 return;
571
572 const auto widths = (width < 2) ? QVector<int>({ 0, 1 }) : QVector<int>({ 0, 1, width / 2, width - 2, width - 1 });
573 const auto heights = (height < 2) ? QVector<int>({ 0, 1 }) : QVector<int>({ 0, 1, height / 2, height - 2, height - 1 });
574 for (int w : widths)
575 {
576 for (int h : heights)
577 {
578 const QPoint point(visualRect.x() + w, visualRect.y() + h);
579 const auto idxAt = view->indexAt(point);
580 if (idxAt != index)
581 qDebug() << "index" << index << "visualRect" << visualRect << point << view->indexAt(point);
582 QCOMPARE(idxAt, index);
583 }
584 }
585
586}
587
588/*!
589 A model that returns an index of parent X should also return X when asking
590 for the parent of the index.
591
592 This recursive function does pretty extensive testing on the whole model in an
593 effort to catch edge cases.
594
595 This function assumes that rowCount(), columnCount() and index() work. If they have
596 a bug it will point it out, but the above tests should have already found the basic bugs
597 because it is easier to figure out the problem in those tests then this one.
598 */
599void checkChildren(const QAbstractItemView *currentView, const QModelIndex &parent = QModelIndex(), int currentDepth = 0)
600{
601 QAbstractItemModel *currentModel = currentView->model();
602
603 int rows = currentModel->rowCount(parent);
604 int columns = currentModel->columnCount(parent);
605
606 for (int r = 0; r < rows; ++r) {
607 for (int c = 0; c < columns; ++c) {
608 QModelIndex index = currentModel->index(row: r, column: c, parent);
609 walkIndex(index, view: currentView);
610 if (QTest::currentTestFailed())
611 return;
612
613 // recursivly go down
614 if (currentModel->hasChildren(parent: index) && currentDepth < 2) {
615 checkChildren(currentView, parent: index, currentDepth: ++currentDepth);
616 // Because this is recursive we will return at the first failure rather then
617 // reporting it over and over
618 if (QTest::currentTestFailed())
619 return;
620 }
621 }
622 }
623}
624
625
626void tst_QItemView::indexAt_data()
627{
628 setupWithNoTestData();
629}
630
631void tst_QItemView::indexAt()
632{
633 QFETCH(QString, viewType);
634 QFETCH(QAbstractItemView::ScrollMode, vscroll);
635 QFETCH(QAbstractItemView::ScrollMode, hscroll);
636
637 view = testViews->createView(viewType);
638 QVERIFY(view);
639 view->setVerticalScrollMode(vscroll);
640 view->setHorizontalScrollMode(hscroll);
641 view->show();
642 view->setModel(treeModel);
643 checkChildren(currentView: view);
644
645 QModelIndex index = view->model()->index(row: 0, column: 0);
646 while (view->model()->hasChildren(parent: index))
647 index = view->model()->index(row: 0, column: 0, parent: index);
648 QCOMPARE(view->model()->hasChildren(index), false);
649 QVERIFY(index.isValid());
650 view->setRootIndex(index);
651 //qDebug() << view->indexAt(QPoint(view->width()/2, view->height()/2)) << view->rootIndex();
652 QPoint p(1, view->height()/2);
653 QModelIndex idx = view->indexAt(point: p);
654 QCOMPARE(idx, QModelIndex());
655}
656
657void tst_QItemView::scrollTo_data()
658{
659 setupWithNoTestData();
660}
661
662void tst_QItemView::scrollTo()
663{
664 QFETCH(QString, viewType);
665 QFETCH(QAbstractItemView::ScrollMode, vscroll);
666 QFETCH(QAbstractItemView::ScrollMode, hscroll);
667
668 view = testViews->createView(viewType);
669 QVERIFY(view);
670 view->setVerticalScrollMode(vscroll);
671 view->setHorizontalScrollMode(hscroll);
672 view->setModel(treeModel);
673 view->show();
674
675 QModelIndex parent;
676 for (int row = 0; row < treeModel->rowCount(parent); ++row) {
677 for (int column = 0; column < treeModel->columnCount(parent); ++column) {
678 QModelIndex idx = treeModel->index(row, column, parent);
679 view->scrollTo(index: idx);
680 QRect rect = view->visualRect(index: idx);
681 view->scrollTo(index: idx);
682 QCOMPARE(rect, view->visualRect(idx));
683 }
684 }
685
686 QModelIndex idx = treeModel->index(row: 0, column: 0, parent);
687 view->scrollTo(index: idx);
688 QRect rect = view->visualRect(index: idx);
689 view->scrollToBottom();
690 view->scrollTo(index: idx);
691 QCOMPARE(rect, view->visualRect(idx));
692}
693
694void tst_QItemView::moveCursor_data()
695{
696 setupWithNoTestData();
697}
698
699struct Event
700{
701 Event(Qt::Key k, const QModelIndex &s, const QModelIndex &e, const QString &n)
702 : key(k), start(s), end(e), name(n){}
703 Qt::Key key;
704 QModelIndex start;
705 QModelIndex end;
706 QString name;
707};
708
709
710void tst_QItemView::moveCursor()
711{
712 QFETCH(QString, viewType);
713 view = testViews->createView(viewType);
714 QVERIFY(view);
715 if (view->objectName() == "QHeaderView")
716 return;
717
718 view->setModel(treeModel);
719 testViews->hideIndexes(view);
720 view->resize(w: 100, h: 100);
721
722 QModelIndex invalidIndex = QModelIndex();
723 QModelIndex firstRow = treeModel->index(row: 0, column: 0);
724 QModelIndex hiddenRowT = treeModel->index(row: 1, column: 0);
725 QModelIndex disabledRowT = treeModel->index(row: 2, column: 0);
726 QModelIndex secondRow = treeModel->index(row: 3, column: 0);
727
728 QModelIndex secondToLastRow = treeModel->index(row: treeModel->rowCount() - 4, column: 0);
729 QModelIndex disabledRowB = treeModel->index(row: treeModel->rowCount() - 3, column: 0);
730 QModelIndex hiddenRowB = treeModel->index(row: treeModel->rowCount() - 2, column: 0);
731 QModelIndex lastRow = treeModel->index(row: treeModel->rowCount() - 1, column: 0);
732
733 QStack<Event> events;
734
735 events.push(t: Event(Qt::Key_Up, invalidIndex, firstRow, "inv, first"));
736 events.push(t: Event(Qt::Key_Up, hiddenRowT, firstRow, "hid, first"));
737 events.push(t: Event(Qt::Key_Up, disabledRowT, firstRow, "dis, first"));
738 events.push(t: Event(Qt::Key_Up, firstRow, firstRow, "first, first"));
739 events.push(t: Event(Qt::Key_Up, secondRow, firstRow, "sec, first"));
740 events.push(t: Event(Qt::Key_Up, hiddenRowB, firstRow, "hidB, first"));
741 events.push(t: Event(Qt::Key_Up, disabledRowB, secondToLastRow, "disB, secLast"));
742 events.push(t: Event(Qt::Key_Up, lastRow, secondToLastRow, "last, secLast"));
743
744 events.push(t: Event(Qt::Key_Down, invalidIndex, firstRow, "inv, first"));
745 events.push(t: Event(Qt::Key_Down, hiddenRowT, firstRow, "hid, first"));
746 events.push(t: Event(Qt::Key_Down, disabledRowT, secondRow, "dis, sec"));
747 events.push(t: Event(Qt::Key_Down, firstRow, secondRow, "first, sec"));
748 events.push(t: Event(Qt::Key_Down, secondToLastRow, lastRow, "secLast, last" ));
749 events.push(t: Event(Qt::Key_Down, disabledRowB, lastRow, "disB, last"));
750 events.push(t: Event(Qt::Key_Down, hiddenRowB, firstRow, "hidB, first"));
751 events.push(t: Event(Qt::Key_Down, lastRow, lastRow, "last, last"));
752
753 events.push(t: Event(Qt::Key_Home, invalidIndex, firstRow, "inv, first"));
754 events.push(t: Event(Qt::Key_End, invalidIndex, firstRow, "inv, first"));
755
756 if (view->objectName() == "QTableView") {
757 // In a table we move to the first/last column
758 events.push(t: Event(Qt::Key_Home, hiddenRowT, firstRow, "hid, first"));
759 events.push(t: Event(Qt::Key_Home, disabledRowT, disabledRowT, "dis, dis"));
760 events.push(t: Event(Qt::Key_Home, firstRow, firstRow, "first, first"));
761 events.push(t: Event(Qt::Key_Home, secondRow, secondRow, "sec, sec"));
762 events.push(t: Event(Qt::Key_Home, disabledRowB, disabledRowB, "disB, disB"));
763 events.push(t: Event(Qt::Key_Home, hiddenRowB, firstRow, "hidB, first"));
764 events.push(t: Event(Qt::Key_Home, secondToLastRow, secondToLastRow, "secLast, secLast"));
765 events.push(t: Event(Qt::Key_Home, lastRow, lastRow, "last, last"));
766
767 int col = treeModel->columnCount() - 1;
768 events.push(t: Event(Qt::Key_End, hiddenRowT, firstRow, "hidT, hidT"));
769 events.push(t: Event(Qt::Key_End, disabledRowT, disabledRowT, "disT, disT"));
770 events.push(t: Event(Qt::Key_End, firstRow, firstRow.sibling(arow: firstRow.row(), acolumn: col), "first, first_C"));
771 events.push(t: Event(Qt::Key_End, secondRow, secondRow.sibling(arow: secondRow.row(), acolumn: col), "sec, sec_C"));
772 events.push(t: Event(Qt::Key_End, disabledRowB, disabledRowB, "disB, disB"));
773 events.push(t: Event(Qt::Key_End, hiddenRowB, firstRow, "hidB, hidB"));
774 events.push(t: Event(Qt::Key_End, secondToLastRow, secondToLastRow.sibling(arow: secondToLastRow.row(), acolumn: col), "secLast, secLast_C"));
775 events.push(t: Event(Qt::Key_End, lastRow, lastRow.sibling(arow: lastRow.row(), acolumn: col), "last, last_C"));
776 } else {
777 events.push(t: Event(Qt::Key_Home, hiddenRowT, firstRow, "hid, first"));
778 events.push(t: Event(Qt::Key_Home, disabledRowT, firstRow, "dis, first"));
779 events.push(t: Event(Qt::Key_Home, firstRow, firstRow, "first, first"));
780 events.push(t: Event(Qt::Key_Home, secondRow, firstRow, "sec, first"));
781 events.push(t: Event(Qt::Key_Home, disabledRowB, firstRow, "disB, first"));
782 events.push(t: Event(Qt::Key_Home, hiddenRowB, firstRow, "hidB, first"));
783 events.push(t: Event(Qt::Key_Home, secondToLastRow, firstRow, "sec, first"));
784 events.push(t: Event(Qt::Key_Home, lastRow, firstRow, "last, first"));
785
786 events.push(t: Event(Qt::Key_End, hiddenRowT, firstRow, "hid, last"));
787 events.push(t: Event(Qt::Key_End, disabledRowT, lastRow, "dis, last"));
788 events.push(t: Event(Qt::Key_End, firstRow, lastRow, "first, last"));
789 events.push(t: Event(Qt::Key_End, secondRow, lastRow, "sec, last"));
790 events.push(t: Event(Qt::Key_End, disabledRowB, lastRow, "disB, last"));
791 events.push(t: Event(Qt::Key_End, hiddenRowB, firstRow, "hidB, last"));
792 events.push(t: Event(Qt::Key_End, secondToLastRow, lastRow, "sec, last"));
793 events.push(t: Event(Qt::Key_End, lastRow, lastRow, "last, last"));
794 }
795
796 events.push(t: Event(Qt::Key_PageDown, invalidIndex, firstRow, "inv, first"));
797 events.push(t: Event(Qt::Key_PageDown, firstRow, QModelIndex(), "first, x"));
798 events.push(t: Event(Qt::Key_PageDown, secondRow, QModelIndex(), "sec, x"));
799 events.push(t: Event(Qt::Key_PageDown, hiddenRowT, QModelIndex(), "hid, x"));
800 events.push(t: Event(Qt::Key_PageDown, disabledRowT, QModelIndex(), "dis, x"));
801 events.push(t: Event(Qt::Key_PageDown, disabledRowB, lastRow, "disB, last"));
802 events.push(t: Event(Qt::Key_PageDown, hiddenRowB, lastRow, "hidB, last"));
803 events.push(t: Event(Qt::Key_PageDown, secondToLastRow, lastRow, "secLast, last"));
804 events.push(t: Event(Qt::Key_PageDown, lastRow, lastRow, "last, last"));
805
806 events.push(t: Event(Qt::Key_PageUp, invalidIndex, firstRow, "inv, first"));
807 events.push(t: Event(Qt::Key_PageUp, firstRow, firstRow, "first, first"));
808 events.push(t: Event(Qt::Key_PageUp, secondRow, firstRow, "sec, first"));
809 events.push(t: Event(Qt::Key_PageUp, secondToLastRow, QModelIndex(), "secLast, x"));
810 events.push(t: Event(Qt::Key_PageUp, lastRow, QModelIndex(), "last, x"));
811
812 if (view->objectName() == "QTableView") {
813 events.push(t: Event(Qt::Key_Left, firstRow, firstRow, "first_0, first"));
814 events.push(t: Event(Qt::Key_Left, firstRow.sibling(arow: 0, acolumn: 1), firstRow, "first_1, first"));
815 events.push(t: Event(Qt::Key_Left, firstRow.sibling(arow: 0, acolumn: 2), firstRow, "first_2, first"));
816 events.push(t: Event(Qt::Key_Left, firstRow.sibling(arow: 0, acolumn: 3), firstRow, "first_3, first"));
817 events.push(t: Event(Qt::Key_Left, secondRow, secondRow, "sec, sec"));
818
819 events.push(t: Event(Qt::Key_Right, firstRow, firstRow.sibling(arow: 0, acolumn: 3), "first, first_3"));
820 events.push(t: Event(Qt::Key_Right, firstRow.sibling(arow: 0, acolumn: 1), firstRow, "first_1, first"));
821 events.push(t: Event(Qt::Key_Right, firstRow.sibling(arow: 0, acolumn: 2), firstRow.sibling(arow: 0, acolumn: 3), "first_2, first_3"));
822 events.push(t: Event(Qt::Key_Right, firstRow.sibling(arow: 0, acolumn: treeModel->columnCount()-1), firstRow.sibling(arow: 0, acolumn: treeModel->columnCount()-1), "first_3, sec"));
823 }
824}
825
826QTEST_MAIN(tst_QItemView)
827#include "tst_qitemview.moc"
828

source code of qtbase/tests/auto/widgets/itemviews/qitemview/tst_qitemview.cpp