| 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 | |