summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/declarative/parallax/qml/ParallaxView.qml8
-rw-r--r--src/declarative/graphicsitems/qmlgraphicsflickable.cpp24
-rw-r--r--src/declarative/graphicsitems/qmlgraphicsflickable_p_p.h4
-rw-r--r--src/declarative/graphicsitems/qmlgraphicsgridview.cpp41
-rw-r--r--src/declarative/graphicsitems/qmlgraphicsgridview_p.h1
-rw-r--r--src/declarative/graphicsitems/qmlgraphicslistview.cpp351
-rw-r--r--src/declarative/graphicsitems/qmlgraphicslistview_p.h7
-rw-r--r--tests/auto/declarative/qmlgraphicsgridview/tst_qmlgraphicsgridview.cpp91
8 files changed, 425 insertions, 102 deletions
diff --git a/examples/declarative/parallax/qml/ParallaxView.qml b/examples/declarative/parallax/qml/ParallaxView.qml
index 98dd246..ff4a85a 100644
--- a/examples/declarative/parallax/qml/ParallaxView.qml
+++ b/examples/declarative/parallax/qml/ParallaxView.qml
@@ -25,13 +25,7 @@ Item {
anchors.fill: parent
model: VisualItemModel { id: visualModel }
- highlight: Rectangle { height: 1; width: 1 }
- highlightMoveSpeed: 2000
- preferredHighlightBegin: 0
- preferredHighlightEnd: 0
- highlightRangeMode: "StrictlyEnforceRange"
-
- flickDeceleration: 1000
+ snapMode: ListView.SnapOneItem
}
ListView {
diff --git a/src/declarative/graphicsitems/qmlgraphicsflickable.cpp b/src/declarative/graphicsitems/qmlgraphicsflickable.cpp
index cc0f905..e010081 100644
--- a/src/declarative/graphicsitems/qmlgraphicsflickable.cpp
+++ b/src/declarative/graphicsitems/qmlgraphicsflickable.cpp
@@ -855,30 +855,6 @@ void QmlGraphicsFlickable::viewportMoved()
QmlGraphicsItemPrivate::restart(d->velocityTime);
d->lastFlickablePosition = QPointF(d->_moveY.value(), d->_moveX.value());
- if (d->flicked && d->timeline.time() > d->vTime) {
- // Near an end and it seems that the extent has changed?
- // Recalculate the flick so that we don't end up in an odd position.
- if (d->velocityY > 0) {
- const qreal minY = minYExtent();
- if (minY - d->_moveY.value() < height()/2 && minY != d->flickTargetY)
- d->flickY(-d->verticalVelocity.value());
- } else if (d->velocityY < 0) {
- const qreal maxY = maxYExtent();
- if (d->_moveY.value() - maxY < height()/2 && maxY != d->flickTargetY)
- d->flickY(-d->verticalVelocity.value());
- }
-
- if (d->velocityX > 0) {
- const qreal minX = minXExtent();
- if (minX - d->_moveX.value() < height()/2 && minX != d->flickTargetX)
- d->flickX(-d->horizontalVelocity.value());
- } else if (d->velocityX < 0) {
- const qreal maxX = maxXExtent();
- if (d->_moveX.value() - maxX < height()/2 && maxX != d->flickTargetX)
- d->flickX(-d->horizontalVelocity.value());
- }
- }
-
d->vTime = d->timeline.time();
d->updateBeginningEnd();
}
diff --git a/src/declarative/graphicsitems/qmlgraphicsflickable_p_p.h b/src/declarative/graphicsitems/qmlgraphicsflickable_p_p.h
index 2850e35..8044ba7 100644
--- a/src/declarative/graphicsitems/qmlgraphicsflickable_p_p.h
+++ b/src/declarative/graphicsitems/qmlgraphicsflickable_p_p.h
@@ -111,8 +111,8 @@ public:
QTime velocityTime;
QPointF lastFlickablePosition;
qreal reportedVelocitySmoothing;
- int flickTargetX;
- int flickTargetY;
+ qreal flickTargetX;
+ qreal flickTargetY;
QGraphicsSceneMouseEvent *delayedPressEvent;
QGraphicsItem *delayedPressTarget;
QBasicTimer delayedPressTimer;
diff --git a/src/declarative/graphicsitems/qmlgraphicsgridview.cpp b/src/declarative/graphicsitems/qmlgraphicsgridview.cpp
index e36ea50..81128e7 100644
--- a/src/declarative/graphicsitems/qmlgraphicsgridview.cpp
+++ b/src/declarative/graphicsitems/qmlgraphicsgridview.cpp
@@ -388,9 +388,9 @@ void QmlGraphicsGridViewPrivate::refill(qreal from, qreal to)
to += buffer;
bool changed = false;
- int colPos = 0;
- int rowPos = 0;
- int modelIndex = 0;
+ int colPos = colPosAt(visibleIndex);
+ int rowPos = rowPosAt(visibleIndex);
+ int modelIndex = visibleIndex;
if (visibleItems.count()) {
rowPos = visibleItems.last()->rowPos();
colPos = visibleItems.last()->colPos() + colSize();
@@ -822,14 +822,14 @@ void QmlGraphicsGridView::setModel(const QVariant &model)
}
/*!
- \qmlproperty component GridView::delegate
+ \qmlproperty component GridView::delegate
The delegate provides a template defining each item instantiated by the view.
The index is exposed as an accessible \c index property. Properties of the
model are also available depending upon the type of \l {qmlmodels}{Data Model}.
- Here is an example delegate:
- \snippet doc/src/snippets/declarative/gridview/gridview.qml 0
+ Here is an example delegate:
+ \snippet doc/src/snippets/declarative/gridview/gridview.qml 0
*/
QmlComponent *QmlGraphicsGridView::delegate() const
{
@@ -1292,6 +1292,35 @@ void QmlGraphicsGridView::moveCurrentIndexRight()
}
}
+void QmlGraphicsGridView::positionViewAtIndex(int index)
+{
+ Q_D(QmlGraphicsGridView);
+ if (!d->isValid() || index < 0 || index >= d->model->count())
+ return;
+
+ qreal maxExtent = d->flow == QmlGraphicsGridView::LeftToRight ? -maxYExtent() : -maxXExtent();
+ FxGridItem *item = d->visibleItem(index);
+ if (item) {
+ // Already created - just move to top of view
+ int pos = qMin(item->rowPos(), maxExtent);
+ d->setPosition(pos);
+ } else {
+ int pos = d->rowPosAt(index);
+ // save the currently visible items in case any of them end up visible again
+ QList<FxGridItem*> oldVisible = d->visibleItems;
+ d->visibleItems.clear();
+ d->visibleIndex = index - index % d->columns;
+ d->setPosition(pos);
+ // setPosition() will cause refill. Adjust if we have moved beyond range
+ if (d->position() > maxExtent)
+ d->setPosition(maxExtent);
+ // now release the reference to all the old visible items.
+ for (int i = 0; i < oldVisible.count(); ++i)
+ d->releaseItem(oldVisible.at(i));
+ }
+}
+
+
void QmlGraphicsGridView::componentComplete()
{
Q_D(QmlGraphicsGridView);
diff --git a/src/declarative/graphicsitems/qmlgraphicsgridview_p.h b/src/declarative/graphicsitems/qmlgraphicsgridview_p.h
index 3e09cf3..99515a3 100644
--- a/src/declarative/graphicsitems/qmlgraphicsgridview_p.h
+++ b/src/declarative/graphicsitems/qmlgraphicsgridview_p.h
@@ -121,6 +121,7 @@ public Q_SLOTS:
void moveCurrentIndexDown();
void moveCurrentIndexLeft();
void moveCurrentIndexRight();
+ void positionViewAtIndex(int index);
Q_SIGNALS:
void countChanged();
diff --git a/src/declarative/graphicsitems/qmlgraphicslistview.cpp b/src/declarative/graphicsitems/qmlgraphicslistview.cpp
index 7d0b99a..9dd2ab4 100644
--- a/src/declarative/graphicsitems/qmlgraphicslistview.cpp
+++ b/src/declarative/graphicsitems/qmlgraphicslistview.cpp
@@ -177,8 +177,9 @@ public:
, highlightComponent(0), highlight(0), trackedItem(0)
, moveReason(Other), buffer(0), highlightPosAnimator(0), highlightSizeAnimator(0), spacing(0.0)
, highlightMoveSpeed(400), highlightResizeSpeed(400), highlightRange(QmlGraphicsListView::NoHighlightRange)
- , ownModel(false), wrap(false), autoHighlight(true)
- , haveHighlightRange(false)
+ , snapMode(QmlGraphicsListView::NoSnap), overshootDist(0.0)
+ , ownModel(false), wrap(false), autoHighlight(true), haveHighlightRange(false)
+ , correctFlick(true)
{}
void init();
@@ -197,6 +198,32 @@ public:
return 0;
}
+ FxListItem *firstVisibleItem() const {
+ const qreal pos = position();
+ for (int i = 0; i < visibleItems.count(); ++i) {
+ FxListItem *item = visibleItems.at(i);
+ if (item->index != -1 && item->endPosition() > pos)
+ return item;
+ }
+ return 0;
+ }
+
+ FxListItem *nextVisibleItem() const {
+ qDebug() << "last vis";
+ const qreal pos = position();
+ bool foundFirst = false;
+ for (int i = 0; i < visibleItems.count(); ++i) {
+ FxListItem *item = visibleItems.at(i);
+ if (item->index != -1) {
+ if (foundFirst)
+ return item;
+ else if (item->position() < pos && item->endPosition() > pos)
+ foundFirst = true;
+ }
+ }
+ return 0;
+ }
+
qreal position() const {
Q_Q(const QmlGraphicsListView);
return orient == QmlGraphicsListView::Vertical ? q->viewportY() : q->viewportX();
@@ -287,11 +314,42 @@ public:
return index;
}
- //XXX Rough. Only works for fixed size items.
qreal snapPosAt(qreal pos) {
+ for (int i = 0; i < visibleItems.count(); ++i) {
+ FxListItem *item = visibleItems[i];
+ if (item->index == -1)
+ continue;
+ qreal itemTop = item->position();
+ if ((item->index == model->count()-1 || itemTop >= pos-item->size()/2)
+ && (item->index == 0 || itemTop <= pos+item->size()/2))
+ return item->position();
+ }
+ if (visibleItems.count()) {
+ qreal firstPos = visibleItems.first()->position();
+ qreal endPos = visibleItems.last()->position();
+ if (pos < firstPos) {
+ return firstPos - qRound((firstPos - pos) / averageSize) * averageSize;
+ } else if (pos > endPos)
+ return endPos + qRound((pos - endPos) / averageSize) * averageSize;
+ }
return qRound((pos - startPosition()) / averageSize) * averageSize + startPosition();
}
+ FxListItem *snapItemAt(qreal pos) {
+ for (int i = 0; i < visibleItems.count(); ++i) {
+ FxListItem *item = visibleItems[i];
+ if (item->index == -1)
+ continue;
+ qreal itemTop = item->position();
+ if ((item->index == model->count()-1 || itemTop >= pos-item->size()/2)
+ && (item->index == 0 || itemTop <= pos+item->size()/2))
+ return item;
+ }
+ if (visibleItems.count() && visibleItems.first()->position() <= pos)
+ return visibleItems.first();
+ return 0;
+ }
+
int lastVisibleIndex() const {
int lastIndex = -1;
for (int i = visibleItems.count()-1; i >= 0; --i) {
@@ -409,11 +467,14 @@ public:
qreal highlightMoveSpeed;
qreal highlightResizeSpeed;
QmlGraphicsListView::HighlightRangeMode highlightRange;
+ QmlGraphicsListView::SnapMode snapMode;
+ qreal overshootDist;
bool ownModel : 1;
bool wrap : 1;
bool autoHighlight : 1;
bool haveHighlightRange : 1;
+ bool correctFlick : 1;
};
void QmlGraphicsListViewPrivate::init()
@@ -809,9 +870,11 @@ void QmlGraphicsListViewPrivate::fixupPosition()
void QmlGraphicsListViewPrivate::fixupY()
{
- QmlGraphicsFlickablePrivate::fixupY();
+ Q_Q(QmlGraphicsListView);
if (orient == QmlGraphicsListView::Horizontal)
return;
+ if (!q->yflick() || _moveY.timeLine())
+ return;
if (haveHighlightRange && highlightRange == QmlGraphicsListView::StrictlyEnforceRange) {
if (currentItem && highlight && currentItem->position() != highlight->position()) {
@@ -820,14 +883,29 @@ void QmlGraphicsListViewPrivate::fixupY()
timeline.move(_moveY, -(currentItem->position() - highlightRangeStart), QEasingCurve(QEasingCurve::InOutQuad), 200);
vTime = timeline.time();
}
+ } else if (snapMode != QmlGraphicsListView::NoSnap) {
+ moveReason = Mouse;
+ if (FxListItem *item = snapItemAt(position())) {
+ qreal pos = qMin(item->position() - highlightRangeStart, -q->maxYExtent());
+ qreal dist = qAbs(_moveY + pos);
+ if (dist > 0) {
+ timeline.reset(_moveY);
+ timeline.move(_moveY, -pos, QEasingCurve(QEasingCurve::InOutQuad), 200);
+ vTime = timeline.time();
+ }
+ }
+ } else {
+ QmlGraphicsFlickablePrivate::fixupY();
}
}
void QmlGraphicsListViewPrivate::fixupX()
{
- QmlGraphicsFlickablePrivate::fixupX();
+ Q_Q(QmlGraphicsListView);
if (orient == QmlGraphicsListView::Vertical)
return;
+ if (!q->xflick() || _moveX.timeLine())
+ return;
if (haveHighlightRange && highlightRange == QmlGraphicsListView::StrictlyEnforceRange) {
if (currentItem && highlight && currentItem->position() != highlight->position()) {
@@ -836,6 +914,19 @@ void QmlGraphicsListViewPrivate::fixupX()
timeline.move(_moveX, -(currentItem->position() - highlightRangeStart), QEasingCurve(QEasingCurve::InOutQuad), 200);
vTime = timeline.time();
}
+ } else if (snapMode != QmlGraphicsListView::NoSnap) {
+ moveReason = Mouse;
+ if (FxListItem *item = snapItemAt(position())) {
+ qreal pos = qMin(item->position() - highlightRangeStart, -q->maxXExtent());
+ qreal dist = qAbs(_moveX + pos);
+ if (dist > 0) {
+ timeline.reset(_moveX);
+ timeline.move(_moveX, -pos, QEasingCurve(QEasingCurve::InOutQuad), 200);
+ vTime = timeline.time();
+ }
+ }
+ } else {
+ QmlGraphicsFlickablePrivate::fixupX();
}
}
@@ -843,23 +934,38 @@ void QmlGraphicsListViewPrivate::flickX(qreal velocity)
{
Q_Q(QmlGraphicsListView);
- if (!haveHighlightRange || highlightRange != QmlGraphicsListView::StrictlyEnforceRange) {
+ if ((!haveHighlightRange || highlightRange != QmlGraphicsListView::StrictlyEnforceRange) && snapMode == QmlGraphicsListView::NoSnap) {
QmlGraphicsFlickablePrivate::flickX(velocity);
return;
}
-
qreal maxDistance = -1;
+ const qreal maxX = q->maxXExtent();
+ const qreal minX = q->minXExtent();
// -ve velocity means list is moving up
if (velocity > 0) {
- if (_moveX.value() < q->minXExtent())
- maxDistance = qAbs(q->minXExtent() -_moveX.value() + (overShoot?30:0));
- flickTargetX = q->minXExtent();
+ if (snapMode == QmlGraphicsListView::SnapOneItem) {
+ if (FxListItem *item = firstVisibleItem())
+ maxDistance = qAbs(item->position() + _moveX.value());
+ } else if (_moveX.value() < minX) {
+ maxDistance = qAbs(minX -_moveX.value() + (overShoot?30:0));
+ }
+ if (snapMode != QmlGraphicsListView::SnapToItem && highlightRange != QmlGraphicsListView::StrictlyEnforceRange)
+ flickTargetX = minX;
} else {
- if (_moveX.value() > q->maxXExtent())
- maxDistance = qAbs(q->maxXExtent() - _moveX.value()) + (overShoot?30:0);
- flickTargetX = q->maxXExtent();
- }
- if (maxDistance > 0) {
+ if (snapMode == QmlGraphicsListView::SnapOneItem) {
+ if (FxListItem *item = nextVisibleItem())
+ maxDistance = qAbs(item->position() + _moveX.value());
+ } else if (_moveX.value() > maxX) {
+ maxDistance = qAbs(maxX - _moveX.value()) + (overShoot?30:0);
+ }
+ if (snapMode != QmlGraphicsListView::SnapToItem && highlightRange != QmlGraphicsListView::StrictlyEnforceRange)
+ flickTargetX = maxX;
+ }
+ if (maxDistance > 0 && (snapMode != QmlGraphicsListView::NoSnap || highlightRange == QmlGraphicsListView::StrictlyEnforceRange)) {
+ // These modes require the list to stop exactly on an item boundary.
+ // The initial flick will estimate the boundary to stop on.
+ // Since list items can have variable sizes, the boundary will be
+ // reevaluated and adjusted as we approach the boundary.
qreal v = velocity;
if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
if (v < 0)
@@ -867,31 +973,57 @@ void QmlGraphicsListViewPrivate::flickX(qreal velocity)
else
v = maxVelocity;
}
- qreal accel = deceleration;
- qreal v2 = v * v;
- qreal maxAccel = v2 / (2.0f * maxDistance);
- if (maxAccel < accel) {
- // If we are not flicking to the end then attempt to stop exactly on an item boundary
- qreal dist = v2 / accel / 2.0;
- if (v > 0)
- dist = -dist;
- dist = -_moveX.value() - snapPosAt(-(_moveX.value() - highlightRangeStart) + dist) + highlightRangeStart;
- if ((v < 0 && dist >= 0) || (v > 0 && dist <= 0)) {
- timeline.reset(_moveX);
- fixupX();
- return;
- }
- accel = v2 / (2.0f * qAbs(dist));
- }
- timeline.reset(_moveX);
- timeline.accel(_moveX, v, accel, maxDistance);
- timeline.execute(fixupXEvent);
if (!flicked) {
+ // the initial flick - estimate boundary
+ qreal accel = deceleration;
+ qreal v2 = v * v;
+ qreal maxAccel = v2 / (2.0f * maxDistance);
+ if (maxAccel < accel) {
+ qreal dist = v2 / (accel * 2.0);
+ if (v > 0)
+ dist = -dist;
+ flickTargetX = -snapPosAt(-(_moveX.value() - highlightRangeStart) + dist) + highlightRangeStart;
+ dist = -flickTargetX + _moveX.value();
+ accel = v2 / (2.0f * qAbs(dist));
+ overshootDist = 0.0;
+ } else {
+ if (velocity > 0)
+ flickTargetX = minX;
+ else
+ flickTargetX = maxX;
+ overshootDist = overShoot ? 30 : 0;
+ }
+ timeline.reset(_moveX);
+ timeline.accel(_moveX, v, accel, maxDistance);
+ timeline.execute(fixupXEvent);
flicked = true;
emit q->flickingChanged();
emit q->flickStarted();
+ correctFlick = true;
+ } else {
+ // reevaluate the target boundary.
+ qreal newtarget = -snapPosAt(-(flickTargetX - highlightRangeStart)) + highlightRangeStart;
+ if (newtarget < maxX) {
+ newtarget = maxX;
+ }
+ if (newtarget == flickTargetX) {
+ // boundary unchanged - nothing to do
+ return;
+ }
+ flickTargetX = newtarget;
+ qreal dist = -newtarget + _moveX.value();
+ if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) {
+ correctFlick = false;
+ timeline.reset(_moveX);
+ fixupX();
+ return;
+ }
+ timeline.reset(_moveX);
+ timeline.accelDistance(_moveX, v, -dist + (v < 0 ? -overshootDist : overshootDist));
+ timeline.execute(fixupXEvent);
}
} else {
+ correctFlick = false;
timeline.reset(_moveX);
fixupX();
}
@@ -901,23 +1033,38 @@ void QmlGraphicsListViewPrivate::flickY(qreal velocity)
{
Q_Q(QmlGraphicsListView);
- if (!haveHighlightRange || highlightRange != QmlGraphicsListView::StrictlyEnforceRange) {
+ if ((!haveHighlightRange || highlightRange != QmlGraphicsListView::StrictlyEnforceRange) && snapMode == QmlGraphicsListView::NoSnap) {
QmlGraphicsFlickablePrivate::flickY(velocity);
return;
}
-
qreal maxDistance = -1;
+ const qreal maxY = q->maxYExtent();
+ const qreal minY = q->minYExtent();
// -ve velocity means list is moving up
if (velocity > 0) {
- if (_moveY.value() < q->minYExtent())
- maxDistance = qAbs(q->minYExtent() -_moveY.value() + (overShoot?30:0));
- flickTargetY = q->minYExtent();
+ if (snapMode == QmlGraphicsListView::SnapOneItem) {
+ if (FxListItem *item = firstVisibleItem())
+ maxDistance = qAbs(item->position() + _moveY.value());
+ } else if (_moveY.value() < minY) {
+ maxDistance = qAbs(minY -_moveY.value() + (overShoot?30:0));
+ }
+ if (snapMode != QmlGraphicsListView::SnapToItem && highlightRange != QmlGraphicsListView::StrictlyEnforceRange)
+ flickTargetY = minY;
} else {
- if (_moveY.value() > q->maxYExtent())
- maxDistance = qAbs(q->maxYExtent() - _moveY.value()) + (overShoot?30:0);
- flickTargetY = q->maxYExtent();
- }
- if (maxDistance > 0) {
+ if (snapMode == QmlGraphicsListView::SnapOneItem) {
+ if (FxListItem *item = nextVisibleItem())
+ maxDistance = qAbs(item->position() + _moveY.value());
+ } else if (_moveY.value() > maxY) {
+ maxDistance = qAbs(maxY - _moveY.value()) + (overShoot?30:0);
+ }
+ if (snapMode != QmlGraphicsListView::SnapToItem && highlightRange != QmlGraphicsListView::StrictlyEnforceRange)
+ flickTargetY = maxY;
+ }
+ if (maxDistance > 0 && (snapMode != QmlGraphicsListView::NoSnap || highlightRange == QmlGraphicsListView::StrictlyEnforceRange)) {
+ // These modes require the list to stop exactly on an item boundary.
+ // The initial flick will estimate the boundary to stop on.
+ // Since list items can have variable sizes, the boundary will be
+ // reevaluated and adjusted as we approach the boundary.
qreal v = velocity;
if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
if (v < 0)
@@ -925,31 +1072,57 @@ void QmlGraphicsListViewPrivate::flickY(qreal velocity)
else
v = maxVelocity;
}
- qreal accel = deceleration;
- qreal v2 = v * v;
- qreal maxAccel = v2 / (2.0f * maxDistance);
- if (maxAccel < accel) {
- // If we are not flicking to the end then attempt to stop exactly on an item boundary
- qreal dist = v2 / accel / 2.0;
- if (v > 0)
- dist = -dist;
- dist = -_moveY.value() - snapPosAt(-(_moveY.value() - highlightRangeStart) + dist) + highlightRangeStart;
- if ((v < 0 && dist >= 0) || (v > 0 && dist <= 0)) {
- timeline.reset(_moveY);
- fixupY();
- return;
- }
- accel = v2 / (2.0f * qAbs(dist));
- }
- timeline.reset(_moveY);
- timeline.accel(_moveY, v, accel, maxDistance);
- timeline.execute(fixupYEvent);
if (!flicked) {
+ // the initial flick - estimate boundary
+ qreal accel = deceleration;
+ qreal v2 = v * v;
+ qreal maxAccel = v2 / (2.0f * maxDistance);
+ if (maxAccel < accel) {
+ qreal dist = v2 / (accel * 2.0);
+ if (v > 0)
+ dist = -dist;
+ flickTargetY = -snapPosAt(-(_moveY.value() - highlightRangeStart) + dist) + highlightRangeStart;
+ dist = -flickTargetY + _moveY.value();
+ accel = v2 / (2.0f * qAbs(dist));
+ overshootDist = 0.0;
+ } else {
+ if (velocity > 0)
+ flickTargetY = minY;
+ else
+ flickTargetY = maxY;
+ overshootDist = overShoot ? 30 : 0;
+ }
+ timeline.reset(_moveY);
+ timeline.accel(_moveY, v, accel, maxDistance);
+ timeline.execute(fixupYEvent);
flicked = true;
emit q->flickingChanged();
emit q->flickStarted();
+ correctFlick = true;
+ } else {
+ // reevaluate the target boundary.
+ qreal newtarget = -snapPosAt(-(flickTargetY - highlightRangeStart)) + highlightRangeStart;
+ if (newtarget < maxY) {
+ newtarget = maxY;
+ }
+ if (newtarget == flickTargetY) {
+ // boundary unchanged - nothing to do
+ return;
+ }
+ flickTargetY = newtarget;
+ qreal dist = -newtarget + _moveY.value();
+ if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) {
+ correctFlick = false;
+ timeline.reset(_moveY);
+ fixupY();
+ return;
+ }
+ timeline.reset(_moveY);
+ timeline.accelDistance(_moveY, v, -dist + (v < 0 ? -overshootDist : overshootDist));
+ timeline.execute(fixupYEvent);
}
} else {
+ correctFlick = false;
timeline.reset(_moveY);
fixupY();
}
@@ -1557,6 +1730,35 @@ void QmlGraphicsListView::setHighlightResizeSpeed(qreal speed)
}
}
+/*!
+ \qmlproperty enumeration ListView::snapMode
+
+ This property determines where the view will settle following a drag or flick.
+ The allowed values are:
+
+ \list
+ \o NoSnap (default) - the view will stop anywhere within the visible area.
+ \o SnapToItem - the view will settle with an item aligned with the start of
+ the view.
+ \o SnapOneItem - the view will settle no more than one item away from the first
+ visible item at the time the mouse button is released. This mode is particularly
+ useful for moving one page at a time.
+ \endlist
+*/
+QmlGraphicsListView::SnapMode QmlGraphicsListView::snapMode() const
+{
+ Q_D(const QmlGraphicsListView);
+ return d->snapMode;
+}
+
+void QmlGraphicsListView::setSnapMode(SnapMode mode)
+{
+ Q_D(QmlGraphicsListView);
+ if (d->snapMode != mode) {
+ d->snapMode = mode;
+ }
+}
+
void QmlGraphicsListView::viewportMoved()
{
Q_D(QmlGraphicsListView);
@@ -1580,6 +1782,29 @@ void QmlGraphicsListView::viewportMoved()
d->updateCurrent(idx);
}
}
+
+ if ((d->haveHighlightRange && d->highlightRange == QmlGraphicsListView::StrictlyEnforceRange)
+ || d->snapMode == QmlGraphicsListView::SnapToItem) {
+ if (d->flicked && d->correctFlick) {
+ // Near an end and it seems that the extent has changed?
+ // Recalculate the flick so that we don't end up in an odd position.
+ if (d->velocityY > 0) {
+ if (d->flickTargetY - d->_moveY.value() < height()/2 && minYExtent() != d->flickTargetY)
+ d->flickY(-d->verticalVelocity.value());
+ } else if (d->velocityY < 0) {
+ if (d->_moveY.value() - d->flickTargetY < height()/2 && maxYExtent() != d->flickTargetY)
+ d->flickY(-d->verticalVelocity.value());
+ }
+
+ if (d->velocityX > 0) {
+ if (d->flickTargetX - d->_moveX.value() < height()/2 && minXExtent() != d->flickTargetX)
+ d->flickX(-d->verticalVelocity.value());
+ } else if (d->velocityX < 0) {
+ if (d->_moveX.value() - d->flickTargetX < height()/2 && maxXExtent() != d->flickTargetX)
+ d->flickX(-d->verticalVelocity.value());
+ }
+ }
+ }
}
qreal QmlGraphicsListView::minYExtent() const
diff --git a/src/declarative/graphicsitems/qmlgraphicslistview_p.h b/src/declarative/graphicsitems/qmlgraphicslistview_p.h
index 795c766..cc74056 100644
--- a/src/declarative/graphicsitems/qmlgraphicslistview_p.h
+++ b/src/declarative/graphicsitems/qmlgraphicslistview_p.h
@@ -82,8 +82,11 @@ class Q_DECLARATIVE_EXPORT QmlGraphicsListView : public QmlGraphicsFlickable
Q_PROPERTY(QString sectionExpression READ sectionExpression WRITE setSectionExpression NOTIFY sectionExpressionChanged)
Q_PROPERTY(QString currentSection READ currentSection NOTIFY currentSectionChanged)
+ Q_PROPERTY(SnapMode snapMode READ snapMode WRITE setSnapMode)
+
Q_ENUMS(HighlightRangeMode)
Q_ENUMS(Orientation)
+ Q_ENUMS(SnapMode)
Q_CLASSINFO("DefaultProperty", "data")
public:
@@ -142,6 +145,10 @@ public:
qreal highlightResizeSpeed() const;
void setHighlightResizeSpeed(qreal);
+ enum SnapMode { NoSnap, SnapToItem, SnapOneItem };
+ SnapMode snapMode() const;
+ void setSnapMode(SnapMode mode);
+
static QmlGraphicsListViewAttached *qmlAttachedProperties(QObject *);
public Q_SLOTS:
diff --git a/tests/auto/declarative/qmlgraphicsgridview/tst_qmlgraphicsgridview.cpp b/tests/auto/declarative/qmlgraphicsgridview/tst_qmlgraphicsgridview.cpp
index f31ea49..96a164b 100644
--- a/tests/auto/declarative/qmlgraphicsgridview/tst_qmlgraphicsgridview.cpp
+++ b/tests/auto/declarative/qmlgraphicsgridview/tst_qmlgraphicsgridview.cpp
@@ -65,6 +65,7 @@ private slots:
void currentIndex();
void defaultValues();
void properties();
+ void positionViewAtIndex();
private:
QmlView *createView(const QString &filename);
@@ -809,6 +810,96 @@ void tst_QmlGraphicsGridView::properties()
delete obj;
}
+void tst_QmlGraphicsGridView::positionViewAtIndex()
+{
+ QmlView *canvas = createView(SRCDIR "/data/gridview.qml");
+
+ TestModel model;
+ for (int i = 0; i < 40; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QmlContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("testTopToBottom", QVariant(false));
+
+ canvas->execute();
+ qApp->processEvents();
+
+ QmlGraphicsGridView *gridview = findItem<QmlGraphicsGridView>(canvas->root(), "grid");
+ QVERIFY(gridview != 0);
+
+ QmlGraphicsItem *viewport = gridview->viewport();
+ QVERIFY(viewport != 0);
+
+ // Confirm items positioned correctly
+ int itemCount = findItems<QmlGraphicsItem>(viewport, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
+ QmlGraphicsItem *item = findItem<QmlGraphicsItem>(viewport, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QVERIFY(item);
+ QCOMPARE(item->x(), (i%3)*80.);
+ QCOMPARE(item->y(), (i/3)*60.);
+ }
+
+ // Position on a currently visible item
+ gridview->positionViewAtIndex(4);
+ QCOMPARE(gridview->viewportY(), 60.);
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QmlGraphicsItem>(viewport, "wrapper").count();
+ for (int i = 3; i < model.count() && i < itemCount-3-1; ++i) {
+ QmlGraphicsItem *item = findItem<QmlGraphicsItem>(viewport, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QVERIFY(item);
+ QCOMPARE(item->x(), (i%3)*80.);
+ QCOMPARE(item->y(), (i/3)*60.);
+ }
+
+ // Position on an item beyond the visible items
+ gridview->positionViewAtIndex(21);
+ QCOMPARE(gridview->viewportY(), 420.);
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QmlGraphicsItem>(viewport, "wrapper").count();
+ for (int i = 22; i < model.count() && i < itemCount-22-1; ++i) {
+ QmlGraphicsItem *item = findItem<QmlGraphicsItem>(viewport, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QVERIFY(item);
+ QCOMPARE(item->x(), (i%3)*80.);
+ QCOMPARE(item->y(), (i/3)*60.);
+ }
+
+ // Position on an item that would leave empty space if positioned at the top
+ gridview->positionViewAtIndex(31);
+ QCOMPARE(gridview->viewportY(), 520.);
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QmlGraphicsItem>(viewport, "wrapper").count();
+ for (int i = 24; i < model.count() && i < itemCount-24-1; ++i) {
+ QmlGraphicsItem *item = findItem<QmlGraphicsItem>(viewport, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QVERIFY(item);
+ QCOMPARE(item->x(), (i%3)*80.);
+ QCOMPARE(item->y(), (i/3)*60.);
+ }
+
+ // Position at the beginning again
+ gridview->positionViewAtIndex(0);
+ QCOMPARE(gridview->viewportY(), 0.);
+
+ // Confirm items positioned correctly
+ itemCount = findItems<QmlGraphicsItem>(viewport, "wrapper").count();
+ for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
+ QmlGraphicsItem *item = findItem<QmlGraphicsItem>(viewport, "wrapper", i);
+ if (!item) qWarning() << "Item" << i << "not found";
+ QVERIFY(item);
+ QCOMPARE(item->x(), (i%3)*80.);
+ QCOMPARE(item->y(), (i/3)*60.);
+ }
+
+ delete canvas;
+}
+
QmlView *tst_QmlGraphicsGridView::createView(const QString &filename)
{
QmlView *canvas = new QmlView(0);