Non-blocking view delegate instantiation.
authorMartin Jones <martin.jones@nokia.com>
Thu, 3 Nov 2011 05:52:13 +0000 (15:52 +1000)
committerQt by Nokia <qt-info@nokia.com>
Thu, 1 Dec 2011 00:14:53 +0000 (01:14 +0100)
Task-number: QTBUG-21792

Change-Id: I29a4028cd24eb55d4768aacaa3abbd1786061398
Reviewed-by: Andrew den Exter <andrew.den-exter@nokia.com>
26 files changed:
src/declarative/items/qquickgridview.cpp
src/declarative/items/qquickitemview.cpp
src/declarative/items/qquickitemview_p.h
src/declarative/items/qquickitemview_p_p.h
src/declarative/items/qquicklistview.cpp
src/declarative/items/qquickloader.cpp
src/declarative/items/qquickpathview.cpp
src/declarative/items/qquickpathview_p.h
src/declarative/items/qquickpathview_p_p.h
src/declarative/items/qquickrepeater.cpp
src/declarative/items/qquickrepeater_p.h
src/declarative/items/qquickrepeater_p_p.h
src/declarative/items/qquickvisualdatamodel.cpp
src/declarative/items/qquickvisualdatamodel_p.h
src/declarative/items/qquickvisualitemmodel.cpp
src/declarative/items/qquickvisualitemmodel_p.h
tests/auto/declarative/qquickgridview/data/asyncloader.qml [new file with mode: 0644]
tests/auto/declarative/qquickgridview/data/gridview1.qml
tests/auto/declarative/qquickgridview/tst_qquickgridview.cpp
tests/auto/declarative/qquicklistview/data/asyncloader.qml [new file with mode: 0644]
tests/auto/declarative/qquicklistview/data/listviewtest.qml
tests/auto/declarative/qquicklistview/tst_qquicklistview.cpp
tests/auto/declarative/qquickpathview/data/asyncloader.qml [new file with mode: 0644]
tests/auto/declarative/qquickpathview/tst_qquickpathview.cpp
tests/auto/declarative/qquickrepeater/data/asyncloader.qml [new file with mode: 0644]
tests/auto/declarative/qquickrepeater/tst_qquickrepeater.cpp

index 51131ab..44e1f14 100644 (file)
@@ -294,7 +294,10 @@ qreal QQuickGridViewPrivate::colPosAt(int modelIndex) const
     if (FxViewItem *item = visibleItem(modelIndex))
         return static_cast<FxGridItemSG*>(item)->colPos();
     if (!visibleItems.isEmpty()) {
-        if (modelIndex < visibleIndex) {
+        if (modelIndex == visibleIndex) {
+            FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
+            return firstItem->colPos();
+        } else if (modelIndex < visibleIndex) {
             int count = (visibleIndex - modelIndex) % columns;
             int col = static_cast<FxGridItemSG*>(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<FxGridItemSG*>(item)->rowPos();
     if (!visibleItems.isEmpty()) {
-        if (modelIndex < visibleIndex) {
+        if (modelIndex == visibleIndex) {
+            FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
+            return firstItem->rowPos();
+        } else if (modelIndex < visibleIndex) {
             FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(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<FxGridItemSG*>(createItem(modelIndex))))
+//        qDebug() << "refill: append item" << modelIndex << colPos << rowPos;
+        if (!(item = static_cast<FxGridItemSG*>(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<FxGridItemSG*>(createItem(visibleIndex-1))))
+        if (!(item = static_cast<FxGridItemSG*>(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<FxGridItemSG*>(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);
index 63855ae..c329cd5 100644 (file)
@@ -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)
 {
index 8bb6353..b8e27e4 100644 (file)
@@ -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();
index ca4c0ce..4db274e 100644 (file)
@@ -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<QQuickItem*,int> 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;
index 60164d5..86af390 100644 (file)
@@ -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<FxListItemSG*>(createItem(modelIndex))))
+        if (!(item = static_cast<FxListItemSG*>(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<FxListItemSG*>(createItem(visibleIndex-1))))
+        if (!(item = static_cast<FxListItemSG*>(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<FxListItemSG*>(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())
index 242d9f3..f9fd914 100644 (file)
@@ -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.
 */
index 1963307..f262562 100644 (file)
@@ -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<QQuickPathViewAttached *>(qmlAttachedPropertiesObject<QQuickPathView>(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<QQuickPathViewAttached *>(qmlAttachedPropertiesObject<QQuickPathView>(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<QQuickPathViewAttached *>(qmlAttachedPropertiesObject<QQuickPathView>(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<QQuickPathViewAttached *>(qmlAttachedPropertiesObject<QQuickPathView>(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; i<d->items.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<QQuickPathViewAttached *>(qmlAttachedPropertiesObject<QQuickPathView>(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();
     }
 }
index 657d721..ebc1587 100644 (file)
@@ -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();
 
index 87d9313..9bf329d 100644 (file)
@@ -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<QQuickItem *> items;
     QList<QQuickItem *> itemCache;
     QDeclarativeGuard<QQuickVisualModel> model;
index 48632ef..f207afd 100644 (file)
@@ -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<QObject*>(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<int, QList<QPointer<QQuickItem> > > moved;
+    QHash<int, QVector<QPointer<QQuickItem> > > 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<QPointer<QQuickItem> > items = moved.value(insert.moveId);
+            QVector<QPointer<QQuickItem> > 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();
 }
index 66e583e..6da6adb 100644 (file)
@@ -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:
index a1ec159..962d177 100644 (file)
@@ -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<QPointer<QQuickItem> > deletables;
+    QVector<QPointer<QQuickItem> > deletables;
 };
 
 QT_END_NAMESPACE
index ce85484..649ca6f 100644 (file)
@@ -47,6 +47,7 @@
 #include <QtDeclarative/qdeclarativeengine.h>
 #include <QtDeclarative/qdeclarativeexpression.h>
 #include <QtDeclarative/qdeclarativeinfo.h>
+#include <QtDeclarative/qdeclarativeincubator.h>
 
 #include <private/qdeclarativecontext_p.h>
 #include <private/qdeclarativepackage_p.h>
@@ -62,6 +63,7 @@
 #include <private/qdeclarativechangeset_p.h>
 #include <private/qdeclarativelistcompositor_p.h>
 #include <private/qdeclarativeengine_p.h>
+#include <private/qquickitem_p.h>
 #include <private/qobject_p.h>
 
 #include <QtCore/qhash.h>
@@ -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<v8::Value> &groupNames) const;
+
+    static v8::Handle<v8::Value> get_model(v8::Local<v8::String>, const v8::AccessorInfo &info);
+    static v8::Handle<v8::Value> get_groups(v8::Local<v8::String>, const v8::AccessorInfo &info);
+    static void set_groups(
+            v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info);
+    static v8::Handle<v8::Value> get_member(v8::Local<v8::String>, const v8::AccessorInfo &info);
+    static void set_member(
+            v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info);
+    static v8::Handle<v8::Value> get_index(v8::Local<v8::String>, const v8::AccessorInfo &info);
+
+    QDeclarativeGuard<QQuickVisualDataModel> model;
+    const int groupCount;
+    const int memberPropertyOffset;
+    const int indexPropertyOffset;
+    QV8Engine * const v8Engine;
+    QMetaObject *metaObject;
+    const QStringList groupNames;
+    v8::Persistent<v8::Function> 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<QObject> 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<QQuickVisualDataModelPrivate *>(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<QQuickVisualDataGroup> *property);
     static QQuickVisualDataGroup *group_at(QDeclarativeListProperty<QQuickVisualDataGroup> *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<QByteArray> watchedRoles;
@@ -199,6 +298,8 @@ public:
         QQuickVisualDataGroup *m_groups[Compositor::MaximumGroupCount];
     };
     int m_groupCount;
+
+    QList<QVDMIncubationTask *> 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<QByteArray> 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<v8::Value> &groupNames) const;
-
-    static v8::Handle<v8::Value> get_model(v8::Local<v8::String>, const v8::AccessorInfo &info);
-    static v8::Handle<v8::Value> get_groups(v8::Local<v8::String>, const v8::AccessorInfo &info);
-    static void set_groups(
-            v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info);
-    static v8::Handle<v8::Value> get_member(v8::Local<v8::String>, const v8::AccessorInfo &info);
-    static void set_member(
-            v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info);
-    static v8::Handle<v8::Value> get_index(v8::Local<v8::String>, const v8::AccessorInfo &info);
-
-    QDeclarativeGuard<QQuickVisualDataModel> model;
-    const int groupCount;
-    const int memberPropertyOffset;
-    const int indexPropertyOffset;
-    QV8Engine * const v8Engine;
-    QMetaObject *metaObject;
-    const QStringList groupNames;
-    v8::Persistent<v8::Function> 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<QObject> 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<QQuickItem *>(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<QDeclarativePackage *>(cacheItem->object))
+            emitCreatedPackage(cacheItem, package);
+        else if (QQuickItem *item = qobject_cast<QQuickItem *>(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<QDeclarativePackage *>(cacheItem->object))
+        emitInitPackage(cacheItem, package);
+    else if (QQuickItem *item = qobject_cast<QQuickItem *>(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<QDeclarativePackage *>(cacheItem->object)) {
-                emitCreatedPackage(it, package);
-            } else if (!reference) {
-                if (QQuickItem *item = qobject_cast<QQuickItem *>(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<QQuickItem *>(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<v8::Value> 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<QDeclarativePackage *>(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<QObject *, QDeclarativePackage *>::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<QQuickItem *>(package->part(m_part)))
+        emit initItem(index, item);
+}
+
 void QQuickVisualPartsModel::destroyingPackage(QDeclarativePackage *package)
 {
     if (QQuickItem *item = qobject_cast<QQuickItem *>(package->part(m_part))) {
index 60b04ab..a5a384f 100644 (file)
@@ -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<QByteArray> roles);
 
index 31d06f6..78c4f88 100644 (file)
@@ -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);
index 9fc3d57..3d9610a 100644 (file)
@@ -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<QByteArray> 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<QByteArray>) {}
 
diff --git a/tests/auto/declarative/qquickgridview/data/asyncloader.qml b/tests/auto/declarative/qquickgridview/data/asyncloader.qml
new file mode 100644 (file)
index 0000000..ab66f20
--- /dev/null
@@ -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 }
+        }
+    }
+}
index e6a3923..4bf6f0b 100644 (file)
@@ -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
     }
 }
index 9cd39fc..3370b72 100644 (file)
@@ -46,6 +46,7 @@
 #include <QtDeclarative/qdeclarativecomponent.h>
 #include <QtDeclarative/qdeclarativecontext.h>
 #include <QtDeclarative/qdeclarativeexpression.h>
+#include <QtDeclarative/qdeclarativeincubator.h>
 #include <QtDeclarative/private/qquickitem_p.h>
 #include <QtDeclarative/private/qlistmodelinterface_p.h>
 #include <QtDeclarative/private/qquickgridview_p.h>
@@ -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<QQuickGridView>(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<QQuickItem>(contentItem, "wrapper").count();
+    for (int i = 0; i < model.count() && i < itemCount; ++i) {
+        QQuickItem *item = findItem<QQuickItem>(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<QQuickItem>(gridview, "wrapper", i) == 0);
+        QQuickItem *item = 0;
+        while (!item) {
+            bool b = false;
+            controller.incubateWhile(&b);
+            item = findItem<QQuickItem>(gridview, "wrapper", i);
+        }
+    }
+
+    {
+        bool b = true;
+        controller.incubateWhile(&b);
+    }
+
+    int newItemCount = 0;
+    newItemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
+
+    // Confirm items positioned correctly
+    for (int i = 0; i < model.count() && i < newItemCount; ++i) {
+        QQuickItem *item = findItem<QQuickItem>(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<QQuickItem>(contentItem, "wrapper", i);
+        QVERIFY(item);
+        QTRY_COMPARE(item->x(), (i%3)*80.0);
+        QTRY_COMPARE(item->y(), (i/3)*60.0);
+    }
+
+    QVERIFY(findItem<QQuickItem>(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<QQuickItem>(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<QQuickItem*>(canvas->rootObject());
+    QVERIFY(rootObject);
+
+    QQuickGridView *gridview = 0;
+    while (!gridview) {
+        bool b = false;
+        controller.incubateWhile(&b);
+        gridview = rootObject->findChild<QQuickGridView*>("view");
+    }
+
+    // items will be created one at a time
+    for (int i = 0; i < 12; ++i) {
+        QVERIFY(findItem<QQuickItem>(gridview, "wrapper", i) == 0);
+        QQuickItem *item = 0;
+        while (!item) {
+            bool b = false;
+            controller.incubateWhile(&b);
+            item = findItem<QQuickItem>(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<QQuickItem>(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 (file)
index 0000000..f038f09
--- /dev/null
@@ -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 }
+        }
+    }
+}
index 0202de1..47b341c 100644 (file)
@@ -64,7 +64,7 @@ Rectangle {
                     x: 200
                     text: wrapper.y
                 }
-                color: ListView.isCurrentItem ? "lightsteelblue" : "white"
+                color: ListView.isCurrentItem ? "lightsteelblue" : "#EEEEEE"
             }
         },
         Component {
index b12bf3e..3b41600 100644 (file)
@@ -45,6 +45,7 @@
 #include <QtDeclarative/qdeclarativeengine.h>
 #include <QtDeclarative/qdeclarativecontext.h>
 #include <QtDeclarative/qdeclarativeexpression.h>
+#include <QtDeclarative/qdeclarativeincubator.h>
 #include <QtDeclarative/private/qquickitem_p.h>
 #include <QtDeclarative/private/qquicklistview_p.h>
 #include <QtDeclarative/private/qquicktext_p.h>
@@ -147,6 +148,8 @@ private slots:
     void QTBUG_11105();
     void QTBUG_21742();
 
+    void asynchronous();
+
 private:
     template <class T> void items();
     template <class T> void changed();
@@ -1049,8 +1052,8 @@ void tst_QQuickListView::removed(bool /* animated */)
     // Confirm items positioned correctly
     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
     for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
-        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i+2);
-        if (!item) qWarning() << "Item" << i+2 << "not found";
+        QQuickItem *item = findItem<QQuickItem>(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<QQuickListView>(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<QQuickItem>(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<QQuickItem>(listview, "wrapper", i) == 0);
+        QQuickItem *item = 0;
+        while (!item) {
+            bool b = false;
+            controller.incubateWhile(&b);
+            item = findItem<QQuickItem>(listview, "wrapper", i);
+        }
+    }
+
+    {
+        bool b = true;
+        controller.incubateWhile(&b);
+    }
+
+    int newItemCount = 0;
+    newItemCount = findItems<QQuickItem>(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<QQuickItem>(contentItem, "wrapper", i);
+        if (!item) qWarning() << "Item" << i << "not found";
+        QVERIFY(item);
+        QVERIFY(item->y() == i*20);
+    }
+
+    QVERIFY(findItem<QQuickItem>(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<QQuickItem>(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<QQuickItem>(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<QQuickItem*>(canvas->rootObject());
+    QVERIFY(rootObject);
+
+    QQuickListView *listview = 0;
+    while (!listview) {
+        bool b = false;
+        controller.incubateWhile(&b);
+        listview = rootObject->findChild<QQuickListView*>("view");
+    }
+
+    // items will be created one at a time
+    for (int i = 0; i < 8; ++i) {
+        QVERIFY(findItem<QQuickItem>(listview, "wrapper", i) == 0);
+        QQuickItem *item = 0;
+        while (!item) {
+            bool b = false;
+            controller.incubateWhile(&b);
+            item = findItem<QQuickItem>(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<QQuickItem>(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 (file)
index 0000000..94f560f
--- /dev/null
@@ -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
+                }
+            }
+        }
+    }
+}
index ee7f993..b0efc58 100644 (file)
@@ -45,6 +45,7 @@
 #include <QtDeclarative/qdeclarativecomponent.h>
 #include <QtDeclarative/qdeclarativecontext.h>
 #include <QtDeclarative/qdeclarativeexpression.h>
+#include <QtDeclarative/qdeclarativeincubator.h>
 #include <QtDeclarative/private/qquickpathview_p.h>
 #include <QtDeclarative/private/qdeclarativepath_p.h>
 #include <QtDeclarative/private/qquicktext_p.h>
@@ -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; i<model.count(); i++) {
@@ -1497,6 +1499,62 @@ void tst_QQuickPathView::currentOffsetOnInsertion()
     delete canvas;
 }
 
+void tst_QQuickPathView::asynchronous()
+{
+    QQuickView *canvas = createView();
+    canvas->show();
+    QDeclarativeIncubationController controller;
+    canvas->engine()->setIncubationController(&controller);
+
+    canvas->setSource(TESTDATA("asyncloader.qml"));
+
+    QQuickItem *rootObject = qobject_cast<QQuickItem*>(canvas->rootObject());
+    QVERIFY(rootObject);
+
+    QQuickPathView *pathview = 0;
+    while (!pathview) {
+        bool b = false;
+        controller.incubateWhile(&b);
+        pathview = rootObject->findChild<QQuickPathView*>("view");
+    }
+
+    // items will be created one at a time
+    for (int i = 0; i < 5; ++i) {
+        QVERIFY(findItem<QQuickItem>(pathview, "wrapper", i) == 0);
+        QQuickItem *item = 0;
+        while (!item) {
+            bool b = false;
+            controller.incubateWhile(&b);
+            item = findItem<QQuickItem>(pathview, "wrapper", i);
+        }
+    }
+
+    {
+        bool b = true;
+        controller.incubateWhile(&b);
+    }
+
+    // verify positioning
+    QQuickRectangle *firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
+    QVERIFY(firstItem);
+    QDeclarativePath *path = qobject_cast<QDeclarativePath*>(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<QQuickItem>(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 (file)
index 0000000..82094e2
--- /dev/null
@@ -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 }
+                }
+            }
+        }
+    }
+}
index 355dd0d..fc76480 100644 (file)
 #include <QtDeclarative/qquickview.h>
 #include <QtDeclarative/qdeclarativecontext.h>
 #include <QtDeclarative/qdeclarativeexpression.h>
+#include <QtDeclarative/qdeclarativeincubator.h>
 #include <private/qquickrepeater_p.h>
 #include <private/qquicktext_p.h>
 
 #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<QQuickItem*>(canvas->rootObject());
+    QVERIFY(rootObject);
+
+    QQuickItem *container = findItem<QQuickItem>(rootObject, "container");
+    QVERIFY(!container);
+    while (!container) {
+        bool b = false;
+        controller.incubateWhile(&b);
+        container = findItem<QQuickItem>(rootObject, "container");
+    }
+
+    QQuickRepeater *repeater = 0;
+    while (!repeater) {
+        bool b = false;
+        controller.incubateWhile(&b);
+        repeater = findItem<QQuickRepeater>(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<QQuickItem>(container, name) == 0);
+        QQuickItem *item = 0;
+        while (!item) {
+            bool b = false;
+            controller.incubateWhile(&b);
+            item = findItem<QQuickItem>(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<QQuickItem>(container, name);
+        QTRY_COMPARE(item->y(), i * 50.0);
+    }
+
+    delete canvas;
+}
+
 QQuickView *tst_QQuickRepeater::createView()
 {
     QQuickView *canvas = new QQuickView(0);