aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Jones <martin.jones@nokia.com>2012-01-31 09:12:13 +1000
committerQt by Nokia <qt-info@nokia.com>2012-02-02 03:55:21 +0100
commitc1e668a96764d1a252322ebae3af72097c27dfe1 (patch)
tree8500010095c43a04170859d8bb926482bab7bb0b
parent0b575d9405972a17eebea653c422442580597263 (diff)
Multiple fast flicks with large content moves faster
Repeatedly flicking quickly in a large view moves faster than the velocity of the touch. Task-number: QTBUG-18600 Change-Id: Ie6747e0d945022e0c0c5f6c5f95bc35403a14d56 Reviewed-by: Martin Jones <martin.jones@nokia.com>
-rw-r--r--src/quick/items/qquickflickable.cpp86
-rw-r--r--src/quick/items/qquickflickable_p_p.h5
-rw-r--r--tests/auto/qtquick2/qquickflickable/data/flickable03.qml4
-rw-r--r--tests/auto/qtquick2/qquickflickable/tst_qquickflickable.cpp18
4 files changed, 94 insertions, 19 deletions
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index f5819099a7..621550057c 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -83,6 +83,21 @@ QT_BEGIN_NAMESPACE
#define QML_FLICK_OVERSHOOTFRICTION 8
#endif
+// Multiflick acceleration minimum flick velocity threshold
+#ifndef QML_FLICK_MULTIFLICK_THRESHOLD
+#define QML_FLICK_MULTIFLICK_THRESHOLD 1250
+#endif
+
+// Multiflick acceleration minimum contentSize/viewSize ratio
+#ifndef QML_FLICK_MULTIFLICK_RATIO
+#define QML_FLICK_MULTIFLICK_RATIO 10
+#endif
+
+// Multiflick acceleration maximum velocity multiplier
+#ifndef QML_FLICK_MULTIFLICK_MAXBOOST
+#define QML_FLICK_MULTIFLICK_MAXBOOST 3.0
+#endif
+
// FlickThreshold determines how far the "mouse" must have moved
// before we perform a flick.
static const int FlickThreshold = 20;
@@ -179,7 +194,7 @@ QQuickFlickablePrivate::QQuickFlickablePrivate()
, deceleration(QML_FLICK_DEFAULTDECELERATION)
, maxVelocity(QML_FLICK_DEFAULTMAXVELOCITY), reportedVelocitySmoothing(100)
, delayedPressEvent(0), delayedPressTarget(0), pressDelay(0), fixupDuration(400)
- , fixupMode(Normal), vTime(0), visibleArea(0)
+ , flickBoost(1.0), fixupMode(Normal), vTime(0), visibleArea(0)
, flickableDirection(QQuickFlickable::AutoFlickDirection)
, boundsBehavior(QQuickFlickable::DragAndOvershootBounds)
{
@@ -664,6 +679,11 @@ void QQuickFlickable::setInteractive(bool interactive)
The instantaneous velocity of movement along the x and y axes, in pixels/sec.
The reported velocity is smoothed to avoid erratic output.
+
+ Note that for views with a large content size (more than 10 times the view size),
+ the velocity of the flick may exceed the velocity of the touch in the case
+ of multiple quick consecutive flicks. This allows the user to flick faster
+ through large content.
*/
qreal QQuickFlickable::horizontalVelocity() const
{
@@ -803,8 +823,23 @@ void QQuickFlickablePrivate::handleMousePressEvent(QMouseEvent *event)
&& (qAbs(hData.smoothVelocity.value()) > RetainGrabVelocity
|| qAbs(vData.smoothVelocity.value()) > RetainGrabVelocity)) {
stealMouse = true; // If we've been flicked then steal the click.
+ int flickTime = timeline.time();
+ if (flickTime > 600) {
+ // too long between flicks - cancel boost
+ hData.continuousFlickVelocity = 0;
+ vData.continuousFlickVelocity = 0;
+ flickBoost = 1.0;
+ } else {
+ hData.continuousFlickVelocity = -hData.smoothVelocity.value();
+ vData.continuousFlickVelocity = -vData.smoothVelocity.value();
+ if (flickTime > 300) // slower flicking - reduce boost
+ flickBoost = qMax(1.0, flickBoost - 0.5);
+ }
} else {
stealMouse = false;
+ hData.continuousFlickVelocity = 0;
+ vData.continuousFlickVelocity = 0;
+ flickBoost = 1.0;
}
q->setKeepMouseGrab(stealMouse);
pressed = true;
@@ -817,13 +852,12 @@ void QQuickFlickablePrivate::handleMousePressEvent(QMouseEvent *event)
vData.dragMaxBound = q->maxYExtent();
fixupMode = Normal;
lastPos = QPointF();
- lastPosTime = computeCurrentTime(event);
pressPos = event->localPos();
hData.pressPos = hData.move.value();
vData.pressPos = vData.move.value();
hData.flicking = false;
vData.flicking = false;
- lastPressTime = computeCurrentTime(event);
+ lastPosTime = lastPressTime = computeCurrentTime(event);
QQuickItemPrivate::start(velocityTime);
}
@@ -960,24 +994,46 @@ void QQuickFlickablePrivate::handleMouseReleaseEvent(QMouseEvent *event)
vTime = timeline.time();
- qreal velocity = elapsed < 100 ? vData.velocity : 0;
- if (vData.atBeginning || vData.atEnd)
- velocity /= 2;
- if (q->yflick() && qAbs(velocity) > MinimumFlickVelocity && qAbs(event->localPos().y() - pressPos.y()) > FlickThreshold) {
+ bool canBoost = false;
+
+ qreal vVelocity = elapsed < 100 ? vData.velocity : 0;
+ if (vData.atBeginning || vData.atEnd) {
+ vVelocity /= 2;
+ } else if (vData.continuousFlickVelocity != 0.0
+ && vData.viewSize/q->height() > QML_FLICK_MULTIFLICK_RATIO
+ && ((vVelocity > 0) == (vData.continuousFlickVelocity > 0))
+ && qAbs(vVelocity) > QML_FLICK_MULTIFLICK_THRESHOLD) {
+ // accelerate flick for large view flicked quickly
+ canBoost = true;
+ }
+
+ qreal hVelocity = elapsed < 100 ? hData.velocity : 0;
+ if (hData.atBeginning || hData.atEnd) {
+ hVelocity /= 2;
+ } else if (hData.continuousFlickVelocity != 0.0
+ && hData.viewSize/q->width() > QML_FLICK_MULTIFLICK_RATIO
+ && ((hVelocity > 0) == (hData.continuousFlickVelocity > 0))
+ && qAbs(hVelocity) > QML_FLICK_MULTIFLICK_THRESHOLD) {
+ // accelerate flick for large view flicked quickly
+ canBoost = true;
+ }
+
+ flickBoost = canBoost ? qBound(1.0, flickBoost+0.25, QML_FLICK_MULTIFLICK_MAXBOOST) : 1.0;
+
+ vVelocity *= flickBoost;
+ if (q->yflick() && qAbs(vVelocity) > MinimumFlickVelocity && qAbs(event->localPos().y() - pressPos.y()) > FlickThreshold) {
velocityTimeline.reset(vData.smoothVelocity);
- vData.smoothVelocity.setValue(-velocity);
- flickY(velocity);
+ vData.smoothVelocity.setValue(-vVelocity);
+ flickY(vVelocity);
} else {
fixupY();
}
- velocity = elapsed < 100 ? hData.velocity : 0;
- if (hData.atBeginning || hData.atEnd)
- velocity /= 2;
- if (q->xflick() && qAbs(velocity) > MinimumFlickVelocity && qAbs(event->localPos().x() - pressPos.x()) > FlickThreshold) {
+ hVelocity *= flickBoost;
+ if (q->xflick() && qAbs(hVelocity) > MinimumFlickVelocity && qAbs(event->localPos().x() - pressPos.x()) > FlickThreshold) {
velocityTimeline.reset(hData.smoothVelocity);
- hData.smoothVelocity.setValue(-velocity);
- flickX(velocity);
+ hData.smoothVelocity.setValue(-hVelocity);
+ flickX(hVelocity);
} else {
fixupX();
}
diff --git a/src/quick/items/qquickflickable_p_p.h b/src/quick/items/qquickflickable_p_p.h
index 0cd7113909..e4290f2bf5 100644
--- a/src/quick/items/qquickflickable_p_p.h
+++ b/src/quick/items/qquickflickable_p_p.h
@@ -71,7 +71,7 @@ QT_BEGIN_NAMESPACE
const qreal MinimumFlickVelocity = 75.0;
class QQuickFlickableVisibleArea;
-class QQuickFlickablePrivate : public QQuickItemPrivate, public QQuickItemChangeListener
+class Q_AUTOTEST_EXPORT QQuickFlickablePrivate : public QQuickItemPrivate, public QQuickItemChangeListener
{
Q_DECLARE_PUBLIC(QQuickFlickable)
@@ -97,6 +97,7 @@ public:
struct AxisData {
AxisData(QQuickFlickablePrivate *fp, void (QQuickFlickablePrivate::*func)(qreal))
: move(fp, func), viewSize(-1), startMargin(0), endMargin(0)
+ , continuousFlickVelocity(0)
, smoothVelocity(fp), atEnd(false), atBeginning(true)
, fixingUp(false), inOvershoot(false), moving(false), flicking(false)
, dragging(false), extentsChanged(false)
@@ -129,6 +130,7 @@ public:
qreal flickTarget;
qreal startMargin;
qreal endMargin;
+ qreal continuousFlickVelocity;
QQuickFlickablePrivate::Velocity smoothVelocity;
QPODVector<qreal,10> velocityBuffer;
bool atEnd : 1;
@@ -198,6 +200,7 @@ public:
QBasicTimer delayedPressTimer;
int pressDelay;
int fixupDuration;
+ qreal flickBoost;
enum FixupMode { Normal, Immediate, ExtentChanged };
FixupMode fixupMode;
diff --git a/tests/auto/qtquick2/qquickflickable/data/flickable03.qml b/tests/auto/qtquick2/qquickflickable/data/flickable03.qml
index ebc49ba90a..719c682ee6 100644
--- a/tests/auto/qtquick2/qquickflickable/data/flickable03.qml
+++ b/tests/auto/qtquick2/qquickflickable/data/flickable03.qml
@@ -1,13 +1,13 @@
import QtQuick 2.0
Flickable {
- width: 100; height: 200
+ width: 100; height: 400
contentWidth: column.width; contentHeight: column.height
Column {
id: column
Repeater {
- model: 4
+ model: 20
Rectangle { width: 200; height: 300; color: "blue" }
}
}
diff --git a/tests/auto/qtquick2/qquickflickable/tst_qquickflickable.cpp b/tests/auto/qtquick2/qquickflickable/tst_qquickflickable.cpp
index 3fa822b0bf..4439d97491 100644
--- a/tests/auto/qtquick2/qquickflickable/tst_qquickflickable.cpp
+++ b/tests/auto/qtquick2/qquickflickable/tst_qquickflickable.cpp
@@ -44,6 +44,7 @@
#include <QtDeclarative/qdeclarativecomponent.h>
#include <QtQuick/qquickview.h>
#include <private/qquickflickable_p.h>
+#include <private/qquickflickable_p_p.h>
#include <private/qdeclarativevaluetype_p.h>
#include <math.h>
#include "../../shared/util.h"
@@ -131,7 +132,7 @@ void tst_qquickflickable::verticalViewportSize()
QVERIFY(obj != 0);
QCOMPARE(obj->contentWidth(), 200.);
- QCOMPARE(obj->contentHeight(), 1200.);
+ QCOMPARE(obj->contentHeight(), 6000.);
QCOMPARE(obj->isAtXBeginning(), true);
QCOMPARE(obj->isAtXEnd(), false);
QCOMPARE(obj->isAtYBeginning(), true);
@@ -538,6 +539,20 @@ void tst_qquickflickable::flickVelocity()
QVERIFY(flickable->verticalVelocity() < 0.0);
QTRY_VERIFY(flickable->verticalVelocity() == 0.0);
+ // Flick multiple times and verify that flick acceleration is applied.
+ QQuickFlickablePrivate *fp = QQuickFlickablePrivate::get(flickable);
+ bool boosted = false;
+ for (int i = 0; i < 6; ++i) {
+ flick(canvas, QPoint(20,390), QPoint(20, 50), 200);
+ boosted |= fp->flickBoost > 1.0;
+ }
+ QVERIFY(boosted);
+
+ // Flick in opposite direction -> boost cancelled.
+ flick(canvas, QPoint(20,10), QPoint(20, 340), 200);
+ QTRY_VERIFY(flickable->verticalVelocity() < 0.0);
+ QVERIFY(fp->flickBoost == 1.0);
+
delete canvas;
}
@@ -616,6 +631,7 @@ void tst_qquickflickable::flick(QQuickView *canvas, const QPoint &from, const QP
}
QTest::mouseRelease(canvas, Qt::LeftButton, 0, to);
+ QTest::qWait(50);
}
template<typename T>