From 5f5aba5b6e690ca54e66f41b93474f7e67e83c8b Mon Sep 17 00:00:00 2001 From: Martin Jones Date: Thu, 3 Nov 2011 15:52:13 +1000 Subject: [PATCH] Non-blocking view delegate instantiation. Task-number: QTBUG-21792 Change-Id: I29a4028cd24eb55d4768aacaa3abbd1786061398 Reviewed-by: Andrew den Exter --- src/declarative/items/qquickgridview.cpp | 75 ++-- src/declarative/items/qquickitemview.cpp | 138 +++++-- src/declarative/items/qquickitemview_p.h | 4 +- src/declarative/items/qquickitemview_p_p.h | 7 +- src/declarative/items/qquicklistview.cpp | 49 ++- src/declarative/items/qquickloader.cpp | 30 +- src/declarative/items/qquickpathview.cpp | 207 ++++++---- src/declarative/items/qquickpathview_p.h | 4 +- src/declarative/items/qquickpathview_p_p.h | 11 +- src/declarative/items/qquickrepeater.cpp | 94 +++-- src/declarative/items/qquickrepeater_p.h | 1 + src/declarative/items/qquickrepeater_p_p.h | 10 +- src/declarative/items/qquickvisualdatamodel.cpp | 425 +++++++++++++------- src/declarative/items/qquickvisualdatamodel_p.h | 4 +- src/declarative/items/qquickvisualitemmodel.cpp | 12 +- src/declarative/items/qquickvisualitemmodel_p.h | 9 +- .../qquickgridview/data/asyncloader.qml | 36 ++ .../declarative/qquickgridview/data/gridview1.qml | 2 + .../qquickgridview/tst_qquickgridview.cpp | 147 +++++++ .../qquicklistview/data/asyncloader.qml | 36 ++ .../qquicklistview/data/listviewtest.qml | 2 +- .../qquicklistview/tst_qquicklistview.cpp | 132 ++++++- .../qquickpathview/data/asyncloader.qml | 71 ++++ .../qquickpathview/tst_qquickpathview.cpp | 60 +++- .../qquickrepeater/data/asyncloader.qml | 32 ++ .../qquickrepeater/tst_qquickrepeater.cpp | 59 +++ 26 files changed, 1233 insertions(+), 424 deletions(-) create mode 100644 tests/auto/declarative/qquickgridview/data/asyncloader.qml create mode 100644 tests/auto/declarative/qquicklistview/data/asyncloader.qml create mode 100644 tests/auto/declarative/qquickpathview/data/asyncloader.qml create mode 100644 tests/auto/declarative/qquickrepeater/data/asyncloader.qml diff --git a/src/declarative/items/qquickgridview.cpp b/src/declarative/items/qquickgridview.cpp index 51131ab..44e1f14 100644 --- a/src/declarative/items/qquickgridview.cpp +++ b/src/declarative/items/qquickgridview.cpp @@ -294,7 +294,10 @@ qreal QQuickGridViewPrivate::colPosAt(int modelIndex) const if (FxViewItem *item = visibleItem(modelIndex)) return static_cast(item)->colPos(); if (!visibleItems.isEmpty()) { - if (modelIndex < visibleIndex) { + if (modelIndex == visibleIndex) { + FxGridItemSG *firstItem = static_cast(visibleItems.first()); + return firstItem->colPos(); + } else if (modelIndex < visibleIndex) { int count = (visibleIndex - modelIndex) % columns; int col = static_cast(visibleItems.first())->colPos() / colSize(); col = (columns - count + col) % columns; @@ -312,7 +315,10 @@ qreal QQuickGridViewPrivate::rowPosAt(int modelIndex) const if (FxViewItem *item = visibleItem(modelIndex)) return static_cast(item)->rowPos(); if (!visibleItems.isEmpty()) { - if (modelIndex < visibleIndex) { + if (modelIndex == visibleIndex) { + FxGridItemSG *firstItem = static_cast(visibleItems.first()); + return firstItem->rowPos(); + } else if (modelIndex < visibleIndex) { FxGridItemSG *firstItem = static_cast(visibleItems.first()); int firstCol = firstItem->colPos() / colSize(); int col = visibleIndex - modelIndex + (columns - firstCol - 1); @@ -438,10 +444,11 @@ bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, bool d bool changed = false; while (modelIndex < model->count() && rowPos <= fillTo + rowSize()*(columns - colNum)/(columns+1)) { -// qDebug() << "refill: append item" << modelIndex; - if (!(item = static_cast(createItem(modelIndex)))) +// qDebug() << "refill: append item" << modelIndex << colPos << rowPos; + if (!(item = static_cast(createItem(modelIndex, doBuffer)))) break; item->setPosition(colPos, rowPos); + item->item->setVisible(!doBuffer); visibleItems.append(item); if (++colNum >= columns) { colNum = 0; @@ -450,8 +457,6 @@ bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, bool d colPos = colNum * colSize(); ++modelIndex; changed = true; - if (doBuffer) // never buffer more than one item per frame - break; } // Find first column @@ -471,10 +476,11 @@ bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, bool d colPos = colNum * colSize(); while (visibleIndex > 0 && rowPos + rowSize() - 1 >= fillFrom - rowSize()*(colNum+1)/(columns+1)){ // qDebug() << "refill: prepend item" << visibleIndex-1 << "top pos" << rowPos << colPos; - if (!(item = static_cast(createItem(visibleIndex-1)))) + if (!(item = static_cast(createItem(visibleIndex-1, doBuffer)))) break; --visibleIndex; item->setPosition(colPos, rowPos); + item->item->setVisible(!doBuffer); visibleItems.prepend(item); if (--colNum < 0) { colNum = columns-1; @@ -482,8 +488,6 @@ bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, bool d } colPos = colNum * colSize(); changed = true; - if (doBuffer) // never buffer more than one item per frame - break; } return changed; @@ -491,6 +495,7 @@ bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, bool d bool QQuickGridViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo) { + Q_Q(QQuickGridView); FxGridItemSG *item = 0; bool changed = false; @@ -499,7 +504,7 @@ bool QQuickGridViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal buffer && item->rowPos()+rowSize()-1 < bufferFrom - rowSize()*(item->colPos()/colSize()+1)/(columns+1)) { if (item->attached->delayRemove()) break; -// qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endRowPos(); +// qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endRowPos(); if (item->index != -1) visibleIndex++; visibleItems.removeFirst(); @@ -511,7 +516,7 @@ bool QQuickGridViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal buffer && item->rowPos() > bufferTo + rowSize()*(columns - item->colPos()/colSize())/(columns+1)) { if (item->attached->delayRemove()) break; -// qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1; +// qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1; visibleItems.removeLast(); releaseItem(item); changed = true; @@ -1341,11 +1346,16 @@ void QQuickGridView::setHighlightFollowsCurrentItem(bool autoHighlight) This property determines whether delegates are retained outside the visible area of the view. - If non-zero the view will keep as many delegates + If non-zero the view may keep as many delegates instantiated as will fit within the buffer specified. For example, - if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is - set to 40, then up to 2 delegates above and 2 delegates below the visible - area may be retained. + if in a vertical view the delegate is 20 pixels high, there are 3 + columns and \c cacheBuffer is + set to 40, then up to 6 delegates above and 6 delegates below the visible + area may be created/retained. The buffered delegates are created asynchronously, + allowing creation to occur across multiple frames and reducing the + likelihood of skipping frames. In order to improve painting performance + delegates outside the visible area have their \l visible property set to + false until they are moved into the visible area. Note that cacheBuffer is not a pixel buffer - it only maintains additional instantiated delegates. @@ -1507,22 +1517,21 @@ void QQuickGridView::viewportMoved() return; d->inViewportMoved = true; - d->lazyRelease = true; - if (d->hData.flicking || d->vData.flicking) { - if (yflick()) { - if (d->vData.velocity > 0) - d->bufferMode = QQuickGridViewPrivate::BufferBefore; - else if (d->vData.velocity < 0) - d->bufferMode = QQuickGridViewPrivate::BufferAfter; - } - - if (xflick()) { - if (d->hData.velocity > 0) - d->bufferMode = QQuickGridViewPrivate::BufferBefore; - else if (d->hData.velocity < 0) - d->bufferMode = QQuickGridViewPrivate::BufferAfter; - } + // Set visibility of items to eliminate cost of items outside the visible area. + qreal from = d->isContentFlowReversed() ? -d->position()-d->size() : d->position(); + qreal to = d->isContentFlowReversed() ? -d->position() : d->position()+d->size(); + for (int i = 0; i < d->visibleItems.count(); ++i) { + FxGridItemSG *item = static_cast(d->visibleItems.at(i)); + item->item->setVisible(item->rowPos() + d->rowSize() >= from && item->rowPos() <= to); } + + if (yflick()) + d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferBefore : QQuickItemViewPrivate::BufferAfter; + else if (d->isRightToLeftTopToBottom()) + d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferAfter : QQuickItemViewPrivate::BufferBefore; + else + d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferBefore : QQuickItemViewPrivate::BufferAfter; + d->refill(); if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving) d->moveReason = QQuickGridViewPrivate::Mouse; @@ -1815,7 +1824,10 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::In } if (!item) item = createItem(modelIndex + i); + if (!item) + return false; + item->item->setVisible(true); visibleItems.insert(insertionIdx, item); if (!change.isMove()) { insertResult->addedItems.append(item); @@ -1843,7 +1855,10 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::In } if (!item) item = createItem(modelIndex + i); + if (!item) + return false; + item->item->setVisible(true); visibleItems.insert(index, item); if (!change.isMove()) insertResult->addedItems.append(item); diff --git a/src/declarative/items/qquickitemview.cpp b/src/declarative/items/qquickitemview.cpp index 63855ae..c329cd5 100644 --- a/src/declarative/items/qquickitemview.cpp +++ b/src/declarative/items/qquickitemview.cpp @@ -178,6 +178,7 @@ void QQuickItemView::setModel(const QVariant &model) if (d->model) { disconnect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)), this, SLOT(modelUpdated(QDeclarativeChangeSet,bool))); + disconnect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*))); disconnect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*))); disconnect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*))); } @@ -212,6 +213,9 @@ void QQuickItemView::setModel(const QVariant &model) if (d->model) { d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter; + connect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*))); + connect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*))); + connect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*))); if (isComponentComplete()) { updateSections(); d->refill(); @@ -231,8 +235,6 @@ void QQuickItemView::setModel(const QVariant &model) } connect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)), this, SLOT(modelUpdated(QDeclarativeChangeSet,bool))); - connect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*))); - connect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*))); emit countChanged(); } emit modelChanged(); @@ -785,26 +787,11 @@ void QQuickItemView::modelUpdated(const QDeclarativeChangeSet &changeSet, bool r } } -void QQuickItemView::createdItem(int index, QQuickItem *item) -{ - Q_D(QQuickItemView); - if (d->requestedIndex != index) { - item->setParentItem(contentItem()); - d->unrequestedItems.insert(item, index); - d->repositionPackageItemAt(item, index); - } -} - -void QQuickItemView::destroyingItem(QQuickItem *item) -{ - Q_D(QQuickItemView); - d->unrequestedItems.remove(item); -} - void QQuickItemView::animStopped() { Q_D(QQuickItemView); - d->bufferMode = QQuickItemViewPrivate::NoBuffer; + d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter; + d->refill(); if (d->haveHighlightRange && d->highlightRange == QQuickItemView::StrictlyEnforceRange) d->updateHighlight(); } @@ -1104,16 +1091,17 @@ QQuickItemViewPrivate::QQuickItemViewPrivate() , moveReason(Other) , visibleIndex(0) , currentIndex(-1), currentItem(0) - , trackedItem(0), requestedIndex(-1) + , trackedItem(0), requestedIndex(-1), requestedItem(0) , highlightComponent(0), highlight(0) , highlightRange(QQuickItemView::NoHighlightRange) , highlightRangeStart(0), highlightRangeEnd(0) , highlightMoveDuration(150) , headerComponent(0), header(0), footerComponent(0), footer(0) , minExtent(0), maxExtent(0) - , ownModel(false), wrap(false), lazyRelease(false), deferredRelease(false) + , ownModel(false), wrap(false), deferredRelease(false) , inApplyModelChanges(false), inViewportMoved(false), forceLayout(false), currentIndexCleared(false) , haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false) + , fillCacheBuffer(false), inRequest(false), requestedAsync(false) { } @@ -1229,6 +1217,7 @@ void QQuickItemViewPrivate::updateCurrent(int modelIndex) currentItem = 0; currentIndex = modelIndex; emit q->currentIndexChanged(); + emit q->currentItemChanged(); updateHighlight(); } else if (currentIndex != modelIndex) { currentIndex = modelIndex; @@ -1243,8 +1232,9 @@ void QQuickItemViewPrivate::updateCurrent(int modelIndex) } FxViewItem *oldCurrentItem = currentItem; + int oldCurrentIndex = currentIndex; currentIndex = modelIndex; - currentItem = createItem(modelIndex); + currentItem = createItem(modelIndex, false); if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item)) oldCurrentItem->attached->setIsCurrentItem(false); if (currentItem) { @@ -1254,7 +1244,10 @@ void QQuickItemViewPrivate::updateCurrent(int modelIndex) } updateHighlight(); - emit q->currentIndexChanged(); + if (oldCurrentIndex != currentIndex) + emit q->currentIndexChanged(); + if (oldCurrentItem != currentItem) + emit q->currentItemChanged(); releaseItem(oldCurrentItem); } @@ -1318,7 +1311,7 @@ void QQuickItemViewPrivate::refill(qreal from, qreal to, bool doBuffer) bool changed = addVisibleItems(fillFrom, fillTo, doBuffer); - if (!lazyRelease || !changed || deferredRelease) { // avoid destroying items in the same frame that we create + if (!changed || deferredRelease) { // avoid destroying items in the same frame that we create if (removeNonVisibleItems(bufferFrom, bufferTo)) changed = true; deferredRelease = false; @@ -1333,7 +1326,11 @@ void QQuickItemViewPrivate::refill(qreal from, qreal to, bool doBuffer) refill(from, to, true); } - lazyRelease = false; + if (!q->isMoving() && changed) { + fillCacheBuffer = true; + q->polish(); + } + if (prevCount != itemCount) emit q->countChanged(); } @@ -1380,8 +1377,11 @@ void QQuickItemViewPrivate::layout() return; } - if (!applyModelChanges() && !forceLayout) + if (!applyModelChanges() && !forceLayout) { + if (fillCacheBuffer) + refill(); return; + } forceLayout = false; layoutVisibleItems(); @@ -1407,6 +1407,7 @@ bool QQuickItemViewPrivate::applyModelChanges() Q_Q(QQuickItemView); if (!q->isComponentComplete() || !currentChanges.hasPendingChanges() || inApplyModelChanges) return false; + inApplyModelChanges = true; updateUnrequestedIndexes(); @@ -1557,38 +1558,87 @@ bool QQuickItemViewPrivate::applyModelChanges() return visibleAffected; } -FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex) +/* + This may return 0 if the item is being created asynchronously. + When the item becomes available, refill() will be called and the item + will be returned on the next call to createItem(). +*/ +FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, bool asynchronous) { Q_Q(QQuickItemView); + if (requestedIndex == modelIndex && (asynchronous || requestedAsync == asynchronous)) + return 0; + + if (requestedIndex != -1 && requestedIndex != modelIndex) { + delete requestedItem; + requestedItem = 0; + } requestedIndex = modelIndex; - FxViewItem *viewItem = 0; + requestedAsync = asynchronous; + inRequest = true; - if (QQuickItem *item = model->item(modelIndex, false)) { - viewItem = newViewItem(modelIndex, item); + if (QQuickItem *item = model->item(modelIndex, asynchronous)) { + item->setParentItem(q->contentItem()); + QDeclarative_setParent_noEvent(item, q->contentItem()); + requestedIndex = -1; + fillCacheBuffer = false; + FxViewItem *viewItem = requestedItem; + if (!viewItem) + viewItem = newViewItem(modelIndex, item); // already in cache, so viewItem not initialized in initItem() if (viewItem) { viewItem->index = modelIndex; - if (model->completePending()) { - // complete - viewItem->item->setZ(1); - QDeclarative_setParent_noEvent(viewItem->item, q->contentItem()); - viewItem->item->setParentItem(q->contentItem()); - model->completeItem(); - } else { - QDeclarative_setParent_noEvent(viewItem->item, q->contentItem()); - viewItem->item->setParentItem(q->contentItem()); - } // do other set up for the new item that should not happen // until after bindings are evaluated initializeViewItem(viewItem); + unrequestedItems.remove(item); + } + requestedItem = 0; + inRequest = false; + return viewItem; + } + + inRequest = false; + return 0; +} - unrequestedItems.remove(viewItem->item); +void QQuickItemView::createdItem(int index, QQuickItem *item) +{ + Q_D(QQuickItemView); + if (d->requestedIndex != index) { + item->setParentItem(contentItem()); + d->unrequestedItems.insert(item, index); + item->setVisible(false); + d->repositionPackageItemAt(item, index); + } else { + d->requestedIndex = -1; + if (!d->inRequest) { + if (index == d->currentIndex) + d->updateCurrent(index); + d->refill(); + } else { + d->fillCacheBuffer = true; + polish(); } } - requestedIndex = -1; - return viewItem; } +void QQuickItemView::initItem(int index, QQuickItem *item) +{ + Q_D(QQuickItemView); + item->setZ(1); + if (d->requestedIndex == index) { + item->setParentItem(contentItem()); + QDeclarative_setParent_noEvent(item, contentItem()); + d->requestedItem = d->newViewItem(index, item); + } +} + +void QQuickItemView::destroyingItem(QQuickItem *item) +{ + Q_D(QQuickItemView); + d->unrequestedItems.remove(item); +} void QQuickItemViewPrivate::releaseItem(FxViewItem *item) { diff --git a/src/declarative/items/qquickitemview_p.h b/src/declarative/items/qquickitemview_p.h index 8bb6353..b8e27e4 100644 --- a/src/declarative/items/qquickitemview_p.h +++ b/src/declarative/items/qquickitemview_p.h @@ -63,7 +63,7 @@ class Q_AUTOTEST_EXPORT QQuickItemView : public QQuickFlickable Q_PROPERTY(int count READ count NOTIFY countChanged) Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) - Q_PROPERTY(QQuickItem *currentItem READ currentItem NOTIFY currentIndexChanged) + Q_PROPERTY(QQuickItem *currentItem READ currentItem NOTIFY currentItemChanged) Q_PROPERTY(bool keyNavigationWraps READ isWrapEnabled WRITE setWrapEnabled NOTIFY keyNavigationWrapsChanged) Q_PROPERTY(int cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY cacheBufferChanged) @@ -161,6 +161,7 @@ signals: void delegateChanged(); void countChanged(); void currentIndexChanged(); + void currentItemChanged(); void keyNavigationWrapsChanged(); void cacheBufferChanged(); @@ -194,6 +195,7 @@ protected slots: virtual void updateSections() {} void destroyRemoved(); void createdItem(int index, QQuickItem *item); + void initItem(int index, QQuickItem *item); void modelUpdated(const QDeclarativeChangeSet &changeSet, bool reset); void destroyingItem(QQuickItem *item); void animStopped(); diff --git a/src/declarative/items/qquickitemview_p_p.h b/src/declarative/items/qquickitemview_p_p.h index ca4c0ce..4db274e 100644 --- a/src/declarative/items/qquickitemview_p_p.h +++ b/src/declarative/items/qquickitemview_p_p.h @@ -133,7 +133,7 @@ public: void refill(qreal from, qreal to, bool doBuffer = false); void mirrorChange(); - FxViewItem *createItem(int modelIndex); + FxViewItem *createItem(int modelIndex, bool asynchronous = false); virtual void releaseItem(FxViewItem *item); QQuickItem *createHighlightItem(); @@ -173,6 +173,7 @@ public: FxViewItem *trackedItem; QHash unrequestedItems; int requestedIndex; + FxViewItem *requestedItem; QQuickItemViewChangeSet currentChanges; // XXX split into struct @@ -193,7 +194,6 @@ public: bool ownModel : 1; bool wrap : 1; - bool lazyRelease : 1; bool deferredRelease : 1; bool inApplyModelChanges : 1; bool inViewportMoved : 1; @@ -203,6 +203,9 @@ public: bool autoHighlight : 1; bool highlightRangeStartValid : 1; bool highlightRangeEndValid : 1; + bool fillCacheBuffer : 1; + bool inRequest : 1; + bool requestedAsync : 1; protected: virtual Qt::Orientation layoutOrientation() const = 0; diff --git a/src/declarative/items/qquicklistview.cpp b/src/declarative/items/qquicklistview.cpp index 60164d5..86af390 100644 --- a/src/declarative/items/qquicklistview.cpp +++ b/src/declarative/items/qquicklistview.cpp @@ -603,27 +603,25 @@ bool QQuickListViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, bool d qreal pos = itemEnd; while (modelIndex < model->count() && pos <= fillTo) { // qDebug() << "refill: append item" << modelIndex << "pos" << pos; - if (!(item = static_cast(createItem(modelIndex)))) + if (!(item = static_cast(createItem(modelIndex, doBuffer)))) break; item->setPosition(pos); + item->item->setVisible(!doBuffer); pos += item->size() + spacing; visibleItems.append(item); ++modelIndex; changed = true; - if (doBuffer) // never buffer more than one item per frame - break; } - while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos >= fillFrom) { + while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos > fillFrom) { // qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos; - if (!(item = static_cast(createItem(visibleIndex-1)))) + if (!(item = static_cast(createItem(visibleIndex-1, doBuffer)))) break; --visibleIndex; visiblePos -= item->size() + spacing; item->setPosition(visiblePos); + item->item->setVisible(!doBuffer); visibleItems.prepend(item); changed = true; - if (doBuffer) // never buffer more than one item per frame - break; } return changed; @@ -640,7 +638,7 @@ bool QQuickListViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal buffer // over by refill(). int index = 0; while (visibleItems.count() > 1 && index < visibleItems.count() - && (item = visibleItems.at(index)) && item->endPosition() <= bufferFrom) { + && (item = visibleItems.at(index)) && item->endPosition() < bufferFrom) { if (item->attached->delayRemove()) break; if (item->size() > 0) { @@ -665,7 +663,7 @@ bool QQuickListViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal buffer while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) { if (item->attached->delayRemove()) break; -// qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position(); +// qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position(); visibleItems.removeLast(); releaseItem(item); changed = true; @@ -1916,11 +1914,15 @@ void QQuickListView::setOrientation(QQuickListView::Orientation orientation) This property determines whether delegates are retained outside the visible area of the view. - If this value is non-zero, the view keeps as many delegates + If this value is non-zero, the view may keep as many delegates instantiated as it can fit within the buffer specified. For example, if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is set to 40, then up to 2 delegates above and 2 delegates below the visible - area may be retained. + area may be created/retained. The buffered delegates are created asynchronously, + allowing creation to occur across multiple frames and reducing the + likelihood of skipping frames. In order to improve painting performance + delegates outside the visible area have their \l visible property set to + false until they are moved into the visible area. Note that cacheBuffer is not a pixel buffer - it only maintains additional instantiated delegates. @@ -2163,7 +2165,22 @@ void QQuickListView::viewportMoved() if (d->inViewportMoved) return; d->inViewportMoved = true; - d->lazyRelease = true; + + // Set visibility of items to eliminate cost of items outside the visible area. + qreal from = d->isContentFlowReversed() ? -d->position()-d->size() : d->position(); + qreal to = d->isContentFlowReversed() ? -d->position() : d->position()+d->size(); + for (int i = 0; i < d->visibleItems.count(); ++i) { + FxViewItem *item = static_cast(d->visibleItems.at(i)); + item->item->setVisible(item->endPosition() >= from && item->position() <= to); + } + + if (yflick()) + d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter; + else if (d->isRightToLeft()) + d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore; + else + d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter; + d->refill(); if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving) d->moveReason = QQuickListViewPrivate::Mouse; @@ -2201,13 +2218,11 @@ void QQuickListView::viewportMoved() if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2) && minY != d->vData.flickTarget) d->flickY(-d->vData.smoothVelocity.value()); - d->bufferMode = QQuickListViewPrivate::BufferBefore; } else if (d->vData.velocity < 0) { const qreal maxY = maxYExtent(); if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2) && maxY != d->vData.flickTarget) d->flickY(-d->vData.smoothVelocity.value()); - d->bufferMode = QQuickListViewPrivate::BufferAfter; } } @@ -2217,13 +2232,11 @@ void QQuickListView::viewportMoved() if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2) && minX != d->hData.flickTarget) d->flickX(-d->hData.smoothVelocity.value()); - d->bufferMode = d->isRightToLeft() ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore; } else if (d->hData.velocity < 0) { const qreal maxX = maxXExtent(); if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2) && maxX != d->hData.flickTarget) d->flickX(-d->hData.smoothVelocity.value()); - d->bufferMode = d->isRightToLeft() ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter; } } d->inFlickCorrection = false; @@ -2398,6 +2411,8 @@ bool QQuickListViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::In } if (!item) item = createItem(modelIndex + i); + if (!item) + return false; visibleItems.insert(insertionIdx, item); if (!change.isMove()) { @@ -2420,6 +2435,8 @@ bool QQuickListViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::In } if (!item) item = createItem(modelIndex + i); + if (!item) + return false; visibleItems.insert(index, item); if (!change.isMove()) diff --git a/src/declarative/items/qquickloader.cpp b/src/declarative/items/qquickloader.cpp index 242d9f3..f9fd914 100644 --- a/src/declarative/items/qquickloader.cpp +++ b/src/declarative/items/qquickloader.cpp @@ -576,6 +576,7 @@ void QQuickLoaderPrivate::incubatorStateChanged(QDeclarativeIncubator::Status st itemContext = 0; delete obj; } + incubator->clear(); } else if (status == QDeclarativeIncubator::Error) { if (!incubator->errors().isEmpty()) QDeclarativeEnginePrivate::warning(qmlEngine(q), incubator->errors()); @@ -616,19 +617,12 @@ void QQuickLoaderPrivate::_q_sourceLoaded() itemContext = new QDeclarativeContext(creationContext); itemContext->setContextObject(q); - if (incubator) { - bool async = incubator->incubationMode() == QDeclarativeIncubator::Asynchronous; - if (asynchronous != async) { - delete incubator; - incubator = 0; - } - } - if (!incubator) - incubator = new QQuickLoaderIncubator(this, asynchronous ? QDeclarativeIncubator::Asynchronous : QDeclarativeIncubator::AsynchronousIfNested); + delete incubator; + incubator = new QQuickLoaderIncubator(this, asynchronous ? QDeclarativeIncubator::Asynchronous : QDeclarativeIncubator::AsynchronousIfNested); component->create(*incubator, itemContext); - if (incubator->status() == QDeclarativeIncubator::Loading) + if (incubator && incubator->status() == QDeclarativeIncubator::Loading) emit q->statusChanged(); } @@ -756,6 +750,22 @@ qreal QQuickLoader::progress() const This property holds whether the component will be instantiated asynchronously. +Loading asynchronously creates the objects declared by the component +across multiple frames, and reduces the +likelihood of glitches in animation. When loading asynchronously the status +will change to Loader.Loading. Once the entire component has been created, the +\l item will be available and the status will change to Loader.Ready. + +To avoid seeing the items loading progressively set \c visible appropriately, e.g. + +\code +Loader { + source: "mycomponent.qml" + asynchronous: true + visible: status == Loader.Ready +} +\endcode + Note that this property affects object instantiation only; it is unrelated to loading a component asynchronously via a network. */ diff --git a/src/declarative/items/qquickpathview.cpp b/src/declarative/items/qquickpathview.cpp index 1963307..f262562 100644 --- a/src/declarative/items/qquickpathview.cpp +++ b/src/declarative/items/qquickpathview.cpp @@ -106,34 +106,71 @@ void QQuickPathViewPrivate::init() FAST_CONNECT(&tl, SIGNAL(completed()), q, SLOT(movementEnding())) } -QQuickItem *QQuickPathViewPrivate::getItem(int modelIndex, bool onPath) +QQuickItem *QQuickPathViewPrivate::getItem(int modelIndex, qreal z, bool onPath) { Q_Q(QQuickPathView); requestedIndex = modelIndex; + requestedOnPath = onPath; + requestedZ = z; + inRequest = true; QQuickItem *item = model->item(modelIndex, false); if (item) { - if (!attType) { - // pre-create one metatype to share with all attached objects - attType = new QDeclarativeOpenMetaObjectType(&QQuickPathViewAttached::staticMetaObject, qmlEngine(q)); - foreach (const QString &attr, path->attributes()) - attType->createProperty(attr.toUtf8()); - } + QDeclarative_setParent_noEvent(item, q); + item->setParentItem(q); + requestedIndex = -1; qPathViewAttachedType = attType; QQuickPathViewAttached *att = static_cast(qmlAttachedPropertiesObject(item)); qPathViewAttachedType = 0; - if (att) { - att->m_view = q; + if (att) att->setOnPath(onPath); - } - QDeclarative_setParent_noEvent(item, q); - item->setParentItem(q); QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry); } - requestedIndex = -1; + inRequest = false; return item; } +void QQuickPathView::createdItem(int index, QQuickItem *item) +{ + Q_D(QQuickPathView); + if (d->requestedIndex != index) { + qPathViewAttachedType = d->attachedType(); + QQuickPathViewAttached *att = static_cast(qmlAttachedPropertiesObject(item)); + qPathViewAttachedType = 0; + if (att) { + att->m_view = this; + att->setOnPath(false); + } + item->setParentItem(this); + QDeclarative_setParent_noEvent(item, this); + d->updateItem(item, index < d->firstIndex ? 0.0 : 1.0); + } else { + d->requestedIndex = -1; + if (!d->inRequest) + refill(); + } +} + +void QQuickPathView::initItem(int index, QQuickItem *item) +{ + Q_D(QQuickPathView); + if (d->requestedIndex == index) { + item->setParentItem(this); + qPathViewAttachedType = d->attachedType(); + QQuickPathViewAttached *att = static_cast(qmlAttachedPropertiesObject(item)); + qPathViewAttachedType = 0; + if (att) { + att->m_view = this; + qreal percent = d->positionOfIndex(index); + foreach (const QString &attr, d->path->attributes()) + att->setValue(attr.toUtf8(), d->path->attributeAt(attr, percent)); + item->setZ(d->requestedZ); + if (att) + att->setOnPath(d->requestedOnPath); + } + } +} + void QQuickPathViewPrivate::releaseItem(QQuickItem *item) { if (!item || !model) @@ -152,6 +189,19 @@ QQuickPathViewAttached *QQuickPathViewPrivate::attached(QQuickItem *item) return static_cast(qmlAttachedPropertiesObject(item, false)); } +QDeclarativeOpenMetaObjectType *QQuickPathViewPrivate::attachedType() +{ + Q_Q(QQuickPathView); + if (!attType) { + // pre-create one metatype to share with all attached objects + attType = new QDeclarativeOpenMetaObjectType(&QQuickPathViewAttached::staticMetaObject, qmlEngine(q)); + foreach (const QString &attr, path->attributes()) + attType->createProperty(attr.toUtf8()); + } + + return attType; +} + void QQuickPathViewPrivate::clear() { if (currentItem) { @@ -493,6 +543,7 @@ void QQuickPathView::setModel(const QVariant &model) disconnect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)), this, SLOT(modelUpdated(QDeclarativeChangeSet,bool))); disconnect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*))); + disconnect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*))); for (int i=0; iitems.count(); i++){ QQuickItem *p = d->items[i]; d->releaseItem(p); @@ -524,6 +575,7 @@ void QQuickPathView::setModel(const QVariant &model) connect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)), this, SLOT(modelUpdated(QDeclarativeChangeSet,bool))); connect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*))); + connect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*))); d->modelCount = d->model->count(); if (d->model->count()) d->offset = qmlMod(d->offset, qreal(d->model->count())); @@ -595,36 +647,28 @@ void QQuickPathView::setCurrentIndex(int idx) Q_D(QQuickPathView); if (d->model && d->modelCount) idx = qAbs(idx % d->modelCount); - if (d->model && idx != d->currentIndex) { + if (d->model && (idx != d->currentIndex || !d->currentItem)) { if (d->currentItem) { if (QQuickPathViewAttached *att = d->attached(d->currentItem)) att->setIsCurrentItem(false); d->releaseItem(d->currentItem); } + int oldCurrentIdx = d->currentIndex; + QQuickItem *oldCurrentItem = d->currentItem; d->currentItem = 0; d->moveReason = QQuickPathViewPrivate::SetIndex; d->currentIndex = idx; if (d->modelCount) { - int itemIndex = (idx - d->firstIndex + d->modelCount) % d->modelCount; - if (itemIndex < d->items.count()) { - d->currentItem = d->model->item(d->currentIndex, true); - d->currentItem->setFocus(true); - if (QQuickPathViewAttached *att = d->attached(d->currentItem)) - att->setIsCurrentItem(true); - } else { - d->currentItem = d->getItem(d->currentIndex, false); - d->updateItem(d->currentItem, d->currentIndex < d->firstIndex ? 0.0 : 1.0); - if (QQuickPathViewAttached *att = d->attached(d->currentItem)) - att->setIsCurrentItem(true); - if (d->model->completePending()) - d->model->completeItem(); - } + d->createCurrentItem(); if (d->haveHighlightRange && d->highlightRangeMode == QQuickPathView::StrictlyEnforceRange) d->snapToCurrent(); d->currentItemOffset = d->positionOfIndex(d->currentIndex); d->updateHighlight(); } - emit currentIndexChanged(); + if (oldCurrentIdx != d->currentIndex) + emit currentIndexChanged(); + if (oldCurrentItem != d->currentItem) + emit currentItemChanged(); } } @@ -1392,6 +1436,7 @@ void QQuickPathView::refill() if (!d->items.count()) d->firstIndex = -1; + bool waiting = false; if (d->modelCount) { // add items to beginning and end int count = d->pathItems == -1 ? d->modelCount : qMin(d->pathItems, d->modelCount); @@ -1406,10 +1451,12 @@ void QQuickPathView::refill() } qreal pos = d->positionOfIndex(idx); while ((pos > startPos || !d->items.count()) && d->items.count() < count) { - // qDebug() << "append" << idx; - QQuickItem *item = d->getItem(idx); - if (d->model->completePending()) - item->setZ(idx+1); +// qDebug() << "append" << idx; + QQuickItem *item = d->getItem(idx, idx+1); + if (!item) { + waiting = true; + break; + } if (d->currentIndex == idx) { currentVisible = true; d->currentItemOffset = pos; @@ -1418,8 +1465,6 @@ void QQuickPathView::refill() d->firstIndex = idx; d->items.append(item); d->updateItem(item, pos); - if (d->model->completePending()) - d->model->completeItem(); ++idx; if (idx >= d->modelCount) idx = 0; @@ -1430,19 +1475,19 @@ void QQuickPathView::refill() if (idx < 0) idx = d->modelCount - 1; pos = d->positionOfIndex(idx); - while ((pos >= 0.0 && pos < startPos) && d->items.count() < count) { - // qDebug() << "prepend" << idx; - QQuickItem *item = d->getItem(idx); - if (d->model->completePending()) - item->setZ(idx+1); + while (!waiting && (pos >= 0.0 && pos < startPos) && d->items.count() < count) { +// qDebug() << "prepend" << idx; + QQuickItem *item = d->getItem(idx, idx+1); + if (!item) { + waiting = true; + break; + } if (d->currentIndex == idx) { currentVisible = true; d->currentItemOffset = pos; } d->items.prepend(item); d->updateItem(item, pos); - if (d->model->completePending()) - d->model->completeItem(); d->firstIndex = idx; idx = d->firstIndex - 1; if (idx < 0) @@ -1457,19 +1502,19 @@ void QQuickPathView::refill() if (d->currentItem) { if (QQuickPathViewAttached *att = d->attached(d->currentItem)) att->setOnPath(false); - } else if (d->currentIndex >= 0 && d->currentIndex < d->modelCount) { - d->currentItem = d->getItem(d->currentIndex, false); - d->updateItem(d->currentItem, d->currentIndex < d->firstIndex ? 0.0 : 1.0); + } else if (!waiting && d->currentIndex >= 0 && d->currentIndex < d->modelCount) { + if (d->currentItem = d->getItem(d->currentIndex, d->currentIndex, false)) { + d->updateItem(d->currentItem, d->currentIndex < d->firstIndex ? 0.0 : 1.0); + if (QQuickPathViewAttached *att = d->attached(d->currentItem)) + att->setIsCurrentItem(true); + } + } + } else if (!waiting && !d->currentItem) { + if (d->currentItem = d->getItem(d->currentIndex, d->currentIndex, true)) { + d->currentItem->setFocus(true); if (QQuickPathViewAttached *att = d->attached(d->currentItem)) att->setIsCurrentItem(true); - if (d->model->completePending()) - d->model->completeItem(); } - } else if (!d->currentItem) { - d->currentItem = d->model->item(d->currentIndex, true); - d->currentItem->setFocus(true); - if (QQuickPathViewAttached *att = d->attached(d->currentItem)) - att->setIsCurrentItem(true); } if (d->highlightItem && d->haveHighlightRange && d->highlightRangeMode == QQuickPathView::StrictlyEnforceRange) { @@ -1582,29 +1627,6 @@ void QQuickPathView::modelUpdated(const QDeclarativeChangeSet &changeSet, bool r emit countChanged(); } -void QQuickPathView::createdItem(int index, QQuickItem *item) -{ - Q_D(QQuickPathView); - if (d->requestedIndex != index) { - if (!d->attType) { - // pre-create one metatype to share with all attached objects - d->attType = new QDeclarativeOpenMetaObjectType(&QQuickPathViewAttached::staticMetaObject, qmlEngine(this)); - foreach (const QString &attr, d->path->attributes()) - d->attType->createProperty(attr.toUtf8()); - } - qPathViewAttachedType = d->attType; - QQuickPathViewAttached *att = static_cast(qmlAttachedPropertiesObject(item)); - qPathViewAttachedType = 0; - if (att) { - att->m_view = this; - att->setOnPath(false); - } - QDeclarative_setParent_noEvent(item, this); - item->setParentItem(this); - d->updateItem(item, index < d->firstIndex ? 0.0 : 1.0); - } -} - void QQuickPathView::destroyingItem(QQuickItem *item) { Q_UNUSED(item); @@ -1646,6 +1668,26 @@ int QQuickPathViewPrivate::calcCurrentIndex() return current; } +void QQuickPathViewPrivate::createCurrentItem() +{ + if (requestedIndex != -1) + return; + int itemIndex = (currentIndex - firstIndex + modelCount) % modelCount; + if (itemIndex < items.count()) { + if (currentItem = getItem(currentIndex, currentIndex, true)) { + currentItem->setFocus(true); + if (QQuickPathViewAttached *att = attached(currentItem)) + att->setIsCurrentItem(true); + } + } else if (currentIndex >= 0 && currentIndex < modelCount) { + if (currentItem = getItem(currentIndex, currentIndex, false)) { + updateItem(currentItem, currentIndex < firstIndex ? 0.0 : 1.0); + if (QQuickPathViewAttached *att = attached(currentItem)) + att->setIsCurrentItem(true); + } + } +} + void QQuickPathViewPrivate::updateCurrent() { Q_Q(QQuickPathView); @@ -1663,20 +1705,7 @@ void QQuickPathViewPrivate::updateCurrent() } currentIndex = idx; currentItem = 0; - int itemIndex = (idx - firstIndex + modelCount) % modelCount; - if (itemIndex < items.count()) { - currentItem = model->item(currentIndex, true); - currentItem->setFocus(true); - if (QQuickPathViewAttached *att = attached(currentItem)) - att->setIsCurrentItem(true); - } else if (currentIndex >= 0 && currentIndex < modelCount) { - currentItem = getItem(currentIndex, false); - updateItem(currentItem, currentIndex < firstIndex ? 0.0 : 1.0); - if (QQuickPathViewAttached *att = attached(currentItem)) - att->setIsCurrentItem(true); - if (model->completePending()) - model->completeItem(); - } + createCurrentItem(); emit q->currentIndexChanged(); } } diff --git a/src/declarative/items/qquickpathview_p.h b/src/declarative/items/qquickpathview_p.h index 657d721..ebc1587 100644 --- a/src/declarative/items/qquickpathview_p.h +++ b/src/declarative/items/qquickpathview_p.h @@ -64,7 +64,7 @@ class Q_AUTOTEST_EXPORT QQuickPathView : public QQuickItem Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged) Q_PROPERTY(QDeclarativePath *path READ path WRITE setPath NOTIFY pathChanged) Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) - Q_PROPERTY(QQuickItem *currentItem READ currentItem NOTIFY currentIndexChanged) + Q_PROPERTY(QQuickItem *currentItem READ currentItem NOTIFY currentItemChanged) Q_PROPERTY(qreal offset READ offset WRITE setOffset NOTIFY offsetChanged) Q_PROPERTY(QDeclarativeComponent *highlight READ highlight WRITE setHighlight NOTIFY highlightChanged) @@ -151,6 +151,7 @@ public Q_SLOTS: Q_SIGNALS: void currentIndexChanged(); + void currentItemChanged(); void offsetChanged(); void modelChanged(); void countChanged(); @@ -190,6 +191,7 @@ private Q_SLOTS: void movementEnding(); void modelUpdated(const QDeclarativeChangeSet &changeSet, bool reset); void createdItem(int index, QQuickItem *item); + void initItem(int index, QQuickItem *item); void destroyingItem(QQuickItem *item); void pathUpdated(); diff --git a/src/declarative/items/qquickpathview_p_p.h b/src/declarative/items/qquickpathview_p_p.h index 87d9313..9bf329d 100644 --- a/src/declarative/items/qquickpathview_p_p.h +++ b/src/declarative/items/qquickpathview_p_p.h @@ -80,10 +80,10 @@ public: , lastElapsed(0), offset(0.0), offsetAdj(0.0), mappedRange(1.0) , stealMouse(false), ownModel(false), interactive(true), haveHighlightRange(true) , autoHighlight(true), highlightUp(false), layoutScheduled(false) - , moving(false), flicking(false) + , moving(false), flicking(false), requestedOnPath(false), inRequest(false) , dragMargin(0), deceleration(100) , moveOffset(this, &QQuickPathViewPrivate::setAdjustedOffset) - , firstIndex(-1), pathItems(-1), requestedIndex(-1) + , firstIndex(-1), pathItems(-1), requestedIndex(-1), requestedZ(0) , moveReason(Other), moveDirection(Shortest), attType(0), highlightComponent(0), highlightItem(0) , moveHighlight(this, &QQuickPathViewPrivate::setHighlightPosition) , highlightPosition(0) @@ -112,9 +112,10 @@ public: } } - QQuickItem *getItem(int modelIndex, bool onPath = true); + QQuickItem *getItem(int modelIndex, qreal z = 0, bool onPath=true); void releaseItem(QQuickItem *item); QQuickPathViewAttached *attached(QQuickItem *item); + QDeclarativeOpenMetaObjectType *attachedType(); void clear(); void updateMappedRange(); qreal positionOfIndex(qreal index) const; @@ -130,6 +131,7 @@ public: void handleMouseReleaseEvent(QMouseEvent *); int calcCurrentIndex(); + void createCurrentItem(); void updateCurrent(); static void fixOffsetCallback(void*); void fixOffset(); @@ -160,6 +162,8 @@ public: bool layoutScheduled : 1; bool moving : 1; bool flicking : 1; + bool requestedOnPath : 1; + bool inRequest : 1; QElapsedTimer lastPosTime; QPointF lastPos; qreal dragMargin; @@ -169,6 +173,7 @@ public: int firstIndex; int pathItems; int requestedIndex; + qreal requestedZ; QList items; QList itemCache; QDeclarativeGuard model; diff --git a/src/declarative/items/qquickrepeater.cpp b/src/declarative/items/qquickrepeater.cpp index 48632ef..f207afd 100644 --- a/src/declarative/items/qquickrepeater.cpp +++ b/src/declarative/items/qquickrepeater.cpp @@ -51,7 +51,7 @@ QT_BEGIN_NAMESPACE QQuickRepeaterPrivate::QQuickRepeaterPrivate() -: model(0), ownModel(false) + : model(0), ownModel(false), inRequest(false), itemCount(0), createFrom(-1) { } @@ -188,10 +188,8 @@ void QQuickRepeater::setModel(const QVariant &model) if (d->model) { disconnect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)), this, SLOT(modelUpdated(QDeclarativeChangeSet,bool))); - /* disconnect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*))); - disconnect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*))); - */ +// disconnect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*))); } d->dataSource = model; QObject *object = qvariant_cast(model); @@ -215,10 +213,8 @@ void QQuickRepeater::setModel(const QVariant &model) if (d->model) { connect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)), this, SLOT(modelUpdated(QDeclarativeChangeSet,bool))); - /* connect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*))); - connect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*))); - */ +// connect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*))); regenerate(); } emit modelChanged(); @@ -339,14 +335,15 @@ void QQuickRepeater::clear() bool complete = isComponentComplete(); if (d->model) { - while (d->deletables.count() > 0) { - QQuickItem *item = d->deletables.takeLast(); + for (int i = 0; i < d->deletables.count(); ++i) { + QQuickItem *item = d->deletables.at(i); if (complete) - emit itemRemoved(d->deletables.count()-1, item); + emit itemRemoved(i, item); d->model->release(item); } } d->deletables.clear(); + d->itemCount = 0; } void QQuickRepeater::regenerate() @@ -360,16 +357,51 @@ void QQuickRepeater::regenerate() if (!d->model || !d->model->count() || !d->model->isValid() || !parentItem() || !isComponentComplete()) return; - for (int ii = 0; ii < count(); ++ii) { - QQuickItem *item = d->model->item(ii); - if (item) { - QDeclarative_setParent_noEvent(item, parentItem()); - item->setParentItem(parentItem()); - item->stackBefore(this); - d->deletables << item; - emit itemAdded(ii, item); + d->itemCount = count(); + d->deletables.resize(d->itemCount); + d->createFrom = 0; + d->createItems(); +} + +void QQuickRepeaterPrivate::createItems() +{ + Q_Q(QQuickRepeater); + if (createFrom == -1) + return; + inRequest = true; + for (int ii = createFrom; ii < itemCount; ++ii) { + if (!deletables.at(ii)) { + QQuickItem *item = model->item(ii, false); + if (!item) { + createFrom = ii; + break; + } + deletables[ii] = item; + QDeclarative_setParent_noEvent(item, q->parentItem()); + item->setParentItem(q->parentItem()); + if (ii > 0 && deletables.at(ii-1)) { + item->stackAfter(deletables.at(ii-1)); + } else { + QQuickItem *after = q; + for (int si = ii+1; si < itemCount; ++si) { + if (deletables.at(si)) { + after = deletables.at(si); + break; + } + } + item->stackBefore(after); + } + emit q->itemAdded(ii, item); } } + inRequest = false; +} + +void QQuickRepeater::createdItem(int index, QQuickItem *item) +{ + Q_D(QQuickRepeater); + if (!d->inRequest) + d->createItems(); } void QQuickRepeater::modelUpdated(const QDeclarativeChangeSet &changeSet, bool reset) @@ -385,7 +417,7 @@ void QQuickRepeater::modelUpdated(const QDeclarativeChangeSet &changeSet, bool r } int difference = 0; - QHash > > moved; + QHash > > moved; foreach (const QDeclarativeChangeSet::Remove &remove, changeSet.removes()) { int index = qMin(remove.index, d->deletables.count()); int count = qMin(remove.index + remove.count, d->deletables.count()) - index; @@ -395,19 +427,22 @@ void QQuickRepeater::modelUpdated(const QDeclarativeChangeSet &changeSet, bool r d->deletables.begin() + index, d->deletables.begin() + index + count); } else while (count--) { - QQuickItem *item = d->deletables.takeAt(index); + QQuickItem *item = d->deletables.at(index); + d->deletables.remove(index); emit itemRemoved(index, item); if (item) d->model->release(item); + --d->itemCount; } difference -= remove.count; } + d->createFrom = -1; foreach (const QDeclarativeChangeSet::Insert &insert, changeSet.inserts()) { int index = qMin(insert.index, d->deletables.count()); if (insert.isMove()) { - QList > items = moved.value(insert.moveId); + QVector > items = moved.value(insert.moveId); d->deletables = d->deletables.mid(0, index) + items + d->deletables.mid(index); QQuickItem *stackBefore = index + items.count() < d->deletables.count() ? d->deletables.at(index + items.count()) @@ -416,21 +451,16 @@ void QQuickRepeater::modelUpdated(const QDeclarativeChangeSet &changeSet, bool r d->deletables.at(i)->stackBefore(stackBefore); } else for (int i = 0; i < insert.count; ++i) { int modelIndex = index + i; - QQuickItem *item = d->model->item(modelIndex); - if (item) { - QDeclarative_setParent_noEvent(item, parentItem()); - item->setParentItem(parentItem()); - if (modelIndex < d->deletables.count()) - item->stackBefore(d->deletables.at(modelIndex)); - else - item->stackBefore(this); - d->deletables.insert(modelIndex, item); - emit itemAdded(modelIndex, item); - } + ++d->itemCount; + d->deletables.insert(modelIndex, 0); + if (d->createFrom == -1) + d->createFrom = modelIndex; } difference += insert.count; } + d->createItems(); + if (difference != 0) emit countChanged(); } diff --git a/src/declarative/items/qquickrepeater_p.h b/src/declarative/items/qquickrepeater_p.h index 66e583e..6da6adb 100644 --- a/src/declarative/items/qquickrepeater_p.h +++ b/src/declarative/items/qquickrepeater_p.h @@ -94,6 +94,7 @@ protected: void itemChange(ItemChange change, const ItemChangeData &value); private Q_SLOTS: + void createdItem(int index, QQuickItem *item); void modelUpdated(const QDeclarativeChangeSet &changeSet, bool reset); private: diff --git a/src/declarative/items/qquickrepeater_p_p.h b/src/declarative/items/qquickrepeater_p_p.h index a1ec159..962d177 100644 --- a/src/declarative/items/qquickrepeater_p_p.h +++ b/src/declarative/items/qquickrepeater_p_p.h @@ -71,11 +71,17 @@ public: QQuickRepeaterPrivate(); ~QQuickRepeaterPrivate(); +private: + void createItems(); + QQuickVisualModel *model; QVariant dataSource; - bool ownModel; + bool ownModel : 1; + bool inRequest : 1; + int itemCount; + int createFrom; - QList > deletables; + QVector > deletables; }; QT_END_NAMESPACE diff --git a/src/declarative/items/qquickvisualdatamodel.cpp b/src/declarative/items/qquickvisualdatamodel.cpp index ce85484..649ca6f 100644 --- a/src/declarative/items/qquickvisualdatamodel.cpp +++ b/src/declarative/items/qquickvisualdatamodel.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -62,6 +63,7 @@ #include #include #include +#include #include #include @@ -71,11 +73,33 @@ QT_BEGIN_NAMESPACE typedef QDeclarativeListCompositor Compositor; +class QQuickVisualDataModelPrivate; +class QVDMIncubationTask : public QDeclarativeIncubator +{ +public: + QVDMIncubationTask(QQuickVisualDataModelPrivate *l, IncubationMode mode) + : QDeclarativeIncubator(mode) + , incubating(0) + , incubatingContext(0) + , vdm(l) {} + + virtual void statusChanged(Status); + virtual void setInitialState(QObject *); + + QQuickVisualDataModelCacheItem *incubating; + QDeclarativeContext *incubatingContext; + +private: + QQuickVisualDataModelPrivate *vdm; +}; + + class QQuickVisualDataGroupEmitter { public: virtual void emitModelUpdated(const QDeclarativeChangeSet &changeSet, bool reset) = 0; virtual void createdPackage(int, QDeclarativePackage *) {} + virtual void initPackage(int, QDeclarativePackage *) {} virtual void destroyingPackage(QDeclarativePackage *) {} QIntrusiveListNode emitterNode; @@ -100,6 +124,7 @@ public: void emitModelUpdated(bool reset); void createdPackage(int index, QDeclarativePackage *package); + void initPackage(int index, QDeclarativePackage *package); void destroyingPackage(QDeclarativePackage *package); bool parseGroupArgs(QDeclarativeV8Function *args, int *index, int *count, int *groups) const; @@ -118,11 +143,78 @@ class QQuickVisualDataModelCacheItem; class QQuickVisualDataModelCacheMetaType; class QQuickVisualDataModelParts; +class QQuickVisualDataModelCacheMetaType : public QDeclarativeRefCount +{ +public: + QQuickVisualDataModelCacheMetaType(QV8Engine *engine, QQuickVisualDataModel *model, const QStringList &groupNames); + ~QQuickVisualDataModelCacheMetaType(); + + int parseGroups(const QStringList &groupNames) const; + int parseGroups(QV8Engine *engine, const v8::Local &groupNames) const; + + static v8::Handle get_model(v8::Local, const v8::AccessorInfo &info); + static v8::Handle get_groups(v8::Local, const v8::AccessorInfo &info); + static void set_groups( + v8::Local, v8::Local value, const v8::AccessorInfo &info); + static v8::Handle get_member(v8::Local, const v8::AccessorInfo &info); + static void set_member( + v8::Local, v8::Local value, const v8::AccessorInfo &info); + static v8::Handle get_index(v8::Local, const v8::AccessorInfo &info); + + QDeclarativeGuard model; + const int groupCount; + const int memberPropertyOffset; + const int indexPropertyOffset; + QV8Engine * const v8Engine; + QMetaObject *metaObject; + const QStringList groupNames; + v8::Persistent constructor; +}; + +class QQuickVisualDataModelCacheItem : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(VisualDataItemType) +public: + QQuickVisualDataModelCacheItem(QQuickVisualDataModelCacheMetaType *metaType) + : QV8ObjectResource(metaType->v8Engine) + , metaType(metaType) + , object(0) + , attached(0) + , objectRef(0) + , scriptRef(0) + , groups(0) + , incubationTask(0) + { + metaType->addref(); + } + + ~QQuickVisualDataModelCacheItem(); + + void referenceObject() { ++objectRef; } + bool releaseObject() { return --objectRef == 0 && !(groups & Compositor::PersistedFlag); } + bool isObjectReferenced() const { return objectRef == 0 && !(groups & Compositor::PersistedFlag); } + + bool isReferenced() const { return objectRef || scriptRef || (groups & Compositor::PersistedFlag) || incubationTask; } + + void Dispose(); + + QQuickVisualDataModelCacheMetaType * const metaType; + QDeclarativeGuard object; + QQuickVisualDataModelAttached *attached; + int objectRef; + int scriptRef; + int groups; + int index[Compositor::MaximumGroupCount]; + QVDMIncubationTask *incubationTask; +}; + + class QQuickVisualDataModelPrivate : public QObjectPrivate, public QQuickVisualDataGroupEmitter { Q_DECLARE_PUBLIC(QQuickVisualDataModel) public: QQuickVisualDataModelPrivate(QDeclarativeContext *); + ~QQuickVisualDataModelPrivate(); static QQuickVisualDataModelPrivate *get(QQuickVisualDataModel *m) { return static_cast(QObjectPrivate::get(m)); @@ -131,14 +223,17 @@ public: void init(); void connectModel(QQuickVisualAdaptorModel *model); - QObject *object(Compositor::Group group, int index, bool complete, bool reference); + QObject *object(Compositor::Group group, int index, bool asynchronous, bool reference); void destroy(QObject *object); QQuickVisualDataModel::ReleaseFlags release(QObject *object); QString stringValue(Compositor::Group group, int index, const QString &name); int cacheIndexOf(QObject *object) const; - void emitCreatedPackage(Compositor::iterator at, QDeclarativePackage *package); - void emitCreatedItem(Compositor::iterator at, QQuickItem *item) { - emit q_func()->createdItem(at.index[m_compositorGroup], item); } + void emitCreatedPackage(QQuickVisualDataModelCacheItem *cacheItem, QDeclarativePackage *package); + void emitInitPackage(QQuickVisualDataModelCacheItem *cacheItem, QDeclarativePackage *package); + void emitCreatedItem(QQuickVisualDataModelCacheItem *cacheItem, QQuickItem *item) { + emit q_func()->createdItem(cacheItem->index[m_compositorGroup], item); } + void emitInitItem(QQuickVisualDataModelCacheItem *cacheItem, QQuickItem *item) { + emit q_func()->initItem(cacheItem->index[m_compositorGroup], item); } void emitDestroyingPackage(QDeclarativePackage *package); void emitDestroyingItem(QQuickItem *item) { emit q_func()->destroyingItem(item); } @@ -170,6 +265,10 @@ public: static int group_count(QDeclarativeListProperty *property); static QQuickVisualDataGroup *group_at(QDeclarativeListProperty *property, int index); + void releaseIncubator(QVDMIncubationTask *incubationTask); + void incubatorStatusChanged(QVDMIncubationTask *incubationTask, QDeclarativeIncubator::Status status); + void setInitialState(QVDMIncubationTask *incubationTask, QObject *o); + QQuickVisualAdaptorModel *m_adaptorModel; QDeclarativeComponent *m_delegate; QQuickVisualDataModelCacheMetaType *m_cacheMetaType; @@ -183,9 +282,9 @@ public: QDeclarativeListCompositor::Group m_compositorGroup; bool m_complete : 1; bool m_delegateValidated : 1; - bool m_completePending : 1; bool m_reset : 1; bool m_transaction : 1; + bool m_incubatorCleanupScheduled : 1; QString m_filterGroup; QList watchedRoles; @@ -199,6 +298,8 @@ public: QQuickVisualDataGroup *m_groups[Compositor::MaximumGroupCount]; }; int m_groupCount; + + QList m_finishedIncubating; }; //--------------------------------------------------------------------------- @@ -219,10 +320,8 @@ public: int count() const; bool isValid() const; - QQuickItem *item(int index, bool complete=true); + QQuickItem *item(int index, bool asynchronous=false); ReleaseFlags release(QQuickItem *item); - bool completePending() const; - void completeItem(); QString stringValue(int index, const QString &role); void setWatchedRoles(QList roles); @@ -231,6 +330,7 @@ public: void emitModelUpdated(const QDeclarativeChangeSet &changeSet, bool reset); void createdPackage(int index, QDeclarativePackage *package); + void initPackage(int index, QDeclarativePackage *package); void destroyingPackage(QDeclarativePackage *package); Q_SIGNALS: @@ -286,78 +386,6 @@ QQuickVisualDataModelParts::QQuickVisualDataModelParts(QQuickVisualDataModel *pa new QQuickVisualDataModelPartsMetaObject(this); } -//--------------------------------------------------------------------------- - -class QQuickVisualDataModelCacheMetaType : public QDeclarativeRefCount -{ -public: - QQuickVisualDataModelCacheMetaType(QV8Engine *engine, QQuickVisualDataModel *model, const QStringList &groupNames); - ~QQuickVisualDataModelCacheMetaType(); - - int parseGroups(const QStringList &groupNames) const; - int parseGroups(QV8Engine *engine, const v8::Local &groupNames) const; - - static v8::Handle get_model(v8::Local, const v8::AccessorInfo &info); - static v8::Handle get_groups(v8::Local, const v8::AccessorInfo &info); - static void set_groups( - v8::Local, v8::Local value, const v8::AccessorInfo &info); - static v8::Handle get_member(v8::Local, const v8::AccessorInfo &info); - static void set_member( - v8::Local, v8::Local value, const v8::AccessorInfo &info); - static v8::Handle get_index(v8::Local, const v8::AccessorInfo &info); - - QDeclarativeGuard model; - const int groupCount; - const int memberPropertyOffset; - const int indexPropertyOffset; - QV8Engine * const v8Engine; - QMetaObject *metaObject; - const QStringList groupNames; - v8::Persistent constructor; -}; - -class QQuickVisualDataModelCacheItem : public QV8ObjectResource -{ - V8_RESOURCE_TYPE(VisualDataItemType) -public: - QQuickVisualDataModelCacheItem(QQuickVisualDataModelCacheMetaType *metaType) - : QV8ObjectResource(metaType->v8Engine) - , metaType(metaType) - , object(0) - , attached(0) - , objectRef(0) - , scriptRef(0) - , groups(0) - { - metaType->addref(); - } - - ~QQuickVisualDataModelCacheItem() - { - Q_ASSERT(scriptRef == 0); - Q_ASSERT(objectRef == 0); - Q_ASSERT(!object); - - metaType->release(); - } - - void referenceObject() { ++objectRef; } - bool releaseObject() { return --objectRef == 0 && !(groups & Compositor::PersistedFlag); } - bool isObjectReferenced() const { return objectRef == 0 && !(groups & Compositor::PersistedFlag); } - - bool isReferenced() const { return objectRef || scriptRef || (groups & Compositor::PersistedFlag); } - - void Dispose(); - - QQuickVisualDataModelCacheMetaType * const metaType; - QDeclarativeGuard object; - QQuickVisualDataModelAttached *attached; - int objectRef; - int scriptRef; - int groups; - int index[Compositor::MaximumGroupCount]; -}; - class QQuickVisualDataModelAttachedMetaObject : public QAbstractDynamicMetaObject { public: @@ -405,9 +433,9 @@ QQuickVisualDataModelPrivate::QQuickVisualDataModelPrivate(QDeclarativeContext * , m_compositorGroup(Compositor::Cache) , m_complete(false) , m_delegateValidated(false) - , m_completePending(false) , m_reset(false) , m_transaction(false) + , m_incubatorCleanupScheduled(false) , m_filterGroup(QStringLiteral("items")) , m_cacheItems(0) , m_items(0) @@ -415,6 +443,11 @@ QQuickVisualDataModelPrivate::QQuickVisualDataModelPrivate(QDeclarativeContext * { } +QQuickVisualDataModelPrivate::~QQuickVisualDataModelPrivate() +{ + qDeleteAll(m_finishedIncubating); +} + void QQuickVisualDataModelPrivate::connectModel(QQuickVisualAdaptorModel *model) { Q_Q(QQuickVisualDataModel); @@ -714,6 +747,8 @@ QQuickVisualDataModel::ReleaseFlags QQuickVisualDataModelPrivate::release(QObjec QQuickVisualDataModelCacheItem *cacheItem = m_cache.at(cacheIndex); if (cacheItem->releaseObject()) { destroy(object); + if (QQuickItem *item = qobject_cast(object)) + emitDestroyingItem(item); cacheItem->object = 0; stat |= QQuickVisualModel::Destroyed; if (!cacheItem->isReferenced()) { @@ -944,10 +979,16 @@ QObject *QQuickVisualDataModel::parts() return d->m_parts; } -void QQuickVisualDataModelPrivate::emitCreatedPackage(Compositor::iterator at, QDeclarativePackage *package) +void QQuickVisualDataModelPrivate::emitCreatedPackage(QQuickVisualDataModelCacheItem *cacheItem, QDeclarativePackage *package) { for (int i = 1; i < m_groupCount; ++i) - QQuickVisualDataGroupPrivate::get(m_groups[i])->createdPackage(at.index[i], package); + QQuickVisualDataGroupPrivate::get(m_groups[i])->createdPackage(cacheItem->index[i], package); +} + +void QQuickVisualDataModelPrivate::emitInitPackage(QQuickVisualDataModelCacheItem *cacheItem, QDeclarativePackage *package) +{ + for (int i = 1; i < m_groupCount; ++i) + QQuickVisualDataGroupPrivate::get(m_groups[i])->initPackage(cacheItem->index[i], package); } void QQuickVisualDataModelPrivate::emitDestroyingPackage(QDeclarativePackage *package) @@ -956,13 +997,87 @@ void QQuickVisualDataModelPrivate::emitDestroyingPackage(QDeclarativePackage *pa QQuickVisualDataGroupPrivate::get(m_groups[i])->destroyingPackage(package); } -QObject *QQuickVisualDataModelPrivate::object(Compositor::Group group, int index, bool complete, bool reference) +void QVDMIncubationTask::statusChanged(Status status) +{ + vdm->incubatorStatusChanged(this, status); +} + +void QQuickVisualDataModelPrivate::releaseIncubator(QVDMIncubationTask *incubationTask) +{ + Q_Q(QQuickVisualDataModel); + if (!incubationTask->isError()) + incubationTask->clear(); + m_finishedIncubating.append(incubationTask); + if (!m_incubatorCleanupScheduled) { + m_incubatorCleanupScheduled = true; + QCoreApplication::postEvent(q, new QEvent(QEvent::User)); + } +} + +void QQuickVisualDataModelPrivate::incubatorStatusChanged(QVDMIncubationTask *incubationTask, QDeclarativeIncubator::Status status) +{ + Q_Q(QQuickVisualDataModel); + if (status != QDeclarativeIncubator::Ready && status != QDeclarativeIncubator::Error) + return; + + QQuickVisualDataModelCacheItem *cacheItem = incubationTask->incubating; + cacheItem->incubationTask = 0; + + if (status == QDeclarativeIncubator::Ready) { + incubationTask->incubating = 0; + releaseIncubator(incubationTask); + if (QDeclarativePackage *package = qobject_cast(cacheItem->object)) + emitCreatedPackage(cacheItem, package); + else if (QQuickItem *item = qobject_cast(cacheItem->object)) + emitCreatedItem(cacheItem, item); + } else if (status == QDeclarativeIncubator::Error) { + delete incubationTask->incubatingContext; + incubationTask->incubatingContext = 0; + if (!cacheItem->isReferenced()) { + int cidx = m_cache.indexOf(cacheItem); + m_compositor.clearFlags(Compositor::Cache, cidx, 1, Compositor::CacheFlag); + m_cache.removeAt(cidx); + delete cacheItem; + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + } + releaseIncubator(incubationTask); + qmlInfo(q, m_delegate->errors()) << "Error creating delegate"; + } +} + +void QVDMIncubationTask::setInitialState(QObject *o) +{ + vdm->setInitialState(this, o); +} + +void QQuickVisualDataModelPrivate::setInitialState(QVDMIncubationTask *incubationTask, QObject *o) +{ + QQuickVisualDataModelCacheItem *cacheItem = incubationTask->incubating; + cacheItem->object = o; + QDeclarative_setParent_noEvent(incubationTask->incubatingContext, cacheItem->object); + incubationTask->incubatingContext = 0; + + cacheItem->attached = QQuickVisualDataModelAttached::properties(cacheItem->object); + cacheItem->attached->m_cacheItem = cacheItem; + new QQuickVisualDataModelAttachedMetaObject(cacheItem->attached, m_cacheMetaType); + cacheItem->attached->emitChanges(); + + if (QDeclarativePackage *package = qobject_cast(cacheItem->object)) + emitInitPackage(cacheItem, package); + else if (QQuickItem *item = qobject_cast(cacheItem->object)) + emitInitItem(cacheItem, item); +} + +QObject *QQuickVisualDataModelPrivate::object(Compositor::Group group, int index, bool asynchronous, bool reference) { Q_Q(QQuickVisualDataModel); + if (!m_delegate || index < 0 || index >= m_compositor.count(group)) { + qWarning() << "VisualDataModel::item: index out range" << index << m_compositor.count(group); + return 0; + } Compositor::iterator it = m_compositor.find(group, index); QQuickVisualDataModelCacheItem *cacheItem = it->inCache() ? m_cache.at(it.cacheIndex) : 0; - if (!cacheItem) { cacheItem = new QQuickVisualDataModelCacheItem(m_cacheMetaType); for (int i = 0; i < m_groupCount; ++i) @@ -970,8 +1085,24 @@ QObject *QQuickVisualDataModelPrivate::object(Compositor::Group group, int index cacheItem->groups = it->flags & Compositor::GroupMask; } - if (!cacheItem->object) { - QObject *data = m_adaptorModel->data(it.modelIndex()); + int modelIndex = it.modelIndex(); + + if (!it->inCache()) { + m_cache.insert(it.cacheIndex, cacheItem); + m_compositor.setFlags(it, 1, Compositor::CacheFlag); + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + } + + if (cacheItem->incubationTask) { + if (!asynchronous) { + // previously requested async - now needed immediately + cacheItem->incubationTask->forceCompletion(); + } + } else if (!cacheItem->object) { + QVDMIncubationTask *incubator = new QVDMIncubationTask(this, asynchronous ? QDeclarativeIncubator::Asynchronous : QDeclarativeIncubator::AsynchronousIfNested); + cacheItem->incubationTask = incubator; + + QObject *data = m_adaptorModel->data(modelIndex); QDeclarativeContext *creationContext = m_delegate->creationContext(); QDeclarativeContext *rootContext = new QDeclarativeContext( @@ -988,49 +1119,27 @@ QObject *QQuickVisualDataModelPrivate::object(Compositor::Group group, int index ctxt->setContextProperty(QLatin1String("model"), data); ctxt->setContextObject(data); - m_completePending = false; - cacheItem->object = m_delegate->beginCreate(ctxt); - - if (cacheItem->object) { - QDeclarative_setParent_noEvent(rootContext, cacheItem->object); - if (!it->inCache()) { - m_cache.insert(it.cacheIndex, cacheItem); - m_compositor.setFlags(it, 1, Compositor::CacheFlag); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - } - - cacheItem->attached = QQuickVisualDataModelAttached::properties(cacheItem->object); - cacheItem->attached->setCacheItem(cacheItem); - new QQuickVisualDataModelAttachedMetaObject(cacheItem->attached, m_cacheMetaType); - cacheItem->attached->emitChanges(); - - if (QDeclarativePackage *package = qobject_cast(cacheItem->object)) { - emitCreatedPackage(it, package); - } else if (!reference) { - if (QQuickItem *item = qobject_cast(cacheItem->object)) - emitCreatedItem(it, item); - } - - m_completePending = !complete; - if (complete) - m_delegate->completeCreate(); - } else { - delete rootContext; - if (!it->inCache()) - delete cacheItem; - qmlInfo(q, m_delegate->errors()) << "Error creating delegate"; - return 0; - } + incubator->incubating = cacheItem; + incubator->incubatingContext = rootContext; + m_delegate->create(*incubator, ctxt, m_context); } if (index == m_compositor.count(group) - 1 && m_adaptorModel->canFetchMore()) QCoreApplication::postEvent(q, new QEvent(QEvent::UpdateRequest)); - if (reference) + if (cacheItem->object && reference) cacheItem->referenceObject(); return cacheItem->object; } -QQuickItem *QQuickVisualDataModel::item(int index, bool complete) +/* + If asynchronous is true or the component is being loaded asynchronously due + to an ancestor being loaded asynchronously, item() may return 0. In this + case itemCreated() will be emitted when the item is available. The item + at this stage does not have any references, so item() must be called again + to ensure a reference is held. Any call to item() which returns a valid item + must be matched by a call to release() in order to destroy the item. +*/ +QQuickItem *QQuickVisualDataModel::item(int index, bool asynchronous) { Q_D(QQuickVisualDataModel); if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) { @@ -1038,11 +1147,13 @@ QQuickItem *QQuickVisualDataModel::item(int index, bool complete) return 0; } - QObject *object = d->object(d->m_compositorGroup, index, complete, true); + QObject *object = d->object(d->m_compositorGroup, index, asynchronous, true); + if (!object) + return 0; + if (QQuickItem *item = qobject_cast(object)) return item; - if (d->m_completePending) - completeItem(); + d->release(object); if (!d->m_delegateValidated) { if (object) @@ -1052,19 +1163,6 @@ QQuickItem *QQuickVisualDataModel::item(int index, bool complete) return 0; } -bool QQuickVisualDataModel::completePending() const -{ - Q_D(const QQuickVisualDataModel); - return d->m_completePending; -} - -void QQuickVisualDataModel::completeItem() -{ - Q_D(QQuickVisualDataModel); - d->m_delegate->completeCreate(); - d->m_completePending = false; -} - QString QQuickVisualDataModelPrivate::stringValue(Compositor::Group group, int index, const QString &name) { Compositor::iterator it = m_compositor.find(group, index); @@ -1138,8 +1236,13 @@ void QQuickVisualDataModelPrivate::setGroups(Compositor::Group group, int index, bool QQuickVisualDataModel::event(QEvent *e) { Q_D(QQuickVisualDataModel); - if (e->type() == QEvent::UpdateRequest) + if (e->type() == QEvent::UpdateRequest) { d->m_adaptorModel->fetchMore(); + } else if (e->type() == QEvent::User) { + d->m_incubatorCleanupScheduled = false; + qDeleteAll(d->m_finishedIncubating); + d->m_finishedIncubating.clear(); + } return QQuickVisualModel::event(e); } @@ -1436,7 +1539,7 @@ void QQuickVisualDataModelPrivate::emitChanges() QQuickVisualDataGroupPrivate::get(m_groups[i])->emitModelUpdated(reset); foreach (QQuickVisualDataModelCacheItem *cacheItem, m_cache) { - if (cacheItem->object) + if (cacheItem->object && cacheItem->attached) cacheItem->attached->emitChanges(); } } @@ -1664,6 +1767,17 @@ v8::Handle QQuickVisualDataModelCacheMetaType::get_index( //--------------------------------------------------------------------------- +QQuickVisualDataModelCacheItem::~QQuickVisualDataModelCacheItem() +{ + Q_ASSERT(scriptRef == 0); + Q_ASSERT(objectRef == 0); + Q_ASSERT(!object); + if (incubationTask && metaType->model) + QQuickVisualDataModelPrivate::get(metaType->model)->releaseIncubator(incubationTask); + + metaType->release(); +} + void QQuickVisualDataModelCacheItem::Dispose() { --scriptRef; @@ -1892,6 +2006,12 @@ void QQuickVisualDataGroupPrivate::createdPackage(int index, QDeclarativePackage it->createdPackage(index, package); } +void QQuickVisualDataGroupPrivate::initPackage(int index, QDeclarativePackage *package) +{ + for (QQuickVisualDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) + it->initPackage(index, package); +} + void QQuickVisualDataGroupPrivate::destroyingPackage(QDeclarativePackage *package) { for (QQuickVisualDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) @@ -2068,7 +2188,7 @@ QObject *QQuickVisualDataGroup::create(int index) return 0; } - QObject *object = model->object(d->group, index, true, false); + QObject *object = model->object(d->group, index, false, false); if (object) model->addGroups(d->group, index, 1, Compositor::PersistedFlag); return object; @@ -2438,7 +2558,7 @@ bool QQuickVisualPartsModel::isValid() const return m_model->isValid(); } -QQuickItem *QQuickVisualPartsModel::item(int index, bool complete) +QQuickItem *QQuickVisualPartsModel::item(int index, bool asynchronous) { QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(m_model); @@ -2447,7 +2567,7 @@ QQuickItem *QQuickVisualPartsModel::item(int index, bool complete) return 0; } - QObject *object = model->object(m_compositorGroup, index, complete, true); + QObject *object = model->object(m_compositorGroup, index, asynchronous, true); if (QDeclarativePackage *package = qobject_cast(object)) { QObject *part = package->part(m_part); @@ -2459,8 +2579,6 @@ QQuickItem *QQuickVisualPartsModel::item(int index, bool complete) } } - if (m_model->completePending()) - m_model->completeItem(); model->release(object); if (!model->m_delegateValidated) { if (object) @@ -2478,6 +2596,7 @@ QQuickVisualModel::ReleaseFlags QQuickVisualPartsModel::release(QQuickItem *item QHash::iterator it = m_packaged.find(item); if (it != m_packaged.end()) { QDeclarativePackage *package = *it; + QDeclarative_setParent_noEvent(item, package); QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(m_model); flags = model->release(package); m_packaged.erase(it); @@ -2491,16 +2610,6 @@ QQuickVisualModel::ReleaseFlags QQuickVisualPartsModel::release(QQuickItem *item return flags; } -bool QQuickVisualPartsModel::completePending() const -{ - return m_model->completePending(); -} - -void QQuickVisualPartsModel::completeItem() -{ - m_model->completeItem(); -} - QString QQuickVisualPartsModel::stringValue(int index, const QString &role) { return QQuickVisualDataModelPrivate::get(m_model)->stringValue(m_compositorGroup, index, role); @@ -2532,6 +2641,12 @@ void QQuickVisualPartsModel::createdPackage(int index, QDeclarativePackage *pack emit createdItem(index, item); } +void QQuickVisualPartsModel::initPackage(int index, QDeclarativePackage *package) +{ + if (QQuickItem *item = qobject_cast(package->part(m_part))) + emit initItem(index, item); +} + void QQuickVisualPartsModel::destroyingPackage(QDeclarativePackage *package) { if (QQuickItem *item = qobject_cast(package->part(m_part))) { diff --git a/src/declarative/items/qquickvisualdatamodel_p.h b/src/declarative/items/qquickvisualdatamodel_p.h index 60b04ab..a5a384f 100644 --- a/src/declarative/items/qquickvisualdatamodel_p.h +++ b/src/declarative/items/qquickvisualdatamodel_p.h @@ -107,10 +107,8 @@ public: int count() const; bool isValid() const { return delegate() != 0; } - QQuickItem *item(int index, bool complete=true); + QQuickItem *item(int index, bool asynchronous=false); ReleaseFlags release(QQuickItem *item); - bool completePending() const; - void completeItem(); virtual QString stringValue(int index, const QString &role); virtual void setWatchedRoles(QList roles); diff --git a/src/declarative/items/qquickvisualitemmodel.cpp b/src/declarative/items/qquickvisualitemmodel.cpp index 31d06f6..78c4f88 100644 --- a/src/declarative/items/qquickvisualitemmodel.cpp +++ b/src/declarative/items/qquickvisualitemmodel.cpp @@ -193,6 +193,8 @@ QQuickItem *QQuickVisualItemModel::item(int index, bool) Q_D(QQuickVisualItemModel); QQuickVisualItemModelPrivate::Item &item = d->children[index]; item.addRef(); + emit initItem(index, item.item); + emit createdItem(index, item.item); return item.item; } @@ -210,16 +212,6 @@ QQuickVisualModel::ReleaseFlags QQuickVisualItemModel::release(QQuickItem *item) return 0; } -bool QQuickVisualItemModel::completePending() const -{ - return false; -} - -void QQuickVisualItemModel::completeItem() -{ - // Nothing to do -} - QString QQuickVisualItemModel::stringValue(int index, const QString &name) { Q_D(QQuickVisualItemModel); diff --git a/src/declarative/items/qquickvisualitemmodel_p.h b/src/declarative/items/qquickvisualitemmodel_p.h index 9fc3d57..3d9610a 100644 --- a/src/declarative/items/qquickvisualitemmodel_p.h +++ b/src/declarative/items/qquickvisualitemmodel_p.h @@ -69,10 +69,8 @@ public: virtual int count() const = 0; virtual bool isValid() const = 0; - virtual QQuickItem *item(int index, bool complete=true) = 0; + virtual QQuickItem *item(int index, bool asynchronous=false) = 0; virtual ReleaseFlags release(QQuickItem *item) = 0; - virtual bool completePending() const = 0; - virtual void completeItem() = 0; virtual QString stringValue(int, const QString &) = 0; virtual void setWatchedRoles(QList roles) = 0; @@ -82,6 +80,7 @@ Q_SIGNALS: void countChanged(); void modelUpdated(const QDeclarativeChangeSet &changeSet, bool reset); void createdItem(int index, QQuickItem *item); + void initItem(int index, QQuickItem *item); void destroyingItem(QQuickItem *item); protected: @@ -108,10 +107,8 @@ public: virtual int count() const; virtual bool isValid() const; - virtual QQuickItem *item(int index, bool complete=true); + virtual QQuickItem *item(int index, bool asynchronous=false); virtual ReleaseFlags release(QQuickItem *item); - virtual bool completePending() const; - virtual void completeItem(); virtual QString stringValue(int index, const QString &role); virtual void setWatchedRoles(QList) {} diff --git a/tests/auto/declarative/qquickgridview/data/asyncloader.qml b/tests/auto/declarative/qquickgridview/data/asyncloader.qml new file mode 100644 index 0000000..ab66f20 --- /dev/null +++ b/tests/auto/declarative/qquickgridview/data/asyncloader.qml @@ -0,0 +1,36 @@ + +import QtQuick 2.0 + +Rectangle { + id: root + width: 300; height: 400 + color: "#2200FF00" + + Loader { + asynchronous: true + sourceComponent: viewComp + anchors.fill: parent + } + + Component { + id: viewComp + GridView { + objectName: "view" + width: 300; height: 400 + model: 40 + delegate: aDelegate + + highlight: Rectangle { color: "lightsteelblue" } + } + } + // The delegate for each list + Component { + id: aDelegate + Item { + objectName: "wrapper" + width: 100 + height: 100 + Text { text: 'Index: ' + index } + } + } +} diff --git a/tests/auto/declarative/qquickgridview/data/gridview1.qml b/tests/auto/declarative/qquickgridview/data/gridview1.qml index e6a3923..4bf6f0b 100644 --- a/tests/auto/declarative/qquickgridview/data/gridview1.qml +++ b/tests/auto/declarative/qquickgridview/data/gridview1.qml @@ -5,6 +5,7 @@ Rectangle { property int count: grid.count property bool showHeader: false property bool showFooter: false + property real cacheBuffer: 0 property int added: -1 property variant removed @@ -63,5 +64,6 @@ Rectangle { delegate: myDelegate header: root.showHeader ? headerFooter : null footer: root.showFooter ? headerFooter : null + cacheBuffer: root.cacheBuffer } } diff --git a/tests/auto/declarative/qquickgridview/tst_qquickgridview.cpp b/tests/auto/declarative/qquickgridview/tst_qquickgridview.cpp index 9cd39fc..3370b72 100644 --- a/tests/auto/declarative/qquickgridview/tst_qquickgridview.cpp +++ b/tests/auto/declarative/qquickgridview/tst_qquickgridview.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -117,6 +118,8 @@ private slots: void snapToRow_data(); void snapToRow(); void unaligned(); + void cacheBuffer(); + void asynchronous(); private: QQuickView *createView(); @@ -3524,6 +3527,150 @@ void tst_QQuickGridView::flick(QQuickView *canvas, const QPoint &from, const QPo QTest::mouseRelease(canvas, Qt::LeftButton, 0, to); } +void tst_QQuickGridView::cacheBuffer() +{ + QQuickView *canvas = createView(); + + TestModel model; + for (int i = 0; i < 90; i++) + model.addItem("Item" + QString::number(i), ""); + + QDeclarativeContext *ctxt = canvas->rootContext(); + ctxt->setContextProperty("testModel", &model); + ctxt->setContextProperty("testRightToLeft", QVariant(false)); + ctxt->setContextProperty("testTopToBottom", QVariant(false)); + + canvas->setSource(QUrl::fromLocalFile(TESTDATA("gridview1.qml"))); + canvas->show(); + qApp->processEvents(); + + QQuickGridView *gridview = findItem(canvas->rootObject(), "grid"); + QVERIFY(gridview != 0); + + QQuickItem *contentItem = gridview->contentItem(); + QVERIFY(contentItem != 0); + QVERIFY(gridview->delegate() != 0); + QVERIFY(gridview->model() != 0); + + // Confirm items positioned correctly + int itemCount = findItems(contentItem, "wrapper").count(); + for (int i = 0; i < model.count() && i < itemCount; ++i) { + QQuickItem *item = findItem(contentItem, "wrapper", i); + QTRY_COMPARE(item->x(), (i%3)*80.0); + QTRY_COMPARE(item->y(), (i/3)*60.0); + } + + QDeclarativeIncubationController controller; + canvas->engine()->setIncubationController(&controller); + + canvas->rootObject()->setProperty("cacheBuffer", 200); + QTRY_VERIFY(gridview->cacheBuffer() == 200); + + // items will be created one at a time + for (int i = itemCount; i < qMin(itemCount+9,model.count()); ++i) { + QVERIFY(findItem(gridview, "wrapper", i) == 0); + QQuickItem *item = 0; + while (!item) { + bool b = false; + controller.incubateWhile(&b); + item = findItem(gridview, "wrapper", i); + } + } + + { + bool b = true; + controller.incubateWhile(&b); + } + + int newItemCount = 0; + newItemCount = findItems(contentItem, "wrapper").count(); + + // Confirm items positioned correctly + for (int i = 0; i < model.count() && i < newItemCount; ++i) { + QQuickItem *item = findItem(contentItem, "wrapper", i); + QVERIFY(item); + QTRY_COMPARE(item->x(), (i%3)*80.0); + QTRY_COMPARE(item->y(), (i/3)*60.0); + } + + // move view and confirm items in view are visible immediately and outside are created async + gridview->setContentY(300); + + for (int i = 15; i < 34; ++i) { // 34 due to staggered item creation + QQuickItem *item = findItem(contentItem, "wrapper", i); + QVERIFY(item); + QTRY_COMPARE(item->x(), (i%3)*80.0); + QTRY_COMPARE(item->y(), (i/3)*60.0); + } + + QVERIFY(findItem(gridview, "wrapper", 34) == 0); + + // ensure buffered items are created + for (int i = 34; i < qMin(44,model.count()); ++i) { + QQuickItem *item = 0; + while (!item) { + qGuiApp->processEvents(); // allow refill to happen + bool b = false; + controller.incubateWhile(&b); + item = findItem(gridview, "wrapper", i); + } + } + + { + bool b = true; + controller.incubateWhile(&b); + } + + delete canvas; +} + +void tst_QQuickGridView::asynchronous() +{ + QQuickView *canvas = createView(); + canvas->show(); + QDeclarativeIncubationController controller; + canvas->engine()->setIncubationController(&controller); + + canvas->setSource(TESTDATA("asyncloader.qml")); + + QQuickItem *rootObject = qobject_cast(canvas->rootObject()); + QVERIFY(rootObject); + + QQuickGridView *gridview = 0; + while (!gridview) { + bool b = false; + controller.incubateWhile(&b); + gridview = rootObject->findChild("view"); + } + + // items will be created one at a time + for (int i = 0; i < 12; ++i) { + QVERIFY(findItem(gridview, "wrapper", i) == 0); + QQuickItem *item = 0; + while (!item) { + bool b = false; + controller.incubateWhile(&b); + item = findItem(gridview, "wrapper", i); + } + } + + { + bool b = true; + controller.incubateWhile(&b); + } + + // verify positioning + QQuickItem *contentItem = gridview->contentItem(); + for (int i = 0; i < 12; ++i) { + QQuickItem *item = findItem(contentItem, "wrapper", i); + if (!item) qWarning() << "Item" << i << "not found"; + QVERIFY(item->x() == (i%3)*100); + QVERIFY(item->y() == (i/3)*100); + } + + delete canvas; +} + /* Find an item with the specified objectName. If index is supplied then the item must also evaluate the {index} expression equal to index diff --git a/tests/auto/declarative/qquicklistview/data/asyncloader.qml b/tests/auto/declarative/qquicklistview/data/asyncloader.qml new file mode 100644 index 0000000..f038f09 --- /dev/null +++ b/tests/auto/declarative/qquicklistview/data/asyncloader.qml @@ -0,0 +1,36 @@ + +import QtQuick 2.0 + +Rectangle { + id: root + width: 300; height: 400 + color: "#2200FF00" + + Loader { + asynchronous: true + sourceComponent: viewComp + anchors.fill: parent + } + + Component { + id: viewComp + ListView { + objectName: "view" + width: 300; height: 400 + model: 20 + delegate: aDelegate + + highlight: Rectangle { color: "lightsteelblue" } + } + } + // The delegate for each list + Component { + id: aDelegate + Item { + objectName: "wrapper" + width: 300 + height: 50 + Text { text: 'Index: ' + index } + } + } +} diff --git a/tests/auto/declarative/qquicklistview/data/listviewtest.qml b/tests/auto/declarative/qquicklistview/data/listviewtest.qml index 0202de1..47b341c 100644 --- a/tests/auto/declarative/qquicklistview/data/listviewtest.qml +++ b/tests/auto/declarative/qquicklistview/data/listviewtest.qml @@ -64,7 +64,7 @@ Rectangle { x: 200 text: wrapper.y } - color: ListView.isCurrentItem ? "lightsteelblue" : "white" + color: ListView.isCurrentItem ? "lightsteelblue" : "#EEEEEE" } }, Component { diff --git a/tests/auto/declarative/qquicklistview/tst_qquicklistview.cpp b/tests/auto/declarative/qquicklistview/tst_qquicklistview.cpp index b12bf3e..3b41600 100644 --- a/tests/auto/declarative/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/declarative/qquicklistview/tst_qquicklistview.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -147,6 +148,8 @@ private slots: void QTBUG_11105(); void QTBUG_21742(); + void asynchronous(); + private: template void items(); template void changed(); @@ -1049,8 +1052,8 @@ void tst_QQuickListView::removed(bool /* animated */) // Confirm items positioned correctly itemCount = findItems(contentItem, "wrapper").count(); for (int i = 0; i < model.count() && i < itemCount-1; ++i) { - QQuickItem *item = findItem(contentItem, "wrapper", i+2); - if (!item) qWarning() << "Item" << i+2 << "not found"; + QQuickItem *item = findItem(contentItem, "wrapper", i+1); + if (!item) qWarning() << "Item" << i+1 << "not found"; QTRY_VERIFY(item); QTRY_COMPARE(item->y(),80+i*20.0); } @@ -2067,33 +2070,33 @@ void tst_QQuickListView::sectionsPositioning() QTRY_COMPARE(item->y(), qreal(i*20*6)); } - QTRY_VERIFY(topItem = findVisibleChild(contentItem, "sect_aaa")); // section header - QCOMPARE(topItem->y(), 120.); QVERIFY(topItem = findVisibleChild(contentItem, "sect_1")); - QTRY_COMPARE(topItem->y(), 140.); + QTRY_COMPARE(topItem->y(), 120.); // Change the next section listview->setContentY(0); bottomItem = findVisibleChild(contentItem, "sect_3"); // section footer QVERIFY(bottomItem); - QTRY_COMPARE(bottomItem->y(), 320.); + QTRY_COMPARE(bottomItem->y(), 300.); model.modifyItem(14, "New", "new"); QTRY_VERIFY(bottomItem = findVisibleChild(contentItem, "sect_new")); // section footer - QTRY_COMPARE(bottomItem->y(), 320.); + QTRY_COMPARE(bottomItem->y(), 300.); // Turn sticky footer off - listview->setContentY(50); + listview->setContentY(40); canvas->rootObject()->setProperty("sectionPositioning", QVariant(int(QQuickViewSection::InlineLabels | QQuickViewSection::CurrentLabelAtStart))); item = findVisibleChild(contentItem, "sect_new"); // inline label restored + QVERIFY(item); QCOMPARE(item->y(), 360.); // Turn sticky header off - listview->setContentY(50); + listview->setContentY(30); canvas->rootObject()->setProperty("sectionPositioning", QVariant(int(QQuickViewSection::InlineLabels))); item = findVisibleChild(contentItem, "sect_aaa"); // inline label restored - QCOMPARE(item->y(), 20.); + QVERIFY(item); + QCOMPARE(item->y(), 0.); delete canvas; } @@ -2119,7 +2122,7 @@ void tst_QQuickListView::currentIndex_delayedItemCreation() QQuickItem *contentItem = listview->contentItem(); QTRY_VERIFY(contentItem != 0); - QSignalSpy spy(listview, SIGNAL(currentIndexChanged())); + QSignalSpy spy(listview, SIGNAL(currentItemChanged())); QCOMPARE(listview->currentIndex(), 0); QTRY_COMPARE(spy.count(), 1); @@ -2355,7 +2358,7 @@ void tst_QQuickListView::cacheBuffer() QQuickView *canvas = createView(); TestModel model; - for (int i = 0; i < 30; i++) + for (int i = 0; i < 90; i++) model.addItem("Item" + QString::number(i), ""); QDeclarativeContext *ctxt = canvas->rootContext(); @@ -2365,6 +2368,7 @@ void tst_QQuickListView::cacheBuffer() ctxt->setContextProperty("testObject", testObject); canvas->setSource(QUrl::fromLocalFile(TESTDATA("listviewtest.qml"))); + canvas->show(); qApp->processEvents(); QQuickListView *listview = findItem(canvas->rootObject(), "list"); @@ -2385,11 +2389,30 @@ void tst_QQuickListView::cacheBuffer() QTRY_VERIFY(item->y() == i*20); } - testObject->setCacheBuffer(400); - QTRY_VERIFY(listview->cacheBuffer() == 400); + QDeclarativeIncubationController controller; + canvas->engine()->setIncubationController(&controller); + + testObject->setCacheBuffer(200); + QTRY_VERIFY(listview->cacheBuffer() == 200); - int newItemCount = findItems(contentItem, "wrapper").count(); - QTRY_VERIFY(newItemCount > itemCount); + // items will be created one at a time + for (int i = itemCount; i < qMin(itemCount+10,model.count()); ++i) { + QVERIFY(findItem(listview, "wrapper", i) == 0); + QQuickItem *item = 0; + while (!item) { + bool b = false; + controller.incubateWhile(&b); + item = findItem(listview, "wrapper", i); + } + } + + { + bool b = true; + controller.incubateWhile(&b); + } + + int newItemCount = 0; + newItemCount = findItems(contentItem, "wrapper").count(); // Confirm items positioned correctly for (int i = 0; i < model.count() && i < newItemCount; ++i) { @@ -2399,6 +2422,34 @@ void tst_QQuickListView::cacheBuffer() QTRY_VERIFY(item->y() == i*20); } + // move view and confirm items in view are visible immediately and outside are created async + listview->setContentY(300); + + for (int i = 15; i < 32; ++i) { + QQuickItem *item = findItem(contentItem, "wrapper", i); + if (!item) qWarning() << "Item" << i << "not found"; + QVERIFY(item); + QVERIFY(item->y() == i*20); + } + + QVERIFY(findItem(listview, "wrapper", 32) == 0); + + // ensure buffered items are created + for (int i = 32; i < qMin(41,model.count()); ++i) { + QQuickItem *item = 0; + while (!item) { + qGuiApp->processEvents(); // allow refill to happen + bool b = false; + controller.incubateWhile(&b); + item = findItem(listview, "wrapper", i); + } + } + + { + bool b = true; + controller.incubateWhile(&b); + } + delete canvas; delete testObject; } @@ -3590,9 +3641,10 @@ void tst_QQuickListView::resizeFirstDelegate() listview->setCurrentIndex(19); qApp->processEvents(); - // items 0-3 should have been deleted - for (int i=0; i<4; i++) + // items 0-2 should have been deleted + for (int i=0; i<3; i++) { QTRY_VERIFY(!findItem(contentItem, "wrapper", i)); + } delete testObject; delete canvas; @@ -4202,6 +4254,50 @@ void tst_QQuickListView::flick(QQuickView *canvas, const QPoint &from, const QPo QTest::mouseRelease(canvas, Qt::LeftButton, 0, to); } +void tst_QQuickListView::asynchronous() +{ + QQuickView *canvas = createView(); + canvas->show(); + QDeclarativeIncubationController controller; + canvas->engine()->setIncubationController(&controller); + + canvas->setSource(TESTDATA("asyncloader.qml")); + + QQuickItem *rootObject = qobject_cast(canvas->rootObject()); + QVERIFY(rootObject); + + QQuickListView *listview = 0; + while (!listview) { + bool b = false; + controller.incubateWhile(&b); + listview = rootObject->findChild("view"); + } + + // items will be created one at a time + for (int i = 0; i < 8; ++i) { + QVERIFY(findItem(listview, "wrapper", i) == 0); + QQuickItem *item = 0; + while (!item) { + bool b = false; + controller.incubateWhile(&b); + item = findItem(listview, "wrapper", i); + } + } + + { + bool b = true; + controller.incubateWhile(&b); + } + + // verify positioning + QQuickItem *contentItem = listview->contentItem(); + for (int i = 0; i < 8; ++i) { + QQuickItem *item = findItem(contentItem, "wrapper", i); + QTRY_COMPARE(item->y(), i*50.0); + } + + delete canvas; +} QQuickItem *tst_QQuickListView::findVisibleChild(QQuickItem *parent, const QString &objectName) { diff --git a/tests/auto/declarative/qquickpathview/data/asyncloader.qml b/tests/auto/declarative/qquickpathview/data/asyncloader.qml new file mode 100644 index 0000000..94f560f --- /dev/null +++ b/tests/auto/declarative/qquickpathview/data/asyncloader.qml @@ -0,0 +1,71 @@ +import QtQuick 2.0 + +Rectangle { + id: root + property real delegateWidth: 60 + property real delegateHeight: 20 + property real delegateScale: 1.0 + width: 240 + height: 320 + color: "#ffffff" + + Loader { + asynchronous: true + sourceComponent: viewComponent + anchors.fill: parent + } + + Component { + id: adelegate + Rectangle { + objectName: "wrapper" + property bool onPath: PathView.onPath + height: root.delegateHeight + width: root.delegateWidth + scale: root.delegateScale + color: PathView.isCurrentItem ? "lightsteelblue" : "white" + border.color: "black" + Text { + text: index + } + } + } + Component { + id: viewComponent + PathView { + id: view + objectName: "view" + width: 240 + height: 320 + model: 5 + delegate: adelegate + highlight: Rectangle { + width: 60 + height: 20 + color: "yellow" + } + path: Path { + startY: 120 + startX: 160 + PathQuad { + y: 120 + x: 80 + controlY: 330 + controlX: 100 + } + PathLine { + y: 160 + x: 20 + } + PathCubic { + y: 120 + x: 160 + control1Y: 0 + control1X: 100 + control2Y: 0 + control2X: 200 + } + } + } + } +} diff --git a/tests/auto/declarative/qquickpathview/tst_qquickpathview.cpp b/tests/auto/declarative/qquickpathview/tst_qquickpathview.cpp index ee7f993..b0efc58 100644 --- a/tests/auto/declarative/qquickpathview/tst_qquickpathview.cpp +++ b/tests/auto/declarative/qquickpathview/tst_qquickpathview.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -118,6 +119,7 @@ private slots: void missingPercent(); void creationContext(); void currentOffsetOnInsertion(); + void asynchronous(); private: QQuickView *createView(); @@ -833,7 +835,7 @@ void tst_QQuickPathView::pathMoved() QPointF offset;//Center of item is at point, but pos is from corner offset.setX(firstItem->width()/2); offset.setY(firstItem->height()/2); - QCOMPARE(firstItem->pos() + offset, start); + QTRY_COMPARE(firstItem->pos() + offset, start); pathview->setOffset(1.0); for (int i=0; ishow(); + QDeclarativeIncubationController controller; + canvas->engine()->setIncubationController(&controller); + + canvas->setSource(TESTDATA("asyncloader.qml")); + + QQuickItem *rootObject = qobject_cast(canvas->rootObject()); + QVERIFY(rootObject); + + QQuickPathView *pathview = 0; + while (!pathview) { + bool b = false; + controller.incubateWhile(&b); + pathview = rootObject->findChild("view"); + } + + // items will be created one at a time + for (int i = 0; i < 5; ++i) { + QVERIFY(findItem(pathview, "wrapper", i) == 0); + QQuickItem *item = 0; + while (!item) { + bool b = false; + controller.incubateWhile(&b); + item = findItem(pathview, "wrapper", i); + } + } + + { + bool b = true; + controller.incubateWhile(&b); + } + + // verify positioning + QQuickRectangle *firstItem = findItem(pathview, "wrapper", 0); + QVERIFY(firstItem); + QDeclarativePath *path = qobject_cast(pathview->path()); + QVERIFY(path); + QPointF start = path->pointAt(0.0); + QPointF offset;//Center of item is at point, but pos is from corner + offset.setX(firstItem->width()/2); + offset.setY(firstItem->height()/2); + QTRY_COMPARE(firstItem->pos() + offset, start); + pathview->setOffset(1.0); + + for (int i=0; i<5; i++) { + QQuickItem *curItem = findItem(pathview, "wrapper", i); + QPointF itemPos(path->pointAt(0.2 + i*0.2)); + QCOMPARE(curItem->pos() + offset, QPointF(qRound(itemPos.x()), qRound(itemPos.y()))); + } + + delete canvas; +} + QQuickView *tst_QQuickPathView::createView() { QQuickView *canvas = new QQuickView(0); diff --git a/tests/auto/declarative/qquickrepeater/data/asyncloader.qml b/tests/auto/declarative/qquickrepeater/data/asyncloader.qml new file mode 100644 index 0000000..82094e2 --- /dev/null +++ b/tests/auto/declarative/qquickrepeater/data/asyncloader.qml @@ -0,0 +1,32 @@ +import QtQuick 2.0 + +Item { + width: 360 + height: 480 + + Loader { + asynchronous: true + sourceComponent: viewComponent + } + + Component { + id: viewComponent + Column { + objectName: "container" + Repeater { + id: repeater + objectName: "repeater" + + model: 10 + + delegate: Rectangle { + objectName: "delegate" + index + color: "red" + width: 360 + height: 50 + Text { text: index } + } + } + } + } +} diff --git a/tests/auto/declarative/qquickrepeater/tst_qquickrepeater.cpp b/tests/auto/declarative/qquickrepeater/tst_qquickrepeater.cpp index 355dd0d..fc76480 100644 --- a/tests/auto/declarative/qquickrepeater/tst_qquickrepeater.cpp +++ b/tests/auto/declarative/qquickrepeater/tst_qquickrepeater.cpp @@ -46,10 +46,12 @@ #include #include #include +#include #include #include #include "../shared/util.h" +#include "../../../shared/util.h" inline QUrl TEST_FILE(const QString &filename) { @@ -73,6 +75,7 @@ private slots: void resetModel(); void modelChanged(); void properties(); + void asynchronous(); private: QQuickView *createView(); @@ -636,6 +639,62 @@ void tst_QQuickRepeater::properties() delete rootObject; } +void tst_QQuickRepeater::asynchronous() +{ + QQuickView *canvas = createView(); + canvas->show(); + QDeclarativeIncubationController controller; + canvas->engine()->setIncubationController(&controller); + + canvas->setSource(TEST_FILE("asyncloader.qml")); + + QQuickItem *rootObject = qobject_cast(canvas->rootObject()); + QVERIFY(rootObject); + + QQuickItem *container = findItem(rootObject, "container"); + QVERIFY(!container); + while (!container) { + bool b = false; + controller.incubateWhile(&b); + container = findItem(rootObject, "container"); + } + + QQuickRepeater *repeater = 0; + while (!repeater) { + bool b = false; + controller.incubateWhile(&b); + repeater = findItem(rootObject, "repeater"); + } + + // items will be created one at a time + for (int i = 0; i < 10; ++i) { + QString name("delegate"); + name += QString::number(i); + QVERIFY(findItem(container, name) == 0); + QQuickItem *item = 0; + while (!item) { + bool b = false; + controller.incubateWhile(&b); + item = findItem(container, name); + } + } + + { + bool b = true; + controller.incubateWhile(&b); + } + + // verify positioning + for (int i = 0; i < 10; ++i) { + QString name("delegate"); + name += QString::number(i); + QQuickItem *item = findItem(container, name); + QTRY_COMPARE(item->y(), i * 50.0); + } + + delete canvas; +} + QQuickView *tst_QQuickRepeater::createView() { QQuickView *canvas = new QQuickView(0); -- 1.7.2.5