1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2018 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 <QtTest/QtTest> |
30 | #include <QtCore/QStringListModel> |
31 | #include <QtCore/QSortFilterProxyModel> |
32 | #include <QtGui/QStandardItemModel> |
33 | #include <QtGui/QStyleHints> |
34 | #include <QtQuick/qquickview.h> |
35 | #include <QtQuickTest/QtQuickTest> |
36 | #include <QStringListModel> |
37 | #include <QQmlApplicationEngine> |
38 | #include <QtQml/qqmlengine.h> |
39 | #include <QtQml/qqmlcontext.h> |
40 | #include <QtQml/qqmlexpression.h> |
41 | #include <QtQml/qqmlincubator.h> |
42 | #include <QtQuick/private/qquickitemview_p_p.h> |
43 | #include <QtQuick/private/qquicklistview_p.h> |
44 | #include <QtQuick/private/qquickmousearea_p.h> |
45 | #include <QtQuick/private/qquicktext_p.h> |
46 | #include <QtQuick/private/qquickrectangle_p.h> |
47 | #include <QtQmlModels/private/qqmlobjectmodel_p.h> |
48 | #include <QtQmlModels/private/qqmllistmodel_p.h> |
49 | #include <QtQmlModels/private/qqmldelegatemodel_p.h> |
50 | #include "../../shared/util.h" |
51 | #include "../shared/viewtestutil.h" |
52 | #include "../shared/visualtestutil.h" |
53 | #include "incrementalmodel.h" |
54 | #include "proxytestinnermodel.h" |
55 | #include "randomsortmodel.h" |
56 | #include "reusemodel.h" |
57 | #include <math.h> |
58 | |
59 | Q_DECLARE_METATYPE(Qt::LayoutDirection) |
60 | Q_DECLARE_METATYPE(QQuickItemView::VerticalLayoutDirection) |
61 | Q_DECLARE_METATYPE(QQuickItemView::PositionMode) |
62 | Q_DECLARE_METATYPE(QQuickListView::Orientation) |
63 | Q_DECLARE_METATYPE(QQuickFlickable::FlickableDirection) |
64 | Q_DECLARE_METATYPE(Qt::Key) |
65 | |
66 | using namespace QQuickViewTestUtil; |
67 | using namespace QQuickVisualTestUtil; |
68 | |
69 | #define SHARE_VIEWS |
70 | |
71 | class tst_QQuickListView : public QQmlDataTest |
72 | { |
73 | Q_OBJECT |
74 | public: |
75 | tst_QQuickListView(); |
76 | |
77 | private slots: |
78 | void init(); |
79 | void cleanupTestCase(); |
80 | // Test QAbstractItemModel model types |
81 | void qAbstractItemModel_package_items(); |
82 | void qAbstractItemModel_items(); |
83 | |
84 | void qAbstractItemModel_package_changed(); |
85 | void qAbstractItemModel_changed(); |
86 | |
87 | void qAbstractItemModel_package_inserted(); |
88 | void qAbstractItemModel_inserted(); |
89 | void qAbstractItemModel_inserted_more(); |
90 | void qAbstractItemModel_inserted_more_data(); |
91 | void qAbstractItemModel_inserted_more_bottomToTop(); |
92 | void qAbstractItemModel_inserted_more_bottomToTop_data(); |
93 | |
94 | void qAbstractItemModel_package_removed(); |
95 | void qAbstractItemModel_removed(); |
96 | void qAbstractItemModel_removed_more(); |
97 | void qAbstractItemModel_removed_more_data(); |
98 | void qAbstractItemModel_removed_more_bottomToTop(); |
99 | void qAbstractItemModel_removed_more_bottomToTop_data(); |
100 | |
101 | void qAbstractItemModel_package_moved(); |
102 | void qAbstractItemModel_package_moved_data(); |
103 | void qAbstractItemModel_moved(); |
104 | void qAbstractItemModel_moved_data(); |
105 | void qAbstractItemModel_moved_bottomToTop(); |
106 | void qAbstractItemModel_moved_bottomToTop_data(); |
107 | |
108 | void multipleChanges_condensed() { multipleChanges(condensed: true); } |
109 | void multipleChanges_condensed_data() { multipleChanges_data(); } |
110 | void multipleChanges_uncondensed() { multipleChanges(condensed: false); } |
111 | void multipleChanges_uncondensed_data() { multipleChanges_data(); } |
112 | |
113 | void qAbstractItemModel_package_clear(); |
114 | void qAbstractItemModel_clear(); |
115 | void qAbstractItemModel_clear_bottomToTop(); |
116 | |
117 | void insertBeforeVisible(); |
118 | void insertBeforeVisible_data(); |
119 | void swapWithFirstItem(); |
120 | void itemList(); |
121 | void itemListFlicker(); |
122 | void currentIndex_delayedItemCreation(); |
123 | void currentIndex_delayedItemCreation_data(); |
124 | void currentIndex(); |
125 | void noCurrentIndex(); |
126 | void keyNavigation(); |
127 | void keyNavigation_data(); |
128 | void checkCountForMultiColumnModels(); |
129 | void enforceRange(); |
130 | void enforceRange_withoutHighlight(); |
131 | void spacing(); |
132 | void qAbstractItemModel_package_sections(); |
133 | void qAbstractItemModel_sections(); |
134 | void sectionsPositioning(); |
135 | void sectionsDelegate_data(); |
136 | void sectionsDelegate(); |
137 | void sectionsDragOutsideBounds_data(); |
138 | void sectionsDragOutsideBounds(); |
139 | void sectionsDelegate_headerVisibility(); |
140 | void sectionPropertyChange(); |
141 | void sectionDelegateChange(); |
142 | void sectionsItemInsertion(); |
143 | void sectionsSnap_data(); |
144 | void sectionsSnap(); |
145 | void cacheBuffer(); |
146 | void positionViewAtBeginningEnd(); |
147 | void positionViewAtIndex(); |
148 | void positionViewAtIndex_data(); |
149 | void resetModel(); |
150 | void propertyChanges(); |
151 | void componentChanges(); |
152 | void modelChanges(); |
153 | void manualHighlight(); |
154 | void initialZValues(); |
155 | void initialZValues_data(); |
156 | void header(); |
157 | void header_data(); |
158 | void header_delayItemCreation(); |
159 | void headerChangesViewport(); |
160 | void footer(); |
161 | void footer_data(); |
162 | void footer2(); |
163 | void extents(); |
164 | void extents_data(); |
165 | void resetModel_headerFooter(); |
166 | void resizeView(); |
167 | void resizeViewAndRepaint(); |
168 | void sizeLessThan1(); |
169 | void QTBUG_14821(); |
170 | void resizeDelegate(); |
171 | void resizeFirstDelegate(); |
172 | void repositionResizedDelegate(); |
173 | void repositionResizedDelegate_data(); |
174 | void QTBUG_16037(); |
175 | void indexAt_itemAt_data(); |
176 | void indexAt_itemAt(); |
177 | void itemAtIndex(); |
178 | void incrementalModel(); |
179 | void onAdd(); |
180 | void onAdd_data(); |
181 | void onRemove(); |
182 | void onRemove_data(); |
183 | void attachedProperties_QTBUG_32836(); |
184 | void rightToLeft(); |
185 | void test_mirroring(); |
186 | void margins(); |
187 | void marginsResize(); |
188 | void marginsResize_data(); |
189 | void creationContext(); |
190 | void snapToItem_data(); |
191 | void snapToItem(); |
192 | void headerSnapToItem_data(); |
193 | void headerSnapToItem(); |
194 | void snapToItemWithSpacing_QTBUG_59852(); |
195 | void snapOneItemResize_QTBUG_43555(); |
196 | void snapOneItem_data(); |
197 | void snapOneItem(); |
198 | void snapOneItemCurrentIndexRemoveAnimation(); |
199 | void snapOneItemWrongDirection(); |
200 | |
201 | void QTBUG_9791(); |
202 | void QTBUG_33568(); |
203 | void QTBUG_11105(); |
204 | void QTBUG_21742(); |
205 | |
206 | void asynchronous(); |
207 | void unrequestedVisibility(); |
208 | |
209 | void populateTransitions(); |
210 | void populateTransitions_data(); |
211 | void sizeTransitions(); |
212 | void sizeTransitions_data(); |
213 | |
214 | void addTransitions(); |
215 | void addTransitions_data(); |
216 | void moveTransitions(); |
217 | void moveTransitions_data(); |
218 | void removeTransitions(); |
219 | void removeTransitions_data(); |
220 | void displacedTransitions(); |
221 | void displacedTransitions_data(); |
222 | void multipleTransitions(); |
223 | void multipleTransitions_data(); |
224 | void multipleDisplaced(); |
225 | |
226 | void flickBeyondBounds(); |
227 | void flickBothDirections(); |
228 | void flickBothDirections_data(); |
229 | void destroyItemOnCreation(); |
230 | |
231 | void parentBinding(); |
232 | void defaultHighlightMoveDuration(); |
233 | void accessEmptyCurrentItem_QTBUG_30227(); |
234 | void delayedChanges_QTBUG_30555(); |
235 | void outsideViewportChangeNotAffectingView(); |
236 | void testProxyModelChangedAfterMove(); |
237 | |
238 | void typedModel(); |
239 | void displayMargin(); |
240 | void negativeDisplayMargin(); |
241 | |
242 | void highlightItemGeometryChanges(); |
243 | |
244 | void QTBUG_36481(); |
245 | void QTBUG_35920(); |
246 | |
247 | void stickyPositioning(); |
248 | void stickyPositioning_data(); |
249 | |
250 | void roundingErrors(); |
251 | void roundingErrors_data(); |
252 | |
253 | void QTBUG_38209(); |
254 | void programmaticFlickAtBounds(); |
255 | void programmaticFlickAtBounds2(); |
256 | void programmaticFlickAtBounds3(); |
257 | |
258 | void layoutChange(); |
259 | |
260 | void QTBUG_39492_data(); |
261 | void QTBUG_39492(); |
262 | |
263 | void jsArrayChange(); |
264 | void objectModel(); |
265 | |
266 | void contentHeightWithDelayRemove(); |
267 | void contentHeightWithDelayRemove_data(); |
268 | |
269 | void QTBUG_48044_currentItemNotVisibleAfterTransition(); |
270 | void QTBUG_48870_fastModelUpdates(); |
271 | |
272 | void QTBUG_50105(); |
273 | void keyNavigationEnabled(); |
274 | void QTBUG_61269_appendDuringScrollDown(); |
275 | void QTBUG_61269_appendDuringScrollDown_data(); |
276 | void QTBUG_50097_stickyHeader_positionViewAtIndex(); |
277 | void QTBUG_63974_stickyHeader_positionViewAtIndex_Contain(); |
278 | void QTBUG_66163_setModelViewPortSizeChange(); |
279 | void itemFiltered(); |
280 | void releaseItems(); |
281 | |
282 | void QTBUG_34576_velocityZero(); |
283 | void QTBUG_61537_modelChangesAsync(); |
284 | |
285 | void useDelegateChooserWithoutDefault(); |
286 | |
287 | void addOnCompleted(); |
288 | void setPositionOnLayout(); |
289 | void touchCancel(); |
290 | void resizeAfterComponentComplete(); |
291 | void dragOverFloatingHeaderOrFooter(); |
292 | |
293 | void delegateWithRequiredProperties(); |
294 | |
295 | void reuse_reuseIsOffByDefault(); |
296 | void reuse_checkThatItemsAreReused(); |
297 | void moveObjectModelItemToAnotherObjectModel(); |
298 | void changeModelAndDestroyTheOldOne(); |
299 | void objectModelCulling(); |
300 | |
301 | void requiredObjectListModel(); |
302 | void clickHeaderAndFooterWhenClip(); |
303 | void animatedDelegate(); |
304 | void dragDelegateWithMouseArea(); |
305 | void dragDelegateWithMouseArea_data(); |
306 | |
307 | |
308 | void singletonModelLifetime(); |
309 | void QTBUG_92809(); |
310 | |
311 | private: |
312 | template <class T> void items(const QUrl &source); |
313 | template <class T> void changed(const QUrl &source); |
314 | template <class T> void inserted(const QUrl &source); |
315 | template <class T> void inserted_more(QQuickItemView::VerticalLayoutDirection verticalLayoutDirection = QQuickItemView::TopToBottom); |
316 | template <class T> void removed(const QUrl &source, bool animated); |
317 | template <class T> void removed_more(const QUrl &source, QQuickItemView::VerticalLayoutDirection verticalLayoutDirection = QQuickItemView::TopToBottom); |
318 | template <class T> void moved(const QUrl &source, QQuickItemView::VerticalLayoutDirection verticalLayoutDirection = QQuickItemView::TopToBottom); |
319 | template <class T> void clear(const QUrl &source, QQuickItemView::VerticalLayoutDirection verticalLayoutDirection = QQuickItemView::TopToBottom); |
320 | template <class T> void sections(const QUrl &source); |
321 | |
322 | void multipleChanges(bool condensed); |
323 | void multipleChanges_data(); |
324 | |
325 | QList<int> toIntList(const QVariantList &list); |
326 | void matchIndexLists(const QVariantList &indexLists, const QList<int> &expectedIndexes); |
327 | void matchItemsAndIndexes(const QVariantMap &items, const QaimModel &model, const QList<int> &expectedIndexes); |
328 | void matchItemLists(const QVariantList &itemLists, const QList<QQuickItem *> &expectedItems); |
329 | |
330 | void inserted_more_data(); |
331 | void removed_more_data(); |
332 | void moved_data(); |
333 | |
334 | #ifdef SHARE_VIEWS |
335 | QQuickView *getView() { |
336 | if (m_view) { |
337 | if (QString(QTest::currentTestFunction()) != testForView) { |
338 | delete m_view; |
339 | m_view = nullptr; |
340 | } else { |
341 | m_view->setSource(QUrl()); |
342 | return m_view; |
343 | } |
344 | } |
345 | |
346 | testForView = QTest::currentTestFunction(); |
347 | m_view = createView(); |
348 | return m_view; |
349 | } |
350 | void releaseView(QQuickView *view) { |
351 | Q_ASSERT(view == m_view); |
352 | m_view->setSource(QUrl()); |
353 | } |
354 | #else |
355 | QQuickView *getView() { |
356 | return createView(); |
357 | } |
358 | void releaseView(QQuickView *view) { |
359 | delete view; |
360 | } |
361 | #endif |
362 | |
363 | QQuickView *m_view; |
364 | QString testForView; |
365 | QTouchDevice *touchDevice = QTest::createTouchDevice(); |
366 | }; |
367 | |
368 | class TestObject : public QObject |
369 | { |
370 | Q_OBJECT |
371 | |
372 | Q_PROPERTY(bool error READ error WRITE setError NOTIFY changedError) |
373 | Q_PROPERTY(bool animate READ animate NOTIFY changedAnim) |
374 | Q_PROPERTY(bool invalidHighlight READ invalidHighlight NOTIFY changedHl) |
375 | Q_PROPERTY(int cacheBuffer READ cacheBuffer NOTIFY changedCacheBuffer) |
376 | |
377 | public: |
378 | TestObject(QObject *parent = nullptr) |
379 | : QObject(parent), mError(true), mAnimate(false), mInvalidHighlight(false) |
380 | , mCacheBuffer(0) {} |
381 | |
382 | bool error() const { return mError; } |
383 | void setError(bool err) { mError = err; emit changedError(); } |
384 | |
385 | bool animate() const { return mAnimate; } |
386 | void setAnimate(bool anim) { mAnimate = anim; emit changedAnim(); } |
387 | |
388 | bool invalidHighlight() const { return mInvalidHighlight; } |
389 | void setInvalidHighlight(bool invalid) { mInvalidHighlight = invalid; emit changedHl(); } |
390 | |
391 | int cacheBuffer() const { return mCacheBuffer; } |
392 | void setCacheBuffer(int buffer) { mCacheBuffer = buffer; emit changedCacheBuffer(); } |
393 | |
394 | signals: |
395 | void changedError(); |
396 | void changedAnim(); |
397 | void changedHl(); |
398 | void changedCacheBuffer(); |
399 | |
400 | public: |
401 | bool mError; |
402 | bool mAnimate; |
403 | bool mInvalidHighlight; |
404 | int mCacheBuffer; |
405 | }; |
406 | |
407 | tst_QQuickListView::tst_QQuickListView() : m_view(nullptr) |
408 | { |
409 | } |
410 | |
411 | void tst_QQuickListView::init() |
412 | { |
413 | #ifdef SHARE_VIEWS |
414 | if (m_view && QString(QTest::currentTestFunction()) != testForView) { |
415 | testForView = QString(); |
416 | delete m_view; |
417 | m_view = nullptr; |
418 | } |
419 | #endif |
420 | qmlRegisterAnonymousType<QAbstractItemModel>(uri: "Proxy" , versionMajor: 1); |
421 | qmlRegisterType<ProxyTestInnerModel>(uri: "Proxy" , versionMajor: 1, versionMinor: 0, qmlName: "ProxyTestInnerModel" ); |
422 | qmlRegisterType<QSortFilterProxyModel>(uri: "Proxy" , versionMajor: 1, versionMinor: 0, qmlName: "QSortFilterProxyModel" ); |
423 | } |
424 | |
425 | void tst_QQuickListView::cleanupTestCase() |
426 | { |
427 | #ifdef SHARE_VIEWS |
428 | testForView = QString(); |
429 | delete m_view; |
430 | m_view = nullptr; |
431 | #endif |
432 | } |
433 | |
434 | template <class T> |
435 | void tst_QQuickListView::items(const QUrl &source) |
436 | { |
437 | QScopedPointer<QQuickView> window(createView()); |
438 | |
439 | T model; |
440 | model.addItem("Fred" , "12345" ); |
441 | model.addItem("John" , "2345" ); |
442 | model.addItem("Bob" , "54321" ); |
443 | |
444 | QQmlContext *ctxt = window->rootContext(); |
445 | ctxt->setContextProperty("testModel" , &model); |
446 | |
447 | QScopedPointer<TestObject> testObject(new TestObject); |
448 | ctxt->setContextProperty("testObject" , testObject.data()); |
449 | |
450 | window->setSource(source); |
451 | qApp->processEvents(); |
452 | |
453 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
454 | QTRY_VERIFY(listview != nullptr); |
455 | listview->forceLayout(); |
456 | |
457 | QQuickItem *contentItem = listview->contentItem(); |
458 | QTRY_VERIFY(contentItem != nullptr); |
459 | |
460 | QMetaObject::invokeMethod(obj: window->rootObject(), member: "checkProperties" ); |
461 | QTRY_VERIFY(!testObject->error()); |
462 | |
463 | QTRY_VERIFY(listview->highlightItem() != nullptr); |
464 | QTRY_COMPARE(listview->count(), model.count()); |
465 | QTRY_COMPARE(window->rootObject()->property("count" ).toInt(), model.count()); |
466 | listview->forceLayout(); |
467 | QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item |
468 | |
469 | // current item should be first item |
470 | QTRY_COMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper" , 0)); |
471 | |
472 | for (int i = 0; i < model.count(); ++i) { |
473 | QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName" , index: i); |
474 | QTRY_VERIFY(name != nullptr); |
475 | QTRY_COMPARE(name->text(), model.name(i)); |
476 | QQuickText *number = findItem<QQuickText>(parent: contentItem, objectName: "textNumber" , index: i); |
477 | QTRY_VERIFY(number != nullptr); |
478 | QTRY_COMPARE(number->text(), model.number(i)); |
479 | } |
480 | |
481 | // switch to other delegate |
482 | testObject->setAnimate(true); |
483 | QMetaObject::invokeMethod(obj: window->rootObject(), member: "checkProperties" ); |
484 | QTRY_VERIFY(!testObject->error()); |
485 | QTRY_VERIFY(listview->currentItem()); |
486 | |
487 | // set invalid highlight |
488 | testObject->setInvalidHighlight(true); |
489 | QMetaObject::invokeMethod(obj: window->rootObject(), member: "checkProperties" ); |
490 | QTRY_VERIFY(!testObject->error()); |
491 | QTRY_VERIFY(listview->currentItem()); |
492 | QTRY_VERIFY(!listview->highlightItem()); |
493 | |
494 | // back to normal highlight |
495 | testObject->setInvalidHighlight(false); |
496 | QMetaObject::invokeMethod(obj: window->rootObject(), member: "checkProperties" ); |
497 | QTRY_VERIFY(!testObject->error()); |
498 | QTRY_VERIFY(listview->currentItem()); |
499 | QTRY_VERIFY(listview->highlightItem() != nullptr); |
500 | |
501 | // set an empty model and confirm that items are destroyed |
502 | T model2; |
503 | ctxt->setContextProperty("testModel" , &model2); |
504 | |
505 | // Force a layout, necessary if ListView is completed before DelegateModel. |
506 | listview->forceLayout(); |
507 | |
508 | QTRY_VERIFY(findItems<QQuickItem>(contentItem, "wrapper" ).isEmpty()); |
509 | |
510 | QTRY_COMPARE(listview->highlightResizeVelocity(), 1000.0); |
511 | QTRY_COMPARE(listview->highlightMoveVelocity(), 100000.0); |
512 | } |
513 | |
514 | |
515 | template <class T> |
516 | void tst_QQuickListView::changed(const QUrl &source) |
517 | { |
518 | QScopedPointer<QQuickView> window(createView()); |
519 | |
520 | T model; |
521 | model.addItem("Fred" , "12345" ); |
522 | model.addItem("John" , "2345" ); |
523 | model.addItem("Bob" , "54321" ); |
524 | |
525 | QQmlContext *ctxt = window->rootContext(); |
526 | ctxt->setContextProperty("testModel" , &model); |
527 | |
528 | QScopedPointer<TestObject> testObject(new TestObject); |
529 | ctxt->setContextProperty("testObject" , testObject.data()); |
530 | |
531 | window->setSource(source); |
532 | qApp->processEvents(); |
533 | |
534 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
535 | QTRY_VERIFY(listview != nullptr); |
536 | listview->forceLayout(); |
537 | |
538 | QQuickItem *contentItem = listview->contentItem(); |
539 | QTRY_VERIFY(contentItem != nullptr); |
540 | |
541 | // Force a layout, necessary if ListView is completed before DelegateModel. |
542 | listview->forceLayout(); |
543 | |
544 | model.modifyItem(1, "Will" , "9876" ); |
545 | QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName" , index: 1); |
546 | QTRY_VERIFY(name != nullptr); |
547 | QTRY_COMPARE(name->text(), model.name(1)); |
548 | QQuickText *number = findItem<QQuickText>(parent: contentItem, objectName: "textNumber" , index: 1); |
549 | QTRY_VERIFY(number != nullptr); |
550 | QTRY_COMPARE(number->text(), model.number(1)); |
551 | } |
552 | |
553 | template <class T> |
554 | void tst_QQuickListView::inserted(const QUrl &source) |
555 | { |
556 | QScopedPointer<QQuickView> window(createView()); |
557 | window->show(); |
558 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
559 | |
560 | T model; |
561 | model.addItem("Fred" , "12345" ); |
562 | model.addItem("John" , "2345" ); |
563 | model.addItem("Bob" , "54321" ); |
564 | |
565 | QQmlContext *ctxt = window->rootContext(); |
566 | ctxt->setContextProperty("testModel" , &model); |
567 | |
568 | QScopedPointer<TestObject> testObject(new TestObject); |
569 | ctxt->setContextProperty("testObject" , testObject.data()); |
570 | |
571 | window->setSource(source); |
572 | qApp->processEvents(); |
573 | |
574 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
575 | QTRY_VERIFY(listview != nullptr); |
576 | |
577 | QQuickItem *contentItem = listview->contentItem(); |
578 | QTRY_VERIFY(contentItem != nullptr); |
579 | |
580 | model.insertItem(1, "Will" , "9876" ); |
581 | |
582 | QTRY_COMPARE(window->rootObject()->property("count" ).toInt(), model.count()); |
583 | QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item |
584 | |
585 | QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName" , index: 1); |
586 | QTRY_VERIFY(name != nullptr); |
587 | QTRY_COMPARE(name->text(), model.name(1)); |
588 | QQuickText *number = findItem<QQuickText>(parent: contentItem, objectName: "textNumber" , index: 1); |
589 | QTRY_VERIFY(number != nullptr); |
590 | QTRY_COMPARE(number->text(), model.number(1)); |
591 | |
592 | // Confirm items positioned correctly |
593 | for (int i = 0; i < model.count(); ++i) { |
594 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
595 | QTRY_COMPARE(item->y(), i*20.0); |
596 | } |
597 | |
598 | model.insertItem(0, "Foo" , "1111" ); // zero index, and current item |
599 | |
600 | QTRY_COMPARE(window->rootObject()->property("count" ).toInt(), model.count()); |
601 | QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item |
602 | |
603 | name = findItem<QQuickText>(parent: contentItem, objectName: "textName" , index: 0); |
604 | QTRY_VERIFY(name != nullptr); |
605 | QTRY_COMPARE(name->text(), model.name(0)); |
606 | number = findItem<QQuickText>(parent: contentItem, objectName: "textNumber" , index: 0); |
607 | QTRY_VERIFY(number != nullptr); |
608 | QTRY_COMPARE(number->text(), model.number(0)); |
609 | |
610 | QTRY_COMPARE(listview->currentIndex(), 1); |
611 | |
612 | // Confirm items positioned correctly |
613 | for (int i = 0; i < model.count(); ++i) { |
614 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
615 | QTRY_COMPARE(item->y(), i*20.0); |
616 | } |
617 | |
618 | for (int i = model.count(); i < 30; ++i) |
619 | model.insertItem(i, "Hello" , QString::number(i)); |
620 | |
621 | listview->setContentY(80); |
622 | |
623 | // Insert item outside visible area |
624 | model.insertItem(1, "Hello" , "1324" ); |
625 | |
626 | QTRY_COMPARE(listview->contentY(), qreal(80)); |
627 | |
628 | // Confirm items positioned correctly |
629 | for (int i = 5; i < 5+15; ++i) { |
630 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
631 | if (!item) qWarning() << "Item" << i << "not found" ; |
632 | QTRY_VERIFY(item); |
633 | QTRY_COMPARE(item->y(), i*20.0 - 20.0); |
634 | } |
635 | |
636 | // QTRY_COMPARE(listview->contentItemHeight(), model.count() * 20.0); |
637 | |
638 | // QTBUG-19675 |
639 | model.clear(); |
640 | model.insertItem(0, "Hello" , "1234" ); |
641 | QTRY_COMPARE(window->rootObject()->property("count" ).toInt(), model.count()); |
642 | |
643 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: 0); |
644 | QVERIFY(item); |
645 | QTRY_COMPARE(item->y() - listview->contentY(), 0.); |
646 | } |
647 | |
648 | template <class T> |
649 | void tst_QQuickListView::inserted_more(QQuickItemView::VerticalLayoutDirection verticalLayoutDirection) |
650 | { |
651 | QFETCH(qreal, contentY); |
652 | QFETCH(int, insertIndex); |
653 | QFETCH(int, insertCount); |
654 | QFETCH(qreal, itemsOffsetAfterMove); |
655 | |
656 | T model; |
657 | for (int i = 0; i < 30; i++) |
658 | model.addItem("Item" + QString::number(i), "" ); |
659 | |
660 | QQuickView *window = getView(); |
661 | QQmlContext *ctxt = window->rootContext(); |
662 | ctxt->setContextProperty("testModel" , &model); |
663 | |
664 | QScopedPointer<TestObject> testObject(new TestObject); |
665 | ctxt->setContextProperty("testObject" , testObject.data()); |
666 | |
667 | window->setSource(testFileUrl(fileName: "listviewtest.qml" )); |
668 | window->show(); |
669 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
670 | |
671 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
672 | QTRY_VERIFY(listview != nullptr); |
673 | QQuickItem *contentItem = listview->contentItem(); |
674 | QTRY_VERIFY(contentItem != nullptr); |
675 | |
676 | if (verticalLayoutDirection == QQuickItemView::BottomToTop) { |
677 | listview->setVerticalLayoutDirection(verticalLayoutDirection); |
678 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
679 | contentY = -listview->height() - contentY; |
680 | } |
681 | listview->setContentY(contentY); |
682 | |
683 | QQuickItemViewPrivate::get(o: listview)->layout(); |
684 | |
685 | QList<QPair<QString, QString> > newData; |
686 | for (int i=0; i<insertCount; i++) |
687 | newData << qMakePair(x: QString("value %1" ).arg(a: i), y: QString::number(i)); |
688 | model.insertItems(insertIndex, newData); |
689 | |
690 | //Wait for polish (updates list to the model changes) |
691 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
692 | |
693 | QTRY_COMPARE(listview->property("count" ).toInt(), model.count()); |
694 | |
695 | // FIXME This is NOT checking anything about visibleItems.first() |
696 | #if 0 |
697 | // check visibleItems.first() is in correct position |
698 | QQuickItem *item0 = findItem<QQuickItem>(contentItem, "wrapper" , 0); |
699 | QVERIFY(item0); |
700 | if (verticalLayoutDirection == QQuickItemView::BottomToTop) |
701 | QCOMPARE(item0->y(), -item0->height() - itemsOffsetAfterMove); |
702 | else |
703 | QCOMPARE(item0->y(), itemsOffsetAfterMove); |
704 | #endif |
705 | |
706 | QList<FxViewItem *> visibleItems = QQuickItemViewPrivate::get(o: listview)->visibleItems; |
707 | for (QList<FxViewItem *>::const_iterator itemIt = visibleItems.begin(); itemIt != visibleItems.end(); ++itemIt) { |
708 | FxViewItem *item = *itemIt; |
709 | if (item->item->position().y() >= 0 && item->item->position().y() < listview->height()) { |
710 | QVERIFY(!QQuickItemPrivate::get(item->item)->culled); |
711 | } |
712 | } |
713 | |
714 | QList<QQuickItem*> items = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ); |
715 | int firstVisibleIndex = -1; |
716 | for (int i=0; i<items.count(); i++) { |
717 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
718 | if (item && !QQuickItemPrivate::get(item)->culled) { |
719 | firstVisibleIndex = i; |
720 | break; |
721 | } |
722 | } |
723 | QVERIFY2(firstVisibleIndex >= 0, QByteArray::number(firstVisibleIndex)); |
724 | |
725 | // Confirm items positioned correctly and indexes correct |
726 | QQuickText *name; |
727 | QQuickText *number; |
728 | const qreal visibleFromPos = listview->contentY() - listview->displayMarginBeginning() - listview->cacheBuffer(); |
729 | const qreal visibleToPos = listview->contentY() + listview->height() + listview->displayMarginEnd() + listview->cacheBuffer(); |
730 | for (int i = firstVisibleIndex; i < model.count() && i < items.count(); ++i) { |
731 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
732 | QVERIFY2(item, qPrintable(QString("Item %1 not found" ).arg(i))); |
733 | qreal pos = i*20.0 + itemsOffsetAfterMove; |
734 | if (verticalLayoutDirection == QQuickItemView::BottomToTop) |
735 | pos = -item->height() - pos; |
736 | // Items outside the visible area (including cache buffer) should be skipped |
737 | if (pos > visibleToPos || pos < visibleFromPos) { |
738 | QTRY_VERIFY2(QQuickItemPrivate::get(item)->culled || item->y() < visibleFromPos || item->y() > visibleToPos, |
739 | qPrintable(QString("index %5, y %1, from %2, to %3, expected pos %4, culled %6" ). |
740 | arg(item->y()).arg(visibleFromPos).arg(visibleToPos).arg(pos).arg(i).arg(bool(QQuickItemPrivate::get(item)->culled)))); |
741 | continue; |
742 | } |
743 | QTRY_COMPARE(item->y(), pos); |
744 | name = findItem<QQuickText>(parent: contentItem, objectName: "textName" , index: i); |
745 | QVERIFY(name != nullptr); |
746 | QTRY_COMPARE(name->text(), model.name(i)); |
747 | number = findItem<QQuickText>(parent: contentItem, objectName: "textNumber" , index: i); |
748 | QVERIFY(number != nullptr); |
749 | QTRY_COMPARE(number->text(), model.number(i)); |
750 | } |
751 | |
752 | releaseView(view: window); |
753 | } |
754 | |
755 | void tst_QQuickListView::inserted_more_data() |
756 | { |
757 | QTest::addColumn<qreal>(name: "contentY" ); |
758 | QTest::addColumn<int>(name: "insertIndex" ); |
759 | QTest::addColumn<int>(name: "insertCount" ); |
760 | QTest::addColumn<qreal>(name: "itemsOffsetAfterMove" ); |
761 | |
762 | QTest::newRow(dataTag: "add 1, before visible items" ) |
763 | << 80.0 // show 4-19 |
764 | << 3 << 1 |
765 | << -20.0; // insert above first visible i.e. 0 is at -20, first visible should not move |
766 | |
767 | QTest::newRow(dataTag: "add multiple, before visible" ) |
768 | << 80.0 // show 4-19 |
769 | << 3 << 3 |
770 | << -20.0 * 3; // again first visible should not move |
771 | |
772 | QTest::newRow(dataTag: "add 1, at start of visible, content at start" ) |
773 | << 0.0 |
774 | << 0 << 1 |
775 | << 0.0; |
776 | |
777 | QTest::newRow(dataTag: "add multiple, start of visible, content at start" ) |
778 | << 0.0 |
779 | << 0 << 3 |
780 | << 0.0; |
781 | |
782 | QTest::newRow(dataTag: "add 1, at start of visible, content not at start" ) |
783 | << 80.0 // show 4-19 |
784 | << 4 << 1 |
785 | << 0.0; |
786 | |
787 | QTest::newRow(dataTag: "add multiple, at start of visible, content not at start" ) |
788 | << 80.0 // show 4-19 |
789 | << 4 << 3 |
790 | << 0.0; |
791 | |
792 | |
793 | QTest::newRow(dataTag: "add 1, at end of visible, content at start" ) |
794 | << 0.0 |
795 | << 15 << 1 |
796 | << 0.0; |
797 | |
798 | QTest::newRow(dataTag: "add multiple, at end of visible, content at start" ) |
799 | << 0.0 |
800 | << 15 << 3 |
801 | << 0.0; |
802 | |
803 | QTest::newRow(dataTag: "add 1, at end of visible, content not at start" ) |
804 | << 80.0 // show 4-19 |
805 | << 19 << 1 |
806 | << 0.0; |
807 | |
808 | QTest::newRow(dataTag: "add multiple, at end of visible, content not at start" ) |
809 | << 80.0 // show 4-19 |
810 | << 19 << 3 |
811 | << 0.0; |
812 | |
813 | |
814 | QTest::newRow(dataTag: "add 1, after visible, content at start" ) |
815 | << 0.0 |
816 | << 16 << 1 |
817 | << 0.0; |
818 | |
819 | QTest::newRow(dataTag: "add multiple, after visible, content at start" ) |
820 | << 0.0 |
821 | << 16 << 3 |
822 | << 0.0; |
823 | |
824 | QTest::newRow(dataTag: "add 1, after visible, content not at start" ) |
825 | << 80.0 // show 4-19 |
826 | << 20 << 1 |
827 | << 0.0; |
828 | |
829 | QTest::newRow(dataTag: "add multiple, after visible, content not at start" ) |
830 | << 80.0 // show 4-19 |
831 | << 20 << 3 |
832 | << 0.0; |
833 | |
834 | QTest::newRow(dataTag: "add multiple, within visible, content at start" ) |
835 | << 0.0 |
836 | << 2 << 50 |
837 | << 0.0; |
838 | } |
839 | |
840 | void tst_QQuickListView::insertBeforeVisible() |
841 | { |
842 | QFETCH(int, insertIndex); |
843 | QFETCH(int, insertCount); |
844 | QFETCH(int, removeIndex); |
845 | QFETCH(int, removeCount); |
846 | QFETCH(int, cacheBuffer); |
847 | |
848 | QQuickText *name; |
849 | QQuickView *window = getView(); |
850 | |
851 | QaimModel model; |
852 | for (int i = 0; i < 30; i++) |
853 | model.addItem(name: "Item" + QString::number(i), number: "" ); |
854 | |
855 | QQmlContext *ctxt = window->rootContext(); |
856 | ctxt->setContextProperty("testModel" , &model); |
857 | |
858 | QScopedPointer<TestObject> testObject(new TestObject); |
859 | ctxt->setContextProperty("testObject" , testObject.data()); |
860 | |
861 | window->setSource(testFileUrl(fileName: "listviewtest.qml" )); |
862 | window->show(); |
863 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
864 | |
865 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
866 | QTRY_VERIFY(listview != nullptr); |
867 | QQuickItem *contentItem = listview->contentItem(); |
868 | QTRY_VERIFY(contentItem != nullptr); |
869 | |
870 | listview->setCacheBuffer(cacheBuffer); |
871 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
872 | |
873 | // trigger a refill (not just setting contentY) so that the visibleItems grid is updated |
874 | int firstVisibleIndex = 20; // move to an index where the top item is not visible |
875 | listview->setContentY(firstVisibleIndex * 20.0); |
876 | listview->setCurrentIndex(firstVisibleIndex); |
877 | |
878 | qApp->processEvents(); |
879 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
880 | QTRY_COMPARE(listview->currentIndex(), firstVisibleIndex); |
881 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: firstVisibleIndex); |
882 | QVERIFY(item); |
883 | QCOMPARE(item->y(), listview->contentY()); |
884 | |
885 | if (removeCount > 0) |
886 | model.removeItems(index: removeIndex, count: removeCount); |
887 | |
888 | if (insertCount > 0) { |
889 | QList<QPair<QString, QString> > newData; |
890 | for (int i=0; i<insertCount; i++) |
891 | newData << qMakePair(x: QString("value %1" ).arg(a: i), y: QString::number(i)); |
892 | model.insertItems(index: insertIndex, items: newData); |
893 | QTRY_COMPARE(listview->property("count" ).toInt(), model.count()); |
894 | } |
895 | |
896 | // now, moving to the top of the view should position the inserted items correctly |
897 | int itemsOffsetAfterMove = (removeCount - insertCount) * 20; |
898 | listview->setCurrentIndex(0); |
899 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
900 | QTRY_COMPARE(listview->currentIndex(), 0); |
901 | QTRY_COMPARE(listview->contentY(), 0.0 + itemsOffsetAfterMove); |
902 | |
903 | // Confirm items positioned correctly and indexes correct |
904 | int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ).count(); |
905 | for (int i = 0; i < model.count() && i < itemCount; ++i) { |
906 | item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
907 | QVERIFY2(item, qPrintable(QString("Item %1 not found" ).arg(i))); |
908 | QTRY_COMPARE(item->y(), i*20.0 + itemsOffsetAfterMove); |
909 | name = findItem<QQuickText>(parent: contentItem, objectName: "textName" , index: i); |
910 | QVERIFY(name != nullptr); |
911 | QTRY_COMPARE(name->text(), model.name(i)); |
912 | } |
913 | |
914 | releaseView(view: window); |
915 | } |
916 | |
917 | void tst_QQuickListView::insertBeforeVisible_data() |
918 | { |
919 | QTest::addColumn<int>(name: "insertIndex" ); |
920 | QTest::addColumn<int>(name: "insertCount" ); |
921 | QTest::addColumn<int>(name: "removeIndex" ); |
922 | QTest::addColumn<int>(name: "removeCount" ); |
923 | QTest::addColumn<int>(name: "cacheBuffer" ); |
924 | |
925 | QTest::newRow(dataTag: "insert 1 at 0, 0 buffer" ) << 0 << 1 << 0 << 0 << 0; |
926 | QTest::newRow(dataTag: "insert 1 at 0, 100 buffer" ) << 0 << 1 << 0 << 0 << 100; |
927 | QTest::newRow(dataTag: "insert 1 at 0, 500 buffer" ) << 0 << 1 << 0 << 0 << 500; |
928 | |
929 | QTest::newRow(dataTag: "insert 1 at 1, 0 buffer" ) << 1 << 1 << 0 << 0 << 0; |
930 | QTest::newRow(dataTag: "insert 1 at 1, 100 buffer" ) << 1 << 1 << 0 << 0 << 100; |
931 | QTest::newRow(dataTag: "insert 1 at 1, 500 buffer" ) << 1 << 1 << 0 << 0 << 500; |
932 | |
933 | QTest::newRow(dataTag: "insert multiple at 0, 0 buffer" ) << 0 << 3 << 0 << 0 << 0; |
934 | QTest::newRow(dataTag: "insert multiple at 0, 100 buffer" ) << 0 << 3 << 0 << 0 << 100; |
935 | QTest::newRow(dataTag: "insert multiple at 0, 500 buffer" ) << 0 << 3 << 0 << 0 << 500; |
936 | |
937 | QTest::newRow(dataTag: "insert multiple at 1, 0 buffer" ) << 1 << 3 << 0 << 0 << 0; |
938 | QTest::newRow(dataTag: "insert multiple at 1, 100 buffer" ) << 1 << 3 << 0 << 0 << 100; |
939 | QTest::newRow(dataTag: "insert multiple at 1, 500 buffer" ) << 1 << 3 << 0 << 0 << 500; |
940 | |
941 | QTest::newRow(dataTag: "remove 1 at 0, 0 buffer" ) << 0 << 0 << 0 << 1 << 0; |
942 | QTest::newRow(dataTag: "remove 1 at 0, 100 buffer" ) << 0 << 0 << 0 << 1 << 100; |
943 | QTest::newRow(dataTag: "remove 1 at 0, 500 buffer" ) << 0 << 0 << 0 << 1 << 500; |
944 | |
945 | QTest::newRow(dataTag: "remove 1 at 1, 0 buffer" ) << 0 << 0 << 1 << 1 << 0; |
946 | QTest::newRow(dataTag: "remove 1 at 1, 100 buffer" ) << 0 << 0 << 1 << 1 << 100; |
947 | QTest::newRow(dataTag: "remove 1 at 1, 500 buffer" ) << 0 << 0 << 1 << 1 << 500; |
948 | |
949 | QTest::newRow(dataTag: "remove multiple at 0, 0 buffer" ) << 0 << 0 << 0 << 3 << 0; |
950 | QTest::newRow(dataTag: "remove multiple at 0, 100 buffer" ) << 0 << 0 << 0 << 3 << 100; |
951 | QTest::newRow(dataTag: "remove multiple at 0, 500 buffer" ) << 0 << 0 << 0 << 3 << 500; |
952 | |
953 | QTest::newRow(dataTag: "remove multiple at 1, 0 buffer" ) << 0 << 0 << 1 << 3 << 0; |
954 | QTest::newRow(dataTag: "remove multiple at 1, 100 buffer" ) << 0 << 0 << 1 << 3 << 100; |
955 | QTest::newRow(dataTag: "remove multiple at 1, 500 buffer" ) << 0 << 0 << 1 << 3 << 500; |
956 | } |
957 | |
958 | template <class T> |
959 | void tst_QQuickListView::removed(const QUrl &source, bool /* animated */) |
960 | { |
961 | QScopedPointer<QQuickView> window(createView()); |
962 | |
963 | T model; |
964 | for (int i = 0; i < 50; i++) |
965 | model.addItem("Item" + QString::number(i), "" ); |
966 | |
967 | QQmlContext *ctxt = window->rootContext(); |
968 | ctxt->setContextProperty("testModel" , &model); |
969 | |
970 | QScopedPointer<TestObject> testObject(new TestObject); |
971 | ctxt->setContextProperty("testObject" , testObject.data()); |
972 | |
973 | window->setSource(source); |
974 | window->show(); |
975 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
976 | |
977 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
978 | QTRY_VERIFY(listview != nullptr); |
979 | QQuickItem *contentItem = listview->contentItem(); |
980 | QTRY_VERIFY(contentItem != nullptr); |
981 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
982 | |
983 | model.removeItem(1); |
984 | QTRY_COMPARE(window->rootObject()->property("count" ).toInt(), model.count()); |
985 | |
986 | QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName" , index: 1); |
987 | QTRY_VERIFY(name != nullptr); |
988 | QTRY_COMPARE(name->text(), model.name(1)); |
989 | QQuickText *number = findItem<QQuickText>(parent: contentItem, objectName: "textNumber" , index: 1); |
990 | QTRY_VERIFY(number != nullptr); |
991 | QTRY_COMPARE(number->text(), model.number(1)); |
992 | |
993 | // Confirm items positioned correctly |
994 | int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ).count(); |
995 | for (int i = 0; i < model.count() && i < itemCount; ++i) { |
996 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
997 | if (!item) qWarning() << "Item" << i << "not found" ; |
998 | QTRY_VERIFY(item); |
999 | QTRY_COMPARE(item->y(), qreal(i*20)); |
1000 | } |
1001 | |
1002 | // Remove first item (which is the current item); |
1003 | model.removeItem(0); |
1004 | QTRY_COMPARE(window->rootObject()->property("count" ).toInt(), model.count()); |
1005 | |
1006 | name = findItem<QQuickText>(parent: contentItem, objectName: "textName" , index: 0); |
1007 | QTRY_VERIFY(name != nullptr); |
1008 | QTRY_COMPARE(name->text(), model.name(0)); |
1009 | number = findItem<QQuickText>(parent: contentItem, objectName: "textNumber" , index: 0); |
1010 | QTRY_VERIFY(number != nullptr); |
1011 | QTRY_COMPARE(number->text(), model.number(0)); |
1012 | |
1013 | // Confirm items positioned correctly |
1014 | itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ).count(); |
1015 | for (int i = 0; i < model.count() && i < itemCount; ++i) { |
1016 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
1017 | if (!item) qWarning() << "Item" << i << "not found" ; |
1018 | QTRY_VERIFY(item); |
1019 | QTRY_COMPARE(item->y(),i*20.0); |
1020 | } |
1021 | |
1022 | // Remove items not visible |
1023 | model.removeItem(18); |
1024 | QTRY_COMPARE(window->rootObject()->property("count" ).toInt(), model.count()); |
1025 | |
1026 | // Confirm items positioned correctly |
1027 | itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ).count(); |
1028 | for (int i = 0; i < model.count() && i < itemCount; ++i) { |
1029 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
1030 | if (!item) qWarning() << "Item" << i << "not found" ; |
1031 | QTRY_VERIFY(item); |
1032 | QTRY_COMPARE(item->y(),i*20.0); |
1033 | } |
1034 | |
1035 | // Remove items before visible |
1036 | listview->setContentY(80); |
1037 | listview->setCurrentIndex(10); |
1038 | |
1039 | model.removeItem(1); // post: top item will be at 20 |
1040 | QTRY_COMPARE(window->rootObject()->property("count" ).toInt(), model.count()); |
1041 | |
1042 | // Confirm items positioned correctly |
1043 | for (int i = 2; i < 18; ++i) { |
1044 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
1045 | if (!item) qWarning() << "Item" << i << "not found" ; |
1046 | QTRY_VERIFY(item); |
1047 | QTRY_COMPARE(item->y(),20+i*20.0); |
1048 | } |
1049 | |
1050 | // Remove current index |
1051 | QTRY_COMPARE(listview->currentIndex(), 9); |
1052 | QQuickItem *oldCurrent = listview->currentItem(); |
1053 | model.removeItem(9); |
1054 | |
1055 | QTRY_COMPARE(listview->currentIndex(), 9); |
1056 | QTRY_VERIFY(listview->currentItem() != oldCurrent); |
1057 | |
1058 | listview->setContentY(20); // That's the top now |
1059 | // let transitions settle. |
1060 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
1061 | |
1062 | // Confirm items positioned correctly |
1063 | itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ).count(); |
1064 | for (int i = 0; i < model.count() && i < itemCount; ++i) { |
1065 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
1066 | if (!item) qWarning() << "Item" << i << "not found" ; |
1067 | QTRY_VERIFY(item); |
1068 | QTRY_COMPARE(item->y(),20+i*20.0); |
1069 | } |
1070 | |
1071 | // remove current item beyond visible items. |
1072 | listview->setCurrentIndex(20); |
1073 | listview->setContentY(40); |
1074 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
1075 | |
1076 | model.removeItem(20); |
1077 | QTRY_COMPARE(listview->currentIndex(), 20); |
1078 | QTRY_VERIFY(listview->currentItem() != nullptr); |
1079 | |
1080 | // remove item before current, but visible |
1081 | listview->setCurrentIndex(8); |
1082 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
1083 | oldCurrent = listview->currentItem(); |
1084 | model.removeItem(6); |
1085 | |
1086 | QTRY_COMPARE(listview->currentIndex(), 7); |
1087 | QTRY_COMPARE(listview->currentItem(), oldCurrent); |
1088 | |
1089 | listview->setContentY(80); |
1090 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
1091 | |
1092 | // remove all visible items |
1093 | model.removeItems(1, 18); |
1094 | QTRY_COMPARE(listview->count() , model.count()); |
1095 | |
1096 | // Confirm items positioned correctly |
1097 | itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ).count(); |
1098 | for (int i = 0; i < model.count() && i < itemCount-1; ++i) { |
1099 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i+1); |
1100 | if (!item) qWarning() << "Item" << i+1 << "not found" ; |
1101 | QTRY_VERIFY(item); |
1102 | QTRY_COMPARE(item->y(),80+i*20.0); |
1103 | } |
1104 | |
1105 | model.removeItems(1, 17); |
1106 | QTRY_COMPARE(listview->count() , model.count()); |
1107 | |
1108 | model.removeItems(2, 1); |
1109 | QTRY_COMPARE(listview->count() , model.count()); |
1110 | |
1111 | model.addItem("New" , "1" ); |
1112 | QTRY_COMPARE(listview->count() , model.count()); |
1113 | |
1114 | QTRY_VERIFY(name = findItem<QQuickText>(contentItem, "textName" , model.count()-1)); |
1115 | QCOMPARE(name->text(), QString("New" )); |
1116 | |
1117 | // Add some more items so that we don't run out |
1118 | model.clear(); |
1119 | for (int i = 0; i < 50; i++) |
1120 | model.addItem("Item" + QString::number(i), "" ); |
1121 | |
1122 | // QTBUG-QTBUG-20575 |
1123 | listview->setCurrentIndex(0); |
1124 | listview->setContentY(30); |
1125 | model.removeItem(0); |
1126 | QTRY_VERIFY(name = findItem<QQuickText>(contentItem, "textName" , 0)); |
1127 | |
1128 | // QTBUG-19198 move to end and remove all visible items one at a time. |
1129 | listview->positionViewAtEnd(); |
1130 | for (int i = 0; i < 18; ++i) |
1131 | model.removeItems(model.count() - 1, 1); |
1132 | QTRY_VERIFY(findItems<QQuickItem>(contentItem, "wrapper" ).count() > 16); |
1133 | } |
1134 | |
1135 | template <class T> |
1136 | void tst_QQuickListView::removed_more(const QUrl &source, QQuickItemView::VerticalLayoutDirection verticalLayoutDirection) |
1137 | { |
1138 | QFETCH(qreal, contentY); |
1139 | QFETCH(int, removeIndex); |
1140 | QFETCH(int, removeCount); |
1141 | QFETCH(qreal, itemsOffsetAfterMove); |
1142 | |
1143 | QQuickView *window = getView(); |
1144 | |
1145 | T model; |
1146 | for (int i = 0; i < 30; i++) |
1147 | model.addItem("Item" + QString::number(i), "" ); |
1148 | |
1149 | QQmlContext *ctxt = window->rootContext(); |
1150 | ctxt->setContextProperty("testModel" , &model); |
1151 | |
1152 | QScopedPointer<TestObject> testObject(new TestObject); |
1153 | ctxt->setContextProperty("testObject" , testObject.data()); |
1154 | |
1155 | window->setSource(source); |
1156 | window->show(); |
1157 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
1158 | |
1159 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
1160 | QTRY_VERIFY(listview != nullptr); |
1161 | QQuickItem *contentItem = listview->contentItem(); |
1162 | QTRY_VERIFY(contentItem != nullptr); |
1163 | |
1164 | if (verticalLayoutDirection == QQuickItemView::BottomToTop) { |
1165 | listview->setVerticalLayoutDirection(verticalLayoutDirection); |
1166 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
1167 | contentY = -listview->height() - contentY; |
1168 | } |
1169 | listview->setContentY(contentY); |
1170 | |
1171 | model.removeItems(removeIndex, removeCount); |
1172 | //Wait for polish (updates list to the model changes) |
1173 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
1174 | |
1175 | QTRY_COMPARE(listview->property("count" ).toInt(), model.count()); |
1176 | |
1177 | // check visibleItems.first() is in correct position |
1178 | QQuickItem *item0 = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: 0); |
1179 | QVERIFY(item0); |
1180 | QVERIFY(item0); |
1181 | if (verticalLayoutDirection == QQuickItemView::BottomToTop) |
1182 | QCOMPARE(item0->y(), -item0->height() - itemsOffsetAfterMove); |
1183 | else |
1184 | QCOMPARE(item0->y(), itemsOffsetAfterMove); |
1185 | |
1186 | QList<QQuickItem*> items = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ); |
1187 | int firstVisibleIndex = -1; |
1188 | for (int i=0; i<items.count(); i++) { |
1189 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
1190 | if (item && delegateVisible(item)) { |
1191 | firstVisibleIndex = i; |
1192 | break; |
1193 | } |
1194 | } |
1195 | QVERIFY2(firstVisibleIndex >= 0, QByteArray::number(firstVisibleIndex)); |
1196 | |
1197 | // Confirm items positioned correctly and indexes correct |
1198 | QQuickText *name; |
1199 | QQuickText *number; |
1200 | for (int i = firstVisibleIndex; i < model.count() && i < items.count(); ++i) { |
1201 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
1202 | QVERIFY2(item, qPrintable(QString("Item %1 not found" ).arg(i))); |
1203 | qreal pos = i*20.0 + itemsOffsetAfterMove; |
1204 | if (verticalLayoutDirection == QQuickItemView::BottomToTop) |
1205 | pos = -item0->height() - pos; |
1206 | QTRY_COMPARE(item->y(), pos); |
1207 | name = findItem<QQuickText>(parent: contentItem, objectName: "textName" , index: i); |
1208 | QVERIFY(name != nullptr); |
1209 | QTRY_COMPARE(name->text(), model.name(i)); |
1210 | number = findItem<QQuickText>(parent: contentItem, objectName: "textNumber" , index: i); |
1211 | QVERIFY(number != nullptr); |
1212 | QTRY_COMPARE(number->text(), model.number(i)); |
1213 | } |
1214 | |
1215 | releaseView(view: window); |
1216 | } |
1217 | |
1218 | void tst_QQuickListView::removed_more_data() |
1219 | { |
1220 | QTest::addColumn<qreal>(name: "contentY" ); |
1221 | QTest::addColumn<int>(name: "removeIndex" ); |
1222 | QTest::addColumn<int>(name: "removeCount" ); |
1223 | QTest::addColumn<qreal>(name: "itemsOffsetAfterMove" ); |
1224 | |
1225 | QTest::newRow(dataTag: "remove 1, before visible items" ) |
1226 | << 80.0 // show 4-19 |
1227 | << 3 << 1 |
1228 | << 20.0; // visible items slide down by 1 item so that first visible does not move |
1229 | |
1230 | QTest::newRow(dataTag: "remove multiple, all before visible items" ) |
1231 | << 80.0 |
1232 | << 1 << 3 |
1233 | << 20.0 * 3; |
1234 | |
1235 | QTest::newRow(dataTag: "remove multiple, all before visible items, remove item 0" ) |
1236 | << 80.0 |
1237 | << 0 << 4 |
1238 | << 20.0 * 4; |
1239 | |
1240 | // remove 1,2,3 before the visible pos, 0 moves down to just before the visible pos, |
1241 | // items 4,5 are removed from view, item 6 slides up to original pos of item 4 (80px) |
1242 | QTest::newRow(dataTag: "remove multiple, mix of items from before and within visible items" ) |
1243 | << 80.0 |
1244 | << 1 << 5 |
1245 | << 20.0 * 3; // adjust for the 3 items removed before the visible |
1246 | |
1247 | QTest::newRow(dataTag: "remove multiple, mix of items from before and within visible items, remove item 0" ) |
1248 | << 80.0 |
1249 | << 0 << 6 |
1250 | << 20.0 * 4; // adjust for the 3 items removed before the visible |
1251 | |
1252 | |
1253 | QTest::newRow(dataTag: "remove 1, from start of visible, content at start" ) |
1254 | << 0.0 |
1255 | << 0 << 1 |
1256 | << 0.0; |
1257 | |
1258 | QTest::newRow(dataTag: "remove multiple, from start of visible, content at start" ) |
1259 | << 0.0 |
1260 | << 0 << 3 |
1261 | << 0.0; |
1262 | |
1263 | QTest::newRow(dataTag: "remove 1, from start of visible, content not at start" ) |
1264 | << 80.0 // show 4-19 |
1265 | << 4 << 1 |
1266 | << 0.0; |
1267 | |
1268 | QTest::newRow(dataTag: "remove multiple, from start of visible, content not at start" ) |
1269 | << 80.0 // show 4-19 |
1270 | << 4 << 3 |
1271 | << 0.0; |
1272 | |
1273 | |
1274 | QTest::newRow(dataTag: "remove 1, from middle of visible, content at start" ) |
1275 | << 0.0 |
1276 | << 10 << 1 |
1277 | << 0.0; |
1278 | |
1279 | QTest::newRow(dataTag: "remove multiple, from middle of visible, content at start" ) |
1280 | << 0.0 |
1281 | << 10 << 5 |
1282 | << 0.0; |
1283 | |
1284 | QTest::newRow(dataTag: "remove 1, from middle of visible, content not at start" ) |
1285 | << 80.0 // show 4-19 |
1286 | << 10 << 1 |
1287 | << 0.0; |
1288 | |
1289 | QTest::newRow(dataTag: "remove multiple, from middle of visible, content not at start" ) |
1290 | << 80.0 // show 4-19 |
1291 | << 10 << 5 |
1292 | << 0.0; |
1293 | |
1294 | |
1295 | QTest::newRow(dataTag: "remove 1, after visible, content at start" ) |
1296 | << 0.0 |
1297 | << 16 << 1 |
1298 | << 0.0; |
1299 | |
1300 | QTest::newRow(dataTag: "remove multiple, after visible, content at start" ) |
1301 | << 0.0 |
1302 | << 16 << 5 |
1303 | << 0.0; |
1304 | |
1305 | QTest::newRow(dataTag: "remove 1, after visible, content not at middle" ) |
1306 | << 80.0 // show 4-19 |
1307 | << 16+4 << 1 |
1308 | << 0.0; |
1309 | |
1310 | QTest::newRow(dataTag: "remove multiple, after visible, content not at start" ) |
1311 | << 80.0 // show 4-19 |
1312 | << 16+4 << 5 |
1313 | << 0.0; |
1314 | |
1315 | QTest::newRow(dataTag: "remove multiple, mix of items from within and after visible items" ) |
1316 | << 80.0 |
1317 | << 18 << 5 |
1318 | << 0.0; |
1319 | } |
1320 | |
1321 | template <class T> |
1322 | void tst_QQuickListView::clear(const QUrl &source, QQuickItemView::VerticalLayoutDirection verticalLayoutDirection) |
1323 | { |
1324 | QScopedPointer<QQuickView> window(createView()); |
1325 | |
1326 | T model; |
1327 | for (int i = 0; i < 30; i++) |
1328 | model.addItem("Item" + QString::number(i), "" ); |
1329 | |
1330 | QQmlContext *ctxt = window->rootContext(); |
1331 | ctxt->setContextProperty("testModel" , &model); |
1332 | |
1333 | QScopedPointer<TestObject> testObject(new TestObject); |
1334 | ctxt->setContextProperty("testObject" , testObject.data()); |
1335 | |
1336 | window->setSource(source); |
1337 | window->show(); |
1338 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
1339 | |
1340 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
1341 | QTRY_VERIFY(listview != nullptr); |
1342 | QQuickItem *contentItem = listview->contentItem(); |
1343 | QTRY_VERIFY(contentItem != nullptr); |
1344 | |
1345 | listview->setVerticalLayoutDirection(verticalLayoutDirection); |
1346 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
1347 | |
1348 | model.clear(); |
1349 | |
1350 | QTRY_COMPARE(findItems<QQuickListView>(contentItem, "wrapper" ).count(), 0); |
1351 | QTRY_COMPARE(listview->count(), 0); |
1352 | QTRY_VERIFY(!listview->currentItem()); |
1353 | if (verticalLayoutDirection == QQuickItemView::TopToBottom) |
1354 | QTRY_COMPARE(listview->contentY(), 0.0); |
1355 | else |
1356 | QTRY_COMPARE(listview->contentY(), -listview->height()); |
1357 | QCOMPARE(listview->currentIndex(), -1); |
1358 | |
1359 | QCOMPARE(listview->contentHeight(), 0.0); |
1360 | |
1361 | // confirm sanity when adding an item to cleared list |
1362 | model.addItem("New" , "1" ); |
1363 | listview->forceLayout(); |
1364 | QTRY_COMPARE(listview->count(), 1); |
1365 | QVERIFY(listview->currentItem() != nullptr); |
1366 | QCOMPARE(listview->currentIndex(), 0); |
1367 | } |
1368 | |
1369 | template <class T> |
1370 | void tst_QQuickListView::moved(const QUrl &source, QQuickItemView::VerticalLayoutDirection verticalLayoutDirection) |
1371 | { |
1372 | QFETCH(qreal, contentY); |
1373 | QFETCH(int, from); |
1374 | QFETCH(int, to); |
1375 | QFETCH(int, count); |
1376 | QFETCH(qreal, itemsOffsetAfterMove); |
1377 | |
1378 | QQuickText *name; |
1379 | QQuickText *number; |
1380 | QQuickView *window = getView(); |
1381 | |
1382 | T model; |
1383 | for (int i = 0; i < 30; i++) |
1384 | model.addItem("Item" + QString::number(i), "" ); |
1385 | |
1386 | QQmlContext *ctxt = window->rootContext(); |
1387 | ctxt->setContextProperty("testModel" , &model); |
1388 | |
1389 | QScopedPointer<TestObject> testObject(new TestObject); |
1390 | ctxt->setContextProperty("testObject" , testObject.data()); |
1391 | |
1392 | window->setSource(source); |
1393 | window->show(); |
1394 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
1395 | |
1396 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
1397 | QTRY_VERIFY(listview != nullptr); |
1398 | QQuickItem *contentItem = listview->contentItem(); |
1399 | QTRY_VERIFY(contentItem != nullptr); |
1400 | |
1401 | // always need to wait for view to be painted before the first move() |
1402 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
1403 | |
1404 | bool waitForPolish = (contentY != 0); |
1405 | if (verticalLayoutDirection == QQuickItemView::BottomToTop) { |
1406 | listview->setVerticalLayoutDirection(verticalLayoutDirection); |
1407 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
1408 | contentY = -listview->height() - contentY; |
1409 | } |
1410 | listview->setContentY(contentY); |
1411 | if (waitForPolish) |
1412 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
1413 | |
1414 | model.moveItems(from, to, count); |
1415 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
1416 | |
1417 | QList<QQuickItem*> items = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ); |
1418 | int firstVisibleIndex = -1; |
1419 | for (int i=0; i<items.count(); i++) { |
1420 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
1421 | if (item && delegateVisible(item)) { |
1422 | firstVisibleIndex = i; |
1423 | break; |
1424 | } |
1425 | } |
1426 | QVERIFY2(firstVisibleIndex >= 0, QByteArray::number(firstVisibleIndex)); |
1427 | |
1428 | // Confirm items positioned correctly and indexes correct |
1429 | for (int i = firstVisibleIndex; i < model.count() && i < items.count(); ++i) { |
1430 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
1431 | QVERIFY2(item, qPrintable(QString("Item %1 not found" ).arg(i))); |
1432 | qreal pos = i*20.0 + itemsOffsetAfterMove; |
1433 | if (verticalLayoutDirection == QQuickItemView::BottomToTop) |
1434 | pos = -item->height() - pos; |
1435 | QTRY_COMPARE(item->y(), pos); |
1436 | name = findItem<QQuickText>(parent: contentItem, objectName: "textName" , index: i); |
1437 | QVERIFY(name != nullptr); |
1438 | QTRY_COMPARE(name->text(), model.name(i)); |
1439 | number = findItem<QQuickText>(parent: contentItem, objectName: "textNumber" , index: i); |
1440 | QVERIFY(number != nullptr); |
1441 | QTRY_COMPARE(number->text(), model.number(i)); |
1442 | |
1443 | // current index should have been updated |
1444 | if (item == listview->currentItem()) |
1445 | QTRY_COMPARE(listview->currentIndex(), i); |
1446 | } |
1447 | |
1448 | releaseView(view: window); |
1449 | } |
1450 | |
1451 | void tst_QQuickListView::moved_data() |
1452 | { |
1453 | QTest::addColumn<qreal>(name: "contentY" ); |
1454 | QTest::addColumn<int>(name: "from" ); |
1455 | QTest::addColumn<int>(name: "to" ); |
1456 | QTest::addColumn<int>(name: "count" ); |
1457 | QTest::addColumn<qreal>(name: "itemsOffsetAfterMove" ); |
1458 | |
1459 | // model starts with 30 items, each 20px high, in area 320px high |
1460 | // 16 items should be visible at a time |
1461 | // itemsOffsetAfterMove should be > 0 whenever items above the visible pos have moved |
1462 | |
1463 | QTest::newRow(dataTag: "move 1 forwards, within visible items" ) |
1464 | << 0.0 |
1465 | << 1 << 4 << 1 |
1466 | << 0.0; |
1467 | |
1468 | QTest::newRow(dataTag: "move 1 forwards, from non-visible -> visible" ) |
1469 | << 80.0 // show 4-19 |
1470 | << 1 << 18 << 1 |
1471 | << 20.0; // removed 1 item above the first visible, so item 0 should drop down by 1 to minimize movement |
1472 | |
1473 | QTest::newRow(dataTag: "move 1 forwards, from non-visible -> visible (move first item)" ) |
1474 | << 80.0 // show 4-19 |
1475 | << 0 << 4 << 1 |
1476 | << 20.0; // first item has moved to below item4, everything drops down by size of 1 item |
1477 | |
1478 | QTest::newRow(dataTag: "move 1 forwards, from visible -> non-visible" ) |
1479 | << 0.0 |
1480 | << 1 << 16 << 1 |
1481 | << 0.0; |
1482 | |
1483 | QTest::newRow(dataTag: "move 1 forwards, from visible -> non-visible (move first item)" ) |
1484 | << 0.0 |
1485 | << 0 << 16 << 1 |
1486 | << 0.0; |
1487 | |
1488 | |
1489 | QTest::newRow(dataTag: "move 1 backwards, within visible items" ) |
1490 | << 0.0 |
1491 | << 4 << 1 << 1 |
1492 | << 0.0; |
1493 | |
1494 | QTest::newRow(dataTag: "move 1 backwards, within visible items (to first index)" ) |
1495 | << 0.0 |
1496 | << 4 << 0 << 1 |
1497 | << 0.0; |
1498 | |
1499 | QTest::newRow(dataTag: "move 1 backwards, from non-visible -> visible" ) |
1500 | << 0.0 |
1501 | << 20 << 4 << 1 |
1502 | << 0.0; |
1503 | |
1504 | QTest::newRow(dataTag: "move 1 backwards, from non-visible -> visible (move last item)" ) |
1505 | << 0.0 |
1506 | << 29 << 15 << 1 |
1507 | << 0.0; |
1508 | |
1509 | QTest::newRow(dataTag: "move 1 backwards, from visible -> non-visible" ) |
1510 | << 80.0 // show 4-19 |
1511 | << 16 << 1 << 1 |
1512 | << -20.0; // to minimize movement, item 0 moves to -20, and other items do not move |
1513 | |
1514 | QTest::newRow(dataTag: "move 1 backwards, from visible -> non-visible (move first item)" ) |
1515 | << 80.0 // show 4-19 |
1516 | << 16 << 0 << 1 |
1517 | << -20.0; // to minimize movement, item 16 (now at 0) moves to -20, and other items do not move |
1518 | |
1519 | |
1520 | QTest::newRow(dataTag: "move multiple forwards, within visible items" ) |
1521 | << 0.0 |
1522 | << 0 << 5 << 3 |
1523 | << 0.0; |
1524 | |
1525 | QTest::newRow(dataTag: "move multiple forwards, before visible items" ) |
1526 | << 140.0 // show 7-22 |
1527 | << 4 << 5 << 3 // 4,5,6 move to below 7 |
1528 | << 20.0 * 3; // 4,5,6 moved down |
1529 | |
1530 | QTest::newRow(dataTag: "move multiple forwards, from non-visible -> visible" ) |
1531 | << 80.0 // show 4-19 |
1532 | << 1 << 5 << 3 |
1533 | << 20.0 * 3; // moving 3 from above the content y should adjust y positions accordingly |
1534 | |
1535 | QTest::newRow(dataTag: "move multiple forwards, from non-visible -> visible (move first item)" ) |
1536 | << 80.0 // show 4-19 |
1537 | << 0 << 5 << 3 |
1538 | << 20.0 * 3; // moving 3 from above the content y should adjust y positions accordingly |
1539 | |
1540 | QTest::newRow(dataTag: "move multiple forwards, mix of non-visible/visible" ) |
1541 | << 40.0 |
1542 | << 1 << 16 << 2 |
1543 | << 20.0; // item 1,2 are removed, item 3 is now first visible |
1544 | |
1545 | QTest::newRow(dataTag: "move multiple forwards, to bottom of view" ) |
1546 | << 0.0 |
1547 | << 5 << 13 << 3 |
1548 | << 0.0; |
1549 | |
1550 | QTest::newRow(dataTag: "move multiple forwards, to bottom of view, first->last" ) |
1551 | << 0.0 |
1552 | << 0 << 13 << 3 |
1553 | << 0.0; |
1554 | |
1555 | QTest::newRow(dataTag: "move multiple forwards, to bottom of view, content y not 0" ) |
1556 | << 80.0 |
1557 | << 5+4 << 13+4 << 3 |
1558 | << 0.0; |
1559 | |
1560 | QTest::newRow(dataTag: "move multiple forwards, from visible -> non-visible" ) |
1561 | << 0.0 |
1562 | << 1 << 16 << 3 |
1563 | << 0.0; |
1564 | |
1565 | QTest::newRow(dataTag: "move multiple forwards, from visible -> non-visible (move first item)" ) |
1566 | << 0.0 |
1567 | << 0 << 16 << 3 |
1568 | << 0.0; |
1569 | |
1570 | |
1571 | QTest::newRow(dataTag: "move multiple backwards, within visible items" ) |
1572 | << 0.0 |
1573 | << 4 << 1 << 3 |
1574 | << 0.0; |
1575 | |
1576 | QTest::newRow(dataTag: "move multiple backwards, within visible items (move first item)" ) |
1577 | << 0.0 |
1578 | << 10 << 0 << 3 |
1579 | << 0.0; |
1580 | |
1581 | QTest::newRow(dataTag: "move multiple backwards, from non-visible -> visible" ) |
1582 | << 0.0 |
1583 | << 20 << 4 << 3 |
1584 | << 0.0; |
1585 | |
1586 | QTest::newRow(dataTag: "move multiple backwards, from non-visible -> visible (move last item)" ) |
1587 | << 0.0 |
1588 | << 27 << 10 << 3 |
1589 | << 0.0; |
1590 | |
1591 | QTest::newRow(dataTag: "move multiple backwards, from visible -> non-visible" ) |
1592 | << 80.0 // show 4-19 |
1593 | << 16 << 1 << 3 |
1594 | << -20.0 * 3; // to minimize movement, 0 moves by -60, and other items do not move |
1595 | |
1596 | QTest::newRow(dataTag: "move multiple backwards, from visible -> non-visible (move first item)" ) |
1597 | << 80.0 // show 4-19 |
1598 | << 16 << 0 << 3 |
1599 | << -20.0 * 3; // to minimize movement, 16,17,18 move to above item 0, and other items do not move |
1600 | } |
1601 | |
1602 | void tst_QQuickListView::multipleChanges(bool condensed) |
1603 | { |
1604 | QFETCH(int, startCount); |
1605 | QFETCH(QList<ListChange>, changes); |
1606 | QFETCH(int, newCount); |
1607 | QFETCH(int, newCurrentIndex); |
1608 | |
1609 | QQuickView *window = getView(); |
1610 | |
1611 | QaimModel model; |
1612 | for (int i = 0; i < startCount; i++) |
1613 | model.addItem(name: "Item" + QString::number(i), number: "" ); |
1614 | |
1615 | QQmlContext *ctxt = window->rootContext(); |
1616 | ctxt->setContextProperty("testModel" , &model); |
1617 | |
1618 | QScopedPointer<TestObject> testObject(new TestObject); |
1619 | ctxt->setContextProperty("testObject" , testObject.data()); |
1620 | |
1621 | window->setSource(testFileUrl(fileName: "listviewtest.qml" )); |
1622 | window->show(); |
1623 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
1624 | |
1625 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
1626 | QTRY_VERIFY(listview != nullptr); |
1627 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
1628 | |
1629 | for (int i=0; i<changes.count(); i++) { |
1630 | switch (changes[i].type) { |
1631 | case ListChange::Inserted: |
1632 | { |
1633 | QList<QPair<QString, QString> > items; |
1634 | for (int j=changes[i].index; j<changes[i].index + changes[i].count; ++j) |
1635 | items << qMakePair(x: QString("new item %1" ).arg(a: j), y: QString::number(j)); |
1636 | model.insertItems(index: changes[i].index, items); |
1637 | break; |
1638 | } |
1639 | case ListChange::Removed: |
1640 | model.removeItems(index: changes[i].index, count: changes[i].count); |
1641 | break; |
1642 | case ListChange::Moved: |
1643 | model.moveItems(from: changes[i].index, to: changes[i].to, count: changes[i].count); |
1644 | break; |
1645 | case ListChange::SetCurrent: |
1646 | listview->setCurrentIndex(changes[i].index); |
1647 | break; |
1648 | case ListChange::SetContentY: |
1649 | listview->setContentY(changes[i].pos); |
1650 | break; |
1651 | default: |
1652 | continue; |
1653 | } |
1654 | if (!condensed) { |
1655 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
1656 | } |
1657 | } |
1658 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
1659 | |
1660 | QCOMPARE(listview->count(), newCount); |
1661 | QCOMPARE(listview->count(), model.count()); |
1662 | QCOMPARE(listview->currentIndex(), newCurrentIndex); |
1663 | |
1664 | QQuickText *name; |
1665 | QQuickText *number; |
1666 | QQuickItem *contentItem = listview->contentItem(); |
1667 | QTRY_VERIFY(contentItem != nullptr); |
1668 | int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ).count(); |
1669 | for (int i=0; i < model.count() && i < itemCount; ++i) { |
1670 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
1671 | QVERIFY2(item, qPrintable(QString("Item %1 not found" ).arg(i))); |
1672 | name = findItem<QQuickText>(parent: contentItem, objectName: "textName" , index: i); |
1673 | QVERIFY(name != nullptr); |
1674 | QTRY_COMPARE(name->text(), model.name(i)); |
1675 | number = findItem<QQuickText>(parent: contentItem, objectName: "textNumber" , index: i); |
1676 | QVERIFY(number != nullptr); |
1677 | QTRY_COMPARE(number->text(), model.number(i)); |
1678 | } |
1679 | |
1680 | releaseView(view: window); |
1681 | } |
1682 | |
1683 | void tst_QQuickListView::multipleChanges_data() |
1684 | { |
1685 | QTest::addColumn<int>(name: "startCount" ); |
1686 | QTest::addColumn<QList<ListChange> >(name: "changes" ); |
1687 | QTest::addColumn<int>(name: "newCount" ); |
1688 | QTest::addColumn<int>(name: "newCurrentIndex" ); |
1689 | |
1690 | QList<ListChange> changes; |
1691 | |
1692 | for (int i=1; i<30; i++) |
1693 | changes << ListChange::remove(index: 0); |
1694 | QTest::newRow(dataTag: "remove all but 1, first->last" ) << 30 << changes << 1 << 0; |
1695 | |
1696 | changes << ListChange::remove(index: 0); |
1697 | QTest::newRow(dataTag: "remove all" ) << 30 << changes << 0 << -1; |
1698 | |
1699 | changes.clear(); |
1700 | changes << ListChange::setCurrent(29); |
1701 | for (int i=29; i>0; i--) |
1702 | changes << ListChange::remove(index: i); |
1703 | QTest::newRow(dataTag: "remove last (current) -> first" ) << 30 << changes << 1 << 0; |
1704 | |
1705 | QTest::newRow(dataTag: "remove then insert at 0" ) << 10 << (QList<ListChange>() |
1706 | << ListChange::remove(index: 0, count: 1) |
1707 | << ListChange::insert(index: 0, count: 1) |
1708 | ) << 10 << 1; |
1709 | |
1710 | QTest::newRow(dataTag: "remove then insert at non-zero index" ) << 10 << (QList<ListChange>() |
1711 | << ListChange::setCurrent(2) |
1712 | << ListChange::remove(index: 2, count: 1) |
1713 | << ListChange::insert(index: 2, count: 1) |
1714 | ) << 10 << 3; |
1715 | |
1716 | QTest::newRow(dataTag: "remove current then insert below it" ) << 10 << (QList<ListChange>() |
1717 | << ListChange::setCurrent(1) |
1718 | << ListChange::remove(index: 1, count: 3) |
1719 | << ListChange::insert(index: 2, count: 2) |
1720 | ) << 9 << 1; |
1721 | |
1722 | QTest::newRow(dataTag: "remove current index then move it down" ) << 10 << (QList<ListChange>() |
1723 | << ListChange::setCurrent(2) |
1724 | << ListChange::remove(index: 1, count: 3) |
1725 | << ListChange::move(index: 1, to: 5, count: 1) |
1726 | ) << 7 << 5; |
1727 | |
1728 | QTest::newRow(dataTag: "remove current index then move it up" ) << 10 << (QList<ListChange>() |
1729 | << ListChange::setCurrent(5) |
1730 | << ListChange::remove(index: 4, count: 3) |
1731 | << ListChange::move(index: 4, to: 1, count: 1) |
1732 | ) << 7 << 1; |
1733 | |
1734 | |
1735 | QTest::newRow(dataTag: "insert multiple times" ) << 0 << (QList<ListChange>() |
1736 | << ListChange::insert(index: 0, count: 2) |
1737 | << ListChange::insert(index: 0, count: 4) |
1738 | << ListChange::insert(index: 0, count: 6) |
1739 | ) << 12 << 10; |
1740 | |
1741 | QTest::newRow(dataTag: "insert multiple times with current index changes" ) << 0 << (QList<ListChange>() |
1742 | << ListChange::insert(index: 0, count: 2) |
1743 | << ListChange::insert(index: 0, count: 4) |
1744 | << ListChange::insert(index: 0, count: 6) |
1745 | << ListChange::setCurrent(3) |
1746 | << ListChange::insert(index: 3, count: 2) |
1747 | ) << 14 << 5; |
1748 | |
1749 | QTest::newRow(dataTag: "insert and remove all" ) << 0 << (QList<ListChange>() |
1750 | << ListChange::insert(index: 0, count: 30) |
1751 | << ListChange::remove(index: 0, count: 30) |
1752 | ) << 0 << -1; |
1753 | |
1754 | QTest::newRow(dataTag: "insert and remove current" ) << 30 << (QList<ListChange>() |
1755 | << ListChange::insert(index: 1) |
1756 | << ListChange::setCurrent(1) |
1757 | << ListChange::remove(index: 1) |
1758 | ) << 30 << 1; |
1759 | |
1760 | QTest::newRow(dataTag: "insert before 0, then remove cross section of new and old items" ) << 10 << (QList<ListChange>() |
1761 | << ListChange::insert(index: 0, count: 10) |
1762 | << ListChange::remove(index: 5, count: 10) |
1763 | ) << 10 << 5; |
1764 | |
1765 | QTest::newRow(dataTag: "insert multiple, then move new items to end" ) << 10 << (QList<ListChange>() |
1766 | << ListChange::insert(index: 0, count: 3) |
1767 | << ListChange::move(index: 0, to: 10, count: 3) |
1768 | ) << 13 << 0; |
1769 | |
1770 | QTest::newRow(dataTag: "insert multiple, then move new and some old items to end" ) << 10 << (QList<ListChange>() |
1771 | << ListChange::insert(index: 0, count: 3) |
1772 | << ListChange::move(index: 0, to: 8, count: 5) |
1773 | ) << 13 << 11; |
1774 | |
1775 | QTest::newRow(dataTag: "insert multiple at end, then move new and some old items to start" ) << 10 << (QList<ListChange>() |
1776 | << ListChange::setCurrent(9) |
1777 | << ListChange::insert(index: 10, count: 3) |
1778 | << ListChange::move(index: 8, to: 0, count: 5) |
1779 | ) << 13 << 1; |
1780 | |
1781 | |
1782 | QTest::newRow(dataTag: "move back and forth to same index" ) << 10 << (QList<ListChange>() |
1783 | << ListChange::setCurrent(1) |
1784 | << ListChange::move(index: 1, to: 2, count: 2) |
1785 | << ListChange::move(index: 2, to: 1, count: 2) |
1786 | ) << 10 << 1; |
1787 | |
1788 | QTest::newRow(dataTag: "move forwards then back" ) << 10 << (QList<ListChange>() |
1789 | << ListChange::setCurrent(2) |
1790 | << ListChange::move(index: 1, to: 2, count: 3) |
1791 | << ListChange::move(index: 3, to: 0, count: 5) |
1792 | ) << 10 << 0; |
1793 | |
1794 | QTest::newRow(dataTag: "move current, then remove it" ) << 10 << (QList<ListChange>() |
1795 | << ListChange::setCurrent(5) |
1796 | << ListChange::move(index: 5, to: 0, count: 1) |
1797 | << ListChange::remove(index: 0) |
1798 | ) << 9 << 0; |
1799 | |
1800 | QTest::newRow(dataTag: "move current, then insert before it" ) << 10 << (QList<ListChange>() |
1801 | << ListChange::setCurrent(5) |
1802 | << ListChange::move(index: 5, to: 0, count: 1) |
1803 | << ListChange::insert(index: 0) |
1804 | ) << 11 << 1; |
1805 | |
1806 | QTest::newRow(dataTag: "move multiple, then remove them" ) << 10 << (QList<ListChange>() |
1807 | << ListChange::setCurrent(1) |
1808 | << ListChange::move(index: 5, to: 1, count: 3) |
1809 | << ListChange::remove(index: 1, count: 3) |
1810 | ) << 7 << 1; |
1811 | |
1812 | QTest::newRow(dataTag: "move multiple, then insert before them" ) << 10 << (QList<ListChange>() |
1813 | << ListChange::setCurrent(5) |
1814 | << ListChange::move(index: 5, to: 1, count: 3) |
1815 | << ListChange::insert(index: 1, count: 5) |
1816 | ) << 15 << 6; |
1817 | |
1818 | QTest::newRow(dataTag: "move multiple, then insert after them" ) << 10 << (QList<ListChange>() |
1819 | << ListChange::setCurrent(3) |
1820 | << ListChange::move(index: 0, to: 1, count: 2) |
1821 | << ListChange::insert(index: 3, count: 5) |
1822 | ) << 15 << 8; |
1823 | |
1824 | QTest::newRow(dataTag: "clear current" ) << 0 << (QList<ListChange>() |
1825 | << ListChange::insert(index: 0, count: 5) |
1826 | << ListChange::setCurrent(-1) |
1827 | << ListChange::remove(index: 0, count: 5) |
1828 | << ListChange::insert(index: 0, count: 5) |
1829 | ) << 5 << -1; |
1830 | |
1831 | QTest::newRow(dataTag: "remove, scroll" ) << 30 << (QList<ListChange>() |
1832 | << ListChange::remove(index: 20, count: 5) |
1833 | << ListChange::setContentY(20) |
1834 | ) << 25 << 0; |
1835 | |
1836 | QTest::newRow(dataTag: "insert, scroll" ) << 10 << (QList<ListChange>() |
1837 | << ListChange::insert(index: 9, count: 5) |
1838 | << ListChange::setContentY(20) |
1839 | ) << 15 << 0; |
1840 | |
1841 | QTest::newRow(dataTag: "move, scroll" ) << 20 << (QList<ListChange>() |
1842 | << ListChange::move(index: 15, to: 8, count: 3) |
1843 | << ListChange::setContentY(0) |
1844 | ) << 20 << 0; |
1845 | |
1846 | QTest::newRow(dataTag: "clear, insert, scroll" ) << 30 << (QList<ListChange>() |
1847 | << ListChange::setContentY(20) |
1848 | << ListChange::remove(index: 0, count: 30) |
1849 | << ListChange::insert(index: 0, count: 2) |
1850 | << ListChange::setContentY(0) |
1851 | ) << 2 << 0; |
1852 | } |
1853 | |
1854 | void tst_QQuickListView::swapWithFirstItem() |
1855 | { |
1856 | QScopedPointer<QQuickView> window(createView()); |
1857 | |
1858 | QaimModel model; |
1859 | for (int i = 0; i < 30; i++) |
1860 | model.addItem(name: "Item" + QString::number(i), number: "" ); |
1861 | |
1862 | QQmlContext *ctxt = window->rootContext(); |
1863 | ctxt->setContextProperty("testModel" , &model); |
1864 | |
1865 | QScopedPointer<TestObject> testObject(new TestObject); |
1866 | ctxt->setContextProperty("testObject" , testObject.data()); |
1867 | |
1868 | window->setSource(testFileUrl(fileName: "listviewtest.qml" )); |
1869 | window->show(); |
1870 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
1871 | |
1872 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
1873 | QTRY_VERIFY(listview != nullptr); |
1874 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
1875 | |
1876 | // ensure content position is stable |
1877 | listview->setContentY(0); |
1878 | model.moveItem(from: 1, to: 0); |
1879 | QTRY_COMPARE(listview->contentY(), qreal(0)); |
1880 | } |
1881 | |
1882 | void tst_QQuickListView::checkCountForMultiColumnModels() |
1883 | { |
1884 | // Check that a list view will only load items for the first |
1885 | // column, even if the model reports that it got several columns. |
1886 | // We test this since QQmlDelegateModel has been changed to |
1887 | // also understand multi-column models, but this should not affect ListView. |
1888 | QScopedPointer<QQuickView> window(createView()); |
1889 | |
1890 | const int rowCount = 10; |
1891 | const int columnCount = 10; |
1892 | |
1893 | QaimModel model; |
1894 | model.columns = columnCount; |
1895 | for (int i = 0; i < rowCount; i++) |
1896 | model.addItem(name: "Item" + QString::number(i), number: "" ); |
1897 | |
1898 | QQmlContext *ctxt = window->rootContext(); |
1899 | ctxt->setContextProperty("testModel" , &model); |
1900 | |
1901 | QScopedPointer<TestObject> testObject(new TestObject); |
1902 | ctxt->setContextProperty("testObject" , testObject.data()); |
1903 | |
1904 | window->setSource(testFileUrl(fileName: "listviewtest.qml" )); |
1905 | window->show(); |
1906 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
1907 | |
1908 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
1909 | QTRY_VERIFY(listview != nullptr); |
1910 | |
1911 | QCOMPARE(listview->count(), rowCount); |
1912 | } |
1913 | |
1914 | void tst_QQuickListView::enforceRange() |
1915 | { |
1916 | QScopedPointer<QQuickView> window(createView()); |
1917 | |
1918 | QaimModel model; |
1919 | for (int i = 0; i < 30; i++) |
1920 | model.addItem(name: "Item" + QString::number(i), number: "" ); |
1921 | |
1922 | QQmlContext *ctxt = window->rootContext(); |
1923 | ctxt->setContextProperty("testModel" , &model); |
1924 | |
1925 | window->setSource(testFileUrl(fileName: "listview-enforcerange.qml" )); |
1926 | window->show(); |
1927 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
1928 | |
1929 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
1930 | QTRY_VERIFY(listview != nullptr); |
1931 | |
1932 | QTRY_COMPARE(listview->preferredHighlightBegin(), 100.0); |
1933 | QTRY_COMPARE(listview->preferredHighlightEnd(), 100.0); |
1934 | QTRY_COMPARE(listview->highlightRangeMode(), QQuickListView::StrictlyEnforceRange); |
1935 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
1936 | |
1937 | QQuickItem *contentItem = listview->contentItem(); |
1938 | QTRY_VERIFY(contentItem != nullptr); |
1939 | |
1940 | // view should be positioned at the top of the range. |
1941 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: 0); |
1942 | QTRY_VERIFY(item); |
1943 | QTRY_COMPARE(listview->contentY(), -100.0); |
1944 | |
1945 | QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName" , index: 0); |
1946 | QTRY_VERIFY(name != nullptr); |
1947 | QTRY_COMPARE(name->text(), model.name(0)); |
1948 | QQuickText *number = findItem<QQuickText>(parent: contentItem, objectName: "textNumber" , index: 0); |
1949 | QTRY_VERIFY(number != nullptr); |
1950 | QTRY_COMPARE(number->text(), model.number(0)); |
1951 | |
1952 | // Check currentIndex is updated when contentItem moves |
1953 | listview->setContentY(20); |
1954 | |
1955 | QTRY_COMPARE(listview->currentIndex(), 6); |
1956 | |
1957 | // Test for [QTBUG-77418] { |
1958 | // explicit set current index |
1959 | listview->setCurrentIndex(5); |
1960 | QTRY_COMPARE(listview->contentY(), 0); |
1961 | |
1962 | // then check if contentY changes if the highlight range is changed |
1963 | listview->setPreferredHighlightBegin(80); |
1964 | listview->setPreferredHighlightEnd(80); |
1965 | QTRY_COMPARE(listview->contentY(), 20); |
1966 | |
1967 | // verify that current index does not change with no highlight |
1968 | listview->setHighlightRangeMode(QQuickListView::NoHighlightRange); |
1969 | listview->setContentY(100); |
1970 | QTRY_COMPARE(listview->currentIndex(), 5); |
1971 | |
1972 | // explicit set current index, contentY should not change now |
1973 | listview->setCurrentIndex(6); |
1974 | QTRY_COMPARE(listview->contentY(), 100); |
1975 | QTest::qWait(ms: 50); // This was needed in order to reproduce a failure for the following test |
1976 | |
1977 | // verify that contentY changes if we turn on highlight again |
1978 | listview->setHighlightRangeMode(QQuickListView::StrictlyEnforceRange); |
1979 | QTRY_COMPARE(listview->contentY(), 40); |
1980 | // } Test for [QTBUG-77418] |
1981 | |
1982 | // change model |
1983 | QaimModel model2; |
1984 | for (int i = 0; i < 5; i++) |
1985 | model2.addItem(name: "Item" + QString::number(i), number: "" ); |
1986 | |
1987 | ctxt->setContextProperty("testModel" , &model2); |
1988 | QCOMPARE(listview->count(), 5); |
1989 | } |
1990 | |
1991 | void tst_QQuickListView::enforceRange_withoutHighlight() |
1992 | { |
1993 | // QTBUG-20287 |
1994 | // If no highlight is set but StrictlyEnforceRange is used, the content should still move |
1995 | // to the correct position (i.e. to the next/previous item, not next/previous section) |
1996 | // when moving up/down via incrementCurrentIndex() and decrementCurrentIndex() |
1997 | |
1998 | QScopedPointer<QQuickView> window(createView()); |
1999 | |
2000 | QaimModel model; |
2001 | model.addItem(name: "Item 0" , number: "a" ); |
2002 | model.addItem(name: "Item 1" , number: "b" ); |
2003 | model.addItem(name: "Item 2" , number: "b" ); |
2004 | model.addItem(name: "Item 3" , number: "c" ); |
2005 | |
2006 | QQmlContext *ctxt = window->rootContext(); |
2007 | ctxt->setContextProperty("testModel" , &model); |
2008 | |
2009 | window->setSource(testFileUrl(fileName: "listview-enforcerange-nohighlight.qml" )); |
2010 | window->show(); |
2011 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
2012 | |
2013 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
2014 | QTRY_VERIFY(listview != nullptr); |
2015 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2016 | |
2017 | qreal expectedPos = -100.0; |
2018 | |
2019 | expectedPos += 10.0; // scroll past 1st section's delegate (10px height) |
2020 | QTRY_COMPARE(listview->contentY(), expectedPos); |
2021 | |
2022 | expectedPos += 20 + 10; // scroll past 1st section and section delegate of 2nd section |
2023 | QTest::keyClick(window: window.data(), key: Qt::Key_Down); |
2024 | |
2025 | QTRY_COMPARE(listview->contentY(), expectedPos); |
2026 | |
2027 | expectedPos += 20; // scroll past 1st item of 2nd section |
2028 | QTest::keyClick(window: window.data(), key: Qt::Key_Down); |
2029 | QTRY_COMPARE(listview->contentY(), expectedPos); |
2030 | |
2031 | expectedPos += 20 + 10; // scroll past 2nd item of 2nd section and section delegate of 3rd section |
2032 | QTest::keyClick(window: window.data(), key: Qt::Key_Down); |
2033 | QTRY_COMPARE(listview->contentY(), expectedPos); |
2034 | } |
2035 | |
2036 | void tst_QQuickListView::spacing() |
2037 | { |
2038 | QScopedPointer<QQuickView> window(createView()); |
2039 | |
2040 | QaimModel model; |
2041 | for (int i = 0; i < 30; i++) |
2042 | model.addItem(name: "Item" + QString::number(i), number: "" ); |
2043 | |
2044 | QQmlContext *ctxt = window->rootContext(); |
2045 | ctxt->setContextProperty("testModel" , &model); |
2046 | |
2047 | QScopedPointer<TestObject> testObject(new TestObject); |
2048 | ctxt->setContextProperty("testObject" , testObject.data()); |
2049 | |
2050 | window->setSource(testFileUrl(fileName: "listviewtest.qml" )); |
2051 | window->show(); |
2052 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
2053 | |
2054 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
2055 | QTRY_VERIFY(listview != nullptr); |
2056 | |
2057 | QQuickItem *contentItem = listview->contentItem(); |
2058 | QTRY_VERIFY(contentItem != nullptr); |
2059 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2060 | |
2061 | // Confirm items positioned correctly |
2062 | int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ).count(); |
2063 | for (int i = 0; i < model.count() && i < itemCount; ++i) { |
2064 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
2065 | if (!item) qWarning() << "Item" << i << "not found" ; |
2066 | QTRY_VERIFY(item); |
2067 | QTRY_COMPARE(item->y(), qreal(i*20)); |
2068 | } |
2069 | |
2070 | listview->setSpacing(10); |
2071 | QTRY_COMPARE(listview->spacing(), qreal(10)); |
2072 | |
2073 | // Confirm items positioned correctly |
2074 | QTRY_VERIFY(findItems<QQuickItem>(contentItem, "wrapper" ).count() == 11); |
2075 | for (int i = 0; i < 11; ++i) { |
2076 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
2077 | if (!item) qWarning() << "Item" << i << "not found" ; |
2078 | QTRY_VERIFY(item); |
2079 | QTRY_COMPARE(item->y(), qreal(i*30)); |
2080 | } |
2081 | |
2082 | listview->setSpacing(0); |
2083 | |
2084 | // Confirm items positioned correctly |
2085 | QTRY_VERIFY(findItems<QQuickItem>(contentItem, "wrapper" ).count() >= 16); |
2086 | for (int i = 0; i < 16; ++i) { |
2087 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
2088 | if (!item) qWarning() << "Item" << i << "not found" ; |
2089 | QTRY_VERIFY(item); |
2090 | QTRY_COMPARE(item->y(), i*20.0); |
2091 | } |
2092 | } |
2093 | |
2094 | template <typename T> |
2095 | void tst_QQuickListView::sections(const QUrl &source) |
2096 | { |
2097 | QScopedPointer<QQuickView> window(createView()); |
2098 | |
2099 | T model; |
2100 | for (int i = 0; i < 30; i++) |
2101 | model.addItem("Item" + QString::number(i), QString::number(i/5)); |
2102 | |
2103 | QQmlContext *ctxt = window->rootContext(); |
2104 | ctxt->setContextProperty("testModel" , &model); |
2105 | |
2106 | window->setSource(source); |
2107 | window->show(); |
2108 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
2109 | |
2110 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
2111 | QTRY_VERIFY(listview != nullptr); |
2112 | |
2113 | QQuickItem *contentItem = listview->contentItem(); |
2114 | QTRY_VERIFY(contentItem != nullptr); |
2115 | |
2116 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2117 | |
2118 | // Confirm items positioned correctly |
2119 | int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ).count(); |
2120 | for (int i = 0; i < model.count() && i < itemCount; ++i) { |
2121 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
2122 | QVERIFY(item); |
2123 | QTRY_COMPARE(item->y(), qreal(i*20 + ((i+4)/5) * 20)); |
2124 | QQuickText *next = findItem<QQuickText>(parent: item, objectName: "nextSection" ); |
2125 | QCOMPARE(next->text().toInt(), (i+1)/5); |
2126 | } |
2127 | |
2128 | QVERIFY(!listview->property("sectionsInvalidOnCompletion" ).toBool()); |
2129 | |
2130 | QSignalSpy currentSectionChangedSpy(listview, SIGNAL(currentSectionChanged())); |
2131 | |
2132 | // Remove section boundary |
2133 | model.removeItem(5); |
2134 | listview->forceLayout(); |
2135 | QTRY_COMPARE(listview->count(), model.count()); |
2136 | |
2137 | // New section header created |
2138 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: 5); |
2139 | QTRY_VERIFY(item); |
2140 | QTRY_COMPARE(item->height(), 40.0); |
2141 | |
2142 | model.insertItem(3, "New Item" , "0" ); |
2143 | listview->forceLayout(); |
2144 | QTRY_COMPARE(listview->count(), model.count()); |
2145 | |
2146 | // Section header moved |
2147 | item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: 5); |
2148 | QTRY_VERIFY(item); |
2149 | QTRY_COMPARE(item->height(), 20.0); |
2150 | |
2151 | item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: 6); |
2152 | QTRY_VERIFY(item); |
2153 | QTRY_COMPARE(item->height(), 40.0); |
2154 | |
2155 | // insert item which will become a section header |
2156 | model.insertItem(6, "Replace header" , "1" ); |
2157 | listview->forceLayout(); |
2158 | QTRY_COMPARE(listview->count(), model.count()); |
2159 | |
2160 | item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: 6); |
2161 | QTRY_VERIFY(item); |
2162 | QTRY_COMPARE(item->height(), 40.0); |
2163 | |
2164 | item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: 7); |
2165 | QTRY_VERIFY(item); |
2166 | QTRY_COMPARE(item->height(), 20.0); |
2167 | |
2168 | QTRY_COMPARE(listview->currentSection(), QString("0" )); |
2169 | |
2170 | listview->setContentY(140); |
2171 | QTRY_COMPARE(listview->currentSection(), QString("1" )); |
2172 | |
2173 | QTRY_COMPARE(currentSectionChangedSpy.count(), 1); |
2174 | |
2175 | listview->setContentY(20); |
2176 | QTRY_COMPARE(listview->currentSection(), QString("0" )); |
2177 | |
2178 | QTRY_COMPARE(currentSectionChangedSpy.count(), 2); |
2179 | |
2180 | item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: 1); |
2181 | QTRY_VERIFY(item); |
2182 | QTRY_COMPARE(item->height(), 20.0); |
2183 | |
2184 | // check that headers change when item changes |
2185 | listview->setContentY(0); |
2186 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2187 | model.modifyItem(0, "changed" , "2" ); |
2188 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2189 | |
2190 | item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: 1); |
2191 | QTRY_VERIFY(item); |
2192 | QTRY_COMPARE(item->height(), 40.0); |
2193 | } |
2194 | |
2195 | void tst_QQuickListView::sectionsDelegate_data() |
2196 | { |
2197 | QTest::addColumn<QUrl>(name: "path" ); |
2198 | QTest::addRow(format: "implicit" ) << testFileUrl(fileName: "listview-sections_delegate.qml" ); |
2199 | QTest::addRow(format: "required" ) << testFileUrl(fileName: "listview-sections_delegate_required.qml" ); |
2200 | } |
2201 | |
2202 | void tst_QQuickListView::sectionsDelegate() |
2203 | { |
2204 | QFETCH(QUrl, path); |
2205 | |
2206 | QScopedPointer<QQuickView> window(createView()); |
2207 | |
2208 | QaimModel model; |
2209 | for (int i = 0; i < 30; i++) |
2210 | model.addItem(name: "Item" + QString::number(i), number: QString::number(i/5)); |
2211 | |
2212 | QQmlContext *ctxt = window->rootContext(); |
2213 | ctxt->setContextProperty("testModel" , &model); |
2214 | |
2215 | window->setSource(path); |
2216 | window->show(); |
2217 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
2218 | |
2219 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
2220 | QTRY_VERIFY(listview != nullptr); |
2221 | |
2222 | QQuickItem *contentItem = listview->contentItem(); |
2223 | QTRY_VERIFY(contentItem != nullptr); |
2224 | |
2225 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2226 | |
2227 | // Confirm items positioned correctly |
2228 | int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ).count(); |
2229 | for (int i = 0; i < model.count() && i < itemCount; ++i) { |
2230 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
2231 | QTRY_VERIFY(item); |
2232 | QTRY_COMPARE(item->y(), qreal(i*20 + ((i+5)/5) * 20)); |
2233 | QQuickText *next = findItem<QQuickText>(parent: item, objectName: "nextSection" ); |
2234 | QCOMPARE(next->text().toInt(), (i+1)/5); |
2235 | } |
2236 | |
2237 | for (int i = 0; i < 3; ++i) { |
2238 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "sect_" + QString::number(i)); |
2239 | QVERIFY(item); |
2240 | QTRY_COMPARE(item->y(), qreal(i*20*6)); |
2241 | } |
2242 | |
2243 | // change section |
2244 | model.modifyItem(idx: 0, name: "One" , number: "aaa" ); |
2245 | model.modifyItem(idx: 1, name: "Two" , number: "aaa" ); |
2246 | model.modifyItem(idx: 2, name: "Three" , number: "aaa" ); |
2247 | model.modifyItem(idx: 3, name: "Four" , number: "aaa" ); |
2248 | model.modifyItem(idx: 4, name: "Five" , number: "aaa" ); |
2249 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2250 | |
2251 | for (int i = 0; i < 3; ++i) { |
2252 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, |
2253 | objectName: "sect_" + (i == 0 ? QString("aaa" ) : QString::number(i))); |
2254 | QVERIFY(item); |
2255 | QTRY_COMPARE(item->y(), qreal(i*20*6)); |
2256 | } |
2257 | |
2258 | // remove section boundary |
2259 | model.removeItem(index: 5); |
2260 | listview->forceLayout(); |
2261 | QTRY_COMPARE(listview->count(), model.count()); |
2262 | for (int i = 0; i < 3; ++i) { |
2263 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, |
2264 | objectName: "sect_" + (i == 0 ? QString("aaa" ) : QString::number(i))); |
2265 | QVERIFY(item); |
2266 | } |
2267 | |
2268 | // QTBUG-17606 |
2269 | QList<QQuickItem*> items = findItems<QQuickItem>(parent: contentItem, objectName: "sect_1" ); |
2270 | QCOMPARE(items.count(), 1); |
2271 | |
2272 | // QTBUG-17759 |
2273 | model.modifyItem(idx: 0, name: "One" , number: "aaa" ); |
2274 | model.modifyItem(idx: 1, name: "One" , number: "aaa" ); |
2275 | model.modifyItem(idx: 2, name: "One" , number: "aaa" ); |
2276 | model.modifyItem(idx: 3, name: "Four" , number: "aaa" ); |
2277 | model.modifyItem(idx: 4, name: "Four" , number: "aaa" ); |
2278 | model.modifyItem(idx: 5, name: "Four" , number: "aaa" ); |
2279 | model.modifyItem(idx: 6, name: "Five" , number: "aaa" ); |
2280 | model.modifyItem(idx: 7, name: "Five" , number: "aaa" ); |
2281 | model.modifyItem(idx: 8, name: "Five" , number: "aaa" ); |
2282 | model.modifyItem(idx: 9, name: "Two" , number: "aaa" ); |
2283 | model.modifyItem(idx: 10, name: "Two" , number: "aaa" ); |
2284 | model.modifyItem(idx: 11, name: "Two" , number: "aaa" ); |
2285 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2286 | QTRY_COMPARE(findItems<QQuickItem>(contentItem, "sect_aaa" ).count(), 1); |
2287 | window->rootObject()->setProperty(name: "sectionProperty" , value: "name" ); |
2288 | // ensure view has settled. |
2289 | QTRY_COMPARE(findItems<QQuickItem>(contentItem, "sect_Four" ).count(), 1); |
2290 | for (int i = 0; i < 4; ++i) { |
2291 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, |
2292 | objectName: "sect_" + model.name(index: i*3)); |
2293 | QVERIFY(item); |
2294 | QTRY_COMPARE(item->y(), qreal(i*20*4)); |
2295 | } |
2296 | } |
2297 | |
2298 | void tst_QQuickListView::sectionsDragOutsideBounds_data() |
2299 | { |
2300 | QTest::addColumn<int>(name: "distance" ); |
2301 | QTest::addColumn<int>(name: "cacheBuffer" ); |
2302 | |
2303 | QTest::newRow(dataTag: "500, no cache buffer" ) << 500 << 0; |
2304 | QTest::newRow(dataTag: "1000, no cache buffer" ) << 1000 << 0; |
2305 | QTest::newRow(dataTag: "500, cache buffer" ) << 500 << 320; |
2306 | QTest::newRow(dataTag: "1000, cache buffer" ) << 1000 << 320; |
2307 | } |
2308 | |
2309 | void tst_QQuickListView::sectionsDragOutsideBounds() |
2310 | { |
2311 | QFETCH(int, distance); |
2312 | QFETCH(int, cacheBuffer); |
2313 | |
2314 | QQuickView *window = getView(); |
2315 | QQuickViewTestUtil::moveMouseAway(window); |
2316 | |
2317 | QaimModel model; |
2318 | for (int i = 0; i < 10; i++) |
2319 | model.addItem(name: "Item" + QString::number(i), number: QString::number(i/5)); |
2320 | |
2321 | QQmlContext *ctxt = window->rootContext(); |
2322 | ctxt->setContextProperty("testModel" , &model); |
2323 | |
2324 | window->setSource(testFileUrl(fileName: "listview-sections_delegate.qml" )); |
2325 | window->show(); |
2326 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
2327 | |
2328 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
2329 | QTRY_VERIFY(listview != nullptr); |
2330 | listview->setCacheBuffer(cacheBuffer); |
2331 | |
2332 | QQuickItem *contentItem = listview->contentItem(); |
2333 | QTRY_VERIFY(contentItem != nullptr); |
2334 | |
2335 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2336 | |
2337 | // QTBUG-17769 |
2338 | // Drag view up beyond bounds |
2339 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(20,20)); |
2340 | QTest::mouseMove(window, pos: QPoint(20,0)); |
2341 | QTest::mouseMove(window, pos: QPoint(20,-50)); |
2342 | QTest::mouseMove(window, pos: QPoint(20,-distance)); |
2343 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(20,-distance)); |
2344 | // view should settle back at 0 |
2345 | QTRY_COMPARE(listview->contentY(), 0.0); |
2346 | |
2347 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(20,0)); |
2348 | QTest::mouseMove(window, pos: QPoint(20,20)); |
2349 | QTest::mouseMove(window, pos: QPoint(20,70)); |
2350 | QTest::mouseMove(window, pos: QPoint(20,distance)); |
2351 | |
2352 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(20,distance)); |
2353 | // view should settle back at 0 |
2354 | QTRY_COMPARE(listview->contentY(), 0.0); |
2355 | |
2356 | releaseView(view: window); |
2357 | } |
2358 | |
2359 | void tst_QQuickListView::() |
2360 | { |
2361 | QSKIP("QTBUG-24395" ); |
2362 | |
2363 | QScopedPointer<QQuickView> window(createView()); |
2364 | |
2365 | QaimModel model; |
2366 | for (int i = 0; i < 30; i++) |
2367 | model.addItem(name: "Item" + QString::number(i), number: QString::number(i/5)); |
2368 | |
2369 | window->rootContext()->setContextProperty("testModel" , &model); |
2370 | window->setSource(testFileUrl(fileName: "listview-sections_delegate.qml" )); |
2371 | window->show(); |
2372 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
2373 | window->requestActivate(); |
2374 | QVERIFY(QTest::qWaitForWindowActive(window.data())); |
2375 | |
2376 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
2377 | QTRY_VERIFY(listview != nullptr); |
2378 | |
2379 | QQuickItem *contentItem = listview->contentItem(); |
2380 | QTRY_VERIFY(contentItem != nullptr); |
2381 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2382 | |
2383 | // ensure section header is maintained in view |
2384 | listview->setCurrentIndex(20); |
2385 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2386 | QTRY_VERIFY(qFuzzyCompare(listview->contentY(), 200.0)); |
2387 | QTRY_VERIFY(!listview->isMoving()); |
2388 | listview->setCurrentIndex(0); |
2389 | QTRY_VERIFY(qFuzzyIsNull(listview->contentY())); |
2390 | } |
2391 | |
2392 | void tst_QQuickListView::sectionsPositioning() |
2393 | { |
2394 | QScopedPointer<QQuickView> window(createView()); |
2395 | |
2396 | QaimModel model; |
2397 | for (int i = 0; i < 30; i++) |
2398 | model.addItem(name: "Item" + QString::number(i), number: QString::number(i/5)); |
2399 | |
2400 | QQmlContext *ctxt = window->rootContext(); |
2401 | ctxt->setContextProperty("testModel" , &model); |
2402 | |
2403 | window->setSource(testFileUrl(fileName: "listview-sections_delegate.qml" )); |
2404 | window->show(); |
2405 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
2406 | window->rootObject()->setProperty(name: "sectionPositioning" , value: QVariant(int(QQuickViewSection::InlineLabels | QQuickViewSection::CurrentLabelAtStart | QQuickViewSection::NextLabelAtEnd))); |
2407 | |
2408 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
2409 | QTRY_VERIFY(listview != nullptr); |
2410 | QQuickItem *contentItem = listview->contentItem(); |
2411 | QTRY_VERIFY(contentItem != nullptr); |
2412 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2413 | |
2414 | for (int i = 0; i < 3; ++i) { |
2415 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "sect_" + QString::number(i)); |
2416 | QVERIFY(item); |
2417 | QTRY_COMPARE(item->y(), qreal(i*20*6)); |
2418 | } |
2419 | |
2420 | QQuickItem *topItem = findVisibleChild(parent: contentItem, objectName: "sect_0" ); // section header |
2421 | QVERIFY(topItem); |
2422 | QCOMPARE(topItem->y(), 0.); |
2423 | |
2424 | QQuickItem *bottomItem = findVisibleChild(parent: contentItem, objectName: "sect_3" ); // section footer |
2425 | QVERIFY(bottomItem); |
2426 | QCOMPARE(bottomItem->y(), 300.); |
2427 | |
2428 | // move down a little and check that section header is at top |
2429 | listview->setContentY(10); |
2430 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2431 | QCOMPARE(topItem->y(), 0.); |
2432 | |
2433 | // push the top header up |
2434 | listview->setContentY(110); |
2435 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2436 | topItem = findVisibleChild(parent: contentItem, objectName: "sect_0" ); // section header |
2437 | QVERIFY(topItem); |
2438 | QCOMPARE(topItem->y(), 100.); |
2439 | |
2440 | QQuickItem *item = findVisibleChild(parent: contentItem, objectName: "sect_1" ); |
2441 | QVERIFY(item); |
2442 | QCOMPARE(item->y(), 120.); |
2443 | |
2444 | bottomItem = findVisibleChild(parent: contentItem, objectName: "sect_4" ); // section footer |
2445 | QVERIFY(bottomItem); |
2446 | QCOMPARE(bottomItem->y(), 410.); |
2447 | |
2448 | // Move past section 0 |
2449 | listview->setContentY(120); |
2450 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2451 | topItem = findVisibleChild(parent: contentItem, objectName: "sect_0" ); // section header |
2452 | QVERIFY(!topItem); |
2453 | |
2454 | // Push section footer down |
2455 | listview->setContentY(70); |
2456 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2457 | bottomItem = findVisibleChild(parent: contentItem, objectName: "sect_4" ); // section footer |
2458 | QVERIFY(bottomItem); |
2459 | QCOMPARE(bottomItem->y(), 380.); |
2460 | |
2461 | // Change current section, and verify case insensitive comparison |
2462 | listview->setContentY(10); |
2463 | model.modifyItem(idx: 0, name: "One" , number: "aaa" ); |
2464 | model.modifyItem(idx: 1, name: "Two" , number: "AAA" ); |
2465 | model.modifyItem(idx: 2, name: "Three" , number: "aAa" ); |
2466 | model.modifyItem(idx: 3, name: "Four" , number: "aaA" ); |
2467 | model.modifyItem(idx: 4, name: "Five" , number: "Aaa" ); |
2468 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2469 | |
2470 | QTRY_COMPARE(listview->currentSection(), QString("aaa" )); |
2471 | |
2472 | for (int i = 0; i < 3; ++i) { |
2473 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, |
2474 | objectName: "sect_" + (i == 0 ? QString("aaa" ) : QString::number(i))); |
2475 | QVERIFY(item); |
2476 | QTRY_COMPARE(item->y(), qreal(i*20*6)); |
2477 | } |
2478 | |
2479 | QTRY_VERIFY((topItem = findVisibleChild(contentItem, "sect_aaa" ))); // section header |
2480 | QCOMPARE(topItem->y(), 10.); |
2481 | |
2482 | // remove section boundary |
2483 | listview->setContentY(120); |
2484 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2485 | model.removeItem(index: 5); |
2486 | listview->forceLayout(); |
2487 | QTRY_COMPARE(listview->count(), model.count()); |
2488 | for (int i = 1; i < 3; ++i) { |
2489 | QQuickItem *item = findVisibleChild(parent: contentItem, |
2490 | objectName: "sect_" + QString::number(i)); |
2491 | QVERIFY(item); |
2492 | QTRY_COMPARE(item->y(), qreal(i*20*6)); |
2493 | } |
2494 | |
2495 | topItem = findVisibleChild(parent: contentItem, objectName: "sect_1" ); |
2496 | QVERIFY(topItem); |
2497 | QTRY_COMPARE(topItem->y(), 120.); |
2498 | |
2499 | // Change the next section |
2500 | listview->setContentY(0); |
2501 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2502 | bottomItem = findVisibleChild(parent: contentItem, objectName: "sect_3" ); // section footer |
2503 | QVERIFY(bottomItem); |
2504 | QTRY_COMPARE(bottomItem->y(), 300.); |
2505 | |
2506 | model.modifyItem(idx: 14, name: "New" , number: "new" ); |
2507 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2508 | |
2509 | QTRY_VERIFY(bottomItem = findVisibleChild(contentItem, "sect_new" )); // section footer |
2510 | QTRY_COMPARE(bottomItem->y(), 300.); |
2511 | |
2512 | // delegate size increase should push section footer down |
2513 | listview->setContentY(70); |
2514 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2515 | QTRY_VERIFY(bottomItem = findVisibleChild(contentItem, "sect_3" )); // section footer |
2516 | QTRY_COMPARE(bottomItem->y(), 370.); |
2517 | QQuickItem *inlineSection = findVisibleChild(parent: contentItem, objectName: "sect_new" ); |
2518 | item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: 13); |
2519 | QVERIFY(item); |
2520 | item->setHeight(40.); |
2521 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2522 | QTRY_COMPARE(bottomItem->y(), 380.); |
2523 | QCOMPARE(inlineSection->y(), 360.); |
2524 | item->setHeight(20.); |
2525 | |
2526 | // Turn sticky footer off |
2527 | listview->setContentY(20); |
2528 | window->rootObject()->setProperty(name: "sectionPositioning" , value: QVariant(int(QQuickViewSection::InlineLabels | QQuickViewSection::CurrentLabelAtStart))); |
2529 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2530 | QTRY_VERIFY(item = findVisibleChild(contentItem, "sect_new" )); // inline label restored |
2531 | QCOMPARE(item->y(), 340.); |
2532 | |
2533 | // Turn sticky header off |
2534 | listview->setContentY(30); |
2535 | window->rootObject()->setProperty(name: "sectionPositioning" , value: QVariant(int(QQuickViewSection::InlineLabels))); |
2536 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2537 | QTRY_VERIFY(item = findVisibleChild(contentItem, "sect_aaa" )); // inline label restored |
2538 | QCOMPARE(item->y(), 0.); |
2539 | |
2540 | // if an empty model is set the header/footer should be cleaned up |
2541 | window->rootObject()->setProperty(name: "sectionPositioning" , value: QVariant(int(QQuickViewSection::InlineLabels | QQuickViewSection::CurrentLabelAtStart | QQuickViewSection::NextLabelAtEnd))); |
2542 | QTRY_VERIFY(findVisibleChild(contentItem, "sect_aaa" )); // section header |
2543 | QTRY_VERIFY(findVisibleChild(contentItem, "sect_new" )); // section footer |
2544 | QaimModel model1; |
2545 | ctxt->setContextProperty("testModel" , &model1); |
2546 | QTRY_VERIFY(!findVisibleChild(contentItem, "sect_aaa" )); // section header |
2547 | QTRY_VERIFY(!findVisibleChild(contentItem, "sect_new" )); // section footer |
2548 | |
2549 | // clear model - header/footer should be cleaned up |
2550 | ctxt->setContextProperty("testModel" , &model); |
2551 | QTRY_VERIFY(findVisibleChild(contentItem, "sect_aaa" )); // section header |
2552 | QTRY_VERIFY(findVisibleChild(contentItem, "sect_new" )); // section footer |
2553 | model.clear(); |
2554 | QTRY_VERIFY(!findVisibleChild(contentItem, "sect_aaa" )); // section header |
2555 | QTRY_VERIFY(!findVisibleChild(contentItem, "sect_new" )); // section footer |
2556 | } |
2557 | |
2558 | void tst_QQuickListView::sectionPropertyChange() |
2559 | { |
2560 | QScopedPointer<QQuickView> window(createView()); |
2561 | |
2562 | window->setSource(testFileUrl(fileName: "sectionpropertychange.qml" )); |
2563 | window->show(); |
2564 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
2565 | |
2566 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
2567 | QTRY_VERIFY(listview != nullptr); |
2568 | |
2569 | QQuickItem *contentItem = listview->contentItem(); |
2570 | QTRY_VERIFY(contentItem != nullptr); |
2571 | |
2572 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2573 | |
2574 | // Confirm items positioned correctly |
2575 | for (int i = 0; i < 2; ++i) { |
2576 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
2577 | QTRY_VERIFY(item); |
2578 | QTRY_COMPARE(item->y(), qreal(25. + i*75.)); |
2579 | } |
2580 | |
2581 | QMetaObject::invokeMethod(obj: window->rootObject(), member: "switchGroups" ); |
2582 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2583 | |
2584 | // Confirm items positioned correctly |
2585 | for (int i = 0; i < 2; ++i) { |
2586 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
2587 | QTRY_VERIFY(item); |
2588 | QTRY_COMPARE(item->y(), qreal(25. + i*75.)); |
2589 | } |
2590 | |
2591 | QMetaObject::invokeMethod(obj: window->rootObject(), member: "switchGroups" ); |
2592 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2593 | |
2594 | // Confirm items positioned correctly |
2595 | for (int i = 0; i < 2; ++i) { |
2596 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
2597 | QTRY_VERIFY(item); |
2598 | QTRY_COMPARE(item->y(), qreal(25. + i*75.)); |
2599 | } |
2600 | |
2601 | QMetaObject::invokeMethod(obj: window->rootObject(), member: "switchGrouped" ); |
2602 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2603 | |
2604 | // Confirm items positioned correctly |
2605 | for (int i = 0; i < 2; ++i) { |
2606 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
2607 | QTRY_VERIFY(item); |
2608 | QTRY_COMPARE(item->y(), qreal(25. + i*50.)); |
2609 | } |
2610 | |
2611 | QMetaObject::invokeMethod(obj: window->rootObject(), member: "switchGrouped" ); |
2612 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2613 | |
2614 | // Confirm items positioned correctly |
2615 | for (int i = 0; i < 2; ++i) { |
2616 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
2617 | QTRY_VERIFY(item); |
2618 | QTRY_COMPARE(item->y(), qreal(25. + i*75.)); |
2619 | } |
2620 | } |
2621 | |
2622 | void tst_QQuickListView::sectionDelegateChange() |
2623 | { |
2624 | QScopedPointer<QQuickView> window(createView()); |
2625 | |
2626 | window->setSource(testFileUrl(fileName: "sectiondelegatechange.qml" )); |
2627 | window->show(); |
2628 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
2629 | |
2630 | QQuickListView *listview = qobject_cast<QQuickListView *>(object: window->rootObject()); |
2631 | QVERIFY(listview != nullptr); |
2632 | |
2633 | QQuickItem *contentItem = listview->contentItem(); |
2634 | QVERIFY(contentItem != nullptr); |
2635 | |
2636 | QQuickTest::qWaitForItemPolished(item: listview); |
2637 | |
2638 | QVERIFY(findItems<QQuickItem>(contentItem, "section1" ).count() > 0); |
2639 | QCOMPARE(findItems<QQuickItem>(contentItem, "section2" ).count(), 0); |
2640 | |
2641 | for (int i = 0; i < 3; ++i) { |
2642 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "item" , index: i); |
2643 | QTRY_VERIFY(item); |
2644 | QTRY_COMPARE(item->y(), qreal(25. + i*50.)); |
2645 | } |
2646 | |
2647 | QMetaObject::invokeMethod(obj: window->rootObject(), member: "switchDelegates" ); |
2648 | QQuickTest::qWaitForItemPolished(item: listview); |
2649 | |
2650 | QCOMPARE(findItems<QQuickItem>(contentItem, "section1" ).count(), 0); |
2651 | QVERIFY(findItems<QQuickItem>(contentItem, "section2" ).count() > 0); |
2652 | |
2653 | for (int i = 0; i < 3; ++i) { |
2654 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "item" , index: i); |
2655 | QVERIFY(item); |
2656 | QTRY_COMPARE(item->y(), qreal(50. + i*75.)); |
2657 | } |
2658 | } |
2659 | |
2660 | // QTBUG-43873 |
2661 | void tst_QQuickListView::sectionsItemInsertion() |
2662 | { |
2663 | QScopedPointer<QQuickView> window(createView()); |
2664 | |
2665 | QaimModel model; |
2666 | for (int i = 0; i < 30; i++) |
2667 | model.addItem(name: "Item" + QString::number(i), number: QString::number(i/5)); |
2668 | |
2669 | QQmlContext *ctxt = window->rootContext(); |
2670 | ctxt->setContextProperty("testModel" , &model); |
2671 | |
2672 | window->setSource(testFileUrl(fileName: "listview-sections_delegate.qml" )); |
2673 | window->show(); |
2674 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
2675 | |
2676 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
2677 | QTRY_VERIFY(listview != nullptr); |
2678 | QQuickItem *contentItem = listview->contentItem(); |
2679 | QTRY_VERIFY(contentItem != nullptr); |
2680 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2681 | |
2682 | for (int i = 0; i < 3; ++i) { |
2683 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "sect_" + QString::number(i)); |
2684 | QVERIFY(item); |
2685 | QTRY_COMPARE(item->y(), qreal(i*20*6)); |
2686 | } |
2687 | |
2688 | QQuickItem *topItem = findVisibleChild(parent: contentItem, objectName: "sect_0" ); // section header |
2689 | QVERIFY(topItem); |
2690 | QCOMPARE(topItem->y(), 0.); |
2691 | |
2692 | // Insert a full screen of items at the beginning. |
2693 | for (int i = 0; i < 10; i++) |
2694 | model.insertItem(index: i, name: "Item" + QString::number(i), number: QLatin1String("A" )); |
2695 | |
2696 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2697 | |
2698 | int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ).count(); |
2699 | QVERIFY(itemCount > 10); |
2700 | |
2701 | // Verify that the new items are postioned correctly, and have the correct attached section properties |
2702 | for (int i = 0; i < 10 && i < itemCount; ++i) { |
2703 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
2704 | QVERIFY(item); |
2705 | QTRY_COMPARE(item->y(), 20+i*20.0); |
2706 | QCOMPARE(item->property("section" ).toString(), QLatin1String("A" )); |
2707 | QCOMPARE(item->property("nextSection" ).toString(), i < 9 ? QLatin1String("A" ) : QLatin1String("0" )); |
2708 | QCOMPARE(item->property("prevSection" ).toString(), i > 0 ? QLatin1String("A" ) : QLatin1String("" )); |
2709 | } |
2710 | // Verify that the exiting items are postioned correctly, and have the correct attached section properties |
2711 | for (int i = 10; i < 15 && i < itemCount; ++i) { |
2712 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
2713 | QVERIFY(item); |
2714 | QTRY_COMPARE(item->y(), 40+i*20.0); |
2715 | QCOMPARE(item->property("section" ).toString(), QLatin1String("0" )); |
2716 | QCOMPARE(item->property("nextSection" ).toString(), i < 14 ? QLatin1String("0" ) : QLatin1String("1" )); |
2717 | QCOMPARE(item->property("prevSection" ).toString(), i > 10 ? QLatin1String("0" ) : QLatin1String("A" )); |
2718 | } |
2719 | } |
2720 | |
2721 | void tst_QQuickListView::sectionsSnap_data() |
2722 | { |
2723 | QTest::addColumn<QQuickListView::SnapMode>(name: "snapMode" ); |
2724 | QTest::addColumn<QPoint>(name: "point" ); |
2725 | QTest::addColumn<int>(name: "duration" ); |
2726 | |
2727 | QTest::newRow(dataTag: "drag" ) << QQuickListView::NoSnap << QPoint(100, 45) << 500; |
2728 | QTest::newRow(dataTag: "flick" ) << QQuickListView::SnapOneItem << QPoint(100, 60) << 100; |
2729 | } |
2730 | |
2731 | void tst_QQuickListView::sectionsSnap() |
2732 | { |
2733 | QFETCH(QQuickListView::SnapMode, snapMode); |
2734 | QFETCH(QPoint, point); |
2735 | QFETCH(int, duration); |
2736 | |
2737 | QScopedPointer<QQuickView> window(createView()); |
2738 | window->setSource(testFileUrl(fileName: "sectionSnapping.qml" )); |
2739 | window->show(); |
2740 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
2741 | |
2742 | QQuickListView *listview = qobject_cast<QQuickListView*>(object: window->rootObject()); |
2743 | QTRY_VERIFY(listview != nullptr); |
2744 | listview->setSnapMode(snapMode); |
2745 | |
2746 | QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false); |
2747 | QTRY_COMPARE(listview->currentIndex(), 0); |
2748 | QCOMPARE(listview->contentY(), qreal(-50)); |
2749 | |
2750 | // move down |
2751 | flick(window: window.data(), from: QPoint(100, 100), to: point, duration); |
2752 | QTRY_VERIFY(!listview->isMovingVertically()); |
2753 | QCOMPARE(listview->contentY(), qreal(0)); |
2754 | |
2755 | flick(window: window.data(), from: QPoint(100, 100), to: point, duration); |
2756 | QTRY_VERIFY(!listview->isMovingVertically()); |
2757 | QCOMPARE(listview->contentY(), qreal(50)); |
2758 | |
2759 | flick(window: window.data(), from: QPoint(100, 100), to: point, duration); |
2760 | QTRY_VERIFY(!listview->isMovingVertically()); |
2761 | QCOMPARE(listview->contentY(), qreal(150)); |
2762 | |
2763 | // move back up |
2764 | flick(window: window.data(), from: point, to: QPoint(100, 100), duration); |
2765 | QTRY_VERIFY(!listview->isMovingVertically()); |
2766 | QCOMPARE(listview->contentY(), qreal(50)); |
2767 | |
2768 | flick(window: window.data(), from: point, to: QPoint(100, 100), duration); |
2769 | QTRY_VERIFY(!listview->isMovingVertically()); |
2770 | QCOMPARE(listview->contentY(), qreal(0)); |
2771 | |
2772 | flick(window: window.data(), from: point, to: QPoint(100, 100), duration); |
2773 | QTRY_VERIFY(!listview->isMovingVertically()); |
2774 | QCOMPARE(listview->contentY(), qreal(-50)); |
2775 | } |
2776 | |
2777 | void tst_QQuickListView::currentIndex_delayedItemCreation() |
2778 | { |
2779 | QFETCH(bool, setCurrentToZero); |
2780 | |
2781 | QQuickView *window = getView(); |
2782 | |
2783 | // test currentIndexChanged() is emitted even if currentIndex = 0 on start up |
2784 | // (since the currentItem will have changed and that shares the same index) |
2785 | window->rootContext()->setContextProperty("setCurrentToZero" , setCurrentToZero); |
2786 | |
2787 | window->setSource(testFileUrl(fileName: "fillModelOnComponentCompleted.qml" )); |
2788 | qApp->processEvents(); |
2789 | |
2790 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
2791 | QTRY_VERIFY(listview != nullptr); |
2792 | QQuickItem *contentItem = listview->contentItem(); |
2793 | QTRY_VERIFY(contentItem != nullptr); |
2794 | |
2795 | QSignalSpy spy(listview, SIGNAL(currentItemChanged())); |
2796 | //QCOMPARE(listview->currentIndex(), 0); |
2797 | listview->forceLayout(); |
2798 | QTRY_COMPARE(spy.count(), 1); |
2799 | |
2800 | releaseView(view: window); |
2801 | } |
2802 | |
2803 | void tst_QQuickListView::currentIndex_delayedItemCreation_data() |
2804 | { |
2805 | QTest::addColumn<bool>(name: "setCurrentToZero" ); |
2806 | |
2807 | QTest::newRow(dataTag: "set to 0" ) << true; |
2808 | QTest::newRow(dataTag: "don't set to 0" ) << false; |
2809 | } |
2810 | |
2811 | void tst_QQuickListView::currentIndex() |
2812 | { |
2813 | QaimModel initModel; |
2814 | |
2815 | for (int i = 0; i < 30; i++) |
2816 | initModel.addItem(name: "Item" + QString::number(i), number: QString::number(i)); |
2817 | |
2818 | QScopedPointer<QQuickView> window(new QQuickView(nullptr)); |
2819 | window->setGeometry(posx: 0,posy: 0,w: 240,h: 320); |
2820 | |
2821 | QQmlContext *ctxt = window->rootContext(); |
2822 | ctxt->setContextProperty("testModel" , &initModel); |
2823 | ctxt->setContextProperty("testWrap" , QVariant(false)); |
2824 | |
2825 | QString filename(testFile(fileName: "listview-initCurrent.qml" )); |
2826 | window->setSource(QUrl::fromLocalFile(localfile: filename)); |
2827 | window->show(); |
2828 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
2829 | |
2830 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
2831 | QTRY_VERIFY(listview != nullptr); |
2832 | QQuickItem *contentItem = listview->contentItem(); |
2833 | QTRY_VERIFY(contentItem != nullptr); |
2834 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2835 | |
2836 | // currentIndex is initialized to 20 |
2837 | // currentItem should be in view |
2838 | QCOMPARE(listview->currentIndex(), 20); |
2839 | QCOMPARE(listview->contentY(), 100.0); |
2840 | QCOMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper" , 20)); |
2841 | QCOMPARE(listview->highlightItem()->y(), listview->currentItem()->y()); |
2842 | |
2843 | // changing model should reset currentIndex to 0 |
2844 | QaimModel model; |
2845 | for (int i = 0; i < 30; i++) |
2846 | model.addItem(name: "Item" + QString::number(i), number: QString::number(i)); |
2847 | ctxt->setContextProperty("testModel" , &model); |
2848 | listview->forceLayout(); |
2849 | |
2850 | QCOMPARE(listview->currentIndex(), 0); |
2851 | QCOMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper" , 0)); |
2852 | |
2853 | // confirm that the velocity is updated |
2854 | listview->setCurrentIndex(20); |
2855 | QTRY_VERIFY(listview->verticalVelocity() != 0.0); |
2856 | listview->setCurrentIndex(0); |
2857 | QTRY_COMPARE(listview->verticalVelocity(), 0.0); |
2858 | |
2859 | // footer should become visible if it is out of view, and then current index is set to count-1 |
2860 | window->rootObject()->setProperty(name: "showFooter" , value: true); |
2861 | QTRY_VERIFY(listview->footerItem()); |
2862 | listview->setCurrentIndex(model.count()-2); |
2863 | QTRY_VERIFY(listview->footerItem()->y() > listview->contentY() + listview->height()); |
2864 | listview->setCurrentIndex(model.count()-1); |
2865 | QTRY_COMPARE(listview->contentY() + listview->height(), (20.0 * model.count()) + listview->footerItem()->height()); |
2866 | window->rootObject()->setProperty(name: "showFooter" , value: false); |
2867 | |
2868 | // header should become visible if it is out of view, and then current index is set to 0 |
2869 | window->rootObject()->setProperty(name: "showHeader" , value: true); |
2870 | QTRY_VERIFY(listview->headerItem()); |
2871 | listview->setCurrentIndex(1); |
2872 | QTRY_VERIFY(listview->headerItem()->y() + listview->headerItem()->height() < listview->contentY()); |
2873 | listview->setCurrentIndex(0); |
2874 | QTRY_COMPARE(listview->contentY(), -listview->headerItem()->height()); |
2875 | window->rootObject()->setProperty(name: "showHeader" , value: false); |
2876 | |
2877 | // turn off auto highlight |
2878 | listview->setHighlightFollowsCurrentItem(false); |
2879 | QVERIFY(!listview->highlightFollowsCurrentItem()); |
2880 | |
2881 | QVERIFY(listview->highlightItem()); |
2882 | qreal hlPos = listview->highlightItem()->y(); |
2883 | |
2884 | listview->setCurrentIndex(4); |
2885 | QTRY_COMPARE(listview->highlightItem()->y(), hlPos); |
2886 | |
2887 | // insert item before currentIndex |
2888 | window->rootObject()->setProperty(name: "currentItemChangedCount" , value: QVariant(0)); |
2889 | listview->setCurrentIndex(28); |
2890 | QTRY_COMPARE(window->rootObject()->property("currentItemChangedCount" ).toInt(), 1); |
2891 | model.insertItem(index: 0, name: "Foo" , number: "1111" ); |
2892 | QTRY_COMPARE(window->rootObject()->property("current" ).toInt(), 29); |
2893 | QCOMPARE(window->rootObject()->property("currentItemChangedCount" ).toInt(), 1); |
2894 | |
2895 | // check removing highlight by setting currentIndex to -1; |
2896 | listview->setCurrentIndex(-1); |
2897 | |
2898 | QCOMPARE(listview->currentIndex(), -1); |
2899 | QVERIFY(!listview->highlightItem()); |
2900 | QVERIFY(!listview->currentItem()); |
2901 | |
2902 | // moving currentItem out of view should make it invisible |
2903 | listview->setCurrentIndex(0); |
2904 | QTRY_VERIFY(delegateVisible(listview->currentItem())); |
2905 | listview->setContentY(200); |
2906 | QTRY_VERIFY(!delegateVisible(listview->currentItem())); |
2907 | |
2908 | // empty model should reset currentIndex to -1 |
2909 | QaimModel emptyModel; |
2910 | window->rootObject()->setProperty(name: "currentItemChangedCount" , value: QVariant(0)); |
2911 | QVERIFY(QQmlProperty(window->rootObject(), "s" ).read().toString() != QLatin1String("-1" )); |
2912 | ctxt->setContextProperty("testModel" , &emptyModel); |
2913 | QCOMPARE(QQmlProperty(window->rootObject(), "s" ).read().toString(), "-1" ); |
2914 | QCOMPARE(window->rootObject()->property("currentItemChangedCount" ).toInt(), 1); |
2915 | QCOMPARE(listview->currentIndex(), -1); |
2916 | } |
2917 | |
2918 | void tst_QQuickListView::noCurrentIndex() |
2919 | { |
2920 | QaimModel model; |
2921 | for (int i = 0; i < 30; i++) |
2922 | model.addItem(name: "Item" + QString::number(i), number: QString::number(i)); |
2923 | |
2924 | QScopedPointer<QQuickView> window(new QQuickView(nullptr)); |
2925 | window->setGeometry(posx: 0,posy: 0,w: 240,h: 320); |
2926 | |
2927 | QQmlContext *ctxt = window->rootContext(); |
2928 | ctxt->setContextProperty("testModel" , &model); |
2929 | |
2930 | QString filename(testFile(fileName: "listview-noCurrent.qml" )); |
2931 | window->setSource(QUrl::fromLocalFile(localfile: filename)); |
2932 | window->show(); |
2933 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
2934 | |
2935 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
2936 | QTRY_VERIFY(listview != nullptr); |
2937 | QQuickItem *contentItem = listview->contentItem(); |
2938 | QTRY_VERIFY(contentItem != nullptr); |
2939 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2940 | |
2941 | // current index should be -1 at startup |
2942 | // and we should not have a currentItem or highlightItem |
2943 | QCOMPARE(listview->currentIndex(), -1); |
2944 | QCOMPARE(listview->contentY(), 0.0); |
2945 | QVERIFY(!listview->highlightItem()); |
2946 | QVERIFY(!listview->currentItem()); |
2947 | |
2948 | listview->setCurrentIndex(2); |
2949 | QCOMPARE(listview->currentIndex(), 2); |
2950 | QVERIFY(listview->highlightItem()); |
2951 | QVERIFY(listview->currentItem()); |
2952 | } |
2953 | |
2954 | void tst_QQuickListView::keyNavigation() |
2955 | { |
2956 | QFETCH(QQuickListView::Orientation, orientation); |
2957 | QFETCH(Qt::LayoutDirection, layoutDirection); |
2958 | QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection); |
2959 | QFETCH(Qt::Key, forwardsKey); |
2960 | QFETCH(Qt::Key, backwardsKey); |
2961 | QFETCH(QPointF, contentPosAtFirstItem); |
2962 | QFETCH(QPointF, contentPosAtLastItem); |
2963 | |
2964 | QaimModel model; |
2965 | for (int i = 0; i < 30; i++) |
2966 | model.addItem(name: "Item" + QString::number(i), number: "" ); |
2967 | |
2968 | QQuickView *window = getView(); |
2969 | QScopedPointer<TestObject> testObject(new TestObject); |
2970 | window->rootContext()->setContextProperty("testModel" , &model); |
2971 | window->rootContext()->setContextProperty("testObject" , testObject.data()); |
2972 | window->setSource(testFileUrl(fileName: "listviewtest.qml" )); |
2973 | window->show(); |
2974 | QVERIFY(QTest::qWaitForWindowActive(window)); |
2975 | |
2976 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
2977 | QTRY_VERIFY(listview != nullptr); |
2978 | |
2979 | listview->setOrientation(orientation); |
2980 | listview->setLayoutDirection(layoutDirection); |
2981 | listview->setVerticalLayoutDirection(verticalLayoutDirection); |
2982 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
2983 | |
2984 | window->requestActivate(); |
2985 | QVERIFY(QTest::qWaitForWindowActive(window)); |
2986 | QTRY_COMPARE(qGuiApp->focusWindow(), window); |
2987 | |
2988 | QTest::keyClick(window, key: forwardsKey); |
2989 | QCOMPARE(listview->currentIndex(), 1); |
2990 | |
2991 | QTest::keyClick(window, key: backwardsKey); |
2992 | QCOMPARE(listview->currentIndex(), 0); |
2993 | |
2994 | // hold down a key to go forwards |
2995 | for (int i=0; i<model.count()-1; i++) { |
2996 | QTest::simulateEvent(window, press: true, code: forwardsKey, modifier: Qt::NoModifier, text: "" , repeat: true); |
2997 | QCOMPARE(listview->currentIndex(), i+1); |
2998 | } |
2999 | QTest::keyRelease(window, key: forwardsKey); |
3000 | listview->forceLayout(); |
3001 | QTRY_COMPARE(listview->currentIndex(), model.count()-1); |
3002 | QTRY_COMPARE(listview->contentX(), contentPosAtLastItem.x()); |
3003 | QTRY_COMPARE(listview->contentY(), contentPosAtLastItem.y()); |
3004 | |
3005 | // hold down a key to go backwards |
3006 | for (int i=model.count()-1; i > 0; i--) { |
3007 | QTest::simulateEvent(window, press: true, code: backwardsKey, modifier: Qt::NoModifier, text: "" , repeat: true); |
3008 | QTRY_COMPARE(listview->currentIndex(), i-1); |
3009 | } |
3010 | QTest::keyRelease(window, key: backwardsKey); |
3011 | listview->forceLayout(); |
3012 | QTRY_COMPARE(listview->currentIndex(), 0); |
3013 | QTRY_COMPARE(listview->contentX(), contentPosAtFirstItem.x()); |
3014 | QTRY_COMPARE(listview->contentY(), contentPosAtFirstItem.y()); |
3015 | |
3016 | // no wrap |
3017 | QVERIFY(!listview->isWrapEnabled()); |
3018 | listview->incrementCurrentIndex(); |
3019 | QCOMPARE(listview->currentIndex(), 1); |
3020 | listview->decrementCurrentIndex(); |
3021 | QCOMPARE(listview->currentIndex(), 0); |
3022 | |
3023 | listview->decrementCurrentIndex(); |
3024 | QCOMPARE(listview->currentIndex(), 0); |
3025 | |
3026 | // with wrap |
3027 | listview->setWrapEnabled(true); |
3028 | QVERIFY(listview->isWrapEnabled()); |
3029 | |
3030 | listview->decrementCurrentIndex(); |
3031 | QCOMPARE(listview->currentIndex(), model.count()-1); |
3032 | QTRY_COMPARE(listview->contentX(), contentPosAtLastItem.x()); |
3033 | QTRY_COMPARE(listview->contentY(), contentPosAtLastItem.y()); |
3034 | |
3035 | listview->incrementCurrentIndex(); |
3036 | QCOMPARE(listview->currentIndex(), 0); |
3037 | QTRY_COMPARE(listview->contentX(), contentPosAtFirstItem.x()); |
3038 | QTRY_COMPARE(listview->contentY(), contentPosAtFirstItem.y()); |
3039 | |
3040 | releaseView(view: window); |
3041 | } |
3042 | |
3043 | void tst_QQuickListView::keyNavigation_data() |
3044 | { |
3045 | QTest::addColumn<QQuickListView::Orientation>(name: "orientation" ); |
3046 | QTest::addColumn<Qt::LayoutDirection>(name: "layoutDirection" ); |
3047 | QTest::addColumn<QQuickItemView::VerticalLayoutDirection>(name: "verticalLayoutDirection" ); |
3048 | QTest::addColumn<Qt::Key>(name: "forwardsKey" ); |
3049 | QTest::addColumn<Qt::Key>(name: "backwardsKey" ); |
3050 | QTest::addColumn<QPointF>(name: "contentPosAtFirstItem" ); |
3051 | QTest::addColumn<QPointF>(name: "contentPosAtLastItem" ); |
3052 | |
3053 | QTest::newRow(dataTag: "Vertical, TopToBottom" ) |
3054 | << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom |
3055 | << Qt::Key_Down << Qt::Key_Up |
3056 | << QPointF(0, 0) |
3057 | << QPointF(0, 30*20 - 320); |
3058 | |
3059 | QTest::newRow(dataTag: "Vertical, BottomToTop" ) |
3060 | << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::BottomToTop |
3061 | << Qt::Key_Up << Qt::Key_Down |
3062 | << QPointF(0, -320) |
3063 | << QPointF(0, -(30 * 20)); |
3064 | |
3065 | QTest::newRow(dataTag: "Horizontal, LeftToRight" ) |
3066 | << QQuickListView::Horizontal << Qt::LeftToRight << QQuickItemView::TopToBottom |
3067 | << Qt::Key_Right << Qt::Key_Left |
3068 | << QPointF(0, 0) |
3069 | << QPointF(30*240 - 240, 0); |
3070 | |
3071 | QTest::newRow(dataTag: "Horizontal, RightToLeft" ) |
3072 | << QQuickListView::Horizontal << Qt::RightToLeft << QQuickItemView::TopToBottom |
3073 | << Qt::Key_Left << Qt::Key_Right |
3074 | << QPointF(-240, 0) |
3075 | << QPointF(-(30 * 240), 0); |
3076 | } |
3077 | |
3078 | void tst_QQuickListView::itemList() |
3079 | { |
3080 | QScopedPointer<QQuickView> window(createView()); |
3081 | window->setSource(testFileUrl(fileName: "itemlist.qml" )); |
3082 | window->show(); |
3083 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
3084 | |
3085 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "view" ); |
3086 | QTRY_VERIFY(listview != nullptr); |
3087 | |
3088 | QQuickItem *contentItem = listview->contentItem(); |
3089 | QTRY_VERIFY(contentItem != nullptr); |
3090 | |
3091 | QQmlObjectModel *model = window->rootObject()->findChild<QQmlObjectModel*>(aName: "itemModel" ); |
3092 | QTRY_VERIFY(model != nullptr); |
3093 | |
3094 | QTRY_COMPARE(model->count(), 3); |
3095 | QTRY_COMPARE(listview->currentIndex(), 0); |
3096 | |
3097 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "item1" ); |
3098 | QTRY_VERIFY(item); |
3099 | QTRY_COMPARE(item->x(), 0.0); |
3100 | QCOMPARE(item->height(), listview->height()); |
3101 | |
3102 | QQuickText *text = findItem<QQuickText>(parent: contentItem, objectName: "text1" ); |
3103 | QTRY_VERIFY(text); |
3104 | QTRY_COMPARE(text->text(), QLatin1String("index: 0" )); |
3105 | |
3106 | listview->setCurrentIndex(2); |
3107 | |
3108 | item = findItem<QQuickItem>(parent: contentItem, objectName: "item3" ); |
3109 | QTRY_VERIFY(item); |
3110 | QTRY_COMPARE(item->x(), 480.0); |
3111 | |
3112 | text = findItem<QQuickText>(parent: contentItem, objectName: "text3" ); |
3113 | QTRY_VERIFY(text); |
3114 | QTRY_COMPARE(text->text(), QLatin1String("index: 2" )); |
3115 | } |
3116 | |
3117 | void tst_QQuickListView::itemListFlicker() |
3118 | { |
3119 | QScopedPointer<QQuickView> window(createView()); |
3120 | window->setSource(testFileUrl(fileName: "itemlist-flicker.qml" )); |
3121 | window->show(); |
3122 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
3123 | |
3124 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "view" ); |
3125 | QTRY_VERIFY(listview != nullptr); |
3126 | |
3127 | QQuickItem *contentItem = listview->contentItem(); |
3128 | QTRY_VERIFY(contentItem != nullptr); |
3129 | |
3130 | QQmlObjectModel *model = window->rootObject()->findChild<QQmlObjectModel*>(aName: "itemModel" ); |
3131 | QTRY_VERIFY(model != nullptr); |
3132 | |
3133 | QTRY_COMPARE(model->count(), 3); |
3134 | QTRY_COMPARE(listview->currentIndex(), 0); |
3135 | |
3136 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "item1" ); |
3137 | QVERIFY(item); |
3138 | QVERIFY(delegateVisible(item)); |
3139 | item = findItem<QQuickItem>(parent: contentItem, objectName: "item2" ); |
3140 | QVERIFY(item); |
3141 | QVERIFY(delegateVisible(item)); |
3142 | item = findItem<QQuickItem>(parent: contentItem, objectName: "item3" ); |
3143 | QVERIFY(item); |
3144 | QVERIFY(delegateVisible(item)); |
3145 | |
3146 | listview->setCurrentIndex(1); |
3147 | |
3148 | item = findItem<QQuickItem>(parent: contentItem, objectName: "item1" ); |
3149 | QVERIFY(item); |
3150 | QVERIFY(delegateVisible(item)); |
3151 | item = findItem<QQuickItem>(parent: contentItem, objectName: "item2" ); |
3152 | QVERIFY(item); |
3153 | QVERIFY(delegateVisible(item)); |
3154 | item = findItem<QQuickItem>(parent: contentItem, objectName: "item3" ); |
3155 | QVERIFY(item); |
3156 | QVERIFY(delegateVisible(item)); |
3157 | |
3158 | listview->setCurrentIndex(2); |
3159 | |
3160 | item = findItem<QQuickItem>(parent: contentItem, objectName: "item1" ); |
3161 | QVERIFY(item); |
3162 | QVERIFY(delegateVisible(item)); |
3163 | item = findItem<QQuickItem>(parent: contentItem, objectName: "item2" ); |
3164 | QVERIFY(item); |
3165 | QVERIFY(delegateVisible(item)); |
3166 | item = findItem<QQuickItem>(parent: contentItem, objectName: "item3" ); |
3167 | QVERIFY(item); |
3168 | QVERIFY(delegateVisible(item)); |
3169 | } |
3170 | |
3171 | void tst_QQuickListView::cacheBuffer() |
3172 | { |
3173 | QScopedPointer<QQuickView> window(createView()); |
3174 | |
3175 | QaimModel model; |
3176 | for (int i = 0; i < 90; i++) |
3177 | model.addItem(name: "Item" + QString::number(i), number: "" ); |
3178 | |
3179 | QQmlContext *ctxt = window->rootContext(); |
3180 | ctxt->setContextProperty("testModel" , &model); |
3181 | |
3182 | QScopedPointer<TestObject> testObject(new TestObject); |
3183 | ctxt->setContextProperty("testObject" , testObject.data()); |
3184 | |
3185 | window->setSource(testFileUrl(fileName: "listviewtest.qml" )); |
3186 | window->show(); |
3187 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
3188 | |
3189 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
3190 | QTRY_VERIFY(listview != nullptr); |
3191 | |
3192 | QQuickItem *contentItem = listview->contentItem(); |
3193 | QTRY_VERIFY(contentItem != nullptr); |
3194 | QTRY_VERIFY(listview->delegate() != nullptr); |
3195 | QTRY_VERIFY(listview->model() != 0); |
3196 | QTRY_VERIFY(listview->highlight() != nullptr); |
3197 | |
3198 | // Confirm items positioned correctly |
3199 | int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ).count(); |
3200 | for (int i = 0; i < model.count() && i < itemCount; ++i) { |
3201 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
3202 | if (!item) qWarning() << "Item" << i << "not found" ; |
3203 | QTRY_VERIFY(item); |
3204 | QTRY_COMPARE(item->y(), qreal(i*20)); |
3205 | } |
3206 | |
3207 | QQmlIncubationController controller; |
3208 | window->engine()->setIncubationController(&controller); |
3209 | |
3210 | testObject->setCacheBuffer(200); |
3211 | QTRY_COMPARE(listview->cacheBuffer(), 200); |
3212 | |
3213 | // items will be created one at a time |
3214 | for (int i = itemCount; i < qMin(a: itemCount+10,b: model.count()); ++i) { |
3215 | QVERIFY(findItem<QQuickItem>(listview, "wrapper" , i) == nullptr); |
3216 | QQuickItem *item = nullptr; |
3217 | while (!item) { |
3218 | bool b = false; |
3219 | controller.incubateWhile(flag: &b); |
3220 | item = findItem<QQuickItem>(parent: listview, objectName: "wrapper" , index: i); |
3221 | } |
3222 | } |
3223 | |
3224 | { |
3225 | bool b = true; |
3226 | controller.incubateWhile(flag: &b); |
3227 | } |
3228 | |
3229 | int newItemCount = 0; |
3230 | newItemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ).count(); |
3231 | |
3232 | // Confirm items positioned correctly |
3233 | for (int i = 0; i < model.count() && i < newItemCount; ++i) { |
3234 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
3235 | if (!item) qWarning() << "Item" << i << "not found" ; |
3236 | QTRY_VERIFY(item); |
3237 | QTRY_COMPARE(item->y(), qreal(i*20)); |
3238 | } |
3239 | |
3240 | // move view and confirm items in view are visible immediately and outside are created async |
3241 | listview->setContentY(300); |
3242 | |
3243 | for (int i = 15; i < 32; ++i) { |
3244 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
3245 | if (!item) qWarning() << "Item" << i << "not found" ; |
3246 | QVERIFY(item); |
3247 | QCOMPARE(item->y(), qreal(i*20)); |
3248 | } |
3249 | |
3250 | QVERIFY(findItem<QQuickItem>(listview, "wrapper" , 32) == nullptr); |
3251 | |
3252 | // ensure buffered items are created |
3253 | for (int i = 32; i < qMin(a: 41,b: model.count()); ++i) { |
3254 | QQuickItem *item = nullptr; |
3255 | while (!item) { |
3256 | qGuiApp->processEvents(); // allow refill to happen |
3257 | bool b = false; |
3258 | controller.incubateWhile(flag: &b); |
3259 | item = findItem<QQuickItem>(parent: listview, objectName: "wrapper" , index: i); |
3260 | } |
3261 | } |
3262 | |
3263 | { |
3264 | bool b = true; |
3265 | controller.incubateWhile(flag: &b); |
3266 | } |
3267 | |
3268 | // negative cache buffer is ignored |
3269 | listview->setCacheBuffer(-1); |
3270 | QCOMPARE(listview->cacheBuffer(), 200); |
3271 | } |
3272 | |
3273 | void tst_QQuickListView::positionViewAtBeginningEnd() |
3274 | { |
3275 | QScopedPointer<QQuickView> window(createView()); |
3276 | |
3277 | QaimModel model; |
3278 | for (int i = 0; i < 40; i++) |
3279 | model.addItem(name: "Item" + QString::number(i), number: "" ); |
3280 | |
3281 | QQmlContext *ctxt = window->rootContext(); |
3282 | ctxt->setContextProperty("testModel" , &model); |
3283 | |
3284 | QScopedPointer<TestObject> testObject(new TestObject); |
3285 | ctxt->setContextProperty("testObject" , testObject.data()); |
3286 | window->show(); |
3287 | window->setSource(testFileUrl(fileName: "listviewtest.qml" )); |
3288 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
3289 | |
3290 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
3291 | QTRY_VERIFY(listview != nullptr); |
3292 | QQuickItem *contentItem = listview->contentItem(); |
3293 | QTRY_VERIFY(contentItem != nullptr); |
3294 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
3295 | |
3296 | listview->setContentY(100); |
3297 | |
3298 | // positionAtBeginnging |
3299 | listview->positionViewAtBeginning(); |
3300 | QTRY_COMPARE(listview->contentY(), 0.); |
3301 | |
3302 | listview->setContentY(80); |
3303 | window->rootObject()->setProperty(name: "showHeader" , value: true); |
3304 | listview->positionViewAtBeginning(); |
3305 | QTRY_COMPARE(listview->contentY(), -30.); |
3306 | |
3307 | // positionAtEnd |
3308 | listview->positionViewAtEnd(); |
3309 | QTRY_COMPARE(listview->contentY(), 480.); // 40*20 - 320 |
3310 | |
3311 | listview->setContentY(80); |
3312 | window->rootObject()->setProperty(name: "showFooter" , value: true); |
3313 | listview->positionViewAtEnd(); |
3314 | QTRY_COMPARE(listview->contentY(), 510.); |
3315 | |
3316 | // set current item to outside visible view, position at beginning |
3317 | // and ensure highlight moves to current item |
3318 | listview->setCurrentIndex(1); |
3319 | listview->positionViewAtBeginning(); |
3320 | QTRY_COMPARE(listview->contentY(), -30.); |
3321 | QVERIFY(listview->highlightItem()); |
3322 | QCOMPARE(listview->highlightItem()->y(), 20.); |
3323 | } |
3324 | |
3325 | void tst_QQuickListView::positionViewAtIndex() |
3326 | { |
3327 | QFETCH(bool, enforceRange); |
3328 | QFETCH(qreal, initContentY); |
3329 | QFETCH(int, index); |
3330 | QFETCH(QQuickListView::PositionMode, mode); |
3331 | QFETCH(qreal, contentY); |
3332 | |
3333 | QQuickView *window = getView(); |
3334 | |
3335 | QaimModel model; |
3336 | for (int i = 0; i < 40; i++) |
3337 | model.addItem(name: "Item" + QString::number(i), number: "" ); |
3338 | |
3339 | QQmlContext *ctxt = window->rootContext(); |
3340 | ctxt->setContextProperty("testModel" , &model); |
3341 | |
3342 | QScopedPointer<TestObject> testObject(new TestObject); |
3343 | ctxt->setContextProperty("testObject" , testObject.data()); |
3344 | window->show(); |
3345 | window->setSource(testFileUrl(fileName: "listviewtest.qml" )); |
3346 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
3347 | |
3348 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
3349 | QTRY_VERIFY(listview != nullptr); |
3350 | QQuickItem *contentItem = listview->contentItem(); |
3351 | QTRY_VERIFY(contentItem != nullptr); |
3352 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
3353 | |
3354 | window->rootObject()->setProperty(name: "enforceRange" , value: enforceRange); |
3355 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
3356 | |
3357 | listview->setContentY(initContentY); |
3358 | |
3359 | listview->positionViewAtIndex(index, mode); |
3360 | QTRY_COMPARE(listview->contentY(), contentY); |
3361 | |
3362 | // Confirm items positioned correctly |
3363 | int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ).count(); |
3364 | for (int i = index; i < model.count() && i < itemCount-index-1; ++i) { |
3365 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
3366 | if (!item) qWarning() << "Item" << i << "not found" ; |
3367 | QTRY_VERIFY(item); |
3368 | QTRY_COMPARE(item->y(), i*20.); |
3369 | } |
3370 | |
3371 | releaseView(view: window); |
3372 | } |
3373 | |
3374 | void tst_QQuickListView::positionViewAtIndex_data() |
3375 | { |
3376 | QTest::addColumn<bool>(name: "enforceRange" ); |
3377 | QTest::addColumn<qreal>(name: "initContentY" ); |
3378 | QTest::addColumn<int>(name: "index" ); |
3379 | QTest::addColumn<QQuickListView::PositionMode>(name: "mode" ); |
3380 | QTest::addColumn<qreal>(name: "contentY" ); |
3381 | |
3382 | QTest::newRow(dataTag: "no range, 3 at Beginning" ) << false << 0. << 3 << QQuickListView::Beginning << 60.; |
3383 | QTest::newRow(dataTag: "no range, 3 at End" ) << false << 0. << 3 << QQuickListView::End << 0.; |
3384 | QTest::newRow(dataTag: "no range, 22 at Beginning" ) << false << 0. << 22 << QQuickListView::Beginning << 440.; |
3385 | // Position on an item that would leave empty space if positioned at the top |
3386 | QTest::newRow(dataTag: "no range, 28 at Beginning" ) << false << 0. << 28 << QQuickListView::Beginning << 480.; |
3387 | // Position at End using last index |
3388 | QTest::newRow(dataTag: "no range, last at End" ) << false << 0. << 39 << QQuickListView::End << 480.; |
3389 | // Position at End |
3390 | QTest::newRow(dataTag: "no range, 20 at End" ) << false << 0. << 20 << QQuickListView::End << 100.; |
3391 | // Position in Center |
3392 | QTest::newRow(dataTag: "no range, 15 at Center" ) << false << 0. << 15 << QQuickListView::Center << 150.; |
3393 | // Ensure at least partially visible |
3394 | QTest::newRow(dataTag: "no range, 15 visible => Visible" ) << false << 150. << 15 << QQuickListView::Visible << 150.; |
3395 | QTest::newRow(dataTag: "no range, 15 partially visible => Visible" ) << false << 302. << 15 << QQuickListView::Visible << 302.; |
3396 | QTest::newRow(dataTag: "no range, 15 before visible => Visible" ) << false << 320. << 15 << QQuickListView::Visible << 300.; |
3397 | QTest::newRow(dataTag: "no range, 20 visible => Visible" ) << false << 85. << 20 << QQuickListView::Visible << 85.; |
3398 | QTest::newRow(dataTag: "no range, 20 before visible => Visible" ) << false << 75. << 20 << QQuickListView::Visible << 100.; |
3399 | QTest::newRow(dataTag: "no range, 20 after visible => Visible" ) << false << 480. << 20 << QQuickListView::Visible << 400.; |
3400 | // Ensure completely visible |
3401 | QTest::newRow(dataTag: "no range, 20 visible => Contain" ) << false << 120. << 20 << QQuickListView::Contain << 120.; |
3402 | QTest::newRow(dataTag: "no range, 15 partially visible => Contain" ) << false << 302. << 15 << QQuickListView::Contain << 300.; |
3403 | QTest::newRow(dataTag: "no range, 20 partially visible => Contain" ) << false << 85. << 20 << QQuickListView::Contain << 100.; |
3404 | |
3405 | QTest::newRow(dataTag: "strict range, 3 at End" ) << true << 0. << 3 << QQuickListView::End << -120.; |
3406 | QTest::newRow(dataTag: "strict range, 38 at Beginning" ) << true << 0. << 38 << QQuickListView::Beginning << 660.; |
3407 | QTest::newRow(dataTag: "strict range, 15 at Center" ) << true << 0. << 15 << QQuickListView::Center << 140.; |
3408 | QTest::newRow(dataTag: "strict range, 3 at SnapPosition" ) << true << 0. << 3 << QQuickListView::SnapPosition << -60.; |
3409 | QTest::newRow(dataTag: "strict range, 10 at SnapPosition" ) << true << 0. << 10 << QQuickListView::SnapPosition << 80.; |
3410 | QTest::newRow(dataTag: "strict range, 38 at SnapPosition" ) << true << 0. << 38 << QQuickListView::SnapPosition << 640.; |
3411 | } |
3412 | |
3413 | void tst_QQuickListView::resetModel() |
3414 | { |
3415 | QScopedPointer<QQuickView> window(createView()); |
3416 | |
3417 | QStringList strings; |
3418 | strings << "one" << "two" << "three" ; |
3419 | QStringListModel model(strings); |
3420 | |
3421 | QQmlContext *ctxt = window->rootContext(); |
3422 | ctxt->setContextProperty("testModel" , &model); |
3423 | |
3424 | window->setSource(testFileUrl(fileName: "displaylist.qml" )); |
3425 | window->show(); |
3426 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
3427 | |
3428 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
3429 | QTRY_VERIFY(listview != nullptr); |
3430 | QQuickItem *contentItem = listview->contentItem(); |
3431 | QTRY_VERIFY(contentItem != nullptr); |
3432 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
3433 | |
3434 | QTRY_COMPARE(listview->count(), model.rowCount()); |
3435 | |
3436 | for (int i = 0; i < model.rowCount(); ++i) { |
3437 | QQuickText *display = findItem<QQuickText>(parent: contentItem, objectName: "displayText" , index: i); |
3438 | QTRY_VERIFY(display != nullptr); |
3439 | QTRY_COMPARE(display->text(), strings.at(i)); |
3440 | } |
3441 | |
3442 | strings.clear(); |
3443 | strings << "four" << "five" << "six" << "seven" ; |
3444 | model.setStringList(strings); |
3445 | |
3446 | listview->forceLayout(); |
3447 | QTRY_COMPARE(listview->count(), model.rowCount()); |
3448 | |
3449 | for (int i = 0; i < model.rowCount(); ++i) { |
3450 | QQuickText *display = findItem<QQuickText>(parent: contentItem, objectName: "displayText" , index: i); |
3451 | QTRY_VERIFY(display != nullptr); |
3452 | QTRY_COMPARE(display->text(), strings.at(i)); |
3453 | } |
3454 | } |
3455 | |
3456 | void tst_QQuickListView::propertyChanges() |
3457 | { |
3458 | QScopedPointer<QQuickView> window(createView()); |
3459 | window->setSource(testFileUrl(fileName: "propertychangestest.qml" )); |
3460 | |
3461 | QQuickListView *listView = window->rootObject()->findChild<QQuickListView*>(aName: "listView" ); |
3462 | QTRY_VERIFY(listView); |
3463 | |
3464 | QSignalSpy highlightFollowsCurrentItemSpy(listView, SIGNAL(highlightFollowsCurrentItemChanged())); |
3465 | QSignalSpy preferredHighlightBeginSpy(listView, SIGNAL(preferredHighlightBeginChanged())); |
3466 | QSignalSpy preferredHighlightEndSpy(listView, SIGNAL(preferredHighlightEndChanged())); |
3467 | QSignalSpy highlightRangeModeSpy(listView, SIGNAL(highlightRangeModeChanged())); |
3468 | QSignalSpy keyNavigationWrapsSpy(listView, SIGNAL(keyNavigationWrapsChanged())); |
3469 | QSignalSpy cacheBufferSpy(listView, SIGNAL(cacheBufferChanged())); |
3470 | QSignalSpy snapModeSpy(listView, SIGNAL(snapModeChanged())); |
3471 | |
3472 | QTRY_COMPARE(listView->highlightFollowsCurrentItem(), true); |
3473 | QTRY_COMPARE(listView->preferredHighlightBegin(), 0.0); |
3474 | QTRY_COMPARE(listView->preferredHighlightEnd(), 0.0); |
3475 | QTRY_COMPARE(listView->highlightRangeMode(), QQuickListView::ApplyRange); |
3476 | QTRY_COMPARE(listView->isWrapEnabled(), true); |
3477 | QTRY_COMPARE(listView->cacheBuffer(), 10); |
3478 | QTRY_COMPARE(listView->snapMode(), QQuickListView::SnapToItem); |
3479 | |
3480 | listView->setHighlightFollowsCurrentItem(false); |
3481 | listView->setPreferredHighlightBegin(1.0); |
3482 | listView->setPreferredHighlightEnd(1.0); |
3483 | listView->setHighlightRangeMode(QQuickListView::StrictlyEnforceRange); |
3484 | listView->setWrapEnabled(false); |
3485 | listView->setCacheBuffer(3); |
3486 | listView->setSnapMode(QQuickListView::SnapOneItem); |
3487 | |
3488 | QTRY_COMPARE(listView->highlightFollowsCurrentItem(), false); |
3489 | QTRY_COMPARE(listView->preferredHighlightBegin(), 1.0); |
3490 | QTRY_COMPARE(listView->preferredHighlightEnd(), 1.0); |
3491 | QTRY_COMPARE(listView->highlightRangeMode(), QQuickListView::StrictlyEnforceRange); |
3492 | QTRY_COMPARE(listView->isWrapEnabled(), false); |
3493 | QTRY_COMPARE(listView->cacheBuffer(), 3); |
3494 | QTRY_COMPARE(listView->snapMode(), QQuickListView::SnapOneItem); |
3495 | |
3496 | QTRY_COMPARE(highlightFollowsCurrentItemSpy.count(),1); |
3497 | QTRY_COMPARE(preferredHighlightBeginSpy.count(),1); |
3498 | QTRY_COMPARE(preferredHighlightEndSpy.count(),1); |
3499 | QTRY_COMPARE(highlightRangeModeSpy.count(),1); |
3500 | QTRY_COMPARE(keyNavigationWrapsSpy.count(),1); |
3501 | QTRY_COMPARE(cacheBufferSpy.count(),1); |
3502 | QTRY_COMPARE(snapModeSpy.count(),1); |
3503 | |
3504 | listView->setHighlightFollowsCurrentItem(false); |
3505 | listView->setPreferredHighlightBegin(1.0); |
3506 | listView->setPreferredHighlightEnd(1.0); |
3507 | listView->setHighlightRangeMode(QQuickListView::StrictlyEnforceRange); |
3508 | listView->setWrapEnabled(false); |
3509 | listView->setCacheBuffer(3); |
3510 | listView->setSnapMode(QQuickListView::SnapOneItem); |
3511 | |
3512 | QTRY_COMPARE(highlightFollowsCurrentItemSpy.count(),1); |
3513 | QTRY_COMPARE(preferredHighlightBeginSpy.count(),1); |
3514 | QTRY_COMPARE(preferredHighlightEndSpy.count(),1); |
3515 | QTRY_COMPARE(highlightRangeModeSpy.count(),1); |
3516 | QTRY_COMPARE(keyNavigationWrapsSpy.count(),1); |
3517 | QTRY_COMPARE(cacheBufferSpy.count(),1); |
3518 | QTRY_COMPARE(snapModeSpy.count(),1); |
3519 | } |
3520 | |
3521 | void tst_QQuickListView::componentChanges() |
3522 | { |
3523 | QScopedPointer<QQuickView> window(createView()); |
3524 | window->setSource(testFileUrl(fileName: "propertychangestest.qml" )); |
3525 | |
3526 | QQuickListView *listView = window->rootObject()->findChild<QQuickListView*>(aName: "listView" ); |
3527 | QTRY_VERIFY(listView); |
3528 | |
3529 | QQmlComponent component(window->engine()); |
3530 | component.setData("import QtQuick 2.0; Rectangle { color: \"blue\"; }" , baseUrl: QUrl::fromLocalFile(localfile: "" )); |
3531 | |
3532 | QQmlComponent delegateComponent(window->engine()); |
3533 | delegateComponent.setData("import QtQuick 2.0; Text { text: '<b>Name:</b> ' + name }" , baseUrl: QUrl::fromLocalFile(localfile: "" )); |
3534 | |
3535 | QSignalSpy highlightSpy(listView, SIGNAL(highlightChanged())); |
3536 | QSignalSpy delegateSpy(listView, SIGNAL(delegateChanged())); |
3537 | QSignalSpy (listView, SIGNAL(headerChanged())); |
3538 | QSignalSpy (listView, SIGNAL(footerChanged())); |
3539 | |
3540 | listView->setHighlight(&component); |
3541 | listView->setHeader(&component); |
3542 | listView->setFooter(&component); |
3543 | listView->setDelegate(&delegateComponent); |
3544 | |
3545 | QTRY_COMPARE(listView->highlight(), &component); |
3546 | QTRY_COMPARE(listView->header(), &component); |
3547 | QTRY_COMPARE(listView->footer(), &component); |
3548 | QTRY_COMPARE(listView->delegate(), &delegateComponent); |
3549 | |
3550 | QTRY_COMPARE(highlightSpy.count(),1); |
3551 | QTRY_COMPARE(delegateSpy.count(),1); |
3552 | QTRY_COMPARE(headerSpy.count(),1); |
3553 | QTRY_COMPARE(footerSpy.count(),1); |
3554 | |
3555 | listView->setHighlight(&component); |
3556 | listView->setHeader(&component); |
3557 | listView->setFooter(&component); |
3558 | listView->setDelegate(&delegateComponent); |
3559 | |
3560 | QTRY_COMPARE(highlightSpy.count(),1); |
3561 | QTRY_COMPARE(delegateSpy.count(),1); |
3562 | QTRY_COMPARE(headerSpy.count(),1); |
3563 | QTRY_COMPARE(footerSpy.count(),1); |
3564 | } |
3565 | |
3566 | void tst_QQuickListView::modelChanges() |
3567 | { |
3568 | QScopedPointer<QQuickView> window(createView()); |
3569 | window->setSource(testFileUrl(fileName: "propertychangestest.qml" )); |
3570 | |
3571 | QQuickListView *listView = window->rootObject()->findChild<QQuickListView*>(aName: "listView" ); |
3572 | QTRY_VERIFY(listView); |
3573 | |
3574 | QQmlListModel *alternateModel = window->rootObject()->findChild<QQmlListModel*>(aName: "alternateModel" ); |
3575 | QTRY_VERIFY(alternateModel); |
3576 | QVariant modelVariant = QVariant::fromValue<QObject *>(value: alternateModel); |
3577 | QSignalSpy modelSpy(listView, SIGNAL(modelChanged())); |
3578 | |
3579 | listView->setModel(modelVariant); |
3580 | QTRY_COMPARE(listView->model(), modelVariant); |
3581 | QTRY_COMPARE(modelSpy.count(),1); |
3582 | |
3583 | listView->setModel(modelVariant); |
3584 | QTRY_COMPARE(modelSpy.count(),1); |
3585 | |
3586 | listView->setModel(QVariant()); |
3587 | QTRY_COMPARE(modelSpy.count(),2); |
3588 | } |
3589 | |
3590 | void tst_QQuickListView::QTBUG_9791() |
3591 | { |
3592 | QScopedPointer<QQuickView> window(createView()); |
3593 | window->setSource(testFileUrl(fileName: "strictlyenforcerange.qml" )); |
3594 | window->show(); |
3595 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
3596 | |
3597 | QQuickListView *listview = qobject_cast<QQuickListView*>(object: window->rootObject()); |
3598 | QTRY_VERIFY(listview != nullptr); |
3599 | |
3600 | QQuickItem *contentItem = listview->contentItem(); |
3601 | QTRY_VERIFY(contentItem != nullptr); |
3602 | QTRY_VERIFY(listview->delegate() != nullptr); |
3603 | QTRY_VERIFY(listview->model() != 0); |
3604 | |
3605 | QMetaObject::invokeMethod(obj: listview, member: "fillModel" ); |
3606 | qApp->processEvents(); |
3607 | |
3608 | // Confirm items positioned correctly |
3609 | int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" , visibleOnly: false).count(); |
3610 | QCOMPARE(itemCount, 3); |
3611 | |
3612 | for (int i = 0; i < itemCount; ++i) { |
3613 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
3614 | if (!item) qWarning() << "Item" << i << "not found" ; |
3615 | QTRY_VERIFY(item); |
3616 | QTRY_COMPARE(item->x(), i*300.0); |
3617 | } |
3618 | |
3619 | // check that view is positioned correctly |
3620 | QTRY_COMPARE(listview->contentX(), 590.0); |
3621 | } |
3622 | |
3623 | void tst_QQuickListView::QTBUG_33568() |
3624 | { |
3625 | QScopedPointer<QQuickView> window(createView()); |
3626 | window->setSource(testFileUrl(fileName: "strictlyenforcerange-resize.qml" )); |
3627 | window->show(); |
3628 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
3629 | |
3630 | QQuickListView *listview = qobject_cast<QQuickListView*>(object: window->rootObject()); |
3631 | QVERIFY(listview != nullptr); |
3632 | |
3633 | // we want to verify that the change animates smoothly, rather than jumping into place |
3634 | QSignalSpy spy(listview, SIGNAL(contentYChanged())); |
3635 | |
3636 | listview->incrementCurrentIndex(); |
3637 | QTRY_COMPARE(listview->contentY(), -100.0); |
3638 | QVERIFY(spy.count() > 1); |
3639 | |
3640 | spy.clear(); |
3641 | listview->incrementCurrentIndex(); |
3642 | QTRY_COMPARE(listview->contentY(), -50.0); |
3643 | QVERIFY(spy.count() > 1); |
3644 | } |
3645 | |
3646 | void tst_QQuickListView::manualHighlight() |
3647 | { |
3648 | QScopedPointer<QQuickView> window(new QQuickView(nullptr)); |
3649 | window->setGeometry(posx: 0,posy: 0,w: 240,h: 320); |
3650 | |
3651 | QString filename(testFile(fileName: "manual-highlight.qml" )); |
3652 | window->setSource(QUrl::fromLocalFile(localfile: filename)); |
3653 | |
3654 | qApp->processEvents(); |
3655 | |
3656 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
3657 | QTRY_VERIFY(listview != nullptr); |
3658 | |
3659 | QQuickItem *contentItem = listview->contentItem(); |
3660 | QTRY_VERIFY(contentItem != nullptr); |
3661 | |
3662 | QTRY_COMPARE(listview->currentIndex(), 0); |
3663 | QTRY_COMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper" , 0)); |
3664 | QTRY_COMPARE(listview->highlightItem()->y() - 5, listview->currentItem()->y()); |
3665 | |
3666 | listview->setCurrentIndex(2); |
3667 | |
3668 | QTRY_COMPARE(listview->currentIndex(), 2); |
3669 | QTRY_COMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper" , 2)); |
3670 | QTRY_COMPARE(listview->highlightItem()->y() - 5, listview->currentItem()->y()); |
3671 | |
3672 | // QTBUG-15972 |
3673 | listview->positionViewAtIndex(index: 3, mode: QQuickListView::Contain); |
3674 | |
3675 | QTRY_COMPARE(listview->currentIndex(), 2); |
3676 | QTRY_COMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper" , 2)); |
3677 | QTRY_COMPARE(listview->highlightItem()->y() - 5, listview->currentItem()->y()); |
3678 | } |
3679 | |
3680 | void tst_QQuickListView::QTBUG_11105() |
3681 | { |
3682 | QScopedPointer<QQuickView> window(createView()); |
3683 | QaimModel model; |
3684 | for (int i = 0; i < 30; i++) |
3685 | model.addItem(name: "Item" + QString::number(i), number: "" ); |
3686 | |
3687 | QQmlContext *ctxt = window->rootContext(); |
3688 | ctxt->setContextProperty("testModel" , &model); |
3689 | |
3690 | QScopedPointer<TestObject> testObject(new TestObject); |
3691 | ctxt->setContextProperty("testObject" , testObject.data()); |
3692 | |
3693 | window->setSource(testFileUrl(fileName: "listviewtest.qml" )); |
3694 | window->show(); |
3695 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
3696 | |
3697 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
3698 | QTRY_VERIFY(listview != nullptr); |
3699 | QQuickItem *contentItem = listview->contentItem(); |
3700 | QTRY_VERIFY(contentItem != nullptr); |
3701 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
3702 | |
3703 | // Confirm items positioned correctly |
3704 | int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ).count(); |
3705 | for (int i = 0; i < model.count() && i < itemCount; ++i) { |
3706 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
3707 | if (!item) qWarning() << "Item" << i << "not found" ; |
3708 | QTRY_VERIFY(item); |
3709 | QTRY_COMPARE(item->y(), qreal(i*20)); |
3710 | } |
3711 | |
3712 | listview->positionViewAtIndex(index: 20, mode: QQuickListView::Beginning); |
3713 | QCOMPARE(listview->contentY(), 280.); |
3714 | |
3715 | QaimModel model2; |
3716 | for (int i = 0; i < 5; i++) |
3717 | model2.addItem(name: "Item" + QString::number(i), number: "" ); |
3718 | |
3719 | ctxt->setContextProperty("testModel" , &model2); |
3720 | |
3721 | itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ).count(); |
3722 | QCOMPARE(itemCount, 5); |
3723 | } |
3724 | |
3725 | void tst_QQuickListView::initialZValues() |
3726 | { |
3727 | QFETCH(QString, fileName); |
3728 | QScopedPointer<QQuickView> window(createView()); |
3729 | window->setSource(testFileUrl(fileName)); |
3730 | qApp->processEvents(); |
3731 | |
3732 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
3733 | QTRY_VERIFY(listview != nullptr); |
3734 | QQuickItem *contentItem = listview->contentItem(); |
3735 | QTRY_VERIFY(contentItem != nullptr); |
3736 | |
3737 | QVERIFY(listview->currentItem()); |
3738 | QTRY_COMPARE(listview->currentItem()->z(), listview->property("itemZ" ).toReal()); |
3739 | |
3740 | QVERIFY(listview->headerItem()); |
3741 | QTRY_COMPARE(listview->headerItem()->z(), listview->property("headerZ" ).toReal()); |
3742 | |
3743 | QVERIFY(listview->footerItem()); |
3744 | QTRY_COMPARE(listview->footerItem()->z(), listview->property("footerZ" ).toReal()); |
3745 | |
3746 | QVERIFY(listview->highlightItem()); |
3747 | QTRY_COMPARE(listview->highlightItem()->z(), listview->property("highlightZ" ).toReal()); |
3748 | |
3749 | QQuickText *sectionItem = nullptr; |
3750 | QTRY_VERIFY(sectionItem = findItem<QQuickText>(contentItem, "section" )); |
3751 | QTRY_COMPARE(sectionItem->z(), listview->property("sectionZ" ).toReal()); |
3752 | } |
3753 | |
3754 | void tst_QQuickListView::initialZValues_data() |
3755 | { |
3756 | QTest::addColumn<QString>(name: "fileName" ); |
3757 | QTest::newRow(dataTag: "defaults" ) << "defaultZValues.qml" ; |
3758 | QTest::newRow(dataTag: "constants" ) << "constantZValues.qml" ; |
3759 | QTest::newRow(dataTag: "bindings" ) << "boundZValues.qml" ; |
3760 | } |
3761 | |
3762 | void tst_QQuickListView::() |
3763 | { |
3764 | QFETCH(QQuickListView::Orientation, orientation); |
3765 | QFETCH(Qt::LayoutDirection, layoutDirection); |
3766 | QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection); |
3767 | QFETCH(QPointF, ); |
3768 | QFETCH(QPointF, ); |
3769 | QFETCH(QPointF, initialContentPos); |
3770 | QFETCH(QPointF, changedContentPos); |
3771 | QFETCH(QPointF, firstDelegatePos); |
3772 | QFETCH(QPointF, resizeContentPos); |
3773 | |
3774 | QaimModel model; |
3775 | for (int i = 0; i < 30; i++) |
3776 | model.addItem(name: "Item" + QString::number(i), number: "" ); |
3777 | |
3778 | QQuickView *window = getView(); |
3779 | window->rootContext()->setContextProperty("testModel" , &model); |
3780 | window->rootContext()->setContextProperty("initialViewWidth" , 240); |
3781 | window->rootContext()->setContextProperty("initialViewHeight" , 320); |
3782 | window->setSource(testFileUrl(fileName: "header.qml" )); |
3783 | window->show(); |
3784 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
3785 | |
3786 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
3787 | QTRY_VERIFY(listview != nullptr); |
3788 | listview->setOrientation(orientation); |
3789 | listview->setLayoutDirection(layoutDirection); |
3790 | listview->setVerticalLayoutDirection(verticalLayoutDirection); |
3791 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
3792 | |
3793 | QQuickItem *contentItem = listview->contentItem(); |
3794 | QTRY_VERIFY(contentItem != nullptr); |
3795 | |
3796 | QQuickText * = nullptr; |
3797 | QTRY_VERIFY(header = findItem<QQuickText>(contentItem, "header" )); |
3798 | QCOMPARE(header, listview->headerItem()); |
3799 | |
3800 | QCOMPARE(header->width(), 100.); |
3801 | QCOMPARE(header->height(), 30.); |
3802 | QCOMPARE(header->position(), initialHeaderPos); |
3803 | QCOMPARE(QPointF(listview->contentX(), listview->contentY()), initialContentPos); |
3804 | |
3805 | if (orientation == QQuickListView::Vertical) |
3806 | QCOMPARE(listview->contentHeight(), model.count() * 30. + header->height()); |
3807 | else |
3808 | QCOMPARE(listview->contentWidth(), model.count() * 240. + header->width()); |
3809 | |
3810 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: 0); |
3811 | QVERIFY(item); |
3812 | QCOMPARE(item->position(), firstDelegatePos); |
3813 | |
3814 | model.clear(); |
3815 | listview->forceLayout(); |
3816 | QTRY_COMPARE(listview->count(), model.count()); |
3817 | QCOMPARE(header->position(), initialHeaderPos); // header should stay where it is |
3818 | if (orientation == QQuickListView::Vertical) |
3819 | QCOMPARE(listview->contentHeight(), header->height()); |
3820 | else |
3821 | QCOMPARE(listview->contentWidth(), header->width()); |
3822 | |
3823 | for (int i = 0; i < 30; i++) |
3824 | model.addItem(name: "Item" + QString::number(i), number: "" ); |
3825 | |
3826 | QSignalSpy (listview, SIGNAL(headerItemChanged())); |
3827 | QMetaObject::invokeMethod(obj: window->rootObject(), member: "changeHeader" ); |
3828 | |
3829 | QCOMPARE(headerItemSpy.count(), 1); |
3830 | |
3831 | header = findItem<QQuickText>(parent: contentItem, objectName: "header" ); |
3832 | QVERIFY(!header); |
3833 | header = findItem<QQuickText>(parent: contentItem, objectName: "header2" ); |
3834 | QVERIFY(header); |
3835 | |
3836 | QCOMPARE(header, listview->headerItem()); |
3837 | |
3838 | QCOMPARE(header->position(), changedHeaderPos); |
3839 | QCOMPARE(header->width(), 50.); |
3840 | QCOMPARE(header->height(), 20.); |
3841 | QTRY_COMPARE(QPointF(listview->contentX(), listview->contentY()), changedContentPos); |
3842 | |
3843 | item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: 0); |
3844 | QVERIFY(item); |
3845 | QCOMPARE(item->position(), firstDelegatePos); |
3846 | |
3847 | listview->positionViewAtBeginning(); |
3848 | header->setHeight(10); |
3849 | header->setWidth(40); |
3850 | QTRY_COMPARE(QPointF(listview->contentX(), listview->contentY()), resizeContentPos); |
3851 | |
3852 | releaseView(view: window); |
3853 | |
3854 | |
3855 | // QTBUG-21207 header should become visible if view resizes from initial empty size |
3856 | |
3857 | window = getView(); |
3858 | window->rootContext()->setContextProperty("testModel" , &model); |
3859 | window->rootContext()->setContextProperty("initialViewWidth" , 0.0); |
3860 | window->rootContext()->setContextProperty("initialViewHeight" , 0.0); |
3861 | window->setSource(testFileUrl(fileName: "header.qml" )); |
3862 | window->show(); |
3863 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
3864 | |
3865 | listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
3866 | QTRY_VERIFY(listview != nullptr); |
3867 | listview->setOrientation(orientation); |
3868 | listview->setLayoutDirection(layoutDirection); |
3869 | listview->setVerticalLayoutDirection(verticalLayoutDirection); |
3870 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
3871 | |
3872 | listview->setWidth(240); |
3873 | listview->setHeight(320); |
3874 | QTRY_COMPARE(listview->headerItem()->position(), initialHeaderPos); |
3875 | QCOMPARE(QPointF(listview->contentX(), listview->contentY()), initialContentPos); |
3876 | |
3877 | releaseView(view: window); |
3878 | } |
3879 | |
3880 | void tst_QQuickListView::() |
3881 | { |
3882 | QTest::addColumn<QQuickListView::Orientation>(name: "orientation" ); |
3883 | QTest::addColumn<Qt::LayoutDirection>(name: "layoutDirection" ); |
3884 | QTest::addColumn<QQuickItemView::VerticalLayoutDirection>(name: "verticalLayoutDirection" ); |
3885 | QTest::addColumn<QPointF>(name: "initialHeaderPos" ); |
3886 | QTest::addColumn<QPointF>(name: "changedHeaderPos" ); |
3887 | QTest::addColumn<QPointF>(name: "initialContentPos" ); |
3888 | QTest::addColumn<QPointF>(name: "changedContentPos" ); |
3889 | QTest::addColumn<QPointF>(name: "firstDelegatePos" ); |
3890 | QTest::addColumn<QPointF>(name: "resizeContentPos" ); |
3891 | |
3892 | // header1 = 100 x 30 |
3893 | // header2 = 50 x 20 |
3894 | // delegates = 240 x 30 |
3895 | // view width = 240 |
3896 | |
3897 | // header above items, top left |
3898 | QTest::newRow(dataTag: "vertical, left to right" ) << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom |
3899 | << QPointF(0, -30) |
3900 | << QPointF(0, -20) |
3901 | << QPointF(0, -30) |
3902 | << QPointF(0, -20) |
3903 | << QPointF(0, 0) |
3904 | << QPointF(0, -10); |
3905 | |
3906 | // header above items, top right |
3907 | QTest::newRow(dataTag: "vertical, layout right to left" ) << QQuickListView::Vertical << Qt::RightToLeft << QQuickItemView::TopToBottom |
3908 | << QPointF(0, -30) |
3909 | << QPointF(0, -20) |
3910 | << QPointF(0, -30) |
3911 | << QPointF(0, -20) |
3912 | << QPointF(0, 0) |
3913 | << QPointF(0, -10); |
3914 | |
3915 | // header to left of items |
3916 | QTest::newRow(dataTag: "horizontal, layout left to right" ) << QQuickListView::Horizontal << Qt::LeftToRight << QQuickItemView::TopToBottom |
3917 | << QPointF(-100, 0) |
3918 | << QPointF(-50, 0) |
3919 | << QPointF(-100, 0) |
3920 | << QPointF(-50, 0) |
3921 | << QPointF(0, 0) |
3922 | << QPointF(-40, 0); |
3923 | |
3924 | // header to right of items |
3925 | QTest::newRow(dataTag: "horizontal, layout right to left" ) << QQuickListView::Horizontal << Qt::RightToLeft << QQuickItemView::TopToBottom |
3926 | << QPointF(0, 0) |
3927 | << QPointF(0, 0) |
3928 | << QPointF(-240 + 100, 0) |
3929 | << QPointF(-240 + 50, 0) |
3930 | << QPointF(-240, 0) |
3931 | << QPointF(-240 + 40, 0); |
3932 | |
3933 | // header below items |
3934 | QTest::newRow(dataTag: "vertical, bottom to top" ) << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::BottomToTop |
3935 | << QPointF(0, 0) |
3936 | << QPointF(0, 0) |
3937 | << QPointF(0, -320 + 30) |
3938 | << QPointF(0, -320 + 20) |
3939 | << QPointF(0, -30) |
3940 | << QPointF(0, -320 + 10); |
3941 | } |
3942 | |
3943 | void tst_QQuickListView::() |
3944 | { |
3945 | QScopedPointer<QQuickView> window(createView()); |
3946 | QaimModel model; |
3947 | |
3948 | window->rootContext()->setContextProperty("setCurrentToZero" , QVariant(false)); |
3949 | window->setSource(testFileUrl(fileName: "fillModelOnComponentCompleted.qml" )); |
3950 | qApp->processEvents(); |
3951 | |
3952 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
3953 | QTRY_VERIFY(listview != nullptr); |
3954 | |
3955 | QQuickItem *contentItem = listview->contentItem(); |
3956 | QTRY_VERIFY(contentItem != nullptr); |
3957 | |
3958 | QQuickText * = findItem<QQuickText>(parent: contentItem, objectName: "header" ); |
3959 | QVERIFY(header); |
3960 | QCOMPARE(header->y(), -header->height()); |
3961 | |
3962 | QCOMPARE(listview->contentY(), -header->height()); |
3963 | |
3964 | model.clear(); |
3965 | QTRY_COMPARE(header->y(), -header->height()); |
3966 | } |
3967 | |
3968 | void tst_QQuickListView::() |
3969 | { |
3970 | QQuickView *window = getView(); |
3971 | window->rootContext()->setContextProperty("headerHeight" , 20); |
3972 | window->rootContext()->setContextProperty("headerWidth" , 240); |
3973 | window->setSource(testFileUrl(fileName: "headerchangesviewport.qml" )); |
3974 | |
3975 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
3976 | QTRY_VERIFY(listview != nullptr); |
3977 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
3978 | |
3979 | QQuickItem *contentItem = listview->contentItem(); |
3980 | QTRY_VERIFY(contentItem != nullptr); |
3981 | |
3982 | QQuickText * = nullptr; |
3983 | QTRY_VERIFY(header = findItem<QQuickText>(contentItem, "header" )); |
3984 | QCOMPARE(header, listview->headerItem()); |
3985 | |
3986 | QCOMPARE(header->height(), 20.); |
3987 | QCOMPARE(listview->contentHeight(), 20.); |
3988 | |
3989 | // change height |
3990 | window->rootContext()->setContextProperty("headerHeight" , 50); |
3991 | |
3992 | // verify that list content height updates also |
3993 | QCOMPARE(header->height(), 50.); |
3994 | QCOMPARE(listview->contentHeight(), 50.); |
3995 | } |
3996 | |
3997 | void tst_QQuickListView::() |
3998 | { |
3999 | QFETCH(QQuickListView::Orientation, orientation); |
4000 | QFETCH(Qt::LayoutDirection, layoutDirection); |
4001 | QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection); |
4002 | QFETCH(QPointF, ); |
4003 | QFETCH(QPointF, firstDelegatePos); |
4004 | QFETCH(QPointF, initialContentPos); |
4005 | QFETCH(QPointF, ); |
4006 | QFETCH(QPointF, changedContentPos); |
4007 | QFETCH(QPointF, resizeContentPos); |
4008 | |
4009 | QQuickView *window = getView(); |
4010 | |
4011 | QaimModel model; |
4012 | for (int i = 0; i < 3; i++) |
4013 | model.addItem(name: "Item" + QString::number(i), number: "" ); |
4014 | |
4015 | QQmlContext *ctxt = window->rootContext(); |
4016 | ctxt->setContextProperty("testModel" , &model); |
4017 | |
4018 | window->setSource(testFileUrl(fileName: "footer.qml" )); |
4019 | window->show(); |
4020 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
4021 | |
4022 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
4023 | QTRY_VERIFY(listview != nullptr); |
4024 | listview->setOrientation(orientation); |
4025 | listview->setLayoutDirection(layoutDirection); |
4026 | listview->setVerticalLayoutDirection(verticalLayoutDirection); |
4027 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
4028 | |
4029 | QQuickItem *contentItem = listview->contentItem(); |
4030 | QTRY_VERIFY(contentItem != nullptr); |
4031 | |
4032 | QQuickText * = findItem<QQuickText>(parent: contentItem, objectName: "footer" ); |
4033 | QVERIFY(footer); |
4034 | |
4035 | QCOMPARE(footer, listview->footerItem()); |
4036 | |
4037 | QCOMPARE(footer->position(), initialFooterPos); |
4038 | QCOMPARE(footer->width(), 100.); |
4039 | QCOMPARE(footer->height(), 30.); |
4040 | QCOMPARE(QPointF(listview->contentX(), listview->contentY()), initialContentPos); |
4041 | |
4042 | if (orientation == QQuickListView::Vertical) |
4043 | QCOMPARE(listview->contentHeight(), model.count() * 20. + footer->height()); |
4044 | else |
4045 | QCOMPARE(listview->contentWidth(), model.count() * 40. + footer->width()); |
4046 | |
4047 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: 0); |
4048 | QVERIFY(item); |
4049 | QCOMPARE(item->position(), firstDelegatePos); |
4050 | |
4051 | // remove one item |
4052 | model.removeItem(index: 1); |
4053 | |
4054 | if (orientation == QQuickListView::Vertical) { |
4055 | QTRY_COMPARE(footer->y(), verticalLayoutDirection == QQuickItemView::TopToBottom ? |
4056 | initialFooterPos.y() - 20 : initialFooterPos.y() + 20); // delegate width = 40 |
4057 | } else { |
4058 | QTRY_COMPARE(footer->x(), layoutDirection == Qt::LeftToRight ? |
4059 | initialFooterPos.x() - 40 : initialFooterPos.x() + 40); // delegate width = 40 |
4060 | } |
4061 | |
4062 | // remove all items |
4063 | model.clear(); |
4064 | if (orientation == QQuickListView::Vertical) |
4065 | QTRY_COMPARE(listview->contentHeight(), footer->height()); |
4066 | else |
4067 | QTRY_COMPARE(listview->contentWidth(), footer->width()); |
4068 | |
4069 | QPointF posWhenNoItems(0, 0); |
4070 | if (orientation == QQuickListView::Horizontal && layoutDirection == Qt::RightToLeft) |
4071 | posWhenNoItems.setX(-100); |
4072 | else if (orientation == QQuickListView::Vertical && verticalLayoutDirection == QQuickItemView::BottomToTop) |
4073 | posWhenNoItems.setY(-30); |
4074 | QTRY_COMPARE(footer->position(), posWhenNoItems); |
4075 | |
4076 | // if header is present, it's at a negative pos, so the footer should not move |
4077 | window->rootObject()->setProperty(name: "showHeader" , value: true); |
4078 | QTRY_COMPARE(footer->position(), posWhenNoItems); |
4079 | window->rootObject()->setProperty(name: "showHeader" , value: false); |
4080 | |
4081 | // add 30 items |
4082 | for (int i = 0; i < 30; i++) |
4083 | model.addItem(name: "Item" + QString::number(i), number: "" ); |
4084 | |
4085 | QSignalSpy (listview, SIGNAL(footerItemChanged())); |
4086 | QMetaObject::invokeMethod(obj: window->rootObject(), member: "changeFooter" ); |
4087 | |
4088 | QCOMPARE(footerItemSpy.count(), 1); |
4089 | |
4090 | footer = findItem<QQuickText>(parent: contentItem, objectName: "footer" ); |
4091 | QVERIFY(!footer); |
4092 | footer = findItem<QQuickText>(parent: contentItem, objectName: "footer2" ); |
4093 | QVERIFY(footer); |
4094 | |
4095 | QCOMPARE(footer, listview->footerItem()); |
4096 | |
4097 | QCOMPARE(footer->position(), changedFooterPos); |
4098 | QCOMPARE(footer->width(), 50.); |
4099 | QCOMPARE(footer->height(), 20.); |
4100 | QTRY_COMPARE(QPointF(listview->contentX(), listview->contentY()), changedContentPos); |
4101 | |
4102 | item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: 0); |
4103 | QVERIFY(item); |
4104 | QCOMPARE(item->position(), firstDelegatePos); |
4105 | |
4106 | listview->positionViewAtEnd(); |
4107 | footer->setHeight(10); |
4108 | footer->setWidth(40); |
4109 | QTRY_COMPARE(QPointF(listview->contentX(), listview->contentY()), resizeContentPos); |
4110 | |
4111 | releaseView(view: window); |
4112 | } |
4113 | |
4114 | void tst_QQuickListView::() |
4115 | { |
4116 | QTest::addColumn<QQuickListView::Orientation>(name: "orientation" ); |
4117 | QTest::addColumn<Qt::LayoutDirection>(name: "layoutDirection" ); |
4118 | QTest::addColumn<QQuickItemView::VerticalLayoutDirection>(name: "verticalLayoutDirection" ); |
4119 | QTest::addColumn<QPointF>(name: "initialFooterPos" ); |
4120 | QTest::addColumn<QPointF>(name: "changedFooterPos" ); |
4121 | QTest::addColumn<QPointF>(name: "initialContentPos" ); |
4122 | QTest::addColumn<QPointF>(name: "changedContentPos" ); |
4123 | QTest::addColumn<QPointF>(name: "firstDelegatePos" ); |
4124 | QTest::addColumn<QPointF>(name: "resizeContentPos" ); |
4125 | |
4126 | // footer1 = 100 x 30 |
4127 | // footer2 = 50 x 20 |
4128 | // delegates = 40 x 20 |
4129 | // view width = 240 |
4130 | // view height = 320 |
4131 | |
4132 | // footer below items, bottom left |
4133 | QTest::newRow(dataTag: "vertical, layout left to right" ) << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom |
4134 | << QPointF(0, 3 * 20) |
4135 | << QPointF(0, 30 * 20) // added 30 items |
4136 | << QPointF(0, 0) |
4137 | << QPointF(0, 0) |
4138 | << QPointF(0, 0) |
4139 | << QPointF(0, 30 * 20 - 320 + 10); |
4140 | |
4141 | // footer below items, bottom right |
4142 | QTest::newRow(dataTag: "vertical, layout right to left" ) << QQuickListView::Vertical << Qt::RightToLeft << QQuickItemView::TopToBottom |
4143 | << QPointF(0, 3 * 20) |
4144 | << QPointF(0, 30 * 20) |
4145 | << QPointF(0, 0) |
4146 | << QPointF(0, 0) |
4147 | << QPointF(0, 0) |
4148 | << QPointF(0, 30 * 20 - 320 + 10); |
4149 | |
4150 | // footer to right of items |
4151 | QTest::newRow(dataTag: "horizontal, layout left to right" ) << QQuickListView::Horizontal << Qt::LeftToRight << QQuickItemView::TopToBottom |
4152 | << QPointF(40 * 3, 0) |
4153 | << QPointF(40 * 30, 0) |
4154 | << QPointF(0, 0) |
4155 | << QPointF(0, 0) |
4156 | << QPointF(0, 0) |
4157 | << QPointF(40 * 30 - 240 + 40, 0); |
4158 | |
4159 | // footer to left of items |
4160 | QTest::newRow(dataTag: "horizontal, layout right to left" ) << QQuickListView::Horizontal << Qt::RightToLeft << QQuickItemView::TopToBottom |
4161 | << QPointF(-(40 * 3) - 100, 0) |
4162 | << QPointF(-(40 * 30) - 50, 0) // 50 = new footer width |
4163 | << QPointF(-240, 0) |
4164 | << QPointF(-240, 0) |
4165 | << QPointF(-40, 0) |
4166 | << QPointF(-(40 * 30) - 40, 0); |
4167 | |
4168 | // footer above items |
4169 | QTest::newRow(dataTag: "vertical, layout left to right" ) << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::BottomToTop |
4170 | << QPointF(0, -(3 * 20) - 30) |
4171 | << QPointF(0, -(30 * 20) - 20) |
4172 | << QPointF(0, -320) |
4173 | << QPointF(0, -320) |
4174 | << QPointF(0, -20) |
4175 | << QPointF(0, -(30 * 20) - 10); |
4176 | } |
4177 | |
4178 | void tst_QQuickListView::() // QTBUG-31677 |
4179 | { |
4180 | QQuickView *window = getView(); |
4181 | window->setSource(testFileUrl(fileName: "footer2.qml" )); |
4182 | window->show(); |
4183 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
4184 | |
4185 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
4186 | QTRY_VERIFY(listview != nullptr); |
4187 | |
4188 | QQuickItem * = listview->footerItem(); |
4189 | QVERIFY(footer != nullptr); |
4190 | QTRY_COMPARE(footer->y(), 0.0); |
4191 | } |
4192 | |
4193 | class LVAccessor : public QQuickListView |
4194 | { |
4195 | public: |
4196 | qreal minY() const { return minYExtent(); } |
4197 | qreal maxY() const { return maxYExtent(); } |
4198 | qreal minX() const { return minXExtent(); } |
4199 | qreal maxX() const { return maxXExtent(); } |
4200 | }; |
4201 | |
4202 | |
4203 | void tst_QQuickListView::extents() |
4204 | { |
4205 | QFETCH(QQuickListView::Orientation, orientation); |
4206 | QFETCH(Qt::LayoutDirection, layoutDirection); |
4207 | QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection); |
4208 | QFETCH(QPointF, ); |
4209 | QFETCH(QPointF, ); |
4210 | QFETCH(QPointF, minPos); |
4211 | QFETCH(QPointF, maxPos); |
4212 | QFETCH(QPointF, origin_empty); |
4213 | QFETCH(QPointF, origin_short); |
4214 | QFETCH(QPointF, origin_long); |
4215 | |
4216 | QQuickView *window = getView(); |
4217 | |
4218 | QaimModel model; |
4219 | QQmlContext *ctxt = window->rootContext(); |
4220 | |
4221 | ctxt->setContextProperty("testModel" , &model); |
4222 | window->setSource(testFileUrl(fileName: "headerfooter.qml" )); |
4223 | window->show(); |
4224 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
4225 | |
4226 | QQuickListView *listview = qobject_cast<QQuickListView*>(object: window->rootObject()); |
4227 | QTRY_VERIFY(listview != nullptr); |
4228 | listview->setOrientation(orientation); |
4229 | listview->setLayoutDirection(layoutDirection); |
4230 | listview->setVerticalLayoutDirection(verticalLayoutDirection); |
4231 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
4232 | |
4233 | QQuickItem *contentItem = listview->contentItem(); |
4234 | QTRY_VERIFY(contentItem != nullptr); |
4235 | |
4236 | QQuickItem * = findItem<QQuickItem>(parent: contentItem, objectName: "header" ); |
4237 | QVERIFY(header); |
4238 | QCOMPARE(header->position(), headerPos); |
4239 | |
4240 | QQuickItem * = findItem<QQuickItem>(parent: contentItem, objectName: "footer" ); |
4241 | QVERIFY(footer); |
4242 | QCOMPARE(footer->position(), footerPos); |
4243 | |
4244 | QCOMPARE(static_cast<LVAccessor*>(listview)->minX(), minPos.x()); |
4245 | QCOMPARE(static_cast<LVAccessor*>(listview)->minY(), minPos.y()); |
4246 | QCOMPARE(static_cast<LVAccessor*>(listview)->maxX(), maxPos.x()); |
4247 | QCOMPARE(static_cast<LVAccessor*>(listview)->maxY(), maxPos.y()); |
4248 | |
4249 | QCOMPARE(listview->originX(), origin_empty.x()); |
4250 | QCOMPARE(listview->originY(), origin_empty.y()); |
4251 | |
4252 | for (int i=0; i<3; i++) |
4253 | model.addItem(name: "Item" + QString::number(i), number: "" ); |
4254 | listview->forceLayout(); |
4255 | QTRY_COMPARE(listview->count(), model.count()); |
4256 | QCOMPARE(listview->originX(), origin_short.x()); |
4257 | QCOMPARE(listview->originY(), origin_short.y()); |
4258 | |
4259 | for (int i=3; i<30; i++) |
4260 | model.addItem(name: "Item" + QString::number(i), number: "" ); |
4261 | listview->forceLayout(); |
4262 | QTRY_COMPARE(listview->count(), model.count()); |
4263 | QCOMPARE(listview->originX(), origin_long.x()); |
4264 | QCOMPARE(listview->originY(), origin_long.y()); |
4265 | |
4266 | releaseView(view: window); |
4267 | } |
4268 | |
4269 | void tst_QQuickListView::extents_data() |
4270 | { |
4271 | QTest::addColumn<QQuickListView::Orientation>(name: "orientation" ); |
4272 | QTest::addColumn<Qt::LayoutDirection>(name: "layoutDirection" ); |
4273 | QTest::addColumn<QQuickItemView::VerticalLayoutDirection>(name: "verticalLayoutDirection" ); |
4274 | QTest::addColumn<QPointF>(name: "headerPos" ); |
4275 | QTest::addColumn<QPointF>(name: "footerPos" ); |
4276 | QTest::addColumn<QPointF>(name: "minPos" ); |
4277 | QTest::addColumn<QPointF>(name: "maxPos" ); |
4278 | QTest::addColumn<QPointF>(name: "origin_empty" ); |
4279 | QTest::addColumn<QPointF>(name: "origin_short" ); |
4280 | QTest::addColumn<QPointF>(name: "origin_long" ); |
4281 | |
4282 | // header is 240x20 (or 20x320 in Horizontal orientation) |
4283 | // footer is 240x30 (or 30x320 in Horizontal orientation) |
4284 | |
4285 | QTest::newRow(dataTag: "Vertical, TopToBottom" ) |
4286 | << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom |
4287 | << QPointF(0, -20) << QPointF(0, 0) |
4288 | << QPointF(0, 20) << QPointF(240, 20) |
4289 | << QPointF(0, -20) << QPointF(0, -20) << QPointF(0, -20); |
4290 | |
4291 | QTest::newRow(dataTag: "Vertical, BottomToTop" ) |
4292 | << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::BottomToTop |
4293 | << QPointF(0, 0) << QPointF(0, -30) |
4294 | << QPointF(0, 320 - 20) << QPointF(240, 320 - 20) // content flow is reversed |
4295 | << QPointF(0, -30) << QPointF(0, (-30.0 * 3) - 30) << QPointF(0, (-30.0 * 30) - 30); |
4296 | |
4297 | QTest::newRow(dataTag: "Horizontal, LeftToRight" ) |
4298 | << QQuickListView::Horizontal << Qt::LeftToRight << QQuickItemView::TopToBottom |
4299 | << QPointF(-20, 0) << QPointF(0, 0) |
4300 | << QPointF(20, 0) << QPointF(20, 320) |
4301 | << QPointF(-20, 0) << QPointF(-20, 0) << QPointF(-20, 0); |
4302 | |
4303 | QTest::newRow(dataTag: "Horizontal, RightToLeft" ) |
4304 | << QQuickListView::Horizontal << Qt::RightToLeft << QQuickItemView::TopToBottom |
4305 | << QPointF(0, 0) << QPointF(-30, 0) |
4306 | << QPointF(240 - 20, 0) << QPointF(240 - 20, 320) // content flow is reversed |
4307 | << QPointF(-30, 0) << QPointF((-240.0 * 3) - 30, 0) << QPointF((-240.0 * 30) - 30, 0); |
4308 | } |
4309 | |
4310 | void tst_QQuickListView::() |
4311 | { |
4312 | // Resetting a model shouldn't crash in views with header/footer |
4313 | QScopedPointer<QQuickView> window(createView()); |
4314 | |
4315 | QaimModel model; |
4316 | for (int i = 0; i < 4; i++) |
4317 | model.addItem(name: "Item" + QString::number(i), number: "" ); |
4318 | QQmlContext *ctxt = window->rootContext(); |
4319 | ctxt->setContextProperty("testModel" , &model); |
4320 | |
4321 | window->setSource(testFileUrl(fileName: "headerfooter.qml" )); |
4322 | qApp->processEvents(); |
4323 | |
4324 | QQuickListView *listview = qobject_cast<QQuickListView*>(object: window->rootObject()); |
4325 | QTRY_VERIFY(listview != nullptr); |
4326 | |
4327 | QQuickItem *contentItem = listview->contentItem(); |
4328 | QTRY_VERIFY(contentItem != nullptr); |
4329 | |
4330 | QQuickItem * = findItem<QQuickItem>(parent: contentItem, objectName: "header" ); |
4331 | QVERIFY(header); |
4332 | QCOMPARE(header->y(), -header->height()); |
4333 | |
4334 | QQuickItem * = findItem<QQuickItem>(parent: contentItem, objectName: "footer" ); |
4335 | QVERIFY(footer); |
4336 | QCOMPARE(footer->y(), 30.*4); |
4337 | |
4338 | model.reset(); |
4339 | |
4340 | // A reset should not force a new header or footer to be created. |
4341 | QQuickItem * = findItem<QQuickItem>(parent: contentItem, objectName: "header" ); |
4342 | QCOMPARE(newHeader, header); |
4343 | QCOMPARE(header->y(), -header->height()); |
4344 | |
4345 | QQuickItem * = findItem<QQuickItem>(parent: contentItem, objectName: "footer" ); |
4346 | QCOMPARE(newFooter, footer); |
4347 | QCOMPARE(footer->y(), 30.*4); |
4348 | } |
4349 | |
4350 | void tst_QQuickListView::resizeView() |
4351 | { |
4352 | QScopedPointer<QQuickView> window(createView()); |
4353 | QaimModel model; |
4354 | for (int i = 0; i < 40; i++) |
4355 | model.addItem(name: "Item" + QString::number(i), number: "" ); |
4356 | |
4357 | QQmlContext *ctxt = window->rootContext(); |
4358 | ctxt->setContextProperty("testModel" , &model); |
4359 | |
4360 | QScopedPointer<TestObject> testObject(new TestObject); |
4361 | ctxt->setContextProperty("testObject" , testObject.data()); |
4362 | |
4363 | window->setSource(testFileUrl(fileName: "listviewtest.qml" )); |
4364 | window->show(); |
4365 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
4366 | |
4367 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
4368 | QTRY_VERIFY(listview != nullptr); |
4369 | QQuickItem *contentItem = listview->contentItem(); |
4370 | QTRY_VERIFY(contentItem != nullptr); |
4371 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
4372 | |
4373 | // Confirm items positioned correctly |
4374 | int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ).count(); |
4375 | for (int i = 0; i < model.count() && i < itemCount; ++i) { |
4376 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
4377 | if (!item) qWarning() << "Item" << i << "not found" ; |
4378 | QTRY_VERIFY(item); |
4379 | QTRY_COMPARE(item->y(), i*20.); |
4380 | } |
4381 | |
4382 | QVariant heightRatio; |
4383 | QMetaObject::invokeMethod(obj: window->rootObject(), member: "heightRatio" , Q_RETURN_ARG(QVariant, heightRatio)); |
4384 | QCOMPARE(heightRatio.toReal(), 0.4); |
4385 | |
4386 | listview->setHeight(200); |
4387 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
4388 | |
4389 | QMetaObject::invokeMethod(obj: window->rootObject(), member: "heightRatio" , Q_RETURN_ARG(QVariant, heightRatio)); |
4390 | QCOMPARE(heightRatio.toReal(), 0.25); |
4391 | |
4392 | // Ensure we handle -ve sizes |
4393 | listview->setHeight(-100); |
4394 | QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper" , false).count(), 1); |
4395 | |
4396 | listview->setCacheBuffer(200); |
4397 | QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper" , false).count(), 11); |
4398 | |
4399 | // ensure items in cache become visible |
4400 | listview->setHeight(200); |
4401 | QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper" , false).count(), 21); |
4402 | |
4403 | itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" , visibleOnly: false).count(); |
4404 | for (int i = 0; i < model.count() && i < itemCount; ++i) { |
4405 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
4406 | if (!item) qWarning() << "Item" << i << "not found" ; |
4407 | QTRY_VERIFY(item); |
4408 | QTRY_COMPARE(item->y(), i*20.); |
4409 | QCOMPARE(delegateVisible(item), i < 11); // inside view visible, outside not visible |
4410 | } |
4411 | |
4412 | // ensure items outside view become invisible |
4413 | listview->setHeight(100); |
4414 | QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper" , false).count(), 16); |
4415 | |
4416 | itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" , visibleOnly: false).count(); |
4417 | for (int i = 0; i < model.count() && i < itemCount; ++i) { |
4418 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
4419 | if (!item) qWarning() << "Item" << i << "not found" ; |
4420 | QTRY_VERIFY(item); |
4421 | QTRY_COMPARE(item->y(), i*20.); |
4422 | QCOMPARE(delegateVisible(item), i < 6); // inside view visible, outside not visible |
4423 | } |
4424 | } |
4425 | |
4426 | void tst_QQuickListView::resizeViewAndRepaint() |
4427 | { |
4428 | QScopedPointer<QQuickView> window(createView()); |
4429 | |
4430 | QaimModel model; |
4431 | for (int i = 0; i < 40; i++) |
4432 | model.addItem(name: "Item" + QString::number(i), number: "" ); |
4433 | |
4434 | QQmlContext *ctxt = window->rootContext(); |
4435 | ctxt->setContextProperty("testModel" , &model); |
4436 | ctxt->setContextProperty("initialHeight" , 100); |
4437 | |
4438 | window->setSource(testFileUrl(fileName: "resizeview.qml" )); |
4439 | window->show(); |
4440 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
4441 | |
4442 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
4443 | QTRY_VERIFY(listview != nullptr); |
4444 | QQuickItem *contentItem = listview->contentItem(); |
4445 | QTRY_VERIFY(contentItem != nullptr); |
4446 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
4447 | |
4448 | // item at index 10 should not be currently visible |
4449 | QVERIFY(!findItem<QQuickItem>(contentItem, "wrapper" , 10)); |
4450 | |
4451 | listview->setHeight(320); |
4452 | |
4453 | QTRY_VERIFY(findItem<QQuickItem>(contentItem, "wrapper" , 10)); |
4454 | |
4455 | listview->setHeight(100); |
4456 | QTRY_VERIFY(!findItem<QQuickItem>(contentItem, "wrapper" , 10)); |
4457 | } |
4458 | |
4459 | void tst_QQuickListView::sizeLessThan1() |
4460 | { |
4461 | QScopedPointer<QQuickView> window(createView()); |
4462 | |
4463 | QaimModel model; |
4464 | for (int i = 0; i < 30; i++) |
4465 | model.addItem(name: "Item" + QString::number(i), number: "" ); |
4466 | |
4467 | QQmlContext *ctxt = window->rootContext(); |
4468 | ctxt->setContextProperty("testModel" , &model); |
4469 | |
4470 | QScopedPointer<TestObject> testObject(new TestObject); |
4471 | ctxt->setContextProperty("testObject" , testObject.data()); |
4472 | |
4473 | window->setSource(testFileUrl(fileName: "sizelessthan1.qml" )); |
4474 | window->show(); |
4475 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
4476 | |
4477 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
4478 | QTRY_VERIFY(listview != nullptr); |
4479 | QQuickItem *contentItem = listview->contentItem(); |
4480 | QTRY_VERIFY(contentItem != nullptr); |
4481 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
4482 | |
4483 | // Confirm items positioned correctly |
4484 | int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ).count(); |
4485 | for (int i = 0; i < model.count() && i < itemCount; ++i) { |
4486 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
4487 | if (!item) qWarning() << "Item" << i << "not found" ; |
4488 | QTRY_VERIFY(item); |
4489 | QTRY_COMPARE(item->y(), i*0.5); |
4490 | } |
4491 | } |
4492 | |
4493 | void tst_QQuickListView::QTBUG_14821() |
4494 | { |
4495 | QScopedPointer<QQuickView> window(createView()); |
4496 | window->setSource(testFileUrl(fileName: "qtbug14821.qml" )); |
4497 | qApp->processEvents(); |
4498 | |
4499 | QQuickListView *listview = qobject_cast<QQuickListView*>(object: window->rootObject()); |
4500 | QVERIFY(listview != nullptr); |
4501 | |
4502 | QQuickItem *contentItem = listview->contentItem(); |
4503 | QVERIFY(contentItem != nullptr); |
4504 | |
4505 | listview->decrementCurrentIndex(); |
4506 | QCOMPARE(listview->currentIndex(), 99); |
4507 | |
4508 | listview->incrementCurrentIndex(); |
4509 | QCOMPARE(listview->currentIndex(), 0); |
4510 | } |
4511 | |
4512 | void tst_QQuickListView::resizeDelegate() |
4513 | { |
4514 | QScopedPointer<QQuickView> window(createView()); |
4515 | QStringList strings; |
4516 | for (int i = 0; i < 30; ++i) |
4517 | strings << QString::number(i); |
4518 | QStringListModel model(strings); |
4519 | |
4520 | QQmlContext *ctxt = window->rootContext(); |
4521 | ctxt->setContextProperty("testModel" , &model); |
4522 | |
4523 | window->setSource(testFileUrl(fileName: "displaylist.qml" )); |
4524 | window->show(); |
4525 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
4526 | |
4527 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
4528 | QVERIFY(listview != nullptr); |
4529 | QQuickItem *contentItem = listview->contentItem(); |
4530 | QVERIFY(contentItem != nullptr); |
4531 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
4532 | |
4533 | QCOMPARE(listview->count(), model.rowCount()); |
4534 | |
4535 | listview->setCurrentIndex(25); |
4536 | listview->setContentY(0); |
4537 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
4538 | |
4539 | for (int i = 0; i < 16; ++i) { |
4540 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
4541 | QVERIFY(item != nullptr); |
4542 | QCOMPARE(item->y(), i*20.0); |
4543 | } |
4544 | |
4545 | QCOMPARE(listview->currentItem()->y(), 500.0); |
4546 | QTRY_COMPARE(listview->highlightItem()->y(), 500.0); |
4547 | |
4548 | window->rootObject()->setProperty(name: "delegateHeight" , value: 30); |
4549 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
4550 | |
4551 | for (int i = 0; i < 11; ++i) { |
4552 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
4553 | QVERIFY(item != nullptr); |
4554 | QTRY_COMPARE(item->y(), i*30.0); |
4555 | } |
4556 | |
4557 | QTRY_COMPARE(listview->currentItem()->y(), 750.0); |
4558 | QTRY_COMPARE(listview->highlightItem()->y(), 750.0); |
4559 | |
4560 | listview->setCurrentIndex(1); |
4561 | listview->positionViewAtIndex(index: 25, mode: QQuickListView::Beginning); |
4562 | listview->positionViewAtIndex(index: 5, mode: QQuickListView::Beginning); |
4563 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
4564 | |
4565 | for (int i = 5; i < 16; ++i) { |
4566 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
4567 | QVERIFY(item != nullptr); |
4568 | QCOMPARE(item->y(), i*30.0); |
4569 | } |
4570 | |
4571 | QTRY_COMPARE(listview->currentItem()->y(), 30.0); |
4572 | QTRY_COMPARE(listview->highlightItem()->y(), 30.0); |
4573 | |
4574 | window->rootObject()->setProperty(name: "delegateHeight" , value: 20); |
4575 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
4576 | |
4577 | for (int i = 5; i < 11; ++i) { |
4578 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
4579 | QVERIFY(item != nullptr); |
4580 | QTRY_COMPARE(item->y(), 150 + (i-5)*20.0); |
4581 | } |
4582 | |
4583 | QTRY_COMPARE(listview->currentItem()->y(), 70.0); |
4584 | QTRY_COMPARE(listview->highlightItem()->y(), 70.0); |
4585 | } |
4586 | |
4587 | void tst_QQuickListView::resizeFirstDelegate() |
4588 | { |
4589 | // QTBUG-20712: Content Y jumps constantly if first delegate height == 0 |
4590 | // and other delegates have height > 0 |
4591 | QScopedPointer<QQuickView> window(createView()); |
4592 | |
4593 | // bug only occurs when all items in the model are visible |
4594 | QaimModel model; |
4595 | for (int i = 0; i < 10; i++) |
4596 | model.addItem(name: "Item" + QString::number(i), number: "" ); |
4597 | |
4598 | QQmlContext *ctxt = window->rootContext(); |
4599 | ctxt->setContextProperty("testModel" , &model); |
4600 | |
4601 | QScopedPointer<TestObject> testObject(new TestObject); |
4602 | ctxt->setContextProperty("testObject" , testObject.data()); |
4603 | |
4604 | window->setSource(testFileUrl(fileName: "listviewtest.qml" )); |
4605 | window->show(); |
4606 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
4607 | |
4608 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
4609 | QVERIFY(listview != nullptr); |
4610 | QQuickItem *contentItem = listview->contentItem(); |
4611 | QVERIFY(contentItem != nullptr); |
4612 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
4613 | |
4614 | QQuickItem *item = nullptr; |
4615 | for (int i = 0; i < model.count(); ++i) { |
4616 | item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
4617 | QVERIFY(item != nullptr); |
4618 | QCOMPARE(item->y(), i*20.0); |
4619 | } |
4620 | |
4621 | item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: 0); |
4622 | item->setHeight(0); |
4623 | |
4624 | // check the content y has not jumped up and down |
4625 | QCOMPARE(listview->contentY(), 0.0); |
4626 | QSignalSpy spy(listview, SIGNAL(contentYChanged())); |
4627 | QTest::qWait(ms: 100); |
4628 | QCOMPARE(spy.count(), 0); |
4629 | |
4630 | for (int i = 1; i < model.count(); ++i) { |
4631 | item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
4632 | QVERIFY(item != nullptr); |
4633 | QTRY_COMPARE(item->y(), (i-1)*20.0); |
4634 | } |
4635 | |
4636 | |
4637 | // QTBUG-22014: refill doesn't clear items scrolling off the top of the |
4638 | // list if they follow a zero-sized delegate |
4639 | |
4640 | for (int i = 0; i < 10; i++) |
4641 | model.addItem(name: "Item" + QString::number(i), number: "" ); |
4642 | listview->forceLayout(); |
4643 | QTRY_COMPARE(listview->count(), model.count()); |
4644 | |
4645 | item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: 1); |
4646 | QVERIFY(item); |
4647 | item->setHeight(0); |
4648 | |
4649 | listview->setCurrentIndex(19); |
4650 | qApp->processEvents(); |
4651 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
4652 | |
4653 | // items 0-2 should have been deleted |
4654 | for (int i=0; i<3; i++) { |
4655 | QTRY_VERIFY(!findItem<QQuickItem>(contentItem, "wrapper" , i)); |
4656 | } |
4657 | } |
4658 | |
4659 | void tst_QQuickListView::repositionResizedDelegate() |
4660 | { |
4661 | QFETCH(QQuickListView::Orientation, orientation); |
4662 | QFETCH(Qt::LayoutDirection, layoutDirection); |
4663 | QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection); |
4664 | QFETCH(QPointF, contentPos_itemFirstHalfVisible); |
4665 | QFETCH(QPointF, contentPos_itemSecondHalfVisible); |
4666 | QFETCH(QRectF, origPositionerRect); |
4667 | QFETCH(QRectF, resizedPositionerRect); |
4668 | |
4669 | QQuickView *window = getView(); |
4670 | QQmlContext *ctxt = window->rootContext(); |
4671 | ctxt->setContextProperty("testHorizontal" , orientation == QQuickListView::Horizontal); |
4672 | ctxt->setContextProperty("testRightToLeft" , layoutDirection == Qt::RightToLeft); |
4673 | ctxt->setContextProperty("testBottomToTop" , verticalLayoutDirection == QQuickListView::BottomToTop); |
4674 | window->setSource(testFileUrl(fileName: "repositionResizedDelegate.qml" )); |
4675 | window->show(); |
4676 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
4677 | |
4678 | QQuickListView *listview = qobject_cast<QQuickListView*>(object: window->rootObject()); |
4679 | QTRY_VERIFY(listview != nullptr); |
4680 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
4681 | |
4682 | QQuickItem *positioner = findItem<QQuickItem>(parent: window->rootObject(), objectName: "positioner" ); |
4683 | QVERIFY(positioner); |
4684 | QTRY_COMPARE(positioner->boundingRect().size(), origPositionerRect.size()); |
4685 | QTRY_COMPARE(positioner->position(), origPositionerRect.topLeft()); |
4686 | QSignalSpy spy(listview, orientation == QQuickListView::Vertical ? SIGNAL(contentYChanged()) : SIGNAL(contentXChanged())); |
4687 | int prevSpyCount = 0; |
4688 | |
4689 | // When an item is resized while it is partially visible, it should resize in the |
4690 | // direction of the content flow. If a RightToLeft or BottomToTop layout is used, |
4691 | // the item should also be re-positioned so its end position stays the same. |
4692 | |
4693 | listview->setContentX(contentPos_itemFirstHalfVisible.x()); |
4694 | listview->setContentY(contentPos_itemFirstHalfVisible.y()); |
4695 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
4696 | prevSpyCount = spy.count(); |
4697 | QVERIFY(QMetaObject::invokeMethod(window->rootObject(), "incrementRepeater" )); |
4698 | QTRY_COMPARE(positioner->boundingRect().size(), resizedPositionerRect.size()); |
4699 | QTRY_COMPARE(positioner->position(), resizedPositionerRect.topLeft()); |
4700 | QCOMPARE(listview->contentX(), contentPos_itemFirstHalfVisible.x()); |
4701 | QCOMPARE(listview->contentY(), contentPos_itemFirstHalfVisible.y()); |
4702 | QCOMPARE(spy.count(), prevSpyCount); |
4703 | |
4704 | QVERIFY(QMetaObject::invokeMethod(window->rootObject(), "decrementRepeater" )); |
4705 | QTRY_COMPARE(positioner->boundingRect().size(), origPositionerRect.size()); |
4706 | QTRY_COMPARE(positioner->position(), origPositionerRect.topLeft()); |
4707 | QCOMPARE(listview->contentX(), contentPos_itemFirstHalfVisible.x()); |
4708 | QCOMPARE(listview->contentY(), contentPos_itemFirstHalfVisible.y()); |
4709 | |
4710 | listview->setContentX(contentPos_itemSecondHalfVisible.x()); |
4711 | listview->setContentY(contentPos_itemSecondHalfVisible.y()); |
4712 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
4713 | prevSpyCount = spy.count(); |
4714 | |
4715 | QVERIFY(QMetaObject::invokeMethod(window->rootObject(), "incrementRepeater" )); |
4716 | positioner = findItem<QQuickItem>(parent: window->rootObject(), objectName: "positioner" ); |
4717 | QTRY_COMPARE(positioner->boundingRect().size(), resizedPositionerRect.size()); |
4718 | QTRY_COMPARE(positioner->position(), resizedPositionerRect.topLeft()); |
4719 | QCOMPARE(listview->contentX(), contentPos_itemSecondHalfVisible.x()); |
4720 | QCOMPARE(listview->contentY(), contentPos_itemSecondHalfVisible.y()); |
4721 | qApp->processEvents(); |
4722 | QCOMPARE(spy.count(), prevSpyCount); |
4723 | |
4724 | releaseView(view: window); |
4725 | } |
4726 | |
4727 | void tst_QQuickListView::repositionResizedDelegate_data() |
4728 | { |
4729 | QTest::addColumn<QQuickListView::Orientation>(name: "orientation" ); |
4730 | QTest::addColumn<Qt::LayoutDirection>(name: "layoutDirection" ); |
4731 | QTest::addColumn<QQuickListView::VerticalLayoutDirection>(name: "verticalLayoutDirection" ); |
4732 | QTest::addColumn<QPointF>(name: "contentPos_itemFirstHalfVisible" ); |
4733 | QTest::addColumn<QPointF>(name: "contentPos_itemSecondHalfVisible" ); |
4734 | QTest::addColumn<QRectF>(name: "origPositionerRect" ); |
4735 | QTest::addColumn<QRectF>(name: "resizedPositionerRect" ); |
4736 | |
4737 | QTest::newRow(dataTag: "vertical" ) |
4738 | << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom |
4739 | << QPointF(0, 60) << QPointF(0, 200 + 60) |
4740 | << QRectF(0, 200, 120, 120) |
4741 | << QRectF(0, 200, 120, 120 * 2); |
4742 | |
4743 | QTest::newRow(dataTag: "vertical, BottomToTop" ) |
4744 | << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::BottomToTop |
4745 | << QPointF(0, -200 - 60) << QPointF(0, -200 - 260) |
4746 | << QRectF(0, -200 - 120, 120, 120) |
4747 | << QRectF(0, -200 - 120*2, 120, 120 * 2); |
4748 | |
4749 | QTest::newRow(dataTag: "horizontal" ) |
4750 | << QQuickListView::Horizontal<< Qt::LeftToRight << QQuickItemView::TopToBottom |
4751 | << QPointF(60, 0) << QPointF(260, 0) |
4752 | << QRectF(200, 0, 120, 120) |
4753 | << QRectF(200, 0, 120 * 2, 120); |
4754 | |
4755 | QTest::newRow(dataTag: "horizontal, rtl" ) |
4756 | << QQuickListView::Horizontal << Qt::RightToLeft << QQuickItemView::TopToBottom |
4757 | << QPointF(-200 - 60, 0) << QPointF(-200 - 260, 0) |
4758 | << QRectF(-200 - 120, 0, 120, 120) |
4759 | << QRectF(-200 - 120 * 2, 0, 120 * 2, 120); |
4760 | } |
4761 | |
4762 | void tst_QQuickListView::QTBUG_16037() |
4763 | { |
4764 | QScopedPointer<QQuickView> window(createView()); |
4765 | window->show(); |
4766 | |
4767 | window->setSource(testFileUrl(fileName: "qtbug16037.qml" )); |
4768 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
4769 | |
4770 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "listview" ); |
4771 | QTRY_VERIFY(listview != nullptr); |
4772 | |
4773 | QVERIFY(listview->contentHeight() <= 0.0); |
4774 | |
4775 | QMetaObject::invokeMethod(obj: window->rootObject(), member: "setModel" ); |
4776 | |
4777 | QTRY_COMPARE(listview->contentHeight(), 80.0); |
4778 | } |
4779 | |
4780 | void tst_QQuickListView::indexAt_itemAt_data() |
4781 | { |
4782 | QTest::addColumn<qreal>(name: "x" ); |
4783 | QTest::addColumn<qreal>(name: "y" ); |
4784 | QTest::addColumn<int>(name: "index" ); |
4785 | |
4786 | QTest::newRow(dataTag: "Item 0 - 0, 0" ) << 0. << 0. << 0; |
4787 | QTest::newRow(dataTag: "Item 0 - 0, 19" ) << 0. << 19. << 0; |
4788 | QTest::newRow(dataTag: "Item 0 - 239, 19" ) << 239. << 19. << 0; |
4789 | QTest::newRow(dataTag: "Item 1 - 0, 20" ) << 0. << 20. << 1; |
4790 | QTest::newRow(dataTag: "No Item - 240, 20" ) << 240. << 20. << -1; |
4791 | } |
4792 | |
4793 | void tst_QQuickListView::indexAt_itemAt() |
4794 | { |
4795 | QFETCH(qreal, x); |
4796 | QFETCH(qreal, y); |
4797 | QFETCH(int, index); |
4798 | |
4799 | QQuickView *window = getView(); |
4800 | |
4801 | QaimModel model; |
4802 | for (int i = 0; i < 30; i++) |
4803 | model.addItem(name: "Item" + QString::number(i), number: "" ); |
4804 | |
4805 | QQmlContext *ctxt = window->rootContext(); |
4806 | ctxt->setContextProperty("testModel" , &model); |
4807 | |
4808 | QScopedPointer<TestObject> testObject(new TestObject); |
4809 | ctxt->setContextProperty("testObject" , testObject.data()); |
4810 | |
4811 | window->setSource(testFileUrl(fileName: "listviewtest.qml" )); |
4812 | window->show(); |
4813 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
4814 | |
4815 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
4816 | QTRY_VERIFY(listview != nullptr); |
4817 | |
4818 | QQuickItem *contentItem = listview->contentItem(); |
4819 | QTRY_VERIFY(contentItem != nullptr); |
4820 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
4821 | |
4822 | QQuickItem *item = nullptr; |
4823 | if (index >= 0) { |
4824 | item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index); |
4825 | QVERIFY(item); |
4826 | } |
4827 | QCOMPARE(listview->indexAt(x,y), index); |
4828 | QCOMPARE(listview->itemAt(x,y), item); |
4829 | |
4830 | releaseView(view: window); |
4831 | } |
4832 | |
4833 | void tst_QQuickListView::itemAtIndex() |
4834 | { |
4835 | QScopedPointer<QQuickView> window(createView()); |
4836 | window->setSource(testFileUrl(fileName: "listview-itematindex.qml" )); |
4837 | window->show(); |
4838 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
4839 | |
4840 | QQuickListView *listview = qobject_cast<QQuickListView*>(object: window->rootObject()); |
4841 | QVERIFY(listview != nullptr); |
4842 | |
4843 | QCOMPARE(listview->itemAtIndex(-1), nullptr); |
4844 | QCOMPARE(listview->itemAtIndex(3), nullptr); |
4845 | QQuickItem *item = listview->itemAtIndex(index: 0); |
4846 | QVERIFY(item); |
4847 | QCOMPARE(item->property("idx" ), 0); |
4848 | item = listview->itemAtIndex(index: 1); |
4849 | QVERIFY(item); |
4850 | QCOMPARE(item->property("idx" ), 1); |
4851 | item = listview->itemAtIndex(index: 2); |
4852 | QVERIFY(item); |
4853 | QCOMPARE(item->property("idx" ), 2); |
4854 | } |
4855 | |
4856 | void tst_QQuickListView::incrementalModel() |
4857 | { |
4858 | QScopedPointer<QQuickView> window(createView()); |
4859 | |
4860 | IncrementalModel model; |
4861 | QQmlContext *ctxt = window->rootContext(); |
4862 | ctxt->setContextProperty("testModel" , &model); |
4863 | |
4864 | window->setSource(testFileUrl(fileName: "displaylist.qml" )); |
4865 | window->show(); |
4866 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
4867 | |
4868 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
4869 | QTRY_VERIFY(listview != nullptr); |
4870 | |
4871 | QQuickItem *contentItem = listview->contentItem(); |
4872 | QTRY_VERIFY(contentItem != nullptr); |
4873 | QTRY_COMPARE(listview->count(), 35); |
4874 | |
4875 | listview->positionViewAtIndex(index: 10, mode: QQuickListView::Beginning); |
4876 | QTRY_COMPARE(listview->count(), 45); |
4877 | } |
4878 | |
4879 | void tst_QQuickListView::onAdd() |
4880 | { |
4881 | QFETCH(int, initialItemCount); |
4882 | QFETCH(int, itemsToAdd); |
4883 | |
4884 | const int delegateHeight = 10; |
4885 | QaimModel model; |
4886 | |
4887 | // these initial items should not trigger ListView.onAdd |
4888 | for (int i=0; i<initialItemCount; i++) |
4889 | model.addItem(name: "dummy value" , number: "dummy value" ); |
4890 | |
4891 | QScopedPointer<QQuickView> window(createView()); |
4892 | window->setGeometry(posx: 0,posy: 0,w: 200, h: delegateHeight * (initialItemCount + itemsToAdd)); |
4893 | QQmlContext *ctxt = window->rootContext(); |
4894 | ctxt->setContextProperty("testModel" , &model); |
4895 | ctxt->setContextProperty("delegateHeight" , delegateHeight); |
4896 | window->setSource(testFileUrl(fileName: "attachedSignals.qml" )); |
4897 | |
4898 | QQuickListView* listview = qobject_cast<QQuickListView*>(object: window->rootObject()); |
4899 | listview->setProperty(name: "width" , value: window->width()); |
4900 | listview->setProperty(name: "height" , value: window->height()); |
4901 | qApp->processEvents(); |
4902 | |
4903 | QList<QPair<QString, QString> > items; |
4904 | for (int i=0; i<itemsToAdd; i++) |
4905 | items << qMakePair(x: QString("value %1" ).arg(a: i), y: QString::number(i)); |
4906 | model.addItems(items); |
4907 | listview->forceLayout(); |
4908 | QTRY_COMPARE(listview->property("count" ).toInt(), model.count()); |
4909 | |
4910 | QVariantList result = listview->property(name: "addedDelegates" ).toList(); |
4911 | QCOMPARE(result.count(), items.count()); |
4912 | for (int i=0; i<items.count(); i++) |
4913 | QCOMPARE(result[i].toString(), items[i].first); |
4914 | } |
4915 | |
4916 | void tst_QQuickListView::onAdd_data() |
4917 | { |
4918 | QTest::addColumn<int>(name: "initialItemCount" ); |
4919 | QTest::addColumn<int>(name: "itemsToAdd" ); |
4920 | |
4921 | QTest::newRow(dataTag: "0, add 1" ) << 0 << 1; |
4922 | QTest::newRow(dataTag: "0, add 2" ) << 0 << 2; |
4923 | QTest::newRow(dataTag: "0, add 10" ) << 0 << 10; |
4924 | |
4925 | QTest::newRow(dataTag: "1, add 1" ) << 1 << 1; |
4926 | QTest::newRow(dataTag: "1, add 2" ) << 1 << 2; |
4927 | QTest::newRow(dataTag: "1, add 10" ) << 1 << 10; |
4928 | |
4929 | QTest::newRow(dataTag: "5, add 1" ) << 5 << 1; |
4930 | QTest::newRow(dataTag: "5, add 2" ) << 5 << 2; |
4931 | QTest::newRow(dataTag: "5, add 10" ) << 5 << 10; |
4932 | } |
4933 | |
4934 | void tst_QQuickListView::onRemove() |
4935 | { |
4936 | QFETCH(int, initialItemCount); |
4937 | QFETCH(int, indexToRemove); |
4938 | QFETCH(int, removeCount); |
4939 | |
4940 | const int delegateHeight = 10; |
4941 | QaimModel model; |
4942 | for (int i=0; i<initialItemCount; i++) |
4943 | model.addItem(name: QString("value %1" ).arg(a: i), number: "dummy value" ); |
4944 | |
4945 | QQuickView *window = getView(); |
4946 | QQmlContext *ctxt = window->rootContext(); |
4947 | ctxt->setContextProperty("testModel" , &model); |
4948 | ctxt->setContextProperty("delegateHeight" , delegateHeight); |
4949 | window->setSource(testFileUrl(fileName: "attachedSignals.qml" )); |
4950 | |
4951 | QQuickListView *listview = qobject_cast<QQuickListView *>(object: window->rootObject()); |
4952 | |
4953 | model.removeItems(index: indexToRemove, count: removeCount); |
4954 | listview->forceLayout(); |
4955 | QTRY_COMPARE(listview->property("count" ).toInt(), model.count()); |
4956 | |
4957 | QCOMPARE(listview->property("removedDelegateCount" ), QVariant(removeCount)); |
4958 | |
4959 | releaseView(view: window); |
4960 | } |
4961 | |
4962 | void tst_QQuickListView::onRemove_data() |
4963 | { |
4964 | QTest::addColumn<int>(name: "initialItemCount" ); |
4965 | QTest::addColumn<int>(name: "indexToRemove" ); |
4966 | QTest::addColumn<int>(name: "removeCount" ); |
4967 | |
4968 | QTest::newRow(dataTag: "remove first" ) << 1 << 0 << 1; |
4969 | QTest::newRow(dataTag: "two items, remove first" ) << 2 << 0 << 1; |
4970 | QTest::newRow(dataTag: "two items, remove last" ) << 2 << 1 << 1; |
4971 | QTest::newRow(dataTag: "two items, remove all" ) << 2 << 0 << 2; |
4972 | |
4973 | QTest::newRow(dataTag: "four items, remove first" ) << 4 << 0 << 1; |
4974 | QTest::newRow(dataTag: "four items, remove 0-2" ) << 4 << 0 << 2; |
4975 | QTest::newRow(dataTag: "four items, remove 1-3" ) << 4 << 1 << 2; |
4976 | QTest::newRow(dataTag: "four items, remove 2-4" ) << 4 << 2 << 2; |
4977 | QTest::newRow(dataTag: "four items, remove last" ) << 4 << 3 << 1; |
4978 | QTest::newRow(dataTag: "four items, remove all" ) << 4 << 0 << 4; |
4979 | |
4980 | QTest::newRow(dataTag: "ten items, remove 1-8" ) << 10 << 0 << 8; |
4981 | QTest::newRow(dataTag: "ten items, remove 2-7" ) << 10 << 2 << 5; |
4982 | QTest::newRow(dataTag: "ten items, remove 4-10" ) << 10 << 4 << 6; |
4983 | } |
4984 | |
4985 | void tst_QQuickListView::rightToLeft() |
4986 | { |
4987 | QScopedPointer<QQuickView> window(createView()); |
4988 | window->setGeometry(posx: 0,posy: 0,w: 640,h: 320); |
4989 | window->setSource(testFileUrl(fileName: "rightToLeft.qml" )); |
4990 | window->show(); |
4991 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
4992 | |
4993 | QVERIFY(window->rootObject() != nullptr); |
4994 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "view" ); |
4995 | QTRY_VERIFY(listview != nullptr); |
4996 | |
4997 | QQuickItem *contentItem = listview->contentItem(); |
4998 | QTRY_VERIFY(contentItem != nullptr); |
4999 | |
5000 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
5001 | |
5002 | QQmlObjectModel *model = window->rootObject()->findChild<QQmlObjectModel*>(aName: "itemModel" ); |
5003 | QTRY_VERIFY(model != nullptr); |
5004 | |
5005 | QTRY_COMPARE(model->count(), 3); |
5006 | QTRY_COMPARE(listview->currentIndex(), 0); |
5007 | |
5008 | // initial position at first item, right edge aligned |
5009 | QCOMPARE(listview->contentX(), -640.); |
5010 | |
5011 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "item1" ); |
5012 | QTRY_VERIFY(item); |
5013 | QTRY_COMPARE(item->x(), -100.0); |
5014 | QCOMPARE(item->height(), listview->height()); |
5015 | |
5016 | QQuickText *text = findItem<QQuickText>(parent: contentItem, objectName: "text1" ); |
5017 | QTRY_VERIFY(text); |
5018 | QTRY_COMPARE(text->text(), QLatin1String("index: 0" )); |
5019 | |
5020 | listview->setCurrentIndex(2); |
5021 | |
5022 | item = findItem<QQuickItem>(parent: contentItem, objectName: "item3" ); |
5023 | QTRY_VERIFY(item); |
5024 | QTRY_COMPARE(item->x(), -540.0); |
5025 | |
5026 | text = findItem<QQuickText>(parent: contentItem, objectName: "text3" ); |
5027 | QTRY_VERIFY(text); |
5028 | QTRY_COMPARE(text->text(), QLatin1String("index: 2" )); |
5029 | |
5030 | QCOMPARE(listview->contentX(), -640.); |
5031 | |
5032 | // Ensure resizing maintains position relative to right edge |
5033 | qobject_cast<QQuickItem*>(object: window->rootObject())->setWidth(600); |
5034 | QTRY_COMPARE(listview->contentX(), -600.); |
5035 | } |
5036 | |
5037 | void tst_QQuickListView::test_mirroring() |
5038 | { |
5039 | QScopedPointer<QQuickView> windowA(createView()); |
5040 | windowA->setSource(testFileUrl(fileName: "rightToLeft.qml" )); |
5041 | QQuickListView *listviewA = findItem<QQuickListView>(parent: windowA->rootObject(), objectName: "view" ); |
5042 | QTRY_VERIFY(listviewA != nullptr); |
5043 | |
5044 | QScopedPointer<QQuickView> windowB(createView()); |
5045 | windowB->setSource(testFileUrl(fileName: "rightToLeft.qml" )); |
5046 | QQuickListView *listviewB = findItem<QQuickListView>(parent: windowB->rootObject(), objectName: "view" ); |
5047 | QTRY_VERIFY(listviewA != nullptr); |
5048 | qApp->processEvents(); |
5049 | |
5050 | QList<QString> objectNames; |
5051 | objectNames << "item1" << "item2" ; // << "item3" |
5052 | |
5053 | listviewA->setProperty(name: "layoutDirection" , value: Qt::LeftToRight); |
5054 | listviewB->setProperty(name: "layoutDirection" , value: Qt::RightToLeft); |
5055 | QCOMPARE(listviewA->layoutDirection(), listviewA->effectiveLayoutDirection()); |
5056 | |
5057 | // LTR != RTL |
5058 | foreach (const QString objectName, objectNames) |
5059 | QVERIFY(findItem<QQuickItem>(listviewA, objectName)->x() != findItem<QQuickItem>(listviewB, objectName)->x()); |
5060 | |
5061 | listviewA->setProperty(name: "layoutDirection" , value: Qt::LeftToRight); |
5062 | listviewB->setProperty(name: "layoutDirection" , value: Qt::LeftToRight); |
5063 | |
5064 | // LTR == LTR |
5065 | foreach (const QString objectName, objectNames) |
5066 | QCOMPARE(findItem<QQuickItem>(listviewA, objectName)->x(), findItem<QQuickItem>(listviewB, objectName)->x()); |
5067 | |
5068 | QCOMPARE(listviewB->layoutDirection(), listviewB->effectiveLayoutDirection()); |
5069 | QQuickItemPrivate::get(item: listviewB)->setLayoutMirror(true); |
5070 | QVERIFY(listviewB->layoutDirection() != listviewB->effectiveLayoutDirection()); |
5071 | |
5072 | // LTR != LTR+mirror |
5073 | foreach (const QString objectName, objectNames) |
5074 | QVERIFY(findItem<QQuickItem>(listviewA, objectName)->x() != findItem<QQuickItem>(listviewB, objectName)->x()); |
5075 | |
5076 | listviewA->setProperty(name: "layoutDirection" , value: Qt::RightToLeft); |
5077 | |
5078 | // RTL == LTR+mirror |
5079 | foreach (const QString objectName, objectNames) |
5080 | QCOMPARE(findItem<QQuickItem>(listviewA, objectName)->x(), findItem<QQuickItem>(listviewB, objectName)->x()); |
5081 | |
5082 | listviewB->setProperty(name: "layoutDirection" , value: Qt::RightToLeft); |
5083 | |
5084 | // RTL != RTL+mirror |
5085 | foreach (const QString objectName, objectNames) |
5086 | QVERIFY(findItem<QQuickItem>(listviewA, objectName)->x() != findItem<QQuickItem>(listviewB, objectName)->x()); |
5087 | |
5088 | listviewA->setProperty(name: "layoutDirection" , value: Qt::LeftToRight); |
5089 | |
5090 | // LTR == RTL+mirror |
5091 | foreach (const QString objectName, objectNames) |
5092 | QCOMPARE(findItem<QQuickItem>(listviewA, objectName)->x(), findItem<QQuickItem>(listviewB, objectName)->x()); |
5093 | } |
5094 | |
5095 | void tst_QQuickListView::margins() |
5096 | { |
5097 | QScopedPointer<QQuickView> window(createView()); |
5098 | QaimModel model; |
5099 | for (int i = 0; i < 50; i++) |
5100 | model.addItem(name: "Item" + QString::number(i), number: "" ); |
5101 | |
5102 | QQmlContext *ctxt = window->rootContext(); |
5103 | ctxt->setContextProperty("testModel" , &model); |
5104 | |
5105 | window->setSource(testFileUrl(fileName: "margins.qml" )); |
5106 | window->show(); |
5107 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
5108 | |
5109 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
5110 | QTRY_VERIFY(listview != nullptr); |
5111 | QQuickItem *contentItem = listview->contentItem(); |
5112 | QTRY_VERIFY(contentItem != nullptr); |
5113 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
5114 | |
5115 | QCOMPARE(listview->contentY(), -30.); |
5116 | QCOMPARE(listview->originY(), 0.); |
5117 | |
5118 | // check end bound |
5119 | listview->positionViewAtEnd(); |
5120 | qreal pos = listview->contentY(); |
5121 | listview->setContentY(pos + 80); |
5122 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
5123 | listview->returnToBounds(); |
5124 | QTRY_COMPARE(listview->contentY(), pos + 50); |
5125 | |
5126 | // remove item before visible and check that top margin is maintained |
5127 | // and originY is updated |
5128 | listview->setContentY(100); |
5129 | model.removeItem(index: 1); |
5130 | listview->forceLayout(); |
5131 | QTRY_COMPARE(listview->count(), model.count()); |
5132 | listview->setContentY(-50); |
5133 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
5134 | listview->returnToBounds(); |
5135 | QCOMPARE(listview->originY(), 20.); |
5136 | QTRY_COMPARE(listview->contentY(), -10.); |
5137 | |
5138 | // reduce top margin |
5139 | listview->setTopMargin(20); |
5140 | QCOMPARE(listview->originY(), 20.); |
5141 | QTRY_COMPARE(listview->contentY(), 0.); |
5142 | |
5143 | // check end bound |
5144 | listview->positionViewAtEnd(); |
5145 | pos = listview->contentY(); |
5146 | listview->setContentY(pos + 80); |
5147 | listview->returnToBounds(); |
5148 | QTRY_COMPARE(listview->contentY(), pos + 50); |
5149 | |
5150 | // reduce bottom margin |
5151 | pos = listview->contentY(); |
5152 | listview->setBottomMargin(40); |
5153 | QCOMPARE(listview->originY(), 20.); |
5154 | QTRY_COMPARE(listview->contentY(), pos-10); |
5155 | } |
5156 | |
5157 | // QTBUG-24028 |
5158 | void tst_QQuickListView::marginsResize() |
5159 | { |
5160 | QFETCH(QQuickListView::Orientation, orientation); |
5161 | QFETCH(Qt::LayoutDirection, layoutDirection); |
5162 | QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection); |
5163 | QFETCH(qreal, start); |
5164 | QFETCH(qreal, end); |
5165 | |
5166 | QPoint flickStart(20, 20); |
5167 | QPoint flickEnd(20, 20); |
5168 | if (orientation == QQuickListView::Vertical) |
5169 | flickStart.ry() += (verticalLayoutDirection == QQuickItemView::TopToBottom) ? 180 : -180; |
5170 | else |
5171 | flickStart.rx() += (layoutDirection == Qt::LeftToRight) ? 180 : -180; |
5172 | |
5173 | QQuickView *window = getView(); |
5174 | |
5175 | window->setSource(testFileUrl(fileName: "margins2.qml" )); |
5176 | QQuickViewTestUtil::moveMouseAway(window); |
5177 | window->show(); |
5178 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
5179 | |
5180 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "listview" ); |
5181 | QTRY_VERIFY(listview != nullptr); |
5182 | |
5183 | listview->setOrientation(orientation); |
5184 | listview->setLayoutDirection(layoutDirection); |
5185 | listview->setVerticalLayoutDirection(verticalLayoutDirection); |
5186 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
5187 | |
5188 | // view is resized after componentCompleted - top margin should still be visible |
5189 | if (orientation == QQuickListView::Vertical) |
5190 | QCOMPARE(listview->contentY(), start); |
5191 | else |
5192 | QCOMPARE(listview->contentX(), start); |
5193 | |
5194 | // move to last index and ensure bottom margin is visible. |
5195 | listview->setCurrentIndex(19); |
5196 | if (orientation == QQuickListView::Vertical) |
5197 | QTRY_COMPARE(listview->contentY(), end); |
5198 | else |
5199 | QTRY_COMPARE(listview->contentX(), end); |
5200 | |
5201 | // flick past the end and check content pos still settles on correct extents |
5202 | flick(window, from: flickStart, to: flickEnd, duration: 180); |
5203 | QTRY_VERIFY(!listview->isMoving()); |
5204 | if (orientation == QQuickListView::Vertical) |
5205 | QTRY_COMPARE(listview->contentY(), end); |
5206 | else |
5207 | QTRY_COMPARE(listview->contentX(), end); |
5208 | |
5209 | // back to top - top margin should be visible. |
5210 | listview->setCurrentIndex(0); |
5211 | if (orientation == QQuickListView::Vertical) |
5212 | QTRY_COMPARE(listview->contentY(), start); |
5213 | else |
5214 | QTRY_COMPARE(listview->contentX(), start); |
5215 | |
5216 | // flick past the beginning and check content pos still settles on correct extents |
5217 | flick(window, from: flickEnd, to: flickStart, duration: 180); |
5218 | QTRY_VERIFY(!listview->isMoving()); |
5219 | if (orientation == QQuickListView::Vertical) |
5220 | QTRY_COMPARE(listview->contentY(), start); |
5221 | else |
5222 | QTRY_COMPARE(listview->contentX(), start); |
5223 | |
5224 | releaseView(view: window); |
5225 | } |
5226 | |
5227 | void tst_QQuickListView::marginsResize_data() |
5228 | { |
5229 | QTest::addColumn<QQuickListView::Orientation>(name: "orientation" ); |
5230 | QTest::addColumn<Qt::LayoutDirection>(name: "layoutDirection" ); |
5231 | QTest::addColumn<QQuickListView::VerticalLayoutDirection>(name: "verticalLayoutDirection" ); |
5232 | QTest::addColumn<qreal>(name: "start" ); |
5233 | QTest::addColumn<qreal>(name: "end" ); |
5234 | |
5235 | // in Right to Left mode, leftMargin still means leftMargin - it doesn't reverse to mean rightMargin |
5236 | |
5237 | QTest::newRow(dataTag: "vertical" ) |
5238 | << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom |
5239 | << -40.0 << 1020.0; |
5240 | |
5241 | QTest::newRow(dataTag: "vertical, BottomToTop" ) |
5242 | << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::BottomToTop |
5243 | << -180.0 << -1240.0; |
5244 | |
5245 | QTest::newRow(dataTag: "horizontal" ) |
5246 | << QQuickListView::Horizontal<< Qt::LeftToRight << QQuickItemView::TopToBottom |
5247 | << -40.0 << 1020.0; |
5248 | |
5249 | QTest::newRow(dataTag: "horizontal, rtl" ) |
5250 | << QQuickListView::Horizontal << Qt::RightToLeft << QQuickItemView::TopToBottom |
5251 | << -180.0 << -1240.0; |
5252 | } |
5253 | |
5254 | void tst_QQuickListView::snapToItem_data() |
5255 | { |
5256 | QTest::addColumn<QQuickListView::Orientation>(name: "orientation" ); |
5257 | QTest::addColumn<Qt::LayoutDirection>(name: "layoutDirection" ); |
5258 | QTest::addColumn<QQuickItemView::VerticalLayoutDirection>(name: "verticalLayoutDirection" ); |
5259 | QTest::addColumn<int>(name: "highlightRangeMode" ); |
5260 | QTest::addColumn<QPoint>(name: "flickStart" ); |
5261 | QTest::addColumn<QPoint>(name: "flickEnd" ); |
5262 | QTest::addColumn<qreal>(name: "snapAlignment" ); |
5263 | QTest::addColumn<qreal>(name: "endExtent" ); |
5264 | QTest::addColumn<qreal>(name: "startExtent" ); |
5265 | |
5266 | QTest::newRow(dataTag: "vertical, top to bottom" ) |
5267 | << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom << int(QQuickItemView::NoHighlightRange) |
5268 | << QPoint(20, 200) << QPoint(20, 20) << 60.0 << 560.0 << 0.0; |
5269 | |
5270 | QTest::newRow(dataTag: "vertical, bottom to top" ) |
5271 | << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::BottomToTop << int(QQuickItemView::NoHighlightRange) |
5272 | << QPoint(20, 20) << QPoint(20, 200) << -60.0 << -560.0 - 240.0 << -240.0; |
5273 | |
5274 | QTest::newRow(dataTag: "horizontal, left to right" ) |
5275 | << QQuickListView::Horizontal << Qt::LeftToRight << QQuickItemView::TopToBottom << int(QQuickItemView::NoHighlightRange) |
5276 | << QPoint(200, 20) << QPoint(20, 20) << 60.0 << 560.0 << 0.0; |
5277 | |
5278 | QTest::newRow(dataTag: "horizontal, right to left" ) |
5279 | << QQuickListView::Horizontal << Qt::RightToLeft << QQuickItemView::TopToBottom << int(QQuickItemView::NoHighlightRange) |
5280 | << QPoint(20, 20) << QPoint(200, 20) << -60.0 << -560.0 - 240.0 << -240.0; |
5281 | |
5282 | QTest::newRow(dataTag: "vertical, top to bottom, enforce range" ) |
5283 | << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom << int(QQuickItemView::StrictlyEnforceRange) |
5284 | << QPoint(20, 200) << QPoint(20, 20) << 60.0 << 700.0 << -20.0; |
5285 | |
5286 | QTest::newRow(dataTag: "vertical, bottom to top, enforce range" ) |
5287 | << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::BottomToTop << int(QQuickItemView::StrictlyEnforceRange) |
5288 | << QPoint(20, 20) << QPoint(20, 200) << -60.0 << -560.0 - 240.0 - 140.0 << -220.0; |
5289 | |
5290 | QTest::newRow(dataTag: "horizontal, left to right, enforce range" ) |
5291 | << QQuickListView::Horizontal << Qt::LeftToRight << QQuickItemView::TopToBottom << int(QQuickItemView::StrictlyEnforceRange) |
5292 | << QPoint(200, 20) << QPoint(20, 20) << 60.0 << 700.0 << -20.0; |
5293 | |
5294 | QTest::newRow(dataTag: "horizontal, right to left, enforce range" ) |
5295 | << QQuickListView::Horizontal << Qt::RightToLeft << QQuickItemView::TopToBottom << int(QQuickItemView::StrictlyEnforceRange) |
5296 | << QPoint(20, 20) << QPoint(200, 20) << -60.0 << -560.0 - 240.0 - 140.0 << -220.0; |
5297 | } |
5298 | |
5299 | void tst_QQuickListView::snapToItem() |
5300 | { |
5301 | QFETCH(QQuickListView::Orientation, orientation); |
5302 | QFETCH(Qt::LayoutDirection, layoutDirection); |
5303 | QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection); |
5304 | QFETCH(int, highlightRangeMode); |
5305 | QFETCH(QPoint, flickStart); |
5306 | QFETCH(QPoint, flickEnd); |
5307 | QFETCH(qreal, snapAlignment); |
5308 | QFETCH(qreal, endExtent); |
5309 | QFETCH(qreal, startExtent); |
5310 | |
5311 | QQuickView *window = getView(); |
5312 | QQuickViewTestUtil::moveMouseAway(window); |
5313 | |
5314 | window->setSource(testFileUrl(fileName: "snapToItem.qml" )); |
5315 | window->show(); |
5316 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
5317 | |
5318 | |
5319 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
5320 | QTRY_VERIFY(listview != nullptr); |
5321 | |
5322 | listview->setOrientation(orientation); |
5323 | listview->setLayoutDirection(layoutDirection); |
5324 | listview->setVerticalLayoutDirection(verticalLayoutDirection); |
5325 | listview->setHighlightRangeMode(QQuickItemView::HighlightRangeMode(highlightRangeMode)); |
5326 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
5327 | |
5328 | QQuickItem *contentItem = listview->contentItem(); |
5329 | QTRY_VERIFY(contentItem != nullptr); |
5330 | |
5331 | // confirm that a flick hits an item boundary |
5332 | flick(window, from: flickStart, to: flickEnd, duration: 180); |
5333 | QTRY_VERIFY(listview->isMoving() == false); // wait until it stops |
5334 | if (orientation == QQuickListView::Vertical) |
5335 | QCOMPARE(qreal(fmod(listview->contentY(),80.0)), snapAlignment); |
5336 | else |
5337 | QCOMPARE(qreal(fmod(listview->contentX(),80.0)), snapAlignment); |
5338 | |
5339 | // flick to end |
5340 | do { |
5341 | flick(window, from: flickStart, to: flickEnd, duration: 180); |
5342 | QTRY_VERIFY(listview->isMoving() == false); // wait until it stops |
5343 | } while (orientation == QQuickListView::Vertical |
5344 | ? verticalLayoutDirection == QQuickItemView::TopToBottom ? !listview->isAtYEnd() : !listview->isAtYBeginning() |
5345 | : layoutDirection == Qt::LeftToRight ? !listview->isAtXEnd() : !listview->isAtXBeginning()); |
5346 | |
5347 | if (orientation == QQuickListView::Vertical) |
5348 | QCOMPARE(listview->contentY(), endExtent); |
5349 | else |
5350 | QCOMPARE(listview->contentX(), endExtent); |
5351 | |
5352 | // flick to start |
5353 | do { |
5354 | flick(window, from: flickEnd, to: flickStart, duration: 180); |
5355 | QTRY_VERIFY(listview->isMoving() == false); // wait until it stops |
5356 | } while (orientation == QQuickListView::Vertical |
5357 | ? verticalLayoutDirection == QQuickItemView::TopToBottom ? !listview->isAtYBeginning() : !listview->isAtYEnd() |
5358 | : layoutDirection == Qt::LeftToRight ? !listview->isAtXBeginning() : !listview->isAtXEnd()); |
5359 | |
5360 | if (orientation == QQuickListView::Vertical) |
5361 | QCOMPARE(listview->contentY(), startExtent); |
5362 | else |
5363 | QCOMPARE(listview->contentX(), startExtent); |
5364 | |
5365 | releaseView(view: window); |
5366 | } |
5367 | void tst_QQuickListView::snapToItemWithSpacing_QTBUG_59852() |
5368 | { |
5369 | QQuickView *window = getView(); |
5370 | |
5371 | window->setSource(testFileUrl(fileName: "snapToItemWithSpacing.qml" )); |
5372 | window->show(); |
5373 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
5374 | |
5375 | auto *listView = qobject_cast<QQuickListView*>(object: window->rootObject()); |
5376 | QVERIFY(listView); |
5377 | |
5378 | QVERIFY(QQuickTest::qWaitForItemPolished(listView)); |
5379 | |
5380 | // each item in the list is 100 pixels tall, and the spacing is 100 |
5381 | |
5382 | listView->setContentY(110); // this is right below the first item |
5383 | listView->returnToBounds(); |
5384 | QCOMPARE(listView->contentY(), 200); // the position of the second item |
5385 | |
5386 | listView->setContentY(60); // this is right below the middle of the first item |
5387 | listView->returnToBounds(); |
5388 | QCOMPARE(listView->contentY(), 0); // it's farther to go to the next item, so snaps to the first |
5389 | |
5390 | releaseView(view: window); |
5391 | } |
5392 | |
5393 | static void drag_helper(QWindow *window, QPoint *startPos, const QPoint &delta) |
5394 | { |
5395 | QPoint pos = *startPos; |
5396 | int dragDistance = delta.manhattanLength(); |
5397 | Q_ASSERT(qAbs(delta.x()) >= 1 || qAbs(delta.y()) >= 1); |
5398 | |
5399 | const int stepSize = 8; |
5400 | QPoint unitVector(0, 0); |
5401 | if (delta.x()) |
5402 | unitVector.setX(qBound(min: -1, val: delta.x(), max: 1)); |
5403 | if (delta.y()) |
5404 | unitVector.setY(qBound(min: -1, val: delta.y(), max: 1)); |
5405 | QPoint dragStepSize = unitVector * stepSize; |
5406 | int nDragSteps = qAbs(t: dragDistance/stepSize); |
5407 | |
5408 | for (int i = 0 ; i < nDragSteps; ++i) { |
5409 | QTest::mouseMove(window, pos); |
5410 | pos += dragStepSize; |
5411 | } |
5412 | // Move to the final position |
5413 | pos = *startPos + delta; |
5414 | QTest::mouseMove(window, pos); |
5415 | *startPos = pos; |
5416 | } |
5417 | |
5418 | static void dragtwice(QWindow *window, QPoint *startPos, const QPoint &delta1, const QPoint &delta2) |
5419 | { |
5420 | const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); |
5421 | QPoint &pos = *startPos; |
5422 | QPoint unitVector(0, 0); |
5423 | if (delta1.x()) |
5424 | unitVector.setX(qBound(min: -1, val: delta1.x(), max: 1)); |
5425 | if (delta1.y()) |
5426 | unitVector.setY(qBound(min: -1, val: delta1.y(), max: 1)); |
5427 | |
5428 | // go just beyond the drag theshold |
5429 | drag_helper(window, startPos: &pos, delta: unitVector * (dragThreshold + 1)); |
5430 | drag_helper(window, startPos: &pos, delta: unitVector); |
5431 | |
5432 | // next drag will actually scroll the listview |
5433 | if (delta1.manhattanLength() >= 1) |
5434 | drag_helper(window, startPos: &pos, delta: delta1); |
5435 | if (delta2.manhattanLength() >= 1) |
5436 | drag_helper(window, startPos: &pos, delta: delta2); |
5437 | } |
5438 | |
5439 | struct MyListView : public QQuickListView{ |
5440 | qreal contentPosition() const |
5441 | { |
5442 | return (orientation() == QQuickListView::Horizontal ? contentX(): contentY()); |
5443 | } |
5444 | |
5445 | qreal () const |
5446 | { |
5447 | return (orientation() == QQuickListView::Horizontal ? headerItem()->x() : headerItem()->y()); |
5448 | } |
5449 | }; |
5450 | |
5451 | void tst_QQuickListView::() |
5452 | { |
5453 | QFETCH(QQuickItemView::LayoutDirection, layoutDirection); |
5454 | QFETCH(QQuickListView::HeaderPositioning, ); |
5455 | QFETCH(int, firstDragDistance); |
5456 | QFETCH(int, secondDragDistance); |
5457 | QFETCH(int, expectedContentPosition); |
5458 | QFETCH(int, ); |
5459 | |
5460 | QQuickView *window = getView(); |
5461 | window->setSource(testFileUrl(fileName: "headerSnapToItem.qml" )); |
5462 | window->show(); |
5463 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
5464 | |
5465 | MyListView *listview = static_cast<MyListView *>(findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" )); |
5466 | QVERIFY(listview != nullptr); |
5467 | |
5468 | const bool horizontal = layoutDirection < QQuickItemView::VerticalTopToBottom; |
5469 | listview->setOrientation(horizontal ? QQuickListView::Horizontal : QQuickListView::Vertical); |
5470 | |
5471 | if (horizontal) |
5472 | listview->setLayoutDirection(static_cast<Qt::LayoutDirection>(layoutDirection)); |
5473 | else |
5474 | listview->setVerticalLayoutDirection(static_cast<QQuickItemView::VerticalLayoutDirection>(layoutDirection)); |
5475 | |
5476 | listview->setHeaderPositioning(headerPositioning); |
5477 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
5478 | |
5479 | QQuickItem *contentItem = listview->contentItem(); |
5480 | QVERIFY(contentItem != nullptr); |
5481 | QQuickItem * = findItem<QQuickItem>(parent: contentItem, objectName: "header" ); |
5482 | QVERIFY(header != nullptr); |
5483 | QCOMPARE(header, listview->headerItem()); |
5484 | |
5485 | QPoint startPos = (QPointF(listview->width(), listview->height())/2).toPoint(); |
5486 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: startPos, delay: 200); |
5487 | |
5488 | QPoint firstDragDelta(0, firstDragDistance); |
5489 | QPoint secondDragDelta = QPoint(0, secondDragDistance); |
5490 | if (horizontal) { |
5491 | firstDragDelta = firstDragDelta.transposed(); |
5492 | secondDragDelta = secondDragDelta.transposed(); |
5493 | } |
5494 | |
5495 | dragtwice(window, startPos: &startPos, delta1: firstDragDelta, delta2: secondDragDelta); |
5496 | |
5497 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: startPos, delay: 200); // Wait 200 ms before we release to avoid trigger a flick |
5498 | |
5499 | // wait for the "fixup" animation to finish |
5500 | QTest::qWaitFor(predicate: [&]() |
5501 | { return !listview->isMoving();} |
5502 | ); |
5503 | |
5504 | QCOMPARE(listview->contentPosition(), expectedContentPosition); |
5505 | QCOMPARE(listview->headerPosition(), expectedHeaderPosition); |
5506 | } |
5507 | |
5508 | void tst_QQuickListView::() |
5509 | { |
5510 | QTest::addColumn<QQuickItemView::LayoutDirection>(name: "layoutDirection" ); |
5511 | QTest::addColumn<QQuickListView::HeaderPositioning>(name: "headerPositioning" ); |
5512 | QTest::addColumn<int>(name: "firstDragDistance" ); |
5513 | QTest::addColumn<int>(name: "secondDragDistance" ); |
5514 | QTest::addColumn<int>(name: "expectedContentPosition" ); |
5515 | QTest::addColumn<int>(name: "expectedHeaderPosition" ); |
5516 | |
5517 | // -------------------- |
5518 | // InlineHeader TopToBottom |
5519 | QTest::newRow(dataTag: "InlineHeader TopToBottom -10" ) << QQuickItemView::VerticalTopToBottom |
5520 | << QQuickListView::InlineHeader |
5521 | << -10 << 0 |
5522 | << -30 << -30; |
5523 | |
5524 | QTest::newRow(dataTag: "InlineHeader TopToBottom -14" ) << QQuickItemView::VerticalTopToBottom |
5525 | << QQuickListView::InlineHeader |
5526 | << -14 << 0 |
5527 | << -30 << -30; |
5528 | |
5529 | QTest::newRow(dataTag: "InlineHeader TopToBottom -16" ) << QQuickItemView::VerticalTopToBottom |
5530 | << QQuickListView::InlineHeader |
5531 | << -16 << 0 |
5532 | << 0 << -30; |
5533 | |
5534 | QTest::newRow(dataTag: "InlineHeader TopToBottom -30" ) << QQuickItemView::VerticalTopToBottom |
5535 | << QQuickListView::InlineHeader |
5536 | << -30 << 0 |
5537 | << 0 << -30; |
5538 | |
5539 | QTest::newRow(dataTag: "InlineHeader TopToBottom -39" ) << QQuickItemView::VerticalTopToBottom |
5540 | << QQuickListView::InlineHeader |
5541 | << -39 << 0 |
5542 | << 0 << -30; |
5543 | |
5544 | QTest::newRow(dataTag: "InlineHeader TopToBottom -41" ) << QQuickItemView::VerticalTopToBottom |
5545 | << QQuickListView::InlineHeader |
5546 | << -41 << 0 |
5547 | << 20 << -30; |
5548 | |
5549 | QTest::newRow(dataTag: "InlineHeader TopToBottom -65+10" ) << QQuickItemView::VerticalTopToBottom |
5550 | << QQuickListView::InlineHeader |
5551 | << -65 << 10 |
5552 | << 20 << -30; |
5553 | |
5554 | // -------------------- |
5555 | // InlineHeader BottomToTop |
5556 | QTest::newRow(dataTag: "InlineHeader BottomToTop +10" ) << QQuickItemView::VerticalBottomToTop |
5557 | << QQuickListView::InlineHeader |
5558 | << 10 << 0 |
5559 | << -170 << 0; |
5560 | |
5561 | QTest::newRow(dataTag: "InlineHeader BottomToTop +14" ) << QQuickItemView::VerticalBottomToTop |
5562 | << QQuickListView::InlineHeader |
5563 | << 14 << 0 |
5564 | << -170 << 0; |
5565 | |
5566 | QTest::newRow(dataTag: "InlineHeader BottomToTop +16" ) << QQuickItemView::VerticalBottomToTop |
5567 | << QQuickListView::InlineHeader |
5568 | << 16 << 0 |
5569 | << -200 << 0; |
5570 | |
5571 | QTest::newRow(dataTag: "InlineHeader BottomToTop +30" ) << QQuickItemView::VerticalBottomToTop |
5572 | << QQuickListView::InlineHeader |
5573 | << 30 << 0 |
5574 | << -200 << 0; |
5575 | |
5576 | QTest::newRow(dataTag: "InlineHeader BottomToTop +39" ) << QQuickItemView::VerticalBottomToTop |
5577 | << QQuickListView::InlineHeader |
5578 | << 39 << 0 |
5579 | << -200 << 0; |
5580 | |
5581 | QTest::newRow(dataTag: "InlineHeader BottomToTop +41" ) << QQuickItemView::VerticalBottomToTop |
5582 | << QQuickListView::InlineHeader |
5583 | << 41 << 0 |
5584 | << -220 << 0; |
5585 | |
5586 | QTest::newRow(dataTag: "InlineHeader BottomToTop +65-10" ) << QQuickItemView::VerticalBottomToTop |
5587 | << QQuickListView::InlineHeader |
5588 | << 65 << -10 |
5589 | << -220 << 0; |
5590 | |
5591 | // -------------------- |
5592 | // InlineHeader LeftToRight |
5593 | QTest::newRow(dataTag: "InlineHeader LeftToRight -10" ) << QQuickItemView::LeftToRight |
5594 | << QQuickListView::InlineHeader |
5595 | << -10 << 0 |
5596 | << -30 << -30; |
5597 | |
5598 | QTest::newRow(dataTag: "InlineHeader LeftToRight -14" ) << QQuickItemView::LeftToRight |
5599 | << QQuickListView::InlineHeader |
5600 | << -14 << 0 |
5601 | << -30 << -30; |
5602 | |
5603 | QTest::newRow(dataTag: "InlineHeader LeftToRight -16" ) << QQuickItemView::LeftToRight |
5604 | << QQuickListView::InlineHeader |
5605 | << -16 << 0 |
5606 | << 0 << -30; |
5607 | |
5608 | QTest::newRow(dataTag: "InlineHeader LeftToRight -30" ) << QQuickItemView::LeftToRight |
5609 | << QQuickListView::InlineHeader |
5610 | << -30 << 0 |
5611 | << 0 << -30; |
5612 | |
5613 | QTest::newRow(dataTag: "InlineHeader LeftToRight -39" ) << QQuickItemView::LeftToRight |
5614 | << QQuickListView::InlineHeader |
5615 | << -39 << 0 |
5616 | << 0 << -30; |
5617 | |
5618 | QTest::newRow(dataTag: "InlineHeader LeftToRight -41" ) << QQuickItemView::LeftToRight |
5619 | << QQuickListView::InlineHeader |
5620 | << -41 << 0 |
5621 | << 20 << -30; |
5622 | |
5623 | QTest::newRow(dataTag: "InlineHeader LeftToRight -65+10" ) << QQuickItemView::LeftToRight |
5624 | << QQuickListView::InlineHeader |
5625 | << -65 << 10 |
5626 | << 20 << -30; |
5627 | |
5628 | // -------------------- |
5629 | // InlineHeader RightToLeft |
5630 | QTest::newRow(dataTag: "InlineHeader RightToLeft +10" ) << QQuickItemView::RightToLeft |
5631 | << QQuickListView::InlineHeader |
5632 | << 10 << 0 |
5633 | << -210 << 0; |
5634 | |
5635 | QTest::newRow(dataTag: "InlineHeader RightToLeft +14" ) << QQuickItemView::RightToLeft |
5636 | << QQuickListView::InlineHeader |
5637 | << 14 << 0 |
5638 | << -210 << 0; |
5639 | |
5640 | QTest::newRow(dataTag: "InlineHeader RightToLeft +16" ) << QQuickItemView::RightToLeft |
5641 | << QQuickListView::InlineHeader |
5642 | << 16 << 0 |
5643 | << -240 << 0; |
5644 | |
5645 | QTest::newRow(dataTag: "InlineHeader RightToLeft +30" ) << QQuickItemView::RightToLeft |
5646 | << QQuickListView::InlineHeader |
5647 | << 30 << 0 |
5648 | << -240 << 0; |
5649 | |
5650 | QTest::newRow(dataTag: "InlineHeader RightToLeft +39" ) << QQuickItemView::RightToLeft |
5651 | << QQuickListView::InlineHeader |
5652 | << 39 << 0 |
5653 | << -240 << 0; |
5654 | |
5655 | QTest::newRow(dataTag: "InlineHeader RightToLeft +41" ) << QQuickItemView::RightToLeft |
5656 | << QQuickListView::InlineHeader |
5657 | << 41 << 0 |
5658 | << -260 << 0; |
5659 | |
5660 | QTest::newRow(dataTag: "InlineHeader RightToLeft +65-10" ) << QQuickItemView::RightToLeft |
5661 | << QQuickListView::InlineHeader |
5662 | << 65 << -10 |
5663 | << -260 << 0; |
5664 | |
5665 | // -------------------- |
5666 | // OverlayHeader TopToBottom |
5667 | QTest::newRow(dataTag: "OverlayHeader TopToBottom +9" ) << QQuickItemView::VerticalTopToBottom |
5668 | << QQuickListView::OverlayHeader |
5669 | << 9 << 0 |
5670 | << -30 << -30; |
5671 | |
5672 | QTest::newRow(dataTag: "OverlayHeader TopToBottom -9" ) << QQuickItemView::VerticalTopToBottom |
5673 | << QQuickListView::OverlayHeader |
5674 | << -9 << 0 |
5675 | << -30 << -30; |
5676 | |
5677 | QTest::newRow(dataTag: "OverlayHeader TopToBottom -11" ) << QQuickItemView::VerticalTopToBottom |
5678 | << QQuickListView::OverlayHeader |
5679 | << -11 << 0 |
5680 | << -10 << -10; |
5681 | |
5682 | QTest::newRow(dataTag: "OverlayHeader TopToBottom -29" ) << QQuickItemView::VerticalTopToBottom |
5683 | << QQuickListView::OverlayHeader |
5684 | << -29 << 0 |
5685 | << -10 << -10; |
5686 | |
5687 | QTest::newRow(dataTag: "OverlayHeader TopToBottom -31" ) << QQuickItemView::VerticalTopToBottom |
5688 | << QQuickListView::OverlayHeader |
5689 | << -31 << 0 |
5690 | << 10 << 10; |
5691 | |
5692 | // -------------------- |
5693 | // OverlayHeader BottomToTop |
5694 | QTest::newRow(dataTag: "OverlayHeader BottomToTop -9" ) << QQuickItemView::VerticalBottomToTop |
5695 | << QQuickListView::OverlayHeader |
5696 | << -9 << 0 |
5697 | << -170 << 0; |
5698 | |
5699 | QTest::newRow(dataTag: "OverlayHeader BottomToTop +9" ) << QQuickItemView::VerticalBottomToTop |
5700 | << QQuickListView::OverlayHeader |
5701 | << 9 << 0 |
5702 | << -170 << 0; |
5703 | |
5704 | QTest::newRow(dataTag: "OverlayHeader BottomToTop +11" ) << QQuickItemView::VerticalBottomToTop |
5705 | << QQuickListView::OverlayHeader |
5706 | << 11 << 0 |
5707 | << -190 << -20; |
5708 | |
5709 | QTest::newRow(dataTag: "OverlayHeader BottomToTop +29" ) << QQuickItemView::VerticalBottomToTop |
5710 | << QQuickListView::OverlayHeader |
5711 | << 29 << 0 |
5712 | << -190 << -20; |
5713 | |
5714 | QTest::newRow(dataTag: "OverlayHeader BottomToTop +31" ) << QQuickItemView::VerticalBottomToTop |
5715 | << QQuickListView::OverlayHeader |
5716 | << 31 << 0 |
5717 | << -210 << -40; |
5718 | |
5719 | // -------------------- |
5720 | // OverlayHeader LeftToRight |
5721 | QTest::newRow(dataTag: "OverlayHeader LeftToRight +9" ) << QQuickItemView::LeftToRight |
5722 | << QQuickListView::OverlayHeader |
5723 | << 9 << 0 |
5724 | << -30 << -30; |
5725 | |
5726 | QTest::newRow(dataTag: "OverlayHeader LeftToRight -9" ) << QQuickItemView::LeftToRight |
5727 | << QQuickListView::OverlayHeader |
5728 | << -9 << 0 |
5729 | << -30 << -30; |
5730 | |
5731 | QTest::newRow(dataTag: "OverlayHeader LeftToRight -11" ) << QQuickItemView::LeftToRight |
5732 | << QQuickListView::OverlayHeader |
5733 | << -11 << 0 |
5734 | << -10 << -10; |
5735 | |
5736 | QTest::newRow(dataTag: "OverlayHeader LeftToRight -29" ) << QQuickItemView::LeftToRight |
5737 | << QQuickListView::OverlayHeader |
5738 | << -29 << 0 |
5739 | << -10 << -10; |
5740 | |
5741 | QTest::newRow(dataTag: "OverlayHeader LeftToRight -31" ) << QQuickItemView::LeftToRight |
5742 | << QQuickListView::OverlayHeader |
5743 | << -31 << 0 |
5744 | << 10 << 10; |
5745 | |
5746 | // -------------------- |
5747 | // OverlayHeader RightToLeft |
5748 | QTest::newRow(dataTag: "OverlayHeader RightToLeft -9" ) << QQuickItemView::RightToLeft |
5749 | << QQuickListView::OverlayHeader |
5750 | << -9 << 0 |
5751 | << -210 << 0; |
5752 | |
5753 | QTest::newRow(dataTag: "OverlayHeader RightToLeft +9" ) << QQuickItemView::RightToLeft |
5754 | << QQuickListView::OverlayHeader |
5755 | << 9 << 0 |
5756 | << -210 << 0; |
5757 | |
5758 | QTest::newRow(dataTag: "OverlayHeader RightToLeft +11" ) << QQuickItemView::RightToLeft |
5759 | << QQuickListView::OverlayHeader |
5760 | << 11 << 0 |
5761 | << -230 << -20; |
5762 | |
5763 | QTest::newRow(dataTag: "OverlayHeader RightToLeft +29" ) << QQuickItemView::RightToLeft |
5764 | << QQuickListView::OverlayHeader |
5765 | << 29 << 0 |
5766 | << -230 << -20; |
5767 | |
5768 | QTest::newRow(dataTag: "OverlayHeader RightToLeft +31" ) << QQuickItemView::RightToLeft |
5769 | << QQuickListView::OverlayHeader |
5770 | << 31 << 0 |
5771 | << -250 << -40; |
5772 | |
5773 | // -------------------- |
5774 | // PullbackHeader TopToBottom |
5775 | QTest::newRow(dataTag: "PullbackHeader TopToBottom -2" ) << QQuickItemView::VerticalTopToBottom |
5776 | << QQuickListView::PullBackHeader |
5777 | << -2 << 0 |
5778 | << -30 << -30; |
5779 | |
5780 | QTest::newRow(dataTag: "PullbackHeader TopToBottom -10" ) << QQuickItemView::VerticalTopToBottom |
5781 | << QQuickListView::PullBackHeader |
5782 | << -10 << 0 |
5783 | << -30 << -30; |
5784 | |
5785 | QTest::newRow(dataTag: "PullbackHeader TopToBottom -11" ) << QQuickItemView::VerticalTopToBottom |
5786 | << QQuickListView::PullBackHeader |
5787 | << -11 << 0 |
5788 | << -10 << -10; |
5789 | |
5790 | QTest::newRow(dataTag: "PullbackHeader TopToBottom -14" ) << QQuickItemView::VerticalTopToBottom |
5791 | << QQuickListView::PullBackHeader |
5792 | << -14 << 0 |
5793 | << -10 << -10; |
5794 | |
5795 | QTest::newRow(dataTag: "PullbackHeader TopToBottom -16" ) << QQuickItemView::VerticalTopToBottom |
5796 | << QQuickListView::PullBackHeader |
5797 | << -16 << 0 |
5798 | << 0 << -30; |
5799 | |
5800 | QTest::newRow(dataTag: "PullbackHeader TopToBottom -20" ) << QQuickItemView::VerticalTopToBottom |
5801 | << QQuickListView::PullBackHeader |
5802 | << -20 << 0 |
5803 | << 0 << -30; |
5804 | |
5805 | QTest::newRow(dataTag: "PullbackHeader TopToBottom -65+10" ) << QQuickItemView::VerticalTopToBottom |
5806 | << QQuickListView::PullBackHeader |
5807 | << -65 << 10 |
5808 | << 20 << -10; |
5809 | |
5810 | QTest::newRow(dataTag: "PullbackHeader TopToBottom -65+20" ) << QQuickItemView::VerticalTopToBottom |
5811 | << QQuickListView::PullBackHeader |
5812 | << -65 << 20 |
5813 | << 10 << 10; |
5814 | |
5815 | // Should move header even if contentY doesn't move (its aligned with top) |
5816 | QTest::newRow(dataTag: "PullbackHeader TopToBottom -55+5" ) << QQuickItemView::VerticalTopToBottom |
5817 | << QQuickListView::PullBackHeader |
5818 | << -55 << 5 |
5819 | << 20 << -10; |
5820 | |
5821 | // Should move header even if contentY doesn't move (it's aligned with header) |
5822 | QTest::newRow(dataTag: "PullbackHeader TopToBottom -76+16" ) << QQuickItemView::VerticalTopToBottom |
5823 | << QQuickListView::PullBackHeader |
5824 | << -76 << 16 |
5825 | << 30 << 30; |
5826 | |
5827 | // -------------------- |
5828 | // PullbackHeader BottomToTop |
5829 | QTest::newRow(dataTag: "PullbackHeader BottomToTop +2" ) << QQuickItemView::VerticalBottomToTop |
5830 | << QQuickListView::PullBackHeader |
5831 | << +2 << 0 |
5832 | << -170 << 0; |
5833 | |
5834 | QTest::newRow(dataTag: "PullbackHeader BottomToTop +9" ) << QQuickItemView::VerticalBottomToTop |
5835 | << QQuickListView::PullBackHeader |
5836 | << +9 << 0 |
5837 | << -170 << 0; |
5838 | |
5839 | QTest::newRow(dataTag: "PullbackHeader BottomToTop +11" ) << QQuickItemView::VerticalBottomToTop |
5840 | << QQuickListView::PullBackHeader |
5841 | << +11 << 0 |
5842 | << -190 << -20; |
5843 | |
5844 | QTest::newRow(dataTag: "PullbackHeader BottomToTop +14" ) << QQuickItemView::VerticalBottomToTop |
5845 | << QQuickListView::PullBackHeader |
5846 | << +14 << 0 |
5847 | << -190 << -20; |
5848 | |
5849 | QTest::newRow(dataTag: "PullbackHeader BottomToTop +16" ) << QQuickItemView::VerticalBottomToTop |
5850 | << QQuickListView::PullBackHeader |
5851 | << +16 << 0 |
5852 | << -200 << 0; |
5853 | |
5854 | QTest::newRow(dataTag: "PullbackHeader BottomToTop +20" ) << QQuickItemView::VerticalBottomToTop |
5855 | << QQuickListView::PullBackHeader |
5856 | << +20 << 0 |
5857 | << -200 << 0; |
5858 | |
5859 | QTest::newRow(dataTag: "PullbackHeader BottomToTop +65-10" ) << QQuickItemView::VerticalBottomToTop |
5860 | << QQuickListView::PullBackHeader |
5861 | << +65 << -10 |
5862 | << -220 << -20; |
5863 | |
5864 | QTest::newRow(dataTag: "PullbackHeader BottomToTop +65-20" ) << QQuickItemView::VerticalBottomToTop |
5865 | << QQuickListView::PullBackHeader |
5866 | << +65 << -20 |
5867 | << -210 << -40; |
5868 | |
5869 | // Should move header even if contentY doesn't move (it's aligned with top) |
5870 | QTest::newRow(dataTag: "PullbackHeader BottomToTop +55-5" ) << QQuickItemView::VerticalBottomToTop |
5871 | << QQuickListView::PullBackHeader |
5872 | << 55 << -5 |
5873 | << -220 << -20; |
5874 | |
5875 | // Should move header even if contentY doesn't move (it's aligned with header) |
5876 | QTest::newRow(dataTag: "PullbackHeader BottomToTop +76-16" ) << QQuickItemView::VerticalBottomToTop |
5877 | << QQuickListView::PullBackHeader |
5878 | << 76 << -16 |
5879 | << -230 << -60; |
5880 | |
5881 | // -------------------- |
5882 | // PullbackHeader LeftToRight |
5883 | QTest::newRow(dataTag: "PullbackHeader LeftToRight -2" ) << QQuickItemView::LeftToRight |
5884 | << QQuickListView::PullBackHeader |
5885 | << -2 << 0 |
5886 | << -30 << -30; |
5887 | |
5888 | QTest::newRow(dataTag: "PullbackHeader LeftToRight -10" ) << QQuickItemView::LeftToRight |
5889 | << QQuickListView::PullBackHeader |
5890 | << -10 << 0 |
5891 | << -30 << -30; |
5892 | |
5893 | QTest::newRow(dataTag: "PullbackHeader LeftToRight -11" ) << QQuickItemView::LeftToRight |
5894 | << QQuickListView::PullBackHeader |
5895 | << -11 << 0 |
5896 | << -10 << -10; |
5897 | |
5898 | QTest::newRow(dataTag: "PullbackHeader LeftToRight -14" ) << QQuickItemView::LeftToRight |
5899 | << QQuickListView::PullBackHeader |
5900 | << -14 << 0 |
5901 | << -10 << -10; |
5902 | |
5903 | QTest::newRow(dataTag: "PullbackHeader LeftToRight -16" ) << QQuickItemView::LeftToRight |
5904 | << QQuickListView::PullBackHeader |
5905 | << -16 << 0 |
5906 | << 0 << -30; |
5907 | |
5908 | QTest::newRow(dataTag: "PullbackHeader LeftToRight -20" ) << QQuickItemView::LeftToRight |
5909 | << QQuickListView::PullBackHeader |
5910 | << -20 << 0 |
5911 | << 0 << -30; |
5912 | |
5913 | QTest::newRow(dataTag: "PullbackHeader LeftToRight -65+10" ) << QQuickItemView::LeftToRight |
5914 | << QQuickListView::PullBackHeader |
5915 | << -65 << 10 |
5916 | << 20 << -10; |
5917 | |
5918 | QTest::newRow(dataTag: "PullbackHeader LeftToRight -65+20" ) << QQuickItemView::LeftToRight |
5919 | << QQuickListView::PullBackHeader |
5920 | << -65 << 20 |
5921 | << 10 << 10; |
5922 | |
5923 | // Should move header even if contentX doesn't move (its aligned with top) |
5924 | QTest::newRow(dataTag: "PullbackHeader LeftToRight -55+5" ) << QQuickItemView::LeftToRight |
5925 | << QQuickListView::PullBackHeader |
5926 | << -55 << 5 |
5927 | << 20 << -10; |
5928 | |
5929 | // Should move header even if contentX doesn't move (it's aligned with header) |
5930 | QTest::newRow(dataTag: "PullbackHeader LeftToRight -76+16" ) << QQuickItemView::LeftToRight |
5931 | << QQuickListView::PullBackHeader |
5932 | << -76 << 16 |
5933 | << 30 << 30; |
5934 | |
5935 | // -------------------- |
5936 | // PullbackHeader RightToLeft |
5937 | QTest::newRow(dataTag: "PullbackHeader RightToLeft +2" ) << QQuickItemView::RightToLeft |
5938 | << QQuickListView::PullBackHeader |
5939 | << +2 << 0 |
5940 | << -210 << 0; |
5941 | |
5942 | QTest::newRow(dataTag: "PullbackHeader RightToLeft +9" ) << QQuickItemView::RightToLeft |
5943 | << QQuickListView::PullBackHeader |
5944 | << +9 << 0 |
5945 | << -210 << 0; |
5946 | |
5947 | QTest::newRow(dataTag: "PullbackHeader RightToLeft +11" ) << QQuickItemView::RightToLeft |
5948 | << QQuickListView::PullBackHeader |
5949 | << +11 << 0 |
5950 | << -230 << -20; |
5951 | |
5952 | QTest::newRow(dataTag: "PullbackHeader RightToLeft +14" ) << QQuickItemView::RightToLeft |
5953 | << QQuickListView::PullBackHeader |
5954 | << +14 << 0 |
5955 | << -230 << -20; |
5956 | |
5957 | QTest::newRow(dataTag: "PullbackHeader RightToLeft +16" ) << QQuickItemView::RightToLeft |
5958 | << QQuickListView::PullBackHeader |
5959 | << +16 << 0 |
5960 | << -240 << 0; |
5961 | |
5962 | QTest::newRow(dataTag: "PullbackHeader RightToLeft +20" ) << QQuickItemView::RightToLeft |
5963 | << QQuickListView::PullBackHeader |
5964 | << +20 << 0 |
5965 | << -240 << 0; |
5966 | |
5967 | QTest::newRow(dataTag: "PullbackHeader RightToLeft +65-10" ) << QQuickItemView::RightToLeft |
5968 | << QQuickListView::PullBackHeader |
5969 | << +65 << -10 |
5970 | << -260 << -20; |
5971 | |
5972 | QTest::newRow(dataTag: "PullbackHeader RightToLeft +65-20" ) << QQuickItemView::RightToLeft |
5973 | << QQuickListView::PullBackHeader |
5974 | << +65 << -20 |
5975 | << -250 << -40; |
5976 | |
5977 | // Should move header even if contentX doesn't move (it's aligned with top) |
5978 | QTest::newRow(dataTag: "PullbackHeader RightToLeft +55-5" ) << QQuickItemView::RightToLeft |
5979 | << QQuickListView::PullBackHeader |
5980 | << 55 << -5 |
5981 | << -260 << -20; |
5982 | |
5983 | // Should move header even if contentX doesn't move (it's aligned with header) |
5984 | QTest::newRow(dataTag: "PullbackHeader RightToLeft +76-16" ) << QQuickItemView::RightToLeft |
5985 | << QQuickListView::PullBackHeader |
5986 | << 76 << -16 |
5987 | << -270 << -60; |
5988 | |
5989 | } |
5990 | |
5991 | void tst_QQuickListView::snapOneItemResize_QTBUG_43555() |
5992 | { |
5993 | QScopedPointer<QQuickView> window(createView()); |
5994 | window->resize(newSize: QSize(100, 320)); |
5995 | window->setResizeMode(QQuickView::SizeRootObjectToView); |
5996 | QQuickViewTestUtil::moveMouseAway(window: window.data()); |
5997 | |
5998 | window->setSource(testFileUrl(fileName: "snapOneItemResize.qml" )); |
5999 | window->show(); |
6000 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
6001 | |
6002 | QQuickListView *listview = qobject_cast<QQuickListView*>(object: window->rootObject()); |
6003 | QTRY_VERIFY(listview != nullptr); |
6004 | |
6005 | QSignalSpy currentIndexSpy(listview, SIGNAL(currentIndexChanged())); |
6006 | |
6007 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
6008 | QTRY_COMPARE(listview->currentIndex(), 5); |
6009 | currentIndexSpy.clear(); |
6010 | |
6011 | window->resize(newSize: QSize(400, 320)); |
6012 | |
6013 | QTRY_COMPARE(int(listview->width()), 400); |
6014 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
6015 | |
6016 | QTRY_COMPARE(listview->currentIndex(), 5); |
6017 | QCOMPARE(currentIndexSpy.count(), 0); |
6018 | } |
6019 | |
6020 | void tst_QQuickListView::qAbstractItemModel_package_items() |
6021 | { |
6022 | items<QaimModel>(source: testFileUrl(fileName: "listviewtest-package.qml" )); |
6023 | } |
6024 | |
6025 | void tst_QQuickListView::qAbstractItemModel_items() |
6026 | { |
6027 | items<QaimModel>(source: testFileUrl(fileName: "listviewtest.qml" )); |
6028 | } |
6029 | |
6030 | void tst_QQuickListView::qAbstractItemModel_package_changed() |
6031 | { |
6032 | changed<QaimModel>(source: testFileUrl(fileName: "listviewtest-package.qml" )); |
6033 | } |
6034 | |
6035 | void tst_QQuickListView::qAbstractItemModel_changed() |
6036 | { |
6037 | changed<QaimModel>(source: testFileUrl(fileName: "listviewtest.qml" )); |
6038 | } |
6039 | |
6040 | void tst_QQuickListView::qAbstractItemModel_package_inserted() |
6041 | { |
6042 | inserted<QaimModel>(source: testFileUrl(fileName: "listviewtest-package.qml" )); |
6043 | } |
6044 | |
6045 | void tst_QQuickListView::qAbstractItemModel_inserted() |
6046 | { |
6047 | inserted<QaimModel>(source: testFileUrl(fileName: "listviewtest.qml" )); |
6048 | } |
6049 | |
6050 | void tst_QQuickListView::qAbstractItemModel_inserted_more() |
6051 | { |
6052 | inserted_more<QaimModel>(); |
6053 | } |
6054 | |
6055 | void tst_QQuickListView::qAbstractItemModel_inserted_more_data() |
6056 | { |
6057 | inserted_more_data(); |
6058 | } |
6059 | |
6060 | void tst_QQuickListView::qAbstractItemModel_inserted_more_bottomToTop() |
6061 | { |
6062 | inserted_more<QaimModel>(verticalLayoutDirection: QQuickItemView::BottomToTop); |
6063 | } |
6064 | |
6065 | void tst_QQuickListView::qAbstractItemModel_inserted_more_bottomToTop_data() |
6066 | { |
6067 | inserted_more_data(); |
6068 | } |
6069 | |
6070 | void tst_QQuickListView::qAbstractItemModel_package_removed() |
6071 | { |
6072 | removed<QaimModel>(source: testFileUrl(fileName: "listviewtest-package.qml" ), false); |
6073 | removed<QaimModel>(source: testFileUrl(fileName: "listviewtest-package.qml" ), true); |
6074 | } |
6075 | |
6076 | void tst_QQuickListView::qAbstractItemModel_removed() |
6077 | { |
6078 | removed<QaimModel>(source: testFileUrl(fileName: "listviewtest.qml" ), false); |
6079 | removed<QaimModel>(source: testFileUrl(fileName: "listviewtest.qml" ), true); |
6080 | } |
6081 | |
6082 | void tst_QQuickListView::qAbstractItemModel_removed_more() |
6083 | { |
6084 | removed_more<QaimModel>(source: testFileUrl(fileName: "listviewtest.qml" )); |
6085 | } |
6086 | |
6087 | void tst_QQuickListView::qAbstractItemModel_removed_more_data() |
6088 | { |
6089 | removed_more_data(); |
6090 | } |
6091 | |
6092 | void tst_QQuickListView::qAbstractItemModel_removed_more_bottomToTop() |
6093 | { |
6094 | removed_more<QaimModel>(source: testFileUrl(fileName: "listviewtest.qml" ), verticalLayoutDirection: QQuickItemView::BottomToTop); |
6095 | } |
6096 | |
6097 | void tst_QQuickListView::qAbstractItemModel_removed_more_bottomToTop_data() |
6098 | { |
6099 | removed_more_data(); |
6100 | } |
6101 | |
6102 | void tst_QQuickListView::qAbstractItemModel_package_moved() |
6103 | { |
6104 | moved<QaimModel>(source: testFileUrl(fileName: "listviewtest-package.qml" )); |
6105 | } |
6106 | |
6107 | void tst_QQuickListView::qAbstractItemModel_package_moved_data() |
6108 | { |
6109 | moved_data(); |
6110 | } |
6111 | |
6112 | void tst_QQuickListView::qAbstractItemModel_moved() |
6113 | { |
6114 | moved<QaimModel>(source: testFileUrl(fileName: "listviewtest.qml" )); |
6115 | } |
6116 | |
6117 | void tst_QQuickListView::qAbstractItemModel_moved_data() |
6118 | { |
6119 | moved_data(); |
6120 | } |
6121 | |
6122 | void tst_QQuickListView::qAbstractItemModel_moved_bottomToTop() |
6123 | { |
6124 | moved<QaimModel>(source: testFileUrl(fileName: "listviewtest-package.qml" ), verticalLayoutDirection: QQuickItemView::BottomToTop); |
6125 | } |
6126 | |
6127 | void tst_QQuickListView::qAbstractItemModel_moved_bottomToTop_data() |
6128 | { |
6129 | moved_data(); |
6130 | } |
6131 | |
6132 | void tst_QQuickListView::qAbstractItemModel_package_clear() |
6133 | { |
6134 | clear<QaimModel>(source: testFileUrl(fileName: "listviewtest-package.qml" )); |
6135 | } |
6136 | |
6137 | void tst_QQuickListView::qAbstractItemModel_clear() |
6138 | { |
6139 | clear<QaimModel>(source: testFileUrl(fileName: "listviewtest.qml" )); |
6140 | } |
6141 | |
6142 | void tst_QQuickListView::qAbstractItemModel_clear_bottomToTop() |
6143 | { |
6144 | clear<QaimModel>(source: testFileUrl(fileName: "listviewtest.qml" ), verticalLayoutDirection: QQuickItemView::BottomToTop); |
6145 | } |
6146 | |
6147 | void tst_QQuickListView::qAbstractItemModel_package_sections() |
6148 | { |
6149 | sections<QaimModel>(source: testFileUrl(fileName: "listview-sections-package.qml" )); |
6150 | } |
6151 | |
6152 | void tst_QQuickListView::qAbstractItemModel_sections() |
6153 | { |
6154 | sections<QaimModel>(source: testFileUrl(fileName: "listview-sections.qml" )); |
6155 | } |
6156 | |
6157 | void tst_QQuickListView::creationContext() |
6158 | { |
6159 | QQuickView window; |
6160 | window.setGeometry(posx: 0,posy: 0,w: 240,h: 320); |
6161 | window.setSource(testFileUrl(fileName: "creationContext.qml" )); |
6162 | qApp->processEvents(); |
6163 | |
6164 | QQuickItem *rootItem = qobject_cast<QQuickItem *>(object: window.rootObject()); |
6165 | QVERIFY(rootItem); |
6166 | QVERIFY(rootItem->property("count" ).toInt() > 0); |
6167 | |
6168 | QQuickItem *item = findItem<QQuickItem>(parent: rootItem, objectName: "listItem" ); |
6169 | QVERIFY(item); |
6170 | QCOMPARE(item->property("text" ).toString(), QString("Hello!" )); |
6171 | item = rootItem->findChild<QQuickItem *>(aName: "header" ); |
6172 | QVERIFY(item); |
6173 | QCOMPARE(item->property("text" ).toString(), QString("Hello!" )); |
6174 | item = rootItem->findChild<QQuickItem *>(aName: "footer" ); |
6175 | QVERIFY(item); |
6176 | QCOMPARE(item->property("text" ).toString(), QString("Hello!" )); |
6177 | item = rootItem->findChild<QQuickItem *>(aName: "section" ); |
6178 | QVERIFY(item); |
6179 | QCOMPARE(item->property("text" ).toString(), QString("Hello!" )); |
6180 | } |
6181 | |
6182 | void tst_QQuickListView::QTBUG_21742() |
6183 | { |
6184 | QQuickView window; |
6185 | window.setGeometry(posx: 0,posy: 0,w: 200,h: 200); |
6186 | window.setSource(testFileUrl(fileName: "qtbug-21742.qml" )); |
6187 | qApp->processEvents(); |
6188 | |
6189 | QQuickItem *rootItem = qobject_cast<QQuickItem *>(object: window.rootObject()); |
6190 | QVERIFY(rootItem); |
6191 | QCOMPARE(rootItem->property("count" ).toInt(), 1); |
6192 | } |
6193 | |
6194 | void tst_QQuickListView::asynchronous() |
6195 | { |
6196 | QScopedPointer<QQuickView> window(createView()); |
6197 | window->show(); |
6198 | QQmlIncubationController controller; |
6199 | window->engine()->setIncubationController(&controller); |
6200 | |
6201 | window->setSource(testFileUrl(fileName: "asyncloader.qml" )); |
6202 | |
6203 | QQuickItem *rootObject = qobject_cast<QQuickItem*>(object: window->rootObject()); |
6204 | QVERIFY(rootObject); |
6205 | |
6206 | QQuickListView *listview = nullptr; |
6207 | while (!listview) { |
6208 | bool b = false; |
6209 | controller.incubateWhile(flag: &b); |
6210 | listview = rootObject->findChild<QQuickListView*>(aName: "view" ); |
6211 | } |
6212 | |
6213 | // items will be created one at a time |
6214 | for (int i = 0; i < 8; ++i) { |
6215 | QVERIFY(findItem<QQuickItem>(listview, "wrapper" , i) == nullptr); |
6216 | QQuickItem *item = nullptr; |
6217 | while (!item) { |
6218 | bool b = false; |
6219 | controller.incubateWhile(flag: &b); |
6220 | item = findItem<QQuickItem>(parent: listview, objectName: "wrapper" , index: i); |
6221 | } |
6222 | } |
6223 | |
6224 | { |
6225 | bool b = true; |
6226 | controller.incubateWhile(flag: &b); |
6227 | } |
6228 | |
6229 | // verify positioning |
6230 | QQuickItem *contentItem = listview->contentItem(); |
6231 | for (int i = 0; i < 8; ++i) { |
6232 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
6233 | QTRY_COMPARE(item->y(), i*50.0); |
6234 | } |
6235 | } |
6236 | |
6237 | void tst_QQuickListView::snapOneItem_data() |
6238 | { |
6239 | QTest::addColumn<QQuickListView::Orientation>(name: "orientation" ); |
6240 | QTest::addColumn<Qt::LayoutDirection>(name: "layoutDirection" ); |
6241 | QTest::addColumn<QQuickItemView::VerticalLayoutDirection>(name: "verticalLayoutDirection" ); |
6242 | QTest::addColumn<int>(name: "highlightRangeMode" ); |
6243 | QTest::addColumn<QPoint>(name: "flickStart" ); |
6244 | QTest::addColumn<QPoint>(name: "flickEnd" ); |
6245 | QTest::addColumn<qreal>(name: "snapAlignment" ); |
6246 | QTest::addColumn<qreal>(name: "endExtent" ); |
6247 | QTest::addColumn<qreal>(name: "startExtent" ); |
6248 | QTest::addColumn<qreal>(name: "flickSlowdown" ); |
6249 | |
6250 | QTest::newRow(dataTag: "vertical, top to bottom" ) |
6251 | << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom << int(QQuickItemView::NoHighlightRange) |
6252 | << QPoint(20, 200) << QPoint(20, 20) << 180.0 << 560.0 << 0.0 << 1.0; |
6253 | |
6254 | QTest::newRow(dataTag: "vertical, bottom to top" ) |
6255 | << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::BottomToTop << int(QQuickItemView::NoHighlightRange) |
6256 | << QPoint(20, 20) << QPoint(20, 200) << -420.0 << -560.0 - 240.0 << -240.0 << 1.0; |
6257 | |
6258 | QTest::newRow(dataTag: "horizontal, left to right" ) |
6259 | << QQuickListView::Horizontal << Qt::LeftToRight << QQuickItemView::TopToBottom << int(QQuickItemView::NoHighlightRange) |
6260 | << QPoint(200, 20) << QPoint(20, 20) << 180.0 << 560.0 << 0.0 << 1.0; |
6261 | |
6262 | QTest::newRow(dataTag: "horizontal, right to left" ) |
6263 | << QQuickListView::Horizontal << Qt::RightToLeft << QQuickItemView::TopToBottom << int(QQuickItemView::NoHighlightRange) |
6264 | << QPoint(20, 20) << QPoint(200, 20) << -420.0 << -560.0 - 240.0 << -240.0 << 1.0; |
6265 | |
6266 | QTest::newRow(dataTag: "vertical, top to bottom, enforce range" ) |
6267 | << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom << int(QQuickItemView::StrictlyEnforceRange) |
6268 | << QPoint(20, 200) << QPoint(20, 20) << 180.0 << 580.0 << -20.0 << 1.0; |
6269 | |
6270 | QTest::newRow(dataTag: "vertical, bottom to top, enforce range" ) |
6271 | << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::BottomToTop << int(QQuickItemView::StrictlyEnforceRange) |
6272 | << QPoint(20, 20) << QPoint(20, 200) << -420.0 << -580.0 - 240.0 << -220.0 << 1.0; |
6273 | |
6274 | QTest::newRow(dataTag: "horizontal, left to right, enforce range" ) |
6275 | << QQuickListView::Horizontal << Qt::LeftToRight << QQuickItemView::TopToBottom << int(QQuickItemView::StrictlyEnforceRange) |
6276 | << QPoint(200, 20) << QPoint(20, 20) << 180.0 << 580.0 << -20.0 << 1.0; |
6277 | |
6278 | QTest::newRow(dataTag: "horizontal, right to left, enforce range" ) |
6279 | << QQuickListView::Horizontal << Qt::RightToLeft << QQuickItemView::TopToBottom << int(QQuickItemView::StrictlyEnforceRange) |
6280 | << QPoint(20, 20) << QPoint(200, 20) << -420.0 << -580.0 - 240.0 << -220.0 << 1.0; |
6281 | |
6282 | // Using e.g. 120 rather than 95 always went to the next item. |
6283 | // Ensure this further movement has the same behavior |
6284 | QTest::newRow(dataTag: "vertical, top to bottom, no more blindspot" ) |
6285 | << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom << int(QQuickItemView::NoHighlightRange) |
6286 | << QPoint(20, 200) << QPoint(20, 95) << 180.0 << 560.0 << 0.0 << 6.0; |
6287 | |
6288 | // StrictlyEnforceRange should not override valid SnapOneItem decisions |
6289 | QTest::newRow(dataTag: "vertical, top to bottom, no more blindspot, enforce range" ) |
6290 | << QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom << int(QQuickItemView::StrictlyEnforceRange) |
6291 | << QPoint(20, 200) << QPoint(20, 95) << 180.0 << 580.0 << -20.0 << 6.0; |
6292 | } |
6293 | |
6294 | void tst_QQuickListView::snapOneItem() |
6295 | { |
6296 | QFETCH(QQuickListView::Orientation, orientation); |
6297 | QFETCH(Qt::LayoutDirection, layoutDirection); |
6298 | QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection); |
6299 | QFETCH(int, highlightRangeMode); |
6300 | QFETCH(QPoint, flickStart); |
6301 | QFETCH(QPoint, flickEnd); |
6302 | QFETCH(qreal, snapAlignment); |
6303 | QFETCH(qreal, endExtent); |
6304 | QFETCH(qreal, startExtent); |
6305 | QFETCH(qreal, flickSlowdown); |
6306 | |
6307 | qreal flickDuration = 180 * flickSlowdown; |
6308 | |
6309 | QQuickView *window = getView(); |
6310 | QQuickViewTestUtil::moveMouseAway(window); |
6311 | |
6312 | window->setSource(testFileUrl(fileName: "snapOneItem.qml" )); |
6313 | window->show(); |
6314 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
6315 | |
6316 | |
6317 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
6318 | QTRY_VERIFY(listview != nullptr); |
6319 | |
6320 | listview->setOrientation(orientation); |
6321 | listview->setLayoutDirection(layoutDirection); |
6322 | listview->setVerticalLayoutDirection(verticalLayoutDirection); |
6323 | listview->setHighlightRangeMode(QQuickItemView::HighlightRangeMode(highlightRangeMode)); |
6324 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
6325 | |
6326 | QQuickItem *contentItem = listview->contentItem(); |
6327 | QTRY_VERIFY(contentItem != nullptr); |
6328 | |
6329 | QSignalSpy currentIndexSpy(listview, SIGNAL(currentIndexChanged())); |
6330 | |
6331 | // confirm that a flick hits the next item boundary |
6332 | flick(window, from: flickStart, to: flickEnd, duration: flickDuration); |
6333 | QTRY_VERIFY(listview->isMoving() == false); // wait until it stops |
6334 | if (orientation == QQuickListView::Vertical) |
6335 | QCOMPARE(listview->contentY(), snapAlignment); |
6336 | else |
6337 | QCOMPARE(listview->contentX(), snapAlignment); |
6338 | |
6339 | if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) { |
6340 | QCOMPARE(listview->currentIndex(), 1); |
6341 | QCOMPARE(currentIndexSpy.count(), 1); |
6342 | } |
6343 | |
6344 | // flick to end |
6345 | do { |
6346 | flick(window, from: flickStart, to: flickEnd, duration: flickDuration); |
6347 | QTRY_VERIFY(listview->isMoving() == false); // wait until it stops |
6348 | } while (orientation == QQuickListView::Vertical |
6349 | ? verticalLayoutDirection == QQuickItemView::TopToBottom ? !listview->isAtYEnd() : !listview->isAtYBeginning() |
6350 | : layoutDirection == Qt::LeftToRight ? !listview->isAtXEnd() : !listview->isAtXBeginning()); |
6351 | |
6352 | if (orientation == QQuickListView::Vertical) |
6353 | QCOMPARE(listview->contentY(), endExtent); |
6354 | else |
6355 | QCOMPARE(listview->contentX(), endExtent); |
6356 | |
6357 | if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) { |
6358 | QCOMPARE(listview->currentIndex(), 3); |
6359 | QCOMPARE(currentIndexSpy.count(), 3); |
6360 | } |
6361 | |
6362 | // flick to start |
6363 | do { |
6364 | flick(window, from: flickEnd, to: flickStart, duration: flickDuration); |
6365 | QTRY_VERIFY(listview->isMoving() == false); // wait until it stops |
6366 | } while (orientation == QQuickListView::Vertical |
6367 | ? verticalLayoutDirection == QQuickItemView::TopToBottom ? !listview->isAtYBeginning() : !listview->isAtYEnd() |
6368 | : layoutDirection == Qt::LeftToRight ? !listview->isAtXBeginning() : !listview->isAtXEnd()); |
6369 | |
6370 | if (orientation == QQuickListView::Vertical) |
6371 | QCOMPARE(listview->contentY(), startExtent); |
6372 | else |
6373 | QCOMPARE(listview->contentX(), startExtent); |
6374 | |
6375 | if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) { |
6376 | QCOMPARE(listview->currentIndex(), 0); |
6377 | QCOMPARE(currentIndexSpy.count(), 6); |
6378 | } |
6379 | |
6380 | releaseView(view: window); |
6381 | } |
6382 | |
6383 | void tst_QQuickListView::snapOneItemCurrentIndexRemoveAnimation() |
6384 | { |
6385 | QScopedPointer<QQuickView> window(createView()); |
6386 | window->setSource(testFileUrl(fileName: "snapOneItemCurrentIndexRemoveAnimation.qml" )); |
6387 | window->show(); |
6388 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
6389 | |
6390 | QQuickListView *listview = qobject_cast<QQuickListView*>(object: window->rootObject()); |
6391 | QTRY_VERIFY(listview != nullptr); |
6392 | |
6393 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
6394 | QTRY_COMPARE(listview->currentIndex(), 0); |
6395 | QSignalSpy currentIndexSpy(listview, SIGNAL(currentIndexChanged())); |
6396 | |
6397 | QMetaObject::invokeMethod(obj: window->rootObject(), member: "removeItemZero" ); |
6398 | QTRY_COMPARE(listview->property("transitionsRun" ).toInt(), 1); |
6399 | |
6400 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
6401 | |
6402 | QCOMPARE(listview->currentIndex(), 0); |
6403 | QCOMPARE(currentIndexSpy.count(), 0); |
6404 | } |
6405 | |
6406 | void tst_QQuickListView::snapOneItemWrongDirection() |
6407 | { |
6408 | QScopedPointer<QQuickView> window(createView()); |
6409 | window->setSource(testFileUrl(fileName: "snapOneItemWrongDirection.qml" )); |
6410 | window->show(); |
6411 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
6412 | |
6413 | QQuickListView *listview = qobject_cast<QQuickListView*>(object: window->rootObject()); |
6414 | QTRY_VERIFY(listview != nullptr); |
6415 | |
6416 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
6417 | QTRY_COMPARE(listview->currentIndex(), 0); |
6418 | |
6419 | listview->flick(xVelocity: 0,yVelocity: 500); |
6420 | QTRY_VERIFY(!listview->isMovingHorizontally()); |
6421 | QCOMPARE(listview->contentX(), qreal(0)); |
6422 | } |
6423 | |
6424 | void tst_QQuickListView::attachedProperties_QTBUG_32836() |
6425 | { |
6426 | QScopedPointer<QQuickView> window(createView()); |
6427 | window->setSource(testFileUrl(fileName: "attachedProperties.qml" )); |
6428 | window->show(); |
6429 | qApp->processEvents(); |
6430 | |
6431 | QQuickListView *listview = qobject_cast<QQuickListView*>(object: window->rootObject()); |
6432 | QVERIFY(listview != nullptr); |
6433 | |
6434 | QQuickItem * = listview->headerItem(); |
6435 | QVERIFY(header); |
6436 | QCOMPARE(header->width(), listview->width()); |
6437 | |
6438 | QQuickItem * = listview->footerItem(); |
6439 | QVERIFY(footer); |
6440 | QCOMPARE(footer->width(), listview->width()); |
6441 | |
6442 | QQuickItem *highlight = listview->highlightItem(); |
6443 | QVERIFY(highlight); |
6444 | QCOMPARE(highlight->width(), listview->width()); |
6445 | |
6446 | QQuickItem *currentItem = listview->currentItem(); |
6447 | QVERIFY(currentItem); |
6448 | QCOMPARE(currentItem->width(), listview->width()); |
6449 | |
6450 | QQuickItem *sectionItem = findItem<QQuickItem>(parent: window->rootObject(), objectName: "sectionItem" ); |
6451 | QVERIFY(sectionItem); |
6452 | QCOMPARE(sectionItem->width(), listview->width()); |
6453 | } |
6454 | |
6455 | void tst_QQuickListView::unrequestedVisibility() |
6456 | { |
6457 | QaimModel model; |
6458 | for (int i = 0; i < 30; i++) |
6459 | model.addItem(name: "Item" + QString::number(i), number: QString::number(i)); |
6460 | |
6461 | QScopedPointer<QQuickView> window(new QQuickView(nullptr)); |
6462 | window->setGeometry(posx: 0,posy: 0,w: 240,h: 320); |
6463 | |
6464 | QQmlContext *ctxt = window->rootContext(); |
6465 | ctxt->setContextProperty("testModel" , &model); |
6466 | ctxt->setContextProperty("testWrap" , QVariant(false)); |
6467 | |
6468 | window->setSource(testFileUrl(fileName: "unrequestedItems.qml" )); |
6469 | window->show(); |
6470 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
6471 | |
6472 | |
6473 | QQuickListView *leftview; |
6474 | QTRY_VERIFY((leftview = findItem<QQuickListView>(window->rootObject(), "leftList" ))); |
6475 | |
6476 | QQuickListView *rightview; |
6477 | QTRY_VERIFY((rightview = findItem<QQuickListView>(window->rootObject(), "rightList" ))); |
6478 | |
6479 | QQuickItem *leftContent = leftview->contentItem(); |
6480 | QTRY_VERIFY((leftContent = leftview->contentItem())); |
6481 | |
6482 | QQuickItem *rightContent; |
6483 | QTRY_VERIFY((rightContent = rightview->contentItem())); |
6484 | |
6485 | rightview->setCurrentIndex(20); |
6486 | |
6487 | QTRY_COMPARE(leftview->contentY(), 0.0); |
6488 | QTRY_COMPARE(rightview->contentY(), 100.0); |
6489 | |
6490 | const QString wrapperObjectName = QStringLiteral("wrapper" ); |
6491 | QQuickItem *item = findItem<QQuickItem>(parent: leftContent, objectName: wrapperObjectName, index: 1); |
6492 | QVERIFY(item); |
6493 | QCOMPARE(delegateVisible(item), true); |
6494 | item = findItem<QQuickItem>(parent: rightContent, objectName: wrapperObjectName, index: 1); |
6495 | QVERIFY(item); |
6496 | QCOMPARE(delegateVisible(item), false); |
6497 | |
6498 | item = findItem<QQuickItem>(parent: leftContent, objectName: wrapperObjectName, index: 19); |
6499 | QVERIFY(item); |
6500 | QCOMPARE(delegateVisible(item), false); |
6501 | item = findItem<QQuickItem>(parent: rightContent, objectName: wrapperObjectName, index: 19); |
6502 | QVERIFY(item); |
6503 | QCOMPARE(delegateVisible(item), true); |
6504 | |
6505 | item = findItem<QQuickItem>(parent: leftContent, objectName: wrapperObjectName, index: 16); |
6506 | QVERIFY(item); |
6507 | QCOMPARE(delegateVisible(item), true); |
6508 | item = findItem<QQuickItem>(parent: leftContent, objectName: wrapperObjectName, index: 17); |
6509 | QVERIFY(item); |
6510 | QCOMPARE(delegateVisible(item), false); |
6511 | item = findItem<QQuickItem>(parent: rightContent, objectName: wrapperObjectName, index: 3); |
6512 | QVERIFY(item); |
6513 | QCOMPARE(delegateVisible(item), false); |
6514 | item = findItem<QQuickItem>(parent: rightContent, objectName: wrapperObjectName, index: 4); |
6515 | QVERIFY(item); |
6516 | QCOMPARE(delegateVisible(item), true); |
6517 | |
6518 | rightview->setCurrentIndex(0); |
6519 | |
6520 | QTRY_COMPARE(leftview->contentY(), 0.0); |
6521 | QTRY_COMPARE(rightview->contentY(), 0.0); |
6522 | |
6523 | item = findItem<QQuickItem>(parent: leftContent, objectName: wrapperObjectName, index: 1); |
6524 | QVERIFY(item); |
6525 | QCOMPARE(delegateVisible(item), true); |
6526 | item = findItem<QQuickItem>(parent: rightContent, objectName: wrapperObjectName, index: 1); |
6527 | QVERIFY(item); |
6528 | QTRY_COMPARE(delegateVisible(item), true); |
6529 | |
6530 | QVERIFY(!findItem<QQuickItem>(leftContent, wrapperObjectName, 19)); |
6531 | QVERIFY(!findItem<QQuickItem>(rightContent, wrapperObjectName, 19)); |
6532 | |
6533 | leftview->setCurrentIndex(20); |
6534 | |
6535 | QTRY_COMPARE(leftview->contentY(), 100.0); |
6536 | QTRY_COMPARE(rightview->contentY(), 0.0); |
6537 | |
6538 | item = findItem<QQuickItem>(parent: leftContent, objectName: wrapperObjectName, index: 1); |
6539 | QVERIFY(item); |
6540 | QTRY_COMPARE(delegateVisible(item), false); |
6541 | item = findItem<QQuickItem>(parent: rightContent, objectName: wrapperObjectName, index: 1); |
6542 | QVERIFY(item); |
6543 | QCOMPARE(delegateVisible(item), true); |
6544 | |
6545 | item = findItem<QQuickItem>(parent: leftContent, objectName: wrapperObjectName, index: 19); |
6546 | QVERIFY(item); |
6547 | QCOMPARE(delegateVisible(item), true); |
6548 | item = findItem<QQuickItem>(parent: rightContent, objectName: wrapperObjectName, index: 19); |
6549 | QVERIFY(item); |
6550 | QCOMPARE(delegateVisible(item), false); |
6551 | |
6552 | item = findItem<QQuickItem>(parent: leftContent, objectName: wrapperObjectName, index: 3); |
6553 | QVERIFY(item); |
6554 | QCOMPARE(delegateVisible(item), false); |
6555 | item = findItem<QQuickItem>(parent: leftContent, objectName: wrapperObjectName, index: 4); |
6556 | QVERIFY(item); |
6557 | QCOMPARE(delegateVisible(item), true); |
6558 | item = findItem<QQuickItem>(parent: rightContent, objectName: wrapperObjectName, index: 16); |
6559 | QVERIFY(item); |
6560 | QCOMPARE(delegateVisible(item), true); |
6561 | item = findItem<QQuickItem>(parent: rightContent, objectName: wrapperObjectName, index: 17); |
6562 | QVERIFY(item); |
6563 | QCOMPARE(delegateVisible(item), false); |
6564 | |
6565 | model.moveItems(from: 19, to: 1, count: 1); |
6566 | QVERIFY(QQuickTest::qWaitForItemPolished(leftview)); |
6567 | |
6568 | QTRY_VERIFY((item = findItem<QQuickItem>(leftContent, wrapperObjectName, 1))); |
6569 | QCOMPARE(delegateVisible(item), false); |
6570 | item = findItem<QQuickItem>(parent: rightContent, objectName: wrapperObjectName, index: 1); |
6571 | QVERIFY(item); |
6572 | QCOMPARE(delegateVisible(item), true); |
6573 | |
6574 | item = findItem<QQuickItem>(parent: leftContent, objectName: wrapperObjectName, index: 19); |
6575 | QVERIFY(item); |
6576 | QCOMPARE(delegateVisible(item), true); |
6577 | item = findItem<QQuickItem>(parent: rightContent, objectName: wrapperObjectName, index: 19); |
6578 | QVERIFY(item); |
6579 | QCOMPARE(delegateVisible(item), false); |
6580 | |
6581 | item = findItem<QQuickItem>(parent: leftContent, objectName: wrapperObjectName, index: 4); |
6582 | QVERIFY(item); |
6583 | QCOMPARE(delegateVisible(item), false); |
6584 | item = findItem<QQuickItem>(parent: leftContent, objectName: wrapperObjectName, index: 5); |
6585 | QVERIFY(item); |
6586 | QCOMPARE(delegateVisible(item), true); |
6587 | item = findItem<QQuickItem>(parent: rightContent, objectName: wrapperObjectName, index: 16); |
6588 | QVERIFY(item); |
6589 | QCOMPARE(delegateVisible(item), true); |
6590 | item = findItem<QQuickItem>(parent: rightContent, objectName: wrapperObjectName, index: 17); |
6591 | QVERIFY(item); |
6592 | QCOMPARE(delegateVisible(item), false); |
6593 | |
6594 | model.moveItems(from: 3, to: 4, count: 1); |
6595 | QVERIFY(QQuickTest::qWaitForItemPolished(leftview)); |
6596 | |
6597 | item = findItem<QQuickItem>(parent: leftContent, objectName: wrapperObjectName, index: 4); |
6598 | QVERIFY(item); |
6599 | QCOMPARE(delegateVisible(item), false); |
6600 | item = findItem<QQuickItem>(parent: leftContent, objectName: wrapperObjectName, index: 5); |
6601 | QVERIFY(item); |
6602 | QCOMPARE(delegateVisible(item), true); |
6603 | item = findItem<QQuickItem>(parent: rightContent, objectName: wrapperObjectName, index: 16); |
6604 | QVERIFY(item); |
6605 | QCOMPARE(delegateVisible(item), true); |
6606 | item = findItem<QQuickItem>(parent: rightContent, objectName: wrapperObjectName, index: 17); |
6607 | QVERIFY(item); |
6608 | QCOMPARE(delegateVisible(item), false); |
6609 | |
6610 | model.moveItems(from: 4, to: 3, count: 1); |
6611 | QVERIFY(QQuickTest::qWaitForItemPolished(leftview)); |
6612 | |
6613 | item = findItem<QQuickItem>(parent: leftContent, objectName: wrapperObjectName, index: 4); |
6614 | QVERIFY(item); |
6615 | QCOMPARE(delegateVisible(item), false); |
6616 | item = findItem<QQuickItem>(parent: leftContent, objectName: wrapperObjectName, index: 5); |
6617 | QVERIFY(item); |
6618 | QCOMPARE(delegateVisible(item), true); |
6619 | item = findItem<QQuickItem>(parent: rightContent, objectName: wrapperObjectName, index: 16); |
6620 | QVERIFY(item); |
6621 | QCOMPARE(delegateVisible(item), true); |
6622 | item = findItem<QQuickItem>(parent: rightContent, objectName: wrapperObjectName, index: 17); |
6623 | QVERIFY(item); |
6624 | QCOMPARE(delegateVisible(item), false); |
6625 | |
6626 | model.moveItems(from: 16, to: 17, count: 1); |
6627 | QVERIFY(QQuickTest::qWaitForItemPolished(leftview)); |
6628 | |
6629 | item = findItem<QQuickItem>(parent: leftContent, objectName: wrapperObjectName, index: 4); |
6630 | QVERIFY(item); |
6631 | QCOMPARE(delegateVisible(item), false); |
6632 | item = findItem<QQuickItem>(parent: leftContent, objectName: wrapperObjectName, index: 5); |
6633 | QVERIFY(item); |
6634 | QCOMPARE(delegateVisible(item), true); |
6635 | item = findItem<QQuickItem>(parent: rightContent, objectName: wrapperObjectName, index: 16); |
6636 | QVERIFY(item); |
6637 | QCOMPARE(delegateVisible(item), true); |
6638 | item = findItem<QQuickItem>(parent: rightContent, objectName: wrapperObjectName, index: 17); |
6639 | QVERIFY(item); |
6640 | QCOMPARE(delegateVisible(item), false); |
6641 | |
6642 | model.moveItems(from: 17, to: 16, count: 1); |
6643 | QVERIFY(QQuickTest::qWaitForItemPolished(leftview)); |
6644 | |
6645 | item = findItem<QQuickItem>(parent: leftContent, objectName: wrapperObjectName, index: 4); |
6646 | QVERIFY(item); |
6647 | QCOMPARE(delegateVisible(item), false); |
6648 | item = findItem<QQuickItem>(parent: leftContent, objectName: wrapperObjectName, index: 5); |
6649 | QVERIFY(item); |
6650 | QCOMPARE(delegateVisible(item), true); |
6651 | item = findItem<QQuickItem>(parent: rightContent, objectName: wrapperObjectName, index: 16); |
6652 | QVERIFY(item); |
6653 | QCOMPARE(delegateVisible(item), true); |
6654 | item = findItem<QQuickItem>(parent: rightContent, objectName: wrapperObjectName, index: 17); |
6655 | QVERIFY(item); |
6656 | QCOMPARE(delegateVisible(item), false); |
6657 | } |
6658 | |
6659 | void tst_QQuickListView::populateTransitions() |
6660 | { |
6661 | QFETCH(bool, staticallyPopulate); |
6662 | QFETCH(bool, dynamicallyPopulate); |
6663 | QFETCH(bool, usePopulateTransition); |
6664 | |
6665 | QPointF transitionFrom(-50, -50); |
6666 | QPointF transitionVia(100, 100); |
6667 | QaimModel model_transitionFrom; |
6668 | QaimModel model_transitionVia; |
6669 | |
6670 | QaimModel model; |
6671 | if (staticallyPopulate) { |
6672 | for (int i = 0; i < 30; i++) |
6673 | model.addItem(name: "item" + QString::number(i), number: "" ); |
6674 | } |
6675 | |
6676 | QQuickView *window = getView(); |
6677 | QScopedPointer<TestObject> testObject(new TestObject(window->rootContext())); |
6678 | window->rootContext()->setContextProperty("testModel" , &model); |
6679 | window->rootContext()->setContextProperty("testObject" , testObject.data()); |
6680 | window->rootContext()->setContextProperty("usePopulateTransition" , usePopulateTransition); |
6681 | window->rootContext()->setContextProperty("dynamicallyPopulate" , dynamicallyPopulate); |
6682 | window->rootContext()->setContextProperty("transitionFrom" , transitionFrom); |
6683 | window->rootContext()->setContextProperty("transitionVia" , transitionVia); |
6684 | window->rootContext()->setContextProperty("model_transitionFrom" , &model_transitionFrom); |
6685 | window->rootContext()->setContextProperty("model_transitionVia" , &model_transitionVia); |
6686 | window->setSource(testFileUrl(fileName: "populateTransitions.qml" )); |
6687 | window->show(); |
6688 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
6689 | |
6690 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
6691 | QVERIFY(listview); |
6692 | QQuickItem *contentItem = listview->contentItem(); |
6693 | QVERIFY(contentItem); |
6694 | |
6695 | if (staticallyPopulate && usePopulateTransition) { |
6696 | QTRY_COMPARE(listview->property("countPopulateTransitions" ).toInt(), 16); |
6697 | QTRY_COMPARE(listview->property("countAddTransitions" ).toInt(), 0); |
6698 | } else if (dynamicallyPopulate) { |
6699 | QTRY_COMPARE(listview->property("countPopulateTransitions" ).toInt(), 0); |
6700 | QTRY_COMPARE(listview->property("countAddTransitions" ).toInt(), 16); |
6701 | } else { |
6702 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
6703 | QCOMPARE(listview->property("countPopulateTransitions" ).toInt(), 0); |
6704 | QCOMPARE(listview->property("countAddTransitions" ).toInt(), 0); |
6705 | } |
6706 | |
6707 | int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ).count(); |
6708 | for (int i=0; i < model.count() && i < itemCount; ++i) { |
6709 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
6710 | QVERIFY2(item, qPrintable(QString("Item %1 not found" ).arg(i))); |
6711 | QTRY_COMPARE(item->x(), 0.0); |
6712 | QTRY_COMPARE(item->y(), i*20.0); |
6713 | QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName" , index: i); |
6714 | QVERIFY(name != nullptr); |
6715 | QTRY_COMPARE(name->text(), model.name(i)); |
6716 | } |
6717 | |
6718 | listview->setProperty(name: "countPopulateTransitions" , value: 0); |
6719 | listview->setProperty(name: "countAddTransitions" , value: 0); |
6720 | |
6721 | // add an item and check this is done with add transition, not populate |
6722 | model.insertItem(index: 0, name: "another item" , number: "" ); |
6723 | QTRY_COMPARE(listview->property("countAddTransitions" ).toInt(), 1); |
6724 | QTRY_COMPARE(listview->property("countPopulateTransitions" ).toInt(), 0); |
6725 | |
6726 | // clear the model |
6727 | window->rootContext()->setContextProperty("testModel" , QVariant()); |
6728 | listview->forceLayout(); |
6729 | QTRY_COMPARE(listview->count(), 0); |
6730 | QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper" ).count(), 0); |
6731 | listview->setProperty(name: "countPopulateTransitions" , value: 0); |
6732 | listview->setProperty(name: "countAddTransitions" , value: 0); |
6733 | |
6734 | // set to a valid model and check populate transition is run a second time |
6735 | model.clear(); |
6736 | for (int i = 0; i < 30; i++) |
6737 | model.addItem(name: "item" + QString::number(i), number: "" ); |
6738 | window->rootContext()->setContextProperty("testModel" , &model); |
6739 | QTRY_COMPARE(listview->property("countPopulateTransitions" ).toInt(), usePopulateTransition ? 16 : 0); |
6740 | QTRY_COMPARE(listview->property("countAddTransitions" ).toInt(), 0); |
6741 | |
6742 | itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ).count(); |
6743 | for (int i=0; i < model.count() && i < itemCount; ++i) { |
6744 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
6745 | QVERIFY2(item, qPrintable(QString("Item %1 not found" ).arg(i))); |
6746 | QTRY_COMPARE(item->x(), 0.0); |
6747 | QTRY_COMPARE(item->y(), i*20.0); |
6748 | QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName" , index: i); |
6749 | QVERIFY(name != nullptr); |
6750 | QTRY_COMPARE(name->text(), model.name(i)); |
6751 | } |
6752 | |
6753 | // reset model and check populate transition is run again |
6754 | listview->setProperty(name: "countPopulateTransitions" , value: 0); |
6755 | listview->setProperty(name: "countAddTransitions" , value: 0); |
6756 | model.reset(); |
6757 | QTRY_COMPARE(listview->property("countPopulateTransitions" ).toInt(), usePopulateTransition ? 16 : 0); |
6758 | QTRY_COMPARE(listview->property("countAddTransitions" ).toInt(), 0); |
6759 | |
6760 | itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ).count(); |
6761 | for (int i=0; i < model.count() && i < itemCount; ++i) { |
6762 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
6763 | QVERIFY2(item, qPrintable(QString("Item %1 not found" ).arg(i))); |
6764 | QTRY_COMPARE(item->x(), 0.0); |
6765 | QTRY_COMPARE(item->y(), i*20.0); |
6766 | QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName" , index: i); |
6767 | QVERIFY(name != nullptr); |
6768 | QTRY_COMPARE(name->text(), model.name(i)); |
6769 | } |
6770 | |
6771 | releaseView(view: window); |
6772 | } |
6773 | |
6774 | void tst_QQuickListView::populateTransitions_data() |
6775 | { |
6776 | QTest::addColumn<bool>(name: "staticallyPopulate" ); |
6777 | QTest::addColumn<bool>(name: "dynamicallyPopulate" ); |
6778 | QTest::addColumn<bool>(name: "usePopulateTransition" ); |
6779 | |
6780 | QTest::newRow(dataTag: "static" ) << true << false << true; |
6781 | QTest::newRow(dataTag: "static, no populate" ) << true << false << false; |
6782 | |
6783 | QTest::newRow(dataTag: "dynamic" ) << false << true << true; |
6784 | QTest::newRow(dataTag: "dynamic, no populate" ) << false << true << false; |
6785 | |
6786 | QTest::newRow(dataTag: "empty to start with" ) << false << false << true; |
6787 | QTest::newRow(dataTag: "empty to start with, no populate" ) << false << false << false; |
6788 | } |
6789 | |
6790 | |
6791 | /* |
6792 | * Tests if the first visible item is not repositioned if the same item |
6793 | * resized + changes position during a transition. The test does not test the |
6794 | * actual position while it is transitioning (since its timing sensitive), but |
6795 | * rather tests if the transition has reached its target state properly. |
6796 | **/ |
6797 | void tst_QQuickListView::sizeTransitions() |
6798 | { |
6799 | QFETCH(bool, topToBottom); |
6800 | QQuickView *window = getView(); |
6801 | QQmlContext *ctxt = window->rootContext(); |
6802 | QaimModel model; |
6803 | ctxt->setContextProperty("testModel" , &model); |
6804 | ctxt->setContextProperty("topToBottom" , topToBottom); |
6805 | QScopedPointer<TestObject> testObject(new TestObject); |
6806 | ctxt->setContextProperty("testObject" , &model); |
6807 | window->setSource(testFileUrl(fileName: "sizeTransitions.qml" )); |
6808 | window->show(); |
6809 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
6810 | |
6811 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
6812 | QTRY_VERIFY(listview != nullptr); |
6813 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
6814 | |
6815 | // the following will start the transition |
6816 | model.addItem(name: QLatin1String("Test" ), number: "" ); |
6817 | |
6818 | // This ensures early failure in case of failure (in which case |
6819 | // transitionFinished == true and scriptActionExecuted == false) |
6820 | QTRY_COMPARE(listview->property("scriptActionExecuted" ).toBool() || |
6821 | listview->property("transitionFinished" ).toBool(), true); |
6822 | QCOMPARE(listview->property("scriptActionExecuted" ).toBool(), true); |
6823 | QCOMPARE(listview->property("transitionFinished" ).toBool(), true); |
6824 | |
6825 | releaseView(view: window); |
6826 | } |
6827 | |
6828 | void tst_QQuickListView::sizeTransitions_data() |
6829 | { |
6830 | QTest::addColumn<bool>(name: "topToBottom" ); |
6831 | |
6832 | QTest::newRow(dataTag: "TopToBottom" ) |
6833 | << true; |
6834 | |
6835 | QTest::newRow(dataTag: "LeftToRight" ) |
6836 | << false; |
6837 | } |
6838 | |
6839 | void tst_QQuickListView::addTransitions() |
6840 | { |
6841 | QFETCH(int, initialItemCount); |
6842 | QFETCH(bool, shouldAnimateTargets); |
6843 | QFETCH(qreal, contentY); |
6844 | QFETCH(int, insertionIndex); |
6845 | QFETCH(int, insertionCount); |
6846 | QFETCH(ListRange, expectedDisplacedIndexes); |
6847 | |
6848 | // added items should start here |
6849 | QPointF targetItems_transitionFrom(-50, -50); |
6850 | |
6851 | // displaced items should pass through this point |
6852 | QPointF displacedItems_transitionVia(100, 100); |
6853 | |
6854 | QaimModel model; |
6855 | for (int i = 0; i < initialItemCount; i++) |
6856 | model.addItem(name: "Original item" + QString::number(i), number: "" ); |
6857 | QaimModel model_targetItems_transitionFrom; |
6858 | QaimModel model_displacedItems_transitionVia; |
6859 | |
6860 | QQuickView *window = getView(); |
6861 | QQmlContext *ctxt = window->rootContext(); |
6862 | QScopedPointer<TestObject> testObject(new TestObject); |
6863 | ctxt->setContextProperty("testModel" , &model); |
6864 | ctxt->setContextProperty("model_targetItems_transitionFrom" , &model_targetItems_transitionFrom); |
6865 | ctxt->setContextProperty("model_displacedItems_transitionVia" , &model_displacedItems_transitionVia); |
6866 | ctxt->setContextProperty("targetItems_transitionFrom" , targetItems_transitionFrom); |
6867 | ctxt->setContextProperty("displacedItems_transitionVia" , displacedItems_transitionVia); |
6868 | ctxt->setContextProperty("testObject" , testObject.data()); |
6869 | window->setSource(testFileUrl(fileName: "addTransitions.qml" )); |
6870 | window->show(); |
6871 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
6872 | |
6873 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
6874 | QTRY_VERIFY(listview != nullptr); |
6875 | QQuickItem *contentItem = listview->contentItem(); |
6876 | QVERIFY(contentItem != nullptr); |
6877 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
6878 | |
6879 | if (contentY != 0) { |
6880 | listview->setContentY(contentY); |
6881 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
6882 | } |
6883 | |
6884 | QList<QPair<QString,QString> > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model); |
6885 | |
6886 | // only target items that will become visible should be animated |
6887 | QList<QPair<QString, QString> > newData; |
6888 | QList<QPair<QString, QString> > expectedTargetData; |
6889 | QList<int> targetIndexes; |
6890 | if (shouldAnimateTargets) { |
6891 | for (int i=insertionIndex; i<insertionIndex+insertionCount; i++) { |
6892 | newData << qMakePair(x: QString("New item %1" ).arg(a: i), y: QString("" )); |
6893 | |
6894 | if (i >= contentY / 20 && i < (contentY + listview->height()) / 20) { // only grab visible items |
6895 | expectedTargetData << newData.last(); |
6896 | targetIndexes << i; |
6897 | } |
6898 | } |
6899 | QVERIFY(expectedTargetData.count() > 0); |
6900 | } |
6901 | |
6902 | // start animation |
6903 | if (!newData.isEmpty()) { |
6904 | model.insertItems(index: insertionIndex, items: newData); |
6905 | QTRY_COMPARE(model.count(), listview->count()); |
6906 | listview->forceLayout(); |
6907 | } |
6908 | |
6909 | QList<QQuickItem *> targetItems = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" , indexes: targetIndexes); |
6910 | |
6911 | if (shouldAnimateTargets) { |
6912 | QTRY_COMPARE(listview->property("targetTransitionsDone" ).toInt(), expectedTargetData.count()); |
6913 | QTRY_COMPARE(listview->property("displaceTransitionsDone" ).toInt(), |
6914 | expectedDisplacedIndexes.isValid() ? expectedDisplacedIndexes.count() : 0); |
6915 | |
6916 | // check the target and displaced items were animated |
6917 | model_targetItems_transitionFrom.matchAgainst(other: expectedTargetData, error1: "wasn't animated from target 'from' pos" , error2: "shouldn't have been animated from target 'from' pos" ); |
6918 | model_displacedItems_transitionVia.matchAgainst(other: expectedDisplacedValues, error1: "wasn't animated with displaced anim" , error2: "shouldn't have been animated with displaced anim" ); |
6919 | |
6920 | // check attached properties |
6921 | matchItemsAndIndexes(items: listview->property(name: "targetTrans_items" ).toMap(), model, expectedIndexes: targetIndexes); |
6922 | matchIndexLists(indexLists: listview->property(name: "targetTrans_targetIndexes" ).toList(), expectedIndexes: targetIndexes); |
6923 | matchItemLists(itemLists: listview->property(name: "targetTrans_targetItems" ).toList(), expectedItems: targetItems); |
6924 | if (expectedDisplacedIndexes.isValid()) { |
6925 | // adjust expectedDisplacedIndexes to their final values after the move |
6926 | QList<int> displacedIndexes = adjustIndexesForAddDisplaced(indexes: expectedDisplacedIndexes.indexes, index: insertionIndex, count: insertionCount); |
6927 | matchItemsAndIndexes(items: listview->property(name: "displacedTrans_items" ).toMap(), model, expectedIndexes: displacedIndexes); |
6928 | matchIndexLists(indexLists: listview->property(name: "displacedTrans_targetIndexes" ).toList(), expectedIndexes: targetIndexes); |
6929 | matchItemLists(itemLists: listview->property(name: "displacedTrans_targetItems" ).toList(), expectedItems: targetItems); |
6930 | } |
6931 | |
6932 | } else { |
6933 | QTRY_COMPARE(model_targetItems_transitionFrom.count(), 0); |
6934 | QTRY_COMPARE(model_displacedItems_transitionVia.count(), 0); |
6935 | } |
6936 | |
6937 | QList<QQuickItem*> items = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ); |
6938 | int firstVisibleIndex = -1; |
6939 | int itemCount = items.count(); |
6940 | for (int i=0; i<items.count(); i++) { |
6941 | if (items[i]->y() >= contentY) { |
6942 | QQmlExpression e(qmlContext(items[i]), items[i], "index" ); |
6943 | firstVisibleIndex = e.evaluate().toInt(); |
6944 | break; |
6945 | } |
6946 | } |
6947 | QVERIFY2(firstVisibleIndex >= 0, QByteArray::number(firstVisibleIndex)); |
6948 | |
6949 | // verify all items moved to the correct final positions |
6950 | for (int i=firstVisibleIndex; i < model.count() && i < itemCount; ++i) { |
6951 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
6952 | QVERIFY2(item, qPrintable(QString("Item %1 not found" ).arg(i))); |
6953 | QTRY_COMPARE(item->y(), i*20.0); |
6954 | QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName" , index: i); |
6955 | QVERIFY(name != nullptr); |
6956 | QTRY_COMPARE(name->text(), model.name(i)); |
6957 | } |
6958 | |
6959 | releaseView(view: window); |
6960 | } |
6961 | |
6962 | void tst_QQuickListView::addTransitions_data() |
6963 | { |
6964 | QTest::addColumn<int>(name: "initialItemCount" ); |
6965 | QTest::addColumn<qreal>(name: "contentY" ); |
6966 | QTest::addColumn<bool>(name: "shouldAnimateTargets" ); |
6967 | QTest::addColumn<int>(name: "insertionIndex" ); |
6968 | QTest::addColumn<int>(name: "insertionCount" ); |
6969 | QTest::addColumn<ListRange>(name: "expectedDisplacedIndexes" ); |
6970 | |
6971 | // if inserting before visible index, items should not appear or animate in, even if there are > 1 new items |
6972 | QTest::newRow(dataTag: "insert 1, just before start" ) |
6973 | << 30 << 20.0 << false |
6974 | << 0 << 1 << ListRange(); |
6975 | QTest::newRow(dataTag: "insert 1, way before start" ) |
6976 | << 30 << 20.0 << false |
6977 | << 0 << 1 << ListRange(); |
6978 | QTest::newRow(dataTag: "insert multiple, just before start" ) |
6979 | << 30 << 100.0 << false |
6980 | << 0 << 3 << ListRange(); |
6981 | QTest::newRow(dataTag: "insert multiple, way before start" ) |
6982 | << 30 << 100.0 << false |
6983 | << 0 << 3 << ListRange(); |
6984 | |
6985 | QTest::newRow(dataTag: "insert 1 at start" ) |
6986 | << 30 << 0.0 << true |
6987 | << 0 << 1 << ListRange(0, 15); |
6988 | QTest::newRow(dataTag: "insert multiple at start" ) |
6989 | << 30 << 0.0 << true |
6990 | << 0 << 3 << ListRange(0, 15); |
6991 | QTest::newRow(dataTag: "insert 1 at start, content y not 0" ) |
6992 | << 30 << 40.0 << true // first visible is index 2, so translate the displaced indexes by 2 |
6993 | << 2 << 1 << ListRange(0 + 2, 15 + 2); |
6994 | QTest::newRow(dataTag: "insert multiple at start, content y not 0" ) |
6995 | << 30 << 40.0 << true // first visible is index 2 |
6996 | << 2 << 3 << ListRange(0 + 2, 15 + 2); |
6997 | |
6998 | QTest::newRow(dataTag: "insert 1 at start, to empty list" ) |
6999 | << 0 << 0.0 << true |
7000 | << 0 << 1 << ListRange(); |
7001 | QTest::newRow(dataTag: "insert multiple at start, to empty list" ) |
7002 | << 0 << 0.0 << true |
7003 | << 0 << 3 << ListRange(); |
7004 | |
7005 | QTest::newRow(dataTag: "insert 1 at middle" ) |
7006 | << 30 << 0.0 << true |
7007 | << 5 << 1 << ListRange(5, 15); |
7008 | QTest::newRow(dataTag: "insert multiple at middle" ) |
7009 | << 30 << 0.0 << true |
7010 | << 5 << 3 << ListRange(5, 15); |
7011 | |
7012 | QTest::newRow(dataTag: "insert 1 at bottom" ) |
7013 | << 30 << 0.0 << true |
7014 | << 15 << 1 << ListRange(15, 15); |
7015 | QTest::newRow(dataTag: "insert multiple at bottom" ) |
7016 | << 30 << 0.0 << true |
7017 | << 15 << 3 << ListRange(15, 15); |
7018 | QTest::newRow(dataTag: "insert 1 at bottom, content y not 0" ) |
7019 | << 30 << 20.0 * 3 << true |
7020 | << 15 + 3 << 1 << ListRange(15 + 3, 15 + 3); |
7021 | QTest::newRow(dataTag: "insert multiple at bottom, content y not 0" ) |
7022 | << 30 << 20.0 * 3 << true |
7023 | << 15 + 3 << 3 << ListRange(15 + 3, 15 + 3); |
7024 | |
7025 | // items added after the last visible will not be animated in, since they |
7026 | // do not appear in the final view |
7027 | QTest::newRow(dataTag: "insert 1 after end" ) |
7028 | << 30 << 0.0 << false |
7029 | << 17 << 1 << ListRange(); |
7030 | QTest::newRow(dataTag: "insert multiple after end" ) |
7031 | << 30 << 0.0 << false |
7032 | << 17 << 3 << ListRange(); |
7033 | } |
7034 | |
7035 | void tst_QQuickListView::moveTransitions() |
7036 | { |
7037 | QFETCH(int, initialItemCount); |
7038 | QFETCH(qreal, contentY); |
7039 | QFETCH(qreal, itemsOffsetAfterMove); |
7040 | QFETCH(int, moveFrom); |
7041 | QFETCH(int, moveTo); |
7042 | QFETCH(int, moveCount); |
7043 | QFETCH(ListRange, expectedDisplacedIndexes); |
7044 | |
7045 | // target and displaced items should pass through these points |
7046 | QPointF targetItems_transitionVia(-50, 50); |
7047 | QPointF displacedItems_transitionVia(100, 100); |
7048 | |
7049 | QaimModel model; |
7050 | for (int i = 0; i < initialItemCount; i++) |
7051 | model.addItem(name: "Original item" + QString::number(i), number: "" ); |
7052 | QaimModel model_targetItems_transitionVia; |
7053 | QaimModel model_displacedItems_transitionVia; |
7054 | |
7055 | QQuickView *window = getView(); |
7056 | QQmlContext *ctxt = window->rootContext(); |
7057 | QScopedPointer<TestObject> testObject(new TestObject); |
7058 | ctxt->setContextProperty("testModel" , &model); |
7059 | ctxt->setContextProperty("model_targetItems_transitionVia" , &model_targetItems_transitionVia); |
7060 | ctxt->setContextProperty("model_displacedItems_transitionVia" , &model_displacedItems_transitionVia); |
7061 | ctxt->setContextProperty("targetItems_transitionVia" , targetItems_transitionVia); |
7062 | ctxt->setContextProperty("displacedItems_transitionVia" , displacedItems_transitionVia); |
7063 | ctxt->setContextProperty("testObject" , testObject.data()); |
7064 | window->setSource(testFileUrl(fileName: "moveTransitions.qml" )); |
7065 | window->show(); |
7066 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
7067 | |
7068 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
7069 | QTRY_VERIFY(listview != nullptr); |
7070 | QQuickItem *contentItem = listview->contentItem(); |
7071 | QVERIFY(contentItem != nullptr); |
7072 | QQuickText *name; |
7073 | |
7074 | if (contentY != 0) { |
7075 | listview->setContentY(contentY); |
7076 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
7077 | } |
7078 | |
7079 | QList<QPair<QString,QString> > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model); |
7080 | |
7081 | // Items moving to *or* from visible positions should be animated. |
7082 | // Otherwise, they should not be animated. |
7083 | QList<QPair<QString, QString> > expectedTargetData; |
7084 | QList<int> targetIndexes; |
7085 | for (int i=moveFrom; i<moveFrom+moveCount; i++) { |
7086 | int toIndex = moveTo + (i - moveFrom); |
7087 | if (i <= (contentY + listview->height()) / 20 |
7088 | || toIndex < (contentY + listview->height()) / 20) { |
7089 | expectedTargetData << qMakePair(x: model.name(index: i), y: model.number(index: i)); |
7090 | targetIndexes << i; |
7091 | } |
7092 | } |
7093 | // ViewTransition.index provides the indices that items are moving to, not from |
7094 | targetIndexes = adjustIndexesForMove(indexes: targetIndexes, from: moveFrom, to: moveTo, count: moveCount); |
7095 | |
7096 | // start animation |
7097 | model.moveItems(from: moveFrom, to: moveTo, count: moveCount); |
7098 | |
7099 | QTRY_COMPARE(listview->property("targetTransitionsDone" ).toInt(), expectedTargetData.count()); |
7100 | QTRY_COMPARE(listview->property("displaceTransitionsDone" ).toInt(), |
7101 | expectedDisplacedIndexes.isValid() ? expectedDisplacedIndexes.count() : 0); |
7102 | |
7103 | QList<QQuickItem *> targetItems = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" , indexes: targetIndexes); |
7104 | |
7105 | // check the target and displaced items were animated |
7106 | model_targetItems_transitionVia.matchAgainst(other: expectedTargetData, error1: "wasn't animated from target 'from' pos" , error2: "shouldn't have been animated from target 'from' pos" ); |
7107 | model_displacedItems_transitionVia.matchAgainst(other: expectedDisplacedValues, error1: "wasn't animated with displaced anim" , error2: "shouldn't have been animated with displaced anim" ); |
7108 | |
7109 | // check attached properties |
7110 | matchItemsAndIndexes(items: listview->property(name: "targetTrans_items" ).toMap(), model, expectedIndexes: targetIndexes); |
7111 | matchIndexLists(indexLists: listview->property(name: "targetTrans_targetIndexes" ).toList(), expectedIndexes: targetIndexes); |
7112 | matchItemLists(itemLists: listview->property(name: "targetTrans_targetItems" ).toList(), expectedItems: targetItems); |
7113 | if (expectedDisplacedIndexes.isValid()) { |
7114 | // adjust expectedDisplacedIndexes to their final values after the move |
7115 | QList<int> displacedIndexes = adjustIndexesForMove(indexes: expectedDisplacedIndexes.indexes, from: moveFrom, to: moveTo, count: moveCount); |
7116 | matchItemsAndIndexes(items: listview->property(name: "displacedTrans_items" ).toMap(), model, expectedIndexes: displacedIndexes); |
7117 | matchIndexLists(indexLists: listview->property(name: "displacedTrans_targetIndexes" ).toList(), expectedIndexes: targetIndexes); |
7118 | matchItemLists(itemLists: listview->property(name: "displacedTrans_targetItems" ).toList(), expectedItems: targetItems); |
7119 | } |
7120 | |
7121 | QList<QQuickItem*> items = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ); |
7122 | int firstVisibleIndex = -1; |
7123 | for (int i=0; i<items.count(); i++) { |
7124 | if (items[i]->y() >= contentY) { |
7125 | QQmlExpression e(qmlContext(items[i]), items[i], "index" ); |
7126 | firstVisibleIndex = e.evaluate().toInt(); |
7127 | break; |
7128 | } |
7129 | } |
7130 | QVERIFY2(firstVisibleIndex >= 0, QByteArray::number(firstVisibleIndex)); |
7131 | |
7132 | // verify all items moved to the correct final positions |
7133 | int itemCount = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ).count(); |
7134 | for (int i=firstVisibleIndex; i < model.count() && i < itemCount; ++i) { |
7135 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
7136 | QVERIFY2(item, qPrintable(QString("Item %1 not found" ).arg(i))); |
7137 | QTRY_COMPARE(item->y(), i*20.0 + itemsOffsetAfterMove); |
7138 | name = findItem<QQuickText>(parent: contentItem, objectName: "textName" , index: i); |
7139 | QVERIFY(name != nullptr); |
7140 | QTRY_COMPARE(name->text(), model.name(i)); |
7141 | } |
7142 | |
7143 | releaseView(view: window); |
7144 | } |
7145 | |
7146 | void tst_QQuickListView::moveTransitions_data() |
7147 | { |
7148 | QTest::addColumn<int>(name: "initialItemCount" ); |
7149 | QTest::addColumn<qreal>(name: "contentY" ); |
7150 | QTest::addColumn<qreal>(name: "itemsOffsetAfterMove" ); |
7151 | QTest::addColumn<int>(name: "moveFrom" ); |
7152 | QTest::addColumn<int>(name: "moveTo" ); |
7153 | QTest::addColumn<int>(name: "moveCount" ); |
7154 | QTest::addColumn<ListRange>(name: "expectedDisplacedIndexes" ); |
7155 | |
7156 | // when removing from above the visible, all items shift down depending on how many |
7157 | // items have been removed from above the visible |
7158 | QTest::newRow(dataTag: "move from above view, outside visible items, move 1" ) << 30 << 4*20.0 << 20.0 |
7159 | << 1 << 10 << 1 << ListRange(11, 15+4); |
7160 | QTest::newRow(dataTag: "move from above view, outside visible items, move 1 (first item)" ) << 30 << 4*20.0 << 20.0 |
7161 | << 0 << 10 << 1 << ListRange(11, 15+4); |
7162 | QTest::newRow(dataTag: "move from above view, outside visible items, move multiple" ) << 30 << 4*20.0 << 2*20.0 |
7163 | << 1 << 10 << 2 << ListRange(12, 15+4); |
7164 | QTest::newRow(dataTag: "move from above view, outside visible items, move multiple (first item)" ) << 30 << 4*20.0 << 3*20.0 |
7165 | << 0 << 10 << 3 << ListRange(13, 15+4); |
7166 | QTest::newRow(dataTag: "move from above view, mix of visible/non-visible" ) << 30 << 4*20.0 << 3*20.0 |
7167 | << 1 << 10 << 5 << ListRange(6, 14) + ListRange(15, 15+4); |
7168 | QTest::newRow(dataTag: "move from above view, mix of visible/non-visible (move first)" ) << 30 << 4*20.0 << 4*20.0 |
7169 | << 0 << 10 << 5 << ListRange(5, 14) + ListRange(15, 15+4); |
7170 | |
7171 | QTest::newRow(dataTag: "move within view, move 1 down" ) << 30 << 0.0 << 0.0 |
7172 | << 1 << 10 << 1 << ListRange(2, 10); |
7173 | QTest::newRow(dataTag: "move within view, move 1 down, move first item" ) << 30 << 0.0 << 0.0 |
7174 | << 0 << 10 << 1 << ListRange(1, 10); |
7175 | QTest::newRow(dataTag: "move within view, move 1 down, move first item, contentY not 0" ) << 30 << 4*20.0 << 0.0 |
7176 | << 0+4 << 10+4 << 1 << ListRange(1+4, 10+4); |
7177 | QTest::newRow(dataTag: "move within view, move 1 down, to last item" ) << 30 << 0.0 << 0.0 |
7178 | << 10 << 15 << 1 << ListRange(11, 15); |
7179 | QTest::newRow(dataTag: "move within view, move first->last" ) << 30 << 0.0 << 0.0 |
7180 | << 0 << 15 << 1 << ListRange(1, 15); |
7181 | |
7182 | QTest::newRow(dataTag: "move within view, move multiple down" ) << 30 << 0.0 << 0.0 |
7183 | << 1 << 10 << 3 << ListRange(4, 12); |
7184 | QTest::newRow(dataTag: "move within view, move multiple down, move first item" ) << 30 << 0.0 << 0.0 |
7185 | << 0 << 10 << 3 << ListRange(3, 12); |
7186 | QTest::newRow(dataTag: "move within view, move multiple down, move first item, contentY not 0" ) << 30 << 4*20.0 << 0.0 |
7187 | << 0+4 << 10+4 << 3 << ListRange(3+4, 12+4); |
7188 | QTest::newRow(dataTag: "move within view, move multiple down, displace last item" ) << 30 << 0.0 << 0.0 |
7189 | << 5 << 13 << 3 << ListRange(8, 15); |
7190 | QTest::newRow(dataTag: "move within view, move multiple down, move first->last" ) << 30 << 0.0 << 0.0 |
7191 | << 0 << 13 << 3 << ListRange(3, 15); |
7192 | |
7193 | QTest::newRow(dataTag: "move within view, move 1 up" ) << 30 << 0.0 << 0.0 |
7194 | << 10 << 1 << 1 << ListRange(1, 9); |
7195 | QTest::newRow(dataTag: "move within view, move 1 up, move to first index" ) << 30 << 0.0 << 0.0 |
7196 | << 10 << 0 << 1 << ListRange(0, 9); |
7197 | QTest::newRow(dataTag: "move within view, move 1 up, move to first index, contentY not 0" ) << 30 << 4*20.0 << 0.0 |
7198 | << 10+4 << 0+4 << 1 << ListRange(0+4, 9+4); |
7199 | QTest::newRow(dataTag: "move within view, move 1 up, move to first index, contentY not on item border" ) << 30 << 4*20.0 - 10 << 0.0 |
7200 | << 10+4 << 0+4 << 1 << ListRange(0+4, 9+4); |
7201 | QTest::newRow(dataTag: "move within view, move 1 up, move last item" ) << 30 << 0.0 << 0.0 |
7202 | << 15 << 10 << 1 << ListRange(10, 14); |
7203 | QTest::newRow(dataTag: "move within view, move 1 up, move last->first" ) << 30 << 0.0 << 0.0 |
7204 | << 15 << 0 << 1 << ListRange(0, 14); |
7205 | |
7206 | QTest::newRow(dataTag: "move within view, move multiple up" ) << 30 << 0.0 << 0.0 |
7207 | << 10 << 1 << 3 << ListRange(1, 9); |
7208 | QTest::newRow(dataTag: "move within view, move multiple up, move to first index" ) << 30 << 0.0 << 0.0 |
7209 | << 10 << 0 << 3 << ListRange(0, 9); |
7210 | QTest::newRow(dataTag: "move within view, move multiple up, move to first index, contentY not 0" ) << 30 << 4*20.0 << 0.0 |
7211 | << 10+4 << 0+4 << 3 << ListRange(0+4, 9+4); |
7212 | QTest::newRow(dataTag: "move within view, move multiple up, move last item" ) << 30 << 0.0 << 0.0 |
7213 | << 13 << 5 << 3 << ListRange(5, 12); |
7214 | QTest::newRow(dataTag: "move within view, move multiple up, move last->first" ) << 30 << 0.0 << 0.0 |
7215 | << 13 << 0 << 3 << ListRange(0, 12); |
7216 | |
7217 | QTest::newRow(dataTag: "move from below view, move 1 up, move to top" ) << 30 << 0.0 << 0.0 |
7218 | << 20 << 0 << 1 << ListRange(0, 15); |
7219 | QTest::newRow(dataTag: "move from below view, move 1 up, move to top, contentY not 0" ) << 30 << 4*20.0 << 0.0 |
7220 | << 25 << 4 << 1 << ListRange(0+4, 15+4); |
7221 | QTest::newRow(dataTag: "move from below view, move multiple up, move to top" ) << 30 << 0.0 << 0.0 |
7222 | << 20 << 0 << 3 << ListRange(0, 15); |
7223 | QTest::newRow(dataTag: "move from below view, move multiple up, move to top, contentY not 0" ) << 30 << 4*20.0 << 0.0 |
7224 | << 25 << 4 << 3 << ListRange(0+4, 15+4); |
7225 | |
7226 | QTest::newRow(dataTag: "move from below view, move 1 up, move to bottom" ) << 30 << 0.0 << 0.0 |
7227 | << 20 << 15 << 1 << ListRange(15, 15); |
7228 | QTest::newRow(dataTag: "move from below view, move 1 up, move to bottom, contentY not 0" ) << 30 << 4*20.0 << 0.0 |
7229 | << 25 << 15+4 << 1 << ListRange(15+4, 15+4); |
7230 | QTest::newRow(dataTag: "move from below view, move multiple up, move to bottom" ) << 30 << 0.0 << 0.0 |
7231 | << 20 << 15 << 3 << ListRange(15, 15); |
7232 | QTest::newRow(dataTag: "move from below view, move multiple up, move to bottom, contentY not 0" ) << 30 << 4*20.0 << 0.0 |
7233 | << 25 << 15+4 << 3 << ListRange(15+4, 15+4); |
7234 | } |
7235 | |
7236 | void tst_QQuickListView::removeTransitions() |
7237 | { |
7238 | QFETCH(int, initialItemCount); |
7239 | QFETCH(bool, shouldAnimateTargets); |
7240 | QFETCH(qreal, contentY); |
7241 | QFETCH(int, removalIndex); |
7242 | QFETCH(int, removalCount); |
7243 | QFETCH(ListRange, expectedDisplacedIndexes); |
7244 | |
7245 | // added items should end here |
7246 | QPointF targetItems_transitionTo(-50, -50); |
7247 | |
7248 | // displaced items should pass through this points |
7249 | QPointF displacedItems_transitionVia(100, 100); |
7250 | |
7251 | QaimModel model; |
7252 | for (int i = 0; i < initialItemCount; i++) |
7253 | model.addItem(name: "Original item" + QString::number(i), number: "" ); |
7254 | QaimModel model_targetItems_transitionTo; |
7255 | QaimModel model_displacedItems_transitionVia; |
7256 | |
7257 | QQuickView *window = getView(); |
7258 | QQmlContext *ctxt = window->rootContext(); |
7259 | QScopedPointer<TestObject> testObject(new TestObject); |
7260 | ctxt->setContextProperty("testModel" , &model); |
7261 | ctxt->setContextProperty("model_targetItems_transitionTo" , &model_targetItems_transitionTo); |
7262 | ctxt->setContextProperty("model_displacedItems_transitionVia" , &model_displacedItems_transitionVia); |
7263 | ctxt->setContextProperty("targetItems_transitionTo" , targetItems_transitionTo); |
7264 | ctxt->setContextProperty("displacedItems_transitionVia" , displacedItems_transitionVia); |
7265 | ctxt->setContextProperty("testObject" , testObject.data()); |
7266 | window->setSource(testFileUrl(fileName: "removeTransitions.qml" )); |
7267 | window->show(); |
7268 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
7269 | |
7270 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
7271 | QTRY_VERIFY(listview != nullptr); |
7272 | QQuickItem *contentItem = listview->contentItem(); |
7273 | QVERIFY(contentItem != nullptr); |
7274 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
7275 | |
7276 | if (contentY != 0) { |
7277 | listview->setContentY(contentY); |
7278 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
7279 | } |
7280 | |
7281 | QList<QPair<QString,QString> > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model); |
7282 | |
7283 | // only target items that are visible should be animated |
7284 | QList<QPair<QString, QString> > expectedTargetData; |
7285 | QList<int> targetIndexes; |
7286 | if (shouldAnimateTargets) { |
7287 | for (int i=removalIndex; i<removalIndex+removalCount; i++) { |
7288 | if (i >= contentY / 20 && i < (contentY + listview->height()) / 20) { |
7289 | expectedTargetData << qMakePair(x: model.name(index: i), y: model.number(index: i)); |
7290 | targetIndexes << i; |
7291 | } |
7292 | } |
7293 | QVERIFY(expectedTargetData.count() > 0); |
7294 | } |
7295 | |
7296 | // calculate targetItems and expectedTargets before model changes |
7297 | QList<QQuickItem *> targetItems = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" , indexes: targetIndexes); |
7298 | QVariantMap expectedTargets; |
7299 | for (int i=0; i<targetIndexes.count(); i++) |
7300 | expectedTargets[model.name(index: targetIndexes[i])] = targetIndexes[i]; |
7301 | |
7302 | // start animation |
7303 | model.removeItems(index: removalIndex, count: removalCount); |
7304 | QTRY_COMPARE(model.count(), listview->count()); |
7305 | |
7306 | if (shouldAnimateTargets) { |
7307 | QTRY_COMPARE(listview->property("targetTransitionsDone" ).toInt(), expectedTargetData.count()); |
7308 | QTRY_COMPARE(listview->property("displaceTransitionsDone" ).toInt(), |
7309 | expectedDisplacedIndexes.isValid() ? expectedDisplacedIndexes.count() : 0); |
7310 | |
7311 | // check the target and displaced items were animated |
7312 | model_targetItems_transitionTo.matchAgainst(other: expectedTargetData, error1: "wasn't animated to target 'to' pos" , error2: "shouldn't have been animated to target 'to' pos" ); |
7313 | model_displacedItems_transitionVia.matchAgainst(other: expectedDisplacedValues, error1: "wasn't animated with displaced anim" , error2: "shouldn't have been animated with displaced anim" ); |
7314 | |
7315 | // check attached properties |
7316 | QCOMPARE(listview->property("targetTrans_items" ).toMap(), expectedTargets); |
7317 | matchIndexLists(indexLists: listview->property(name: "targetTrans_targetIndexes" ).toList(), expectedIndexes: targetIndexes); |
7318 | matchItemLists(itemLists: listview->property(name: "targetTrans_targetItems" ).toList(), expectedItems: targetItems); |
7319 | if (expectedDisplacedIndexes.isValid()) { |
7320 | // adjust expectedDisplacedIndexes to their final values after the move |
7321 | QList<int> displacedIndexes = adjustIndexesForRemoveDisplaced(indexes: expectedDisplacedIndexes.indexes, index: removalIndex, count: removalCount); |
7322 | matchItemsAndIndexes(items: listview->property(name: "displacedTrans_items" ).toMap(), model, expectedIndexes: displacedIndexes); |
7323 | matchIndexLists(indexLists: listview->property(name: "displacedTrans_targetIndexes" ).toList(), expectedIndexes: targetIndexes); |
7324 | matchItemLists(itemLists: listview->property(name: "displacedTrans_targetItems" ).toList(), expectedItems: targetItems); |
7325 | } |
7326 | } else { |
7327 | QTRY_COMPARE(model_targetItems_transitionTo.count(), 0); |
7328 | QTRY_COMPARE(model_displacedItems_transitionVia.count(), 0); |
7329 | } |
7330 | |
7331 | QList<QQuickItem*> items = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ); |
7332 | int firstVisibleIndex = -1; |
7333 | int itemCount = items.count(); |
7334 | |
7335 | for (int i=0; i<items.count(); i++) { |
7336 | QQmlExpression e(qmlContext(items[i]), items[i], "index" ); |
7337 | int index = e.evaluate().toInt(); |
7338 | if (firstVisibleIndex < 0 && items[i]->y() >= contentY) |
7339 | firstVisibleIndex = index; |
7340 | if (index < 0) |
7341 | itemCount--; // exclude deleted items |
7342 | } |
7343 | QVERIFY2(firstVisibleIndex >= 0, QByteArray::number(firstVisibleIndex)); |
7344 | |
7345 | // verify all items moved to the correct final positions |
7346 | for (int i=firstVisibleIndex; i < model.count() && i < itemCount; ++i) { |
7347 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
7348 | QVERIFY2(item, qPrintable(QString("Item %1 not found" ).arg(i))); |
7349 | QCOMPARE(item->x(), 0.0); |
7350 | QCOMPARE(item->y(), contentY + (i-firstVisibleIndex) * 20.0); |
7351 | QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName" , index: i); |
7352 | QVERIFY(name != nullptr); |
7353 | QTRY_COMPARE(name->text(), model.name(i)); |
7354 | } |
7355 | |
7356 | releaseView(view: window); |
7357 | } |
7358 | |
7359 | void tst_QQuickListView::removeTransitions_data() |
7360 | { |
7361 | QTest::addColumn<int>(name: "initialItemCount" ); |
7362 | QTest::addColumn<qreal>(name: "contentY" ); |
7363 | QTest::addColumn<bool>(name: "shouldAnimateTargets" ); |
7364 | QTest::addColumn<int>(name: "removalIndex" ); |
7365 | QTest::addColumn<int>(name: "removalCount" ); |
7366 | QTest::addColumn<ListRange>(name: "expectedDisplacedIndexes" ); |
7367 | |
7368 | // All items that are visible following the remove operation should be animated. |
7369 | // Remove targets that are outside of the view should not be animated. |
7370 | |
7371 | QTest::newRow(dataTag: "remove 1 before start" ) |
7372 | << 30 << 20.0 * 3 << false |
7373 | << 2 << 1 << ListRange(); |
7374 | QTest::newRow(dataTag: "remove multiple, all before start" ) |
7375 | << 30 << 20.0 * 3 << false |
7376 | << 0 << 3 << ListRange(); |
7377 | QTest::newRow(dataTag: "remove mix of before and after start" ) |
7378 | << 30 << 20.0 * 3 << true |
7379 | << 2 << 3 << ListRange(5, 20); // 5-20 are visible after the remove |
7380 | |
7381 | QTest::newRow(dataTag: "remove 1 from start" ) |
7382 | << 30 << 0.0 << true |
7383 | << 0 << 1 << ListRange(1, 16); // 1-16 are visible after the remove |
7384 | QTest::newRow(dataTag: "remove multiple from start" ) |
7385 | << 30 << 0.0 << true |
7386 | << 0 << 3 << ListRange(3, 18); // 3-18 are visible after the remove |
7387 | QTest::newRow(dataTag: "remove 1 from start, content y not 0" ) |
7388 | << 30 << 20.0 * 2 << true // first visible is index 2, so translate the displaced indexes by 2 |
7389 | << 2 << 1 << ListRange(1 + 2, 16 + 2); |
7390 | QTest::newRow(dataTag: "remove multiple from start, content y not 0" ) |
7391 | << 30 << 20.0 * 2 << true // first visible is index 2 |
7392 | << 2 << 3 << ListRange(3 + 2, 18 + 2); |
7393 | |
7394 | QTest::newRow(dataTag: "remove 1 from middle" ) |
7395 | << 30 << 0.0 << true |
7396 | << 5 << 1 << ListRange(6, 16); |
7397 | QTest::newRow(dataTag: "remove multiple from middle" ) |
7398 | << 30 << 0.0 << true |
7399 | << 5 << 3 << ListRange(8, 18); |
7400 | |
7401 | |
7402 | QTest::newRow(dataTag: "remove 1 from bottom" ) |
7403 | << 30 << 0.0 << true |
7404 | << 15 << 1 << ListRange(16, 16); |
7405 | |
7406 | // remove 15, 16, 17 |
7407 | // 15 will animate as the target item, 16 & 17 won't be animated since they are outside |
7408 | // the view, and 18 will be animated as the displaced item to replace the last item |
7409 | QTest::newRow(dataTag: "remove multiple from bottom" ) |
7410 | << 30 << 0.0 << true |
7411 | << 15 << 3 << ListRange(18, 18); |
7412 | |
7413 | QTest::newRow(dataTag: "remove 1 from bottom, content y not 0" ) |
7414 | << 30 << 20.0 * 2 << true |
7415 | << 15 + 2 << 1 << ListRange(16 + 2, 16 + 2); |
7416 | QTest::newRow(dataTag: "remove multiple from bottom, content y not 0" ) |
7417 | << 30 << 20.0 * 2 << true |
7418 | << 15 + 2 << 3 << ListRange(18 + 2, 18 + 2); |
7419 | |
7420 | |
7421 | QTest::newRow(dataTag: "remove 1 after end" ) |
7422 | << 30 << 0.0 << false |
7423 | << 17 << 1 << ListRange(); |
7424 | QTest::newRow(dataTag: "remove multiple after end" ) |
7425 | << 30 << 0.0 << false |
7426 | << 17 << 3 << ListRange(); |
7427 | } |
7428 | |
7429 | void tst_QQuickListView::displacedTransitions() |
7430 | { |
7431 | QFETCH(bool, useDisplaced); |
7432 | QFETCH(bool, displacedEnabled); |
7433 | QFETCH(bool, useAddDisplaced); |
7434 | QFETCH(bool, addDisplacedEnabled); |
7435 | QFETCH(bool, useMoveDisplaced); |
7436 | QFETCH(bool, moveDisplacedEnabled); |
7437 | QFETCH(bool, useRemoveDisplaced); |
7438 | QFETCH(bool, removeDisplacedEnabled); |
7439 | QFETCH(ListChange, change); |
7440 | QFETCH(ListRange, expectedDisplacedIndexes); |
7441 | |
7442 | QaimModel model; |
7443 | for (int i = 0; i < 30; i++) |
7444 | model.addItem(name: "Original item" + QString::number(i), number: "" ); |
7445 | QaimModel model_displaced_transitionVia; |
7446 | QaimModel model_addDisplaced_transitionVia; |
7447 | QaimModel model_moveDisplaced_transitionVia; |
7448 | QaimModel model_removeDisplaced_transitionVia; |
7449 | |
7450 | QPointF displaced_transitionVia(-50, -100); |
7451 | QPointF addDisplaced_transitionVia(-150, 100); |
7452 | QPointF moveDisplaced_transitionVia(50, -100); |
7453 | QPointF removeDisplaced_transitionVia(150, 100); |
7454 | |
7455 | QQuickView *window = getView(); |
7456 | QQmlContext *ctxt = window->rootContext(); |
7457 | QScopedPointer<TestObject> testObject(new TestObject(window)); |
7458 | ctxt->setContextProperty("testModel" , &model); |
7459 | ctxt->setContextProperty("testObject" , testObject.data()); |
7460 | ctxt->setContextProperty("model_displaced_transitionVia" , &model_displaced_transitionVia); |
7461 | ctxt->setContextProperty("model_addDisplaced_transitionVia" , &model_addDisplaced_transitionVia); |
7462 | ctxt->setContextProperty("model_moveDisplaced_transitionVia" , &model_moveDisplaced_transitionVia); |
7463 | ctxt->setContextProperty("model_removeDisplaced_transitionVia" , &model_removeDisplaced_transitionVia); |
7464 | ctxt->setContextProperty("displaced_transitionVia" , displaced_transitionVia); |
7465 | ctxt->setContextProperty("addDisplaced_transitionVia" , addDisplaced_transitionVia); |
7466 | ctxt->setContextProperty("moveDisplaced_transitionVia" , moveDisplaced_transitionVia); |
7467 | ctxt->setContextProperty("removeDisplaced_transitionVia" , removeDisplaced_transitionVia); |
7468 | ctxt->setContextProperty("useDisplaced" , useDisplaced); |
7469 | ctxt->setContextProperty("displacedEnabled" , displacedEnabled); |
7470 | ctxt->setContextProperty("useAddDisplaced" , useAddDisplaced); |
7471 | ctxt->setContextProperty("addDisplacedEnabled" , addDisplacedEnabled); |
7472 | ctxt->setContextProperty("useMoveDisplaced" , useMoveDisplaced); |
7473 | ctxt->setContextProperty("moveDisplacedEnabled" , moveDisplacedEnabled); |
7474 | ctxt->setContextProperty("useRemoveDisplaced" , useRemoveDisplaced); |
7475 | ctxt->setContextProperty("removeDisplacedEnabled" , removeDisplacedEnabled); |
7476 | window->setSource(testFileUrl(fileName: "displacedTransitions.qml" )); |
7477 | window->show(); |
7478 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
7479 | |
7480 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
7481 | QTRY_VERIFY(listview != nullptr); |
7482 | QQuickItem *contentItem = listview->contentItem(); |
7483 | QVERIFY(contentItem != nullptr); |
7484 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
7485 | |
7486 | QList<QPair<QString,QString> > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model); |
7487 | listview->setProperty(name: "displaceTransitionsDone" , value: false); |
7488 | |
7489 | switch (change.type) { |
7490 | case ListChange::Inserted: |
7491 | { |
7492 | QList<QPair<QString, QString> > targetItemData; |
7493 | for (int i=change.index; i<change.index + change.count; ++i) |
7494 | targetItemData << qMakePair(x: QString("new item %1" ).arg(a: i), y: QString::number(i)); |
7495 | model.insertItems(index: change.index, items: targetItemData); |
7496 | QTRY_COMPARE(model.count(), listview->count()); |
7497 | break; |
7498 | } |
7499 | case ListChange::Removed: |
7500 | model.removeItems(index: change.index, count: change.count); |
7501 | QTRY_COMPARE(model.count(), listview->count()); |
7502 | break; |
7503 | case ListChange::Moved: |
7504 | model.moveItems(from: change.index, to: change.to, count: change.count); |
7505 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
7506 | break; |
7507 | case ListChange::SetCurrent: |
7508 | case ListChange::SetContentY: |
7509 | case ListChange::Polish: |
7510 | break; |
7511 | } |
7512 | listview->forceLayout(); |
7513 | |
7514 | QVariantList resultTargetIndexes = listview->property(name: "displacedTargetIndexes" ).toList(); |
7515 | QVariantList resultTargetItems = listview->property(name: "displacedTargetItems" ).toList(); |
7516 | |
7517 | if ((useDisplaced && displacedEnabled) |
7518 | || (useAddDisplaced && addDisplacedEnabled) |
7519 | || (useMoveDisplaced && moveDisplacedEnabled) |
7520 | || (useRemoveDisplaced && removeDisplacedEnabled)) { |
7521 | QTRY_VERIFY(listview->property("displaceTransitionsDone" ).toBool()); |
7522 | |
7523 | // check the correct number of target items and indexes were received |
7524 | QCOMPARE(resultTargetIndexes.count(), expectedDisplacedIndexes.count()); |
7525 | for (int i=0; i<resultTargetIndexes.count(); i++) |
7526 | QCOMPARE(resultTargetIndexes[i].value<QList<int> >().count(), change.count); |
7527 | QCOMPARE(resultTargetItems.count(), expectedDisplacedIndexes.count()); |
7528 | for (int i=0; i<resultTargetItems.count(); i++) |
7529 | QCOMPARE(resultTargetItems[i].toList().count(), change.count); |
7530 | } else { |
7531 | QCOMPARE(resultTargetIndexes.count(), 0); |
7532 | QCOMPARE(resultTargetItems.count(), 0); |
7533 | } |
7534 | |
7535 | if (change.type == ListChange::Inserted && useAddDisplaced && addDisplacedEnabled) |
7536 | model_addDisplaced_transitionVia.matchAgainst(other: expectedDisplacedValues, error1: "wasn't animated with add displaced" , error2: "shouldn't have been animated with add displaced" ); |
7537 | else |
7538 | QCOMPARE(model_addDisplaced_transitionVia.count(), 0); |
7539 | if (change.type == ListChange::Moved && useMoveDisplaced && moveDisplacedEnabled) |
7540 | model_moveDisplaced_transitionVia.matchAgainst(other: expectedDisplacedValues, error1: "wasn't animated with move displaced" , error2: "shouldn't have been animated with move displaced" ); |
7541 | else |
7542 | QCOMPARE(model_moveDisplaced_transitionVia.count(), 0); |
7543 | if (change.type == ListChange::Removed && useRemoveDisplaced && removeDisplacedEnabled) |
7544 | model_removeDisplaced_transitionVia.matchAgainst(other: expectedDisplacedValues, error1: "wasn't animated with remove displaced" , error2: "shouldn't have been animated with remove displaced" ); |
7545 | else |
7546 | QCOMPARE(model_removeDisplaced_transitionVia.count(), 0); |
7547 | |
7548 | if (useDisplaced && displacedEnabled |
7549 | && ( (change.type == ListChange::Inserted && (!useAddDisplaced || !addDisplacedEnabled)) |
7550 | || (change.type == ListChange::Moved && (!useMoveDisplaced || !moveDisplacedEnabled)) |
7551 | || (change.type == ListChange::Removed && (!useRemoveDisplaced || !removeDisplacedEnabled))) ) { |
7552 | model_displaced_transitionVia.matchAgainst(other: expectedDisplacedValues, error1: "wasn't animated with generic displaced" , error2: "shouldn't have been animated with generic displaced" ); |
7553 | } else { |
7554 | QCOMPARE(model_displaced_transitionVia.count(), 0); |
7555 | } |
7556 | |
7557 | // verify all items moved to the correct final positions |
7558 | QList<QQuickItem*> items = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ); |
7559 | for (int i=0; i < model.count() && i < items.count(); ++i) { |
7560 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
7561 | QVERIFY2(item, qPrintable(QString("Item %1 not found" ).arg(i))); |
7562 | QCOMPARE(item->x(), 0.0); |
7563 | QCOMPARE(item->y(), i * 20.0); |
7564 | QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName" , index: i); |
7565 | QVERIFY(name != nullptr); |
7566 | QTRY_COMPARE(name->text(), model.name(i)); |
7567 | } |
7568 | |
7569 | releaseView(view: window); |
7570 | } |
7571 | |
7572 | void tst_QQuickListView::displacedTransitions_data() |
7573 | { |
7574 | QTest::addColumn<bool>(name: "useDisplaced" ); |
7575 | QTest::addColumn<bool>(name: "displacedEnabled" ); |
7576 | QTest::addColumn<bool>(name: "useAddDisplaced" ); |
7577 | QTest::addColumn<bool>(name: "addDisplacedEnabled" ); |
7578 | QTest::addColumn<bool>(name: "useMoveDisplaced" ); |
7579 | QTest::addColumn<bool>(name: "moveDisplacedEnabled" ); |
7580 | QTest::addColumn<bool>(name: "useRemoveDisplaced" ); |
7581 | QTest::addColumn<bool>(name: "removeDisplacedEnabled" ); |
7582 | QTest::addColumn<ListChange>(name: "change" ); |
7583 | QTest::addColumn<ListRange>(name: "expectedDisplacedIndexes" ); |
7584 | |
7585 | QTest::newRow(dataTag: "no displaced transitions at all" ) |
7586 | << false << false |
7587 | << false << false |
7588 | << false << false |
7589 | << false << false |
7590 | << ListChange::insert(index: 0, count: 1) << ListRange(0, 15); |
7591 | |
7592 | QTest::newRow(dataTag: "just displaced" ) |
7593 | << true << true |
7594 | << false << false |
7595 | << false << false |
7596 | << false << false |
7597 | << ListChange::insert(index: 0, count: 1) << ListRange(0, 15); |
7598 | |
7599 | QTest::newRow(dataTag: "just displaced (not enabled)" ) |
7600 | << true << false |
7601 | << false << false |
7602 | << false << false |
7603 | << false << false |
7604 | << ListChange::insert(index: 0, count: 1) << ListRange(0, 15); |
7605 | |
7606 | QTest::newRow(dataTag: "displaced + addDisplaced" ) |
7607 | << true << true |
7608 | << true << true |
7609 | << false << false |
7610 | << false << false |
7611 | << ListChange::insert(index: 0, count: 1) << ListRange(0, 15); |
7612 | |
7613 | QTest::newRow(dataTag: "displaced + addDisplaced (not enabled)" ) |
7614 | << true << true |
7615 | << true << false |
7616 | << false << false |
7617 | << false << false |
7618 | << ListChange::insert(index: 0, count: 1) << ListRange(0, 15); |
7619 | |
7620 | QTest::newRow(dataTag: "displaced + moveDisplaced" ) |
7621 | << true << true |
7622 | << false << false |
7623 | << true << true |
7624 | << false << false |
7625 | << ListChange::move(index: 0, to: 10, count: 1) << ListRange(1, 10); |
7626 | |
7627 | QTest::newRow(dataTag: "displaced + moveDisplaced (not enabled)" ) |
7628 | << true << true |
7629 | << false << false |
7630 | << true << false |
7631 | << false << false |
7632 | << ListChange::move(index: 0, to: 10, count: 1) << ListRange(1, 10); |
7633 | |
7634 | QTest::newRow(dataTag: "displaced + removeDisplaced" ) |
7635 | << true << true |
7636 | << false << false |
7637 | << false << false |
7638 | << true << true |
7639 | << ListChange::remove(index: 0, count: 1) << ListRange(1, 16); |
7640 | |
7641 | QTest::newRow(dataTag: "displaced + removeDisplaced (not enabled)" ) |
7642 | << true << true |
7643 | << false << false |
7644 | << false << false |
7645 | << true << false |
7646 | << ListChange::remove(index: 0, count: 1) << ListRange(1, 16); |
7647 | |
7648 | |
7649 | QTest::newRow(dataTag: "displaced + add, should use generic displaced for a remove" ) |
7650 | << true << true |
7651 | << true << true |
7652 | << false << false |
7653 | << true << false |
7654 | << ListChange::remove(index: 0, count: 1) << ListRange(1, 16); |
7655 | } |
7656 | |
7657 | void tst_QQuickListView::multipleTransitions() |
7658 | { |
7659 | // Tests that if you interrupt a transition in progress with another action that |
7660 | // cancels the previous transition, the resulting items are still placed correctly. |
7661 | |
7662 | QFETCH(int, initialCount); |
7663 | QFETCH(qreal, contentY); |
7664 | QFETCH(QList<ListChange>, changes); |
7665 | QFETCH(bool, enableAddTransitions); |
7666 | QFETCH(bool, enableMoveTransitions); |
7667 | QFETCH(bool, enableRemoveTransitions); |
7668 | QFETCH(bool, rippleAddDisplaced); |
7669 | |
7670 | QPointF addTargets_transitionFrom(-50, -50); |
7671 | QPointF addDisplaced_transitionFrom(-50, 50); |
7672 | QPointF moveTargets_transitionFrom(50, -50); |
7673 | QPointF moveDisplaced_transitionFrom(50, 50); |
7674 | QPointF removeTargets_transitionTo(-100, 300); |
7675 | QPointF removeDisplaced_transitionFrom(100, 300); |
7676 | |
7677 | QaimModel model; |
7678 | for (int i = 0; i < initialCount; i++) |
7679 | model.addItem(name: "Original item" + QString::number(i), number: "" ); |
7680 | |
7681 | QQuickView *window = getView(); |
7682 | QQmlContext *ctxt = window->rootContext(); |
7683 | QScopedPointer<TestObject> testObject(new TestObject); |
7684 | ctxt->setContextProperty("testModel" , &model); |
7685 | ctxt->setContextProperty("testObject" , testObject.data()); |
7686 | ctxt->setContextProperty("addTargets_transitionFrom" , addTargets_transitionFrom); |
7687 | ctxt->setContextProperty("addDisplaced_transitionFrom" , addDisplaced_transitionFrom); |
7688 | ctxt->setContextProperty("moveTargets_transitionFrom" , moveTargets_transitionFrom); |
7689 | ctxt->setContextProperty("moveDisplaced_transitionFrom" , moveDisplaced_transitionFrom); |
7690 | ctxt->setContextProperty("removeTargets_transitionTo" , removeTargets_transitionTo); |
7691 | ctxt->setContextProperty("removeDisplaced_transitionFrom" , removeDisplaced_transitionFrom); |
7692 | ctxt->setContextProperty("enableAddTransitions" , enableAddTransitions); |
7693 | ctxt->setContextProperty("enableMoveTransitions" , enableMoveTransitions); |
7694 | ctxt->setContextProperty("enableRemoveTransitions" , enableRemoveTransitions); |
7695 | ctxt->setContextProperty("rippleAddDisplaced" , rippleAddDisplaced); |
7696 | window->setSource(testFileUrl(fileName: "multipleTransitions.qml" )); |
7697 | window->show(); |
7698 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
7699 | |
7700 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
7701 | QTRY_VERIFY(listview != nullptr); |
7702 | QQuickItem *contentItem = listview->contentItem(); |
7703 | QVERIFY(contentItem != nullptr); |
7704 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
7705 | |
7706 | if (contentY != 0) { |
7707 | listview->setContentY(contentY); |
7708 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
7709 | } |
7710 | |
7711 | int timeBetweenActions = window->rootObject()->property(name: "timeBetweenActions" ).toInt(); |
7712 | |
7713 | for (int i=0; i<changes.count(); i++) { |
7714 | switch (changes[i].type) { |
7715 | case ListChange::Inserted: |
7716 | { |
7717 | QList<QPair<QString, QString> > targetItems; |
7718 | for (int j=changes[i].index; j<changes[i].index + changes[i].count; ++j) |
7719 | targetItems << qMakePair(x: QString("new item %1" ).arg(a: j), y: QString::number(j)); |
7720 | model.insertItems(index: changes[i].index, items: targetItems); |
7721 | QTRY_COMPARE(model.count(), listview->count()); |
7722 | if (i == changes.count() - 1) { |
7723 | QTRY_VERIFY(!listview->property("runningAddTargets" ).toBool()); |
7724 | QTRY_VERIFY(!listview->property("runningAddDisplaced" ).toBool()); |
7725 | } else { |
7726 | QTest::qWait(ms: timeBetweenActions); |
7727 | } |
7728 | break; |
7729 | } |
7730 | case ListChange::Removed: |
7731 | model.removeItems(index: changes[i].index, count: changes[i].count); |
7732 | QTRY_COMPARE(model.count(), listview->count()); |
7733 | if (i == changes.count() - 1) { |
7734 | QTRY_VERIFY(!listview->property("runningRemoveTargets" ).toBool()); |
7735 | QTRY_VERIFY(!listview->property("runningRemoveDisplaced" ).toBool()); |
7736 | } else { |
7737 | QTest::qWait(ms: timeBetweenActions); |
7738 | } |
7739 | break; |
7740 | case ListChange::Moved: |
7741 | model.moveItems(from: changes[i].index, to: changes[i].to, count: changes[i].count); |
7742 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
7743 | if (i == changes.count() - 1) { |
7744 | QTRY_VERIFY(!listview->property("runningMoveTargets" ).toBool()); |
7745 | QTRY_VERIFY(!listview->property("runningMoveDisplaced" ).toBool()); |
7746 | } else { |
7747 | QTest::qWait(ms: timeBetweenActions); |
7748 | } |
7749 | break; |
7750 | case ListChange::SetCurrent: |
7751 | listview->setCurrentIndex(changes[i].index); |
7752 | break; |
7753 | case ListChange::SetContentY: |
7754 | listview->setContentY(changes[i].pos); |
7755 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
7756 | break; |
7757 | case ListChange::Polish: |
7758 | break; |
7759 | } |
7760 | } |
7761 | listview->forceLayout(); |
7762 | QTest::qWait(ms: 200); |
7763 | QCOMPARE(listview->count(), model.count()); |
7764 | |
7765 | // verify all items moved to the correct final positions |
7766 | QList<QQuickItem*> items = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ); |
7767 | for (int i=0; i < model.count() && i < items.count(); ++i) { |
7768 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
7769 | QVERIFY2(item, qPrintable(QString("Item %1 not found" ).arg(i))); |
7770 | QTRY_COMPARE(item->x(), 0.0); |
7771 | QTRY_COMPARE(item->y(), i*20.0); |
7772 | QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName" , index: i); |
7773 | QVERIFY(name != nullptr); |
7774 | QTRY_COMPARE(name->text(), model.name(i)); |
7775 | } |
7776 | |
7777 | releaseView(view: window); |
7778 | } |
7779 | |
7780 | void tst_QQuickListView::multipleTransitions_data() |
7781 | { |
7782 | QTest::addColumn<int>(name: "initialCount" ); |
7783 | QTest::addColumn<qreal>(name: "contentY" ); |
7784 | QTest::addColumn<QList<ListChange> >(name: "changes" ); |
7785 | QTest::addColumn<bool>(name: "enableAddTransitions" ); |
7786 | QTest::addColumn<bool>(name: "enableMoveTransitions" ); |
7787 | QTest::addColumn<bool>(name: "enableRemoveTransitions" ); |
7788 | QTest::addColumn<bool>(name: "rippleAddDisplaced" ); |
7789 | |
7790 | // the added item and displaced items should move to final dest correctly |
7791 | QTest::newRow(dataTag: "add item, then move it immediately" ) << 10 << 0.0 << (QList<ListChange>() |
7792 | << ListChange::insert(index: 0, count: 1) |
7793 | << ListChange::move(index: 0, to: 3, count: 1) |
7794 | ) |
7795 | << true << true << true << false; |
7796 | |
7797 | // items affected by the add should change from move to add transition |
7798 | QTest::newRow(dataTag: "move, then insert item before the moved item" ) << 20 << 0.0 << (QList<ListChange>() |
7799 | << ListChange::move(index: 1, to: 10, count: 3) |
7800 | << ListChange::insert(index: 0, count: 1) |
7801 | ) |
7802 | << true << true << true << false; |
7803 | |
7804 | // items should be placed correctly if you trigger a transition then refill for that index |
7805 | QTest::newRow(dataTag: "add at 0, flick down, flick back to top and add at 0 again" ) << 20 << 0.0 << (QList<ListChange>() |
7806 | << ListChange::insert(index: 0, count: 1) |
7807 | << ListChange::setContentY(80.0) |
7808 | << ListChange::setContentY(0.0) |
7809 | << ListChange::insert(index: 0, count: 1) |
7810 | ) |
7811 | << true << true << true << false; |
7812 | |
7813 | QTest::newRow(dataTag: "insert then remove same index, with ripple effect on add displaced" ) << 20 << 0.0 << (QList<ListChange>() |
7814 | << ListChange::insert(index: 1, count: 1) |
7815 | << ListChange::remove(index: 1, count: 1) |
7816 | ) |
7817 | << true << true << true << true; |
7818 | |
7819 | // if item is removed while undergoing a displaced transition, all other items should end up at their correct positions, |
7820 | // even if a remove-displace transition is not present to re-animate them |
7821 | QTest::newRow(dataTag: "insert then remove, with remove disabled" ) << 20 << 0.0 << (QList<ListChange>() |
7822 | << ListChange::insert(index: 0, count: 1) |
7823 | << ListChange::remove(index: 2, count: 1) |
7824 | ) |
7825 | << true << true << false << false; |
7826 | |
7827 | // if last item is not flush with the edge of the view, it should still be refilled in correctly after a |
7828 | // remove has changed the position of where it will move to |
7829 | QTest::newRow(dataTag: "insert twice then remove, with remove disabled" ) << 20 << 0.0 << (QList<ListChange>() |
7830 | << ListChange::setContentY(-10.0) |
7831 | << ListChange::insert(index: 0, count: 1) |
7832 | << ListChange::insert(index: 0, count: 1) |
7833 | << ListChange::remove(index: 2, count: 1) |
7834 | ) |
7835 | << true << true << false << false; |
7836 | } |
7837 | |
7838 | void tst_QQuickListView::multipleDisplaced() |
7839 | { |
7840 | // multiple move() operations should only restart displace transitions for items that |
7841 | // moved from previously set positions, and not those that have moved from their current |
7842 | // item positions (which may e.g. still be changing from easing bounces in the last transition) |
7843 | |
7844 | QaimModel model; |
7845 | for (int i = 0; i < 30; i++) |
7846 | model.addItem(name: "Original item" + QString::number(i), number: "" ); |
7847 | |
7848 | QQuickView *window = getView(); |
7849 | QQmlContext *ctxt = window->rootContext(); |
7850 | QScopedPointer<TestObject> testObject(new TestObject(window)); |
7851 | ctxt->setContextProperty("testModel" , &model); |
7852 | ctxt->setContextProperty("testObject" , testObject.data()); |
7853 | window->setSource(testFileUrl(fileName: "multipleDisplaced.qml" )); |
7854 | window->show(); |
7855 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
7856 | |
7857 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
7858 | QTRY_VERIFY(listview != nullptr); |
7859 | QQuickItem *contentItem = listview->contentItem(); |
7860 | QVERIFY(contentItem != nullptr); |
7861 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
7862 | |
7863 | model.moveItems(from: 12, to: 8, count: 1); |
7864 | QTest::qWait(ms: window->rootObject()->property(name: "duration" ).toInt() / 2); |
7865 | model.moveItems(from: 8, to: 3, count: 1); |
7866 | QTRY_VERIFY(listview->property("displaceTransitionsDone" ).toBool()); |
7867 | |
7868 | QVariantMap transitionsStarted = listview->property(name: "displaceTransitionsStarted" ).toMap(); |
7869 | foreach (const QString &name, transitionsStarted.keys()) { |
7870 | QVERIFY2(transitionsStarted[name] == 1, |
7871 | qPrintable(QString("%1 was displaced %2 times" ).arg(name).arg(transitionsStarted[name].toInt()))); |
7872 | } |
7873 | |
7874 | // verify all items moved to the correct final positions |
7875 | QList<QQuickItem*> items = findItems<QQuickItem>(parent: contentItem, objectName: "wrapper" ); |
7876 | for (int i=0; i < model.count() && i < items.count(); ++i) { |
7877 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
7878 | QVERIFY2(item, qPrintable(QString("Item %1 not found" ).arg(i))); |
7879 | QTRY_COMPARE(item->x(), 0.0); |
7880 | QTRY_COMPARE(item->y(), i*20.0); |
7881 | QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "textName" , index: i); |
7882 | QVERIFY(name != nullptr); |
7883 | QTRY_COMPARE(name->text(), model.name(i)); |
7884 | } |
7885 | |
7886 | releaseView(view: window); |
7887 | } |
7888 | |
7889 | QList<int> tst_QQuickListView::toIntList(const QVariantList &list) |
7890 | { |
7891 | QList<int> ret; |
7892 | bool ok = true; |
7893 | for (int i=0; i<list.count(); i++) { |
7894 | ret << list[i].toInt(ok: &ok); |
7895 | if (!ok) |
7896 | qWarning() << "tst_QQuickListView::toIntList(): not a number:" << list[i]; |
7897 | } |
7898 | |
7899 | return ret; |
7900 | } |
7901 | |
7902 | void tst_QQuickListView::matchIndexLists(const QVariantList &indexLists, const QList<int> &expectedIndexes) |
7903 | { |
7904 | const QSet<int> expectedIndexSet(expectedIndexes.cbegin(), expectedIndexes.cend()); |
7905 | for (int i=0; i<indexLists.count(); i++) { |
7906 | const auto ¤tList = indexLists[i].value<QList<int> >(); |
7907 | const QSet<int> current(currentList.cbegin(), currentList.cend()); |
7908 | if (current != expectedIndexSet) |
7909 | qDebug() << "Cannot match actual targets" << current << "with expected" << expectedIndexes; |
7910 | QCOMPARE(current, expectedIndexSet); |
7911 | } |
7912 | } |
7913 | |
7914 | void tst_QQuickListView::matchItemsAndIndexes(const QVariantMap &items, const QaimModel &model, const QList<int> &expectedIndexes) |
7915 | { |
7916 | for (QVariantMap::const_iterator it = items.begin(); it != items.end(); ++it) { |
7917 | QCOMPARE(it.value().type(), QVariant::Int); |
7918 | QString name = it.key(); |
7919 | int itemIndex = it.value().toInt(); |
7920 | QVERIFY2(expectedIndexes.contains(itemIndex), qPrintable(QString("Index %1 not found in expectedIndexes" ).arg(itemIndex))); |
7921 | if (model.name(index: itemIndex) != name) |
7922 | qDebug() << itemIndex; |
7923 | QCOMPARE(model.name(itemIndex), name); |
7924 | } |
7925 | QCOMPARE(items.count(), expectedIndexes.count()); |
7926 | } |
7927 | |
7928 | void tst_QQuickListView::matchItemLists(const QVariantList &itemLists, const QList<QQuickItem *> &expectedItems) |
7929 | { |
7930 | for (int i=0; i<itemLists.count(); i++) { |
7931 | QCOMPARE(itemLists[i].type(), QVariant::List); |
7932 | QVariantList current = itemLists[i].toList(); |
7933 | for (int j=0; j<current.count(); j++) { |
7934 | QQuickItem *o = qobject_cast<QQuickItem*>(object: current[j].value<QObject*>()); |
7935 | QVERIFY2(o, qPrintable(QString("Invalid actual item at %1" ).arg(j))); |
7936 | QVERIFY2(expectedItems.contains(o), qPrintable(QString("Cannot match item %1" ).arg(j))); |
7937 | } |
7938 | QCOMPARE(current.count(), expectedItems.count()); |
7939 | } |
7940 | } |
7941 | |
7942 | void tst_QQuickListView::flickBeyondBounds() |
7943 | { |
7944 | QScopedPointer<QQuickView> window(createView()); |
7945 | QQuickViewTestUtil::moveMouseAway(window: window.data()); |
7946 | |
7947 | window->setSource(testFileUrl(fileName: "flickBeyondBoundsBug.qml" )); |
7948 | window->show(); |
7949 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
7950 | |
7951 | |
7952 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
7953 | QTRY_VERIFY(listview != nullptr); |
7954 | |
7955 | QQuickItem *contentItem = listview->contentItem(); |
7956 | QTRY_VERIFY(contentItem != nullptr); |
7957 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
7958 | |
7959 | // Flick view up beyond bounds |
7960 | flick(window: window.data(), from: QPoint(10, 10), to: QPoint(10, -2000), duration: 180); |
7961 | #ifdef Q_OS_MAC |
7962 | QSKIP("Disabled due to flaky behavior on CI system (QTBUG-44493)" ); |
7963 | QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper" ).count(), 0); |
7964 | #endif |
7965 | |
7966 | // We're really testing that we don't get stuck in a loop, |
7967 | // but also confirm items positioned correctly. |
7968 | QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper" ).count(), 2); |
7969 | for (int i = 0; i < 2; ++i) { |
7970 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: i); |
7971 | if (!item) qWarning() << "Item" << i << "not found" ; |
7972 | QTRY_VERIFY(item); |
7973 | QTRY_COMPARE(item->y(), qreal(i*45)); |
7974 | } |
7975 | } |
7976 | |
7977 | void tst_QQuickListView::flickBothDirections() |
7978 | { |
7979 | QFETCH(bool, initValues); |
7980 | QFETCH(QQuickListView::Orientation, orientation); |
7981 | QFETCH(QQuickFlickable::FlickableDirection, flickableDirection); |
7982 | QFETCH(qreal, contentWidth); |
7983 | QFETCH(qreal, contentHeight); |
7984 | QFETCH(QPointF, targetPos); |
7985 | |
7986 | QQuickView *window = getView(); |
7987 | QQuickViewTestUtil::moveMouseAway(window); |
7988 | |
7989 | QQmlContext *ctxt = window->rootContext(); |
7990 | ctxt->setContextProperty("initialOrientation" , initValues ? orientation : QQuickListView::Vertical); |
7991 | ctxt->setContextProperty("initialFlickableDirection" , initValues ? flickableDirection : QQuickFlickable::VerticalFlick); |
7992 | ctxt->setContextProperty("initialContentWidth" , initValues ? contentWidth : -1); |
7993 | ctxt->setContextProperty("initialContentHeight" , initValues ? contentHeight : -1); |
7994 | |
7995 | window->setSource(testFileUrl(fileName: "flickBothDirections.qml" )); |
7996 | window->show(); |
7997 | QVERIFY(QTest::qWaitForWindowActive(window)); |
7998 | |
7999 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
8000 | QVERIFY(listview); |
8001 | |
8002 | if (!initValues) { |
8003 | listview->setOrientation(orientation); |
8004 | listview->setFlickableDirection(flickableDirection); |
8005 | if (contentWidth > 0) |
8006 | listview->setContentWidth(contentWidth); |
8007 | if (contentHeight > 0) |
8008 | listview->setContentHeight(contentHeight); |
8009 | } |
8010 | |
8011 | flick(window, from: QPoint(100, 100), to: QPoint(25, 25), duration: 50); |
8012 | QVERIFY(listview->isMoving()); |
8013 | QTRY_VERIFY(!listview->isMoving()); |
8014 | QCOMPARE(listview->contentX(), targetPos.x()); |
8015 | QCOMPARE(listview->contentY(), targetPos.y()); |
8016 | } |
8017 | |
8018 | void tst_QQuickListView::flickBothDirections_data() |
8019 | { |
8020 | QTest::addColumn<bool>(name: "initValues" ); |
8021 | QTest::addColumn<QQuickListView::Orientation>(name: "orientation" ); |
8022 | QTest::addColumn<QQuickFlickable::FlickableDirection>(name: "flickableDirection" ); |
8023 | QTest::addColumn<qreal>(name: "contentWidth" ); |
8024 | QTest::addColumn<qreal>(name: "contentHeight" ); |
8025 | QTest::addColumn<QPointF>(name: "targetPos" ); |
8026 | |
8027 | // model: 20 |
8028 | // listview: 100x100 |
8029 | // vertical delegate: 120x20 -> contentHeight: 20x20=400 |
8030 | // horizontal delegate: 10x110 -> contentWidth: 20x10=200 |
8031 | |
8032 | QTest::newRow(dataTag: "init:vertical,-1" ) << true << QQuickListView::Vertical << QQuickFlickable::VerticalFlick << -1. << -1. << QPointF(0, 300); |
8033 | QTest::newRow(dataTag: "init:vertical,120" ) << true << QQuickListView::Vertical << QQuickFlickable::VerticalFlick<< 120. << -1. << QPointF(0, 300); |
8034 | QTest::newRow(dataTag: "init:vertical,auto,-1" ) << true << QQuickListView::Vertical << QQuickFlickable::AutoFlickDirection << -1. << -1. << QPointF(0, 300); |
8035 | QTest::newRow(dataTag: "init:vertical,auto,120" ) << true << QQuickListView::Vertical << QQuickFlickable::AutoFlickDirection << 120. << -1. << QPointF(20, 300); |
8036 | |
8037 | QTest::newRow(dataTag: "completed:vertical,-1" ) << false << QQuickListView::Vertical << QQuickFlickable::VerticalFlick << -1. << -1. << QPointF(0, 300); |
8038 | QTest::newRow(dataTag: "completed:vertical,120" ) << false << QQuickListView::Vertical << QQuickFlickable::VerticalFlick << 120. << -1. << QPointF(0, 300); |
8039 | QTest::newRow(dataTag: "completed:vertical,auto,-1" ) << false << QQuickListView::Vertical << QQuickListView::AutoFlickDirection << -1. << -1. << QPointF(0, 300); |
8040 | QTest::newRow(dataTag: "completed:vertical,auto,120" ) << false << QQuickListView::Vertical << QQuickListView::AutoFlickDirection << 120. << -1. << QPointF(20, 300); |
8041 | |
8042 | QTest::newRow(dataTag: "init:horizontal,-1" ) << true << QQuickListView::Horizontal << QQuickFlickable::HorizontalFlick << -1. << -1. << QPointF(100, 0); |
8043 | QTest::newRow(dataTag: "init:horizontal,110" ) << true << QQuickListView::Horizontal << QQuickFlickable::HorizontalFlick <<-1. << 110. << QPointF(100, 0); |
8044 | QTest::newRow(dataTag: "init:horizontal,auto,-1" ) << true << QQuickListView::Horizontal << QQuickListView::AutoFlickDirection << -1. << -1. << QPointF(100, 0); |
8045 | QTest::newRow(dataTag: "init:horizontal,auto,110" ) << true << QQuickListView::Horizontal << QQuickListView::AutoFlickDirection << -1. << 110. << QPointF(100, 10); |
8046 | |
8047 | QTest::newRow(dataTag: "completed:horizontal,-1" ) << false << QQuickListView::Horizontal << QQuickFlickable::HorizontalFlick << -1. << -1. << QPointF(100, 0); |
8048 | QTest::newRow(dataTag: "completed:horizontal,110" ) << false << QQuickListView::Horizontal << QQuickFlickable::HorizontalFlick << -1. << 110. << QPointF(100, 0); |
8049 | QTest::newRow(dataTag: "completed:horizontal,auto,-1" ) << false << QQuickListView::Horizontal << QQuickListView::AutoFlickDirection << -1. << -1. << QPointF(100, 0); |
8050 | QTest::newRow(dataTag: "completed:horizontal,auto,110" ) << false << QQuickListView::Horizontal << QQuickListView::AutoFlickDirection << -1. << 110. << QPointF(100, 10); |
8051 | } |
8052 | |
8053 | void tst_QQuickListView::destroyItemOnCreation() |
8054 | { |
8055 | QaimModel model; |
8056 | QScopedPointer<QQuickView> window(createView()); |
8057 | window->rootContext()->setContextProperty("testModel" , &model); |
8058 | |
8059 | window->setSource(testFileUrl(fileName: "destroyItemOnCreation.qml" )); |
8060 | window->show(); |
8061 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
8062 | |
8063 | |
8064 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
8065 | QVERIFY(listview != nullptr); |
8066 | |
8067 | QQuickItem *contentItem = listview->contentItem(); |
8068 | QVERIFY(contentItem != nullptr); |
8069 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
8070 | |
8071 | QCOMPARE(window->rootObject()->property("createdIndex" ).toInt(), -1); |
8072 | model.addItem(name: "new item" , number: "" ); |
8073 | QTRY_COMPARE(window->rootObject()->property("createdIndex" ).toInt(), 0); |
8074 | |
8075 | QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper" ).count(), 0); |
8076 | QCOMPARE(model.count(), 0); |
8077 | } |
8078 | |
8079 | void tst_QQuickListView::parentBinding() |
8080 | { |
8081 | QScopedPointer<QQuickView> window(createView()); |
8082 | QQmlTestMessageHandler messageHandler; |
8083 | |
8084 | window->setSource(testFileUrl(fileName: "parentBinding.qml" )); |
8085 | window->show(); |
8086 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
8087 | |
8088 | QQuickListView *listview = qobject_cast<QQuickListView*>(object: window->rootObject()); |
8089 | QVERIFY(listview != nullptr); |
8090 | |
8091 | QQuickItem *contentItem = listview->contentItem(); |
8092 | QVERIFY(contentItem != nullptr); |
8093 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
8094 | |
8095 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "wrapper" , index: 0); |
8096 | QVERIFY(item); |
8097 | QCOMPARE(item->width(), listview->width()); |
8098 | QCOMPARE(item->height(), listview->height()/12); |
8099 | |
8100 | // there should be no transient binding error |
8101 | QVERIFY2(messageHandler.messages().isEmpty(), qPrintable(messageHandler.messageString())); |
8102 | } |
8103 | |
8104 | void tst_QQuickListView::defaultHighlightMoveDuration() |
8105 | { |
8106 | QQmlEngine engine; |
8107 | QQmlComponent component(&engine); |
8108 | component.setData("import QtQuick 2.0; ListView {}" , baseUrl: QUrl::fromLocalFile(localfile: "" )); |
8109 | |
8110 | QScopedPointer<QObject> obj(component.create()); |
8111 | QVERIFY(obj); |
8112 | |
8113 | QCOMPARE(obj->property("highlightMoveDuration" ).toInt(), -1); |
8114 | } |
8115 | |
8116 | void tst_QQuickListView::accessEmptyCurrentItem_QTBUG_30227() |
8117 | { |
8118 | QScopedPointer<QQuickView> window(createView()); |
8119 | window->setSource(testFileUrl(fileName: "emptymodel.qml" )); |
8120 | |
8121 | QQuickListView *listview = window->rootObject()->findChild<QQuickListView*>(); |
8122 | QTRY_VERIFY(listview != nullptr); |
8123 | listview->forceLayout(); |
8124 | |
8125 | QMetaObject::invokeMethod(obj: window->rootObject(), member: "remove" ); |
8126 | QVERIFY(window->rootObject()->property("isCurrentItemNull" ).toBool()); |
8127 | |
8128 | QMetaObject::invokeMethod(obj: window->rootObject(), member: "add" ); |
8129 | QVERIFY(!window->rootObject()->property("isCurrentItemNull" ).toBool()); |
8130 | } |
8131 | |
8132 | void tst_QQuickListView::delayedChanges_QTBUG_30555() |
8133 | { |
8134 | QScopedPointer<QQuickView> window(createView()); |
8135 | window->setSource(testFileUrl(fileName: "delayedChanges.qml" )); |
8136 | |
8137 | QQuickListView *listview = window->rootObject()->findChild<QQuickListView*>(); |
8138 | QTRY_VERIFY(listview != nullptr); |
8139 | |
8140 | QCOMPARE(listview->count(), 10); |
8141 | |
8142 | //Takes two just like in the bug report |
8143 | QMetaObject::invokeMethod(obj: window->rootObject(), member: "takeTwo" ); |
8144 | QTRY_COMPARE(listview->count(), 8); |
8145 | |
8146 | QMetaObject::invokeMethod(obj: window->rootObject(), member: "takeTwo_sync" ); |
8147 | QCOMPARE(listview->count(), 6); |
8148 | } |
8149 | |
8150 | void tst_QQuickListView::outsideViewportChangeNotAffectingView() |
8151 | { |
8152 | QScopedPointer<QQuickView> window(createView()); |
8153 | QQuickViewTestUtil::moveMouseAway(window: window.data()); |
8154 | window->setSource(testFileUrl(fileName: "outsideViewportChangeNotAffectingView.qml" )); |
8155 | |
8156 | QQuickListView *listview = window->rootObject()->findChild<QQuickListView*>(); |
8157 | QTRY_VERIFY(listview != nullptr); |
8158 | |
8159 | window->show(); |
8160 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
8161 | |
8162 | listview->setContentY(1250); |
8163 | |
8164 | QTRY_COMPARE(listview->indexAt(0, listview->contentY()), 4); |
8165 | QTRY_COMPARE(listview->itemAt(0, listview->contentY())->y(), 1200.); |
8166 | |
8167 | QMetaObject::invokeMethod(obj: window->rootObject(), member: "resizeThirdItem" , Q_ARG(QVariant, 290)); |
8168 | QTRY_COMPARE(listview->indexAt(0, listview->contentY()), 4); |
8169 | QTRY_COMPARE(listview->itemAt(0, listview->contentY())->y(), 1200.); |
8170 | |
8171 | QMetaObject::invokeMethod(obj: window->rootObject(), member: "resizeThirdItem" , Q_ARG(QVariant, 300)); |
8172 | QTRY_COMPARE(listview->indexAt(0, listview->contentY()), 4); |
8173 | QTRY_COMPARE(listview->itemAt(0, listview->contentY())->y(), 1200.); |
8174 | |
8175 | QMetaObject::invokeMethod(obj: window->rootObject(), member: "resizeThirdItem" , Q_ARG(QVariant, 310)); |
8176 | QTRY_COMPARE(listview->indexAt(0, listview->contentY()), 4); |
8177 | QTRY_COMPARE(listview->itemAt(0, listview->contentY())->y(), 1200.); |
8178 | |
8179 | QMetaObject::invokeMethod(obj: window->rootObject(), member: "resizeThirdItem" , Q_ARG(QVariant, 400)); |
8180 | QTRY_COMPARE(listview->indexAt(0, listview->contentY()), 4); |
8181 | QTRY_COMPARE(listview->itemAt(0, listview->contentY())->y(), 1200.); |
8182 | } |
8183 | |
8184 | void tst_QQuickListView::testProxyModelChangedAfterMove() |
8185 | { |
8186 | QScopedPointer<QQuickView> window(createView()); |
8187 | QQuickViewTestUtil::moveMouseAway(window: window.data()); |
8188 | window->setSource(testFileUrl(fileName: "proxytest.qml" )); |
8189 | |
8190 | QQuickListView *listview = window->rootObject()->findChild<QQuickListView*>(); |
8191 | QTRY_VERIFY(listview != nullptr); |
8192 | |
8193 | window->show(); |
8194 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
8195 | |
8196 | QTRY_COMPARE(listview->count(), 3); |
8197 | } |
8198 | |
8199 | void tst_QQuickListView::typedModel() |
8200 | { |
8201 | QQmlEngine engine; |
8202 | QQmlComponent component(&engine, testFileUrl(fileName: "typedModel.qml" )); |
8203 | |
8204 | QScopedPointer<QObject> object(component.create()); |
8205 | |
8206 | QQuickListView *listview = qobject_cast<QQuickListView *>(object: object.data()); |
8207 | QVERIFY(listview); |
8208 | |
8209 | QCOMPARE(listview->count(), 6); |
8210 | |
8211 | QQmlListModel *listModel = nullptr; |
8212 | |
8213 | listview->setModel(QVariant::fromValue(value: listModel)); |
8214 | QCOMPARE(listview->count(), 0); |
8215 | } |
8216 | |
8217 | void tst_QQuickListView::displayMargin() |
8218 | { |
8219 | QScopedPointer<QQuickView> window(createView()); |
8220 | window->setSource(testFileUrl(fileName: "displayMargin.qml" )); |
8221 | window->show(); |
8222 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
8223 | |
8224 | QQuickListView *listview = window->rootObject()->findChild<QQuickListView*>(); |
8225 | QVERIFY(listview != nullptr); |
8226 | |
8227 | QQuickItem *content = listview->contentItem(); |
8228 | QVERIFY(content != nullptr); |
8229 | |
8230 | QQuickItem *item0 = findItem<QQuickItem>(parent: content, objectName: "delegate" , index: 0); |
8231 | QVERIFY(item0); |
8232 | QCOMPARE(delegateVisible(item0), true); |
8233 | |
8234 | // the 14th item should be within the end margin |
8235 | |
8236 | QQuickItem *item14 = findItem<QQuickItem>(parent: content, objectName: "delegate" , index: 13); |
8237 | QVERIFY(item14); |
8238 | QCOMPARE(delegateVisible(item14), true); |
8239 | |
8240 | // the 15th item should be outside the end margin |
8241 | QVERIFY(findItem<QQuickItem>(content, "delegate" , 14) == nullptr); |
8242 | |
8243 | // the first delegate should still be within the begin margin |
8244 | listview->positionViewAtIndex(index: 3, mode: QQuickListView::Beginning); |
8245 | QCOMPARE(delegateVisible(item0), true); |
8246 | |
8247 | // the first delegate should now be outside the begin margin |
8248 | listview->positionViewAtIndex(index: 4, mode: QQuickListView::Beginning); |
8249 | QCOMPARE(delegateVisible(item0), false); |
8250 | } |
8251 | |
8252 | void tst_QQuickListView::negativeDisplayMargin() |
8253 | { |
8254 | QScopedPointer<QQuickView> window(createView()); |
8255 | window->setSource(testFileUrl(fileName: "negativeDisplayMargin.qml" )); |
8256 | window->show(); |
8257 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
8258 | |
8259 | QQuickItem *listview = window->rootObject(); |
8260 | QQuickListView *innerList = findItem<QQuickListView>(parent: window->rootObject(), objectName: "innerList" ); |
8261 | QVERIFY(innerList != nullptr); |
8262 | |
8263 | QTRY_COMPARE(innerList->property("createdItems" ).toInt(), 11); |
8264 | QCOMPARE(innerList->property("destroyedItem" ).toInt(), 0); |
8265 | |
8266 | QQuickItem *content = innerList->contentItem(); |
8267 | QVERIFY(content != nullptr); |
8268 | |
8269 | QQuickItem *item = findItem<QQuickItem>(parent: content, objectName: "delegate" , index: 0); |
8270 | QVERIFY(item); |
8271 | QCOMPARE(delegateVisible(item), true); |
8272 | |
8273 | item = findItem<QQuickItem>(parent: content, objectName: "delegate" , index: 7); |
8274 | QVERIFY(item); |
8275 | QCOMPARE(delegateVisible(item), true); |
8276 | |
8277 | item = findItem<QQuickItem>(parent: content, objectName: "delegate" , index: 8); |
8278 | QVERIFY(item); |
8279 | QCOMPARE(delegateVisible(item), false); |
8280 | |
8281 | // Flick until contentY means that delegate8 should be visible |
8282 | listview->setProperty(name: "contentY" , value: 500); |
8283 | item = findItem<QQuickItem>(parent: content, objectName: "delegate" , index: 8); |
8284 | QVERIFY(item); |
8285 | QTRY_COMPARE(delegateVisible(item), true); |
8286 | |
8287 | listview->setProperty(name: "contentY" , value: 1000); |
8288 | QTRY_VERIFY((item = findItem<QQuickItem>(content, "delegate" , 14))); |
8289 | QTRY_COMPARE(delegateVisible(item), true); |
8290 | |
8291 | listview->setProperty(name: "contentY" , value: 0); |
8292 | QTRY_VERIFY(item = findItem<QQuickItem>(content, "delegate" , 4)); |
8293 | QTRY_COMPARE(delegateVisible(item), true); |
8294 | } |
8295 | |
8296 | void tst_QQuickListView::highlightItemGeometryChanges() |
8297 | { |
8298 | QScopedPointer<QQuickView> window(createView()); |
8299 | window->setSource(testFileUrl(fileName: "HighlightResize.qml" )); |
8300 | window->show(); |
8301 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
8302 | |
8303 | QQuickListView *listview = qobject_cast<QQuickListView *>(object: window->rootObject()); |
8304 | QVERIFY(listview); |
8305 | |
8306 | QCOMPARE(listview->count(), 5); |
8307 | |
8308 | for (int i = 0; i < listview->count(); ++i) { |
8309 | listview->setCurrentIndex(i); |
8310 | QTRY_COMPARE(listview->highlightItem()->width(), qreal(100 + i * 20)); |
8311 | QTRY_COMPARE(listview->highlightItem()->height(), qreal(100 + i * 10)); |
8312 | } |
8313 | } |
8314 | |
8315 | void tst_QQuickListView::QTBUG_36481() |
8316 | { |
8317 | QQmlEngine engine; |
8318 | QQmlComponent component(&engine, testFileUrl(fileName: "headerCrash.qml" )); |
8319 | |
8320 | // just testing that we don't crash when creating |
8321 | QScopedPointer<QObject> object(component.create()); |
8322 | } |
8323 | |
8324 | void tst_QQuickListView::QTBUG_35920() |
8325 | { |
8326 | QScopedPointer<QQuickView> window(createView()); |
8327 | window->setSource(testFileUrl(fileName: "qtbug35920.qml" )); |
8328 | window->show(); |
8329 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
8330 | |
8331 | QQuickListView *listview = qobject_cast<QQuickListView *>(object: window->rootObject()); |
8332 | QVERIFY(listview); |
8333 | |
8334 | QTest::mousePress(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(10,0)); |
8335 | for (int i = 0; i < 100; ++i) { |
8336 | QTest::mouseMove(window: window.data(), pos: QPoint(10,i)); |
8337 | if (listview->isMoving()) { |
8338 | // do not fixup() the position while in movement to avoid flicker |
8339 | const qreal contentY = listview->contentY(); |
8340 | listview->setPreferredHighlightBegin(i); |
8341 | QCOMPARE(listview->contentY(), contentY); |
8342 | listview->resetPreferredHighlightBegin(); |
8343 | QCOMPARE(listview->contentY(), contentY); |
8344 | |
8345 | listview->setPreferredHighlightEnd(i+10); |
8346 | QCOMPARE(listview->contentY(), contentY); |
8347 | listview->resetPreferredHighlightEnd(); |
8348 | QCOMPARE(listview->contentY(), contentY); |
8349 | } |
8350 | } |
8351 | QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(10,100)); |
8352 | } |
8353 | |
8354 | Q_DECLARE_METATYPE(Qt::Orientation) |
8355 | |
8356 | void tst_QQuickListView::stickyPositioning() |
8357 | { |
8358 | QFETCH(QString, fileName); |
8359 | |
8360 | QFETCH(Qt::Orientation, orientation); |
8361 | QFETCH(Qt::LayoutDirection, layoutDirection); |
8362 | QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection); |
8363 | |
8364 | QFETCH(int, positionIndex); |
8365 | QFETCH(QQuickItemView::PositionMode, positionMode); |
8366 | QFETCH(QList<QPointF>, movement); |
8367 | |
8368 | QFETCH(QPointF, ); |
8369 | QFETCH(QPointF, ); |
8370 | |
8371 | QQuickView *window = getView(); |
8372 | |
8373 | QaimModel model; |
8374 | for (int i = 0; i < 20; i++) |
8375 | model.addItem(name: QString::number(i), number: QString::number(i/10)); |
8376 | |
8377 | QQmlContext *ctxt = window->rootContext(); |
8378 | ctxt->setContextProperty("testModel" , &model); |
8379 | ctxt->setContextProperty("testOrientation" , orientation); |
8380 | ctxt->setContextProperty("testLayoutDirection" , layoutDirection); |
8381 | ctxt->setContextProperty("testVerticalLayoutDirection" , verticalLayoutDirection); |
8382 | |
8383 | window->setSource(testFileUrl(fileName)); |
8384 | window->show(); |
8385 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
8386 | |
8387 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
8388 | QVERIFY(listview); |
8389 | |
8390 | QQuickItem *contentItem = listview->contentItem(); |
8391 | QVERIFY(contentItem); |
8392 | |
8393 | listview->positionViewAtIndex(index: positionIndex, mode: positionMode); |
8394 | |
8395 | foreach (const QPointF &offset, movement) { |
8396 | listview->setContentX(listview->contentX() + offset.x()); |
8397 | listview->setContentY(listview->contentY() + offset.y()); |
8398 | } |
8399 | |
8400 | if (listview->header()) { |
8401 | QQuickItem * = listview->headerItem(); |
8402 | QVERIFY(headerItem); |
8403 | QPointF actualPos = listview->mapFromItem(item: contentItem, point: headerItem->position()); |
8404 | QCOMPARE(actualPos, headerPos); |
8405 | } |
8406 | |
8407 | if (listview->footer()) { |
8408 | QQuickItem * = listview->footerItem(); |
8409 | QVERIFY(footerItem); |
8410 | QPointF actualPos = listview->mapFromItem(item: contentItem, point: footerItem->position()); |
8411 | QCOMPARE(actualPos, footerPos); |
8412 | } |
8413 | |
8414 | releaseView(view: window); |
8415 | } |
8416 | |
8417 | void tst_QQuickListView::stickyPositioning_data() |
8418 | { |
8419 | qRegisterMetaType<Qt::Orientation>(); |
8420 | qRegisterMetaType<Qt::LayoutDirection>(); |
8421 | qRegisterMetaType<QQuickItemView::VerticalLayoutDirection>(); |
8422 | qRegisterMetaType<QQuickItemView::PositionMode>(); |
8423 | |
8424 | QTest::addColumn<QString>(name: "fileName" ); |
8425 | |
8426 | QTest::addColumn<Qt::Orientation>(name: "orientation" ); |
8427 | QTest::addColumn<Qt::LayoutDirection>(name: "layoutDirection" ); |
8428 | QTest::addColumn<QQuickItemView::VerticalLayoutDirection>(name: "verticalLayoutDirection" ); |
8429 | |
8430 | QTest::addColumn<int>(name: "positionIndex" ); |
8431 | QTest::addColumn<QQuickItemView::PositionMode>(name: "positionMode" ); |
8432 | QTest::addColumn<QList<QPointF> >(name: "movement" ); |
8433 | |
8434 | QTest::addColumn<QPointF>(name: "headerPos" ); |
8435 | QTest::addColumn<QPointF>(name: "footerPos" ); |
8436 | |
8437 | // header at the top |
8438 | QTest::newRow(dataTag: "top header" ) << "stickyPositioning-header.qml" |
8439 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::TopToBottom |
8440 | << 0 << QQuickItemView::Beginning << QList<QPointF>() |
8441 | << QPointF(0,0) << QPointF(); |
8442 | |
8443 | QTest::newRow(dataTag: "top header: 1/2 up" ) << "stickyPositioning-header.qml" |
8444 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::TopToBottom |
8445 | << 1 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(0,-5)) |
8446 | << QPointF(0,-5) << QPointF(); |
8447 | |
8448 | QTest::newRow(dataTag: "top header: up" ) << "stickyPositioning-header.qml" |
8449 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::TopToBottom |
8450 | << 2 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(0,-15)) |
8451 | << QPointF(0,0) << QPointF(); |
8452 | |
8453 | QTest::newRow(dataTag: "top header: 1/2 down" ) << "stickyPositioning-header.qml" |
8454 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::TopToBottom |
8455 | << 3 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(0,-15) << QPointF(0,5)) |
8456 | << QPointF(0,-5) << QPointF(); |
8457 | |
8458 | QTest::newRow(dataTag: "top header: down" ) << "stickyPositioning-header.qml" |
8459 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::TopToBottom |
8460 | << 4 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(0,-15) << QPointF(0,10)) |
8461 | << QPointF(0,-10) << QPointF(); |
8462 | |
8463 | |
8464 | // footer at the top |
8465 | QTest::newRow(dataTag: "top footer" ) << "stickyPositioning-footer.qml" |
8466 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::BottomToTop |
8467 | << 19 << QQuickItemView::End << QList<QPointF>() |
8468 | << QPointF() << QPointF(0,0); |
8469 | |
8470 | QTest::newRow(dataTag: "top footer: 1/2 up" ) << "stickyPositioning-footer.qml" |
8471 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::BottomToTop |
8472 | << 18 << QQuickItemView::End << (QList<QPointF>() << QPointF(0,-5)) |
8473 | << QPointF() << QPointF(0,-5); |
8474 | |
8475 | QTest::newRow(dataTag: "top footer: up" ) << "stickyPositioning-footer.qml" |
8476 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::BottomToTop |
8477 | << 17 << QQuickItemView::End << (QList<QPointF>() << QPointF(0,-15)) |
8478 | << QPointF() << QPointF(0,0); |
8479 | |
8480 | QTest::newRow(dataTag: "top footer: 1/2 down" ) << "stickyPositioning-footer.qml" |
8481 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::BottomToTop |
8482 | << 16 << QQuickItemView::End << (QList<QPointF>() << QPointF(0,-15) << QPointF(0,5)) |
8483 | << QPointF() << QPointF(0,-5); |
8484 | |
8485 | QTest::newRow(dataTag: "top footer: down" ) << "stickyPositioning-footer.qml" |
8486 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::BottomToTop |
8487 | << 15 << QQuickItemView::End << (QList<QPointF>() << QPointF(0,-15) << QPointF(0,10)) |
8488 | << QPointF() << QPointF(0,-10); |
8489 | |
8490 | |
8491 | // header at the bottom |
8492 | QTest::newRow(dataTag: "bottom header" ) << "stickyPositioning-header.qml" |
8493 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::BottomToTop |
8494 | << 0 << QQuickItemView::Beginning << QList<QPointF>() |
8495 | << QPointF(0,90) << QPointF(); |
8496 | |
8497 | QTest::newRow(dataTag: "bottom header: 1/2 down" ) << "stickyPositioning-header.qml" |
8498 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::BottomToTop |
8499 | << 1 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(0,5)) |
8500 | << QPointF(0,95) << QPointF(); |
8501 | |
8502 | QTest::newRow(dataTag: "bottom header: down" ) << "stickyPositioning-header.qml" |
8503 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::BottomToTop |
8504 | << 2 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(0,15)) |
8505 | << QPointF(0,90) << QPointF(); |
8506 | |
8507 | QTest::newRow(dataTag: "bottom header: 1/2 up" ) << "stickyPositioning-header.qml" |
8508 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::BottomToTop |
8509 | << 3 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(0,15) << QPointF(0,-5)) |
8510 | << QPointF(0,95) << QPointF(); |
8511 | |
8512 | QTest::newRow(dataTag: "bottom header: up" ) << "stickyPositioning-header.qml" |
8513 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::BottomToTop |
8514 | << 4 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(0,15) << QPointF(0,-10)) |
8515 | << QPointF(0,100) << QPointF(); |
8516 | |
8517 | |
8518 | // footer at the bottom |
8519 | QTest::newRow(dataTag: "bottom footer" ) << "stickyPositioning-footer.qml" |
8520 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::TopToBottom |
8521 | << 19 << QQuickItemView::End << QList<QPointF>() |
8522 | << QPointF() << QPointF(0,90); |
8523 | |
8524 | QTest::newRow(dataTag: "bottom footer: 1/2 down" ) << "stickyPositioning-footer.qml" |
8525 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::TopToBottom |
8526 | << 18 << QQuickItemView::End << (QList<QPointF>() << QPointF(0,5)) |
8527 | << QPointF() << QPointF(0,95); |
8528 | |
8529 | QTest::newRow(dataTag: "bottom footer: down" ) << "stickyPositioning-footer.qml" |
8530 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::TopToBottom |
8531 | << 17 << QQuickItemView::End << (QList<QPointF>() << QPointF(0,15)) |
8532 | << QPointF() << QPointF(0,90); |
8533 | |
8534 | QTest::newRow(dataTag: "bottom footer: 1/2 up" ) << "stickyPositioning-footer.qml" |
8535 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::TopToBottom |
8536 | << 16 << QQuickItemView::End << (QList<QPointF>() << QPointF(0,15) << QPointF(0,-5)) |
8537 | << QPointF() << QPointF(0,95); |
8538 | |
8539 | QTest::newRow(dataTag: "bottom footer: up" ) << "stickyPositioning-footer.qml" |
8540 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::TopToBottom |
8541 | << 15 << QQuickItemView::End << (QList<QPointF>() << QPointF(0,15) << QPointF(0,-10)) |
8542 | << QPointF() << QPointF(0,100); |
8543 | |
8544 | |
8545 | // header at the top (& footer at the bottom) |
8546 | QTest::newRow(dataTag: "top header & bottom footer" ) << "stickyPositioning-both.qml" |
8547 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::TopToBottom |
8548 | << 0 << QQuickItemView::Beginning << QList<QPointF>() |
8549 | << QPointF(0,0) << QPointF(0,100); |
8550 | |
8551 | QTest::newRow(dataTag: "top header & bottom footer: 1/2 up" ) << "stickyPositioning-both.qml" |
8552 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::TopToBottom |
8553 | << 1 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(0,-5)) |
8554 | << QPointF(0,-5) << QPointF(0,95); |
8555 | |
8556 | QTest::newRow(dataTag: "top header & bottom footer: up" ) << "stickyPositioning-both.qml" |
8557 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::TopToBottom |
8558 | << 2 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(0,-15)) |
8559 | << QPointF(0,0) << QPointF(0,100); |
8560 | |
8561 | QTest::newRow(dataTag: "top header & bottom footer: 1/2 down" ) << "stickyPositioning-both.qml" |
8562 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::TopToBottom |
8563 | << 3 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(0,-15) << QPointF(0,5)) |
8564 | << QPointF(0,-5) << QPointF(0,95); |
8565 | |
8566 | QTest::newRow(dataTag: "top header & bottom footer: down" ) << "stickyPositioning-both.qml" |
8567 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::TopToBottom |
8568 | << 4 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(0,-15) << QPointF(0,10)) |
8569 | << QPointF(0,-10) << QPointF(0,90); |
8570 | |
8571 | |
8572 | // footer at the bottom (& header at the top) |
8573 | QTest::newRow(dataTag: "bottom footer & top header" ) << "stickyPositioning-both.qml" |
8574 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::TopToBottom |
8575 | << 1 << QQuickItemView::Beginning << QList<QPointF>() |
8576 | << QPointF(0,-10) << QPointF(0,90); |
8577 | |
8578 | QTest::newRow(dataTag: "bottom footer & top header: 1/2 down" ) << "stickyPositioning-both.qml" |
8579 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::TopToBottom |
8580 | << 1 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(0,5)) |
8581 | << QPointF(0,-10) << QPointF(0,90); |
8582 | |
8583 | QTest::newRow(dataTag: "bottom footer & top header: down" ) << "stickyPositioning-both.qml" |
8584 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::TopToBottom |
8585 | << 2 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(0,15)) |
8586 | << QPointF(0,-10) << QPointF(0,90); |
8587 | |
8588 | QTest::newRow(dataTag: "bottom footer & top header: 1/2 up" ) << "stickyPositioning-both.qml" |
8589 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::TopToBottom |
8590 | << 3 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(0,15) << QPointF(0,-5)) |
8591 | << QPointF(0,-5) << QPointF(0,95); |
8592 | |
8593 | QTest::newRow(dataTag: "bottom footer & top header: up" ) << "stickyPositioning-both.qml" |
8594 | << Qt::Vertical << Qt::LeftToRight << QQuickListView::TopToBottom |
8595 | << 4 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(0,15) << QPointF(0,-10)) |
8596 | << QPointF(0,0) << QPointF(0,100); |
8597 | |
8598 | // header on the left |
8599 | QTest::newRow(dataTag: "left header" ) << "stickyPositioning-header.qml" |
8600 | << Qt::Horizontal << Qt::LeftToRight << QQuickListView::TopToBottom |
8601 | << 0 << QQuickItemView::Beginning << QList<QPointF>() |
8602 | << QPointF(0,0) << QPointF(); |
8603 | |
8604 | QTest::newRow(dataTag: "left header: 1/2 left" ) << "stickyPositioning-header.qml" |
8605 | << Qt::Horizontal << Qt::LeftToRight << QQuickListView::TopToBottom |
8606 | << 1 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(-5,0)) |
8607 | << QPointF(-5,0) << QPointF(); |
8608 | |
8609 | QTest::newRow(dataTag: "left header: left" ) << "stickyPositioning-header.qml" |
8610 | << Qt::Horizontal << Qt::LeftToRight << QQuickListView::TopToBottom |
8611 | << 2 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(-15,0)) |
8612 | << QPointF(0,0) << QPointF(); |
8613 | |
8614 | QTest::newRow(dataTag: "left header: 1/2 right" ) << "stickyPositioning-header.qml" |
8615 | << Qt::Horizontal << Qt::LeftToRight << QQuickListView::TopToBottom |
8616 | << 3 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(-15,0) << QPointF(5,0)) |
8617 | << QPointF(-5,0) << QPointF(); |
8618 | |
8619 | QTest::newRow(dataTag: "left header: right" ) << "stickyPositioning-header.qml" |
8620 | << Qt::Horizontal << Qt::LeftToRight << QQuickListView::TopToBottom |
8621 | << 4 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(-15,0) << QPointF(10,0)) |
8622 | << QPointF(-10,0) << QPointF(); |
8623 | |
8624 | |
8625 | // footer on the left |
8626 | QTest::newRow(dataTag: "left footer" ) << "stickyPositioning-footer.qml" |
8627 | << Qt::Horizontal << Qt::RightToLeft << QQuickListView::TopToBottom |
8628 | << 19 << QQuickItemView::End << QList<QPointF>() |
8629 | << QPointF() << QPointF(0,0); |
8630 | |
8631 | QTest::newRow(dataTag: "left footer: 1/2 left" ) << "stickyPositioning-footer.qml" |
8632 | << Qt::Horizontal << Qt::RightToLeft << QQuickListView::TopToBottom |
8633 | << 18 << QQuickItemView::End << (QList<QPointF>() << QPointF(-5,0)) |
8634 | << QPointF() << QPointF(-5,0); |
8635 | |
8636 | QTest::newRow(dataTag: "left footer: left" ) << "stickyPositioning-footer.qml" |
8637 | << Qt::Horizontal << Qt::RightToLeft << QQuickListView::TopToBottom |
8638 | << 17 << QQuickItemView::End << (QList<QPointF>() << QPointF(-15,0)) |
8639 | << QPointF() << QPointF(0,0); |
8640 | |
8641 | QTest::newRow(dataTag: "left footer: 1/2 right" ) << "stickyPositioning-footer.qml" |
8642 | << Qt::Horizontal << Qt::RightToLeft << QQuickListView::TopToBottom |
8643 | << 16 << QQuickItemView::End << (QList<QPointF>() << QPointF(-15,0) << QPointF(5,0)) |
8644 | << QPointF() << QPointF(-5,0); |
8645 | |
8646 | QTest::newRow(dataTag: "left footer: right" ) << "stickyPositioning-footer.qml" |
8647 | << Qt::Horizontal << Qt::RightToLeft << QQuickListView::TopToBottom |
8648 | << 15 << QQuickItemView::End << (QList<QPointF>() << QPointF(-15,0) << QPointF(10,0)) |
8649 | << QPointF() << QPointF(-10,0); |
8650 | |
8651 | |
8652 | // header on the right |
8653 | QTest::newRow(dataTag: "right header" ) << "stickyPositioning-header.qml" |
8654 | << Qt::Horizontal << Qt::RightToLeft << QQuickListView::TopToBottom |
8655 | << 0 << QQuickItemView::Beginning << QList<QPointF>() |
8656 | << QPointF(90,0) << QPointF(); |
8657 | |
8658 | QTest::newRow(dataTag: "right header: 1/2 right" ) << "stickyPositioning-header.qml" |
8659 | << Qt::Horizontal << Qt::RightToLeft << QQuickListView::TopToBottom |
8660 | << 1 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(5,0)) |
8661 | << QPointF(95,0) << QPointF(); |
8662 | |
8663 | QTest::newRow(dataTag: "right header: right" ) << "stickyPositioning-header.qml" |
8664 | << Qt::Horizontal << Qt::RightToLeft << QQuickListView::TopToBottom |
8665 | << 2 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(15,0)) |
8666 | << QPointF(90,0) << QPointF(); |
8667 | |
8668 | QTest::newRow(dataTag: "right header: 1/2 left" ) << "stickyPositioning-header.qml" |
8669 | << Qt::Horizontal << Qt::RightToLeft << QQuickListView::TopToBottom |
8670 | << 3 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(15,0) << QPointF(-5,0)) |
8671 | << QPointF(95,0) << QPointF(); |
8672 | |
8673 | QTest::newRow(dataTag: "right header: left" ) << "stickyPositioning-header.qml" |
8674 | << Qt::Horizontal << Qt::RightToLeft << QQuickListView::TopToBottom |
8675 | << 4 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(15,0) << QPointF(-10,0)) |
8676 | << QPointF(100,0) << QPointF(); |
8677 | |
8678 | |
8679 | // footer on the right |
8680 | QTest::newRow(dataTag: "right footer" ) << "stickyPositioning-footer.qml" |
8681 | << Qt::Horizontal << Qt::LeftToRight << QQuickListView::TopToBottom |
8682 | << 19 << QQuickItemView::End << QList<QPointF>() |
8683 | << QPointF() << QPointF(90,0); |
8684 | |
8685 | QTest::newRow(dataTag: "right footer: 1/2 right" ) << "stickyPositioning-footer.qml" |
8686 | << Qt::Horizontal << Qt::LeftToRight << QQuickListView::TopToBottom |
8687 | << 18 << QQuickItemView::End << (QList<QPointF>() << QPointF(5,0)) |
8688 | << QPointF() << QPointF(95,0); |
8689 | |
8690 | QTest::newRow(dataTag: "right footer: right" ) << "stickyPositioning-footer.qml" |
8691 | << Qt::Horizontal << Qt::LeftToRight << QQuickListView::TopToBottom |
8692 | << 17 << QQuickItemView::End << (QList<QPointF>() << QPointF(15,0)) |
8693 | << QPointF() << QPointF(90,0); |
8694 | |
8695 | QTest::newRow(dataTag: "right footer: 1/2 left" ) << "stickyPositioning-footer.qml" |
8696 | << Qt::Horizontal << Qt::LeftToRight << QQuickListView::TopToBottom |
8697 | << 16 << QQuickItemView::End << (QList<QPointF>() << QPointF(15,0) << QPointF(-5,0)) |
8698 | << QPointF() << QPointF(95,0); |
8699 | |
8700 | QTest::newRow(dataTag: "right footer: left" ) << "stickyPositioning-footer.qml" |
8701 | << Qt::Horizontal << Qt::LeftToRight << QQuickListView::TopToBottom |
8702 | << 15 << QQuickItemView::End << (QList<QPointF>() << QPointF(15,0) << QPointF(-10,0)) |
8703 | << QPointF() << QPointF(100,0); |
8704 | |
8705 | |
8706 | // header on the left (& footer on the right) |
8707 | QTest::newRow(dataTag: "left header & right footer" ) << "stickyPositioning-both.qml" |
8708 | << Qt::Horizontal << Qt::LeftToRight << QQuickListView::TopToBottom |
8709 | << 0 << QQuickItemView::Beginning << QList<QPointF>() |
8710 | << QPointF(0,0) << QPointF(100,0); |
8711 | |
8712 | QTest::newRow(dataTag: "left header & right footer: 1/2 left" ) << "stickyPositioning-both.qml" |
8713 | << Qt::Horizontal << Qt::LeftToRight << QQuickListView::TopToBottom |
8714 | << 1 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(-5,0)) |
8715 | << QPointF(-5,0) << QPointF(95,0); |
8716 | |
8717 | QTest::newRow(dataTag: "left header & right footer: left" ) << "stickyPositioning-both.qml" |
8718 | << Qt::Horizontal << Qt::LeftToRight << QQuickListView::TopToBottom |
8719 | << 2 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(-15,0)) |
8720 | << QPointF(0,0) << QPointF(100,0); |
8721 | |
8722 | QTest::newRow(dataTag: "left header & right footer: 1/2 right" ) << "stickyPositioning-both.qml" |
8723 | << Qt::Horizontal << Qt::LeftToRight << QQuickListView::TopToBottom |
8724 | << 3 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(-15,0) << QPointF(5,0)) |
8725 | << QPointF(-5,0) << QPointF(95,0); |
8726 | |
8727 | QTest::newRow(dataTag: "left header & right footer: right" ) << "stickyPositioning-both.qml" |
8728 | << Qt::Horizontal << Qt::LeftToRight << QQuickListView::TopToBottom |
8729 | << 4 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(-15,0) << QPointF(10,0)) |
8730 | << QPointF(-10,0) << QPointF(90,0); |
8731 | |
8732 | |
8733 | // footer on the right (& header on the left) |
8734 | QTest::newRow(dataTag: "right footer & left header" ) << "stickyPositioning-both.qml" |
8735 | << Qt::Horizontal << Qt::LeftToRight << QQuickListView::TopToBottom |
8736 | << 1 << QQuickItemView::Beginning << QList<QPointF>() |
8737 | << QPointF(-10,0) << QPointF(90,0); |
8738 | |
8739 | QTest::newRow(dataTag: "right footer & left header: 1/2 right" ) << "stickyPositioning-both.qml" |
8740 | << Qt::Horizontal << Qt::LeftToRight << QQuickListView::TopToBottom |
8741 | << 1 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(5,0)) |
8742 | << QPointF(-10,0) << QPointF(90,0); |
8743 | |
8744 | QTest::newRow(dataTag: "right footer & left header: right" ) << "stickyPositioning-both.qml" |
8745 | << Qt::Horizontal << Qt::LeftToRight << QQuickListView::TopToBottom |
8746 | << 2 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(15,0)) |
8747 | << QPointF(-10,0) << QPointF(90,0); |
8748 | |
8749 | QTest::newRow(dataTag: "right footer & left header: 1/2 left" ) << "stickyPositioning-both.qml" |
8750 | << Qt::Horizontal << Qt::LeftToRight << QQuickListView::TopToBottom |
8751 | << 3 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(15,0) << QPointF(-5,0)) |
8752 | << QPointF(-5,0) << QPointF(95,0); |
8753 | |
8754 | QTest::newRow(dataTag: "right footer & left header: left" ) << "stickyPositioning-both.qml" |
8755 | << Qt::Horizontal << Qt::LeftToRight << QQuickListView::TopToBottom |
8756 | << 4 << QQuickItemView::Beginning << (QList<QPointF>() << QPointF(15,0) << QPointF(-10,0)) |
8757 | << QPointF(0,0) << QPointF(100,0); |
8758 | } |
8759 | |
8760 | void tst_QQuickListView::roundingErrors() |
8761 | { |
8762 | QFETCH(bool, pixelAligned); |
8763 | |
8764 | QScopedPointer<QQuickView> window(createView()); |
8765 | window->setSource(testFileUrl(fileName: "roundingErrors.qml" )); |
8766 | window->show(); |
8767 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
8768 | |
8769 | QQuickListView *listview = qobject_cast<QQuickListView *>(object: window->rootObject()); |
8770 | QVERIFY(listview); |
8771 | listview->setPixelAligned(pixelAligned); |
8772 | listview->positionViewAtIndex(index: 20, mode: QQuickListView::Beginning); |
8773 | |
8774 | QQuickItem *content = listview->contentItem(); |
8775 | QVERIFY(content); |
8776 | |
8777 | const QPoint viewPos(150, 36); |
8778 | const QPointF contentPos = content->mapFromItem(item: listview, point: viewPos); |
8779 | |
8780 | QPointer<QQuickItem> item = listview->itemAt(x: contentPos.x(), y: contentPos.y()); |
8781 | QVERIFY(item); |
8782 | |
8783 | // QTBUG-37339: drag an item and verify that it doesn't |
8784 | // get prematurely released due to rounding errors |
8785 | QTest::mousePress(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: viewPos); |
8786 | for (int i = 0; i < 150; i += 5) { |
8787 | QTest::mouseMove(window: window.data(), pos: viewPos - QPoint(i, 0)); |
8788 | QVERIFY(item); |
8789 | } |
8790 | QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(0, 36)); |
8791 | |
8792 | // maintain position relative to the right edge |
8793 | listview->setLayoutDirection(Qt::RightToLeft); |
8794 | const qreal contentX = listview->contentX(); |
8795 | listview->setContentX(contentX + 0.2); |
8796 | QCOMPARE(listview->contentX(), pixelAligned ? qRound(contentX + 0.2) : contentX + 0.2); |
8797 | listview->setWidth(listview->width() - 0.2); |
8798 | QCOMPARE(listview->contentX(), pixelAligned ? qRound(contentX + 0.2) : contentX + 0.2); |
8799 | |
8800 | // maintain position relative to the bottom edge |
8801 | listview->setOrientation(QQuickListView::Vertical); |
8802 | listview->setVerticalLayoutDirection(QQuickListView::BottomToTop); |
8803 | const qreal contentY = listview->contentY(); |
8804 | listview->setContentY(contentY + 0.2); |
8805 | QCOMPARE(listview->contentY(), pixelAligned ? qRound(contentY + 0.2) : contentY + 0.2); |
8806 | listview->setHeight(listview->height() - 0.2); |
8807 | QCOMPARE(listview->contentY(), pixelAligned ? qRound(contentY + 0.2) : contentY + 0.2); |
8808 | } |
8809 | |
8810 | void tst_QQuickListView::roundingErrors_data() |
8811 | { |
8812 | QTest::addColumn<bool>(name: "pixelAligned" ); |
8813 | QTest::newRow(dataTag: "pixelAligned=true" ) << true; |
8814 | QTest::newRow(dataTag: "pixelAligned=false" ) << false; |
8815 | } |
8816 | |
8817 | void tst_QQuickListView::QTBUG_38209() |
8818 | { |
8819 | QScopedPointer<QQuickView> window(createView()); |
8820 | window->setSource(testFileUrl(fileName: "simplelistview.qml" )); |
8821 | window->show(); |
8822 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
8823 | |
8824 | QQuickListView *listview = qobject_cast<QQuickListView *>(object: window->rootObject()); |
8825 | QVERIFY(listview); |
8826 | |
8827 | // simulate mouse flick |
8828 | flick(window: window.data(), from: QPoint(200, 200), to: QPoint(200, 50), duration: 100); |
8829 | QTRY_VERIFY(!listview->isMoving()); |
8830 | qreal contentY = listview->contentY(); |
8831 | |
8832 | // flick down |
8833 | listview->flick(xVelocity: 0, yVelocity: 1000); |
8834 | |
8835 | // ensure we move more than just a couple pixels |
8836 | QTRY_VERIFY(contentY - listview->contentY() > qreal(100.0)); |
8837 | } |
8838 | |
8839 | void tst_QQuickListView::programmaticFlickAtBounds() |
8840 | { |
8841 | QSKIP("Disabled due to false negatives (QTBUG-41228)" ); |
8842 | |
8843 | QScopedPointer<QQuickView> window(createView()); |
8844 | window->setSource(testFileUrl(fileName: "simplelistview.qml" )); |
8845 | window->show(); |
8846 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
8847 | |
8848 | QQuickListView *listview = qobject_cast<QQuickListView *>(object: window->rootObject()); |
8849 | QVERIFY(listview); |
8850 | QSignalSpy spy(listview, SIGNAL(contentYChanged())); |
8851 | |
8852 | // flick down |
8853 | listview->flick(xVelocity: 0, yVelocity: 1000); |
8854 | |
8855 | // verify that there is movement beyond bounds |
8856 | QVERIFY(spy.wait(100)); |
8857 | |
8858 | // reset, and test with StopAtBounds |
8859 | listview->cancelFlick(); |
8860 | listview->returnToBounds(); |
8861 | QTRY_COMPARE(listview->contentY(), qreal(0.0)); |
8862 | listview->setBoundsBehavior(QQuickFlickable::StopAtBounds); |
8863 | |
8864 | // flick down |
8865 | listview->flick(xVelocity: 0, yVelocity: 1000); |
8866 | |
8867 | // verify that there is no movement beyond bounds |
8868 | QVERIFY(!spy.wait(100)); |
8869 | } |
8870 | |
8871 | void tst_QQuickListView::programmaticFlickAtBounds2() |
8872 | { |
8873 | QScopedPointer<QQuickView> window(createView()); |
8874 | window->setSource(testFileUrl(fileName: "programmaticFlickAtBounds2.qml" )); |
8875 | window->show(); |
8876 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
8877 | |
8878 | QQuickListView *listview = qobject_cast<QQuickListView *>(object: window->rootObject()); |
8879 | QVERIFY(listview); |
8880 | |
8881 | // move exactly one item |
8882 | qreal velocity = -qSqrt(v: 2 * 50 * listview->flickDeceleration()); |
8883 | |
8884 | // flick down |
8885 | listview->flick(xVelocity: 0, yVelocity: velocity); |
8886 | |
8887 | QTRY_COMPARE(listview->contentY(), qreal(50.0)); |
8888 | |
8889 | // flick down |
8890 | listview->flick(xVelocity: 0, yVelocity: velocity); |
8891 | |
8892 | QTRY_COMPARE(listview->contentY(), qreal(100.0)); |
8893 | } |
8894 | |
8895 | void tst_QQuickListView::programmaticFlickAtBounds3() |
8896 | { |
8897 | QScopedPointer<QQuickView> window(createView()); |
8898 | window->setSource(testFileUrl(fileName: "programmaticFlickAtBounds3.qml" )); |
8899 | window->show(); |
8900 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
8901 | |
8902 | QQuickListView *listview = qobject_cast<QQuickListView *>(object: window->rootObject()); |
8903 | QVERIFY(listview); |
8904 | |
8905 | // flick down |
8906 | listview->flick(xVelocity: 0, yVelocity: 2000); |
8907 | |
8908 | // verify scope of the movement |
8909 | QTRY_VERIFY(listview->property("minOvershoot" ).toReal() < qreal(-50.0)); |
8910 | |
8911 | // reset, and test a second time |
8912 | listview->cancelFlick(); |
8913 | listview->returnToBounds(); |
8914 | QTRY_COMPARE(listview->contentY(), qreal(0.0)); |
8915 | listview->setProperty(name: "minOvershoot" , value: qreal(0.0)); |
8916 | |
8917 | // flick down |
8918 | listview->flick(xVelocity: 0, yVelocity: 2000); |
8919 | |
8920 | // verify scope of the movement is the same |
8921 | QTRY_VERIFY(listview->property("minOvershoot" ).toReal() < qreal(-50.0)); |
8922 | } |
8923 | |
8924 | void tst_QQuickListView::layoutChange() |
8925 | { |
8926 | QScopedPointer<RandomSortModel> model(new RandomSortModel); |
8927 | QScopedPointer<QSortFilterProxyModel> sortModel(new QSortFilterProxyModel); |
8928 | sortModel->setSourceModel(model.data()); |
8929 | sortModel->setSortRole(Qt::UserRole); |
8930 | sortModel->setDynamicSortFilter(true); |
8931 | sortModel->sort(column: 0); |
8932 | |
8933 | QScopedPointer<QQuickView> window(createView()); |
8934 | window->rootContext()->setContextProperty("testModel" , QVariant::fromValue(value: sortModel.data())); |
8935 | window->setSource(testFileUrl(fileName: "layoutChangeSort.qml" )); |
8936 | window->show(); |
8937 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
8938 | |
8939 | QQuickListView *listview = window->rootObject()->findChild<QQuickListView *>(aName: "listView" ); |
8940 | QVERIFY(listview); |
8941 | |
8942 | for (int iter = 0; iter < 100; iter++) { |
8943 | for (int i = 0; i < sortModel->rowCount(); ++i) { |
8944 | QQuickItem *delegateItem = listview->itemAt(x: 10, y: 10 + 2 * i * 20 + 20); // item + group |
8945 | QVERIFY(delegateItem); |
8946 | QQuickItem *delegateText = delegateItem->findChild<QQuickItem *>(aName: "delegateText" ); |
8947 | QVERIFY(delegateText); |
8948 | |
8949 | QCOMPARE(delegateText->property("text" ).toString(), |
8950 | sortModel->index(i, 0, QModelIndex()).data().toString()); |
8951 | } |
8952 | |
8953 | model->randomize(); |
8954 | listview->forceLayout(); |
8955 | QTest::qWait(ms: 5); // give view a chance to update |
8956 | } |
8957 | } |
8958 | |
8959 | void tst_QQuickListView::QTBUG_39492_data() |
8960 | { |
8961 | QStandardItemModel *sourceModel = new QStandardItemModel(this); |
8962 | for (int i = 0; i < 5; ++i) { |
8963 | QStandardItem *item = new QStandardItem(QString::number(i)); |
8964 | for (int j = 0; j < 5; ++j) { |
8965 | QStandardItem *subItem = new QStandardItem(QString("%1-%2" ).arg(a: i).arg(a: j)); |
8966 | item->appendRow(aitem: subItem); |
8967 | } |
8968 | sourceModel->appendRow(aitem: item); |
8969 | } |
8970 | |
8971 | QSortFilterProxyModel *sortModel = new QSortFilterProxyModel(this); |
8972 | sortModel->setSourceModel(sourceModel); |
8973 | |
8974 | QTest::addColumn<QSortFilterProxyModel*>(name: "model" ); |
8975 | QTest::addColumn<QPersistentModelIndex>(name: "rootIndex" ); |
8976 | |
8977 | QTest::newRow(dataTag: "invalid rootIndex" ) |
8978 | << sortModel |
8979 | << QPersistentModelIndex(); |
8980 | |
8981 | QTest::newRow(dataTag: "rootIndex 1" ) |
8982 | << sortModel |
8983 | << QPersistentModelIndex(sortModel->index(row: 1, column: 0)); |
8984 | |
8985 | QTest::newRow(dataTag: "rootIndex 3" ) |
8986 | << sortModel |
8987 | << QPersistentModelIndex(sortModel->index(row: 3, column: 0)); |
8988 | |
8989 | const QModelIndex rootIndex2 = sortModel->index(row: 2, column: 0); |
8990 | QTest::newRow(dataTag: "rootIndex 2-1" ) |
8991 | << sortModel |
8992 | << QPersistentModelIndex(sortModel->index(row: 1, column: 0, parent: rootIndex2)); |
8993 | } |
8994 | |
8995 | void tst_QQuickListView::QTBUG_39492() |
8996 | { |
8997 | QFETCH(QSortFilterProxyModel*, model); |
8998 | QFETCH(QPersistentModelIndex, rootIndex); |
8999 | |
9000 | QQuickView *window = getView(); |
9001 | window->rootContext()->setContextProperty("testModel" , QVariant::fromValue(value: model)); |
9002 | window->setSource(testFileUrl(fileName: "qtbug39492.qml" )); |
9003 | |
9004 | QQuickListView *listview = window->rootObject()->findChild<QQuickListView *>(aName: "listView" ); |
9005 | QVERIFY(listview); |
9006 | |
9007 | QQmlDelegateModel *delegateModel = window->rootObject()->findChild<QQmlDelegateModel *>(aName: "delegateModel" ); |
9008 | QVERIFY(delegateModel); |
9009 | |
9010 | delegateModel->setRootIndex(QVariant::fromValue(value: QModelIndex(rootIndex))); |
9011 | model->sort(column: 0, order: Qt::AscendingOrder); |
9012 | listview->forceLayout(); |
9013 | |
9014 | for (int i = 0; i < model->rowCount(parent: rootIndex); ++i) { |
9015 | QQuickItem *delegateItem = listview->itemAt(x: 10, y: 10 + i * 20); |
9016 | QVERIFY(delegateItem); |
9017 | QQuickItem *delegateText = delegateItem->findChild<QQuickItem *>(aName: "delegateText" ); |
9018 | QVERIFY(delegateText); |
9019 | QCOMPARE(delegateText->property("text" ).toString(), |
9020 | model->index(i, 0, rootIndex).data().toString()); |
9021 | } |
9022 | |
9023 | model->sort(column: 0, order: Qt::DescendingOrder); |
9024 | listview->forceLayout(); |
9025 | |
9026 | for (int i = 0; i < model->rowCount(parent: rootIndex); ++i) { |
9027 | QQuickItem *delegateItem = listview->itemAt(x: 10, y: 10 + i * 20); |
9028 | QVERIFY(delegateItem); |
9029 | QQuickItem *delegateText = delegateItem->findChild<QQuickItem *>(aName: "delegateText" ); |
9030 | QVERIFY(delegateText); |
9031 | QCOMPARE(delegateText->property("text" ).toString(), |
9032 | model->index(i, 0, rootIndex).data().toString()); |
9033 | } |
9034 | |
9035 | releaseView(view: window); |
9036 | } |
9037 | |
9038 | void tst_QQuickListView::jsArrayChange() |
9039 | { |
9040 | QQmlEngine engine; |
9041 | QQmlComponent component(&engine); |
9042 | component.setData("import QtQuick 2.4; ListView {}" , baseUrl: QUrl()); |
9043 | |
9044 | QScopedPointer<QQuickListView> view(qobject_cast<QQuickListView *>(object: component.create())); |
9045 | QVERIFY(!view.isNull()); |
9046 | |
9047 | QSignalSpy spy(view.data(), SIGNAL(modelChanged())); |
9048 | QVERIFY(spy.isValid()); |
9049 | |
9050 | QJSValue array1 = engine.newArray(length: 3); |
9051 | QJSValue array2 = engine.newArray(length: 3); |
9052 | for (int i = 0; i < 3; ++i) { |
9053 | array1.setProperty(arrayIndex: i, value: i); |
9054 | array2.setProperty(arrayIndex: i, value: i); |
9055 | } |
9056 | |
9057 | view->setModel(QVariant::fromValue(value: array1)); |
9058 | QCOMPARE(spy.count(), 1); |
9059 | |
9060 | // no change |
9061 | view->setModel(QVariant::fromValue(value: array2)); |
9062 | QCOMPARE(spy.count(), 1); |
9063 | } |
9064 | |
9065 | static bool compareObjectModel(QQuickListView *listview, QQmlObjectModel *model) |
9066 | { |
9067 | if (listview->count() != model->count()) |
9068 | return false; |
9069 | for (int i = 0; i < listview->count(); ++i) { |
9070 | listview->setCurrentIndex(i); |
9071 | if (listview->currentItem() != model->get(index: i)) |
9072 | return false; |
9073 | } |
9074 | return true; |
9075 | } |
9076 | |
9077 | void tst_QQuickListView::objectModel() |
9078 | { |
9079 | QQmlEngine engine; |
9080 | QQmlComponent component(&engine, testFileUrl(fileName: "objectmodel.qml" )); |
9081 | |
9082 | QScopedPointer<QObject> obj(component.create()); |
9083 | QQuickListView *listview = qobject_cast<QQuickListView *>(object: obj.data()); |
9084 | QVERIFY(listview); |
9085 | |
9086 | QQmlObjectModel *model = listview->model().value<QQmlObjectModel *>(); |
9087 | QVERIFY(model); |
9088 | |
9089 | listview->setCurrentIndex(0); |
9090 | QVERIFY(listview->currentItem()); |
9091 | QCOMPARE(listview->currentItem()->property("color" ).toString(), QColor("red" ).name()); |
9092 | |
9093 | listview->setCurrentIndex(1); |
9094 | QVERIFY(listview->currentItem()); |
9095 | QCOMPARE(listview->currentItem()->property("color" ).toString(), QColor("green" ).name()); |
9096 | |
9097 | listview->setCurrentIndex(2); |
9098 | QVERIFY(listview->currentItem()); |
9099 | QCOMPARE(listview->currentItem()->property("color" ).toString(), QColor("blue" ).name()); |
9100 | |
9101 | QQuickItem *item0 = new QQuickItem(listview); |
9102 | item0->setSize(QSizeF(20, 20)); |
9103 | model->append(object: item0); |
9104 | QCOMPARE(model->count(), 4); |
9105 | QVERIFY(compareObjectModel(listview, model)); |
9106 | |
9107 | QQuickItem *item1 = new QQuickItem(listview); |
9108 | item1->setSize(QSizeF(20, 20)); |
9109 | model->insert(index: 0, object: item1); |
9110 | QCOMPARE(model->count(), 5); |
9111 | QVERIFY(compareObjectModel(listview, model)); |
9112 | |
9113 | model->move(from: 1, to: 2, n: 3); |
9114 | QVERIFY(compareObjectModel(listview, model)); |
9115 | |
9116 | model->remove(index: 2, n: 2); |
9117 | QCOMPARE(model->count(), 3); |
9118 | QVERIFY(compareObjectModel(listview, model)); |
9119 | |
9120 | model->clear(); |
9121 | QCOMPARE(model->count(), 0); |
9122 | QCOMPARE(listview->count(), 0); |
9123 | } |
9124 | |
9125 | void tst_QQuickListView::contentHeightWithDelayRemove_data() |
9126 | { |
9127 | QTest::addColumn<bool>(name: "useDelayRemove" ); |
9128 | QTest::addColumn<QByteArray>(name: "removeFunc" ); |
9129 | QTest::addColumn<int>(name: "countDelta" ); |
9130 | QTest::addColumn<qreal>(name: "contentHeightDelta" ); |
9131 | |
9132 | QTest::newRow(dataTag: "remove without delayRemove" ) |
9133 | << false |
9134 | << QByteArray("takeOne" ) |
9135 | << -1 |
9136 | << qreal(-1 * 100.0); |
9137 | |
9138 | QTest::newRow(dataTag: "remove with delayRemove" ) |
9139 | << true |
9140 | << QByteArray("takeOne" ) |
9141 | << -1 |
9142 | << qreal(-1 * 100.0); |
9143 | |
9144 | QTest::newRow(dataTag: "remove with multiple delayRemove" ) |
9145 | << true |
9146 | << QByteArray("takeThree" ) |
9147 | << -3 |
9148 | << qreal(-3 * 100.0); |
9149 | |
9150 | QTest::newRow(dataTag: "clear with delayRemove" ) |
9151 | << true |
9152 | << QByteArray("takeAll" ) |
9153 | << -5 |
9154 | << qreal(-5 * 100.0); |
9155 | } |
9156 | |
9157 | void tst_QQuickListView::contentHeightWithDelayRemove() |
9158 | { |
9159 | QFETCH(bool, useDelayRemove); |
9160 | QFETCH(QByteArray, removeFunc); |
9161 | QFETCH(int, countDelta); |
9162 | QFETCH(qreal, contentHeightDelta); |
9163 | |
9164 | QScopedPointer<QQuickView> window(createView()); |
9165 | window->setSource(testFileUrl(fileName: "contentHeightWithDelayRemove.qml" )); |
9166 | window->show(); |
9167 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
9168 | |
9169 | QQuickListView *listview = window->rootObject()->findChild<QQuickListView*>(); |
9170 | QTRY_VERIFY(listview != nullptr); |
9171 | |
9172 | const int initialCount(listview->count()); |
9173 | const int eventualCount(initialCount + countDelta); |
9174 | |
9175 | const qreal initialContentHeight(listview->contentHeight()); |
9176 | const int eventualContentHeight(qRound(d: initialContentHeight + contentHeightDelta)); |
9177 | |
9178 | listview->setProperty(name: "useDelayRemove" , value: useDelayRemove); |
9179 | QMetaObject::invokeMethod(obj: window->rootObject(), member: removeFunc.constData()); |
9180 | QTest::qWait(ms: 50); |
9181 | QCOMPARE(listview->count(), eventualCount); |
9182 | |
9183 | if (useDelayRemove) { |
9184 | QCOMPARE(qRound(listview->contentHeight()), qRound(initialContentHeight)); |
9185 | QTRY_COMPARE(qRound(listview->contentHeight()), eventualContentHeight); |
9186 | } else { |
9187 | QCOMPARE(qRound(listview->contentHeight()), eventualContentHeight); |
9188 | } |
9189 | } |
9190 | |
9191 | void tst_QQuickListView::QTBUG_48044_currentItemNotVisibleAfterTransition() |
9192 | { |
9193 | QScopedPointer<QQuickView> window(createView()); |
9194 | window->setSource(testFileUrl(fileName: "qtbug48044.qml" )); |
9195 | window->show(); |
9196 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
9197 | |
9198 | QQuickListView *listview = window->rootObject()->findChild<QQuickListView*>(); |
9199 | QTRY_VERIFY(listview != nullptr); |
9200 | |
9201 | // Expand 2nd header |
9202 | listview->setProperty(name: "transitionsDone" , value: QVariant(false)); |
9203 | QTest::mouseClick(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(window->width() / 2, 75)); |
9204 | QTRY_VERIFY(listview->property("transitionsDone" ).toBool()); |
9205 | |
9206 | // Flick listview to the bottom |
9207 | flick(window: window.data(), from: QPoint(window->width() / 2, 400), to: QPoint(window->width() / 2, 0), duration: 100); |
9208 | QTRY_VERIFY(!listview->isMoving()); |
9209 | |
9210 | // Expand 3rd header |
9211 | listview->setProperty(name: "transitionsDone" , value: QVariant(false)); |
9212 | QTest::mouseClick(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(window->width() / 2, window->height() - 25)); |
9213 | QTRY_VERIFY(listview->property("transitionsDone" ).toBool()); |
9214 | |
9215 | // Check current item is what we expect |
9216 | QCOMPARE(listview->currentIndex(), 2); |
9217 | QQuickItem *currentItem = listview->currentItem(); |
9218 | QVERIFY(currentItem); |
9219 | QVERIFY(currentItem->isVisible()); |
9220 | |
9221 | // This is the actual test |
9222 | QQuickItemPrivate *currentPriv = QQuickItemPrivate::get(item: currentItem); |
9223 | QVERIFY(!currentPriv->culled); |
9224 | } |
9225 | |
9226 | void tst_QQuickListView::keyNavigationEnabled() |
9227 | { |
9228 | QScopedPointer<QQuickView> window(createView()); |
9229 | window->setSource(testFileUrl(fileName: "keyNavigationEnabled.qml" )); |
9230 | window->show(); |
9231 | window->requestActivate(); |
9232 | QVERIFY(QTest::qWaitForWindowActive(window.data())); |
9233 | |
9234 | QQuickListView *listView = qobject_cast<QQuickListView *>(object: window->rootObject()); |
9235 | QVERIFY(listView); |
9236 | QCOMPARE(listView->isKeyNavigationEnabled(), true); |
9237 | |
9238 | listView->setFocus(true); |
9239 | QVERIFY(listView->hasActiveFocus()); |
9240 | |
9241 | listView->setHighlightMoveDuration(0); |
9242 | |
9243 | // If keyNavigationEnabled is not explicitly set to true, respect the original behavior |
9244 | // of disabling both mouse and keyboard interaction. |
9245 | QSignalSpy enabledSpy(listView, SIGNAL(keyNavigationEnabledChanged())); |
9246 | listView->setInteractive(false); |
9247 | QCOMPARE(enabledSpy.count(), 1); |
9248 | QCOMPARE(listView->isKeyNavigationEnabled(), false); |
9249 | |
9250 | flick(window: window.data(), from: QPoint(200, 200), to: QPoint(200, 50), duration: 100); |
9251 | QVERIFY(!listView->isMoving()); |
9252 | QCOMPARE(listView->contentY(), 0.0); |
9253 | QCOMPARE(listView->currentIndex(), 0); |
9254 | |
9255 | QTest::keyClick(window: window.data(), key: Qt::Key_Down); |
9256 | QCOMPARE(listView->currentIndex(), 0); |
9257 | |
9258 | // Check that isKeyNavigationEnabled implicitly follows the value of interactive. |
9259 | listView->setInteractive(true); |
9260 | QCOMPARE(enabledSpy.count(), 2); |
9261 | QCOMPARE(listView->isKeyNavigationEnabled(), true); |
9262 | |
9263 | // Change it back again for the next check. |
9264 | listView->setInteractive(false); |
9265 | QCOMPARE(enabledSpy.count(), 3); |
9266 | QCOMPARE(listView->isKeyNavigationEnabled(), false); |
9267 | |
9268 | // Setting keyNavigationEnabled to true shouldn't enable mouse interaction. |
9269 | listView->setKeyNavigationEnabled(true); |
9270 | QCOMPARE(enabledSpy.count(), 4); |
9271 | flick(window: window.data(), from: QPoint(200, 200), to: QPoint(200, 50), duration: 100); |
9272 | QVERIFY(!listView->isMoving()); |
9273 | QCOMPARE(listView->contentY(), 0.0); |
9274 | QCOMPARE(listView->currentIndex(), 0); |
9275 | |
9276 | // Should now work. |
9277 | QTest::keyClick(window: window.data(), key: Qt::Key_Down); |
9278 | QCOMPARE(listView->currentIndex(), 1); |
9279 | // contentY won't change for one index change in a view this high. |
9280 | |
9281 | // Changing interactive now shouldn't result in keyNavigationEnabled changing, |
9282 | // since we broke the "binding". |
9283 | listView->setInteractive(true); |
9284 | QCOMPARE(enabledSpy.count(), 4); |
9285 | |
9286 | // Keyboard interaction shouldn't work now. |
9287 | listView->setKeyNavigationEnabled(false); |
9288 | QTest::keyClick(window: window.data(), key: Qt::Key_Down); |
9289 | QCOMPARE(listView->currentIndex(), 1); |
9290 | } |
9291 | |
9292 | void tst_QQuickListView::QTBUG_61269_appendDuringScrollDown_data() |
9293 | { |
9294 | QTest::addColumn<QQuickListView::SnapMode>(name: "snapMode" ); |
9295 | |
9296 | QTest::newRow(dataTag: "NoSnap" ) << QQuickListView::NoSnap; |
9297 | QTest::newRow(dataTag: "SnapToItem" ) << QQuickListView::SnapToItem; |
9298 | QTest::newRow(dataTag: "SnapOneItem" ) << QQuickListView::SnapOneItem; |
9299 | } |
9300 | |
9301 | void tst_QQuickListView::QTBUG_61269_appendDuringScrollDown() // AKA QTBUG-62864 |
9302 | { |
9303 | QFETCH(QQuickListView::SnapMode, snapMode); |
9304 | |
9305 | QScopedPointer<QQuickView> window(createView()); |
9306 | window->setSource(testFileUrl(fileName: "appendDuringScrollDown.qml" )); |
9307 | window->show(); |
9308 | window->requestActivate(); |
9309 | QVERIFY(QTest::qWaitForWindowActive(window.data())); |
9310 | |
9311 | QQuickListView *listView = qobject_cast<QQuickListView *>(object: window->rootObject()); |
9312 | listView->setSnapMode(snapMode); |
9313 | QQuickItem *highlightItem = listView->highlightItem(); |
9314 | QVERIFY(listView); |
9315 | QCOMPARE(listView->isKeyNavigationEnabled(), true); |
9316 | listView->setHighlightMoveVelocity(400); |
9317 | listView->setHighlightMoveDuration(-1); // let it animate |
9318 | listView->setFocus(true); |
9319 | QVERIFY(listView->hasActiveFocus()); |
9320 | qreal highlightYLimit = listView->height() - highlightItem->height(); // should be 200 |
9321 | |
9322 | for (int i = 1; i < 15; ++i) { |
9323 | QTest::keyClick(window: window.data(), key: Qt::Key_Down); |
9324 | |
9325 | // Wait for the highlight movement animation to finish. |
9326 | QTRY_COMPARE(highlightItem->y(), 40.0 * i); |
9327 | |
9328 | // As we scroll down, the QML will append rows to its own model. |
9329 | // Make sure the highlighted row and highlight item stay within the view. |
9330 | // In QTBUG-62864 and QTBUG-61269, it would go off the bottom. |
9331 | QVERIFY(highlightItem->y() - listView->contentY() <= highlightYLimit); |
9332 | } |
9333 | } |
9334 | |
9335 | void tst_QQuickListView::QTBUG_48870_fastModelUpdates() |
9336 | { |
9337 | StressTestModel model; |
9338 | |
9339 | QScopedPointer<QQuickView> window(createView()); |
9340 | QQmlContext *ctxt = window->rootContext(); |
9341 | ctxt->setContextProperty("testModel" , &model); |
9342 | |
9343 | window->setSource(testFileUrl(fileName: "qtbug48870.qml" )); |
9344 | window->show(); |
9345 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
9346 | |
9347 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
9348 | QTRY_VERIFY(listview != nullptr); |
9349 | |
9350 | QQuickItemViewPrivate *priv = QQuickItemViewPrivate::get(o: listview); |
9351 | bool nonUnique; |
9352 | FxViewItem *item = nullptr; |
9353 | int expectedIdx; |
9354 | QVERIFY(testVisibleItems(priv, &nonUnique, &item, &expectedIdx)); |
9355 | |
9356 | for (int i = 0; i < 10; i++) { |
9357 | QTest::qWait(ms: 100); |
9358 | QVERIFY2(testVisibleItems(priv, &nonUnique, &item, &expectedIdx), |
9359 | qPrintable(!item ? QString("Unexpected null item" ) |
9360 | : nonUnique ? QString("Non-unique item at %1 and %2" ).arg(item->index).arg(expectedIdx) |
9361 | : QString("Found index %1, expected index is %3" ).arg(item->index).arg(expectedIdx))); |
9362 | if (i % 3 != 0) { |
9363 | if (i & 1) |
9364 | flick(window: window.data(), from: QPoint(100, 200), to: QPoint(100, 0), duration: 100); |
9365 | else |
9366 | flick(window: window.data(), from: QPoint(100, 200), to: QPoint(100, 400), duration: 100); |
9367 | } |
9368 | } |
9369 | } |
9370 | |
9371 | // infinite loop in overlay header positioning due to undesired rounding in QQuickFlickablePrivate::fixup() |
9372 | void tst_QQuickListView::QTBUG_50105() |
9373 | { |
9374 | QQmlEngine engine; |
9375 | QQmlComponent component(&engine); |
9376 | component.loadUrl(url: testFileUrl(fileName: "qtbug50105.qml" )); |
9377 | |
9378 | QScopedPointer<QQuickWindow> window(qobject_cast<QQuickWindow *>(object: component.create())); |
9379 | QVERIFY(window.data()); |
9380 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
9381 | } |
9382 | |
9383 | void tst_QQuickListView::() |
9384 | { |
9385 | QScopedPointer<QQuickView> window(createView()); |
9386 | window->setSource(testFileUrl(fileName: "qtbug50097.qml" )); |
9387 | window->show(); |
9388 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
9389 | |
9390 | QQuickListView *listview = qobject_cast<QQuickListView*>(object: window->rootObject()); |
9391 | QVERIFY(listview != nullptr); |
9392 | QTRY_COMPARE(listview->contentY(), -100.0); // the header size, since the header is overlaid |
9393 | listview->setProperty(name: "currentPage" , value: 2); |
9394 | QTRY_COMPARE(listview->contentY(), 400.0); // a full page of items down, sans the original negative header position |
9395 | listview->setProperty(name: "currentPage" , value: 1); |
9396 | QTRY_COMPARE(listview->contentY(), -100.0); // back to the same position: header visible, items not under the header. |
9397 | } |
9398 | |
9399 | void tst_QQuickListView::() |
9400 | { |
9401 | QScopedPointer<QQuickView> window(createView()); |
9402 | window->setSource(testFileUrl(fileName: "qtbug63974.qml" )); |
9403 | window->show(); |
9404 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
9405 | |
9406 | QQuickListView *listview = qobject_cast<QQuickListView*>(object: window->rootObject()); |
9407 | QVERIFY(listview != nullptr); |
9408 | |
9409 | const qreal = 20; |
9410 | const qreal = 20; |
9411 | const qreal itemSize = 20; |
9412 | const int itemCount = 30; |
9413 | const qreal contentHeight = itemCount * itemSize; |
9414 | |
9415 | const qreal initialY = listview->contentY(); |
9416 | const qreal endPosition = contentHeight + footerSize - listview->height(); |
9417 | |
9418 | QVERIFY(qFuzzyCompare(initialY, -headerSize)); |
9419 | |
9420 | listview->positionViewAtIndex(index: itemCount - 1, mode: QQuickListView::Contain); |
9421 | QTRY_COMPARE(listview->contentY(), endPosition); |
9422 | |
9423 | listview->positionViewAtIndex(index: 0, mode: QQuickListView::Contain); |
9424 | QTRY_COMPARE(listview->contentY(), -headerSize); |
9425 | |
9426 | listview->positionViewAtIndex(index: itemCount - 1, mode: QQuickListView::Visible); |
9427 | QTRY_COMPARE(listview->contentY(), endPosition); |
9428 | |
9429 | listview->positionViewAtIndex(index: 0, mode: QQuickListView::Visible); |
9430 | QTRY_COMPARE(listview->contentY(), -headerSize); |
9431 | |
9432 | listview->positionViewAtIndex(index: itemCount - 1, mode: QQuickListView::SnapPosition); |
9433 | QTRY_COMPARE(listview->contentY(), endPosition); |
9434 | |
9435 | listview->positionViewAtIndex(index: 0, mode: QQuickListView::SnapPosition); |
9436 | QTRY_COMPARE(listview->contentY(), -headerSize); |
9437 | } |
9438 | |
9439 | void tst_QQuickListView::QTBUG_66163_setModelViewPortSizeChange() |
9440 | { |
9441 | QScopedPointer<QQuickView> window(createView()); |
9442 | QQmlComponent comp(window->engine()); |
9443 | comp.setData(R"( |
9444 | import QtQuick 2.0 |
9445 | |
9446 | Item { |
9447 | id: root |
9448 | width: 400 |
9449 | height: 400 |
9450 | |
9451 | ListView { |
9452 | id: view |
9453 | objectName: "view" |
9454 | anchors.fill: parent |
9455 | |
9456 | model: 4 |
9457 | highlightRangeMode: ListView.StrictlyEnforceRange |
9458 | |
9459 | delegate: Rectangle { |
9460 | color: index % 2 ? "green" : "orange" |
9461 | width: parent.width |
9462 | height: 50 |
9463 | } |
9464 | |
9465 | populate: Transition { |
9466 | SequentialAnimation { |
9467 | NumberAnimation { property: "y"; from: 100; duration: 1000 } |
9468 | } |
9469 | } |
9470 | } |
9471 | } |
9472 | )" , baseUrl: QUrl("testData" )); |
9473 | auto root {qobject_cast<QQuickItem*>(object: comp.create())}; |
9474 | QVERIFY(root); |
9475 | window->setContent(url: QUrl(), component: &comp, item: root); |
9476 | window->show(); |
9477 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
9478 | auto view = root->findChild<QQuickListView *>(aName: "view" ); |
9479 | QVERIFY(view); |
9480 | QVERIFY(QQuickTest::qWaitForItemPolished(view)); |
9481 | QSignalSpy spy(view, &QQuickListView::contentYChanged); |
9482 | auto transition = view->property(name: "populate" ).value<QQuickTransition*>(); |
9483 | QVERIFY(transition); |
9484 | QQmlProperty model(view, "model" ); |
9485 | QVERIFY(model.isValid()); |
9486 | model.write(5); |
9487 | // Animations inside a Transition do not emit a finished signal |
9488 | // so we cannot wait for them in that way |
9489 | QTest::qWait(ms: 1100); // animation takes 1000ms, + 10% extra delay |
9490 | /* the viewport should not have changed, thus there should not have |
9491 | been any contentYChanged signal*/ |
9492 | QCOMPARE(spy.count(), 0); |
9493 | } |
9494 | |
9495 | void tst_QQuickListView::itemFiltered() |
9496 | { |
9497 | QStringListModel model(QStringList() << "one" << "two" << "three" << "four" << "five" << "six" ); |
9498 | QSortFilterProxyModel proxy1; |
9499 | proxy1.setSourceModel(&model); |
9500 | proxy1.setSortRole(Qt::DisplayRole); |
9501 | proxy1.setDynamicSortFilter(true); |
9502 | proxy1.sort(column: 0); |
9503 | |
9504 | QSortFilterProxyModel proxy2; |
9505 | proxy2.setSourceModel(&proxy1); |
9506 | proxy2.setFilterRole(Qt::DisplayRole); |
9507 | proxy2.setFilterRegExp("^[^ ]*$" ); |
9508 | proxy2.setDynamicSortFilter(true); |
9509 | |
9510 | QScopedPointer<QQuickView> window(createView()); |
9511 | window->engine()->rootContext()->setContextProperty("_model" , &proxy2); |
9512 | QQmlComponent component(window->engine()); |
9513 | component.setData("import QtQuick 2.4; ListView { " |
9514 | "anchors.fill: parent; model: _model; delegate: Text { width: parent.width;" |
9515 | "text: model.display; } }" , |
9516 | baseUrl: QUrl()); |
9517 | window->setContent(url: QUrl(), component: &component, item: component.create()); |
9518 | |
9519 | window->show(); |
9520 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
9521 | |
9522 | // this should not crash |
9523 | model.setData(index: model.index(row: 2), QStringLiteral("modified three" ), role: Qt::DisplayRole); |
9524 | } |
9525 | |
9526 | void tst_QQuickListView::releaseItems() |
9527 | { |
9528 | QScopedPointer<QQuickView> view(createView()); |
9529 | view->setSource(testFileUrl(fileName: "releaseItems.qml" )); |
9530 | |
9531 | QQuickListView *listview = qobject_cast<QQuickListView *>(object: view->rootObject()); |
9532 | QVERIFY(listview); |
9533 | |
9534 | // don't crash (QTBUG-61294) |
9535 | listview->setModel(123); |
9536 | } |
9537 | |
9538 | void tst_QQuickListView::QTBUG_34576_velocityZero() |
9539 | { |
9540 | QScopedPointer<QQuickView> window(new QQuickView(nullptr)); |
9541 | window->setGeometry(posx: 0,posy: 0,w: 240,h: 320); |
9542 | |
9543 | QString filename(testFile(fileName: "qtbug34576.qml" )); |
9544 | window->setSource(QUrl::fromLocalFile(localfile: filename)); |
9545 | window->show(); |
9546 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
9547 | |
9548 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
9549 | QVERIFY(listview); |
9550 | QQuickItem *contentItem = listview->contentItem(); |
9551 | QVERIFY(contentItem); |
9552 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
9553 | |
9554 | QSignalSpy horizontalVelocitySpy(listview, SIGNAL(horizontalVelocityChanged())); |
9555 | |
9556 | // currentIndex is initialized to 0 |
9557 | QCOMPARE(listview->currentIndex(), 0); |
9558 | |
9559 | // set currentIndex to last item currently visible item |
9560 | window->rootObject()->setProperty(name: "horizontalVelocityZeroCount" , value: QVariant(0)); |
9561 | listview->setCurrentIndex(2); |
9562 | QTRY_COMPARE(window->rootObject()->property("current" ).toInt(), 2); |
9563 | QCOMPARE(horizontalVelocitySpy.count(), 0); |
9564 | QCOMPARE(window->rootObject()->property("horizontalVelocityZeroCount" ).toInt(), 0); |
9565 | |
9566 | QSignalSpy currentIndexChangedSpy(listview, SIGNAL(currentIndexChanged())); |
9567 | |
9568 | // click button which increases currentIndex |
9569 | QTest::mousePress(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(295,215)); |
9570 | QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(295,215)); |
9571 | |
9572 | // verify that currentIndexChanged is triggered |
9573 | QTRY_VERIFY(currentIndexChangedSpy.count() > 0); |
9574 | |
9575 | // since we have set currentIndex to an item out of view, the listview will scroll |
9576 | QTRY_COMPARE(window->rootObject()->property("current" ).toInt(), 3); |
9577 | QTRY_VERIFY(horizontalVelocitySpy.count() > 0); |
9578 | |
9579 | // velocity should be always > 0.0 |
9580 | QTRY_COMPARE(window->rootObject()->property("horizontalVelocityZeroCount" ).toInt(), 0); |
9581 | } |
9582 | |
9583 | void tst_QQuickListView::QTBUG_61537_modelChangesAsync() |
9584 | { |
9585 | // The purpose of this test if to check that any model changes that happens |
9586 | // during start-up, while a loader higher up in the chain is still incubating |
9587 | // async, will not fail. |
9588 | QQuickView window; |
9589 | window.setGeometry(posx: 0,posy: 0,w: 640,h: 480); |
9590 | |
9591 | QString filename(testFile(fileName: "qtbug61537_modelChangesAsync.qml" )); |
9592 | window.setSource(QUrl::fromLocalFile(localfile: filename)); |
9593 | window.show(); |
9594 | QVERIFY(QTest::qWaitForWindowExposed(&window)); |
9595 | |
9596 | // The qml file will assign the listview to the 'listView' property once the |
9597 | // loader is ready with async incubation. So we need to wait for it. |
9598 | QObject *root = window.rootObject(); |
9599 | QTRY_VERIFY(root->property("listView" ).value<QQuickListView *>()); |
9600 | QQuickListView *listView = root->property(name: "listView" ).value<QQuickListView *>(); |
9601 | QVERIFY(listView); |
9602 | |
9603 | // Check that the number of delegates we expect to be visible in |
9604 | // the listview matches the number of items we find if we count. |
9605 | int reportedCount = listView->count(); |
9606 | int actualCount = findItems<QQuickItem>(parent: listView, objectName: "delegate" ).count(); |
9607 | QCOMPARE(reportedCount, actualCount); |
9608 | } |
9609 | |
9610 | void tst_QQuickListView::addOnCompleted() |
9611 | { |
9612 | QScopedPointer<QQuickView> window(createView()); |
9613 | window->setSource(testFileUrl(fileName: "addoncompleted.qml" )); |
9614 | window->show(); |
9615 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
9616 | |
9617 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "view" ); |
9618 | QTRY_VERIFY(listview != nullptr); |
9619 | |
9620 | QQuickItem *contentItem = listview->contentItem(); |
9621 | QTRY_VERIFY(contentItem != nullptr); |
9622 | |
9623 | qreal y = -1; |
9624 | for (char name = 'a'; name <= 'j'; ++name) { |
9625 | for (int num = 9; num >= 0; --num) { |
9626 | const QString objName = QString::fromLatin1(str: "%1%2" ).arg(a: name).arg(a: num); |
9627 | QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: objName); |
9628 | if (!item) { |
9629 | QVERIFY(name >= 'd'); |
9630 | y = 9999999; |
9631 | } else { |
9632 | const qreal newY = item->y(); |
9633 | QVERIFY(newY != 9999999); // once we could not find an item, we shouldn' find any further ones |
9634 | QVERIFY2(newY > y, objName.toUtf8().constData()); |
9635 | y = newY; |
9636 | } |
9637 | } |
9638 | } |
9639 | } |
9640 | |
9641 | void tst_QQuickListView::setPositionOnLayout() |
9642 | { |
9643 | // Make sure we don't trigger a crash by removing items during layout from setPosition(). |
9644 | QScopedPointer<QQuickView> window(createView()); |
9645 | window->setSource(testFileUrl(fileName: "setpositiononlayout.qml" )); |
9646 | window->show(); |
9647 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
9648 | window->requestActivate(); |
9649 | QVERIFY(QTest::qWaitForWindowActive(window.data())); |
9650 | for (int i = 0; i < 1000; ++i) { |
9651 | QTest::keyPress(window: window.data(), key: Qt::Key_Down); |
9652 | QTest::qWait(ms: 1); |
9653 | QTest::keyRelease(window: window.data(), key: Qt::Key_Down); |
9654 | } |
9655 | } |
9656 | |
9657 | void tst_QQuickListView::useDelegateChooserWithoutDefault() |
9658 | { |
9659 | // Check that the application doesn't crash |
9660 | // if the delegate chooser doesn't cover all cells |
9661 | QScopedPointer<QQuickView> window(createView()); |
9662 | window->setSource(testFileUrl(fileName: "usechooserwithoutdefault.qml" )); |
9663 | window->show(); |
9664 | }; |
9665 | |
9666 | void tst_QQuickListView::touchCancel() // QTBUG-74679 |
9667 | { |
9668 | QScopedPointer<QQuickView> window(createView()); |
9669 | window->setSource(testFileUrl(fileName: "delegateWithMouseArea.qml" )); |
9670 | window->show(); |
9671 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
9672 | |
9673 | QQuickListView *listview = qobject_cast<QQuickListView *>(object: window->rootObject()); |
9674 | QVERIFY(listview); |
9675 | QQuickMouseArea *mouseArea = listview->currentItem()->findChild<QQuickMouseArea *>(); |
9676 | QVERIFY(mouseArea); |
9677 | |
9678 | QPoint p1(300, 300); |
9679 | QTest::touchEvent(window: window.data(), device: touchDevice).press(touchId: 0, pt: p1, window: window.data()); |
9680 | QQuickTouchUtils::flush(window: window.data()); |
9681 | QTRY_VERIFY(mouseArea->pressed()); |
9682 | // and because Flickable filtered it, QQuickFlickablePrivate::pressed |
9683 | // should be true, but it's not easily tested here |
9684 | |
9685 | QTouchEvent cancelEvent(QEvent::TouchCancel); |
9686 | cancelEvent.setDevice(touchDevice); |
9687 | QCoreApplication::sendEvent(receiver: window.data(), event: &cancelEvent); |
9688 | // now QQuickWindowPrivate::sendUngrabEvent() will be called, Flickable will filter it, |
9689 | // QQuickFlickablePrivate::pressed will be set to false, and that will allow setCurrentIndex() to make it move |
9690 | QQuickTouchUtils::flush(window: window.data()); |
9691 | |
9692 | listview->setCurrentIndex(1); |
9693 | // ensure that it actually moves (animates) to the second delegate |
9694 | QTRY_COMPARE(listview->contentY(), 500.0); |
9695 | } |
9696 | |
9697 | void tst_QQuickListView::resizeAfterComponentComplete() // QTBUG-76487 |
9698 | { |
9699 | QScopedPointer<QQuickView> window(createView()); |
9700 | window->setSource(testFileUrl(fileName: "resizeAfterComponentComplete.qml" )); |
9701 | window->resize(w: 640, h: 480); |
9702 | window->show(); |
9703 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
9704 | |
9705 | QObject *listView = window->rootObject(); |
9706 | QVERIFY(listView); |
9707 | |
9708 | QObject *lastItem = qvariant_cast<QObject *>(v: listView->property(name: "lastItem" )); |
9709 | QTRY_COMPARE(lastItem->property("y" ).toInt(), 9 * lastItem->property("height" ).toInt()); |
9710 | } |
9711 | |
9712 | class Animal |
9713 | { |
9714 | public: |
9715 | Animal(const int cost, const QString &name) {m_name = name; m_cost = cost;} |
9716 | |
9717 | int cost() const {return m_cost;} |
9718 | QString name() const {return m_name;} |
9719 | |
9720 | QString m_name; |
9721 | int m_cost; |
9722 | }; |
9723 | |
9724 | class FruitModel : public QAbstractListModel |
9725 | { |
9726 | Q_OBJECT |
9727 | public: |
9728 | enum AnimalRoles { |
9729 | NameRole = Qt::UserRole + 1, |
9730 | CostRole |
9731 | }; |
9732 | |
9733 | FruitModel(QObject* = nullptr) { |
9734 | m_animals.push_back(t: Animal {4, QLatin1String("Melon" )}); |
9735 | m_animals.push_back(t: Animal {5, QLatin1String("Cherry" )}); |
9736 | } |
9737 | |
9738 | int rowCount(const QModelIndex & = QModelIndex()) const override {return m_animals.count();} |
9739 | |
9740 | QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override { |
9741 | if (!checkIndex(index)) |
9742 | return {}; |
9743 | const Animal &animal = m_animals[index.row()]; |
9744 | if (role == CostRole) |
9745 | return animal.cost(); |
9746 | else if (role == NameRole) |
9747 | return animal.name(); |
9748 | return QVariant(); |
9749 | } |
9750 | |
9751 | protected: |
9752 | QHash<int, QByteArray> roleNames() const override { |
9753 | QHash<int, QByteArray> roles; |
9754 | roles[CostRole] = "cost" ; |
9755 | roles[NameRole] = "name" ; |
9756 | return roles; |
9757 | } |
9758 | private: |
9759 | QList<Animal> m_animals; |
9760 | }; |
9761 | |
9762 | void tst_QQuickListView::delegateWithRequiredProperties() |
9763 | { |
9764 | FruitModel myModel; |
9765 | qmlRegisterSingletonInstance(uri: "Qt.fruit" , versionMajor: 1, versionMinor: 0, typeName: "FruitModelCpp" , cppObject: &myModel); |
9766 | { |
9767 | // ListModel |
9768 | QTest::ignoreMessage(type: QtMsgType::QtDebugMsg, message: "Apple2" ); |
9769 | QTest::ignoreMessage(type: QtMsgType::QtDebugMsg, message: "Orange3" ); |
9770 | QTest::ignoreMessage(type: QtMsgType::QtDebugMsg, message: "Banana1" ); |
9771 | QScopedPointer<QQuickView> window(createView()); |
9772 | window->setInitialProperties({{QLatin1String("useCpp" ), false}}); |
9773 | window->setSource(testFileUrl(fileName: "delegatesWithRequiredProperties.qml" )); |
9774 | window->show(); |
9775 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
9776 | |
9777 | QObject *listView = window->rootObject(); |
9778 | QVERIFY(listView); |
9779 | } |
9780 | { |
9781 | // C++ model |
9782 | QTest::ignoreMessage(type: QtMsgType::QtDebugMsg, message: "Melon4" ); |
9783 | QTest::ignoreMessage(type: QtMsgType::QtDebugMsg, message: "Cherry5" ); |
9784 | QScopedPointer<QQuickView> window(createView()); |
9785 | window->setInitialProperties({{QLatin1String("useCpp" ), true}}); |
9786 | window->setSource(testFileUrl(fileName: "delegatesWithRequiredProperties.qml" )); |
9787 | |
9788 | window->show(); |
9789 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
9790 | |
9791 | QObject *listView = window->rootObject(); |
9792 | QVERIFY(listView); |
9793 | } |
9794 | } |
9795 | |
9796 | void tst_QQuickListView::reuse_reuseIsOffByDefault() |
9797 | { |
9798 | // Check that delegate recycling is off by default. The reason is that |
9799 | // ListView needs to be backwards compatible with legacy applications. And |
9800 | // when using delegate recycling, there are certain differences, like that |
9801 | // a delegates Component.onCompleted will just be called the first time the |
9802 | // item is created, and not when it's reused. |
9803 | QScopedPointer<QQuickView> window(createView()); |
9804 | window->setSource(testFileUrl(fileName: "listviewtest.qml" )); |
9805 | window->resize(w: 640, h: 480); |
9806 | window->show(); |
9807 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
9808 | |
9809 | QQuickListView *listView = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
9810 | QVERIFY(listView != nullptr); |
9811 | QVERIFY(!listView->reuseItems()); |
9812 | } |
9813 | |
9814 | void tst_QQuickListView::reuse_checkThatItemsAreReused() |
9815 | { |
9816 | // Flick up and down one page of items. Check that this results in the |
9817 | // delegate items being reused once. |
9818 | // Note that this is slightly different from tableview, which will reuse the items |
9819 | // twice during a similar down-then-up flick. The reason is that listview fills up |
9820 | // free space in the view with items _before_ it release old items that have been |
9821 | // flicked out. But changing this will break other auto tests (and perhaps legacy |
9822 | // apps), so we have chosen to stick with this behavior for now. |
9823 | QScopedPointer<QQuickView> window(createView()); |
9824 | |
9825 | ReuseModel model(100); |
9826 | QQmlContext *ctxt = window->rootContext(); |
9827 | ctxt->setContextProperty("reuseModel" , &model); |
9828 | |
9829 | window->setSource(testFileUrl(fileName: "reusedelegateitems.qml" )); |
9830 | window->resize(w: 640, h: 480); |
9831 | window->show(); |
9832 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
9833 | QVERIFY(window->rootObject() != nullptr); |
9834 | |
9835 | QQuickListView *listView = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
9836 | QTRY_VERIFY(listView != nullptr); |
9837 | const auto itemView_d = QQuickItemViewPrivate::get(o: listView); |
9838 | |
9839 | QVERIFY(listView->reuseItems()); |
9840 | |
9841 | auto items = findItems<QQuickItem>(parent: listView, objectName: "delegate" ); |
9842 | const int initialItemCount = items.count(); |
9843 | QVERIFY(initialItemCount > 0); |
9844 | |
9845 | // Sanity check that the size of the initial list of items match the count we tracked from QML |
9846 | QCOMPARE(listView->property("delegatesCreatedCount" ).toInt(), initialItemCount); |
9847 | |
9848 | // Go through all the initial items and check that they have not been reused yet |
9849 | for (const auto item : qAsConst(t&: items)) |
9850 | QCOMPARE(item->property("reusedCount" ).toInt(), 0); |
9851 | |
9852 | // Flick one page down and count how many items we have created thus |
9853 | // far. We expect this number to be twice as high as the initial count |
9854 | // since we flicked one whole page. |
9855 | const qreal delegateHeight = items.at(i: 0)->height(); |
9856 | const qreal flickDistance = (initialItemCount * delegateHeight) + 1; |
9857 | listView->setContentY(flickDistance); |
9858 | QVERIFY(QQuickTest::qWaitForItemPolished(listView)); |
9859 | const int countAfterDownFlick = listView->property(name: "delegatesCreatedCount" ).toInt(); |
9860 | QCOMPARE(countAfterDownFlick, initialItemCount * 2); |
9861 | |
9862 | // Check that the reuse pool is now populated. We expect all initial items to be pooled, |
9863 | // except model index 0, which was never reused or released, since it's ListView.currentItem. |
9864 | const int poolSizeAfterDownFlick = itemView_d->model->poolSize(); |
9865 | QCOMPARE(poolSizeAfterDownFlick, initialItemCount - 1); |
9866 | |
9867 | // Go through all items and check that all model data inside the delegate |
9868 | // have values updated according to their model index. Since model roles |
9869 | // like 'display' are injected into the context in a special way by the |
9870 | // QML model classes, we need to catch it through a binding instead (which is |
9871 | // OK, since then we can also check that bindings are updated when reused). |
9872 | items = findItems<QQuickItem>(parent: listView, objectName: "delegate" ); |
9873 | for (const auto item : qAsConst(t&: items)) { |
9874 | const QString display = item->property(name: "displayBinding" ).toString(); |
9875 | const int modelIndex = item->property(name: "modelIndex" ).toInt(); |
9876 | QVERIFY(modelIndex >= initialItemCount); |
9877 | QCOMPARE(display, model.displayStringForRow(modelIndex)); |
9878 | } |
9879 | |
9880 | // Flick one page up. This time there shouldn't be any new items created, so |
9881 | // delegatesCreatedCount should remain unchanged. But while we reuse all the items |
9882 | // in the pool during the flick, we also fill it up again with all the items that |
9883 | // were inside the page that was flicked out. |
9884 | listView->setContentY(0); |
9885 | QVERIFY(QQuickTest::qWaitForItemPolished(listView)); |
9886 | const int countAfterUpFlick = listView->property(name: "delegatesCreatedCount" ).toInt(); |
9887 | const int poolSizeAfterUpFlick = itemView_d->model->poolSize(); |
9888 | QCOMPARE(countAfterUpFlick, countAfterDownFlick); |
9889 | QCOMPARE(poolSizeAfterUpFlick, initialItemCount); |
9890 | |
9891 | // Go through all items and check that they have been reused exactly once |
9892 | // (except for ListView.currentItem, which was never released). |
9893 | const auto listViewCurrentItem = listView->currentItem(); |
9894 | items = findItems<QQuickItem>(parent: listView, objectName: "delegate" ); |
9895 | for (const auto item : qAsConst(t&: items)) { |
9896 | const int reusedCount = item->property(name: "reusedCount" ).toInt(); |
9897 | if (item == listViewCurrentItem) |
9898 | QCOMPARE(reusedCount, 0); |
9899 | else |
9900 | QCOMPARE(reusedCount, 1); |
9901 | } |
9902 | |
9903 | // Go through all items again and check that all model data inside the delegate |
9904 | // have correct values now that they have been reused. |
9905 | items = findItems<QQuickItem>(parent: listView, objectName: "delegate" ); |
9906 | for (const auto item : qAsConst(t&: items)) { |
9907 | const QString display = item->property(name: "displayBinding" ).toString(); |
9908 | const int modelIndex = item->property(name: "modelIndex" ).toInt(); |
9909 | QVERIFY(modelIndex < initialItemCount); |
9910 | QCOMPARE(display, model.displayStringForRow(modelIndex)); |
9911 | } |
9912 | } |
9913 | |
9914 | void tst_QQuickListView::() // QTBUG-74046 |
9915 | { |
9916 | QQuickView *window = getView(); |
9917 | QQuickViewTestUtil::moveMouseAway(window); |
9918 | window->setSource(testFileUrl(fileName: "qtbug63974.qml" )); |
9919 | window->show(); |
9920 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
9921 | |
9922 | QQuickListView *listview = qmlobject_cast<QQuickListView *>(object: window->rootObject()); |
9923 | QVERIFY(listview); |
9924 | QCOMPARE(listview->contentY(), -20); |
9925 | |
9926 | // Drag downwards from the header: the list shouldn't move |
9927 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(10,10)); |
9928 | for (int i = 0; i < 10; ++i) |
9929 | QTest::mouseMove(window, pos: QPoint(10, 10 + i * 10)); |
9930 | QCOMPARE(listview->isMoving(), false); |
9931 | QCOMPARE(listview->contentY(), -20); |
9932 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier); |
9933 | |
9934 | // Drag upwards from the footer: the list shouldn't move |
9935 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(10,190)); |
9936 | for (int i = 0; i < 10; ++i) |
9937 | QTest::mouseMove(window, pos: QPoint(10, 190 - i * 10)); |
9938 | QCOMPARE(listview->isMoving(), false); |
9939 | QCOMPARE(listview->contentY(), -20); |
9940 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier); |
9941 | |
9942 | // Drag upwards from the middle: the list should move |
9943 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(10,100)); |
9944 | for (int i = 0; i < 10 && listview->contentY() == -20; ++i) |
9945 | QTest::mouseMove(window, pos: QPoint(10, 100 - i * 10)); |
9946 | QVERIFY(listview->isMoving()); |
9947 | QVERIFY(listview->contentY() > -20); |
9948 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier); |
9949 | |
9950 | releaseView(view: window); |
9951 | } |
9952 | |
9953 | void tst_QQuickListView::moveObjectModelItemToAnotherObjectModel() |
9954 | { |
9955 | QScopedPointer<QQuickView> window(createView()); |
9956 | window->setSource(testFileUrl(fileName: "moveObjectModelItemToAnotherObjectModel.qml" )); |
9957 | QCOMPARE(window->status(), QQuickView::Ready); |
9958 | window->show(); |
9959 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
9960 | |
9961 | QObject *root = window->rootObject(); |
9962 | QVERIFY(root); |
9963 | |
9964 | const QQuickListView *listView1 = root->property(name: "listView1" ).value<QQuickListView*>(); |
9965 | QVERIFY(listView1); |
9966 | |
9967 | const QQuickListView *listView2 = root->property(name: "listView2" ).value<QQuickListView*>(); |
9968 | QVERIFY(listView2); |
9969 | |
9970 | const QQuickItem *redRect = listView1->itemAtIndex(index: 0); |
9971 | QVERIFY(redRect); |
9972 | QCOMPARE(redRect->objectName(), QString::fromLatin1("redRect" )); |
9973 | |
9974 | QVERIFY(QMetaObject::invokeMethod(root, "moveRedRectToModel2" )); |
9975 | QVERIFY(QQuickTest::qIsPolishScheduled(listView2)); |
9976 | QVERIFY(QQuickTest::qWaitForItemPolished(listView2)); |
9977 | QVERIFY(redRect->isVisible()); |
9978 | QVERIFY(!QQuickItemPrivate::get(redRect)->culled); |
9979 | |
9980 | QVERIFY(QMetaObject::invokeMethod(root, "moveRedRectToModel1" )); |
9981 | QVERIFY(QQuickTest::qIsPolishScheduled(listView1)); |
9982 | QVERIFY(QQuickTest::qWaitForItemPolished(listView1)); |
9983 | QVERIFY(redRect->isVisible()); |
9984 | QVERIFY(!QQuickItemPrivate::get(redRect)->culled); |
9985 | } |
9986 | |
9987 | void tst_QQuickListView::changeModelAndDestroyTheOldOne() // QTBUG-80203 |
9988 | { |
9989 | QScopedPointer<QQuickView> window(createView()); |
9990 | window->setSource(testFileUrl(fileName: "changeModelAndDestroyTheOldOne.qml" )); |
9991 | window->resize(w: 640, h: 480); |
9992 | window->show(); |
9993 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
9994 | |
9995 | QQuickItem *root = window->rootObject(); |
9996 | QVERIFY(root); |
9997 | |
9998 | QVERIFY(QQuickTest::qWaitForItemPolished(root)); |
9999 | // no crash |
10000 | } |
10001 | |
10002 | void tst_QQuickListView::objectModelCulling() |
10003 | { |
10004 | QScopedPointer<QQuickView> window(createView()); |
10005 | window->setSource(testFileUrl(fileName: "objectModelCulling.qml" )); |
10006 | window->resize(w: 640, h: 480); |
10007 | window->show(); |
10008 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
10009 | QObject *root = window->rootObject(); |
10010 | QVERIFY(root); |
10011 | auto listView = root->findChild<QQuickListView *>(aName: "lv" ); |
10012 | QVERIFY(listView); |
10013 | auto model1 = root->findChild<QObject *>(aName: "model1" ); |
10014 | QVERIFY(model1); |
10015 | auto model2 = root->findChild<QObject *>(aName: "model2" ); |
10016 | QVERIFY(model2); |
10017 | auto redRect = root->findChild<QQuickItem *>(aName: "redRect" ); |
10018 | QVERIFY(redRect); |
10019 | auto redRectPriv = QQuickItemPrivate::get(item: redRect); |
10020 | listView->setModel(QVariant::fromValue(value: model1)); |
10021 | QVERIFY(!redRectPriv->culled); |
10022 | listView->setModel(QVariant::fromValue(value: model2)); |
10023 | QTRY_VERIFY(redRectPriv->culled); |
10024 | } |
10025 | |
10026 | class DataObject : public QObject |
10027 | { |
10028 | Q_OBJECT |
10029 | Q_PROPERTY(QString name READ name CONSTANT) |
10030 | Q_PROPERTY(QString color READ color CONSTANT) |
10031 | |
10032 | public: |
10033 | DataObject(QObject *parent = nullptr) : QObject(parent) {} |
10034 | DataObject(const QString &name, const QString &color, QObject *parent = nullptr) |
10035 | : QObject(parent), m_name(name), m_color(color) {} |
10036 | |
10037 | QString name() const { return m_name; } |
10038 | QString color() const { return m_color; } |
10039 | |
10040 | private: |
10041 | QString m_name; |
10042 | QString m_color; |
10043 | }; |
10044 | |
10045 | void tst_QQuickListView::requiredObjectListModel() |
10046 | { |
10047 | QList<QObject *> dataList = { |
10048 | new DataObject("Item 1" , "red" , this), |
10049 | new DataObject("Item 2" , "green" , this), |
10050 | new DataObject("Item 3" , "blue" , this), |
10051 | new DataObject("Item 4" , "yellow" , this) |
10052 | }; |
10053 | |
10054 | const auto deleter = qScopeGuard(f: [&](){ qDeleteAll(c: dataList); }); |
10055 | Q_UNUSED(deleter); |
10056 | |
10057 | QQuickView view; |
10058 | view.setInitialProperties({{ "model" , QVariant::fromValue(value: dataList) }}); |
10059 | view.setSource(testFileUrl(fileName: "requiredObjectListModel.qml" )); |
10060 | view.show(); |
10061 | |
10062 | QVERIFY(QTest::qWaitForWindowExposed(&view)); |
10063 | |
10064 | const auto *root = qobject_cast<QQuickListView *>(object: view.rootObject()); |
10065 | QVERIFY(root); |
10066 | |
10067 | QCOMPARE(root->count(), dataList.count()); |
10068 | |
10069 | for (int i = 0, end = dataList.count(); i != end; ++i) { |
10070 | const auto *rect = qobject_cast<QQuickRectangle *>(object: root->itemAtIndex(index: i)); |
10071 | QVERIFY(rect); |
10072 | const auto *data = qobject_cast<DataObject *>(object: dataList.at(i)); |
10073 | QVERIFY(data); |
10074 | |
10075 | QCOMPARE(rect->color(), QColor(data->color())); |
10076 | QCOMPARE(rect->property("name" ).toString(), data->name()); |
10077 | } |
10078 | } |
10079 | |
10080 | void tst_QQuickListView::clickHeaderAndFooterWhenClip() // QTBUG-85302 |
10081 | { |
10082 | QScopedPointer<QQuickView> window(createView()); |
10083 | window->setSource(testFileUrl(fileName: "clickHeaderAndFooterWhenClip.qml" )); |
10084 | window->resize(w: 640, h: 480); |
10085 | window->show(); |
10086 | |
10087 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
10088 | auto *root = window->rootObject(); |
10089 | QVERIFY(root); |
10090 | |
10091 | auto * = root->findChild<QQuickItem *>(aName: "header" ); |
10092 | QVERIFY(header); |
10093 | |
10094 | auto * = root->findChild<QQuickItem *>(aName: "footer" ); |
10095 | QVERIFY(footer); |
10096 | |
10097 | QVERIFY(root->property("headerPressed" ).isValid() && root->property("headerPressed" ).canConvert<bool>() && !root->property("headerPressed" ).toBool()); |
10098 | QTest::mouseClick(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: header->mapToItem(item: root, point: QPoint(header->width() / 2, header->height() / 2)).toPoint()); |
10099 | QVERIFY(root->property("headerPressed" ).toBool()); |
10100 | |
10101 | QVERIFY(root->property("footerPressed" ).isValid() && root->property("footerPressed" ).canConvert<bool>() && !root->property("footerPressed" ).toBool()); |
10102 | QTest::mouseClick(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: footer->mapToItem(item: root, point: QPoint(footer->width() / 2, footer->height() / 2)).toPoint()); |
10103 | QVERIFY(root->property("footerPressed" ).toBool()); |
10104 | } |
10105 | |
10106 | void tst_QQuickListView::animatedDelegate() |
10107 | { |
10108 | // QTBUG-86567: Should not crash |
10109 | QScopedPointer<QQuickView> window(createView()); |
10110 | window->setSource(testFileUrl(fileName: "animatedDelegate.qml" )); |
10111 | window->show(); |
10112 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
10113 | |
10114 | for (int i = 0; i < 100; ++i) { |
10115 | QMetaObject::invokeMethod(obj: window->rootObject(), member: "refreshModel" ); |
10116 | QTest::qWait(ms: 10); |
10117 | } |
10118 | } |
10119 | |
10120 | static void dragListView(QWindow *window, QPoint *startPos, const QPoint &delta) |
10121 | { |
10122 | auto drag_helper = [&](QWindow *window, QPoint *startPos, const QPoint &d) { |
10123 | QPoint pos = *startPos; |
10124 | const int dragDistance = d.manhattanLength(); |
10125 | const QPoint unitVector(qBound(min: -1, val: d.x(), max: 1), qBound(min: -1, val: d.y(), max: 1)); |
10126 | for (int i = 0; i < dragDistance; ++i) { |
10127 | QTest::mouseMove(window, pos); |
10128 | pos += unitVector; |
10129 | } |
10130 | // Move to the final position |
10131 | pos = *startPos + d; |
10132 | QTest::mouseMove(window, pos); |
10133 | *startPos = pos; |
10134 | }; |
10135 | |
10136 | if (delta.manhattanLength() == 0) |
10137 | return; |
10138 | const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); |
10139 | const QPoint unitVector(qBound(min: -1, val: delta.x(), max: 1), qBound(min: -1, val: delta.y(), max: 1)); |
10140 | // go just beyond the drag theshold |
10141 | drag_helper(window, startPos, unitVector * (dragThreshold + 1)); |
10142 | drag_helper(window, startPos, unitVector); |
10143 | |
10144 | // next drag will actually scroll the listview |
10145 | drag_helper(window, startPos, delta); |
10146 | } |
10147 | |
10148 | void tst_QQuickListView::dragDelegateWithMouseArea() |
10149 | { |
10150 | QFETCH(QQuickItemView::LayoutDirection, layoutDirection); |
10151 | |
10152 | QScopedPointer<QQuickView> window(createView()); |
10153 | QVERIFY(window); |
10154 | window->setSource(testFileUrl(fileName: "delegateWithMouseArea2.qml" )); |
10155 | window->show(); |
10156 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
10157 | |
10158 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
10159 | QVERIFY(listview != nullptr); |
10160 | |
10161 | const bool horizontal = layoutDirection < QQuickItemView::VerticalTopToBottom; |
10162 | listview->setOrientation(horizontal ? QQuickListView::Horizontal : QQuickListView::Vertical); |
10163 | |
10164 | if (horizontal) |
10165 | listview->setLayoutDirection(static_cast<Qt::LayoutDirection>(layoutDirection)); |
10166 | else |
10167 | listview->setVerticalLayoutDirection(static_cast<QQuickItemView::VerticalLayoutDirection>(layoutDirection)); |
10168 | |
10169 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
10170 | |
10171 | auto contentPosition = [&](QQuickListView *listview) { |
10172 | return (listview->orientation() == QQuickListView::Horizontal ? listview->contentX(): listview->contentY()); |
10173 | }; |
10174 | |
10175 | qreal expectedContentPosition = contentPosition(listview); |
10176 | QPoint startPos = (QPointF(listview->width(), listview->height())/2).toPoint(); |
10177 | QTest::mousePress(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: startPos, delay: 200); |
10178 | |
10179 | QPoint dragDelta(0, -10); |
10180 | |
10181 | if (layoutDirection == QQuickItemView::RightToLeft || layoutDirection == QQuickItemView::VerticalBottomToTop) |
10182 | dragDelta = -dragDelta; |
10183 | expectedContentPosition -= dragDelta.y(); |
10184 | if (horizontal) |
10185 | dragDelta = dragDelta.transposed(); |
10186 | |
10187 | dragListView(window: window.data(), startPos: &startPos, delta: dragDelta); |
10188 | |
10189 | QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: startPos, delay: 200); // Wait 200 ms before we release to avoid trigger a flick |
10190 | |
10191 | // wait for the "fixup" animation to finish |
10192 | QVERIFY(QTest::qWaitFor([&]() |
10193 | { return !listview->isMoving();} |
10194 | )); |
10195 | |
10196 | QCOMPARE(contentPosition(listview), expectedContentPosition); |
10197 | } |
10198 | |
10199 | void tst_QQuickListView::dragDelegateWithMouseArea_data() |
10200 | { |
10201 | QTest::addColumn<QQuickItemView::LayoutDirection>(name: "layoutDirection" ); |
10202 | |
10203 | for (int layDir = QQuickItemView::LeftToRight; layDir <= (int)QQuickItemView::VerticalBottomToTop; layDir++) { |
10204 | const char *enumValueName = QMetaEnum::fromType<QQuickItemView::LayoutDirection>().valueToKey(value: layDir); |
10205 | QTest::newRow(dataTag: enumValueName) << static_cast<QQuickItemView::LayoutDirection>(layDir); |
10206 | } |
10207 | } |
10208 | |
10209 | class SingletonModel : public QStringListModel |
10210 | { |
10211 | Q_OBJECT |
10212 | public: |
10213 | SingletonModel(QObject* parent = nullptr) : QStringListModel(parent) { } |
10214 | }; |
10215 | |
10216 | void tst_QQuickListView::singletonModelLifetime() |
10217 | { |
10218 | // this does not really test any functionality of listview, but we do not have a good way |
10219 | // to unit test QQmlAdaptorModel in isolation. |
10220 | qmlRegisterSingletonType<SingletonModel>(uri: "test" , versionMajor: 1, versionMinor: 0, typeName: "SingletonModel" , |
10221 | callback: [](QQmlEngine* , QJSEngine*) -> QObject* { return new SingletonModel; }); |
10222 | |
10223 | QQmlApplicationEngine engine(testFile(fileName: "singletonModelLifetime.qml" )); |
10224 | // needs event loop iteration for callLater to execute |
10225 | QTRY_VERIFY(engine.rootObjects().first()->property("alive" ).toBool()); |
10226 | } |
10227 | |
10228 | void tst_QQuickListView::QTBUG_92809() |
10229 | { |
10230 | QScopedPointer<QQuickView> window(createView()); |
10231 | QTRY_VERIFY(window); |
10232 | window->setSource(testFileUrl(fileName: "qtbug_92809.qml" )); |
10233 | window->show(); |
10234 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
10235 | |
10236 | QQuickListView *listview = findItem<QQuickListView>(parent: window->rootObject(), objectName: "list" ); |
10237 | QTRY_VERIFY(listview != nullptr); |
10238 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
10239 | listview->setCurrentIndex(1); |
10240 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
10241 | listview->setCurrentIndex(2); |
10242 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
10243 | listview->setCurrentIndex(3); |
10244 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
10245 | QTest::qWait(ms: 500); |
10246 | listview->setCurrentIndex(10); |
10247 | QVERIFY(QQuickTest::qWaitForItemPolished(listview)); |
10248 | QTest::qWait(ms: 500); |
10249 | int currentIndex = listview->currentIndex(); |
10250 | QTRY_COMPARE(currentIndex, 9); |
10251 | } |
10252 | |
10253 | QTEST_MAIN(tst_QQuickListView) |
10254 | |
10255 | #include "tst_qquicklistview.moc" |
10256 | |