summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRalf Engels <ralf.engels@nokia.com>2010-04-12 19:59:22 +0200
committerRalf Engels <ralf.engels@nokia.com>2010-04-12 19:59:22 +0200
commit959bc6e1177c987e7dff1d811dae26d18812eb37 (patch)
treecf841b779027d4966ee2d8fcdb484847d91d637f
parent88e018356973866017db25cbc16700fd3da59a99 (diff)
Big rewrite
-rw-r--r--main.cpp2
-rw-r--r--qkineticscroller.cpp390
-rw-r--r--qkineticscroller_p.h24
3 files changed, 234 insertions, 182 deletions
diff --git a/main.cpp b/main.cpp
index 128d69b..a1b1155 100644
--- a/main.cpp
+++ b/main.cpp
@@ -14,7 +14,7 @@ int main(int argc, char **argv)
s->setWidget(lw);
lw->show();
- s->scrollTo(QPointF(80,800));
+ s->scrollTo(QPointF(80,300));
return a.exec();
}
diff --git a/qkineticscroller.cpp b/qkineticscroller.cpp
index 2197ec8..7a378d1 100644
--- a/qkineticscroller.cpp
+++ b/qkineticscroller.cpp
@@ -57,6 +57,36 @@ QT_BEGIN_NAMESPACE
# define qKSDebug while (false) qDebug
#endif
+
+inline bool operator<=(const QPointF &p, qreal f)
+{
+ return (qAbs(p.x()) <= f) && (qAbs(p.y()) <= f);
+}
+inline bool operator<(const QPointF &p, qreal f)
+{
+ return (qAbs(p.x()) < f) && (qAbs(p.y()) < f);
+}
+
+inline bool operator>=(const QPointF &p, qreal f)
+{
+ return (qAbs(p.x()) >= f) || (qAbs(p.y()) >= f);
+}
+inline bool operator>(const QPointF &p, qreal f)
+{
+ return (qAbs(p.x()) > f) || (qAbs(p.y()) > f);
+}
+
+inline QPointF qAbs(const QPointF &p)
+{
+ return QPointF(qAbs(p.x()), qAbs(p.y()));
+}
+
+inline int qSign(qreal r)
+{
+ return (r < 0) ? -1 : ((r > 0) ? 1 : 0);
+}
+
+
/*!
\class QKineticScroller
\brief The QKineticScroller class enables kinetic scrolling for any scrolling widget or graphics item.
@@ -150,9 +180,15 @@ QKineticScroller::~QKineticScroller()
*/
QKineticScrollerPrivate::QKineticScrollerPrivate()
- : enabled(true), state(QKineticScroller::StateInactive),
- overshootPolicy(QKineticScroller::OvershootWhenScrollable),
- pressTimestamp(0), lastTimestamp(0), scrollToX(false), scrollToY(false)
+ : enabled(true)
+ , state(QKineticScroller::StateInactive)
+ , overshootPolicy(QKineticScroller::OvershootWhenScrollable)
+ , pressTimestamp(0)
+ , lastTimestamp(0)
+ , scrollToX(false)
+ , scrollToY(false)
+ , overshootX(false)
+ , overshootY(false)
{ }
QKineticScrollerPrivate::~QKineticScrollerPrivate()
@@ -254,22 +290,20 @@ void QKineticScrollerPrivate::timerEvent(QTimerEvent *e)
struct timerevent {
QKineticScroller::State state;
- QElapsedTimer *elapsedTimer;
- typedef void (QKineticScrollerPrivate::*timerhandler_t)(qint64 elapsed);
+ typedef void (QKineticScrollerPrivate::*timerhandler_t)();
timerhandler_t handler;
};
timerevent timerevents[] = {
- { QKineticScroller::StateDragging, &dragTimer, &QKineticScrollerPrivate::timerEventWhileDragging },
- { QKineticScroller::StateScrolling, &scrollTimer, &QKineticScrollerPrivate::timerEventWhileScrolling },
+ { QKineticScroller::StateDragging, &QKineticScrollerPrivate::timerEventWhileDragging },
+ { QKineticScroller::StateScrolling, &QKineticScrollerPrivate::timerEventWhileScrolling },
};
for (int i = 0; i < int(sizeof(timerevents) / sizeof(*timerevents)); ++i) {
timerevent *te = timerevents + i;
if (state == te->state) {
- qint64 elapsed = te->elapsedTimer ? te->elapsedTimer->restart() : 0;
- (this->*te->handler)(elapsed);
+ (this->*te->handler)();
return;
}
}
@@ -366,7 +400,7 @@ QVariant QKineticScroller::scrollMetric(ScrollMetric metric) const
case DragVelocitySmoothingFactor: return d->dragVelocitySmoothingFactor;
case LinearDecelerationFactor: return d->linearDecelerationFactor;
case ExponentialDecelerationBase: return d->exponentialDecelerationBase;
- case OvershootSpringConstant: return d->overshootSpringConstant;
+ case OvershootSpringConstant: return d->overshootSpringConstantRoot * d->overshootSpringConstantRoot;
case OvershootDragResistanceFactor: return d->overshootDragResistanceFactor;
case OvershootMaximumDistance: return d->overshootMaximumDistance;
case DragStartDistance: return d->dragStartDistance;
@@ -395,7 +429,7 @@ void QKineticScroller::setScrollMetric(ScrollMetric metric, const QVariant &valu
case DragVelocitySmoothingFactor: d->dragVelocitySmoothingFactor = qBound(qreal(0), value.toReal(), qreal(1)); break;
case LinearDecelerationFactor: d->linearDecelerationFactor = qBound(qreal(0), value.toReal(), qreal(1)); break;
case ExponentialDecelerationBase: d->exponentialDecelerationBase = qBound(qreal(0), value.toReal(), qreal(1)); break;
- case OvershootSpringConstant: d->overshootSpringConstant = value.toReal(); break;
+ case OvershootSpringConstant: d->overshootSpringConstantRoot = qSqrt(value.toReal()); break;
case OvershootDragResistanceFactor: d->overshootDragResistanceFactor = value.toReal(); break;
case OvershootMaximumDistance: d->overshootMaximumDistance = value.toPointF(); break;
case DragStartDistance: d->dragStartDistance = value.toReal(); break;
@@ -467,6 +501,63 @@ void QKineticScrollerPrivate::updateVelocity(const QPointF &deltaPixelRaw, qint6
qKSDebug() << "resulting new velocity:" << velocity;
}
+qreal QKineticScrollerPrivate::decelerate(qreal v, qreal t)
+{
+ qreal result = v * qPow(exponentialDecelerationBase, t);
+ qreal linear = linearDecelerationFactor * t;
+ if (qAbs(result) > linear)
+ return result + (result < 0 ? linear : -linear);
+ else
+ return 0;
+}
+
+/*! Calculates the current velocity during scrolling
+ */
+QPointF QKineticScrollerPrivate::calculateVelocity(qreal time)
+{
+ QPointF velocity;
+
+ // -- x coordinate
+ if (overshootX) {
+ velocity.setX(overshootVelocity.x() * qCos(overshootSpringConstantRoot * (time - overshootStartTimeX)));
+
+ } else {
+ qreal newVelocity = decelerate(releaseVelocity.x(), time);
+
+ if (scrollToX) {
+ if (qAbs(newVelocity) < qreal(30.0 / 1000) /* 30mm/s */)
+ newVelocity = qreal(30.0 / 1000) * qSign(releaseVelocity.x());
+
+ } else {
+ if (qAbs(newVelocity) < qreal(0.5) * qreal(framesPerSecond) / pixelPerMeter /* 0.5 [pix/frame] */)
+ newVelocity = 0;
+ }
+
+ velocity.setX(newVelocity);
+ }
+
+ // -- y coordinate
+ if (overshootY) {
+ velocity.setY(overshootVelocity.y() * qCos(overshootSpringConstantRoot * (time - overshootStartTimeY)));
+
+ } else {
+ qreal newVelocity = decelerate(releaseVelocity.y(), time);
+
+ if (scrollToY) {
+ if (qAbs(newVelocity) < qreal(30.0 / 1000) /* 30mm/s */)
+ newVelocity = qreal(30.0 / 1000) * qSign(releaseVelocity.y());
+
+ } else {
+ if (qAbs(newVelocity) < qreal(0.5) * qreal(framesPerSecond) / pixelPerMeter /* 0.5 [pix/frame] */)
+ newVelocity = 0;
+ }
+
+ velocity.setY(newVelocity);
+ }
+
+ return velocity;
+}
+
void QKineticScrollerPrivate::handleDrag(const QPointF &position, qint64 timestamp)
{
@@ -524,31 +615,6 @@ void QKineticScrollerPrivate::handleDrag(const QPointF &position, qint64 timesta
}
-inline bool operator<=(const QPointF &p, qreal f)
-{
- return (qAbs(p.x()) <= f) && (qAbs(p.y()) <= f);
-}
-inline bool operator<(const QPointF &p, qreal f)
-{
- return (qAbs(p.x()) < f) && (qAbs(p.y()) < f);
-}
-
-inline bool operator>=(const QPointF &p, qreal f)
-{
- return (qAbs(p.x()) >= f) || (qAbs(p.y()) >= f);
-}
-inline bool operator>(const QPointF &p, qreal f)
-{
- return (qAbs(p.x()) > f) || (qAbs(p.y()) > f);
-}
-
-inline QPointF qAbs(const QPointF &p)
-{
- return QPointF(qAbs(p.x()), qAbs(p.y()));
-}
-
-
-
bool QKineticScrollerPrivate::pressWhileInactive(QKineticScroller::Input, const QPointF &position, qint64 timestamp)
{
@@ -566,7 +632,7 @@ bool QKineticScrollerPrivate::pressWhileInactive(QKineticScroller::Input, const
bool QKineticScrollerPrivate::releaseWhilePressed(QKineticScroller::Input, const QPointF &, qint64)
{
- if (!overshootDistance.isNull())
+ if (overshootX || overshootY)
setState(QKineticScroller::StateScrolling);
else
setState(QKineticScroller::StateInactive);
@@ -623,12 +689,10 @@ bool QKineticScrollerPrivate::moveWhileDragging(QKineticScroller::Input, const Q
return true;
}
-void QKineticScrollerPrivate::timerEventWhileDragging(qint64)
+void QKineticScrollerPrivate::timerEventWhileDragging()
{
- Q_Q(QKineticScroller);
-
if (!dragDistance.isNull()) {
- setContentPositionHelper(q->contentPosition() + overshootDistance - dragDistance);
+ setContentPositionHelper(-dragDistance);
dragDistance = QPointF(0, 0);
}
}
@@ -675,7 +739,7 @@ bool QKineticScrollerPrivate::releaseWhileDragging(QKineticScroller::Input, cons
}
qKSDebug() << "release While dragging, velocity: "<<velocity<<"minimum velocity"<<minimumVelocity;
- if (!overshootDistance.isNull()) {
+ if (overshootX || overshootY) {
velocity = QPointF(0, 0);
setState(QKineticScroller::StateScrolling);
} else if (velocity >= minimumVelocity)
@@ -686,98 +750,20 @@ bool QKineticScrollerPrivate::releaseWhileDragging(QKineticScroller::Input, cons
return true;
}
-qreal QKineticScrollerPrivate::decelerate(qreal v, qreal expDecel, qreal linDecel)
+void QKineticScrollerPrivate::timerEventWhileScrolling()
{
- v *= expDecel;
- if (qAbs(v) >= linDecel)
- return v + (v < 0 ? linDecel : -linDecel);
- else
- return 0;;
-}
+ qreal deltaTime = qreal(scrollRelativeTimer.restart()) / 1000;
+ qreal time = qreal(scrollAbsoluteTimer.elapsed()) / 1000;
-void QKineticScrollerPrivate::timerEventWhileScrolling(qint64 time_ms)
-{
- Q_Q(QKineticScroller);
-
- qreal time = qreal(time_ms) / 1000;
-
- // --- handle overshooting and stop if the coordinate is going back inside the normal area
- if (!overshootDistance.isNull()) {
- // calculate the new overshoot distance (convert from [m/s] to [pixel/frame]
- QPointF newOvershootDistance = overshootDistance + velocity * time * pixelPerMeter;
-
- qKSDebug() << "Overshooting, old: "<<overshootDistance<<"new:"<<newOvershootDistance;
-
- // -- check if we going back. Then stop
- if (overshootDistance.x() && ((overshootDistance.x() > 0) != (newOvershootDistance.x() > 0))) {
- overshootDistance.setX(0);
- velocity.setX(0);
- }
- if (overshootDistance.y() && ((overshootDistance.y() > 0) != (newOvershootDistance.y() > 0))) {
- overshootDistance.setY(0);
- velocity.setY(0);
- }
- }
-
- qWarning() << "Pos: " << q->contentPosition() << " - Vel: " << velocity << " - Time: " << qreal(time) << " - PPM: " << pixelPerMeter;
+ QPointF newVelocity = calculateVelocity(time);
+ QPointF deltaPos = newVelocity * deltaTime * pixelPerMeter;
// -- move (convert from [m/s] to [pix/frame]
- setContentPositionHelper(q->contentPosition() + overshootDistance + velocity * time * pixelPerMeter);
-
-
- // --- (de)accelerate overshooting axis using spring constant or friction
- // actually we would need to calculate a root and not just divide
- qreal forcePerFrame = overshootSpringConstant * time;
- qreal exponentialDecelerationPerFrame = (qreal(1) - ((qreal(1) - exponentialDecelerationBase) * time));
- qreal linearDecelerationPerFrame = linearDecelerationFactor * time;
-
- // -- x coordinate
- if (overshootDistance.x()) {
- velocity.rx() -= forcePerFrame * overshootDistance.x() / pixelPerMeter;
- velocity.setX(qBound(-overshootMaximumVelocity, velocity.x(), overshootMaximumVelocity));
-
- } else if (scrollToX) {
- if (qAbs(velocity.x()) * exponentialDecelerationPerFrame > linearDecelerationPerFrame + qreal(30.0 / 1000) )/* 30 px/s */
- velocity.setX(decelerate(velocity.x(), exponentialDecelerationPerFrame, linearDecelerationPerFrame));
- else
- velocity.setX((velocity.x()<0 ? -1.0 : 1.0) * qreal(30.0 / 1000));
-
- // - target reached?
- qreal dist = scrollToPosition.x() - q->contentPosition().x();
-qDebug() << "scroll to x" << scrollToPosition.x() << "v:"<< velocity.x() << " dist " << dist;
- if ((velocity.x() > 0.0 && dist <= 0.0) || (velocity.x() < 0.0 && dist >= 0.0)) {
- velocity.setX(0.0);
- scrollToX = false;
- }
+ setContentPositionHelper(deltaPos);
- } else {
- velocity.setX(decelerate(velocity.x(), exponentialDecelerationPerFrame, linearDecelerationPerFrame));
- }
-
- // -- y coordinate
- if (overshootDistance.y()) {
- velocity.ry() -= forcePerFrame * overshootDistance.y() / pixelPerMeter;
- velocity.setY(qBound(-overshootMaximumVelocity, velocity.y(), overshootMaximumVelocity));
-
- } else if (scrollToY) {
- if (qAbs(velocity.y()) * exponentialDecelerationPerFrame > linearDecelerationPerFrame + qreal(30.0 / 1000) )/* 30 px/s */
- velocity.setY(decelerate(velocity.y(), exponentialDecelerationPerFrame, linearDecelerationPerFrame));
- else
- velocity.setY((velocity.y()<0 ? -1.0 : 1.0) * qreal(30.0 / 1000));
-
- // - target reached?
- qreal dist = scrollToPosition.y() - q->contentPosition().y();
-qDebug() << "scroll to y" << scrollToPosition.y() << "v:"<< velocity.y() << " dist " << dist;
- if ((velocity.y() > 0.0 && dist <= 0.0) || (velocity.y() < 0.0 && dist >= 0.0)) {
- velocity.setY(0.0);
- scrollToY = false;
- }
-
- } else {
- velocity.setY(decelerate(velocity.y(), exponentialDecelerationPerFrame, linearDecelerationPerFrame));
- }
+ qWarning() << "DeltaPos: " << deltaPos << " - newVel: " << newVelocity << " - Time: " << time << " - PPM: " << pixelPerMeter;
- if (overshootDistance.isNull() && (velocity < qreal(0.5) * qreal(framesPerSecond) / pixelPerMeter /* 1 [pix/frame] */) && !scrollToX && !scrollToY)
+ if (newVelocity.isNull())
setState(QKineticScroller::StateInactive);
}
@@ -800,7 +786,6 @@ void QKineticScrollerPrivate::setState(QKineticScroller::State newstate)
switch (state) {
case QKineticScroller::StateDragging:
- overshootDistance *= overshootDragResistanceFactor;
break;
default:
break;
@@ -839,16 +824,29 @@ void QKineticScrollerPrivate::setState(QKineticScroller::State newstate)
qWarning() << "State change from " << stateName(state) << " to " << stateName(newstate) << ", but timer is already active.";
}
}
- dragTimer.start();
break;
case QKineticScroller::StateScrolling:
- // if (state == QKineticScroller::StatePressed) {
- if (!timerId) {
- timerId = startTimer(1000 / framesPerSecond);
- }
- // }
- scrollTimer.start();
+ if (!timerId) {
+ timerId = startTimer(1000 / framesPerSecond);
+ }
+ scrollRelativeTimer.start();
+ scrollAbsoluteTimer.start();
+ releaseVelocity = velocity;
+
+ if (state == QKineticScroller::StateDragging) {
+ QPointF oldPos = q->contentPosition();
+ QPointF maxPos = q->maximumContentPosition();
+ QPointF oldClampedPos;
+ oldClampedPos.setX(qBound(qreal(0), oldPos.x(), maxPos.x()));
+ oldClampedPos.setY(qBound(qreal(0), oldPos.y(), maxPos.y()));
+
+ QPointF overshootDistance = oldPos - oldClampedPos;
+
+ overshootStartTimeX = overshootStartTimeY = scrollAbsoluteTimer.elapsed() - M_PI / (overshootSpringConstantRoot * 2);
+ overshootVelocity = overshootDistance * overshootSpringConstantRoot;
+ }
+
break;
}
@@ -885,6 +883,7 @@ void QKineticScroller::scrollTo(const QPointF &pos, int scrollTime)
// if the start velocity is below that then the scrolling would stop before scrollTime.
qreal vMin = d->linearDecelerationFactor * time / qPow(d->exponentialDecelerationBase, time);
+ /*
// estimate of the distance passed within the vMin time during scrollTime
qreal distMin = qreal(scrollTime) * vMin / 2.0;
@@ -893,25 +892,26 @@ void QKineticScroller::scrollTo(const QPointF &pos, int scrollTime)
QPointF v = QPointF((pos.x()-contentPosition().x()) / distMin * vMin,
(pos.y()-contentPosition().y()) / distMin * vMin);
- qKSDebug() << "QAbstractKineticScroller::scrollTo(" << pos << ", " << v;
+ */
+
+ // qKSDebug() << "QAbstractKineticScroller::scrollTo(" << pos << ", " << v;
// v(t) = vstart * exponentialDecelerationBase ^ t - linearDecelerationFactor * t
// pos(t) = integrate(v(t) * dt)
- // pos(t) = vstart * eDB ^ t / ln(eDB) - lDF / (2 * t ^ 2)
+ // pos(t) = vstart * eDB ^ t / ln(eDB) - lDF / 2 * t ^ 2
//
// pos(scrollTime) = pos - contentsPos()
- // vstart = (lDF / 2 * scrollTime ^ 2 - (pos - contentPos())) * ln(eDB) / eDB ^ scrollTime
-/*
+ // vstart = ((lDF / 2) * scrollTime ^ 2 + (pos - contentPos())) * ln(eDB) / eDB ^ scrollTime
- QPointF v = QPointF(-1, -1) * d->linearDecelerationFactor / (qreal(2) * qreal(scrollTime) * qreal(scrollTime)) - (pos - contentPosition()) *
- qLn(d->exponentialDecelerationBase) / qPow(d->exponentialDecelerationBase, scrollTime);
+ QPointF v = (QPointF(1, 1) * (d->linearDecelerationFactor / qreal(2)) * qreal(time) * qreal(time) + (pos - contentPosition()) / d->pixelPerMeter);
+ if (d->exponentialDecelerationBase != qreal(1))
+ v *= qLn(d->exponentialDecelerationBase) / qPow(d->exponentialDecelerationBase, time);
qKSDebug() << "QAbstractKineticScroller::scrollTo(" << pos << ", " << v;
qKSDebug() << "QAbstr " << d->linearDecelerationFactor <<" 1: "<< (qreal(2) * qreal(scrollTime) * qreal(scrollTime)) << " 2: " << (pos - contentPosition()) << " 3: " <<
qLn(d->exponentialDecelerationBase) << "4: " << qPow(d->exponentialDecelerationBase, scrollTime);
- v = QPointF(0.01, 0.01);
-*/
+
// start the scrolling
d->scrollToPosition = pos;
d->scrollToX = true;
@@ -962,54 +962,98 @@ void QKineticScroller::ensureVisible(const QPointF &pos, int xmargin, int ymargi
Decomposes the position into a scroll and an overshoot part.
Also keeps track of the current over-shooting value in overshootDist.
*/
-void QKineticScrollerPrivate::setContentPositionHelper(const QPointF &pos)
+void QKineticScrollerPrivate::setContentPositionHelper(const QPointF &deltaPos)
{
Q_Q(QKineticScroller);
+ QPointF oldPos = q->contentPosition();
+ QPointF newPos = oldPos + deltaPos;
QPointF maxPos = q->maximumContentPosition();
- QPointF clampedPos;
- clampedPos.setX(qBound(qreal(0), pos.x(), maxPos.x()));
- clampedPos.setY(qBound(qreal(0), pos.y(), maxPos.y()));
+ QPointF oldClampedPos;
+ oldClampedPos.setX(qBound(qreal(0), oldPos.x(), maxPos.x()));
+ oldClampedPos.setY(qBound(qreal(0), oldPos.y(), maxPos.y()));
- // -- stop scroll to if hitting boundary
- if (clampedPos.x() != pos.x())
- scrollToX = false;
- if (clampedPos.y() != pos.y())
- scrollToY = false;
+ QPointF newClampedPos;
+ newClampedPos.setX(qBound(qreal(0), newPos.x(), maxPos.x()));
+ newClampedPos.setY(qBound(qreal(0), newPos.y(), maxPos.y()));
+ // --- handle overshooting and stop if the coordinate is going back inside the normal area
bool alwaysOvershoot = (overshootPolicy == QKineticScroller::OvershootAlwaysOn);
- qreal overshootX = (maxPos.x() || alwaysOvershoot) ? pos.x() - clampedPos.x() : 0;
- qreal overshootY = (maxPos.y() || alwaysOvershoot) ? pos.y() - clampedPos.y() : 0;
- // -- stop at the maximum overshoot distance (if set)
- if (!overshootMaximumDistance.isNull()) {
+ qreal oldOvershootX = (maxPos.x() || alwaysOvershoot) ? oldPos.x() - oldClampedPos.x() : 0;
+ qreal oldOvershootY = (maxPos.y() || alwaysOvershoot) ? oldPos.y() - oldClampedPos.y() : 0;
+ if (state == QKineticScroller::StateDragging) {
+ oldOvershootX /= overshootDragResistanceFactor;
+ oldOvershootY /= overshootDragResistanceFactor;
+ }
- qreal x = qBound(-overshootMaximumDistance.x() * pixelPerMeter, overshootX, overshootMaximumDistance.x() * pixelPerMeter);
- if (x != overshootX)
- velocity.setX(0);
- overshootDistance.setX(x);
+ qreal newOvershootX = (maxPos.x() || alwaysOvershoot) ? newPos.x() - newClampedPos.x() : 0;
+ qreal newOvershootY = (maxPos.y() || alwaysOvershoot) ? newPos.y() - newClampedPos.y() : 0;
- qKSDebug() << "Overshoot y: "<<(overshootMaximumDistance.y() * pixelPerMeter)<<"distance: "<<overshootY;
- qreal y = qBound(-overshootMaximumDistance.y() * pixelPerMeter, overshootY, overshootMaximumDistance.y() * pixelPerMeter);
- if (y != overshootY)
- velocity.setY(0);
- overshootDistance.setY(y);
+ QPointF oldScrollToDist = scrollToPosition - oldPos;
+ QPointF newScrollToDist = scrollToPosition - newPos;
- } else {
- overshootDistance = QPointF(overshootX, overshootY);
+ QPointF realOvershootDistance;
+
+ qDebug() << "oldOver: " << oldOvershootY << " newOver: " << newOvershootY;
+ // -- x axis
+ if (oldOvershootX && (qSign(oldOvershootX) != qSign(newOvershootX))) {
+ realOvershootDistance.setX(0);
+ releaseVelocity.setX(0);
+ overshootX = false;
+
+ } else if (newOvershootX) {
+ if (!oldOvershootX) {
+ overshootStartTimeX = scrollAbsoluteTimer.elapsed();
+ overshootVelocity.setX(velocity.x());
+ scrollToX = false;
+ overshootX = true;
+ }
+ realOvershootDistance.setX(newOvershootX);
+
+ } else if (scrollToX && qSign(oldScrollToDist.x()) != qSign(newScrollToDist.x())) {
+ scrollToX = false;
+ newClampedPos.setX(scrollToPosition.x());
}
- qKSDebug() << "setPosition raw: " << pos << ", clamped: " << clampedPos << ", overshoot: " << overshootDistance;
+ // -- y axis
+ if (oldOvershootY && (qSign(oldOvershootY) != qSign(newOvershootY))) {
+ realOvershootDistance.setY(0);
+ releaseVelocity.setY(0);
+ overshootY = false;
- QPointF realOvershootDistance;
- if (overshootPolicy != QKineticScroller::OvershootAlwaysOff) {
- realOvershootDistance = overshootDistance;
+ } else if (newOvershootY) {
+ if (!oldOvershootY) {
+ overshootStartTimeY = scrollAbsoluteTimer.elapsed();
+ overshootVelocity.setY(velocity.y());
+ scrollToY = false;
+ overshootY = true;
+ }
+ realOvershootDistance.setY(newOvershootY);
+
+ } else if (scrollToY && qSign(oldScrollToDist.y()) != qSign(newScrollToDist.y())) {
+ scrollToY = false;
+ newClampedPos.setY(scrollToPosition.y());
+ }
+
+
+ // -- finishing overshoot distance
+ if (!realOvershootDistance.isNull()) {
if (state == QKineticScroller::StateDragging)
realOvershootDistance *= overshootDragResistanceFactor;
+
+ // -- stop at the maximum overshoot distance (if set)
+ if (!overshootMaximumDistance.isNull()) {
+ realOvershootDistance.setX(qBound(-overshootMaximumDistance.x() * pixelPerMeter, realOvershootDistance.x(), overshootMaximumDistance.x() * pixelPerMeter));
+
+ realOvershootDistance.setY(qBound(-overshootMaximumDistance.y() * pixelPerMeter, realOvershootDistance.y(), overshootMaximumDistance.y() * pixelPerMeter));
+ }
}
- q->setContentPosition(clampedPos, realOvershootDistance);
+ q->setContentPosition(newClampedPos, realOvershootDistance);
+
+ qDebug() << "setContentPositionHelper" << newClampedPos << " overshoot:" << realOvershootDistance;
}
diff --git a/qkineticscroller_p.h b/qkineticscroller_p.h
index cb06feb..1f5a564 100644
--- a/qkineticscroller_p.h
+++ b/qkineticscroller_p.h
@@ -79,14 +79,16 @@ public:
bool pressWhileScrolling(QKineticScroller::Input input, const QPointF &position, qint64 timestamp);
void timerEvent(QTimerEvent *);
- void timerEventWhileDragging(qint64 deltaTime);
- void timerEventWhileScrolling(qint64 deltaTime);
+ void timerEventWhileDragging();
+ void timerEventWhileScrolling();
void handleDrag(const QPointF &position, qint64 timestamp);
void updateVelocity(const QPointF &deltaPixel, qint64 deltaTime);
- void setContentPositionHelper(const QPointF &position);
- qreal decelerate(qreal v, qreal expDecel, qreal linDecel);
+ qreal decelerate(qreal v, qreal t);
+ QPointF calculateVelocity(qreal time);
+ void setContentPositionHelper(const QPointF &deltaPos);
+
static const char *stateName(QKineticScroller::State state);
static const char *inputName(QKineticScroller::Input input);
@@ -96,7 +98,7 @@ public:
qreal dragVelocitySmoothingFactor;
qreal linearDecelerationFactor;
qreal exponentialDecelerationBase;
- qreal overshootSpringConstant;
+ qreal overshootSpringConstantRoot;
qreal overshootDragResistanceFactor;
QPointF overshootMaximumDistance;
qreal overshootMaximumVelocity;
@@ -126,16 +128,22 @@ public:
qint64 lastTimestamp;
QPointF dragDistance;
- QPointF overshootDistance;
QPointF scrollToPosition;
bool scrollToX;
bool scrollToY;
+ bool overshootX;
+ bool overshootY;
+
qreal pixelPerMeter;
- QElapsedTimer dragTimer;
- QElapsedTimer scrollTimer;
+ QElapsedTimer scrollRelativeTimer;
+ QElapsedTimer scrollAbsoluteTimer;
+ QPointF releaseVelocity;
+ QPointF overshootVelocity;
+ qreal overshootStartTimeX;
+ qreal overshootStartTimeY;
int timerId;
QKineticScroller *q_ptr;