summaryrefslogtreecommitdiffstats
path: root/src/declarative
diff options
context:
space:
mode:
authorMartin Jones <martin.jones@nokia.com>2011-04-06 04:51:52 (GMT)
committerMartin Jones <martin.jones@nokia.com>2011-04-06 05:04:11 (GMT)
commit160f1867868cdea916923652b00484ed11f90aaa (patch)
tree46116214939279cea86933fef15bc9cbffabb94c /src/declarative
parentfd1e840eba4567e67b55a00f7f761fc9815eaa47 (diff)
downloadQt-160f1867868cdea916923652b00484ed11f90aaa.zip
Qt-160f1867868cdea916923652b00484ed11f90aaa.tar.gz
Qt-160f1867868cdea916923652b00484ed11f90aaa.tar.bz2
Improve Flickable dynamics and allow platform specific tweaking.
Flickable's previous overshoot behavior was to continue past the boundary without any additional friction (deceleration). It now decelerates faster when overshooting. On touch screens in particular, the last points when a touch point is released are unreliable, resulting in sporadic flick velocities. It also now allows the number of samples used for velocity calclations to be tuned. It is now easy to tune the Flickable behavior for different platforms. Change-Id: I24142a50be1fde2f8877e359e30b8efcdd1f7d5c Task-number: QTBUG-10894, QTBUG-16388, QTBUG-17830 Reviewed-by: Michael Brasser
Diffstat (limited to 'src/declarative')
-rw-r--r--src/declarative/graphicsitems/qdeclarativeflickable.cpp235
-rw-r--r--src/declarative/graphicsitems/qdeclarativeflickable_p_p.h16
-rw-r--r--src/declarative/graphicsitems/qdeclarativegridview.cpp3
-rw-r--r--src/declarative/graphicsitems/qdeclarativelistview.cpp13
4 files changed, 186 insertions, 81 deletions
diff --git a/src/declarative/graphicsitems/qdeclarativeflickable.cpp b/src/declarative/graphicsitems/qdeclarativeflickable.cpp
index 3cc280c..affb456 100644
--- a/src/declarative/graphicsitems/qdeclarativeflickable.cpp
+++ b/src/declarative/graphicsitems/qdeclarativeflickable.cpp
@@ -45,9 +45,40 @@
#include <QGraphicsSceneMouseEvent>
#include <QPointer>
#include <QTimer>
+#include "qplatformdefs.h"
QT_BEGIN_NAMESPACE
+// The maximum number of pixels a flick can overshoot
+#ifndef QML_FLICK_OVERSHOOT
+#define QML_FLICK_OVERSHOOT 200
+#endif
+
+// The number of samples to use in calculating the velocity of a flick
+#ifndef QML_FLICK_SAMPLEBUFFER
+#define QML_FLICK_SAMPLEBUFFER 3
+#endif
+
+// The number of samples to discard when calculating the flick velocity.
+// Touch panels often produce inaccurate results as the finger is lifted.
+#ifndef QML_FLICK_DISCARDSAMPLES
+#define QML_FLICK_DISCARDSAMPLES 1
+#endif
+
+// The default maximum velocity of a flick.
+#ifndef QML_FLICK_DEFAULTMAXVELOCITY
+#define QML_FLICK_DEFAULTMAXVELOCITY 2500
+#endif
+
+// The default deceleration of a flick.
+#ifndef QML_FLICK_DEFAULTDECELERATION
+#define QML_FLICK_DEFAULTDECELERATION 1750
+#endif
+
+// How much faster to decelerate when overshooting
+#ifndef QML_FLICK_OVERSHOOTFRICTION
+#define QML_FLICK_OVERSHOOTFRICTION 8
+#endif
// FlickThreshold determines how far the "mouse" must have moved
// before we perform a flick.
@@ -141,8 +172,9 @@ QDeclarativeFlickablePrivate::QDeclarativeFlickablePrivate()
, hMoved(false), vMoved(false)
, movingHorizontally(false), movingVertically(false)
, stealMouse(false), pressed(false), interactive(true), calcVelocity(false)
- , deceleration(500), maxVelocity(2000), reportedVelocitySmoothing(100)
- , delayedPressEvent(0), delayedPressTarget(0), pressDelay(0), fixupDuration(600)
+ , deceleration(QML_FLICK_DEFAULTDECELERATION)
+ , maxVelocity(QML_FLICK_DEFAULTMAXVELOCITY), reportedVelocitySmoothing(100)
+ , delayedPressEvent(0), delayedPressTarget(0), pressDelay(0), fixupDuration(400)
, fixupMode(Normal), vTime(0), visibleArea(0)
, flickableDirection(QDeclarativeFlickable::AutoFlickDirection)
, boundsBehavior(QDeclarativeFlickable::DragAndOvershootBounds)
@@ -176,19 +208,39 @@ void QDeclarativeFlickablePrivate::init()
}
/*
- Returns the amount to overshoot by given a velocity.
- Will be roughly in range 0 - size/4
+ Returns the amount to overshoot by given a view size.
+ Will be up to the lesser of 1/3 of the view size or QML_FLICK_OVERSHOOT
*/
-qreal QDeclarativeFlickablePrivate::overShootDistance(qreal velocity, qreal size)
+qreal QDeclarativeFlickablePrivate::overShootDistance(qreal size)
{
if (maxVelocity <= 0)
return 0.0;
- velocity = qAbs(velocity);
- if (velocity > maxVelocity)
- velocity = maxVelocity;
- qreal dist = size / 4 * velocity / maxVelocity;
- return dist;
+ return qMin(qreal(QML_FLICK_OVERSHOOT), size/3);
+}
+
+void QDeclarativeFlickablePrivate::AxisData::addVelocitySample(qreal v, qreal maxVelocity)
+{
+ if (v > maxVelocity)
+ v = maxVelocity;
+ else if (v < -maxVelocity)
+ v = -maxVelocity;
+ velocityBuffer.append(v);
+ if (velocityBuffer.count() > QML_FLICK_SAMPLEBUFFER)
+ velocityBuffer.remove(0);
+}
+
+void QDeclarativeFlickablePrivate::AxisData::updateVelocity()
+{
+ if (velocityBuffer.count() > QML_FLICK_DISCARDSAMPLES) {
+ velocity = 0;
+ int count = velocityBuffer.count()-QML_FLICK_DISCARDSAMPLES;
+ for (int i = 0; i < count; ++i) {
+ qreal v = velocityBuffer.at(i);
+ velocity += v;
+ }
+ velocity /= count;
+ }
}
void QDeclarativeFlickablePrivate::itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeom, const QRectF &oldGeom)
@@ -220,15 +272,12 @@ void QDeclarativeFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal
Q_Q(QDeclarativeFlickable);
qreal maxDistance = -1;
data.fixingUp = false;
- bool overShoot = boundsBehavior == QDeclarativeFlickable::DragAndOvershootBounds;
// -ve velocity means list is moving up
if (velocity > 0) {
- if (data.move.value() < minExtent)
- maxDistance = qAbs(minExtent - data.move.value() + (overShoot?overShootDistance(velocity,vSize):0));
+ maxDistance = qAbs(minExtent - data.move.value());
data.flickTarget = minExtent;
} else {
- if (data.move.value() > maxExtent)
- maxDistance = qAbs(maxExtent - data.move.value()) + (overShoot?overShootDistance(velocity,vSize):0);
+ maxDistance = qAbs(maxExtent - data.move.value());
data.flickTarget = maxExtent;
}
if (maxDistance > 0) {
@@ -240,7 +289,10 @@ void QDeclarativeFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal
v = maxVelocity;
}
timeline.reset(data.move);
- timeline.accel(data.move, v, deceleration, maxDistance);
+ if (boundsBehavior == QDeclarativeFlickable::DragAndOvershootBounds)
+ timeline.accel(data.move, v, deceleration);
+ else
+ timeline.accel(data.move, v, deceleration, maxDistance);
timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
if (!flickingHorizontally && q->xflick()) {
flickingHorizontally = true;
@@ -327,6 +379,7 @@ void QDeclarativeFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal
}
}
}
+ data.inOvershoot = false;
fixupMode = Normal;
vTime = timeline.time();
}
@@ -706,16 +759,13 @@ void QDeclarativeFlickablePrivate::handleMousePressEvent(QGraphicsSceneMouseEven
q->setKeepMouseGrab(stealMouse);
pressed = true;
timeline.clear();
- hData.velocity = 0;
- vData.velocity = 0;
- hData.dragStartOffset = 0;
- vData.dragStartOffset = 0;
+ hData.reset();
+ vData.reset();
hData.dragMinBound = q->minXExtent();
vData.dragMinBound = q->minYExtent();
hData.dragMaxBound = q->maxXExtent();
vData.dragMaxBound = q->maxYExtent();
- hData.fixingUp = false;
- vData.fixingUp = false;
+ fixupMode = Normal;
lastPos = QPoint();
QDeclarativeItemPrivate::start(lastPosTime);
pressPos = event->pos();
@@ -807,33 +857,33 @@ void QDeclarativeFlickablePrivate::handleMouseMoveEvent(QGraphicsSceneMouseEvent
if (stealMouse)
q->setKeepMouseGrab(true);
- if (!lastPos.isNull()) {
- qreal elapsed = qreal(QDeclarativeItemPrivate::restart(lastPosTime)) / 1000.;
- if (elapsed <= 0)
- elapsed = 1;
- if (q->yflick()) {
- qreal diff = event->pos().y() - lastPos.y();
- // average to reduce the effect of spurious moves
- vData.velocity += diff / elapsed;
- vData.velocity /= 2;
- }
-
- if (q->xflick()) {
- qreal diff = event->pos().x() - lastPos.x();
- // average to reduce the effect of spurious moves
- hData.velocity += diff / elapsed;
- hData.velocity /= 2;
- }
+ if (rejectY) {
+ vData.velocityBuffer.clear();
+ vData.velocity = 0;
+ }
+ if (rejectX) {
+ hData.velocityBuffer.clear();
+ hData.velocity = 0;
}
-
- if (rejectY) vData.velocity = 0;
- if (rejectX) hData.velocity = 0;
if (hMoved || vMoved) {
q->movementStarting();
q->viewportMoved();
}
+ if (!lastPos.isNull()) {
+ qreal elapsed = qreal(QDeclarativeItemPrivate::elapsed(lastPosTime)) / 1000.;
+ if (elapsed <= 0)
+ return;
+ QDeclarativeItemPrivate::restart(lastPosTime);
+ qreal dy = event->pos().y()-lastPos.y();
+ if (q->yflick() && !rejectY)
+ vData.addVelocitySample(dy/elapsed, maxVelocity);
+ qreal dx = event->pos().x()-lastPos.x();
+ if (q->xflick() && !rejectX)
+ hData.addVelocitySample(dx/elapsed, maxVelocity);
+ }
+
lastPos = event->pos();
}
@@ -846,25 +896,33 @@ void QDeclarativeFlickablePrivate::handleMouseReleaseEvent(QGraphicsSceneMouseEv
if (!lastPosTime.isValid())
return;
- if (QDeclarativeItemPrivate::elapsed(lastPosTime) > 100) {
- // if we drag then pause before release we should not cause a flick.
+ // if we drag then pause before release we should not cause a flick.
+ if (QDeclarativeItemPrivate::elapsed(lastPosTime) < 100) {
+ vData.updateVelocity();
+ hData.updateVelocity();
+ } else {
hData.velocity = 0.0;
vData.velocity = 0.0;
}
vTime = timeline.time();
- if (qAbs(vData.velocity) > MinimumFlickVelocity && qAbs(event->pos().y() - pressPos.y()) > FlickThreshold)
- flickY(vData.velocity);
+
+ qreal velocity = vData.velocity;
+ if (vData.atBeginning || vData.atEnd)
+ velocity /= 2;
+ if (qAbs(velocity) > MinimumFlickVelocity && qAbs(event->pos().y() - pressPos.y()) > FlickThreshold)
+ flickY(velocity);
else
fixupY();
- if (qAbs(hData.velocity) > MinimumFlickVelocity && qAbs(event->pos().x() - pressPos.x()) > FlickThreshold)
- flickX(hData.velocity);
+ velocity = hData.velocity;
+ if (hData.atBeginning || hData.atEnd)
+ velocity /= 2;
+ if (qAbs(velocity) > MinimumFlickVelocity && qAbs(event->pos().x() - pressPos.x()) > FlickThreshold)
+ flickX(velocity);
else
fixupX();
- lastPosTime.invalidate();
-
if (!timeline.isActive())
q->movementEnding();
}
@@ -911,29 +969,41 @@ void QDeclarativeFlickable::wheelEvent(QGraphicsSceneWheelEvent *event)
if (!d->interactive) {
QDeclarativeItem::wheelEvent(event);
} else if (yflick() && event->orientation() == Qt::Vertical) {
- if (event->delta() > 0)
- d->vData.velocity = qMax(event->delta() - d->vData.smoothVelocity.value(), qreal(250.0));
- else
- d->vData.velocity = qMin(event->delta() - d->vData.smoothVelocity.value(), qreal(-250.0));
- d->flickingVertically = false;
- d->flickY(d->vData.velocity);
- if (d->flickingVertically) {
- d->vMoved = true;
- movementStarting();
+ bool valid = false;
+ if (event->delta() > 0 && contentY() > -minYExtent()) {
+ d->vData.velocity = qMax(event->delta()*2 - d->vData.smoothVelocity.value(), qreal(d->maxVelocity/4));
+ valid = true;
+ } else if (event->delta() < 0 && contentY() < -maxYExtent()) {
+ d->vData.velocity = qMin(event->delta()*2 - d->vData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
+ valid = true;
+ }
+ if (valid) {
+ d->flickingVertically = false;
+ d->flickY(d->vData.velocity);
+ if (d->flickingVertically) {
+ d->vMoved = true;
+ movementStarting();
+ }
+ event->accept();
}
- event->accept();
} else if (xflick() && event->orientation() == Qt::Horizontal) {
- if (event->delta() > 0)
- d->hData.velocity = qMax(event->delta() - d->hData.smoothVelocity.value(), qreal(250.0));
- else
- d->hData.velocity = qMin(event->delta() - d->hData.smoothVelocity.value(), qreal(-250.0));
- d->flickingHorizontally = false;
- d->flickX(d->hData.velocity);
- if (d->flickingHorizontally) {
- d->hMoved = true;
- movementStarting();
+ bool valid = false;
+ if (event->delta() > 0 && contentX() > -minXExtent()) {
+ d->hData.velocity = qMax(event->delta()*2 - d->hData.smoothVelocity.value(), qreal(d->maxVelocity/4));
+ valid = true;
+ } else if (event->delta() < 0 && contentX() < -maxXExtent()) {
+ d->hData.velocity = qMin(event->delta()*2 - d->hData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
+ valid = true;
+ }
+ if (valid) {
+ d->flickingHorizontally = false;
+ d->flickX(d->hData.velocity);
+ if (d->flickingHorizontally) {
+ d->hMoved = true;
+ movementStarting();
+ }
+ event->accept();
}
- event->accept();
} else {
QDeclarativeItem::wheelEvent(event);
}
@@ -1071,6 +1141,27 @@ void QDeclarativeFlickable::viewportMoved()
}
}
+ if (!d->vData.inOvershoot && !d->vData.fixingUp && d->flickingVertically
+ && (d->vData.move.value() > minYExtent() || d->vData.move.value() < maxYExtent())
+ && qAbs(d->vData.smoothVelocity.value()) > 100) {
+ // Increase deceleration if we've passed a bound
+ d->vData.inOvershoot = true;
+ qreal maxDistance = d->overShootDistance(height());
+ d->timeline.reset(d->vData.move);
+ d->timeline.accel(d->vData.move, -d->vData.smoothVelocity.value(), d->deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance);
+ d->timeline.callback(QDeclarativeTimeLineCallback(&d->vData.move, d->fixupY_callback, d));
+ }
+ if (!d->hData.inOvershoot && !d->hData.fixingUp && d->flickingHorizontally
+ && (d->hData.move.value() > minXExtent() || d->hData.move.value() < maxXExtent())
+ && qAbs(d->hData.smoothVelocity.value()) > 100) {
+ // Increase deceleration if we've passed a bound
+ d->hData.inOvershoot = true;
+ qreal maxDistance = d->overShootDistance(width());
+ d->timeline.reset(d->hData.move);
+ d->timeline.accel(d->hData.move, -d->hData.smoothVelocity.value(), d->deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance);
+ d->timeline.callback(QDeclarativeTimeLineCallback(&d->hData.move, d->fixupX_callback, d));
+ }
+
d->lastFlickablePosition = QPointF(d->hData.move.value(), d->vData.move.value());
d->vTime = d->timeline.time();
@@ -1524,7 +1615,7 @@ bool QDeclarativeFlickable::sceneEventFilter(QGraphicsItem *i, QEvent *e)
\qmlproperty real Flickable::maximumFlickVelocity
This property holds the maximum velocity that the user can flick the view in pixels/second.
- The default is 2000 pixels/s
+ The default value is platform dependent.
*/
qreal QDeclarativeFlickable::maximumFlickVelocity() const
{
@@ -1545,7 +1636,7 @@ void QDeclarativeFlickable::setMaximumFlickVelocity(qreal v)
\qmlproperty real Flickable::flickDeceleration
This property holds the rate at which a flick will decelerate.
- The default is 500.
+ The default value is platform dependent.
*/
qreal QDeclarativeFlickable::flickDeceleration() const
{
diff --git a/src/declarative/graphicsitems/qdeclarativeflickable_p_p.h b/src/declarative/graphicsitems/qdeclarativeflickable_p_p.h
index 38a5eb3..1117abb 100644
--- a/src/declarative/graphicsitems/qdeclarativeflickable_p_p.h
+++ b/src/declarative/graphicsitems/qdeclarativeflickable_p_p.h
@@ -94,9 +94,19 @@ public:
struct AxisData {
AxisData(QDeclarativeFlickablePrivate *fp, void (QDeclarativeFlickablePrivate::*func)(qreal))
: move(fp, func), viewSize(-1), smoothVelocity(fp), atEnd(false), atBeginning(true)
- , fixingUp(false)
+ , fixingUp(false), inOvershoot(false)
{}
+ void reset() {
+ velocityBuffer.clear();
+ dragStartOffset = 0;
+ fixingUp = false;
+ inOvershoot = false;
+ }
+
+ void addVelocitySample(qreal v, qreal maxVelocity);
+ void updateVelocity();
+
QDeclarativeTimeLineValueProxy<QDeclarativeFlickablePrivate> move;
qreal viewSize;
qreal pressPos;
@@ -106,9 +116,11 @@ public:
qreal velocity;
qreal flickTarget;
QDeclarativeFlickablePrivate::Velocity smoothVelocity;
+ QPODVector<qreal,10> velocityBuffer;
bool atEnd : 1;
bool atBeginning : 1;
bool fixingUp : 1;
+ bool inOvershoot : 1;
};
void flickX(qreal velocity);
@@ -129,7 +141,7 @@ public:
void setRoundedViewportX(qreal x);
void setRoundedViewportY(qreal y);
- qreal overShootDistance(qreal velocity, qreal size);
+ qreal overShootDistance(qreal size);
void itemGeometryChanged(QDeclarativeItem *, const QRectF &, const QRectF &);
diff --git a/src/declarative/graphicsitems/qdeclarativegridview.cpp b/src/declarative/graphicsitems/qdeclarativegridview.cpp
index b409f87..3ec29c1 100644
--- a/src/declarative/graphicsitems/qdeclarativegridview.cpp
+++ b/src/declarative/graphicsitems/qdeclarativegridview.cpp
@@ -1115,6 +1115,7 @@ void QDeclarativeGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal m
} else {
QDeclarativeFlickablePrivate::fixup(data, minExtent, maxExtent);
}
+ data.inOvershoot = false;
fixupMode = Normal;
}
@@ -1196,7 +1197,7 @@ void QDeclarativeGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal m
accel = v2 / (2.0f * qAbs(dist));
} else {
data.flickTarget = velocity > 0 ? minExtent : maxExtent;
- overshootDist = overShoot ? overShootDistance(v, vSize) : 0;
+ overshootDist = overShoot ? overShootDistance(vSize) : 0;
}
timeline.reset(data.move);
timeline.accel(data.move, v, accel, maxDistance + overshootDist);
diff --git a/src/declarative/graphicsitems/qdeclarativelistview.cpp b/src/declarative/graphicsitems/qdeclarativelistview.cpp
index 6ae1ddc..f176916 100644
--- a/src/declarative/graphicsitems/qdeclarativelistview.cpp
+++ b/src/declarative/graphicsitems/qdeclarativelistview.cpp
@@ -1354,6 +1354,7 @@ void QDeclarativeListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal m
} else {
QDeclarativeFlickablePrivate::fixup(data, minExtent, maxExtent);
}
+ data.inOvershoot = false;
fixupMode = Normal;
}
@@ -1428,10 +1429,10 @@ void QDeclarativeListViewPrivate::flick(AxisData &data, qreal minExtent, qreal m
data.flickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
if (overShoot) {
if (data.flickTarget >= minExtent) {
- overshootDist = overShootDistance(v, vSize);
+ overshootDist = overShootDistance(vSize);
data.flickTarget += overshootDist;
} else if (data.flickTarget <= maxExtent) {
- overshootDist = overShootDistance(v, vSize);
+ overshootDist = overShootDistance(vSize);
data.flickTarget -= overshootDist;
}
}
@@ -1451,10 +1452,10 @@ void QDeclarativeListViewPrivate::flick(AxisData &data, qreal minExtent, qreal m
} else if (overShoot) {
data.flickTarget = data.move.value() - dist;
if (data.flickTarget >= minExtent) {
- overshootDist = overShootDistance(v, vSize);
+ overshootDist = overShootDistance(vSize);
data.flickTarget += overshootDist;
} else if (data.flickTarget <= maxExtent) {
- overshootDist = overShootDistance(v, vSize);
+ overshootDist = overShootDistance(vSize);
data.flickTarget -= overshootDist;
}
}
@@ -2581,7 +2582,7 @@ void QDeclarativeListView::viewportMoved()
d->inFlickCorrection = true;
// 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 (yflick()) {
+ if (yflick() && !d->vData.inOvershoot) {
if (d->vData.velocity > 0) {
const qreal minY = minYExtent();
if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
@@ -2597,7 +2598,7 @@ void QDeclarativeListView::viewportMoved()
}
}
- if (xflick()) {
+ if (xflick() && !d->hData.inOvershoot) {
if (d->hData.velocity > 0) {
const qreal minX = minXExtent();
if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)