summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobert Griebl <rgriebl@trolltech.com>2010-03-30 12:02:17 +0200
committerRobert Griebl <rgriebl@trolltech.com>2010-03-30 12:02:17 +0200
commitd6b663fcd6230492728d7c4b4a304b209533232c (patch)
tree01ab3626b355fb74e3c9138a2c1de85ae571c577
initial commit - Scrolling and Backshooting states are still missing
-rw-r--r--qkineticscroller.cpp657
-rw-r--r--qkineticscroller.h152
-rw-r--r--qkineticscroller_p.h121
3 files changed, 930 insertions, 0 deletions
diff --git a/qkineticscroller.cpp b/qkineticscroller.cpp
new file mode 100644
index 0000000..cdc5e9c
--- /dev/null
+++ b/qkineticscroller.cpp
@@ -0,0 +1,657 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qkineticscroller.h>
+#include <qkineticscroller_p.h>
+
+#include <QtDebug>
+
+QT_BEGIN_NAMESPACE
+
+//#define KINETIC_SCROLLER_DEBUG
+
+#ifdef KINETIC_SCROLLER_DEBUG
+# define qKSDebug qDebug
+#else
+# define qKSDebug while (false) qDebug
+#endif
+
+
+/*!
+ \class QKineticScroller
+ \brief The QKineticScroller class enables kinetic scrolling for any scrolling widget or graphics item.
+ \ingroup qtmaemo5
+ \since 4.6
+ \preliminary
+
+ With kinetic scrolling, the user can push the widget in a given
+ direction and it will continue to scroll in this direction until it is
+ stopped either by the user or by friction. Aspects of inertia, friction
+ and other physical concepts can be changed in order to fine-tune an
+ intuitive user experience.
+
+ To enable kinetic scrolling for a widget or graphics item, you need to
+ derive from this class and implement at least all the pure-virtual
+ functions.
+
+ Qt for Maemo 5 already comes with two implementations for
+ QScrollArea and QWebView, and those kinetic scrollers are
+ automatically instantiated and attached to these widgets on creation.
+ In the QScrollArea case, the kinetic scroller is initially
+ disabled. However, for QItemView and QScrollArea derived classes
+ it is enabled by default. You can obtain these automatically created
+ objects via a dynamic property:
+
+ \code
+ // disable the kinetic scroller on scrollArea
+ QKineticScroller *scroller = scrollArea->property("kineticScroller")
+ .value<QKineticScroller *>();
+ if (scroller)
+ scroller->setEnabled(false);
+ \endcode
+
+ In addition there is also an example on how you would add kinetic
+ scrolling to a QGraphicsView based application in \c maemobrowser
+ examples in the \c maemo5 examples directory.
+
+ The kinetic scroller installs an event filter on the widget to handle mouse
+ presses and moves on the widget \mdash presses and moves on a device's touch screen
+ are also handled by this mechanism. These events will be interpreted as scroll actions
+ depending on the current state() of the scroller.
+
+ Even though this kinetic scroller has a huge number of settings, we
+ recommend that you leave them all at their default values. In case you
+ really want to change them you can try out the \c kineticscroller
+ example in the \c maemo5 examples directory.
+
+ \sa QWidget
+*/
+
+
+/*!
+ Constructs a new kinetic scroller.
+*/
+QKineticScroller::QKineticScroller()
+ : d_ptr(new QKineticScrollerPrivate())
+{
+ Q_D(QKineticScroller);
+ d->q_ptr = this;
+ d->init();
+}
+
+/*! \internal
+*/
+QKineticScroller::QKineticScroller(QKineticScrollerPrivate &dd)
+ : d_ptr(&dd)
+{
+ Q_D(QKineticScroller);
+ d->q_ptr = this;
+ d->init();
+}
+
+/*!
+ Destroys the scroller.
+*/
+QKineticScroller::~QKineticScroller()
+{
+}
+
+/*!
+ Resets the internal state of the kinetic scroller. This function is not
+ needed for normal use. This function only needs to be called if the
+ kinetic scroller is being re-attached to a different widget.
+*/
+void QKineticScroller::reset()
+{
+ Q_D(QKineticScroller);
+
+ d->setState(Inactive);
+}
+
+/*!
+ \enum QKineticScroller::State
+
+ This enum describes the possible states the kinetic scroller can be in.
+
+ \value Inactive The scroller is inactive. It may also have been disabled.
+ \value MousePressed The user has pressed the mouse button (or pressed the
+ the touch screen).
+ \value Pushing The user is dragging the mouse cursor (or other input
+ point) over the scroll area.
+ \value AutoScrolling Scrolling is occurring without direct user input.
+*/
+
+
+static const char *stateName(QKineticScroller::State state)
+{
+ switch (state) {
+ case QKineticScroller::StateInactive: return "inactive";
+ case QKineticScroller::StatePressed: return "pressed";
+ case QKineticScroller::StateDragging: return "dragging";
+ case QKineticScroller::StateScrolling: return "scrolling";
+ case QKineticScroller::StateBackshooting: return "backshooting";
+ default: return "(invalid)";
+}
+
+static const char *inputName(QKineticScroller::Input input)
+{
+ switch (input) {
+ case QKineticScroller::InputPress: return "press";
+ case QKineticScroller::InputMove: return "move";
+ case QKineticScroller::InputRelease: return "release";
+ default: return "(invalid)";
+}
+
+
+
+
+void QKineticScrollerPrivate::timerEvent(QTimerEvent *e)
+{
+ Q_Q(QKineticScroller);
+
+ if (e->timerId() != timerId) {
+ QObject::timerEvent(e);
+ return;
+ }
+
+ struct timerevent {
+ State state;
+ bool (QKineticScrollerPrivate::*handler)(qint64 elapsed);
+ };
+
+ timerevent timerevents[] = {
+ { StateDragging, QKineticScrollerPrivate::timerEventWhileDragging }
+ { StateScrolling, QKineticScrollerPrivate::timerEventWhileScrolling }
+ { StateBackshooting, QKineticScrollerPrivate::timerEventWhileBackshooting }
+ }
+
+ for (int i = 0; i < sizeof(timerevents) / sizeof(*timerevents); ++i) {
+ timerevent *te = timerevents + i;
+
+ if (state == te->state) {
+ te->handler(elapsed.restart());
+ return;
+ }
+ }
+
+ qWarning() << "Unhandled timer event, while in state " << stateName(state);
+ killTimer(timerId);
+ timerId = 0;
+}
+
+bool QKineticScroller::handleInput(Input input, const QPointF &position, qint64 timestamp)
+{
+ Q_D(QKineticScroller);
+
+ struct statechange {
+ State state;
+ Input input;
+ bool (QKineticScrollerPrivate::*handler)(Input input, const QPointF &position, qint64 timestamp);
+ };
+
+ statechange statechanges[] = {
+ { StateInactive, InputPress, QKineticScrollerPrivate::pressWhileInactive },
+ { StatePressed, InputMove, QKineticScrollerPrivate::moveWhilePressed },
+ { StatePressed, InputRelease, QKineticScrollerPrivate::releaseWhilePressed },
+ { StateDragging, InputMove, QKineticScrollerPrivate::moveWhileDragging },
+ { StateDragging, InputRelease, QKineticScrollerPrivate::releaseWhileDragging },
+ { StateScrolling, InputPress, QKineticScrollerPrivate::pressWhileScrolling }
+ }
+
+ for (int i = 0; i < sizeof(statechanges) / sizeof(*statechanges); ++i) {
+ statchange *sc = statechanges + i;
+
+ if (d->state == sc->state && input == sc->input)
+ return sc->handler(input, position, timestamp);
+ }
+
+ qWarning() << "Unhandled input: got input " << d->inputName(input) << " while in state " << d->stateName(d->state);
+ return false;
+}
+
+
+
+QVariant QKineticScroller::scrollMetrics(ScrollMetrics metric) const
+{
+ Q_D(QKineticScroller);
+
+ switch (metric) {
+ case DragVelocitySmoothingFactor: return d->dragVelocitySmoothingFactor;
+ case FrictionCoefficent: return d->frictionCoefficent;
+ case OvershootFrictionCoefficent: return d->overshootFricitionCoefficient;
+ case OvershootSpringConstant: return d->overshootSpringConstant;
+ case OvershootMaximumDistance: return d->overshootMaximumDistance;
+ case DragStartDistance: return d->dragStartDistance;
+ case DragStartDirectionErrorMargin: return d->dragStartDirectionErrorMargin;
+ case MinimumVelocity: return d->minimumVelocity;
+ case MaximumVelocity: return d->maximumVelocity;
+ case MaximumNonAcceleratedVelocity: return d->maximumNonAcceleratedVelocity;
+ case MaximumClickThroughVelocity: return d->maximumClickThroughVelocity;
+ case AxisLockThreshold: return d->axisLockThreshold;
+ case FramesPerMillisecond: return d->framesPerMillisecond;
+ }
+ return QVariant();
+}
+
+
+void QKineticScroller::setScrollMetrics(ScrollMetrics metric, const QVariant &value)
+{
+ Q_D(QKineticScroller);
+
+ switch (metric) {
+ case DragVelocitySmoothingFactor: d->dragVelocitySmoothingFactor = qBound(0, value.toReal(), 1); break;
+ case FrictionCoefficent: d->frictionCoefficent = qBound(0, value.toReal(), 1); break;
+ case OvershootFrictionCoefficent: d->overshootFricitionCoefficient = qBound(0, value.toReal(), 1); break;
+ case OvershootSpringConstant: d->overshootSpringConstant = value.toReal(); break;
+ case OvershootMaximumDistance: d->overshootMaximumDistance = value.toPointF(); break;
+ case DragStartDistance: d->dragStartDistance = value.toReal(); break;
+ case DragStartDirectionErrorMargin: d->dragStartDirectionErrorMargin = value.toReal(); break;
+ case MinimumVelocity: d->minimumVelocity = value.toReal(); break;
+ case MaximumVelocity: d->maximumVelocity = value.toReal(); break;
+ case MaximumNonAcceleratedVelocity: d->maximumNonAcceleratedVelocity = value.toReal(); break;
+ case MaximumClickThroughVelocity: d->maximumClickThroughVelocity = value.toReal(); break;
+ case AxisLockThreshold: d->axisLockThreshold = qBound(0, value.toReal(), 1); break;
+ case FramesPerMillisecond: d->framesPerMillisecond = qBound(1, value.toInt(), 100); break;
+ }
+}
+
+void QKineticScrollerPrivate::handleDrag(const QPointF &position, qint64 timestamp)
+{
+ Q_Q(QKineticScroller);
+
+ QPointF deltaPixel = position - lastPostion;
+ qint64 deltaTime = timestamp - lastTimestamp;
+
+ if (axisLockThreshold) {
+ int dx = qAbs(deltaPixel.x());
+ int dy = qAbs(deltaPixel.y());
+ if (dx || dy) {
+ bool vertical = (dy > dx);
+ qreal alpha = qreal(vertical ? dx : dy) / qreal(vertical ? dy : dx);
+ //qKSDebug() << "axis lock: " << alpha << " / " << axisLockThreshold << " - isvertical: " << vertical << " - dx: " << dx << " - dy: " << dy;
+ if (alpha <= axisLockThreshold) {
+ if (vertical)
+ deltaPixel.setX(0);
+ else
+ deltaPixel.setY(0);
+ }
+ }
+ }
+
+ // calculate velocity (if the user would release the mouse NOW)
+ QPointF newVelocity = calculateVelocity(deltaPixel, deltaTime);
+
+ // restrict velocity, if content is not scrollable
+ QPoint maxPos = q->maximumContentPosition();
+ bool canScrollX = maxPos.x() || (overshootPolicy == QKineticScroller::OvershootAlwaysOn);
+ bool canScrollY = maxPos.y() || (overshootPolicy == QKineticScroller::OvershootAlwaysOn);
+
+ if (!canScrolX)) {
+ deltaPixel.setX(0);
+ newVelocity.setX(0);
+ }
+ if (!canScrollY) {
+ deltaPixel.setY(0);
+ newVelocity.setY(0);
+ }
+ velocity = newVelocity;
+
+// if (firstDrag) {
+// // Do not delay the first drag
+// setScrollPositionHelper(q->contentPosition() - overshootDistance - deltaPixel);
+// dragDistance = QPointF(0, 0);
+// } else {
+ dragDistance += deltaPixel;
+// }
+
+ if (canScrollX)
+ lastPosition.setX(position.x());
+ if (canScrollY)
+ lastPosition.setY(position.y());
+ lastTimestamp = timestamp;
+}
+
+
+bool operator<=(const QPointF &p, qreal f)
+{
+ return (qAbs(p.x()) <= f) && (qAbs(p.y()) <= f);
+}
+bool operator<(const QPointF &p, qreal f)
+{
+ return (qAbs(p.x()) < f) && (qAbs(p.y()) < f);
+}
+
+bool operator>=(const QPointF &p, qreal f)
+{
+ return (qAbs(p.x()) >= f) || (qAbs(p.y()) >= f);
+}
+bool operator>(const QPointF &p, qreal f)
+{
+ return (qAbs(p.x()) > f) || (qAbs(p.y()) > f);
+}
+
+QPointF qAbs(const QPointF &p)
+{
+ return QPointF(qAbs(p.x()), qAbs(p.y()));
+}
+
+
+
+bool QKineticScrollerPrivate::pressWhileInactive(Input, const QPointF &position, qint64 timestamp)
+{
+ Q_Q(QKineticScroller);
+
+ if ((q->maximumContentPosition() > qreal(0)) || (overshootPolicy == QKineticScroller::OvershootAlwaysOn))
+ if (q->canStartScrollingAt(position)) {
+ lastPosition = pressPosition = position;
+ lastTimestamp = pressTimestamp = timestamp;
+ setState(QKineticScroller::StatePressed);
+ }
+ }
+}
+
+bool QKineticScrollerPrivate::releaseWhilePressed(Input, const QPointF &position, qint64 timestamp)
+{
+ setState(QKineticScroller::StateInactive);
+ return false;
+}
+
+bool QKineticScrollerPrivate::moveWhilePressed(Input, const QPointF &position, qint64 timestamp)
+{
+ Q_Q(QKineticScroller);
+
+ QPointF deltaPixel = position - pressPosition;
+
+ bool moveStarted = (deltaPixel.manhattanLength() > dragStartDistance);
+
+ if (moveStarted) {
+ qreal deltaXtoY = qAbs(pressPosition.x() - position.x()) - qAbs(pressPosition.y() - position.y());
+
+ QPointF maxPos = q->maximumContentPosition();
+ bool canScrollX = (maxPos.x() > 0);
+ bool canScrollY = (maxPos.y() > 0);
+
+ if (overshootPolicy == QKineticScroller::OvershootAlwaysOn)
+ canScrollX = canScrollY = true;
+
+ if (deltaXtoY < 0) {
+ if (!canScrollY && (!canScrollX || (-deltaXtoY >= dragStartDirectionErrorMargin)))
+ moveStarted = false;
+ } else {
+ if (!canScrollX && (!canScrollY || (deltaXtoY >= dragStartDirectionErrorMargin)))
+ moveStarted = false;
+ }
+ }
+
+ if (moveStarted) {
+ q->cancelPress(pressPosition);
+ setState(QKineticScroller::Dragging);
+
+ // ignore the dragStartDistance
+ deltaPixel = deltaPixel - deltaPixel * (dragStartDistance / deltaPixel.manhattanLength());
+
+ if (!deltaPixel.isNull()) {
+ // handleDrag updates lastPosition, lastTimestamp and velocity
+ handleDrag(pressPosition + deltaPixel, timestamp);
+ }
+ }
+ return moveStarted;
+}
+
+bool QKineticScrollerPrivate::moveWhileDragging(Input, const QPointF &position, qint64 timestamp)
+{
+ // handleDrag updates lastPosition, lastTimestamp and velocity
+ handleDrag(position, timestamp);
+ return true;
+}
+
+void QKineticScrollerPrivate::timerEventWhileDragging(qint64 timestamp)
+{
+ if (!dragDistance.isNull())
+ setScrollPositionHelper(q->contentPosition() - overshootDistance - dragDistance);
+}
+
+bool QKineticScrollerPrivate::releaseWhileDragging(Input, const QPointF &position, qint64 timestamp)
+{
+ Q_Q(QKineticScroller);
+
+ // ...
+
+ setState(QKineticScroller::AutoScrolling);
+ return true;
+}
+
+void QKineticScrollerPrivate::timerEventWhileScrolling(qint64 timestamp)
+{
+ Q_Q(QKineticScroller);
+}
+
+bool QKineticScrollerPrivate::pressWhileScrolling(Input, const QPointF &position, qint64 timestamp)
+{
+ oldVelocity = velocity;
+ velocity = QPointF(0, 0);
+
+ setState(QKineticScroller::Pressed);
+ return true;
+}
+
+void QKineticScrollerPrivate::timerEventWhileBackshooting(qint64 timestamp)
+{
+ Q_Q(QKineticScroller);
+}
+
+
+void QKineticScrollerPrivate::setState(QKineticScroller::State newstate)
+{
+ if (state == newstate)
+ return;
+
+ bool startTimer = false, stopTimer = false;
+
+ switch (state) {
+ case QKineticScroller::StatePressed:
+ if (newstate == QKineticScroller::Dragging) {
+ if (!timerId) {
+ timerId = startTimer(1000 / framesPerSecond);
+ } else {
+ qWarning() << "State change from " << stateName(state) << " to " << stateName(newstate) << ", but timer is already activate.";
+ }
+ break;
+ case QKineticScroller::Scrolling:
+ case QKineticScroller::Backshooting:
+ if (newstate == QKineticScroller::Inactive) {
+ if (timerId) {
+ killTimer(timerId);
+ timerId = 0;
+ } else {
+ qWarning() << "State change from " << stateName(state) << " to " << stateName(newstate) << ", but timer is not activate.";
+ }
+ }
+ break;
+ }
+
+ switch (newstate) {
+ case QKineticScroller::Inactive:
+ case QKineticScroller::Pressed:
+ velocity = QPointF(0, 0);
+ break;
+ case QKineticScroller::Dragging:
+ dragDistance = QPointF(0, 0);
+ break;
+ }
+
+ qSwap(state, newstate);
+ emit stateChanged(newstate);
+}
+
+
+/*
+ Decomposes the position into a scroll and an overshoot part.
+ Also keeps track of the current over-shooting value in overshootDist.
+*/
+void QKineticScrollerPrivate::setScrollPositionHelper(const QPoint &pos)
+{
+ Q_Q(QKineticScroller);
+
+ QPointF maxPos = q->maximumScrollPosition();
+
+ QPointF clampedPos;
+ clampedPos.setX(qBound(0, pos.x(), maxPos.x()));
+ clampedPos.setY(qBound(0, pos.y(), maxPos.y()));
+
+ bool alwaysOvershoot = (overshootPolicy == QKineticScroller::OvershootAlwaysOn);
+ qreal overshootX = (maxPos.x() || alwaysOvershoot) ? clampedPos.x() - pos.x() : 0;
+ qreal overshootY = (maxPos.y() || alwaysOvershoot) ? clampedPos.y() - pos.y() : 0;
+
+ if (overshootMaximumDistance) {
+ overshootDistance.setX(qBound(-overshootMaximumDistance.x(), overshootX, overshootMaximumDistance.x()));
+ overshootDistance.setY(qBound(-overshootMaximumDistance.y(), overshootY, overshootMaximumDistance.y()));
+ } else {
+ overshootDistance = QPointF(overshootX, overshootY);
+ }
+
+ qKSDebug() << "setPosition raw: " << pos << ", clamped: " << clampedPos << ", overshoot: " << overshootDist;
+ q->setScrollPosition(clampedPos, overshootPolicy == QKineticScroller::OvershootAlwaysOff ? QPoint() : overshootDistance);
+}
+
+
+/*!
+ \enum QKineticScroller::OvershootPolicy
+
+ This enum describes the various modes of overshooting.
+
+ \value OvershootWhenScrollable Overshooting is when the content is scrollable. This is the default.
+
+ \value OvershootAlwaysOff Overshooting is never enabled (even when the content is scrollable).
+
+ \value OvershootAlwaysOn Overshooting is always enabled (even when the content is not scrollable).
+*/
+
+
+/*!
+ If kinetic scrolling can be started at the given content's \a position,
+ this function needs to return true; otherwise it needs to return false.
+
+ The default value is true, regardless of \a position.
+*/
+bool QKineticScroller::canStartScrollingAt(const QPointF &position) const
+{
+ Q_UNUSED(position);
+ return true;
+}
+
+/*!
+ Since a mouse press is always delivered normally when the scroller is in
+ the StateInactive state, we may need to cancel it as soon as the user
+ has moved the mouse far enough to actually start a kinetic scroll
+ operation.
+
+ The \a pressPosition parameter can be used to find out which widget (or
+ graphics item) received the mouse press in the first place.
+
+ Subclasses may choose to simulate a fake mouse release event for that
+ widget (or graphics item), preferably \bold not within its boundaries.
+ The default implementation does nothing.
+*/
+void QKineticScroller::cancelPress(const QPointF &pressPosition)
+{
+ Q_UNUSED(pressPosition);
+}
+
+
+/*!
+ This function get called whenever the state of the kinetic scroller changes.
+ The old state is supplied as \a oldState, while the new state is returned by
+ calling state().
+
+ The default implementation does nothing.
+
+ \sa state()
+*/
+void QKineticScroller::stateChanged(State oldState)
+{
+ Q_UNUSED(oldState);
+}
+
+/*!
+ \fn QPointF QKineticScroller::maximumContentPosition() const
+
+ Returns the maximum valid content position. The minimum is always \c
+ (0,0).
+
+ \sa scrollTo()
+*/
+
+/*!
+ \fn QSizeF QKineticScroller::viewportSize() const
+
+ Returns the size of the currently visible content positions. In the
+ case where an QAbstractScrollArea is used, this is equivalent to the
+ viewport() size.
+
+ \sa scrollTo()
+*/
+
+/*!
+ \fn QPointF QKineticScroller::contentPosition() const
+
+ \brief Returns the current position of the content.
+
+ Note that overshooting is not considered to be "real" scrolling so the
+ position might be (0,0) even if the user is currently dragging the
+ widget outside the "normal" maximumContentPosition().
+
+ \sa maximumContentPosition()
+*/
+
+
+/*!
+ \fn void QKineticScroller::setContentPosition(const QPointF &position, const QPointF &overshoot)
+
+ Set the content's \a position. This parameter will always be in the
+ valid range QPointF(0, 0) and maximumContentPosition().
+
+ In the case where overshooting is required, the \a overshoot parameter
+ will give the direction and the absolute distance to overshoot.
+
+ \sa maximumContentPosition()
+*/
+
+QT_END_NAMESPACE
diff --git a/qkineticscroller.h b/qkineticscroller.h
new file mode 100644
index 0000000..3210e11
--- /dev/null
+++ b/qkineticscroller.h
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTKINETICSCROLLER_H
+#define QABSTRACTKINETICSCROLLER_H
+
+#include <QtCore/qmetatype.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qrect.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QKineticScrollerPrivate;
+
+class Q_GUI_EXPORT QKineticScroller
+{
+public:
+ ~QKineticScroller();
+
+ bool isEnabled() const;
+ void setEnabled(bool b);
+
+ enum State
+ {
+ StateInactive,
+ StatePressed,
+ StateDragging,
+ StateScrolling,
+ StateBackshooting
+ };
+
+ State state() const;
+
+ enum OvershootPolicy
+ {
+ OvershootWhenScrollable,
+ OvershootAlwaysOff,
+ OvershootAlwaysOn,
+ };
+
+ OvershootPolicy overshootPolicy() const;
+ void setOvershootPolicy(OvershootPolicy policy);
+
+ enum ScrollMetric
+ {
+ DragVelocitySmoothingFactor, // [0..1] v = v_new* DASF + v_old * (1-DASF)
+
+ /* A factor between \c 0 and \c 1 */
+
+ FrictionCoefficent, // [0..1]
+ OvershootFrictionCoefficent, // [0..1]
+ OvershootSpringConstant, // [0...[
+ OvershootMaximumDistance, // ([m], [m])
+
+ DragStartDistance, // [m]
+ DragStartDirectionErrorMargin, // [] dx/dy
+
+ MinimumVelocity, // [m/s]
+ MaximumVelocity, // [m/s]
+ MaximumNonAcceleratedVelocity, // [m/s]
+
+ MaximumClickThroughVelocity, // [m/s]
+ //fastVelocityFactor????
+
+ AxisLockThreshold, // [0..1] atan(|min(dx,dy)|/|max(dx,dy)|)
+
+ FramesPerMilliSecond, // [frames/s]
+
+ FastSwipeMaximumTimeBetweenReleases, // [s]
+ FastSwipeMinimumVelocity, // [m/s]
+ FastSwipeAccelerationFactor, // [m/s^2]
+ };
+
+ QVariant scrollMetric(ScrollMetric metric) const;
+ void setScrollMetric(ScrollMetric metric, const QVariant &value);
+
+protected:
+ virtual QSizeF viewportSize() const = 0;
+ virtual QPointF maximumContentPosition() const = 0;
+ virtual QPointF contentPosition() const = 0;
+ virtual void setContentPosition(const QPointF &pos, const QPointF &overshootDelta) = 0;
+
+ virtual void stateChanged(State oldState);
+ virtual bool canStartScrollingAt(const QPointF &pos) const;
+ virtual void cancelPress(const QPointF &pressPos);
+
+ enum Input {
+ InputPress,
+ InputMove,
+ InputRelease
+ };
+
+ bool handleInput(Input input, const QPointF &position, qint64 timestamp)
+
+ QKineticScroller(QKineticScrollerPrivate &dd);
+ QScopedPointer<QKineticScrollerPrivate> d_ptr;
+
+private:
+ Q_DISABLE_COPY(QKineticScroller)
+ Q_DECLARE_PRIVATE(QKineticScroller)
+
+};
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QKineticScroller *)
+
+QT_END_HEADER
+
+#endif // QKINETICSCROLLER_H
diff --git a/qkineticscroller_p.h b/qkineticscroller_p.h
new file mode 100644
index 0000000..8b4b99b
--- /dev/null
+++ b/qkineticscroller_p.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QTime>
+#include <QPointer>
+#include <QObject>
+#include <QKineticScroller>
+#include <QEvent>
+
+QT_BEGIN_NAMESPACE
+
+class QKineticScrollerPrivate : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PUBLIC(QKineticScroller)
+
+public:
+ QKineticScrollerPrivate();
+ virtual ~QKineticScrollerPrivate();
+ void init();
+
+ void setState(QKineticScroller::State s);
+
+ bool pressWhileInactive(QKineticScroller::Input input, const QPointF &position, qint64 timestamp);
+ bool moveWhilePressed(QKineticScroller::Input input, const QPointF &position, qint64 timestamp);
+ bool releaseWhilePressed(QKineticScroller::Input input, const QPointF &position, qint64 timestamp);
+ bool moveWhileDragging(QKineticScroller::Input input, const QPointF &position, qint64 timestamp);
+ bool releaseWhileDragging(QKineticScroller::Input input, const QPointF &position, qint64 timestamp);
+ bool pressWhileScrolling(QKineticScroller::Input input, const QPointF &position, qint64 timestamp);
+
+ void timerEvent(QTimerEvent *);
+ void timerEventWhileDragging(qint64 timestamp);
+ void timerEventWhileScrolling(qint64 timestamp);
+ void timerEventWhileBackshooting(qint64 timestamp);
+
+ void handleDrag(const QPointF &position, qint64 timestamp);
+ void setScrollPositionHelper(const QPoint &position);
+
+ // metrics
+
+ qreal dragVelocitySmoothingFactor;
+ qreal frictionCoefficent;
+ qreal overshootFricitionCoefficient;
+ qreal overshootSpringConstant;
+ QPointF overshootMaximumDistance;
+ qreal dragStartDistance;
+ qreal dragStartDirectionErrorMargin;
+ qreal maximumVelocity;
+ qreal minimumVelocity;
+ qreal maximumNonAcceleratedVelocity;
+ qreal maximumClickThroughVelocity;
+ qreal axisLockThreshold;
+ int framesPerMillisecond;
+
+ // state
+
+ bool enabled;
+ QKineticScroller::State state;
+ QPointF velocity;
+ QPointF oldVelocity;
+
+ QPointF pressPosition;
+ QPointF lastPosition;
+ qint64 lastTimestamp;
+
+ QPointF dragDistance;
+ QPointF overshootDistance;
+
+ int timerId;
+};
+
+QT_END_NAMESPACE