Initial import from qtquick2.
[profile/ivi/qtdeclarative.git] / src / declarative / items / qsgflickable.cpp
1 // Commit: ee767e8c16742316068e83323374ea54f2b939cb
2 /****************************************************************************
3 **
4 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
5 ** All rights reserved.
6 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 **
8 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 **
10 ** $QT_BEGIN_LICENSE:LGPL$
11 ** No Commercial Usage
12 ** This file contains pre-release code and may not be distributed.
13 ** You may use this file in accordance with the terms and conditions
14 ** contained in the Technology Preview License Agreement accompanying
15 ** this package.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights.  These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 **
39 ** $QT_END_LICENSE$
40 **
41 ****************************************************************************/
42
43 #include "qsgflickable_p.h"
44 #include "qsgflickable_p_p.h"
45 #include "qsgcanvas.h"
46 #include "qsgcanvas_p.h"
47
48 #include <QtDeclarative/qdeclarativeinfo.h>
49 #include <QtGui/qgraphicssceneevent.h>
50 #include <QtGui/qapplication.h>
51
52 QT_BEGIN_NAMESPACE
53
54 // FlickThreshold determines how far the "mouse" must have moved
55 // before we perform a flick.
56 static const int FlickThreshold = 20;
57
58 // RetainGrabVelocity is the maxmimum instantaneous velocity that
59 // will ensure the Flickable retains the grab on consecutive flicks.
60 static const int RetainGrabVelocity = 15;
61
62 QSGFlickableVisibleArea::QSGFlickableVisibleArea(QSGFlickable *parent)
63     : QObject(parent), flickable(parent), m_xPosition(0.), m_widthRatio(0.)
64     , m_yPosition(0.), m_heightRatio(0.)
65 {
66 }
67
68 qreal QSGFlickableVisibleArea::widthRatio() const
69 {
70     return m_widthRatio;
71 }
72
73 qreal QSGFlickableVisibleArea::xPosition() const
74 {
75     return m_xPosition;
76 }
77
78 qreal QSGFlickableVisibleArea::heightRatio() const
79 {
80     return m_heightRatio;
81 }
82
83 qreal QSGFlickableVisibleArea::yPosition() const
84 {
85     return m_yPosition;
86 }
87
88 void QSGFlickableVisibleArea::updateVisible()
89 {
90     QSGFlickablePrivate *p = QSGFlickablePrivate::get(flickable);
91
92     bool changeX = false;
93     bool changeY = false;
94     bool changeWidth = false;
95     bool changeHeight = false;
96
97     // Vertical
98     const qreal viewheight = flickable->height();
99     const qreal maxyextent = -flickable->maxYExtent() + flickable->minYExtent();
100     qreal pagePos = (-p->vData.move.value() + flickable->minYExtent()) / (maxyextent + viewheight);
101     qreal pageSize = viewheight / (maxyextent + viewheight);
102
103     if (pageSize != m_heightRatio) {
104         m_heightRatio = pageSize;
105         changeHeight = true;
106     }
107     if (pagePos != m_yPosition) {
108         m_yPosition = pagePos;
109         changeY = true;
110     }
111
112     // Horizontal
113     const qreal viewwidth = flickable->width();
114     const qreal maxxextent = -flickable->maxXExtent() + flickable->minXExtent();
115     pagePos = (-p->hData.move.value() + flickable->minXExtent()) / (maxxextent + viewwidth);
116     pageSize = viewwidth / (maxxextent + viewwidth);
117
118     if (pageSize != m_widthRatio) {
119         m_widthRatio = pageSize;
120         changeWidth = true;
121     }
122     if (pagePos != m_xPosition) {
123         m_xPosition = pagePos;
124         changeX = true;
125     }
126
127     if (changeX)
128         emit xPositionChanged(m_xPosition);
129     if (changeY)
130         emit yPositionChanged(m_yPosition);
131     if (changeWidth)
132         emit widthRatioChanged(m_widthRatio);
133     if (changeHeight)
134         emit heightRatioChanged(m_heightRatio);
135 }
136
137
138 QSGFlickablePrivate::QSGFlickablePrivate()
139   : contentItem(new QSGItem)
140     , hData(this, &QSGFlickablePrivate::setRoundedViewportX)
141     , vData(this, &QSGFlickablePrivate::setRoundedViewportY)
142     , flickingHorizontally(false), flickingVertically(false)
143     , hMoved(false), vMoved(false)
144     , movingHorizontally(false), movingVertically(false)
145     , stealMouse(false), pressed(false), interactive(true), calcVelocity(false)
146     , deceleration(500), maxVelocity(2000), reportedVelocitySmoothing(100)
147     , delayedPressEvent(0), delayedPressTarget(0), pressDelay(0), fixupDuration(600)
148     , fixupMode(Normal), vTime(0), visibleArea(0)
149     , flickableDirection(QSGFlickable::AutoFlickDirection)
150     , boundsBehavior(QSGFlickable::DragAndOvershootBounds)
151 {
152 }
153
154 void QSGFlickablePrivate::init()
155 {
156     Q_Q(QSGFlickable);
157     QDeclarative_setParent_noEvent(contentItem, q);
158     contentItem->setParentItem(q);
159     static int timelineUpdatedIdx = -1;
160     static int timelineCompletedIdx = -1;
161     static int flickableTickedIdx = -1;
162     static int flickableMovementEndingIdx = -1;
163     if (timelineUpdatedIdx == -1) {
164         timelineUpdatedIdx = QDeclarativeTimeLine::staticMetaObject.indexOfSignal("updated()");
165         timelineCompletedIdx = QDeclarativeTimeLine::staticMetaObject.indexOfSignal("completed()");
166         flickableTickedIdx = QSGFlickable::staticMetaObject.indexOfSlot("ticked()");
167         flickableMovementEndingIdx = QSGFlickable::staticMetaObject.indexOfSlot("movementEnding()");
168     }
169     QMetaObject::connect(&timeline, timelineUpdatedIdx,
170                          q, flickableTickedIdx, Qt::DirectConnection);
171     QMetaObject::connect(&timeline, timelineCompletedIdx,
172                          q, flickableMovementEndingIdx, Qt::DirectConnection);
173     q->setAcceptedMouseButtons(Qt::LeftButton);
174     q->setFiltersChildMouseEvents(true);
175     QSGItemPrivate *viewportPrivate = QSGItemPrivate::get(contentItem);
176     viewportPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
177     lastPosTime.invalidate();
178 }
179
180 /*
181     Returns the amount to overshoot by given a velocity.
182     Will be roughly in range 0 - size/4
183 */
184 qreal QSGFlickablePrivate::overShootDistance(qreal velocity, qreal size)
185 {
186     if (maxVelocity <= 0)
187         return 0.0;
188
189     velocity = qAbs(velocity);
190     if (velocity > maxVelocity)
191         velocity = maxVelocity;
192     qreal dist = size / 4 * velocity / maxVelocity;
193     return dist;
194 }
195
196 void QSGFlickablePrivate::itemGeometryChanged(QSGItem *item, const QRectF &newGeom, const QRectF &oldGeom)
197 {
198     Q_Q(QSGFlickable);
199     if (item == contentItem) {
200         if (newGeom.x() != oldGeom.x())
201             emit q->contentXChanged();
202         if (newGeom.y() != oldGeom.y())
203             emit q->contentYChanged();
204     }
205 }
206
207 void QSGFlickablePrivate::flickX(qreal velocity)
208 {
209     Q_Q(QSGFlickable);
210     flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, velocity);
211 }
212
213 void QSGFlickablePrivate::flickY(qreal velocity)
214 {
215     Q_Q(QSGFlickable);
216     flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, velocity);
217 }
218
219 void QSGFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
220                                          QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
221 {
222     Q_Q(QSGFlickable);
223     qreal maxDistance = -1;
224     data.fixingUp = false;
225     bool overShoot = boundsBehavior == QSGFlickable::DragAndOvershootBounds;
226     // -ve velocity means list is moving up
227     if (velocity > 0) {
228         if (data.move.value() < minExtent)
229             maxDistance = qAbs(minExtent - data.move.value() + (overShoot?overShootDistance(velocity,vSize):0));
230         data.flickTarget = minExtent;
231     } else {
232         if (data.move.value() > maxExtent)
233             maxDistance = qAbs(maxExtent - data.move.value()) + (overShoot?overShootDistance(velocity,vSize):0);
234         data.flickTarget = maxExtent;
235     }
236     if (maxDistance > 0) {
237         qreal v = velocity;
238         if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
239             if (v < 0)
240                 v = -maxVelocity;
241             else
242                 v = maxVelocity;
243         }
244         timeline.reset(data.move);
245         timeline.accel(data.move, v, deceleration, maxDistance);
246         timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
247         if (!flickingHorizontally && q->xflick()) {
248             flickingHorizontally = true;
249             emit q->flickingChanged();
250             emit q->flickingHorizontallyChanged();
251             if (!flickingVertically)
252                 emit q->flickStarted();
253         }
254         if (!flickingVertically && q->yflick()) {
255             flickingVertically = true;
256             emit q->flickingChanged();
257             emit q->flickingVerticallyChanged();
258             if (!flickingHorizontally)
259                 emit q->flickStarted();
260         }
261     } else {
262         timeline.reset(data.move);
263         fixup(data, minExtent, maxExtent);
264     }
265 }
266
267 void QSGFlickablePrivate::fixupY_callback(void *data)
268 {
269     ((QSGFlickablePrivate *)data)->fixupY();
270 }
271
272 void QSGFlickablePrivate::fixupX_callback(void *data)
273 {
274     ((QSGFlickablePrivate *)data)->fixupX();
275 }
276
277 void QSGFlickablePrivate::fixupX()
278 {
279     Q_Q(QSGFlickable);
280     fixup(hData, q->minXExtent(), q->maxXExtent());
281 }
282
283 void QSGFlickablePrivate::fixupY()
284 {
285     Q_Q(QSGFlickable);
286     fixup(vData, q->minYExtent(), q->maxYExtent());
287 }
288
289 void QSGFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
290 {
291     if (data.move.value() > minExtent || maxExtent > minExtent) {
292         timeline.reset(data.move);
293         if (data.move.value() != minExtent) {
294             switch (fixupMode) {
295             case Immediate:
296                 timeline.set(data.move, minExtent);
297                 break;
298             case ExtentChanged:
299                 // The target has changed. Don't start from the beginning; just complete the
300                 // second half of the animation using the new extent.
301                 timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
302                 data.fixingUp = true;
303                 break;
304             default: {
305                     qreal dist = minExtent - data.move;
306                     timeline.move(data.move, minExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4);
307                     timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
308                     data.fixingUp = true;
309                 }
310             }
311         }
312     } else if (data.move.value() < maxExtent) {
313         timeline.reset(data.move);
314         switch (fixupMode) {
315         case Immediate:
316             timeline.set(data.move, maxExtent);
317             break;
318         case ExtentChanged:
319             // The target has changed. Don't start from the beginning; just complete the
320             // second half of the animation using the new extent.
321             timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
322             data.fixingUp = true;
323             break;
324         default: {
325                 qreal dist = maxExtent - data.move;
326                 timeline.move(data.move, maxExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4);
327                 timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
328                 data.fixingUp = true;
329             }
330         }
331     }
332     fixupMode = Normal;
333     vTime = timeline.time();
334 }
335
336 void QSGFlickablePrivate::updateBeginningEnd()
337 {
338     Q_Q(QSGFlickable);
339     bool atBoundaryChange = false;
340
341     // Vertical
342     const int maxyextent = int(-q->maxYExtent());
343     const qreal ypos = -vData.move.value();
344     bool atBeginning = (ypos <= -q->minYExtent());
345     bool atEnd = (maxyextent <= ypos);
346
347     if (atBeginning != vData.atBeginning) {
348         vData.atBeginning = atBeginning;
349         atBoundaryChange = true;
350     }
351     if (atEnd != vData.atEnd) {
352         vData.atEnd = atEnd;
353         atBoundaryChange = true;
354     }
355
356     // Horizontal
357     const int maxxextent = int(-q->maxXExtent());
358     const qreal xpos = -hData.move.value();
359     atBeginning = (xpos <= -q->minXExtent());
360     atEnd = (maxxextent <= xpos);
361
362     if (atBeginning != hData.atBeginning) {
363         hData.atBeginning = atBeginning;
364         atBoundaryChange = true;
365     }
366     if (atEnd != hData.atEnd) {
367         hData.atEnd = atEnd;
368         atBoundaryChange = true;
369     }
370
371     if (atBoundaryChange)
372         emit q->isAtBoundaryChanged();
373
374     if (visibleArea)
375         visibleArea->updateVisible();
376 }
377
378 QSGFlickable::QSGFlickable(QSGItem *parent)
379   : QSGItem(*(new QSGFlickablePrivate), parent)
380 {
381     Q_D(QSGFlickable);
382     d->init();
383 }
384
385 QSGFlickable::QSGFlickable(QSGFlickablePrivate &dd, QSGItem *parent)
386   : QSGItem(dd, parent)
387 {
388     Q_D(QSGFlickable);
389     d->init();
390 }
391
392 QSGFlickable::~QSGFlickable()
393 {
394 }
395
396 qreal QSGFlickable::contentX() const
397 {
398     Q_D(const QSGFlickable);
399     return -d->contentItem->x();
400 }
401
402 void QSGFlickable::setContentX(qreal pos)
403 {
404     Q_D(QSGFlickable);
405     d->timeline.reset(d->hData.move);
406     d->vTime = d->timeline.time();
407     movementXEnding();
408     if (-pos != d->hData.move.value()) {
409         d->hData.move.setValue(-pos);
410         viewportMoved();
411     }
412 }
413
414 qreal QSGFlickable::contentY() const
415 {
416     Q_D(const QSGFlickable);
417     return -d->contentItem->y();
418 }
419
420 void QSGFlickable::setContentY(qreal pos)
421 {
422     Q_D(QSGFlickable);
423     d->timeline.reset(d->vData.move);
424     d->vTime = d->timeline.time();
425     movementYEnding();
426     if (-pos != d->vData.move.value()) {
427         d->vData.move.setValue(-pos);
428         viewportMoved();
429     }
430 }
431
432 bool QSGFlickable::isInteractive() const
433 {
434     Q_D(const QSGFlickable);
435     return d->interactive;
436 }
437
438 void QSGFlickable::setInteractive(bool interactive)
439 {
440     Q_D(QSGFlickable);
441     if (interactive != d->interactive) {
442         d->interactive = interactive;
443         if (!interactive && (d->flickingHorizontally || d->flickingVertically)) {
444             d->timeline.clear();
445             d->vTime = d->timeline.time();
446             d->flickingHorizontally = false;
447             d->flickingVertically = false;
448             emit flickingChanged();
449             emit flickingHorizontallyChanged();
450             emit flickingVerticallyChanged();
451             emit flickEnded();
452         }
453         emit interactiveChanged();
454     }
455 }
456
457 qreal QSGFlickable::horizontalVelocity() const
458 {
459     Q_D(const QSGFlickable);
460     return d->hData.smoothVelocity.value();
461 }
462
463 qreal QSGFlickable::verticalVelocity() const
464 {
465     Q_D(const QSGFlickable);
466     return d->vData.smoothVelocity.value();
467 }
468
469 bool QSGFlickable::isAtXEnd() const
470 {
471     Q_D(const QSGFlickable);
472     return d->hData.atEnd;
473 }
474
475 bool QSGFlickable::isAtXBeginning() const
476 {
477     Q_D(const QSGFlickable);
478     return d->hData.atBeginning;
479 }
480
481 bool QSGFlickable::isAtYEnd() const
482 {
483     Q_D(const QSGFlickable);
484     return d->vData.atEnd;
485 }
486
487 bool QSGFlickable::isAtYBeginning() const
488 {
489     Q_D(const QSGFlickable);
490     return d->vData.atBeginning;
491 }
492
493 void QSGFlickable::ticked()
494 {
495     viewportMoved();
496 }
497
498 QSGItem *QSGFlickable::contentItem()
499 {
500     Q_D(QSGFlickable);
501     return d->contentItem;
502 }
503
504 QSGFlickableVisibleArea *QSGFlickable::visibleArea()
505 {
506     Q_D(QSGFlickable);
507     if (!d->visibleArea)
508         d->visibleArea = new QSGFlickableVisibleArea(this);
509     return d->visibleArea;
510 }
511
512 QSGFlickable::FlickableDirection QSGFlickable::flickableDirection() const
513 {
514     Q_D(const QSGFlickable);
515     return d->flickableDirection;
516 }
517
518 void QSGFlickable::setFlickableDirection(FlickableDirection direction)
519 {
520     Q_D(QSGFlickable);
521     if (direction != d->flickableDirection) {
522         d->flickableDirection = direction;
523         emit flickableDirectionChanged();
524     }
525 }
526
527 void QSGFlickablePrivate::handleMousePressEvent(QGraphicsSceneMouseEvent *event)
528 {
529     Q_Q(QSGFlickable);
530     if (interactive && timeline.isActive()
531         && (qAbs(hData.smoothVelocity.value()) > RetainGrabVelocity
532             || qAbs(vData.smoothVelocity.value()) > RetainGrabVelocity)) {
533         stealMouse = true; // If we've been flicked then steal the click.
534     } else {
535         stealMouse = false;
536     }
537     q->setKeepMouseGrab(stealMouse);
538     pressed = true;
539     timeline.clear();
540     hData.velocity = 0;
541     vData.velocity = 0;
542     hData.dragStartOffset = 0;
543     vData.dragStartOffset = 0;
544     hData.dragMinBound = q->minXExtent();
545     vData.dragMinBound = q->minYExtent();
546     hData.dragMaxBound = q->maxXExtent();
547     vData.dragMaxBound = q->maxYExtent();
548     hData.fixingUp = false;
549     vData.fixingUp = false;
550     lastPos = QPoint();
551     QSGItemPrivate::start(lastPosTime);
552     pressPos = event->pos();
553     hData.pressPos = hData.move.value();
554     vData.pressPos = vData.move.value();
555     flickingHorizontally = false;
556     flickingVertically = false;
557     QSGItemPrivate::start(pressTime);
558     QSGItemPrivate::start(velocityTime);
559 }
560
561 void QSGFlickablePrivate::handleMouseMoveEvent(QGraphicsSceneMouseEvent *event)
562 {
563     Q_Q(QSGFlickable);
564     if (!interactive || !lastPosTime.isValid())
565         return;
566     bool rejectY = false;
567     bool rejectX = false;
568
569     bool stealY = stealMouse;
570     bool stealX = stealMouse;
571
572     if (q->yflick()) {
573         int dy = int(event->pos().y() - pressPos.y());
574         if (qAbs(dy) > QApplication::startDragDistance() || QSGItemPrivate::elapsed(pressTime) > 200) {
575             if (!vMoved)
576                 vData.dragStartOffset = dy;
577             qreal newY = dy + vData.pressPos - vData.dragStartOffset;
578             const qreal minY = vData.dragMinBound;
579             const qreal maxY = vData.dragMaxBound;
580             if (newY > minY)
581                 newY = minY + (newY - minY) / 2;
582             if (newY < maxY && maxY - minY <= 0)
583                 newY = maxY + (newY - maxY) / 2;
584             if (boundsBehavior == QSGFlickable::StopAtBounds && (newY > minY || newY < maxY)) {
585                 rejectY = true;
586                 if (newY < maxY) {
587                     newY = maxY;
588                     rejectY = false;
589                 }
590                 if (newY > minY) {
591                     newY = minY;
592                     rejectY = false;
593                 }
594             }
595             if (!rejectY && stealMouse) {
596                 vData.move.setValue(qRound(newY));
597                 vMoved = true;
598             }
599             if (qAbs(dy) > QApplication::startDragDistance())
600                 stealY = true;
601         }
602     }
603
604     if (q->xflick()) {
605         int dx = int(event->pos().x() - pressPos.x());
606         if (qAbs(dx) > QApplication::startDragDistance() || QSGItemPrivate::elapsed(pressTime) > 200) {
607             if (!hMoved)
608                 hData.dragStartOffset = dx;
609             qreal newX = dx + hData.pressPos - hData.dragStartOffset;
610             const qreal minX = hData.dragMinBound;
611             const qreal maxX = hData.dragMaxBound;
612             if (newX > minX)
613                 newX = minX + (newX - minX) / 2;
614             if (newX < maxX && maxX - minX <= 0)
615                 newX = maxX + (newX - maxX) / 2;
616             if (boundsBehavior == QSGFlickable::StopAtBounds && (newX > minX || newX < maxX)) {
617                 rejectX = true;
618                 if (newX < maxX) {
619                     newX = maxX;
620                     rejectX = false;
621                 }
622                 if (newX > minX) {
623                     newX = minX;
624                     rejectX = false;
625                 }
626             }
627             if (!rejectX && stealMouse) {
628                 hData.move.setValue(qRound(newX));
629                 hMoved = true;
630             }
631
632             if (qAbs(dx) > QApplication::startDragDistance())
633                 stealX = true;
634         }
635     }
636
637     stealMouse = stealX || stealY;
638     if (stealMouse)
639         q->setKeepMouseGrab(true);
640
641     if (!lastPos.isNull()) {
642         qreal elapsed = qreal(QSGItemPrivate::restart(lastPosTime)) / 1000.;
643         if (elapsed <= 0)
644             elapsed = 1;
645         if (q->yflick()) {
646             qreal diff = event->pos().y() - lastPos.y();
647             // average to reduce the effect of spurious moves
648             vData.velocity += diff / elapsed;
649             vData.velocity /= 2;
650         }
651
652         if (q->xflick()) {
653             qreal diff = event->pos().x() - lastPos.x();
654             // average to reduce the effect of spurious moves
655             hData.velocity += diff / elapsed;
656             hData.velocity /= 2;
657         }
658     }
659
660     if (rejectY) vData.velocity = 0;
661     if (rejectX) hData.velocity = 0;
662
663     if (hMoved || vMoved) {
664         q->movementStarting();
665         q->viewportMoved();
666     }
667
668     lastPos = event->pos();
669 }
670
671 void QSGFlickablePrivate::handleMouseReleaseEvent(QGraphicsSceneMouseEvent *event)
672 {
673     Q_Q(QSGFlickable);
674     stealMouse = false;
675     q->setKeepMouseGrab(false);
676     pressed = false;
677     if (!lastPosTime.isValid())
678         return;
679
680     if (QSGItemPrivate::elapsed(lastPosTime) > 100) {
681         // if we drag then pause before release we should not cause a flick.
682         hData.velocity = 0.0;
683         vData.velocity = 0.0;
684     }
685
686     vTime = timeline.time();
687     if (qAbs(vData.velocity) > MinimumFlickVelocity && qAbs(event->pos().y() - pressPos.y()) > FlickThreshold)
688         flickY(vData.velocity);
689     else
690         fixupY();
691
692     if (qAbs(hData.velocity) > MinimumFlickVelocity && qAbs(event->pos().x() - pressPos.x()) > FlickThreshold)
693         flickX(hData.velocity);
694     else
695         fixupX();
696
697     lastPosTime.invalidate();
698
699     if (!timeline.isActive())
700         q->movementEnding();
701 }
702
703 void QSGFlickable::mousePressEvent(QGraphicsSceneMouseEvent *event)
704 {
705     Q_D(QSGFlickable);
706     if (d->interactive) {
707         if (!d->pressed)
708             d->handleMousePressEvent(event);
709         event->accept();
710     } else {
711         QSGItem::mousePressEvent(event);
712     }
713 }
714
715 void QSGFlickable::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
716 {
717     Q_D(QSGFlickable);
718     if (d->interactive) {
719         d->handleMouseMoveEvent(event);
720         event->accept();
721     } else {
722         QSGItem::mouseMoveEvent(event);
723     }
724 }
725
726 void QSGFlickable::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
727 {
728     Q_D(QSGFlickable);
729     if (d->interactive) {
730         d->clearDelayedPress();
731         d->handleMouseReleaseEvent(event);
732         event->accept();
733         ungrabMouse();
734     } else {
735         QSGItem::mouseReleaseEvent(event);
736     }
737 }
738
739 void QSGFlickable::wheelEvent(QGraphicsSceneWheelEvent *event)
740 {
741     Q_D(QSGFlickable);
742     if (!d->interactive) {
743         QSGItem::wheelEvent(event);
744     } else if (yflick() && event->orientation() == Qt::Vertical) {
745         if (event->delta() > 0)
746             d->vData.velocity = qMax(event->delta() - d->vData.smoothVelocity.value(), qreal(250.0));
747         else
748             d->vData.velocity = qMin(event->delta() - d->vData.smoothVelocity.value(), qreal(-250.0));
749         d->flickingVertically = false;
750         d->flickY(d->vData.velocity);
751         if (d->flickingVertically) {
752             d->vMoved = true;
753             movementStarting();
754         }
755         event->accept();
756     } else if (xflick() && event->orientation() == Qt::Horizontal) {
757         if (event->delta() > 0)
758             d->hData.velocity = qMax(event->delta() - d->hData.smoothVelocity.value(), qreal(250.0));
759         else
760             d->hData.velocity = qMin(event->delta() - d->hData.smoothVelocity.value(), qreal(-250.0));
761         d->flickingHorizontally = false;
762         d->flickX(d->hData.velocity);
763         if (d->flickingHorizontally) {
764             d->hMoved = true;
765             movementStarting();
766         }
767         event->accept();
768     } else {
769         QSGItem::wheelEvent(event);
770     }
771 }
772
773 bool QSGFlickablePrivate::isOutermostPressDelay() const
774 {
775     Q_Q(const QSGFlickable);
776     QSGItem *item = q->parentItem();
777     while (item) {
778         QSGFlickable *flick = qobject_cast<QSGFlickable*>(item);
779         if (flick && flick->pressDelay() > 0 && flick->isInteractive())
780             return false;
781         item = item->parentItem();
782     }
783
784     return true;
785 }
786
787 void QSGFlickablePrivate::captureDelayedPress(QGraphicsSceneMouseEvent *event)
788 {
789     Q_Q(QSGFlickable);
790     if (!q->canvas() || pressDelay <= 0)
791         return;
792     if (!isOutermostPressDelay())
793         return;
794     delayedPressTarget = q->canvas()->mouseGrabberItem();
795     delayedPressEvent = new QGraphicsSceneMouseEvent(event->type());
796     delayedPressEvent->setAccepted(false);
797     for (int i = 0x1; i <= 0x10; i <<= 1) {
798         if (event->buttons() & i) {
799             Qt::MouseButton button = Qt::MouseButton(i);
800             delayedPressEvent->setButtonDownPos(button, event->buttonDownPos(button));
801             delayedPressEvent->setButtonDownScenePos(button, event->buttonDownScenePos(button));
802             delayedPressEvent->setButtonDownScreenPos(button, event->buttonDownScreenPos(button));
803         }
804     }
805     delayedPressEvent->setButtons(event->buttons());
806     delayedPressEvent->setButton(event->button());
807     delayedPressEvent->setPos(event->pos());
808     delayedPressEvent->setScenePos(event->scenePos());
809     delayedPressEvent->setScreenPos(event->screenPos());
810     delayedPressEvent->setLastPos(event->lastPos());
811     delayedPressEvent->setLastScenePos(event->lastScenePos());
812     delayedPressEvent->setLastScreenPos(event->lastScreenPos());
813     delayedPressEvent->setModifiers(event->modifiers());
814     delayedPressTimer.start(pressDelay, q);
815 }
816
817 void QSGFlickablePrivate::clearDelayedPress()
818 {
819     if (delayedPressEvent) {
820         delayedPressTimer.stop();
821         delete delayedPressEvent;
822         delayedPressEvent = 0;
823     }
824 }
825
826 void QSGFlickablePrivate::setRoundedViewportX(qreal x)
827 {
828     contentItem->setX(qRound(x));
829 }
830
831 void QSGFlickablePrivate::setRoundedViewportY(qreal y)
832 {
833     contentItem->setY(qRound(y));
834 }
835
836 void QSGFlickable::timerEvent(QTimerEvent *event)
837 {
838     Q_D(QSGFlickable);
839     if (event->timerId() == d->delayedPressTimer.timerId()) {
840         d->delayedPressTimer.stop();
841         if (d->delayedPressEvent) {
842             QSGItem *grabber = canvas() ? canvas()->mouseGrabberItem() : 0;
843             if (!grabber || grabber != this) {
844                 // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
845                 // so we reset the grabber
846                 if (canvas()->mouseGrabberItem() == d->delayedPressTarget)
847                     d->delayedPressTarget->ungrabMouse();
848                 // Use the event handler that will take care of finding the proper item to propagate the event
849                 QSGCanvasPrivate::get(canvas())->deliverMouseEvent(d->delayedPressEvent);
850             }
851             delete d->delayedPressEvent;
852             d->delayedPressEvent = 0;
853         }
854     }
855 }
856
857 qreal QSGFlickable::minYExtent() const
858 {
859     return 0.0;
860 }
861
862 qreal QSGFlickable::minXExtent() const
863 {
864     return 0.0;
865 }
866
867 /* returns -ve */
868 qreal QSGFlickable::maxXExtent() const
869 {
870     return width() - vWidth();
871 }
872 /* returns -ve */
873 qreal QSGFlickable::maxYExtent() const
874 {
875     return height() - vHeight();
876 }
877
878 void QSGFlickable::viewportMoved()
879 {
880     Q_D(QSGFlickable);
881
882     qreal prevX = d->lastFlickablePosition.x();
883     qreal prevY = d->lastFlickablePosition.y();
884     d->velocityTimeline.clear();
885     if (d->pressed || d->calcVelocity) {
886         int elapsed = QSGItemPrivate::restart(d->velocityTime);
887         if (elapsed > 0) {
888             qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / elapsed;
889             qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / elapsed;
890             d->velocityTimeline.move(d->hData.smoothVelocity, horizontalVelocity, d->reportedVelocitySmoothing);
891             d->velocityTimeline.move(d->hData.smoothVelocity, 0, d->reportedVelocitySmoothing);
892             d->velocityTimeline.move(d->vData.smoothVelocity, verticalVelocity, d->reportedVelocitySmoothing);
893             d->velocityTimeline.move(d->vData.smoothVelocity, 0, d->reportedVelocitySmoothing);
894         }
895     } else {
896         if (d->timeline.time() > d->vTime) {
897             qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / (d->timeline.time() - d->vTime);
898             qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / (d->timeline.time() - d->vTime);
899             d->hData.smoothVelocity.setValue(horizontalVelocity);
900             d->vData.smoothVelocity.setValue(verticalVelocity);
901         }
902     }
903
904     d->lastFlickablePosition = QPointF(d->hData.move.value(), d->vData.move.value());
905
906     d->vTime = d->timeline.time();
907     d->updateBeginningEnd();
908 }
909
910 void QSGFlickable::geometryChanged(const QRectF &newGeometry,
911                              const QRectF &oldGeometry)
912 {
913     Q_D(QSGFlickable);
914     QSGItem::geometryChanged(newGeometry, oldGeometry);
915
916     bool changed = false;
917     if (newGeometry.width() != oldGeometry.width()) {
918         if (xflick())
919             changed = true;
920         if (d->hData.viewSize < 0) {
921             d->contentItem->setWidth(width());
922             emit contentWidthChanged();
923         }
924         // Make sure that we're entirely in view.
925         if (!d->pressed && !d->movingHorizontally && !d->movingVertically) {
926             d->fixupMode = QSGFlickablePrivate::Immediate;
927             d->fixupX();
928         }
929     }
930     if (newGeometry.height() != oldGeometry.height()) {
931         if (yflick())
932             changed = true;
933         if (d->vData.viewSize < 0) {
934             d->contentItem->setHeight(height());
935             emit contentHeightChanged();
936         }
937         // Make sure that we're entirely in view.
938         if (!d->pressed && !d->movingHorizontally && !d->movingVertically) {
939             d->fixupMode = QSGFlickablePrivate::Immediate;
940             d->fixupY();
941         }
942     }
943
944     if (changed)
945         d->updateBeginningEnd();
946 }
947
948 void QSGFlickable::cancelFlick()
949 {
950     Q_D(QSGFlickable);
951     d->timeline.reset(d->hData.move);
952     d->timeline.reset(d->vData.move);
953     movementEnding();
954 }
955
956 void QSGFlickablePrivate::data_append(QDeclarativeListProperty<QObject> *prop, QObject *o)
957 {
958     QSGItem *i = qobject_cast<QSGItem *>(o);
959     if (i) {
960         i->setParentItem(static_cast<QSGFlickablePrivate*>(prop->data)->contentItem);
961     } else {
962         o->setParent(prop->object); // XXX todo - do we want this?
963     }
964 }
965
966 int QSGFlickablePrivate::data_count(QDeclarativeListProperty<QObject> *)
967 {
968     // XXX todo
969     return 0;
970 }
971
972 QObject *QSGFlickablePrivate::data_at(QDeclarativeListProperty<QObject> *, int)
973 {
974     // XXX todo
975     return 0;
976 }
977
978 void QSGFlickablePrivate::data_clear(QDeclarativeListProperty<QObject> *)
979 {
980     // XXX todo
981 }
982
983 QDeclarativeListProperty<QObject> QSGFlickable::flickableData()
984 {
985     Q_D(QSGFlickable);
986     return QDeclarativeListProperty<QObject>(this, (void *)d, QSGFlickablePrivate::data_append,
987                                              QSGFlickablePrivate::data_count,
988                                              QSGFlickablePrivate::data_at,
989                                              QSGFlickablePrivate::data_clear);
990 }
991
992 QDeclarativeListProperty<QSGItem> QSGFlickable::flickableChildren()
993 {
994     Q_D(QSGFlickable);
995     return QSGItemPrivate::get(d->contentItem)->children();
996 }
997
998 QSGFlickable::BoundsBehavior QSGFlickable::boundsBehavior() const
999 {
1000     Q_D(const QSGFlickable);
1001     return d->boundsBehavior;
1002 }
1003
1004 void QSGFlickable::setBoundsBehavior(BoundsBehavior b)
1005 {
1006     Q_D(QSGFlickable);
1007     if (b == d->boundsBehavior)
1008         return;
1009     d->boundsBehavior = b;
1010     emit boundsBehaviorChanged();
1011 }
1012
1013 qreal QSGFlickable::contentWidth() const
1014 {
1015     Q_D(const QSGFlickable);
1016     return d->hData.viewSize;
1017 }
1018
1019 void QSGFlickable::setContentWidth(qreal w)
1020 {
1021     Q_D(QSGFlickable);
1022     if (d->hData.viewSize == w)
1023         return;
1024     d->hData.viewSize = w;
1025     if (w < 0)
1026         d->contentItem->setWidth(width());
1027     else
1028         d->contentItem->setWidth(w);
1029     // Make sure that we're entirely in view.
1030     if (!d->pressed && !d->movingHorizontally && !d->movingVertically) {
1031         d->fixupMode = QSGFlickablePrivate::Immediate;
1032         d->fixupX();
1033     } else if (!d->pressed && d->hData.fixingUp) {
1034         d->fixupMode = QSGFlickablePrivate::ExtentChanged;
1035         d->fixupX();
1036     }
1037     emit contentWidthChanged();
1038     d->updateBeginningEnd();
1039 }
1040
1041 qreal QSGFlickable::contentHeight() const
1042 {
1043     Q_D(const QSGFlickable);
1044     return d->vData.viewSize;
1045 }
1046
1047 void QSGFlickable::setContentHeight(qreal h)
1048 {
1049     Q_D(QSGFlickable);
1050     if (d->vData.viewSize == h)
1051         return;
1052     d->vData.viewSize = h;
1053     if (h < 0)
1054         d->contentItem->setHeight(height());
1055     else
1056         d->contentItem->setHeight(h);
1057     // Make sure that we're entirely in view.
1058     if (!d->pressed && !d->movingHorizontally && !d->movingVertically) {
1059         d->fixupMode = QSGFlickablePrivate::Immediate;
1060         d->fixupY();
1061     } else if (!d->pressed && d->vData.fixingUp) {
1062         d->fixupMode = QSGFlickablePrivate::ExtentChanged;
1063         d->fixupY();
1064     }
1065     emit contentHeightChanged();
1066     d->updateBeginningEnd();
1067 }
1068
1069 void QSGFlickable::resizeContent(qreal w, qreal h, QPointF center)
1070 {
1071     Q_D(QSGFlickable);
1072     if (w != d->hData.viewSize) {
1073         qreal oldSize = d->hData.viewSize;
1074         setContentWidth(w);
1075         if (center.x() != 0) {
1076             qreal pos = center.x() * w / oldSize;
1077             setContentX(contentX() + pos - center.x());
1078         }
1079     }
1080     if (h != d->vData.viewSize) {
1081         qreal oldSize = d->vData.viewSize;
1082         setContentHeight(h);
1083         if (center.y() != 0) {
1084             qreal pos = center.y() * h / oldSize;
1085             setContentY(contentY() + pos - center.y());
1086         }
1087     }
1088 }
1089
1090 void QSGFlickable::returnToBounds()
1091 {
1092     Q_D(QSGFlickable);
1093     d->fixupX();
1094     d->fixupY();
1095 }
1096
1097 qreal QSGFlickable::vWidth() const
1098 {
1099     Q_D(const QSGFlickable);
1100     if (d->hData.viewSize < 0)
1101         return width();
1102     else
1103         return d->hData.viewSize;
1104 }
1105
1106 qreal QSGFlickable::vHeight() const
1107 {
1108     Q_D(const QSGFlickable);
1109     if (d->vData.viewSize < 0)
1110         return height();
1111     else
1112         return d->vData.viewSize;
1113 }
1114
1115 bool QSGFlickable::xflick() const
1116 {
1117     Q_D(const QSGFlickable);
1118     if (d->flickableDirection == QSGFlickable::AutoFlickDirection)
1119         return vWidth() != width();
1120     return d->flickableDirection & QSGFlickable::HorizontalFlick;
1121 }
1122
1123 bool QSGFlickable::yflick() const
1124 {
1125     Q_D(const QSGFlickable);
1126     if (d->flickableDirection == QSGFlickable::AutoFlickDirection)
1127         return vHeight() !=  height();
1128     return d->flickableDirection & QSGFlickable::VerticalFlick;
1129 }
1130
1131 void QSGFlickable::mouseUngrabEvent()
1132 {
1133     Q_D(QSGFlickable);
1134     if (d->pressed) {
1135         // if our mouse grab has been removed (probably by another Flickable),
1136         // fix our state
1137         d->pressed = false;
1138         d->stealMouse = false;
1139         setKeepMouseGrab(false);
1140     }
1141 }
1142
1143 bool QSGFlickable::sendMouseEvent(QGraphicsSceneMouseEvent *event)
1144 {
1145     Q_D(QSGFlickable);
1146     QGraphicsSceneMouseEvent mouseEvent(event->type());
1147     QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
1148
1149     QSGCanvas *c = canvas();
1150     QSGItem *grabber = c ? c->mouseGrabberItem() : 0;
1151     bool stealThisEvent = d->stealMouse;
1152     if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) {
1153         mouseEvent.setAccepted(false);
1154         for (int i = 0x1; i <= 0x10; i <<= 1) {
1155             if (event->buttons() & i) {
1156                 Qt::MouseButton button = Qt::MouseButton(i);
1157                 mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button)));
1158             }
1159         }
1160         mouseEvent.setScenePos(event->scenePos());
1161         mouseEvent.setLastScenePos(event->lastScenePos());
1162         mouseEvent.setPos(mapFromScene(event->scenePos()));
1163         mouseEvent.setLastPos(mapFromScene(event->lastScenePos()));
1164
1165         switch(mouseEvent.type()) {
1166         case QEvent::GraphicsSceneMouseMove:
1167             d->handleMouseMoveEvent(&mouseEvent);
1168             break;
1169         case QEvent::GraphicsSceneMousePress:
1170             if (d->pressed) // we are already pressed - this is a delayed replay
1171                 return false;
1172
1173             d->handleMousePressEvent(&mouseEvent);
1174             d->captureDelayedPress(event);
1175             stealThisEvent = d->stealMouse;   // Update stealThisEvent in case changed by function call above
1176             break;
1177         case QEvent::GraphicsSceneMouseRelease:
1178             if (d->delayedPressEvent) {
1179                 // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
1180                 // so we reset the grabber
1181                 if (c->mouseGrabberItem() == d->delayedPressTarget)
1182                     d->delayedPressTarget->ungrabMouse();
1183                 //Use the event handler that will take care of finding the proper item to propagate the event
1184                 QSGCanvasPrivate::get(canvas())->deliverMouseEvent(d->delayedPressEvent);
1185                 d->clearDelayedPress();
1186                 // We send the release
1187                 canvas()->sendEvent(c->mouseGrabberItem(), event);
1188                 // And the event has been consumed
1189                 d->stealMouse = false;
1190                 d->pressed = false;
1191                 return true;
1192             }
1193             d->handleMouseReleaseEvent(&mouseEvent);
1194             break;
1195         default:
1196             break;
1197         }
1198         grabber = qobject_cast<QSGItem*>(c->mouseGrabberItem());
1199         if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) {
1200             d->clearDelayedPress();
1201             grabMouse();
1202         }
1203
1204         return stealThisEvent || d->delayedPressEvent;
1205     } else if (d->lastPosTime.isValid()) {
1206         d->lastPosTime.invalidate();
1207     }
1208     if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease) {
1209         d->clearDelayedPress();
1210         d->stealMouse = false;
1211         d->pressed = false;
1212     }
1213     return false;
1214 }
1215
1216
1217 bool QSGFlickable::childMouseEventFilter(QSGItem *i, QEvent *e)
1218 {
1219     Q_D(QSGFlickable);
1220     if (!isVisible() || !d->interactive)
1221         return QSGItem::childMouseEventFilter(i, e);
1222     switch (e->type()) {
1223     case QEvent::GraphicsSceneMousePress:
1224     case QEvent::GraphicsSceneMouseMove:
1225     case QEvent::GraphicsSceneMouseRelease:
1226         return sendMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e));
1227     default:
1228         break;
1229     }
1230
1231     return QSGItem::childMouseEventFilter(i, e);
1232 }
1233
1234 qreal QSGFlickable::maximumFlickVelocity() const
1235 {
1236     Q_D(const QSGFlickable);
1237     return d->maxVelocity;
1238 }
1239
1240 void QSGFlickable::setMaximumFlickVelocity(qreal v)
1241 {
1242     Q_D(QSGFlickable);
1243     if (v == d->maxVelocity)
1244         return;
1245     d->maxVelocity = v;
1246     emit maximumFlickVelocityChanged();
1247 }
1248
1249 qreal QSGFlickable::flickDeceleration() const
1250 {
1251     Q_D(const QSGFlickable);
1252     return d->deceleration;
1253 }
1254
1255 void QSGFlickable::setFlickDeceleration(qreal deceleration)
1256 {
1257     Q_D(QSGFlickable);
1258     if (deceleration == d->deceleration)
1259         return;
1260     d->deceleration = deceleration;
1261     emit flickDecelerationChanged();
1262 }
1263
1264 bool QSGFlickable::isFlicking() const
1265 {
1266     Q_D(const QSGFlickable);
1267     return d->flickingHorizontally ||  d->flickingVertically;
1268 }
1269
1270 bool QSGFlickable::isFlickingHorizontally() const
1271 {
1272     Q_D(const QSGFlickable);
1273     return d->flickingHorizontally;
1274 }
1275
1276 bool QSGFlickable::isFlickingVertically() const
1277 {
1278     Q_D(const QSGFlickable);
1279     return d->flickingVertically;
1280 }
1281
1282 int QSGFlickable::pressDelay() const
1283 {
1284     Q_D(const QSGFlickable);
1285     return d->pressDelay;
1286 }
1287
1288 void QSGFlickable::setPressDelay(int delay)
1289 {
1290     Q_D(QSGFlickable);
1291     if (d->pressDelay == delay)
1292         return;
1293     d->pressDelay = delay;
1294     emit pressDelayChanged();
1295 }
1296
1297
1298 bool QSGFlickable::isMoving() const
1299 {
1300     Q_D(const QSGFlickable);
1301     return d->movingHorizontally || d->movingVertically;
1302 }
1303
1304 bool QSGFlickable::isMovingHorizontally() const
1305 {
1306     Q_D(const QSGFlickable);
1307     return d->movingHorizontally;
1308 }
1309
1310 bool QSGFlickable::isMovingVertically() const
1311 {
1312     Q_D(const QSGFlickable);
1313     return d->movingVertically;
1314 }
1315
1316 void QSGFlickable::movementStarting()
1317 {
1318     Q_D(QSGFlickable);
1319     if (d->hMoved && !d->movingHorizontally) {
1320         d->movingHorizontally = true;
1321         emit movingChanged();
1322         emit movingHorizontallyChanged();
1323         if (!d->movingVertically)
1324             emit movementStarted();
1325     }
1326     else if (d->vMoved && !d->movingVertically) {
1327         d->movingVertically = true;
1328         emit movingChanged();
1329         emit movingVerticallyChanged();
1330         if (!d->movingHorizontally)
1331             emit movementStarted();
1332     }
1333 }
1334
1335 void QSGFlickable::movementEnding()
1336 {
1337     Q_D(QSGFlickable);
1338     movementXEnding();
1339     movementYEnding();
1340     d->hData.smoothVelocity.setValue(0);
1341     d->vData.smoothVelocity.setValue(0);
1342 }
1343
1344 void QSGFlickable::movementXEnding()
1345 {
1346     Q_D(QSGFlickable);
1347     if (d->flickingHorizontally) {
1348         d->flickingHorizontally = false;
1349         emit flickingChanged();
1350         emit flickingHorizontallyChanged();
1351         if (!d->flickingVertically)
1352            emit flickEnded();
1353     }
1354     if (!d->pressed && !d->stealMouse) {
1355         if (d->movingHorizontally) {
1356             d->movingHorizontally = false;
1357             d->hMoved = false;
1358             emit movingChanged();
1359             emit movingHorizontallyChanged();
1360             if (!d->movingVertically)
1361                 emit movementEnded();
1362         }
1363     }
1364     d->hData.fixingUp = false;
1365 }
1366
1367 void QSGFlickable::movementYEnding()
1368 {
1369     Q_D(QSGFlickable);
1370     if (d->flickingVertically) {
1371         d->flickingVertically = false;
1372         emit flickingChanged();
1373         emit flickingVerticallyChanged();
1374         if (!d->flickingHorizontally)
1375            emit flickEnded();
1376     }
1377     if (!d->pressed && !d->stealMouse) {
1378         if (d->movingVertically) {
1379             d->movingVertically = false;
1380             d->vMoved = false;
1381             emit movingChanged();
1382             emit movingVerticallyChanged();
1383             if (!d->movingHorizontally)
1384                 emit movementEnded();
1385         }
1386     }
1387     d->vData.fixingUp = false;
1388 }
1389
1390 void QSGFlickablePrivate::updateVelocity()
1391 {
1392     Q_Q(QSGFlickable);
1393     emit q->horizontalVelocityChanged();
1394     emit q->verticalVelocityChanged();
1395 }
1396
1397 QT_END_NAMESPACE