Search
SailfishOS Open Build Service
>
Projects
>
home:kaltsi
:
tnhlbug
>
qt
> 0015-Backport-PathView-snapMode-dragging-and-maximumFlick.patch
Log In
Username
Password
Cancel
Overview
Repositories
Revisions
Requests
Users
Advanced
Attributes
Meta
File 0015-Backport-PathView-snapMode-dragging-and-maximumFlick.patch of Package qt
From 6d9ef31bc9754f9734208fcfcd7303b7d4d2936d Mon Sep 17 00:00:00 2001 From: Bea Lam <bea.lam@qinetic.com.au> Date: Fri, 2 Nov 2012 13:37:45 +1000 Subject: [PATCH 15/29] Backport PathView snapMode, dragging and maximumFlickVelocity Backport PathView snapMode, maximumFlickVelocity and dragging properties to QtQuick 1.2. --- .../graphicsitems/qdeclarativeitemsmodule.cpp | 1 + .../graphicsitems/qdeclarativepathview.cpp | 245 ++++++++++++++++---- .../graphicsitems/qdeclarativepathview_p.h | 17 ++ .../graphicsitems/qdeclarativepathview_p_p.h | 24 +- 4 files changed, 238 insertions(+), 49 deletions(-) diff --git a/src/declarative/graphicsitems/qdeclarativeitemsmodule.cpp b/src/declarative/graphicsitems/qdeclarativeitemsmodule.cpp index e5254a0..f0d950d 100644 --- a/src/declarative/graphicsitems/qdeclarativeitemsmodule.cpp +++ b/src/declarative/graphicsitems/qdeclarativeitemsmodule.cpp @@ -202,6 +202,7 @@ void QDeclarativeItemModule::defineModule() // QtQuick 1.2 items qmlRegisterType<QDeclarativeFlickable,2>("QtQuick",1,2,"Flickable"); + qmlRegisterType<QDeclarativePathView,2>("QtQuick",1,2,"PathView"); #ifndef QT_NO_IMPORT_QT47_QML #ifdef QT_NO_MOVIE diff --git a/src/declarative/graphicsitems/qdeclarativepathview.cpp b/src/declarative/graphicsitems/qdeclarativepathview.cpp index 20dc0b7..a5c7534 100644 --- a/src/declarative/graphicsitems/qdeclarativepathview.cpp +++ b/src/declarative/graphicsitems/qdeclarativepathview.cpp @@ -52,8 +52,27 @@ #include <qmath.h> #include <math.h> +// The number of samples to use in calculating the velocity of a flick +#ifndef QML_FLICK_SAMPLEBUFFER +#define QML_FLICK_SAMPLEBUFFER 1 +#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 0 +#endif + +// The default maximum velocity of a flick. +#ifndef QML_FLICK_DEFAULTMAXVELOCITY +#define QML_FLICK_DEFAULTMAXVELOCITY 2500 +#endif + + QT_BEGIN_NAMESPACE +const qreal MinimumFlickVelocity = 75.0; + inline qreal qmlMod(qreal x, qreal y) { #ifdef QT_USE_MATH_H_FLOATS @@ -94,12 +113,13 @@ void QDeclarativePathViewAttached::setValue(const QByteArray &name, const QVaria void QDeclarativePathViewPrivate::init() { Q_Q(QDeclarativePathView); + maximumFlickVelocity = QML_FLICK_DEFAULTMAXVELOCITY; offset = 0; q->setAcceptedMouseButtons(Qt::LeftButton); q->setFlag(QGraphicsItem::ItemIsFocusScope); q->setFiltersChildEvents(true); q->connect(&tl, SIGNAL(updated()), q, SLOT(ticked())); - lastPosTime.invalidate(); + timer.invalidate(); static int timelineCompletedIdx = -1; static int movementEndingIdx = -1; if (timelineCompletedIdx == -1) { @@ -178,7 +198,8 @@ qreal QDeclarativePathViewPrivate::positionOfIndex(qreal index) const if (model && index >= 0 && index < modelCount) { qreal start = 0.0; - if (haveHighlightRange && highlightRangeMode != QDeclarativePathView::NoHighlightRange) + if (haveHighlightRange && (highlightRangeMode != QDeclarativePathView::NoHighlightRange + || snapMode != QDeclarativePathView::NoSnap)) start = highlightRangeStart; qreal globalPos = index + offset; globalPos = qmlMod(globalPos, qreal(modelCount)) / modelCount; @@ -345,6 +366,21 @@ void QDeclarativePathViewPrivate::regenerate() q->refill(); } +void QDeclarativePathViewPrivate::setDragging(bool d) +{ + Q_Q(QDeclarativePathView); + if (dragging == d) + return; + + dragging = d; + if (dragging) + emit q->dragStarted(); + else + emit q->dragEnded(); + + emit q->draggingChanged(); +} + /*! \qmlclass PathView QDeclarativePathView \ingroup qml-view-elements @@ -607,7 +643,7 @@ void QDeclarativePathView::setCurrentIndex(int idx) d->currentIndex = idx; if (d->modelCount) { if (d->haveHighlightRange && d->highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) - d->snapToCurrent(); + d->snapToIndex(d->currentIndex); int itemIndex = (idx - d->firstIndex + d->modelCount) % d->modelCount; if (itemIndex < d->items.count()) { d->currentItem = d->items.at(itemIndex); @@ -797,7 +833,7 @@ void QDeclarativePathView::setPreferredHighlightBegin(qreal start) if (d->highlightRangeStart == start || start < 0 || start > 1.0) return; d->highlightRangeStart = start; - d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + d->haveHighlightRange = d->highlightRangeStart <= d->highlightRangeEnd; refill(); emit preferredHighlightBeginChanged(); } @@ -814,7 +850,7 @@ void QDeclarativePathView::setPreferredHighlightEnd(qreal end) if (d->highlightRangeEnd == end || end < 0 || end > 1.0) return; d->highlightRangeEnd = end; - d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + d->haveHighlightRange = d->highlightRangeStart <= d->highlightRangeEnd; refill(); emit preferredHighlightEndChanged(); } @@ -905,6 +941,27 @@ void QDeclarativePathView::setFlickDeceleration(qreal dec) } /*! + \qmlproperty real PathView::maximumFlickVelocity + This property holds the approximate maximum velocity that the user can flick the view in pixels/second. + + The default value is platform dependent. +*/ +qreal QDeclarativePathView::maximumFlickVelocity() const +{ + Q_D(const QDeclarativePathView); + return d->maximumFlickVelocity; +} + +void QDeclarativePathView::setMaximumFlickVelocity(qreal vel) +{ + Q_D(QDeclarativePathView); + if (vel == d->maximumFlickVelocity) + return; + d->maximumFlickVelocity = vel; + emit maximumFlickVelocityChanged(); +} + +/*! \qmlproperty bool PathView::interactive A user cannot drag or flick a PathView that is not interactive. @@ -954,6 +1011,18 @@ bool QDeclarativePathView::isFlicking() const } /*! + \qmlproperty bool PathView::dragging + + This property holds whether the view is currently moving + due to the user dragging the view. +*/ +bool QDeclarativePathView::isDragging() const +{ + Q_D(const QDeclarativePathView); + return d->dragging; +} + +/*! \qmlsignal PathView::onMovementStarted() This handler is called when the view begins moving due to user @@ -985,6 +1054,22 @@ bool QDeclarativePathView::isFlicking() const */ /*! + \qmlsignal PathView::onDragStarted() + + This handler is called when the view starts to be dragged due to user + interaction. +*/ + +/*! + \qmlsignal PathView::onDragEnded() + + This handler is called when the user stops dragging the view. + + If the velocity of the drag is suffient at the time the + touch/mouse button is released then a flick will start. +*/ + +/*! \qmlproperty Component PathView::delegate The delegate provides a template defining each item instantiated by the view. @@ -1058,6 +1143,41 @@ void QDeclarativePathView::setPathItemCount(int i) emit pathItemCountChanged(); } +/*! + \qmlproperty enumeration PathView::snapMode + + This property determines how the items will settle following a drag or flick. + The possible values are: + + \list + \li PathView.NoSnap (default) - the items stop anywhere along the path. + \li PathView.SnapToItem - the items settle with an item aligned with the \l preferredHighlightBegin. + \li PathView.SnapOneItem - the items settle no more than one item away from the item nearest + \l preferredHighlightBegin at the time the press is released. This mode is particularly + useful for moving one page at a time. + \endlist + + \c snapMode does not affect the \l currentIndex. To update the + \l currentIndex as the view is moved, set \l highlightRangeMode + to \c PathView.StrictlyEnforceRange (default for PathView). + + \sa highlightRangeMode +*/ +QDeclarativePathView::SnapMode QDeclarativePathView::snapMode() const +{ + Q_D(const QDeclarativePathView); + return d->snapMode; +} + +void QDeclarativePathView::setSnapMode(SnapMode mode) +{ + Q_D(QDeclarativePathView); + if (mode == d->snapMode) + return; + d->snapMode = mode; + emit snapModeChanged(); +} + QPointF QDeclarativePathViewPrivate::pointNear(const QPointF &point, qreal *nearPercent) const { //XXX maybe do recursively at increasing resolution. @@ -1081,6 +1201,27 @@ QPointF QDeclarativePathViewPrivate::pointNear(const QPointF &point, qreal *near return nearPoint; } +void QDeclarativePathViewPrivate::addVelocitySample(qreal v) +{ + velocityBuffer.append(v); + if (velocityBuffer.count() > QML_FLICK_SAMPLEBUFFER) + velocityBuffer.remove(0); +} + +qreal QDeclarativePathViewPrivate::calcVelocity() const +{ + qreal velocity = 0; + if (velocityBuffer.count() > QML_FLICK_DISCARDSAMPLES) { + int count = velocityBuffer.count()-QML_FLICK_DISCARDSAMPLES; + for (int i = 0; i < count; ++i) { + qreal v = velocityBuffer.at(i); + velocity += v; + } + velocity /= count; + } + return velocity; +} + void QDeclarativePathView::mousePressEvent(QGraphicsSceneMouseEvent *event) { Q_D(QDeclarativePathView); @@ -1095,8 +1236,10 @@ void QDeclarativePathView::mousePressEvent(QGraphicsSceneMouseEvent *event) void QDeclarativePathViewPrivate::handleMousePressEvent(QGraphicsSceneMouseEvent *event) { Q_Q(QDeclarativePathView); - if (!interactive || !items.count()) + if (!interactive || !items.count() || !model || !modelCount) return; + velocityBuffer.clear(); + QPointF scenePoint = q->mapToScene(event->pos()); int idx = 0; for (; idx < items.count(); ++idx) { @@ -1120,9 +1263,8 @@ void QDeclarativePathViewPrivate::handleMousePressEvent(QGraphicsSceneMouseEvent else stealMouse = false; - lastElapsed = 0; - lastDist = 0; - QDeclarativeItemPrivate::start(lastPosTime); + QDeclarativeItemPrivate::start(timer); + lastPosTimer.start(); tl.clear(); } @@ -1142,16 +1284,16 @@ void QDeclarativePathView::mouseMoveEvent(QGraphicsSceneMouseEvent *event) void QDeclarativePathViewPrivate::handleMouseMoveEvent(QGraphicsSceneMouseEvent *event) { Q_Q(QDeclarativePathView); - if (!interactive || !lastPosTime.isValid()) + if (!interactive || !timer.isValid() || !model || !modelCount) return; + qint64 elapsed = lastPosTimer.restart(); qreal newPc; QPointF pathPoint = pointNear(event->pos(), &newPc); if (!stealMouse) { QPointF delta = pathPoint - startPoint; if (qAbs(delta.x()) > QApplication::startDragDistance() || qAbs(delta.y()) > QApplication::startDragDistance()) { stealMouse = true; - startPc = newPc; } } @@ -1166,16 +1308,17 @@ void QDeclarativePathViewPrivate::handleMouseMoveEvent(QGraphicsSceneMouseEvent else if (diff < -modelCount/2) diff += modelCount; - lastElapsed = QDeclarativeItemPrivate::restart(lastPosTime); - lastDist = diff; - startPc = newPc; + if (elapsed > 0) + addVelocitySample(diff / (qreal(elapsed) / 1000.)); } if (!moving) { moving = true; emit q->movingChanged(); emit q->movementStarted(); } + setDragging(true); } + startPc = newPc; } void QDeclarativePathView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) @@ -1195,33 +1338,54 @@ void QDeclarativePathViewPrivate::handleMouseReleaseEvent(QGraphicsSceneMouseEve Q_Q(QDeclarativePathView); stealMouse = false; q->setKeepMouseGrab(false); - if (!interactive || !lastPosTime.isValid()) + setDragging(false); + if (!interactive || !timer.isValid() || !model || !modelCount) { + timer.invalidate(); + if (!tl.isActive()) + q->movementEnding(); return; + } - qreal elapsed = qreal(lastElapsed + QDeclarativeItemPrivate::elapsed(lastPosTime)) / 1000.; - qreal velocity = elapsed > 0. ? lastDist / elapsed : 0; - if (model && modelCount && qAbs(velocity) > qreal(1.)) { - qreal count = pathItems == -1 ? modelCount : pathItems; - if (qAbs(velocity) > count * 2) // limit velocity - velocity = (velocity > 0 ? count : -count) * 2; + qreal velocity = calcVelocity(); + qreal count = pathItems == -1 ? modelCount : qMin(pathItems, modelCount); + qreal pixelVelocity = (path->path().length()/count) * velocity; + if (qAbs(pixelVelocity) > MinimumFlickVelocity) { + if (qAbs(pixelVelocity) > maximumFlickVelocity || snapMode == QDeclarativePathView::SnapOneItem) { + // limit velocity + qreal maxVel = velocity < 0 ? -maximumFlickVelocity : maximumFlickVelocity; + velocity = maxVel / (path->path().length()/count); + } // Calculate the distance to be travelled qreal v2 = velocity*velocity; qreal accel = deceleration/10; - // + 0.25 to encourage moving at least one item in the flick direction - qreal dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * qreal(2.0)) + qreal(0.25))); - if (haveHighlightRange && highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) { - // round to nearest item. - if (velocity > 0.) - dist = qRound(dist + offset) - offset; - else - dist = qRound(dist - offset) + offset; + qreal dist = 0; + if (haveHighlightRange && (highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange + || snapMode != QDeclarativePathView::NoSnap)) { + if (snapMode == QDeclarativePathView::SnapOneItem) { + // encourage snapping one item in direction of motion + if (velocity > 0.) + dist = qRound(0.5 + offset) - offset; + else + dist = qRound(0.5 - offset) + offset; + } else { + // + 0.25 to encourage moving at least one item in the flick direction + dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2.0) + 0.25)); + + // round to nearest item. + if (velocity > 0.) + dist = qRound(dist + offset) - offset; + else + dist = qRound(dist - offset) + offset; + } // Calculate accel required to stop on item boundary if (dist <= 0.) { - dist = qreal(0.); - accel = qreal(0.); + dist = 0.; + accel = 0.; } else { accel = v2 / (2.0f * qAbs(dist)); } + } else { + dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2.0))); } offsetAdj = qreal(0.0); moveOffset.setValue(offset); @@ -1236,7 +1400,7 @@ void QDeclarativePathViewPrivate::handleMouseReleaseEvent(QGraphicsSceneMouseEve fixOffset(); } - lastPosTime.invalidate(); + timer.invalidate(); if (!tl.isActive()) q->movementEnding(); } @@ -1281,8 +1445,8 @@ bool QDeclarativePathView::sendMouseEvent(QGraphicsSceneMouseEvent *event) grabMouse(); return d->stealMouse; - } else if (d->lastPosTime.isValid()) { - d->lastPosTime.invalidate(); + } else if (d->timer.isValid()) { + d->timer.invalidate(); } if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease) d->stealMouse = false; @@ -1531,7 +1695,7 @@ void QDeclarativePathView::itemsRemoved(int modelIndex, int count) d->regenerate(); d->updateCurrent(); if (!d->flicking && !d->moving && d->haveHighlightRange && d->highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) - d->snapToCurrent(); + d->snapToIndex(d->currentIndex); } if (changedOffset) emit offsetChanged(); @@ -1672,22 +1836,23 @@ void QDeclarativePathViewPrivate::fixOffset() { Q_Q(QDeclarativePathView); if (model && items.count()) { - if (haveHighlightRange && highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) { + if (haveHighlightRange && (highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange + || snapMode != QDeclarativePathView::NoSnap)) { int curr = calcCurrentIndex(); - if (curr != currentIndex) + if (curr != currentIndex && highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) q->setCurrentIndex(curr); else - snapToCurrent(); + snapToIndex(curr); } } } -void QDeclarativePathViewPrivate::snapToCurrent() +void QDeclarativePathViewPrivate::snapToIndex(int index) { if (!model || modelCount <= 0) return; - qreal targetOffset = qmlMod(modelCount - currentIndex, modelCount); + qreal targetOffset = qmlMod(modelCount - index, modelCount); moveReason = Other; offsetAdj = 0.0; diff --git a/src/declarative/graphicsitems/qdeclarativepathview_p.h b/src/declarative/graphicsitems/qdeclarativepathview_p.h index 669a46d..e529b8e 100644 --- a/src/declarative/graphicsitems/qdeclarativepathview_p.h +++ b/src/declarative/graphicsitems/qdeclarativepathview_p.h @@ -71,17 +71,21 @@ class Q_AUTOTEST_EXPORT QDeclarativePathView : public QDeclarativeItem Q_PROPERTY(int highlightMoveDuration READ highlightMoveDuration WRITE setHighlightMoveDuration NOTIFY highlightMoveDurationChanged) Q_PROPERTY(qreal dragMargin READ dragMargin WRITE setDragMargin NOTIFY dragMarginChanged) + Q_PROPERTY(qreal maximumFlickVelocity READ maximumFlickVelocity WRITE setMaximumFlickVelocity NOTIFY maximumFlickVelocityChanged REVISION 2) Q_PROPERTY(qreal flickDeceleration READ flickDeceleration WRITE setFlickDeceleration NOTIFY flickDecelerationChanged) Q_PROPERTY(bool interactive READ isInteractive WRITE setInteractive NOTIFY interactiveChanged) Q_PROPERTY(bool moving READ isMoving NOTIFY movingChanged) Q_PROPERTY(bool flicking READ isFlicking NOTIFY flickingChanged) + Q_PROPERTY(bool dragging READ isDragging NOTIFY draggingChanged REVISION 2) Q_PROPERTY(int count READ count NOTIFY countChanged) Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) Q_PROPERTY(int pathItemCount READ pathItemCount WRITE setPathItemCount NOTIFY pathItemCountChanged) + Q_PROPERTY(SnapMode snapMode READ snapMode WRITE setSnapMode NOTIFY snapModeChanged REVISION 2) Q_ENUMS(HighlightRangeMode) + Q_ENUMS(SnapMode) public: QDeclarativePathView(QDeclarativeItem *parent=0); @@ -122,11 +126,15 @@ public: qreal flickDeceleration() const; void setFlickDeceleration(qreal dec); + qreal maximumFlickVelocity() const; + void setMaximumFlickVelocity(qreal); + bool isInteractive() const; void setInteractive(bool); bool isMoving() const; bool isFlicking() const; + bool isDragging() const; int count() const; @@ -136,6 +144,10 @@ public: int pathItemCount() const; void setPathItemCount(int); + enum SnapMode { NoSnap, SnapToItem, SnapOneItem }; + SnapMode snapMode() const; + void setSnapMode(SnapMode mode); + static QDeclarativePathViewAttached *qmlAttachedProperties(QObject *); public Q_SLOTS: @@ -155,10 +167,12 @@ Q_SIGNALS: void snapPositionChanged(); void delegateChanged(); void pathItemCountChanged(); + void maximumFlickVelocityChanged(); void flickDecelerationChanged(); void interactiveChanged(); void movingChanged(); void flickingChanged(); + void draggingChanged(); void highlightChanged(); void highlightItemChanged(); void highlightMoveDurationChanged(); @@ -166,6 +180,9 @@ Q_SIGNALS: void movementEnded(); void flickStarted(); void flickEnded(); + void dragStarted(); + void dragEnded(); + void snapModeChanged(); protected: void mousePressEvent(QGraphicsSceneMouseEvent *event); diff --git a/src/declarative/graphicsitems/qdeclarativepathview_p_p.h b/src/declarative/graphicsitems/qdeclarativepathview_p_p.h index bd042c1..e7db085 100644 --- a/src/declarative/graphicsitems/qdeclarativepathview_p_p.h +++ b/src/declarative/graphicsitems/qdeclarativepathview_p_p.h @@ -74,12 +74,12 @@ class QDeclarativePathViewPrivate : public QDeclarativeItemPrivate, public QDecl public: QDeclarativePathViewPrivate() - : path(0), currentIndex(0), currentItemOffset(0.0), startPc(0), lastDist(0) - , lastElapsed(0), offset(0.0), offsetAdj(0.0), mappedRange(1.0) + : path(0), currentIndex(0), currentItemOffset(0.0), startPc(0) + , offset(0.0), offsetAdj(0.0), mappedRange(1.0) , stealMouse(false), ownModel(false), interactive(true), haveHighlightRange(true) , autoHighlight(true), highlightUp(false), layoutScheduled(false) - , moving(false), flicking(false) - , dragMargin(0), deceleration(100) + , moving(false), flicking(false), dragging(false) + , dragMargin(0), deceleration(100), maximumFlickVelocity(0) , moveOffset(this, &QDeclarativePathViewPrivate::setAdjustedOffset) , firstIndex(-1), pathItems(-1), requestedIndex(-1) , moveReason(Other), moveDirection(Shortest), attType(0), highlightComponent(0), highlightItem(0) @@ -87,7 +87,7 @@ public: , highlightPosition(0) , highlightRangeStart(0), highlightRangeEnd(0) , highlightRangeMode(QDeclarativePathView::StrictlyEnforceRange) - , highlightMoveDuration(300), modelCount(0) + , highlightMoveDuration(300), modelCount(0), snapMode(QDeclarativePathView::NoSnap) { } @@ -135,8 +135,11 @@ public: void setAdjustedOffset(qreal offset); void regenerate(); void updateItem(QDeclarativeItem *, qreal); - void snapToCurrent(); + void snapToIndex(int index); QPointF pointNear(const QPointF &point, qreal *nearPercent=0) const; + void addVelocitySample(qreal v); + qreal calcVelocity() const; + void setDragging(bool d); QDeclarativePath *path; int currentIndex; @@ -144,8 +147,6 @@ public: qreal currentItemOffset; qreal startPc; QPointF startPoint; - qreal lastDist; - int lastElapsed; qreal offset; qreal offsetAdj; qreal mappedRange; @@ -158,10 +159,13 @@ public: bool layoutScheduled : 1; bool moving : 1; bool flicking : 1; - QElapsedTimer lastPosTime; + bool dragging : 1; + QElapsedTimer timer; + QElapsedTimer lastPosTimer; QPointF lastPos; qreal dragMargin; qreal deceleration; + qreal maximumFlickVelocity; QDeclarativeTimeLine tl; QDeclarativeTimeLineValueProxy<QDeclarativePathViewPrivate> moveOffset; int firstIndex; @@ -185,6 +189,8 @@ public: QDeclarativePathView::HighlightRangeMode highlightRangeMode; int highlightMoveDuration; int modelCount; + QPODVector<qreal,10> velocityBuffer; + QDeclarativePathView::SnapMode snapMode; }; QT_END_NAMESPACE -- 1.7.10.3