| 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 <QColumnView> | 
| 30 | #include <QScrollBar> | 
| 31 | #include <QSignalSpy> | 
| 32 | #include <QStringListModel> | 
| 33 | #include <QStyledItemDelegate> | 
| 34 | #include <QTest> | 
| 35 | #include <QtTest/private/qtesthelpers_p.h> | 
| 36 | #include <QtWidgets/private/qcolumnviewgrip_p.h> | 
| 37 | #include "../../../../shared/fakedirmodel.h" | 
| 38 |  | 
| 39 | #define ANIMATION_DELAY 300 | 
| 40 |  | 
| 41 | class tst_QColumnView : public QObject | 
| 42 | { | 
| 43 |     Q_OBJECT | 
| 44 | public: | 
| 45 |     tst_QColumnView(); | 
| 46 |  | 
| 47 | private slots: | 
| 48 |     void initTestCase(); | 
| 49 |     void init(); | 
| 50 |     void rootIndex(); | 
| 51 |     void grips(); | 
| 52 |     void isIndexHidden(); | 
| 53 |     void indexAt(); | 
| 54 |     void scrollContentsBy_data(); | 
| 55 |     void scrollContentsBy(); | 
| 56 |     void scrollTo_data(); | 
| 57 |     void scrollTo(); | 
| 58 |     void moveCursor_data(); | 
| 59 |     void moveCursor(); | 
| 60 |     void selectAll(); | 
| 61 |     void clicked(); | 
| 62 |     void selectedColumns(); | 
| 63 |     void setSelection(); | 
| 64 |     void setSelectionModel(); | 
| 65 |     void visualRegionForSelection(); | 
| 66 |  | 
| 67 |     void dynamicModelChanges(); | 
| 68 |  | 
| 69 |     // grip | 
| 70 |     void moveGrip_basic(); | 
| 71 |     void moveGrip_data(); | 
| 72 |     void moveGrip(); | 
| 73 |     void doubleClick(); | 
| 74 |     void gripMoved(); | 
| 75 |  | 
| 76 |     void preview(); | 
| 77 |     void swapPreview(); | 
| 78 |     void sizes(); | 
| 79 |     void rowDelegate(); | 
| 80 |     void resize(); | 
| 81 |     void changeSameColumn(); | 
| 82 |     void parentCurrentIndex_data(); | 
| 83 |     void parentCurrentIndex(); | 
| 84 |     void pullRug_data(); | 
| 85 |     void pullRug(); | 
| 86 |  | 
| 87 | protected slots: | 
| 88 |     void setPreviewWidget(); | 
| 89 |  | 
| 90 | private: | 
| 91 |     QStandardItemModel m_fakeDirModel; | 
| 92 |     QModelIndex m_fakeDirHomeIndex; | 
| 93 | }; | 
| 94 |  | 
| 95 | class TreeModel : public QStandardItemModel | 
| 96 | { | 
| 97 |     Q_OBJECT | 
| 98 | public: | 
| 99 |     TreeModel() | 
| 100 |     { | 
| 101 |         for (int j = 0; j < 10; ++j) { | 
| 102 |             QStandardItem *parentItem = invisibleRootItem(); | 
| 103 |             for (int i = 0; i < 10; ++i) { | 
| 104 |                 const QString iS =  QString::number(i); | 
| 105 |                 const QString itemText = QLatin1String("item " ) + iS; | 
| 106 |                 QStandardItem *item = new QStandardItem(itemText); | 
| 107 |                 parentItem->appendRow(aitem: item); | 
| 108 |                 QStandardItem *item2 = new QStandardItem(itemText); | 
| 109 |                 parentItem->appendRow(aitem: item2); | 
| 110 |                 item2->appendRow(aitem: new QStandardItem(itemText)); | 
| 111 |                 parentItem->appendRow(aitem: new QStandardItem(QLatin1String("file " ) + iS)); | 
| 112 |                 parentItem = item; | 
| 113 |             } | 
| 114 |         } | 
| 115 |     } | 
| 116 |  | 
| 117 |     inline QModelIndex firstLevel() { return index(row: 0, column: 0, parent: QModelIndex()); } | 
| 118 |     inline QModelIndex secondLevel() { return index(row: 0, column: 0, parent: firstLevel()); } | 
| 119 |     inline QModelIndex thirdLevel() { return index(row: 0, column: 0, parent: secondLevel()); } | 
| 120 | }; | 
| 121 |  | 
| 122 | class ColumnView : public QColumnView | 
| 123 | { | 
| 124 |     Q_OBJECT | 
| 125 | public: | 
| 126 |     using QColumnView::QColumnView; | 
| 127 |     using QColumnView::horizontalOffset; | 
| 128 |     using QColumnView::clicked; | 
| 129 |     using QColumnView::isIndexHidden; | 
| 130 |     using QColumnView::moveCursor; | 
| 131 |     using QColumnView::scrollContentsBy; | 
| 132 |     using QColumnView::setSelection; | 
| 133 |     using QColumnView::visualRegionForSelection; | 
| 134 |  | 
| 135 |     friend class tst_QColumnView; | 
| 136 |  | 
| 137 |     QVector<QPointer<QAbstractItemView>> createdColumns; | 
| 138 |  | 
| 139 | protected: | 
| 140 |     QAbstractItemView *createColumn(const QModelIndex &index) override | 
| 141 |     { | 
| 142 |         QAbstractItemView *view = QColumnView::createColumn(rootIndex: index); | 
| 143 |         QPointer<QAbstractItemView> savedView = view; | 
| 144 |         createdColumns.append(t: savedView); | 
| 145 |         return view; | 
| 146 |     } | 
| 147 | }; | 
| 148 |  | 
| 149 | tst_QColumnView::tst_QColumnView() | 
| 150 | { | 
| 151 |     QStandardItem *homeItem = populateFakeDirModel(model: &m_fakeDirModel); | 
| 152 |     m_fakeDirHomeIndex = m_fakeDirModel.indexFromItem(item: homeItem); | 
| 153 | } | 
| 154 |  | 
| 155 | void tst_QColumnView::initTestCase() | 
| 156 | { | 
| 157 |     QVERIFY(m_fakeDirHomeIndex.isValid()); | 
| 158 |     QVERIFY(m_fakeDirModel.rowCount(m_fakeDirHomeIndex) > 1); // Needs some entries in 'home'. | 
| 159 | } | 
| 160 |  | 
| 161 | void tst_QColumnView::init() | 
| 162 | { | 
| 163 |     QGuiApplication::setLayoutDirection(Qt::LeftToRight); | 
| 164 | } | 
| 165 |  | 
| 166 | void tst_QColumnView::rootIndex() | 
| 167 | { | 
| 168 |     ColumnView view; | 
| 169 |     // no model | 
| 170 |     view.setRootIndex(QModelIndex()); | 
| 171 |  | 
| 172 |     TreeModel model; | 
| 173 |     view.setModel(&model); | 
| 174 |  | 
| 175 |     // A top level index | 
| 176 |     QModelIndex drive = model.firstLevel(); | 
| 177 |     QVERIFY(view.visualRect(drive).isValid()); | 
| 178 |     view.setRootIndex(QModelIndex()); | 
| 179 |     QCOMPARE(view.horizontalOffset(), 0); | 
| 180 |     QCOMPARE(view.rootIndex(), QModelIndex()); | 
| 181 |     QVERIFY(view.visualRect(drive).isValid()); | 
| 182 |  | 
| 183 |     // A item under the rootIndex exists | 
| 184 |     QModelIndex home = model.thirdLevel(); | 
| 185 |     QModelIndex homeFile = model.index(row: 0, column: 0, parent: home); | 
| 186 |     int i = 0; | 
| 187 |     while (i < model.rowCount(parent: home) - 1 && !model.hasChildren(parent: homeFile)) | 
| 188 |         homeFile = model.index(row: ++i, column: 0, parent: home); | 
| 189 |     view.setRootIndex(home); | 
| 190 |     QCOMPARE(view.horizontalOffset(), 0); | 
| 191 |     QCOMPARE(view.rootIndex(), home); | 
| 192 |     QVERIFY(!view.visualRect(drive).isValid()); | 
| 193 |     QVERIFY(!view.visualRect(home).isValid()); | 
| 194 |     if (homeFile.isValid()) | 
| 195 |         QVERIFY(view.visualRect(homeFile).isValid()); | 
| 196 |  | 
| 197 |     // set root when there already is one and everything should still be ok | 
| 198 |     view.setRootIndex(home); | 
| 199 |     view.setCurrentIndex(homeFile); | 
| 200 |     view.scrollTo(index: model.index(row: 0,column: 0, parent: homeFile)); | 
| 201 |     QCOMPARE(view.horizontalOffset(), 0); | 
| 202 |     QCOMPARE(view.rootIndex(), home); | 
| 203 |     QVERIFY(!view.visualRect(drive).isValid()); | 
| 204 |     QVERIFY(!view.visualRect(home).isValid()); | 
| 205 |      if (homeFile.isValid()) | 
| 206 |         QVERIFY(view.visualRect(homeFile).isValid()); | 
| 207 |  | 
| 208 |     // | 
| 209 |     homeFile = model.thirdLevel(); | 
| 210 |     home = homeFile.parent(); | 
| 211 |     view.setRootIndex(home); | 
| 212 |     view.setCurrentIndex(homeFile); | 
| 213 |     view.show(); | 
| 214 |     i = 0; | 
| 215 |     QModelIndex two = model.index(row: 0, column: 0, parent: homeFile); | 
| 216 |     while (i < model.rowCount(parent: homeFile) - 1 && !model.hasChildren(parent: two)) | 
| 217 |         two = model.index(row: ++i, column: 0, parent: homeFile); | 
| 218 |     QTest::qWait(ANIMATION_DELAY); | 
| 219 |     view.setCurrentIndex(two); | 
| 220 |     view.scrollTo(index: two); | 
| 221 |     QTest::qWait(ANIMATION_DELAY); | 
| 222 |     QVERIFY(two.isValid()); | 
| 223 |     QVERIFY(view.horizontalOffset() != 0); | 
| 224 |  | 
| 225 |     view.setRootIndex(homeFile); | 
| 226 |     QCOMPARE(view.horizontalOffset(), 0); | 
| 227 | } | 
| 228 |  | 
| 229 | void tst_QColumnView::grips() | 
| 230 | { | 
| 231 |     QColumnView view; | 
| 232 |     view.setModel(&m_fakeDirModel); | 
| 233 |     QCOMPARE(view.resizeGripsVisible(), true); | 
| 234 |  | 
| 235 |     view.setResizeGripsVisible(true); | 
| 236 |     QCOMPARE(view.resizeGripsVisible(), true); | 
| 237 |  | 
| 238 |     { | 
| 239 |         const QObjectList list = view.viewport()->children(); | 
| 240 |         for (QObject *obj : list) { | 
| 241 |             if (QAbstractItemView *view = qobject_cast<QAbstractItemView*>(object: obj)) | 
| 242 |                 QVERIFY(view->cornerWidget() != nullptr); | 
| 243 |         } | 
| 244 |     } | 
| 245 |     view.setResizeGripsVisible(false); | 
| 246 |     QCOMPARE(view.resizeGripsVisible(), false); | 
| 247 |  | 
| 248 |     { | 
| 249 |         const QObjectList list = view.viewport()->children(); | 
| 250 |         for (QObject *obj : list) { | 
| 251 |             if (QAbstractItemView *view = qobject_cast<QAbstractItemView*>(object: obj)) { | 
| 252 |                 if (view->isVisible()) | 
| 253 |                     QVERIFY(!view->cornerWidget()); | 
| 254 |             } | 
| 255 |         } | 
| 256 |     } | 
| 257 |  | 
| 258 |     view.setResizeGripsVisible(true); | 
| 259 |     QCOMPARE(view.resizeGripsVisible(), true); | 
| 260 | } | 
| 261 |  | 
| 262 | void tst_QColumnView::isIndexHidden() | 
| 263 | { | 
| 264 |     ColumnView view; | 
| 265 |     QModelIndex idx; | 
| 266 |     QCOMPARE(view.isIndexHidden(idx), false); | 
| 267 |     view.setModel(&m_fakeDirModel); | 
| 268 |     QCOMPARE(view.isIndexHidden(idx), false); | 
| 269 | } | 
| 270 |  | 
| 271 | void tst_QColumnView::indexAt() | 
| 272 | { | 
| 273 |     QColumnView view; | 
| 274 |     QCOMPARE(view.indexAt(QPoint(0,0)), QModelIndex()); | 
| 275 |     view.setModel(&m_fakeDirModel); | 
| 276 |  | 
| 277 |     QModelIndex homeFile = m_fakeDirModel.index(row: 0, column: 0, parent: m_fakeDirHomeIndex); | 
| 278 |     if (!homeFile.isValid()) | 
| 279 |         return; | 
| 280 |     view.setRootIndex(m_fakeDirHomeIndex); | 
| 281 |     QRect rect = view.visualRect(index: QModelIndex()); | 
| 282 |     QVERIFY(!rect.isValid()); | 
| 283 |     rect = view.visualRect(index: homeFile); | 
| 284 |     QVERIFY(rect.isValid()); | 
| 285 |  | 
| 286 |     QModelIndex child; | 
| 287 |     for (int i = 0; i < m_fakeDirModel.rowCount(parent: m_fakeDirHomeIndex); ++i) { | 
| 288 |         child = m_fakeDirModel.index(row: i, column: 0, parent: m_fakeDirHomeIndex); | 
| 289 |         rect = view.visualRect(index: child); | 
| 290 |         QVERIFY(rect.isValid()); | 
| 291 |         if (i > 0) | 
| 292 |             QVERIFY(rect.top() > 0); | 
| 293 |         QCOMPARE(view.indexAt(rect.center()), child); | 
| 294 |  | 
| 295 |         view.selectionModel()->select(index: child, command: QItemSelectionModel::SelectCurrent); | 
| 296 |         view.setCurrentIndex(child); | 
| 297 |         QTest::qWait(ms: 200); | 
| 298 |  | 
| 299 |         // test that the second row doesn't start at 0 | 
| 300 |         if (m_fakeDirModel.rowCount(parent: child) > 0) { | 
| 301 |             child = m_fakeDirModel.index(row: 0, column: 0, parent: child); | 
| 302 |             QVERIFY(child.isValid()); | 
| 303 |             rect = view.visualRect(index: child); | 
| 304 |             QVERIFY(rect.isValid()); | 
| 305 |             QVERIFY(rect.left() > 0); | 
| 306 |             QCOMPARE(view.indexAt(rect.center()), child); | 
| 307 |             break; | 
| 308 |         } | 
| 309 |     } | 
| 310 | } | 
| 311 |  | 
| 312 | void tst_QColumnView::scrollContentsBy_data() | 
| 313 | { | 
| 314 |     QTest::addColumn<bool>(name: "reverse" ); | 
| 315 |     QTest::newRow(dataTag: "normal" ) << false; | 
| 316 |     QTest::newRow(dataTag: "reverse" ) << true; | 
| 317 | } | 
| 318 |  | 
| 319 | void tst_QColumnView::scrollContentsBy() | 
| 320 | { | 
| 321 |     QFETCH(bool, reverse); | 
| 322 |     ColumnView view; | 
| 323 |     if (reverse) | 
| 324 |         view.setLayoutDirection(Qt::RightToLeft); | 
| 325 |     view.scrollContentsBy(dx: -1, dy: -1); | 
| 326 |     view.scrollContentsBy(dx: 0, dy: 0); | 
| 327 |  | 
| 328 |     TreeModel model; | 
| 329 |     view.setModel(&model); | 
| 330 |     view.scrollContentsBy(dx: 0, dy: 0); | 
| 331 |  | 
| 332 |     QModelIndex home = model.thirdLevel(); | 
| 333 |     view.setCurrentIndex(home); | 
| 334 |     QTest::qWait(ANIMATION_DELAY); | 
| 335 |     view.scrollContentsBy(dx: 0, dy: 0); | 
| 336 | } | 
| 337 |  | 
| 338 | void tst_QColumnView::scrollTo_data() | 
| 339 | { | 
| 340 |     QTest::addColumn<bool>(name: "reverse" ); | 
| 341 |     QTest::addColumn<bool>(name: "giveFocus" ); | 
| 342 |     /// ### add test later for giveFocus == true | 
| 343 |     QTest::newRow(dataTag: "normal" ) << false << false; | 
| 344 |     QTest::newRow(dataTag: "reverse" ) << true << false; | 
| 345 | } | 
| 346 |  | 
| 347 | void tst_QColumnView::scrollTo() | 
| 348 | { | 
| 349 |     if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland" ), cs: Qt::CaseInsensitive)) | 
| 350 |         QSKIP("Wayland: This fails. Figure out why." ); | 
| 351 |  | 
| 352 |     QFETCH(bool, reverse); | 
| 353 |     QFETCH(bool, giveFocus); | 
| 354 |     QWidget topLevel; | 
| 355 |     if (reverse) | 
| 356 |         topLevel.setLayoutDirection(Qt::RightToLeft); | 
| 357 |     ColumnView view(&topLevel); | 
| 358 |     view.resize(w: 200, h: 200); | 
| 359 |     topLevel.show(); | 
| 360 |     topLevel.activateWindow(); | 
| 361 |     QTestPrivate::centerOnScreen(w: &topLevel); | 
| 362 |     QVERIFY(QTest::qWaitForWindowActive(&topLevel)); | 
| 363 |  | 
| 364 |     view.scrollTo(index: QModelIndex(), hint: QAbstractItemView::EnsureVisible); | 
| 365 |     QCOMPARE(view.horizontalOffset(), 0); | 
| 366 |  | 
| 367 |     TreeModel model; | 
| 368 |     view.setModel(&model); | 
| 369 |     view.scrollTo(index: QModelIndex(), hint: QAbstractItemView::EnsureVisible); | 
| 370 |  | 
| 371 |     QModelIndex home; | 
| 372 |     home = model.index(row: 0, column: 0, parent: home); | 
| 373 |     home = model.index(row: 0, column: 0, parent: home); | 
| 374 |     home = model.index(row: 0, column: 0, parent: home); | 
| 375 |     view.scrollTo(index: home, hint: QAbstractItemView::EnsureVisible); | 
| 376 |     view.setRootIndex(home); | 
| 377 |  | 
| 378 |     QModelIndex index = model.index(row: 0, column: 0, parent: home); | 
| 379 |     view.scrollTo(index, hint: QAbstractItemView::EnsureVisible); | 
| 380 |     QCOMPARE(view.horizontalOffset(), 0); | 
| 381 |  | 
| 382 |     // Embedded requires that at least one widget have focus | 
| 383 |     QWidget w; | 
| 384 |     w.show(); | 
| 385 |  | 
| 386 |     QCOMPARE(view.horizontalOffset(), 0); | 
| 387 |     if (giveFocus) | 
| 388 |         view.setFocus(Qt::OtherFocusReason); | 
| 389 |     else | 
| 390 |         view.clearFocus(); | 
| 391 |  | 
| 392 |     QCOMPARE(view.horizontalOffset(), 0); | 
| 393 |     QCoreApplication::processEvents(); | 
| 394 |     QCOMPARE(view.horizontalOffset(), 0); | 
| 395 |     QTRY_COMPARE(view.hasFocus(), giveFocus); | 
| 396 |     // scroll to the right | 
| 397 |     int level = 0; | 
| 398 |     int last = view.horizontalOffset(); | 
| 399 |     while (model.hasChildren(parent: index) && level < 5) { | 
| 400 |         view.setCurrentIndex(index); | 
| 401 |         QTest::qWait(ANIMATION_DELAY); | 
| 402 |         view.scrollTo(index, hint: QAbstractItemView::EnsureVisible); | 
| 403 |         QTest::qWait(ANIMATION_DELAY); | 
| 404 |         index = model.index(row: 0, column: 0, parent: index); | 
| 405 |         level++; | 
| 406 |         if (level >= 2) { | 
| 407 |             if (!reverse) { | 
| 408 |                 QTRY_VERIFY(view.horizontalOffset() < 0); | 
| 409 |                 qDebug() << "last="  << last | 
| 410 |                              << " ; horizontalOffset= "  << view.horizontalOffset(); | 
| 411 |                 QTRY_VERIFY(last > view.horizontalOffset()); | 
| 412 |             } else { | 
| 413 |                 QTRY_VERIFY(view.horizontalOffset() > 0); | 
| 414 |                 QTRY_VERIFY(last < view.horizontalOffset()); | 
| 415 |             } | 
| 416 |         } | 
| 417 |         last = view.horizontalOffset(); | 
| 418 |     } | 
| 419 |  | 
| 420 |     // scroll to the left | 
| 421 |     int start = level; | 
| 422 |     while(index.parent().isValid() && index != view.rootIndex()) { | 
| 423 |         view.setCurrentIndex(index); | 
| 424 |         QTest::qWait(ANIMATION_DELAY); | 
| 425 |         view.scrollTo(index, hint: QAbstractItemView::EnsureVisible); | 
| 426 |         index = index.parent(); | 
| 427 |         if (start != level) { | 
| 428 |             if (!reverse) { | 
| 429 |                 QTRY_VERIFY(last < view.horizontalOffset()); | 
| 430 |             } else { | 
| 431 |                 if (last <= view.horizontalOffset()) { | 
| 432 |                     qDebug() << "Test failure. last="  << last | 
| 433 |                              << " ; horizontalOffset= "  << view.horizontalOffset(); | 
| 434 |                 } | 
| 435 |                 QTRY_VERIFY(last > view.horizontalOffset()); | 
| 436 |             } | 
| 437 |         } | 
| 438 |         level--; | 
| 439 |         last = view.horizontalOffset(); | 
| 440 |     } | 
| 441 |     // It shouldn't automatically steal focus if it doesn't have it | 
| 442 |     QTRY_COMPARE(view.hasFocus(), giveFocus); | 
| 443 |  | 
| 444 |     // Try scrolling to something that is above the root index | 
| 445 |     home = model.index(row: 0, column: 0, parent: QModelIndex()); | 
| 446 |     QModelIndex temp = model.index(row: 1, column: 0, parent: home); | 
| 447 |     home = model.index(row: 0, column: 0, parent: home); | 
| 448 |     home = model.index(row: 0, column: 0, parent: home); | 
| 449 |     view.setRootIndex(home); | 
| 450 |     view.scrollTo(index: model.index(row: 0, column: 0, parent: home)); | 
| 451 |     QTest::qWait(ANIMATION_DELAY); | 
| 452 |     view.scrollTo(index: temp); | 
| 453 | } | 
| 454 |  | 
| 455 | void tst_QColumnView::moveCursor_data() | 
| 456 | { | 
| 457 |     QTest::addColumn<bool>(name: "reverse" ); | 
| 458 |     QTest::newRow(dataTag: "normal" ) << false; | 
| 459 |     QTest::newRow(dataTag: "reverse" ) << true; | 
| 460 | } | 
| 461 |  | 
| 462 | void tst_QColumnView::moveCursor() | 
| 463 | { | 
| 464 |     QFETCH(bool, reverse); | 
| 465 |     ColumnView view; | 
| 466 |     if (reverse) | 
| 467 |         view.setLayoutDirection(Qt::RightToLeft); | 
| 468 |     // don't crash | 
| 469 |     view.moveCursor(cursorAction: ColumnView::MoveUp, modifiers: Qt::NoModifier); | 
| 470 |  | 
| 471 |     // don't do anything | 
| 472 |     QCOMPARE(view.moveCursor(ColumnView::MoveEnd, Qt::NoModifier), QModelIndex()); | 
| 473 |  | 
| 474 |     view.setModel(&m_fakeDirModel); | 
| 475 |     QModelIndex ci = view.currentIndex(); | 
| 476 |     QCOMPARE(view.moveCursor(ColumnView::MoveUp, Qt::NoModifier), QModelIndex()); | 
| 477 |     QCOMPARE(view.moveCursor(ColumnView::MoveDown, Qt::NoModifier), QModelIndex()); | 
| 478 |  | 
| 479 |     // left at root | 
| 480 |     view.setCurrentIndex(m_fakeDirModel.index(row: 0,column: 0)); | 
| 481 |     ColumnView::CursorAction action = reverse ? ColumnView::MoveRight : ColumnView::MoveLeft; | 
| 482 |     QCOMPARE(view.moveCursor(action, Qt::NoModifier), m_fakeDirModel.index(0,0)); | 
| 483 |  | 
| 484 |     // left shouldn't move up | 
| 485 |     int i = 0; | 
| 486 |     ci = m_fakeDirModel.index(row: 0, column: 0); | 
| 487 |     while (i < m_fakeDirModel.rowCount() - 1 && !m_fakeDirModel.hasChildren(parent: ci)) | 
| 488 |         ci = m_fakeDirModel.index(row: ++i, column: 0); | 
| 489 |     QVERIFY(m_fakeDirModel.hasChildren(ci)); | 
| 490 |     view.setCurrentIndex(ci); | 
| 491 |     action = reverse ? ColumnView::MoveRight : ColumnView::MoveLeft; | 
| 492 |     QCOMPARE(view.moveCursor(action, Qt::NoModifier), ci); | 
| 493 |  | 
| 494 |     // now move to the left (i.e. move over one column) | 
| 495 |     view.setCurrentIndex(m_fakeDirHomeIndex); | 
| 496 |     QCOMPARE(view.moveCursor(action, Qt::NoModifier), m_fakeDirHomeIndex.parent()); | 
| 497 |  | 
| 498 |     // right | 
| 499 |     action = reverse ? ColumnView::MoveLeft : ColumnView::MoveRight; | 
| 500 |     view.setCurrentIndex(ci); | 
| 501 |     QModelIndex mc = view.moveCursor(cursorAction: action, modifiers: Qt::NoModifier); | 
| 502 |     QCOMPARE(mc, m_fakeDirModel.index(0,0, ci)); | 
| 503 |  | 
| 504 |     // for empty directories (no way to go 'right'), next one should move down | 
| 505 |     QModelIndex idx = m_fakeDirModel.index(row: 0, column: 0, parent: ci); | 
| 506 |     const int rowCount = m_fakeDirModel.rowCount(parent: ci); | 
| 507 |     while (m_fakeDirModel.hasChildren(parent: idx) && rowCount > idx.row() + 1) | 
| 508 |         idx = idx.sibling(arow: idx.row() + 1, acolumn: idx.column()); | 
| 509 |     static const char error[]  = "This test requires an empty directory followed by another directory." ; | 
| 510 |     QVERIFY2(idx.isValid(), error); | 
| 511 |     QVERIFY2(!m_fakeDirModel.hasChildren(idx), error); | 
| 512 |     QVERIFY2(idx.row() + 1 < rowCount, error); | 
| 513 |     view.setCurrentIndex(idx); | 
| 514 |     mc = view.moveCursor(cursorAction: action, modifiers: Qt::NoModifier); | 
| 515 |     QCOMPARE(mc, idx.sibling(idx.row() + 1, idx.column())); | 
| 516 | } | 
| 517 |  | 
| 518 | void tst_QColumnView::selectAll() | 
| 519 | { | 
| 520 |     ColumnView view; | 
| 521 |     view.selectAll(); | 
| 522 |  | 
| 523 |     view.setModel(&m_fakeDirModel); | 
| 524 |     view.selectAll(); | 
| 525 |     QVERIFY(view.selectionModel()->selectedIndexes().count() >= 0); | 
| 526 |  | 
| 527 |     view.setCurrentIndex(m_fakeDirHomeIndex); | 
| 528 |     view.selectAll(); | 
| 529 |     QVERIFY(view.selectionModel()->selectedIndexes().count() > 0); | 
| 530 |  | 
| 531 |     QModelIndex file; | 
| 532 |     for (int i = 0; i < m_fakeDirModel.rowCount(parent: m_fakeDirHomeIndex); ++i) { | 
| 533 |         if (!m_fakeDirModel.hasChildren(parent: m_fakeDirModel.index(row: i, column: 0, parent: m_fakeDirHomeIndex))) { | 
| 534 |             file = m_fakeDirModel.index(row: i, column: 0, parent: m_fakeDirHomeIndex); | 
| 535 |             break; | 
| 536 |         } | 
| 537 |     } | 
| 538 |     view.setCurrentIndex(file); | 
| 539 |     view.selectAll(); | 
| 540 |     QVERIFY(view.selectionModel()->selectedIndexes().count() > 0); | 
| 541 |  | 
| 542 |     view.setCurrentIndex(QModelIndex()); | 
| 543 |     QCOMPARE(view.selectionModel()->selectedIndexes().count(), 0); | 
| 544 | } | 
| 545 |  | 
| 546 | void tst_QColumnView::clicked() | 
| 547 | { | 
| 548 |     ColumnView view; | 
| 549 |  | 
| 550 |     view.setModel(&m_fakeDirModel); | 
| 551 |     view.resize(w: 800, h: 300); | 
| 552 |     view.show(); | 
| 553 |  | 
| 554 |     view.setCurrentIndex(m_fakeDirHomeIndex); | 
| 555 |     QTest::qWait(ANIMATION_DELAY); | 
| 556 |  | 
| 557 |     QModelIndex parent = m_fakeDirHomeIndex.parent(); | 
| 558 |     QVERIFY(parent.isValid()); | 
| 559 |  | 
| 560 |     QSignalSpy clickedSpy(&view, &QAbstractItemView::clicked); | 
| 561 |  | 
| 562 |     QPoint localPoint = view.visualRect(index: m_fakeDirHomeIndex).center(); | 
| 563 |     QTest::mouseClick(widget: view.viewport(), button: Qt::LeftButton, stateKey: {}, pos: localPoint); | 
| 564 |     QCOMPARE(clickedSpy.count(), 1); | 
| 565 |     QCoreApplication::processEvents(); | 
| 566 |  | 
| 567 |     if (sizeof(qreal) != sizeof(double)) | 
| 568 |         QSKIP("Skipped due to rounding errors" ); | 
| 569 |  | 
| 570 |     for (int i = 0; i < view.createdColumns.count(); ++i) { | 
| 571 |         QAbstractItemView *column = view.createdColumns.at(i); | 
| 572 |         if (column && column->selectionModel() && (column->rootIndex() == m_fakeDirHomeIndex)) | 
| 573 |                 QVERIFY(column->selectionModel()->selectedIndexes().isEmpty()); | 
| 574 |     } | 
| 575 | } | 
| 576 |  | 
| 577 | void tst_QColumnView::selectedColumns() | 
| 578 | { | 
| 579 |     ColumnView view; | 
| 580 |     view.setModel(&m_fakeDirModel); | 
| 581 |     view.resize(w: 800,h: 300); | 
| 582 |     view.show(); | 
| 583 |  | 
| 584 |     view.setCurrentIndex(m_fakeDirHomeIndex); | 
| 585 |  | 
| 586 |     QTest::qWait(ANIMATION_DELAY); | 
| 587 |  | 
| 588 |     for (int i = 0; i < view.createdColumns.count(); ++i) { | 
| 589 |         QAbstractItemView *column = view.createdColumns.at(i); | 
| 590 |         if (!column) | 
| 591 |             continue; | 
| 592 |         if (!column->rootIndex().isValid() || column->rootIndex() == m_fakeDirHomeIndex) | 
| 593 |             continue; | 
| 594 |         QTRY_VERIFY(column->currentIndex().isValid()); | 
| 595 |     } | 
| 596 | } | 
| 597 |  | 
| 598 | void tst_QColumnView::setSelection() | 
| 599 | { | 
| 600 |     ColumnView view; | 
| 601 |     // shouldn't do anything, it falls to the columns to handle this | 
| 602 |     QRect r; | 
| 603 |     view.setSelection(rect: r, command: QItemSelectionModel::NoUpdate); | 
| 604 | } | 
| 605 |  | 
| 606 | void tst_QColumnView::setSelectionModel() | 
| 607 | { | 
| 608 |     ColumnView view; | 
| 609 |     view.setModel(&m_fakeDirModel); | 
| 610 |     view.show(); | 
| 611 |  | 
| 612 |     view.setCurrentIndex(m_fakeDirHomeIndex); | 
| 613 |     QTest::qWait(ANIMATION_DELAY); | 
| 614 |  | 
| 615 |     QItemSelectionModel *selectionModel = new QItemSelectionModel(&m_fakeDirModel); | 
| 616 |     view.setSelectionModel(selectionModel); | 
| 617 |  | 
| 618 |     bool found = false; | 
| 619 |     for (int i = 0; i < view.createdColumns.count(); ++i) { | 
| 620 |         if (view.createdColumns.at(i)->selectionModel() == selectionModel) { | 
| 621 |             found = true; | 
| 622 |             break; | 
| 623 |         } | 
| 624 |     } | 
| 625 |     QVERIFY(found); | 
| 626 | } | 
| 627 |  | 
| 628 | void tst_QColumnView::visualRegionForSelection() | 
| 629 | { | 
| 630 |     ColumnView view; | 
| 631 |     QItemSelection emptyItemSelection; | 
| 632 |     QCOMPARE(QRegion(), view.visualRegionForSelection(emptyItemSelection)); | 
| 633 |  | 
| 634 |     // a region that isn't empty | 
| 635 |     view.setModel(&m_fakeDirModel); | 
| 636 |  | 
| 637 |  | 
| 638 |     QItemSelection itemSelection(m_fakeDirModel.index(row: 0, column: 0, parent: m_fakeDirHomeIndex), m_fakeDirModel.index(row: m_fakeDirModel.rowCount(parent: m_fakeDirHomeIndex) - 1, column: 0, parent: m_fakeDirHomeIndex)); | 
| 639 |     QVERIFY(QRegion() != view.visualRegionForSelection(itemSelection)); | 
| 640 | } | 
| 641 |  | 
| 642 | void tst_QColumnView::moveGrip_basic() | 
| 643 | { | 
| 644 |     QColumnView view; | 
| 645 |     QColumnViewGrip *grip = new QColumnViewGrip(&view); | 
| 646 |     QSignalSpy spy(grip, &QColumnViewGrip::gripMoved); | 
| 647 |     view.setCornerWidget(grip); | 
| 648 |     int oldX = view.width(); | 
| 649 |     grip->moveGrip(offset: 10); | 
| 650 |     QCOMPARE(oldX + 10, view.width()); | 
| 651 |     grip->moveGrip(offset: -10); | 
| 652 |     QCOMPARE(oldX, view.width()); | 
| 653 |     grip->moveGrip(offset: -800); | 
| 654 |     QVERIFY(view.width() == 0 || view.width() == 1); | 
| 655 |     grip->moveGrip(offset: 800); | 
| 656 |     view.setMinimumWidth(200); | 
| 657 |     grip->moveGrip(offset: -800); | 
| 658 |     QCOMPARE(view.width(), 200); | 
| 659 |     QCOMPARE(spy.count(), 5); | 
| 660 | } | 
| 661 |  | 
| 662 | void tst_QColumnView::moveGrip_data() | 
| 663 | { | 
| 664 |     QTest::addColumn<bool>(name: "reverse" ); | 
| 665 |     QTest::newRow(dataTag: "normal" ) << false; | 
| 666 |     QTest::newRow(dataTag: "reverse" ) << true; | 
| 667 | } | 
| 668 |  | 
| 669 | void tst_QColumnView::moveGrip() | 
| 670 | { | 
| 671 |     if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland" ), cs: Qt::CaseInsensitive)) | 
| 672 |         QSKIP("Wayland: This fails. Figure out why." ); | 
| 673 |  | 
| 674 |     QFETCH(bool, reverse); | 
| 675 |     QWidget topLevel; | 
| 676 |     if (reverse) | 
| 677 |         topLevel.setLayoutDirection(Qt::RightToLeft); | 
| 678 |     ColumnView view(&topLevel); | 
| 679 |     TreeModel model; | 
| 680 |     view.setModel(&model); | 
| 681 |     QModelIndex home = model.thirdLevel(); | 
| 682 |     view.setCurrentIndex(home); | 
| 683 |     view.resize(w: 640, h: 200); | 
| 684 |     topLevel.show(); | 
| 685 |     QVERIFY(QTest::qWaitForWindowActive(&topLevel)); | 
| 686 |  | 
| 687 |     int columnNum = view.createdColumns.count() - 2; | 
| 688 |     QVERIFY(columnNum >= 0); | 
| 689 |     const QObjectList list = view.createdColumns[columnNum]->children(); | 
| 690 |     QColumnViewGrip *grip = nullptr; | 
| 691 |     for (QObject *obj : list) { | 
| 692 |         if ((grip = qobject_cast<QColumnViewGrip *>(object: obj))) | 
| 693 |             break; | 
| 694 |     } | 
| 695 |     if (!grip) | 
| 696 |         return; | 
| 697 |  | 
| 698 |     QAbstractItemView *column = qobject_cast<QAbstractItemView *>(object: grip->parent()); | 
| 699 |     int oldX = column->width(); | 
| 700 |     QCOMPARE(view.columnWidths().value(columnNum), oldX); | 
| 701 |     grip->moveGrip(offset: 10); | 
| 702 |     QCOMPARE(view.columnWidths().value(columnNum), (oldX + (reverse ? -10 : 10))); | 
| 703 | } | 
| 704 |  | 
| 705 | void tst_QColumnView::doubleClick() | 
| 706 | { | 
| 707 |     QColumnView view; | 
| 708 |     QColumnViewGrip *grip = new QColumnViewGrip(&view); | 
| 709 |     QSignalSpy spy(grip, &QColumnViewGrip::gripMoved); | 
| 710 |     view.setCornerWidget(grip); | 
| 711 |     view.resize(w: 200, h: 200); | 
| 712 |     QCOMPARE(view.width(), 200); | 
| 713 |     QTest::mouseDClick(widget: grip, button: Qt::LeftButton); | 
| 714 |     QCOMPARE(view.width(), view.sizeHint().width()); | 
| 715 |     QCOMPARE(spy.count(), 1); | 
| 716 | } | 
| 717 |  | 
| 718 | void tst_QColumnView::gripMoved() | 
| 719 | { | 
| 720 |     QColumnView view; | 
| 721 |     QColumnViewGrip *grip = new QColumnViewGrip(&view); | 
| 722 |     QSignalSpy spy(grip, &QColumnViewGrip::gripMoved); | 
| 723 |     view.setCornerWidget(grip); | 
| 724 |     view.move(ax: 300, ay: 300); | 
| 725 |     view.resize(w: 200, h: 200); | 
| 726 |     QCoreApplication::processEvents(); | 
| 727 |  | 
| 728 |     int oldWidth = view.width(); | 
| 729 |  | 
| 730 |     QTest::mousePress(widget: grip, button: Qt::LeftButton, stateKey: {}, pos: QPoint(1, 1)); | 
| 731 |     //QTest::mouseMove(grip, QPoint(grip->globalX()+50, y)); | 
| 732 |  | 
| 733 |     QPoint posNew = QPoint(grip->mapToGlobal(QPoint(1, 1)).x() + 65, 0); | 
| 734 |     QMouseEvent *event = new QMouseEvent(QEvent::MouseMove, posNew, posNew, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier); | 
| 735 |     QCoreApplication::postEvent(receiver: grip, event); | 
| 736 |     QCoreApplication::processEvents(); | 
| 737 |     QTest::mouseRelease(widget: grip, button: Qt::LeftButton); | 
| 738 |  | 
| 739 |     QTRY_COMPARE(spy.count(), 1); | 
| 740 |     QCOMPARE(view.width(), oldWidth + 65); | 
| 741 | } | 
| 742 |  | 
| 743 | void tst_QColumnView::preview() | 
| 744 | { | 
| 745 |     QColumnView view; | 
| 746 |     QCOMPARE(view.previewWidget(), nullptr); | 
| 747 |     TreeModel model; | 
| 748 |     view.setModel(&model); | 
| 749 |     QCOMPARE(view.previewWidget(), nullptr); | 
| 750 |     QModelIndex home = model.index(row: 0, column: 0); | 
| 751 |     QVERIFY(home.isValid()); | 
| 752 |     QVERIFY(model.hasChildren(home)); | 
| 753 |     view.setCurrentIndex(home); | 
| 754 |     QCOMPARE(view.previewWidget(), nullptr); | 
| 755 |  | 
| 756 |     QModelIndex file; | 
| 757 |     QVERIFY(model.rowCount(home) > 0); | 
| 758 |     for (int i = 0; i < model.rowCount(parent: home); ++i) { | 
| 759 |         if (!model.hasChildren(parent: model.index(row: i, column: 0, parent: home))) { | 
| 760 |             file = model.index(row: i, column: 0, parent: home); | 
| 761 |             break; | 
| 762 |         } | 
| 763 |     } | 
| 764 |     QVERIFY(file.isValid()); | 
| 765 |     view.setCurrentIndex(file); | 
| 766 |     QVERIFY(view.previewWidget() != nullptr); | 
| 767 |  | 
| 768 |     QWidget *previewWidget = new QWidget(&view); | 
| 769 |     view.setPreviewWidget(previewWidget); | 
| 770 |     QCOMPARE(view.previewWidget(), previewWidget); | 
| 771 |     QVERIFY(previewWidget->parent() != &view); | 
| 772 |     view.setCurrentIndex(home); | 
| 773 |  | 
| 774 |     // previewWidget should be marked for deletion | 
| 775 |     QWidget *previewWidget2 = new QWidget(&view); | 
| 776 |     view.setPreviewWidget(previewWidget2); | 
| 777 |     QCOMPARE(view.previewWidget(), previewWidget2); | 
| 778 | } | 
| 779 |  | 
| 780 | void tst_QColumnView::swapPreview() | 
| 781 | { | 
| 782 |     // swap the preview widget in updatePreviewWidget | 
| 783 |     QColumnView view; | 
| 784 |     QStringListModel model({ QLatin1String("test" ) }); | 
| 785 |     view.setModel(&model); | 
| 786 |     view.setCurrentIndex(view.indexAt(point: QPoint(1, 1))); | 
| 787 |     connect(sender: &view, signal: &QColumnView::updatePreviewWidget, | 
| 788 |             receiver: this, slot: &tst_QColumnView::setPreviewWidget); | 
| 789 |     view.setCurrentIndex(view.indexAt(point: QPoint(1, 1))); | 
| 790 |     QTest::qWait(ANIMATION_DELAY); | 
| 791 |     QCoreApplication::processEvents(); | 
| 792 | } | 
| 793 |  | 
| 794 | void tst_QColumnView::setPreviewWidget() | 
| 795 | { | 
| 796 |     auto ptr = qobject_cast<QColumnView *>(object: sender()); | 
| 797 |     QVERIFY(ptr); | 
| 798 |     ptr->setPreviewWidget(new QWidget); | 
| 799 | } | 
| 800 |  | 
| 801 | void tst_QColumnView::sizes() | 
| 802 | { | 
| 803 |     QColumnView view; | 
| 804 |     QCOMPARE(view.columnWidths().count(), 0); | 
| 805 |  | 
| 806 |     const QList<int> newSizes{ 10, 4, 50, 6 }; | 
| 807 |  | 
| 808 |     QList<int> visibleSizes; | 
| 809 |     view.setColumnWidths(newSizes); | 
| 810 |     QCOMPARE(view.columnWidths(), visibleSizes); | 
| 811 |  | 
| 812 |     view.setModel(&m_fakeDirModel); | 
| 813 |     view.setCurrentIndex(m_fakeDirHomeIndex); | 
| 814 |  | 
| 815 |     QList<int> postSizes = view.columnWidths().mid(pos: 0, alength: newSizes.count()); | 
| 816 |     QCOMPARE(postSizes, newSizes.mid(0, postSizes.count())); | 
| 817 |  | 
| 818 |     QVERIFY(view.columnWidths().count() > 1); | 
| 819 |     QList<int> smallerSizes{ 6 }; | 
| 820 |     view.setColumnWidths(smallerSizes); | 
| 821 |     QList<int> expectedSizes = newSizes; | 
| 822 |     expectedSizes[0] = 6; | 
| 823 |     postSizes = view.columnWidths().mid(pos: 0, alength: newSizes.count()); | 
| 824 |     QCOMPARE(postSizes, expectedSizes.mid(0, postSizes.count())); | 
| 825 | } | 
| 826 |  | 
| 827 | void tst_QColumnView::rowDelegate() | 
| 828 | { | 
| 829 |     ColumnView view; | 
| 830 |     QStyledItemDelegate *d = new QStyledItemDelegate; | 
| 831 |     view.setItemDelegateForRow(row: 3, delegate: d); | 
| 832 |  | 
| 833 |     view.setModel(&m_fakeDirModel); | 
| 834 |     for (int i = 0; i < view.createdColumns.count(); ++i) { | 
| 835 |         QAbstractItemView *column = view.createdColumns.at(i); | 
| 836 |         QCOMPARE(column->itemDelegateForRow(3), d); | 
| 837 |     } | 
| 838 |     delete d; | 
| 839 | } | 
| 840 |  | 
| 841 | void tst_QColumnView::resize() | 
| 842 | { | 
| 843 |     QWidget topLevel; | 
| 844 |     ColumnView view(&topLevel); | 
| 845 |     view.setModel(&m_fakeDirModel); | 
| 846 |     view.resize(w: 200, h: 200); | 
| 847 |  | 
| 848 |     topLevel.show(); | 
| 849 |     view.setCurrentIndex(m_fakeDirHomeIndex); | 
| 850 |     QTest::qWait(ANIMATION_DELAY); | 
| 851 |     view.resize(w: 200, h: 300); | 
| 852 |     QTest::qWait(ANIMATION_DELAY); | 
| 853 |  | 
| 854 |     QVERIFY(view.horizontalScrollBar()->maximum() != 0); | 
| 855 |     view.resize(w: view.horizontalScrollBar()->maximum() * 10, h: 300); | 
| 856 |     QTest::qWait(ANIMATION_DELAY); | 
| 857 |     QVERIFY(view.horizontalScrollBar()->maximum() <= 0); | 
| 858 | } | 
| 859 |  | 
| 860 | void tst_QColumnView::changeSameColumn() | 
| 861 | { | 
| 862 |     ColumnView view; | 
| 863 |     TreeModel model; | 
| 864 |     view.setModel(&model); | 
| 865 |     QModelIndex second; | 
| 866 |  | 
| 867 |     QModelIndex home = model.secondLevel(); | 
| 868 |     //index(QDir::homePath()); | 
| 869 |     view.setCurrentIndex(home); | 
| 870 |     for (int i = 0; i < model.rowCount(parent: home.parent()); ++i) { | 
| 871 |         QModelIndex idx = model.index(row: i, column: 0, parent: home.parent()); | 
| 872 |         if (model.hasChildren(parent: idx) && idx != home) { | 
| 873 |             second = idx; | 
| 874 |             break; | 
| 875 |         } | 
| 876 |     } | 
| 877 |     QVERIFY(second.isValid()); | 
| 878 |  | 
| 879 |     const auto old = view.createdColumns; | 
| 880 |     view.setCurrentIndex(second); | 
| 881 |  | 
| 882 |     QCOMPARE(old, view.createdColumns); | 
| 883 | } | 
| 884 |  | 
| 885 | void tst_QColumnView::parentCurrentIndex_data() | 
| 886 | { | 
| 887 |     QTest::addColumn<int>(name: "firstRow" ); | 
| 888 |     QTest::addColumn<int>(name: "secondRow" ); | 
| 889 |     QTest::newRow(dataTag: "down" ) << 0 << 1; | 
| 890 |     QTest::newRow(dataTag: "up" ) << 1 << 0; | 
| 891 | } | 
| 892 |  | 
| 893 | void tst_QColumnView::parentCurrentIndex() | 
| 894 | { | 
| 895 |     QFETCH(int, firstRow); | 
| 896 |     QFETCH(int, secondRow); | 
| 897 |  | 
| 898 |     ColumnView view; | 
| 899 |     TreeModel model; | 
| 900 |     view.setModel(&model); | 
| 901 |     view.show(); | 
| 902 |  | 
| 903 |     QModelIndex first; | 
| 904 |     QModelIndex second; | 
| 905 |     QModelIndex third; | 
| 906 |     first = model.index(row: 0, column: 0, parent: QModelIndex()); | 
| 907 |     second = model.index(row: firstRow, column: 0, parent: first); | 
| 908 |     third = model.index(row: 0, column: 0, parent: second); | 
| 909 |     QVERIFY(first.isValid()); | 
| 910 |     QVERIFY(second.isValid()); | 
| 911 |     QVERIFY(third.isValid()); | 
| 912 |     view.setCurrentIndex(third); | 
| 913 |     QTRY_COMPARE(view.createdColumns[0]->currentIndex(), first); | 
| 914 |     QTRY_COMPARE(view.createdColumns[1]->currentIndex(), second); | 
| 915 |     QTRY_COMPARE(view.createdColumns[2]->currentIndex(), third); | 
| 916 |  | 
| 917 |     first = model.index(row: 0, column: 0, parent: QModelIndex()); | 
| 918 |     second = model.index(row: secondRow, column: 0, parent: first); | 
| 919 |     third = model.index(row: 0, column: 0, parent: second); | 
| 920 |     QVERIFY(first.isValid()); | 
| 921 |     QVERIFY(second.isValid()); | 
| 922 |     QVERIFY(third.isValid()); | 
| 923 |     view.setCurrentIndex(third); | 
| 924 |     QTRY_COMPARE(view.createdColumns[0]->currentIndex(), first); | 
| 925 |     QTRY_COMPARE(view.createdColumns[1]->currentIndex(), second); | 
| 926 |  | 
| 927 | #ifndef Q_OS_WINRT | 
| 928 |     // The next two lines should be removed when QTBUG-22707 is resolved. | 
| 929 |     QEXPECT_FAIL("" , "QTBUG-22707" , Abort); | 
| 930 | #endif | 
| 931 |     QVERIFY(view.createdColumns[2]); | 
| 932 |  | 
| 933 |     QTRY_COMPARE(view.createdColumns[2]->currentIndex(), third); | 
| 934 | } | 
| 935 |  | 
| 936 | void tst_QColumnView::pullRug_data() | 
| 937 | { | 
| 938 |     QTest::addColumn<bool>(name: "removeModel" ); | 
| 939 |     QTest::newRow(dataTag: "model" ) << true; | 
| 940 |     QTest::newRow(dataTag: "index" ) << false; | 
| 941 | } | 
| 942 |  | 
| 943 | void tst_QColumnView::pullRug() | 
| 944 | { | 
| 945 |     QFETCH(bool, removeModel); | 
| 946 |     ColumnView view; | 
| 947 |     TreeModel model; | 
| 948 |     view.setModel(&model); | 
| 949 |     QModelIndex home = model.thirdLevel(); | 
| 950 |     view.setCurrentIndex(home); | 
| 951 |     if (removeModel) | 
| 952 |         view.setModel(nullptr); | 
| 953 |     else | 
| 954 |         view.setCurrentIndex(QModelIndex()); | 
| 955 |     QTest::qWait(ANIMATION_DELAY); | 
| 956 |     // don't crash | 
| 957 | } | 
| 958 |  | 
| 959 | void tst_QColumnView::dynamicModelChanges() | 
| 960 | { | 
| 961 |     struct MyItemDelegate : public QStyledItemDelegate | 
| 962 |     { | 
| 963 |         void paint(QPainter *painter, | 
| 964 |                    const QStyleOptionViewItem &option, | 
| 965 |                    const QModelIndex &index) const override | 
| 966 |         { | 
| 967 |             paintedIndexes += index; | 
| 968 |             QStyledItemDelegate::paint(painter, option, index); | 
| 969 |         } | 
| 970 |  | 
| 971 |         mutable QSet<QModelIndex> paintedIndexes; | 
| 972 |  | 
| 973 |     } delegate; | 
| 974 |     QStandardItemModel model; | 
| 975 |     ColumnView view; | 
| 976 |     view.setModel(&model); | 
| 977 |     view.setItemDelegate(&delegate); | 
| 978 |     QTestPrivate::centerOnScreen(w: &view); | 
| 979 |     view.show(); | 
| 980 |  | 
| 981 |     QStandardItem *item = new QStandardItem(QLatin1String("item" )); | 
| 982 |     model.appendRow(aitem: item); | 
| 983 |  | 
| 984 |     QVERIFY(QTest::qWaitForWindowExposed(&view)); //let the time for painting to occur | 
| 985 |     QTRY_COMPARE(delegate.paintedIndexes.count(), 1); | 
| 986 |     QCOMPARE(*delegate.paintedIndexes.begin(), model.index(0,0)); | 
| 987 | } | 
| 988 |  | 
| 989 |  | 
| 990 | QTEST_MAIN(tst_QColumnView) | 
| 991 | #include "tst_qcolumnview.moc" | 
| 992 |  | 
| 993 |  |