aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items/qquickpathview.cpp
diff options
context:
space:
mode:
authorMartin Jones <martin.jones@nokia.com>2012-08-21 14:47:48 +1000
committerQt by Nokia <qt-info@nokia.com>2012-08-28 04:32:48 +0200
commit4a4a08d61a9771b5f49d7377b48d821075df3d97 (patch)
treec21f3d242a4a1d69f104efa41d66622ea119309f /src/quick/items/qquickpathview.cpp
parenta47597ea986e76cb6deeaaa64b3714add4688a01 (diff)
Add cacheItemCount property to PathView
cacheItemCount specifies the number of items to cache off the path when pathItemCount is specified. This allows up to cacheItemCount items to be kept alive when they move off the path, and also to asynchronously create items off path in preparation for display when the path offset changes. This is the equivalent of cacheBuffer for other views. Task-number: QTBUG-23931 Change-Id: I03497537d3f929e5e3579536850dd43eb2724c38 Reviewed-by: Bea Lam <bea.lam@nokia.com>
Diffstat (limited to 'src/quick/items/qquickpathview.cpp')
-rw-r--r--src/quick/items/qquickpathview.cpp167
1 files changed, 115 insertions, 52 deletions
diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp
index a30a292942..6ecad79f69 100644
--- a/src/quick/items/qquickpathview.cpp
+++ b/src/quick/items/qquickpathview.cpp
@@ -114,13 +114,13 @@ void QQuickPathViewAttached::setValue(const QByteArray &name, const QVariant &va
QQuickPathViewPrivate::QQuickPathViewPrivate()
: path(0), currentIndex(0), currentItemOffset(0.0), startPc(0)
- , offset(0.0), offsetAdj(0.0), mappedRange(1.0)
+ , offset(0.0), offsetAdj(0.0), mappedRange(1.0), mappedCache(0.0)
, stealMouse(false), ownModel(false), interactive(true), haveHighlightRange(true)
, autoHighlight(true), highlightUp(false), layoutScheduled(false)
- , moving(false), flicking(false), dragging(false), requestedOnPath(false), inRequest(false)
+ , moving(false), flicking(false), dragging(false), inRequest(false)
, dragMargin(0), deceleration(100), maximumFlickVelocity(QML_FLICK_DEFAULTMAXVELOCITY)
, moveOffset(this, &QQuickPathViewPrivate::setAdjustedOffset), flickDuration(0)
- , firstIndex(-1), pathItems(-1), requestedIndex(-1), requestedZ(0)
+ , firstIndex(-1), pathItems(-1), requestedIndex(-1), cacheSize(0), requestedZ(0)
, moveReason(Other), moveDirection(Shortest), attType(0), highlightComponent(0), highlightItem(0)
, moveHighlight(this, &QQuickPathViewPrivate::setHighlightPosition)
, highlightPosition(0)
@@ -144,22 +144,16 @@ void QQuickPathViewPrivate::init()
q, QQuickPathView, SLOT(movementEnding()))
}
-QQuickItem *QQuickPathViewPrivate::getItem(int modelIndex, qreal z, bool onPath)
+QQuickItem *QQuickPathViewPrivate::getItem(int modelIndex, qreal z, bool async)
{
Q_Q(QQuickPathView);
requestedIndex = modelIndex;
- requestedOnPath = onPath;
requestedZ = z;
inRequest = true;
- QQuickItem *item = model->item(modelIndex, false);
+ QQuickItem *item = model->item(modelIndex, async);
if (item) {
item->setParentItem(q);
requestedIndex = -1;
- qPathViewAttachedType = attType;
- QQuickPathViewAttached *att = static_cast<QQuickPathViewAttached *>(qmlAttachedPropertiesObject<QQuickPathView>(item));
- qPathViewAttachedType = 0;
- if (att)
- att->setOnPath(onPath);
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
}
@@ -179,7 +173,7 @@ void QQuickPathView::createdItem(int index, QQuickItem *item)
att->setOnPath(false);
}
item->setParentItem(this);
- d->updateItem(item, index < d->firstIndex ? 0.0 : 1.0);
+ d->updateItem(item, 1.0);
} else {
d->requestedIndex = -1;
if (!d->inRequest)
@@ -191,6 +185,7 @@ void QQuickPathView::initItem(int index, QQuickItem *item)
{
Q_D(QQuickPathView);
if (d->requestedIndex == index) {
+ QQuickItemPrivate::get(item)->setCulled(true);
item->setParentItem(this);
qPathViewAttachedType = d->attachedType();
QQuickPathViewAttached *att = static_cast<QQuickPathViewAttached *>(qmlAttachedPropertiesObject<QQuickPathView>(item));
@@ -198,11 +193,12 @@ void QQuickPathView::initItem(int index, QQuickItem *item)
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);
+ if (percent < 1.0) {
+ foreach (const QString &attr, d->path->attributes())
+ att->setValue(attr.toUtf8(), d->path->attributeAt(attr, percent));
+ item->setZ(d->requestedZ);
+ }
+ att->setOnPath(percent < 1.0);
}
}
}
@@ -248,16 +244,25 @@ void QQuickPathViewPrivate::clear()
QQuickItem *p = items[i];
releaseItem(p);
}
+ if (requestedIndex >= 0) {
+ if (model)
+ model->cancel(requestedIndex);
+ requestedIndex = -1;
+ }
+
items.clear();
tl.clear();
}
void QQuickPathViewPrivate::updateMappedRange()
{
- if (model && pathItems != -1 && pathItems < modelCount)
- mappedRange = qreal(pathItems)/modelCount;
- else
+ if (model && pathItems != -1 && pathItems < modelCount) {
+ mappedRange = qreal(modelCount)/pathItems;
+ mappedCache = qreal(cacheSize)/pathItems/2; // Half of cache at each end
+ } else {
mappedRange = 1.0;
+ mappedCache = 0.0;
+ }
}
qreal QQuickPathViewPrivate::positionOfIndex(qreal index) const
@@ -272,10 +277,9 @@ qreal QQuickPathViewPrivate::positionOfIndex(qreal index) const
qreal globalPos = index + offset;
globalPos = qmlMod(globalPos, qreal(modelCount)) / modelCount;
if (pathItems != -1 && pathItems < modelCount) {
- globalPos += start * mappedRange;
+ globalPos += start / mappedRange;
globalPos = qmlMod(globalPos, 1.0);
- if (globalPos < mappedRange)
- pos = globalPos / mappedRange;
+ pos = globalPos * mappedRange;
} else {
pos = qmlMod(globalPos + start, 1.0);
}
@@ -284,6 +288,18 @@ qreal QQuickPathViewPrivate::positionOfIndex(qreal index) const
return pos;
}
+// returns true if position is between lower and upper, taking into
+// account the circular space.
+bool QQuickPathViewPrivate::isInBound(qreal position, qreal lower, qreal upper) const
+{
+ if (lower > upper) {
+ if (position > upper && position > lower)
+ position -= mappedRange;
+ lower -= mappedRange;
+ }
+ return position >= lower && position < upper;
+}
+
void QQuickPathViewPrivate::createHighlight()
{
Q_Q(QQuickPathView);
@@ -376,11 +392,11 @@ void QQuickPathViewPrivate::setHighlightPosition(qreal pos)
// calc normalized position of highlight relative to offset
qreal relativeHighlight = qmlMod(pos + offset, range) / range;
- if (!highlightUp && relativeHighlight > end * mappedRange) {
+ if (!highlightUp && relativeHighlight > end / mappedRange) {
qreal diff = 1.0 - relativeHighlight;
setOffset(offset + diff * range);
- } else if (highlightUp && relativeHighlight >= (end - start) * mappedRange) {
- qreal diff = relativeHighlight - (end - start) * mappedRange;
+ } else if (highlightUp && relativeHighlight >= (end - start) / mappedRange) {
+ qreal diff = relativeHighlight - (end - start) / mappedRange;
setOffset(offset - diff * range - 0.00001);
}
@@ -388,7 +404,7 @@ void QQuickPathViewPrivate::setHighlightPosition(qreal pos)
qreal pathPos = positionOfIndex(pos);
updateItem(highlightItem, pathPos);
if (QQuickPathViewAttached *att = attached(highlightItem))
- att->setOnPath(pathPos != -1.0);
+ att->setOnPath(pathPos < 1.0);
}
}
@@ -413,8 +429,10 @@ void QQuickPathViewPrivate::updateItem(QQuickItem *item, qreal percent)
att->m_percent = percent;
foreach (const QString &attr, path->attributes())
att->setValue(attr.toUtf8(), path->attributeAt(attr, percent));
+ att->setOnPath(percent < 1.0);
}
- QPointF pf = path->pointAt(percent);
+ QQuickItemPrivate::get(item)->setCulled(percent >= 1.0);
+ QPointF pf = path->pointAt(qMin(percent, qreal(1.0)));
item->setX(pf.x() - item->width()/2);
item->setY(pf.y() - item->height()/2);
}
@@ -1255,6 +1273,43 @@ void QQuickPathView::resetPathItemCount()
}
/*!
+ \qmlproperty int QtQuick2::PathView::cacheItemCount
+ This property holds the maximum number of items to cache off the path.
+
+ For example, a PathView with a model containing 20 items, a pathItemCount
+ of 10, and an cacheItemCount of 4 will create up to 14 items, with 10 visible
+ on the path and 4 invisible cached items.
+
+ The cached delegates are created asynchronously,
+ allowing creation to occur across multiple frames and reducing the
+ likelihood of skipping frames.
+
+ Setting this value can improve the smoothness of scrolling behavior at the expense
+ of additional memory usage. It is not a substitute for creating efficient
+ delegates; the fewer objects and bindings in a delegate, the faster a view can be
+ moved.
+
+ \sa pathItemCount
+*/
+int QQuickPathView::cacheItemCount() const
+{
+ Q_D(const QQuickPathView);
+ return d->cacheSize;
+}
+
+void QQuickPathView::setCacheItemCount(int i)
+{
+ Q_D(QQuickPathView);
+ if (i == d->cacheSize || i < 0)
+ return;
+
+ d->cacheSize = i;
+ d->updateMappedRange();
+ refill();
+ emit cacheItemCountChanged();
+}
+
+/*!
\qmlproperty enumeration QtQuick2::PathView::snapMode
This property determines how the items will settle following a drag or flick.
@@ -1567,7 +1622,8 @@ void QQuickPathViewPrivate::handleMouseMoveEvent(QMouseEvent *event)
}
} else {
moveReason = QQuickPathViewPrivate::Mouse;
- qreal diff = (newPc - startPc)*modelCount*mappedRange;
+ int count = pathItems == -1 ? modelCount : qMin(pathItems, modelCount);
+ qreal diff = (newPc - startPc)*count;
if (diff) {
q->setOffset(offset + diff);
@@ -1617,7 +1673,7 @@ void QQuickPathViewPrivate::handleMouseReleaseEvent(QMouseEvent *)
}
qreal velocity = calcVelocity();
- qreal count = modelCount*mappedRange;
+ qreal count = pathItems == -1 ? modelCount : qMin(pathItems, modelCount);
qreal pixelVelocity = (path->path().length()/count) * velocity;
if (qAbs(pixelVelocity) > MinimumFlickVelocity) {
if (qAbs(pixelVelocity) > maximumFlickVelocity || snapMode == QQuickPathView::SnapOneItem) {
@@ -1790,6 +1846,7 @@ void QQuickPathView::refill()
return;
bool currentVisible = false;
+ int count = d->pathItems == -1 ? d->modelCount : qMin(d->pathItems, d->modelCount);
// first move existing items and remove items off path
int idx = d->firstIndex;
@@ -1797,7 +1854,7 @@ void QQuickPathView::refill()
while (it != d->items.end()) {
qreal pos = d->positionOfIndex(idx);
QQuickItem *item = *it;
- if (pos >= 0.0) {
+ if (pos < 1.0) {
d->updateItem(item, pos);
if (idx == d->currentIndex) {
currentVisible = true;
@@ -1805,27 +1862,34 @@ void QQuickPathView::refill()
}
++it;
} else {
- // qDebug() << "release";
- d->updateItem(item, 1.0);
- d->releaseItem(item);
- if (it == d->items.begin()) {
- if (++d->firstIndex >= d->modelCount)
- d->firstIndex = 0;
+ d->updateItem(item, pos);
+ if (QQuickPathViewAttached *att = d->attached(item))
+ att->setOnPath(pos < 1.0);
+ if (!d->isInBound(pos, d->mappedRange - d->mappedCache, 1.0 + d->mappedCache)) {
+// qDebug() << "release";
+ d->releaseItem(item);
+ if (it == d->items.begin()) {
+ if (++d->firstIndex >= d->modelCount) {
+ d->firstIndex = 0;
+ }
+ }
+ it = d->items.erase(it);
+ } else {
+ ++it;
}
- it = d->items.erase(it);
}
++idx;
if (idx >= d->modelCount)
idx = 0;
}
+
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);
- if (d->items.count() < count) {
+ if (d->items.count() < count+d->cacheSize) {
int idx = qRound(d->modelCount - d->offset) % d->modelCount;
qreal startPos = 0.0;
if (d->haveHighlightRange && (d->highlightRangeMode != QQuickPathView::NoHighlightRange
@@ -1836,9 +1900,9 @@ void QQuickPathView::refill()
idx = (d->firstIndex + d->items.count()) % d->modelCount;
}
qreal pos = d->positionOfIndex(idx);
- while ((pos > startPos || !d->items.count()) && d->items.count() < count) {
+ while ((d->isInBound(pos, startPos, 1.0 + d->mappedCache) || !d->items.count()) && d->items.count() < count+d->cacheSize) {
// qDebug() << "append" << idx;
- QQuickItem *item = d->getItem(idx, idx+1);
+ QQuickItem *item = d->getItem(idx, idx+1, pos >= 1.0);
if (!item) {
waiting = true;
break;
@@ -1861,9 +1925,9 @@ void QQuickPathView::refill()
if (idx < 0)
idx = d->modelCount - 1;
pos = d->positionOfIndex(idx);
- while (!waiting && (pos >= 0.0 && pos < startPos) && d->items.count() < count) {
+ while (!waiting && d->isInBound(pos, d->mappedRange - d->mappedCache, startPos) && d->items.count() < count+d->cacheSize) {
// qDebug() << "prepend" << idx;
- QQuickItem *item = d->getItem(idx, idx+1);
+ QQuickItem *item = d->getItem(idx, idx+1, pos >= 1.0);
if (!item) {
waiting = true;
break;
@@ -1886,17 +1950,16 @@ void QQuickPathView::refill()
if (!currentVisible) {
d->currentItemOffset = 1.0;
if (d->currentItem) {
- if (QQuickPathViewAttached *att = d->attached(d->currentItem))
- att->setOnPath(false);
+ d->updateItem(d->currentItem, 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 ((d->currentItem = d->getItem(d->currentIndex, d->currentIndex))) {
+ d->updateItem(d->currentItem, 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))) {
+ if ((d->currentItem = d->getItem(d->currentIndex, d->currentIndex))) {
d->currentItem->setFocus(true);
if (QQuickPathViewAttached *att = d->attached(d->currentItem))
att->setIsCurrentItem(true);
@@ -2062,14 +2125,14 @@ void QQuickPathViewPrivate::createCurrentItem()
return;
int itemIndex = (currentIndex - firstIndex + modelCount) % modelCount;
if (itemIndex < items.count()) {
- if ((currentItem = getItem(currentIndex, currentIndex, true))) {
+ if ((currentItem = getItem(currentIndex, currentIndex))) {
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 ((currentItem = getItem(currentIndex, currentIndex))) {
+ updateItem(currentItem, 1.0);
if (QQuickPathViewAttached *att = attached(currentItem))
att->setIsCurrentItem(true);
}