summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAaron Kennedy <aaron.kennedy@nokia.com>2009-11-30 06:26:12 (GMT)
committerAaron Kennedy <aaron.kennedy@nokia.com>2009-11-30 06:26:12 (GMT)
commitc8cb256644c143f9d85eed0a8d295ffd05a2017d (patch)
tree4dc1b8794b7de842a91372576a81b76f07f67156
parent1c8cf1b080c81c40bedc9d7c342555585fc394b0 (diff)
parentc4cd2138ea46f9e7990bd732b63d7322f21a2f38 (diff)
downloadQt-c8cb256644c143f9d85eed0a8d295ffd05a2017d.zip
Qt-c8cb256644c143f9d85eed0a8d295ffd05a2017d.tar.gz
Qt-c8cb256644c143f9d85eed0a8d295ffd05a2017d.tar.bz2
Merge branch 'kinetic-declarativeui' of scm.dev.nokia.troll.no:qt/kinetic into kinetic-declarativeui
-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--src/declarative/util/qmlbehavior.cpp34
-rw-r--r--src/declarative/util/qmlbehavior_p.h7
-rw-r--r--tests/auto/declarative/behaviors/data/disabled.qml27
-rw-r--r--tests/auto/declarative/behaviors/tst_behaviors.cpp15
-rw-r--r--tests/auto/declarative/qmlgraphicsgridview/tst_qmlgraphicsgridview.cpp91
12 files changed, 505 insertions, 105 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/src/declarative/util/qmlbehavior.cpp b/src/declarative/util/qmlbehavior.cpp
index 711c70d..23d3838 100644
--- a/src/declarative/util/qmlbehavior.cpp
+++ b/src/declarative/util/qmlbehavior.cpp
@@ -55,18 +55,21 @@ class QmlBehaviorPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QmlBehavior)
public:
- QmlBehaviorPrivate() : animation(0) {}
+ QmlBehaviorPrivate() : animation(0), enabled(true) {}
QmlMetaProperty property;
QVariant currentValue;
QmlAbstractAnimation *animation;
+ bool enabled;
};
/*!
\qmlclass Behavior QmlBehavior
\brief The Behavior element allows you to specify a default animation for a property change.
- In example below, the rect will use a bounce easing curve over 200 millisecond for any changes to its y property:
+ Behaviors provide one way to specify \l{qmlanimation.html}{animations} in QML.
+
+ In the example below, the rect will use a bounce easing curve over 200 millisecond for any changes to its y property:
\code
Rectangle {
width: 20; height: 20
@@ -80,6 +83,9 @@ public:
}
}
\endcode
+
+ Currently only a single Behavior may be specified for a property;
+ this Behavior can be enabled and disabled via the \l{enabled} property.
*/
@@ -118,10 +124,32 @@ void QmlBehavior::setAnimation(QmlAbstractAnimation *animation)
d->animation->setTarget(d->property);
}
+/*!
+ \qmlproperty bool Behavior::enabled
+ Whether the Behavior will be triggered when the property it is tracking changes.
+
+ By default a Behavior is enabled.
+*/
+
+bool QmlBehavior::enabled() const
+{
+ Q_D(const QmlBehavior);
+ return d->enabled;
+}
+
+void QmlBehavior::setEnabled(bool enabled)
+{
+ Q_D(QmlBehavior);
+ if (d->enabled == enabled)
+ return;
+ d->enabled = enabled;
+ emit enabledChanged();
+}
+
void QmlBehavior::write(const QVariant &value)
{
Q_D(QmlBehavior);
- if (!d->animation) {
+ if (!d->animation || !d->enabled) {
d->property.write(value, QmlMetaProperty::BypassInterceptor | QmlMetaProperty::DontRemoveBinding);
return;
}
diff --git a/src/declarative/util/qmlbehavior_p.h b/src/declarative/util/qmlbehavior_p.h
index b61df32..581a0a8 100644
--- a/src/declarative/util/qmlbehavior_p.h
+++ b/src/declarative/util/qmlbehavior_p.h
@@ -63,6 +63,7 @@ class Q_DECLARATIVE_EXPORT QmlBehavior : public QObject, public QmlPropertyValue
Q_INTERFACES(QmlPropertyValueInterceptor)
Q_CLASSINFO("DefaultProperty", "animation")
Q_PROPERTY(QmlAbstractAnimation *animation READ animation WRITE setAnimation)
+ Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
public:
QmlBehavior(QObject *parent=0);
@@ -73,6 +74,12 @@ public:
QmlAbstractAnimation *animation();
void setAnimation(QmlAbstractAnimation *);
+
+ bool enabled() const;
+ void setEnabled(bool enabled);
+
+Q_SIGNALS:
+ void enabledChanged();
};
QT_END_NAMESPACE
diff --git a/tests/auto/declarative/behaviors/data/disabled.qml b/tests/auto/declarative/behaviors/data/disabled.qml
new file mode 100644
index 0000000..e7b8d51
--- /dev/null
+++ b/tests/auto/declarative/behaviors/data/disabled.qml
@@ -0,0 +1,27 @@
+import Qt 4.6
+Rectangle {
+ width: 400
+ height: 400
+ Rectangle {
+ id: rect
+ objectName: "MyRect"
+ width: 100; height: 100; color: "green"
+ x: Behavior {
+ objectName: "MyBehavior";
+ enabled: false
+ NumberAnimation { duration: 200; }
+ }
+ }
+ MouseRegion {
+ id: clicker
+ anchors.fill: parent
+ }
+ states: State {
+ name: "moved"
+ when: clicker.pressed
+ PropertyChanges {
+ target: rect
+ x: 200
+ }
+ }
+}
diff --git a/tests/auto/declarative/behaviors/tst_behaviors.cpp b/tests/auto/declarative/behaviors/tst_behaviors.cpp
index 6343968..e1376ce 100644
--- a/tests/auto/declarative/behaviors/tst_behaviors.cpp
+++ b/tests/auto/declarative/behaviors/tst_behaviors.cpp
@@ -64,6 +64,7 @@ private slots:
void emptyBehavior();
void nonSelectingBehavior();
void reassignedAnimation();
+ void disabled();
};
void tst_behaviors::simpleBehavior()
@@ -243,6 +244,20 @@ void tst_behaviors::reassignedAnimation()
rect->findChild<QmlBehavior*>("MyBehavior"))->animation())->duration(), 200);
}
+void tst_behaviors::disabled()
+{
+ QmlEngine engine;
+ QmlComponent c(&engine, QUrl("file://" SRCDIR "/data/disabled.qml"));
+ QmlGraphicsRectangle *rect = qobject_cast<QmlGraphicsRectangle*>(c.create());
+ QVERIFY(rect);
+ QCOMPARE(rect->findChild<QmlBehavior*>("MyBehavior")->enabled(), false);
+
+ rect->setState("moved");
+ qreal x = qobject_cast<QmlGraphicsRectangle*>(rect->findChild<QmlGraphicsRectangle*>("MyRect"))->x();
+ QCOMPARE(x, qreal(200)); //should change immediately
+
+}
+
QTEST_MAIN(tst_behaviors)
#include "tst_behaviors.moc"
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);