Multiple fast flicks with large content moves faster
[profile/ivi/qtdeclarative.git] / src / quick / items / qquickflickable.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquickflickable_p.h"
43 #include "qquickflickable_p_p.h"
44 #include "qquickcanvas.h"
45 #include "qquickcanvas_p.h"
46
47 #include <QtDeclarative/qdeclarativeinfo.h>
48 #include <QtGui/qevent.h>
49 #include <QtGui/qguiapplication.h>
50 #include <QtGui/qstylehints.h>
51 #include "qplatformdefs.h"
52
53 QT_BEGIN_NAMESPACE
54
55 // The maximum number of pixels a flick can overshoot
56 #ifndef QML_FLICK_OVERSHOOT
57 #define QML_FLICK_OVERSHOOT 200
58 #endif
59
60 // The number of samples to use in calculating the velocity of a flick
61 #ifndef QML_FLICK_SAMPLEBUFFER
62 #define QML_FLICK_SAMPLEBUFFER 3
63 #endif
64
65 // The number of samples to discard when calculating the flick velocity.
66 // Touch panels often produce inaccurate results as the finger is lifted.
67 #ifndef QML_FLICK_DISCARDSAMPLES
68 #define QML_FLICK_DISCARDSAMPLES 1
69 #endif
70
71 // The default maximum velocity of a flick.
72 #ifndef QML_FLICK_DEFAULTMAXVELOCITY
73 #define QML_FLICK_DEFAULTMAXVELOCITY 2500
74 #endif
75
76 // The default deceleration of a flick.
77 #ifndef QML_FLICK_DEFAULTDECELERATION
78 #define QML_FLICK_DEFAULTDECELERATION 1500
79 #endif
80
81 // How much faster to decelerate when overshooting
82 #ifndef QML_FLICK_OVERSHOOTFRICTION
83 #define QML_FLICK_OVERSHOOTFRICTION 8
84 #endif
85
86 // Multiflick acceleration minimum flick velocity threshold
87 #ifndef QML_FLICK_MULTIFLICK_THRESHOLD
88 #define QML_FLICK_MULTIFLICK_THRESHOLD 1250
89 #endif
90
91 // Multiflick acceleration minimum contentSize/viewSize ratio
92 #ifndef QML_FLICK_MULTIFLICK_RATIO
93 #define QML_FLICK_MULTIFLICK_RATIO 10
94 #endif
95
96 // Multiflick acceleration maximum velocity multiplier
97 #ifndef QML_FLICK_MULTIFLICK_MAXBOOST
98 #define QML_FLICK_MULTIFLICK_MAXBOOST 3.0
99 #endif
100
101 // FlickThreshold determines how far the "mouse" must have moved
102 // before we perform a flick.
103 static const int FlickThreshold = 20;
104
105 // RetainGrabVelocity is the maxmimum instantaneous velocity that
106 // will ensure the Flickable retains the grab on consecutive flicks.
107 static const int RetainGrabVelocity = 15;
108
109 QQuickFlickableVisibleArea::QQuickFlickableVisibleArea(QQuickFlickable *parent)
110     : QObject(parent), flickable(parent), m_xPosition(0.), m_widthRatio(0.)
111     , m_yPosition(0.), m_heightRatio(0.)
112 {
113 }
114
115 qreal QQuickFlickableVisibleArea::widthRatio() const
116 {
117     return m_widthRatio;
118 }
119
120 qreal QQuickFlickableVisibleArea::xPosition() const
121 {
122     return m_xPosition;
123 }
124
125 qreal QQuickFlickableVisibleArea::heightRatio() const
126 {
127     return m_heightRatio;
128 }
129
130 qreal QQuickFlickableVisibleArea::yPosition() const
131 {
132     return m_yPosition;
133 }
134
135 void QQuickFlickableVisibleArea::updateVisible()
136 {
137     QQuickFlickablePrivate *p = QQuickFlickablePrivate::get(flickable);
138
139     bool changeX = false;
140     bool changeY = false;
141     bool changeWidth = false;
142     bool changeHeight = false;
143
144     // Vertical
145     const qreal viewheight = flickable->height();
146     const qreal maxyextent = -flickable->maxYExtent() + flickable->minYExtent();
147     qreal pagePos = (-p->vData.move.value() + flickable->minYExtent()) / (maxyextent + viewheight);
148     qreal pageSize = viewheight / (maxyextent + viewheight);
149
150     if (pageSize != m_heightRatio) {
151         m_heightRatio = pageSize;
152         changeHeight = true;
153     }
154     if (pagePos != m_yPosition) {
155         m_yPosition = pagePos;
156         changeY = true;
157     }
158
159     // Horizontal
160     const qreal viewwidth = flickable->width();
161     const qreal maxxextent = -flickable->maxXExtent() + flickable->minXExtent();
162     pagePos = (-p->hData.move.value() + flickable->minXExtent()) / (maxxextent + viewwidth);
163     pageSize = viewwidth / (maxxextent + viewwidth);
164
165     if (pageSize != m_widthRatio) {
166         m_widthRatio = pageSize;
167         changeWidth = true;
168     }
169     if (pagePos != m_xPosition) {
170         m_xPosition = pagePos;
171         changeX = true;
172     }
173
174     if (changeX)
175         emit xPositionChanged(m_xPosition);
176     if (changeY)
177         emit yPositionChanged(m_yPosition);
178     if (changeWidth)
179         emit widthRatioChanged(m_widthRatio);
180     if (changeHeight)
181         emit heightRatioChanged(m_heightRatio);
182 }
183
184
185 QQuickFlickablePrivate::QQuickFlickablePrivate()
186   : contentItem(new QQuickItem)
187     , hData(this, &QQuickFlickablePrivate::setViewportX)
188     , vData(this, &QQuickFlickablePrivate::setViewportY)
189     , hMoved(false), vMoved(false)
190     , stealMouse(false), pressed(false), interactive(true), calcVelocity(false)
191     , pixelAligned(false)
192     , lastPosTime(-1)
193     , lastPressTime(0)
194     , deceleration(QML_FLICK_DEFAULTDECELERATION)
195     , maxVelocity(QML_FLICK_DEFAULTMAXVELOCITY), reportedVelocitySmoothing(100)
196     , delayedPressEvent(0), delayedPressTarget(0), pressDelay(0), fixupDuration(400)
197     , flickBoost(1.0), fixupMode(Normal), vTime(0), visibleArea(0)
198     , flickableDirection(QQuickFlickable::AutoFlickDirection)
199     , boundsBehavior(QQuickFlickable::DragAndOvershootBounds)
200 {
201 }
202
203 void QQuickFlickablePrivate::init()
204 {
205     Q_Q(QQuickFlickable);
206     QDeclarative_setParent_noEvent(contentItem, q);
207     contentItem->setParentItem(q);
208     FAST_CONNECT(&timeline, SIGNAL(completed()), q, SLOT(movementEnding()))
209     q->setAcceptedMouseButtons(Qt::LeftButton);
210     q->setFiltersChildMouseEvents(true);
211     QQuickItemPrivate *viewportPrivate = QQuickItemPrivate::get(contentItem);
212     viewportPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
213 }
214
215 /*
216     Returns the amount to overshoot by given a velocity.
217     Will be roughly in range 0 - size/4
218 */
219 qreal QQuickFlickablePrivate::overShootDistance(qreal size)
220 {
221     if (maxVelocity <= 0)
222         return 0.0;
223
224     return qMin(qreal(QML_FLICK_OVERSHOOT), size/3);
225 }
226
227 void QQuickFlickablePrivate::AxisData::addVelocitySample(qreal v, qreal maxVelocity)
228 {
229     if (v > maxVelocity)
230         v = maxVelocity;
231     else if (v < -maxVelocity)
232         v = -maxVelocity;
233     velocityBuffer.append(v);
234     if (velocityBuffer.count() > QML_FLICK_SAMPLEBUFFER)
235         velocityBuffer.remove(0);
236 }
237
238 void QQuickFlickablePrivate::AxisData::updateVelocity()
239 {
240     velocity = 0;
241     if (velocityBuffer.count() > QML_FLICK_DISCARDSAMPLES) {
242         int count = velocityBuffer.count()-QML_FLICK_DISCARDSAMPLES;
243         for (int i = 0; i < count; ++i) {
244             qreal v = velocityBuffer.at(i);
245             velocity += v;
246         }
247         velocity /= count;
248     }
249 }
250
251 void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeom, const QRectF &oldGeom)
252 {
253     Q_Q(QQuickFlickable);
254     if (item == contentItem) {
255         bool xChanged = newGeom.x() != oldGeom.x();
256         bool yChanged = newGeom.y() != oldGeom.y();
257         if (xChanged || yChanged)
258             q->viewportMoved();
259         if (xChanged)
260             emit q->contentXChanged();
261         if (yChanged)
262             emit q->contentYChanged();
263     }
264 }
265
266 void QQuickFlickablePrivate::flickX(qreal velocity)
267 {
268     Q_Q(QQuickFlickable);
269     flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, velocity);
270 }
271
272 void QQuickFlickablePrivate::flickY(qreal velocity)
273 {
274     Q_Q(QQuickFlickable);
275     flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, velocity);
276 }
277
278 void QQuickFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal,
279                                          QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
280 {
281     Q_Q(QQuickFlickable);
282     qreal maxDistance = -1;
283     data.fixingUp = false;
284     // -ve velocity means list is moving up
285     if (velocity > 0) {
286         maxDistance = qAbs(minExtent - data.move.value());
287         data.flickTarget = minExtent;
288     } else {
289         maxDistance = qAbs(maxExtent - data.move.value());
290         data.flickTarget = maxExtent;
291     }
292     if (maxDistance > 0) {
293         qreal v = velocity;
294         if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
295             if (v < 0)
296                 v = -maxVelocity;
297             else
298                 v = maxVelocity;
299         }
300         timeline.reset(data.move);
301         if (boundsBehavior == QQuickFlickable::DragAndOvershootBounds)
302             timeline.accel(data.move, v, deceleration);
303         else
304             timeline.accel(data.move, v, deceleration, maxDistance);
305         timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
306         if (!hData.flicking && q->xflick()) {
307             hData.flicking = true;
308             emit q->flickingChanged();
309             emit q->flickingHorizontallyChanged();
310             if (!vData.flicking)
311                 emit q->flickStarted();
312         }
313         if (!vData.flicking && q->yflick()) {
314             vData.flicking = true;
315             emit q->flickingChanged();
316             emit q->flickingVerticallyChanged();
317             if (!hData.flicking)
318                 emit q->flickStarted();
319         }
320     } else {
321         timeline.reset(data.move);
322         fixup(data, minExtent, maxExtent);
323     }
324 }
325
326 void QQuickFlickablePrivate::fixupY_callback(void *data)
327 {
328     ((QQuickFlickablePrivate *)data)->fixupY();
329 }
330
331 void QQuickFlickablePrivate::fixupX_callback(void *data)
332 {
333     ((QQuickFlickablePrivate *)data)->fixupX();
334 }
335
336 void QQuickFlickablePrivate::fixupX()
337 {
338     Q_Q(QQuickFlickable);
339     fixup(hData, q->minXExtent(), q->maxXExtent());
340 }
341
342 void QQuickFlickablePrivate::fixupY()
343 {
344     Q_Q(QQuickFlickable);
345     fixup(vData, q->minYExtent(), q->maxYExtent());
346 }
347
348 void QQuickFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
349 {
350     if (data.move.value() > minExtent || maxExtent > minExtent) {
351         timeline.reset(data.move);
352         if (data.move.value() != minExtent) {
353             switch (fixupMode) {
354             case Immediate:
355                 timeline.set(data.move, minExtent);
356                 break;
357             case ExtentChanged:
358                 // The target has changed. Don't start from the beginning; just complete the
359                 // second half of the animation using the new extent.
360                 timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
361                 data.fixingUp = true;
362                 break;
363             default: {
364                     qreal dist = minExtent - data.move;
365                     timeline.move(data.move, minExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4);
366                     timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
367                     data.fixingUp = true;
368                 }
369             }
370         }
371     } else if (data.move.value() < maxExtent) {
372         timeline.reset(data.move);
373         switch (fixupMode) {
374         case Immediate:
375             timeline.set(data.move, maxExtent);
376             break;
377         case ExtentChanged:
378             // The target has changed. Don't start from the beginning; just complete the
379             // second half of the animation using the new extent.
380             timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
381             data.fixingUp = true;
382             break;
383         default: {
384                 qreal dist = maxExtent - data.move;
385                 timeline.move(data.move, maxExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4);
386                 timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
387                 data.fixingUp = true;
388             }
389         }
390     }
391     data.inOvershoot = false;
392     fixupMode = Normal;
393     vTime = timeline.time();
394 }
395
396 void QQuickFlickablePrivate::updateBeginningEnd()
397 {
398     Q_Q(QQuickFlickable);
399     bool atBoundaryChange = false;
400
401     // Vertical
402     const int maxyextent = int(-q->maxYExtent());
403     const qreal ypos = -vData.move.value();
404     bool atBeginning = (ypos <= -q->minYExtent());
405     bool atEnd = (maxyextent <= ypos);
406
407     if (atBeginning != vData.atBeginning) {
408         vData.atBeginning = atBeginning;
409         atBoundaryChange = true;
410     }
411     if (atEnd != vData.atEnd) {
412         vData.atEnd = atEnd;
413         atBoundaryChange = true;
414     }
415
416     // Horizontal
417     const int maxxextent = int(-q->maxXExtent());
418     const qreal xpos = -hData.move.value();
419     atBeginning = (xpos <= -q->minXExtent());
420     atEnd = (maxxextent <= xpos);
421
422     if (atBeginning != hData.atBeginning) {
423         hData.atBeginning = atBeginning;
424         atBoundaryChange = true;
425     }
426     if (atEnd != hData.atEnd) {
427         hData.atEnd = atEnd;
428         atBoundaryChange = true;
429     }
430
431     if (vData.extentsChanged) {
432         vData.extentsChanged = false;
433         emit q->yOriginChanged();
434     }
435
436     if (hData.extentsChanged) {
437         hData.extentsChanged = false;
438         emit q->xOriginChanged();
439     }
440
441     if (atBoundaryChange)
442         emit q->isAtBoundaryChanged();
443
444     if (visibleArea)
445         visibleArea->updateVisible();
446 }
447
448 /*
449 XXXTODO add docs describing moving, dragging, flicking properties, e.g.
450
451 When the user starts dragging the Flickable, the dragging and moving properties
452 will be true.
453
454 If the velocity is sufficient when the drag is ended, flicking may begin.
455
456 The moving properties will remain true until all dragging and flicking
457 is finished.
458 */
459
460 /*!
461     \qmlsignal QtQuick2::Flickable::onDragStarted()
462
463     This handler is called when the view starts to be dragged due to user
464     interaction.
465 */
466
467 /*!
468     \qmlsignal QtQuick2::Flickable::onDragEnded()
469
470     This handler is called when the user stops dragging the view.
471
472     If the velocity of the drag is suffient at the time the
473     touch/mouse button is released then a flick will start.
474 */
475
476 /*!
477     \qmlclass Flickable QQuickFlickable
478     \inqmlmodule QtQuick 2
479     \ingroup qml-basic-interaction-elements
480
481     \brief The Flickable item provides a surface that can be "flicked".
482     \inherits Item
483
484     The Flickable item places its children on a surface that can be dragged
485     and flicked, causing the view onto the child items to scroll. This
486     behavior forms the basis of Items that are designed to show large numbers
487     of child items, such as \l ListView and \l GridView.
488
489     In traditional user interfaces, views can be scrolled using standard
490     controls, such as scroll bars and arrow buttons. In some situations, it
491     is also possible to drag the view directly by pressing and holding a
492     mouse button while moving the cursor. In touch-based user interfaces,
493     this dragging action is often complemented with a flicking action, where
494     scrolling continues after the user has stopped touching the view.
495
496     Flickable does not automatically clip its contents. If it is not used as
497     a full-screen item, you should consider setting the \l{Item::}{clip} property
498     to true.
499
500     \section1 Example Usage
501
502     \div {class="float-right"}
503     \inlineimage flickable.gif
504     \enddiv
505
506     The following example shows a small view onto a large image in which the
507     user can drag or flick the image in order to view different parts of it.
508
509     \snippet doc/src/snippets/declarative/flickable.qml document
510
511     \clearfloat
512
513     Items declared as children of a Flickable are automatically parented to the
514     Flickable's \l contentItem.  This should be taken into account when
515     operating on the children of the Flickable; it is usually the children of
516     \c contentItem that are relevant.  For example, the bound of Items added
517     to the Flickable will be available by \c contentItem.childrenRect
518
519     \section1 Limitations
520
521     \note Due to an implementation detail, items placed inside a Flickable cannot anchor to it by
522     \c id. Use \c parent instead.
523 */
524
525 /*!
526     \qmlsignal QtQuick2::Flickable::onMovementStarted()
527
528     This handler is called when the view begins moving due to user
529     interaction.
530 */
531
532 /*!
533     \qmlsignal QtQuick2::Flickable::onMovementEnded()
534
535     This handler is called when the view stops moving due to user
536     interaction.  If a flick was generated, this handler will
537     be triggered once the flick stops.  If a flick was not
538     generated, the handler will be triggered when the
539     user stops dragging - i.e. a mouse or touch release.
540 */
541
542 /*!
543     \qmlsignal QtQuick2::Flickable::onFlickStarted()
544
545     This handler is called when the view is flicked.  A flick
546     starts from the point that the mouse or touch is released,
547     while still in motion.
548 */
549
550 /*!
551     \qmlsignal QtQuick2::Flickable::onFlickEnded()
552
553     This handler is called when the view stops moving due to a flick.
554 */
555
556 /*!
557     \qmlproperty real QtQuick2::Flickable::visibleArea.xPosition
558     \qmlproperty real QtQuick2::Flickable::visibleArea.widthRatio
559     \qmlproperty real QtQuick2::Flickable::visibleArea.yPosition
560     \qmlproperty real QtQuick2::Flickable::visibleArea.heightRatio
561
562     These properties describe the position and size of the currently viewed area.
563     The size is defined as the percentage of the full view currently visible,
564     scaled to 0.0 - 1.0.  The page position is usually in the range 0.0 (beginning) to
565     1.0 minus size ratio (end), i.e. \c yPosition is in the range 0.0 to 1.0-\c heightRatio.
566     However, it is possible for the contents to be dragged outside of the normal
567     range, resulting in the page positions also being outside the normal range.
568
569     These properties are typically used to draw a scrollbar. For example:
570
571     \snippet doc/src/snippets/declarative/flickableScrollbar.qml 0
572     \dots 8
573     \snippet doc/src/snippets/declarative/flickableScrollbar.qml 1
574
575     \sa {declarative/ui-components/scrollbar}{scrollbar example}
576 */
577 QQuickFlickable::QQuickFlickable(QQuickItem *parent)
578   : QQuickItem(*(new QQuickFlickablePrivate), parent)
579 {
580     Q_D(QQuickFlickable);
581     d->init();
582 }
583
584 QQuickFlickable::QQuickFlickable(QQuickFlickablePrivate &dd, QQuickItem *parent)
585   : QQuickItem(dd, parent)
586 {
587     Q_D(QQuickFlickable);
588     d->init();
589 }
590
591 QQuickFlickable::~QQuickFlickable()
592 {
593 }
594
595 /*!
596     \qmlproperty real QtQuick2::Flickable::contentX
597     \qmlproperty real QtQuick2::Flickable::contentY
598
599     These properties hold the surface coordinate currently at the top-left
600     corner of the Flickable. For example, if you flick an image up 100 pixels,
601     \c contentY will be 100.
602 */
603 qreal QQuickFlickable::contentX() const
604 {
605     Q_D(const QQuickFlickable);
606     return -d->contentItem->x();
607 }
608
609 void QQuickFlickable::setContentX(qreal pos)
610 {
611     Q_D(QQuickFlickable);
612     d->hData.explicitValue = true;
613     d->timeline.reset(d->hData.move);
614     d->vTime = d->timeline.time();
615     movementXEnding();
616     if (-pos != d->hData.move.value())
617         d->hData.move.setValue(-pos);
618 }
619
620 qreal QQuickFlickable::contentY() const
621 {
622     Q_D(const QQuickFlickable);
623     return -d->contentItem->y();
624 }
625
626 void QQuickFlickable::setContentY(qreal pos)
627 {
628     Q_D(QQuickFlickable);
629     d->vData.explicitValue = true;
630     d->timeline.reset(d->vData.move);
631     d->vTime = d->timeline.time();
632     movementYEnding();
633     if (-pos != d->vData.move.value())
634         d->vData.move.setValue(-pos);
635 }
636
637 /*!
638     \qmlproperty bool QtQuick2::Flickable::interactive
639
640     This property describes whether the user can interact with the Flickable.
641     A user cannot drag or flick a Flickable that is not interactive.
642
643     By default, this property is true.
644
645     This property is useful for temporarily disabling flicking. This allows
646     special interaction with Flickable's children; for example, you might want
647     to freeze a flickable map while scrolling through a pop-up dialog that
648     is a child of the Flickable.
649 */
650 bool QQuickFlickable::isInteractive() const
651 {
652     Q_D(const QQuickFlickable);
653     return d->interactive;
654 }
655
656 void QQuickFlickable::setInteractive(bool interactive)
657 {
658     Q_D(QQuickFlickable);
659     if (interactive != d->interactive) {
660         d->interactive = interactive;
661         if (!interactive && (d->hData.flicking || d->vData.flicking)) {
662             d->timeline.clear();
663             d->vTime = d->timeline.time();
664             d->hData.flicking = false;
665             d->vData.flicking = false;
666             emit flickingChanged();
667             emit flickingHorizontallyChanged();
668             emit flickingVerticallyChanged();
669             emit flickEnded();
670         }
671         emit interactiveChanged();
672     }
673 }
674
675 /*!
676     \qmlproperty real QtQuick2::Flickable::horizontalVelocity
677     \qmlproperty real QtQuick2::Flickable::verticalVelocity
678
679     The instantaneous velocity of movement along the x and y axes, in pixels/sec.
680
681     The reported velocity is smoothed to avoid erratic output.
682
683     Note that for views with a large content size (more than 10 times the view size),
684     the velocity of the flick may exceed the velocity of the touch in the case
685     of multiple quick consecutive flicks.  This allows the user to flick faster
686     through large content.
687 */
688 qreal QQuickFlickable::horizontalVelocity() const
689 {
690     Q_D(const QQuickFlickable);
691     return d->hData.smoothVelocity.value();
692 }
693
694 qreal QQuickFlickable::verticalVelocity() const
695 {
696     Q_D(const QQuickFlickable);
697     return d->vData.smoothVelocity.value();
698 }
699
700 /*!
701     \qmlproperty bool QtQuick2::Flickable::atXBeginning
702     \qmlproperty bool QtQuick2::Flickable::atXEnd
703     \qmlproperty bool QtQuick2::Flickable::atYBeginning
704     \qmlproperty bool QtQuick2::Flickable::atYEnd
705
706     These properties are true if the flickable view is positioned at the beginning,
707     or end respectively.
708 */
709 bool QQuickFlickable::isAtXEnd() const
710 {
711     Q_D(const QQuickFlickable);
712     return d->hData.atEnd;
713 }
714
715 bool QQuickFlickable::isAtXBeginning() const
716 {
717     Q_D(const QQuickFlickable);
718     return d->hData.atBeginning;
719 }
720
721 bool QQuickFlickable::isAtYEnd() const
722 {
723     Q_D(const QQuickFlickable);
724     return d->vData.atEnd;
725 }
726
727 bool QQuickFlickable::isAtYBeginning() const
728 {
729     Q_D(const QQuickFlickable);
730     return d->vData.atBeginning;
731 }
732
733 /*!
734     \qmlproperty Item QtQuick2::Flickable::contentItem
735
736     The internal item that contains the Items to be moved in the Flickable.
737
738     Items declared as children of a Flickable are automatically parented to the Flickable's contentItem.
739
740     Items created dynamically need to be explicitly parented to the \e contentItem:
741     \code
742     Flickable {
743         id: myFlickable
744         function addItem(file) {
745             var component = Qt.createComponent(file)
746             component.createObject(myFlickable.contentItem);
747         }
748     }
749     \endcode
750 */
751 QQuickItem *QQuickFlickable::contentItem()
752 {
753     Q_D(QQuickFlickable);
754     return d->contentItem;
755 }
756
757 QQuickFlickableVisibleArea *QQuickFlickable::visibleArea()
758 {
759     Q_D(QQuickFlickable);
760     if (!d->visibleArea)
761         d->visibleArea = new QQuickFlickableVisibleArea(this);
762     return d->visibleArea;
763 }
764
765 /*!
766     \qmlproperty enumeration QtQuick2::Flickable::flickableDirection
767
768     This property determines which directions the view can be flicked.
769
770     \list
771     \o Flickable.AutoFlickDirection (default) - allows flicking vertically if the
772     \e contentHeight is not equal to the \e height of the Flickable.
773     Allows flicking horizontally if the \e contentWidth is not equal
774     to the \e width of the Flickable.
775     \o Flickable.HorizontalFlick - allows flicking horizontally.
776     \o Flickable.VerticalFlick - allows flicking vertically.
777     \o Flickable.HorizontalAndVerticalFlick - allows flicking in both directions.
778     \endlist
779 */
780 QQuickFlickable::FlickableDirection QQuickFlickable::flickableDirection() const
781 {
782     Q_D(const QQuickFlickable);
783     return d->flickableDirection;
784 }
785
786 void QQuickFlickable::setFlickableDirection(FlickableDirection direction)
787 {
788     Q_D(QQuickFlickable);
789     if (direction != d->flickableDirection) {
790         d->flickableDirection = direction;
791         emit flickableDirectionChanged();
792     }
793 }
794
795 bool QQuickFlickable::pixelAligned() const
796 {
797     Q_D(const QQuickFlickable);
798     return d->pixelAligned;
799 }
800
801 void QQuickFlickable::setPixelAligned(bool align)
802 {
803     Q_D(QQuickFlickable);
804     if (align != d->pixelAligned) {
805         d->pixelAligned = align;
806         emit pixelAlignedChanged();
807     }
808 }
809
810 qint64 QQuickFlickablePrivate::computeCurrentTime(QInputEvent *event)
811 {
812     if (0 != event->timestamp() && QQuickItemPrivate::consistentTime == -1)
813         return event->timestamp();
814
815     return QQuickItemPrivate::elapsed(timer);
816 }
817
818 void QQuickFlickablePrivate::handleMousePressEvent(QMouseEvent *event)
819 {
820     Q_Q(QQuickFlickable);
821     QQuickItemPrivate::start(timer);
822     if (interactive && timeline.isActive()
823         && (qAbs(hData.smoothVelocity.value()) > RetainGrabVelocity
824             || qAbs(vData.smoothVelocity.value()) > RetainGrabVelocity)) {
825         stealMouse = true; // If we've been flicked then steal the click.
826         int flickTime = timeline.time();
827         if (flickTime > 600) {
828             // too long between flicks - cancel boost
829             hData.continuousFlickVelocity = 0;
830             vData.continuousFlickVelocity = 0;
831             flickBoost = 1.0;
832         } else {
833             hData.continuousFlickVelocity = -hData.smoothVelocity.value();
834             vData.continuousFlickVelocity = -vData.smoothVelocity.value();
835             if (flickTime > 300) // slower flicking - reduce boost
836                 flickBoost = qMax(1.0, flickBoost - 0.5);
837         }
838     } else {
839         stealMouse = false;
840         hData.continuousFlickVelocity = 0;
841         vData.continuousFlickVelocity = 0;
842         flickBoost = 1.0;
843     }
844     q->setKeepMouseGrab(stealMouse);
845     pressed = true;
846     timeline.clear();
847     hData.reset();
848     vData.reset();
849     hData.dragMinBound = q->minXExtent();
850     vData.dragMinBound = q->minYExtent();
851     hData.dragMaxBound = q->maxXExtent();
852     vData.dragMaxBound = q->maxYExtent();
853     fixupMode = Normal;
854     lastPos = QPointF();
855     pressPos = event->localPos();
856     hData.pressPos = hData.move.value();
857     vData.pressPos = vData.move.value();
858     hData.flicking = false;
859     vData.flicking = false;
860     lastPosTime = lastPressTime = computeCurrentTime(event);
861     QQuickItemPrivate::start(velocityTime);
862 }
863
864 void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event)
865 {
866     Q_Q(QQuickFlickable);
867     if (!interactive || lastPosTime == -1)
868         return;
869     bool rejectY = false;
870     bool rejectX = false;
871
872     bool stealY = stealMouse;
873     bool stealX = stealMouse;
874
875     qint64 elapsed = computeCurrentTime(event) - lastPressTime;
876
877     if (q->yflick()) {
878         qreal dy = event->localPos().y() - pressPos.y();
879         if (qAbs(dy) > qApp->styleHints()->startDragDistance() || elapsed > 200) {
880             if (!vMoved)
881                 vData.dragStartOffset = dy;
882             qreal newY = dy + vData.pressPos - vData.dragStartOffset;
883             const qreal minY = vData.dragMinBound;
884             const qreal maxY = vData.dragMaxBound;
885             if (newY > minY)
886                 newY = minY + (newY - minY) / 2;
887             if (newY < maxY && maxY - minY <= 0)
888                 newY = maxY + (newY - maxY) / 2;
889             if (boundsBehavior == QQuickFlickable::StopAtBounds && (newY > minY || newY < maxY)) {
890                 rejectY = true;
891                 if (newY < maxY) {
892                     newY = maxY;
893                     rejectY = false;
894                 }
895                 if (newY > minY) {
896                     newY = minY;
897                     rejectY = false;
898                 }
899             }
900             if (!rejectY && stealMouse) {
901                 vData.move.setValue(qRound(newY));
902                 vMoved = true;
903             }
904             if (qAbs(dy) > qApp->styleHints()->startDragDistance())
905                 stealY = true;
906         }
907     }
908
909     if (q->xflick()) {
910         qreal dx = event->localPos().x() - pressPos.x();
911         if (qAbs(dx) > qApp->styleHints()->startDragDistance() || elapsed > 200) {
912             if (!hMoved)
913                 hData.dragStartOffset = dx;
914             qreal newX = dx + hData.pressPos - hData.dragStartOffset;
915             const qreal minX = hData.dragMinBound;
916             const qreal maxX = hData.dragMaxBound;
917             if (newX > minX)
918                 newX = minX + (newX - minX) / 2;
919             if (newX < maxX && maxX - minX <= 0)
920                 newX = maxX + (newX - maxX) / 2;
921             if (boundsBehavior == QQuickFlickable::StopAtBounds && (newX > minX || newX < maxX)) {
922                 rejectX = true;
923                 if (newX < maxX) {
924                     newX = maxX;
925                     rejectX = false;
926                 }
927                 if (newX > minX) {
928                     newX = minX;
929                     rejectX = false;
930                 }
931             }
932             if (!rejectX && stealMouse) {
933                 hData.move.setValue(qRound(newX));
934                 hMoved = true;
935             }
936
937             if (qAbs(dx) > qApp->styleHints()->startDragDistance())
938                 stealX = true;
939         }
940     }
941
942     stealMouse = stealX || stealY;
943     if (stealMouse)
944         q->setKeepMouseGrab(true);
945
946     if (rejectY) {
947         vData.velocityBuffer.clear();
948         vData.velocity = 0;
949     }
950     if (rejectX) {
951         hData.velocityBuffer.clear();
952         hData.velocity = 0;
953     }
954
955     if (hMoved || vMoved) {
956         draggingStarting();
957         q->movementStarting();
958     }
959
960     if (!lastPos.isNull()) {
961         qint64 currentTimestamp = computeCurrentTime(event);
962         qreal elapsed = qreal(currentTimestamp - lastPosTime) / 1000.;
963         if (elapsed <= 0)
964             return;
965         lastPosTime = currentTimestamp;
966         qreal dy = event->localPos().y()-lastPos.y();
967         if (q->yflick() && !rejectY)
968             vData.addVelocitySample(dy/elapsed, maxVelocity);
969         qreal dx = event->localPos().x()-lastPos.x();
970         if (q->xflick() && !rejectX)
971             hData.addVelocitySample(dx/elapsed, maxVelocity);
972     }
973
974     lastPos = event->localPos();
975 }
976
977 void QQuickFlickablePrivate::handleMouseReleaseEvent(QMouseEvent *event)
978 {
979     Q_Q(QQuickFlickable);
980     stealMouse = false;
981     q->setKeepMouseGrab(false);
982     pressed = false;
983
984     // if we drag then pause before release we should not cause a flick.
985     qint64 elapsed = computeCurrentTime(event) - lastPosTime;
986
987     vData.updateVelocity();
988     hData.updateVelocity();
989
990     draggingEnding();
991
992     if (lastPosTime == -1)
993         return;
994
995     vTime = timeline.time();
996
997     bool canBoost = false;
998
999     qreal vVelocity = elapsed < 100 ? vData.velocity : 0;
1000     if (vData.atBeginning || vData.atEnd) {
1001         vVelocity /= 2;
1002     } else if (vData.continuousFlickVelocity != 0.0
1003                && vData.viewSize/q->height() > QML_FLICK_MULTIFLICK_RATIO
1004                && ((vVelocity > 0) == (vData.continuousFlickVelocity > 0))
1005                && qAbs(vVelocity) > QML_FLICK_MULTIFLICK_THRESHOLD) {
1006         // accelerate flick for large view flicked quickly
1007         canBoost = true;
1008     }
1009
1010     qreal hVelocity = elapsed < 100 ? hData.velocity : 0;
1011     if (hData.atBeginning || hData.atEnd) {
1012         hVelocity /= 2;
1013     } else if (hData.continuousFlickVelocity != 0.0
1014                && hData.viewSize/q->width() > QML_FLICK_MULTIFLICK_RATIO
1015                && ((hVelocity > 0) == (hData.continuousFlickVelocity > 0))
1016                && qAbs(hVelocity) > QML_FLICK_MULTIFLICK_THRESHOLD) {
1017         // accelerate flick for large view flicked quickly
1018         canBoost = true;
1019     }
1020
1021     flickBoost = canBoost ? qBound(1.0, flickBoost+0.25, QML_FLICK_MULTIFLICK_MAXBOOST) : 1.0;
1022
1023     vVelocity *= flickBoost;
1024     if (q->yflick() && qAbs(vVelocity) > MinimumFlickVelocity && qAbs(event->localPos().y() - pressPos.y()) > FlickThreshold) {
1025         velocityTimeline.reset(vData.smoothVelocity);
1026         vData.smoothVelocity.setValue(-vVelocity);
1027         flickY(vVelocity);
1028     } else {
1029         fixupY();
1030     }
1031
1032     hVelocity *= flickBoost;
1033     if (q->xflick() && qAbs(hVelocity) > MinimumFlickVelocity && qAbs(event->localPos().x() - pressPos.x()) > FlickThreshold) {
1034         velocityTimeline.reset(hData.smoothVelocity);
1035         hData.smoothVelocity.setValue(-hVelocity);
1036         flickX(hVelocity);
1037     } else {
1038         fixupX();
1039     }
1040
1041     if (!timeline.isActive())
1042         q->movementEnding();
1043 }
1044
1045 void QQuickFlickable::mousePressEvent(QMouseEvent *event)
1046 {
1047     Q_D(QQuickFlickable);
1048     if (d->interactive) {
1049         if (!d->pressed)
1050             d->handleMousePressEvent(event);
1051         event->accept();
1052     } else {
1053         QQuickItem::mousePressEvent(event);
1054     }
1055 }
1056
1057 void QQuickFlickable::mouseMoveEvent(QMouseEvent *event)
1058 {
1059     Q_D(QQuickFlickable);
1060     if (d->interactive) {
1061         d->handleMouseMoveEvent(event);
1062         event->accept();
1063     } else {
1064         QQuickItem::mouseMoveEvent(event);
1065     }
1066 }
1067
1068 void QQuickFlickable::mouseReleaseEvent(QMouseEvent *event)
1069 {
1070     Q_D(QQuickFlickable);
1071     if (d->interactive) {
1072         d->clearDelayedPress();
1073         d->handleMouseReleaseEvent(event);
1074         event->accept();
1075         ungrabMouse();
1076     } else {
1077         QQuickItem::mouseReleaseEvent(event);
1078     }
1079 }
1080
1081 void QQuickFlickable::wheelEvent(QWheelEvent *event)
1082 {
1083     Q_D(QQuickFlickable);
1084     if (!d->interactive) {
1085         QQuickItem::wheelEvent(event);
1086     } else if (yflick() && event->orientation() == Qt::Vertical) {
1087         bool valid = false;
1088         if (event->delta() > 0 && contentY() > -minYExtent()) {
1089             d->vData.velocity = qMax(event->delta()*2 - d->vData.smoothVelocity.value(), qreal(d->maxVelocity/4));
1090             valid = true;
1091         } else if (event->delta() < 0 && contentY() < -maxYExtent()) {
1092             d->vData.velocity = qMin(event->delta()*2 - d->vData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
1093             valid = true;
1094         }
1095         if (valid) {
1096             d->vData.flicking = false;
1097             d->flickY(d->vData.velocity);
1098             if (d->vData.flicking) {
1099                 d->vMoved = true;
1100                 movementStarting();
1101             }
1102             event->accept();
1103         }
1104     } else if (xflick() && event->orientation() == Qt::Horizontal) {
1105         bool valid = false;
1106         if (event->delta() > 0 && contentX() > -minXExtent()) {
1107             d->hData.velocity = qMax(event->delta()*2 - d->hData.smoothVelocity.value(), qreal(d->maxVelocity/4));
1108             valid = true;
1109         } else if (event->delta() < 0 && contentX() < -maxXExtent()) {
1110             d->hData.velocity = qMin(event->delta()*2 - d->hData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
1111             valid = true;
1112         }
1113         if (valid) {
1114             d->hData.flicking = false;
1115             d->flickX(d->hData.velocity);
1116             if (d->hData.flicking) {
1117                 d->hMoved = true;
1118                 movementStarting();
1119             }
1120             event->accept();
1121         }
1122     } else {
1123         QQuickItem::wheelEvent(event);
1124     }
1125 }
1126
1127 bool QQuickFlickablePrivate::isOutermostPressDelay() const
1128 {
1129     Q_Q(const QQuickFlickable);
1130     QQuickItem *item = q->parentItem();
1131     while (item) {
1132         QQuickFlickable *flick = qobject_cast<QQuickFlickable*>(item);
1133         if (flick && flick->pressDelay() > 0 && flick->isInteractive())
1134             return false;
1135         item = item->parentItem();
1136     }
1137
1138     return true;
1139 }
1140
1141 void QQuickFlickablePrivate::captureDelayedPress(QMouseEvent *event)
1142 {
1143     Q_Q(QQuickFlickable);
1144     if (!q->canvas() || pressDelay <= 0)
1145         return;
1146     if (!isOutermostPressDelay())
1147         return;
1148     delayedPressTarget = q->canvas()->mouseGrabberItem();
1149     delayedPressEvent = new QMouseEvent(*event);
1150     delayedPressEvent->setAccepted(false);
1151     delayedPressTimer.start(pressDelay, q);
1152 }
1153
1154 void QQuickFlickablePrivate::clearDelayedPress()
1155 {
1156     if (delayedPressEvent) {
1157         delayedPressTimer.stop();
1158         delete delayedPressEvent;
1159         delayedPressEvent = 0;
1160     }
1161 }
1162
1163 //XXX pixelAligned ignores the global position of the Flickable, i.e. assumes Flickable itself is pixel aligned.
1164 void QQuickFlickablePrivate::setViewportX(qreal x)
1165 {
1166     contentItem->setX(pixelAligned ? qRound(x) : x);
1167 }
1168
1169 void QQuickFlickablePrivate::setViewportY(qreal y)
1170 {
1171     contentItem->setY(pixelAligned ? qRound(y) : y);
1172 }
1173
1174 void QQuickFlickable::timerEvent(QTimerEvent *event)
1175 {
1176     Q_D(QQuickFlickable);
1177     if (event->timerId() == d->delayedPressTimer.timerId()) {
1178         d->delayedPressTimer.stop();
1179         if (d->delayedPressEvent) {
1180             QQuickItem *grabber = canvas() ? canvas()->mouseGrabberItem() : 0;
1181             if (!grabber || grabber != this) {
1182                 // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
1183                 // so we reset the grabber
1184                 if (canvas()->mouseGrabberItem() == d->delayedPressTarget)
1185                     d->delayedPressTarget->ungrabMouse();
1186                 // Use the event handler that will take care of finding the proper item to propagate the event
1187                 QQuickCanvasPrivate::get(canvas())->deliverMouseEvent(d->delayedPressEvent);
1188             }
1189             delete d->delayedPressEvent;
1190             d->delayedPressEvent = 0;
1191         }
1192     }
1193 }
1194
1195 qreal QQuickFlickable::minYExtent() const
1196 {
1197     Q_D(const QQuickFlickable);
1198     return d->vData.startMargin;
1199 }
1200
1201 qreal QQuickFlickable::minXExtent() const
1202 {
1203     Q_D(const QQuickFlickable);
1204     return d->hData.startMargin;
1205 }
1206
1207 /* returns -ve */
1208 qreal QQuickFlickable::maxXExtent() const
1209 {
1210     Q_D(const QQuickFlickable);
1211     return width() - vWidth() - d->hData.endMargin;
1212 }
1213 /* returns -ve */
1214 qreal QQuickFlickable::maxYExtent() const
1215 {
1216     Q_D(const QQuickFlickable);
1217     return height() - vHeight() - d->vData.endMargin;
1218 }
1219
1220 void QQuickFlickable::componentComplete()
1221 {
1222     Q_D(QQuickFlickable);
1223     QQuickItem::componentComplete();
1224     if (!d->hData.explicitValue && d->hData.startMargin != 0.)
1225         setContentX(-minXExtent());
1226     if (!d->vData.explicitValue && d->vData.startMargin != 0.)
1227         setContentY(-minYExtent());
1228 }
1229
1230 void QQuickFlickable::viewportMoved()
1231 {
1232     Q_D(QQuickFlickable);
1233
1234     qreal prevX = d->lastFlickablePosition.x();
1235     qreal prevY = d->lastFlickablePosition.y();
1236     if (d->pressed || d->calcVelocity) {
1237         int elapsed = QQuickItemPrivate::restart(d->velocityTime);
1238         if (elapsed > 0) {
1239             qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / elapsed;
1240             if (qAbs(horizontalVelocity) > 0) {
1241                 d->velocityTimeline.reset(d->hData.smoothVelocity);
1242                 if (d->calcVelocity)
1243                     d->velocityTimeline.set(d->hData.smoothVelocity, horizontalVelocity);
1244                 else
1245                     d->velocityTimeline.move(d->hData.smoothVelocity, horizontalVelocity, d->reportedVelocitySmoothing);
1246                 d->velocityTimeline.move(d->hData.smoothVelocity, 0, d->reportedVelocitySmoothing);
1247             }
1248             qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / elapsed;
1249             if (qAbs(verticalVelocity) > 0) {
1250                 d->velocityTimeline.reset(d->vData.smoothVelocity);
1251                 if (d->calcVelocity)
1252                     d->velocityTimeline.set(d->vData.smoothVelocity, verticalVelocity);
1253                 else
1254                     d->velocityTimeline.move(d->vData.smoothVelocity, verticalVelocity, d->reportedVelocitySmoothing);
1255                 d->velocityTimeline.move(d->vData.smoothVelocity, 0, d->reportedVelocitySmoothing);
1256             }
1257         }
1258     } else {
1259         if (d->timeline.time() > d->vTime) {
1260             d->velocityTimeline.clear();
1261             qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / (d->timeline.time() - d->vTime);
1262             qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / (d->timeline.time() - d->vTime);
1263             d->hData.smoothVelocity.setValue(horizontalVelocity);
1264             d->vData.smoothVelocity.setValue(verticalVelocity);
1265         }
1266     }
1267
1268     if (!d->vData.inOvershoot && !d->vData.fixingUp && d->vData.flicking
1269             && (d->vData.move.value() > minYExtent() || d->vData.move.value() < maxYExtent())
1270             && qAbs(d->vData.smoothVelocity.value()) > 100) {
1271         // Increase deceleration if we've passed a bound
1272         d->vData.inOvershoot = true;
1273         qreal maxDistance = d->overShootDistance(height());
1274         d->timeline.reset(d->vData.move);
1275         d->timeline.accel(d->vData.move, -d->vData.smoothVelocity.value(), d->deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance);
1276         d->timeline.callback(QDeclarativeTimeLineCallback(&d->vData.move, d->fixupY_callback, d));
1277     }
1278     if (!d->hData.inOvershoot && !d->hData.fixingUp && d->hData.flicking
1279             && (d->hData.move.value() > minXExtent() || d->hData.move.value() < maxXExtent())
1280             && qAbs(d->hData.smoothVelocity.value()) > 100) {
1281         // Increase deceleration if we've passed a bound
1282         d->hData.inOvershoot = true;
1283         qreal maxDistance = d->overShootDistance(width());
1284         d->timeline.reset(d->hData.move);
1285         d->timeline.accel(d->hData.move, -d->hData.smoothVelocity.value(), d->deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance);
1286         d->timeline.callback(QDeclarativeTimeLineCallback(&d->hData.move, d->fixupX_callback, d));
1287     }
1288
1289     d->lastFlickablePosition = QPointF(d->hData.move.value(), d->vData.move.value());
1290
1291     d->vTime = d->timeline.time();
1292     d->updateBeginningEnd();
1293 }
1294
1295 void QQuickFlickable::geometryChanged(const QRectF &newGeometry,
1296                              const QRectF &oldGeometry)
1297 {
1298     Q_D(QQuickFlickable);
1299     QQuickItem::geometryChanged(newGeometry, oldGeometry);
1300
1301     bool changed = false;
1302     if (newGeometry.width() != oldGeometry.width()) {
1303         if (xflick())
1304             changed = true;
1305         if (d->hData.viewSize < 0) {
1306             d->contentItem->setWidth(width());
1307             emit contentWidthChanged();
1308         }
1309         // Make sure that we're entirely in view.
1310         if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1311             d->fixupMode = QQuickFlickablePrivate::Immediate;
1312             d->fixupX();
1313         }
1314     }
1315     if (newGeometry.height() != oldGeometry.height()) {
1316         if (yflick())
1317             changed = true;
1318         if (d->vData.viewSize < 0) {
1319             d->contentItem->setHeight(height());
1320             emit contentHeightChanged();
1321         }
1322         // Make sure that we're entirely in view.
1323         if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1324             d->fixupMode = QQuickFlickablePrivate::Immediate;
1325             d->fixupY();
1326         }
1327     }
1328
1329     if (changed)
1330         d->updateBeginningEnd();
1331 }
1332
1333 /*!
1334     \qmlmethod QtQuick2::Flickable::flick(qreal xVelocity, qreal yVelocity)
1335
1336     Flicks the content with \a xVelocity horizontally and \a yVelocity vertically in pixels/sec.
1337 */
1338
1339 void QQuickFlickable::flick(qreal xVelocity, qreal yVelocity)
1340 {
1341     Q_D(QQuickFlickable);
1342     d->flickX(xVelocity);
1343     d->flickY(yVelocity);
1344 }
1345
1346 /*!
1347     \qmlmethod QtQuick2::Flickable::cancelFlick()
1348
1349     Cancels the current flick animation.
1350 */
1351
1352 void QQuickFlickable::cancelFlick()
1353 {
1354     Q_D(QQuickFlickable);
1355     d->timeline.reset(d->hData.move);
1356     d->timeline.reset(d->vData.move);
1357     movementEnding();
1358 }
1359
1360 void QQuickFlickablePrivate::data_append(QDeclarativeListProperty<QObject> *prop, QObject *o)
1361 {
1362     QQuickItem *i = qobject_cast<QQuickItem *>(o);
1363     if (i) {
1364         i->setParentItem(static_cast<QQuickFlickablePrivate*>(prop->data)->contentItem);
1365     } else {
1366         o->setParent(prop->object); // XXX todo - do we want this?
1367     }
1368 }
1369
1370 int QQuickFlickablePrivate::data_count(QDeclarativeListProperty<QObject> *)
1371 {
1372     // XXX todo
1373     return 0;
1374 }
1375
1376 QObject *QQuickFlickablePrivate::data_at(QDeclarativeListProperty<QObject> *, int)
1377 {
1378     // XXX todo
1379     return 0;
1380 }
1381
1382 void QQuickFlickablePrivate::data_clear(QDeclarativeListProperty<QObject> *)
1383 {
1384     // XXX todo
1385 }
1386
1387 QDeclarativeListProperty<QObject> QQuickFlickable::flickableData()
1388 {
1389     Q_D(QQuickFlickable);
1390     return QDeclarativeListProperty<QObject>(this, (void *)d, QQuickFlickablePrivate::data_append,
1391                                              QQuickFlickablePrivate::data_count,
1392                                              QQuickFlickablePrivate::data_at,
1393                                              QQuickFlickablePrivate::data_clear);
1394 }
1395
1396 QDeclarativeListProperty<QQuickItem> QQuickFlickable::flickableChildren()
1397 {
1398     Q_D(QQuickFlickable);
1399     return QQuickItemPrivate::get(d->contentItem)->children();
1400 }
1401
1402 /*!
1403     \qmlproperty enumeration QtQuick2::Flickable::boundsBehavior
1404     This property holds whether the surface may be dragged
1405     beyond the Flickable's boundaries, or overshoot the
1406     Flickable's boundaries when flicked.
1407
1408     This enables the feeling that the edges of the view are soft,
1409     rather than a hard physical boundary.
1410
1411     The \c boundsBehavior can be one of:
1412
1413     \list
1414     \o Flickable.StopAtBounds - the contents can not be dragged beyond the boundary
1415     of the flickable, and flicks will not overshoot.
1416     \o Flickable.DragOverBounds - the contents can be dragged beyond the boundary
1417     of the Flickable, but flicks will not overshoot.
1418     \o Flickable.DragAndOvershootBounds (default) - the contents can be dragged
1419     beyond the boundary of the Flickable, and can overshoot the
1420     boundary when flicked.
1421     \endlist
1422 */
1423 QQuickFlickable::BoundsBehavior QQuickFlickable::boundsBehavior() const
1424 {
1425     Q_D(const QQuickFlickable);
1426     return d->boundsBehavior;
1427 }
1428
1429 void QQuickFlickable::setBoundsBehavior(BoundsBehavior b)
1430 {
1431     Q_D(QQuickFlickable);
1432     if (b == d->boundsBehavior)
1433         return;
1434     d->boundsBehavior = b;
1435     emit boundsBehaviorChanged();
1436 }
1437
1438 /*!
1439     \qmlproperty real QtQuick2::Flickable::contentWidth
1440     \qmlproperty real QtQuick2::Flickable::contentHeight
1441
1442     The dimensions of the content (the surface controlled by Flickable).
1443     This should typically be set to the combined size of the items placed in the
1444     Flickable.
1445
1446     The following snippet shows how these properties are used to display
1447     an image that is larger than the Flickable item itself:
1448
1449     \snippet doc/src/snippets/declarative/flickable.qml document
1450
1451     In some cases, the the content dimensions can be automatically set
1452     using the \l {Item::childrenRect.width}{childrenRect.width}
1453     and \l {Item::childrenRect.height}{childrenRect.height} properties.
1454 */
1455 qreal QQuickFlickable::contentWidth() const
1456 {
1457     Q_D(const QQuickFlickable);
1458     return d->hData.viewSize;
1459 }
1460
1461 void QQuickFlickable::setContentWidth(qreal w)
1462 {
1463     Q_D(QQuickFlickable);
1464     if (d->hData.viewSize == w)
1465         return;
1466     d->hData.viewSize = w;
1467     if (w < 0)
1468         d->contentItem->setWidth(width());
1469     else
1470         d->contentItem->setWidth(w);
1471     d->hData.markExtentsDirty();
1472     // Make sure that we're entirely in view.
1473     if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1474         d->fixupMode = QQuickFlickablePrivate::Immediate;
1475         d->fixupX();
1476     } else if (!d->pressed && d->hData.fixingUp) {
1477         d->fixupMode = QQuickFlickablePrivate::ExtentChanged;
1478         d->fixupX();
1479     }
1480     emit contentWidthChanged();
1481     d->updateBeginningEnd();
1482 }
1483
1484 qreal QQuickFlickable::contentHeight() const
1485 {
1486     Q_D(const QQuickFlickable);
1487     return d->vData.viewSize;
1488 }
1489
1490 void QQuickFlickable::setContentHeight(qreal h)
1491 {
1492     Q_D(QQuickFlickable);
1493     if (d->vData.viewSize == h)
1494         return;
1495     d->vData.viewSize = h;
1496     if (h < 0)
1497         d->contentItem->setHeight(height());
1498     else
1499         d->contentItem->setHeight(h);
1500     d->vData.markExtentsDirty();
1501     // Make sure that we're entirely in view.
1502     if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1503         d->fixupMode = QQuickFlickablePrivate::Immediate;
1504         d->fixupY();
1505     } else if (!d->pressed && d->vData.fixingUp) {
1506         d->fixupMode = QQuickFlickablePrivate::ExtentChanged;
1507         d->fixupY();
1508     }
1509     emit contentHeightChanged();
1510     d->updateBeginningEnd();
1511 }
1512
1513 /*!
1514     \qmlproperty real QtQuick2::Flickable::topMargin
1515     \qmlproperty real QtQuick2::Flickable::leftMargin
1516     \qmlproperty real QtQuick2::Flickable::bottomMargin
1517     \qmlproperty real QtQuick2::Flickable::rightMargin
1518
1519     These properties hold the margins around the content.  This space is reserved
1520     in addition to the contentWidth and contentHeight.
1521 */
1522
1523
1524 qreal QQuickFlickable::topMargin() const
1525 {
1526     Q_D(const QQuickFlickable);
1527     return d->vData.startMargin;
1528 }
1529
1530 void QQuickFlickable::setTopMargin(qreal m)
1531 {
1532     Q_D(QQuickFlickable);
1533     if (d->vData.startMargin == m)
1534         return;
1535     d->vData.startMargin = m;
1536     d->vData.markExtentsDirty();
1537     if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1538         d->fixupMode = QQuickFlickablePrivate::Immediate;
1539         d->fixupY();
1540     }
1541     emit topMarginChanged();
1542     d->updateBeginningEnd();
1543 }
1544
1545 qreal QQuickFlickable::bottomMargin() const
1546 {
1547     Q_D(const QQuickFlickable);
1548     return d->vData.endMargin;
1549 }
1550
1551 void QQuickFlickable::setBottomMargin(qreal m)
1552 {
1553     Q_D(QQuickFlickable);
1554     if (d->vData.endMargin == m)
1555         return;
1556     d->vData.endMargin = m;
1557     d->vData.markExtentsDirty();
1558     if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1559         d->fixupMode = QQuickFlickablePrivate::Immediate;
1560         d->fixupY();
1561     }
1562     emit bottomMarginChanged();
1563     d->updateBeginningEnd();
1564 }
1565
1566 qreal QQuickFlickable::leftMargin() const
1567 {
1568     Q_D(const QQuickFlickable);
1569     return d->hData.startMargin;
1570 }
1571
1572 void QQuickFlickable::setLeftMargin(qreal m)
1573 {
1574     Q_D(QQuickFlickable);
1575     if (d->hData.startMargin == m)
1576         return;
1577     d->hData.startMargin = m;
1578     d->hData.markExtentsDirty();
1579     if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1580         d->fixupMode = QQuickFlickablePrivate::Immediate;
1581         d->fixupX();
1582     }
1583     emit leftMarginChanged();
1584     d->updateBeginningEnd();
1585 }
1586
1587 qreal QQuickFlickable::rightMargin() const
1588 {
1589     Q_D(const QQuickFlickable);
1590     return d->hData.endMargin;
1591 }
1592
1593 void QQuickFlickable::setRightMargin(qreal m)
1594 {
1595     Q_D(QQuickFlickable);
1596     if (d->hData.endMargin == m)
1597         return;
1598     d->hData.endMargin = m;
1599     d->hData.markExtentsDirty();
1600     if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1601         d->fixupMode = QQuickFlickablePrivate::Immediate;
1602         d->fixupX();
1603     }
1604     emit rightMarginChanged();
1605     d->updateBeginningEnd();
1606 }
1607
1608 /*!
1609     \qmlproperty real QtQuick2::Flickable::xOrigin
1610     \qmlproperty real QtQuick2::Flickable::yOrigin
1611
1612     These properties hold the origin of the content.  This is usually (0,0), however
1613     ListView and GridView may have an arbitrary origin due to delegate size variation,
1614     or item insertion/removal outside the visible region.
1615 */
1616
1617 qreal QQuickFlickable::yOrigin() const
1618 {
1619     Q_D(const QQuickFlickable);
1620     return -minYExtent() + d->vData.startMargin;
1621 }
1622
1623 qreal QQuickFlickable::xOrigin() const
1624 {
1625     Q_D(const QQuickFlickable);
1626     return -minXExtent() + d->hData.startMargin;
1627 }
1628
1629
1630 /*!
1631     \qmlmethod QtQuick2::Flickable::resizeContent(real width, real height, QPointF center)
1632
1633     Resizes the content to \a width x \a height about \a center.
1634
1635     This does not scale the contents of the Flickable - it only resizes the \l contentWidth
1636     and \l contentHeight.
1637
1638     Resizing the content may result in the content being positioned outside
1639     the bounds of the Flickable.  Calling \l returnToBounds() will
1640     move the content back within legal bounds.
1641 */
1642 void QQuickFlickable::resizeContent(qreal w, qreal h, QPointF center)
1643 {
1644     Q_D(QQuickFlickable);
1645     if (w != d->hData.viewSize) {
1646         qreal oldSize = d->hData.viewSize;
1647         d->hData.viewSize = w;
1648         d->contentItem->setWidth(w);
1649         emit contentWidthChanged();
1650         if (center.x() != 0) {
1651             qreal pos = center.x() * w / oldSize;
1652             setContentX(contentX() + pos - center.x());
1653         }
1654     }
1655     if (h != d->vData.viewSize) {
1656         qreal oldSize = d->vData.viewSize;
1657         d->vData.viewSize = h;
1658         d->contentItem->setHeight(h);
1659         emit contentHeightChanged();
1660         if (center.y() != 0) {
1661             qreal pos = center.y() * h / oldSize;
1662             setContentY(contentY() + pos - center.y());
1663         }
1664     }
1665     d->updateBeginningEnd();
1666 }
1667
1668 /*!
1669     \qmlmethod QtQuick2::Flickable::returnToBounds()
1670
1671     Ensures the content is within legal bounds.
1672
1673     This may be called to ensure that the content is within legal bounds
1674     after manually positioning the content.
1675 */
1676 void QQuickFlickable::returnToBounds()
1677 {
1678     Q_D(QQuickFlickable);
1679     d->fixupX();
1680     d->fixupY();
1681 }
1682
1683 qreal QQuickFlickable::vWidth() const
1684 {
1685     Q_D(const QQuickFlickable);
1686     if (d->hData.viewSize < 0)
1687         return width();
1688     else
1689         return d->hData.viewSize;
1690 }
1691
1692 qreal QQuickFlickable::vHeight() const
1693 {
1694     Q_D(const QQuickFlickable);
1695     if (d->vData.viewSize < 0)
1696         return height();
1697     else
1698         return d->vData.viewSize;
1699 }
1700
1701 bool QQuickFlickable::xflick() const
1702 {
1703     Q_D(const QQuickFlickable);
1704     if (d->flickableDirection == QQuickFlickable::AutoFlickDirection)
1705         return vWidth() != width();
1706     return d->flickableDirection & QQuickFlickable::HorizontalFlick;
1707 }
1708
1709 bool QQuickFlickable::yflick() const
1710 {
1711     Q_D(const QQuickFlickable);
1712     if (d->flickableDirection == QQuickFlickable::AutoFlickDirection)
1713         return vHeight() !=  height();
1714     return d->flickableDirection & QQuickFlickable::VerticalFlick;
1715 }
1716
1717 void QQuickFlickable::mouseUngrabEvent()
1718 {
1719     Q_D(QQuickFlickable);
1720     if (d->pressed) {
1721         // if our mouse grab has been removed (probably by another Flickable),
1722         // fix our state
1723         d->pressed = false;
1724         d->draggingEnding();
1725         d->stealMouse = false;
1726         setKeepMouseGrab(false);
1727     }
1728 }
1729
1730 bool QQuickFlickable::sendMouseEvent(QMouseEvent *event)
1731 {
1732     Q_D(QQuickFlickable);
1733     QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
1734
1735     QQuickCanvas *c = canvas();
1736     QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
1737     bool disabledItem = grabber && !grabber->isEnabled();
1738     bool stealThisEvent = d->stealMouse;
1739     if ((stealThisEvent || myRect.contains(event->windowPos())) && (!grabber || !grabber->keepMouseGrab() || disabledItem)) {
1740         QMouseEvent mouseEvent(event->type(), mapFromScene(event->windowPos()), event->windowPos(), event->screenPos(),
1741                                event->button(), event->buttons(), event->modifiers());
1742
1743         mouseEvent.setTimestamp(event->timestamp());
1744         mouseEvent.setAccepted(false);
1745
1746         switch (mouseEvent.type()) {
1747         case QEvent::MouseMove:
1748             d->handleMouseMoveEvent(&mouseEvent);
1749             break;
1750         case QEvent::MouseButtonPress:
1751             if (d->pressed) // we are already pressed - this is a delayed replay
1752                 return false;
1753
1754             d->handleMousePressEvent(&mouseEvent);
1755             d->captureDelayedPress(event);
1756             stealThisEvent = d->stealMouse;   // Update stealThisEvent in case changed by function call above
1757             break;
1758         case QEvent::MouseButtonRelease:
1759             if (d->delayedPressEvent) {
1760                 // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
1761                 // so we reset the grabber
1762                 if (c->mouseGrabberItem() == d->delayedPressTarget)
1763                     d->delayedPressTarget->ungrabMouse();
1764                 //Use the event handler that will take care of finding the proper item to propagate the event
1765                 QQuickCanvasPrivate::get(canvas())->deliverMouseEvent(d->delayedPressEvent);
1766                 d->clearDelayedPress();
1767                 // We send the release
1768                 canvas()->sendEvent(c->mouseGrabberItem(), event);
1769                 // And the event has been consumed
1770                 d->stealMouse = false;
1771                 d->pressed = false;
1772                 return true;
1773             }
1774             d->handleMouseReleaseEvent(&mouseEvent);
1775             break;
1776         default:
1777             break;
1778         }
1779         grabber = qobject_cast<QQuickItem*>(c->mouseGrabberItem());
1780         if ((grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) || disabledItem) {
1781             d->clearDelayedPress();
1782             grabMouse();
1783         }
1784
1785         return stealThisEvent || d->delayedPressEvent || disabledItem;
1786     } else if (d->lastPosTime != -1) {
1787         d->lastPosTime = -1;
1788         returnToBounds();
1789     }
1790     if (event->type() == QEvent::MouseButtonRelease) {
1791         d->lastPosTime = -1;
1792         d->clearDelayedPress();
1793         d->stealMouse = false;
1794         d->pressed = false;
1795     }
1796     return false;
1797 }
1798
1799
1800 bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e)
1801 {
1802     Q_D(QQuickFlickable);
1803     if (!isVisible() || !d->interactive || !isEnabled())
1804         return QQuickItem::childMouseEventFilter(i, e);
1805     switch (e->type()) {
1806     case QEvent::MouseButtonPress:
1807     case QEvent::MouseMove:
1808     case QEvent::MouseButtonRelease:
1809         return sendMouseEvent(static_cast<QMouseEvent *>(e));
1810     default:
1811         break;
1812     }
1813
1814     return QQuickItem::childMouseEventFilter(i, e);
1815 }
1816
1817 /*!
1818     \qmlproperty real QtQuick2::Flickable::maximumFlickVelocity
1819     This property holds the maximum velocity that the user can flick the view in pixels/second.
1820
1821     The default value is platform dependent.
1822 */
1823 qreal QQuickFlickable::maximumFlickVelocity() const
1824 {
1825     Q_D(const QQuickFlickable);
1826     return d->maxVelocity;
1827 }
1828
1829 void QQuickFlickable::setMaximumFlickVelocity(qreal v)
1830 {
1831     Q_D(QQuickFlickable);
1832     if (v == d->maxVelocity)
1833         return;
1834     d->maxVelocity = v;
1835     emit maximumFlickVelocityChanged();
1836 }
1837
1838 /*!
1839     \qmlproperty real QtQuick2::Flickable::flickDeceleration
1840     This property holds the rate at which a flick will decelerate.
1841
1842     The default value is platform dependent.
1843 */
1844 qreal QQuickFlickable::flickDeceleration() const
1845 {
1846     Q_D(const QQuickFlickable);
1847     return d->deceleration;
1848 }
1849
1850 void QQuickFlickable::setFlickDeceleration(qreal deceleration)
1851 {
1852     Q_D(QQuickFlickable);
1853     if (deceleration == d->deceleration)
1854         return;
1855     d->deceleration = deceleration;
1856     emit flickDecelerationChanged();
1857 }
1858
1859 bool QQuickFlickable::isFlicking() const
1860 {
1861     Q_D(const QQuickFlickable);
1862     return d->hData.flicking ||  d->vData.flicking;
1863 }
1864
1865 /*!
1866     \qmlproperty bool QtQuick2::Flickable::flicking
1867     \qmlproperty bool QtQuick2::Flickable::flickingHorizontally
1868     \qmlproperty bool QtQuick2::Flickable::flickingVertically
1869
1870     These properties describe whether the view is currently moving horizontally,
1871     vertically or in either direction, due to the user flicking the view.
1872 */
1873 bool QQuickFlickable::isFlickingHorizontally() const
1874 {
1875     Q_D(const QQuickFlickable);
1876     return d->hData.flicking;
1877 }
1878
1879 bool QQuickFlickable::isFlickingVertically() const
1880 {
1881     Q_D(const QQuickFlickable);
1882     return d->vData.flicking;
1883 }
1884
1885 /*!
1886     \qmlproperty bool QtQuick2::Flickable::dragging
1887     \qmlproperty bool QtQuick2::Flickable::draggingHorizontally
1888     \qmlproperty bool QtQuick2::Flickable::draggingVertically
1889
1890     These properties describe whether the view is currently moving horizontally,
1891     vertically or in either direction, due to the user dragging the view.
1892 */
1893 bool QQuickFlickable::isDragging() const
1894 {
1895     Q_D(const QQuickFlickable);
1896     return d->hData.dragging ||  d->vData.dragging;
1897 }
1898
1899 bool QQuickFlickable::isDraggingHorizontally() const
1900 {
1901     Q_D(const QQuickFlickable);
1902     return d->hData.dragging;
1903 }
1904
1905 bool QQuickFlickable::isDraggingVertically() const
1906 {
1907     Q_D(const QQuickFlickable);
1908     return d->vData.dragging;
1909 }
1910
1911 void QQuickFlickablePrivate::draggingStarting()
1912 {
1913     Q_Q(QQuickFlickable);
1914     bool wasDragging = hData.dragging || vData.dragging;
1915     if (hMoved && !hData.dragging) {
1916         hData.dragging = true;
1917         emit q->draggingHorizontallyChanged();
1918     }
1919     if (vMoved && !vData.dragging) {
1920         vData.dragging = true;
1921         emit q->draggingVerticallyChanged();
1922     }
1923     if (!wasDragging && (hData.dragging || vData.dragging)) {
1924         emit q->draggingChanged();
1925         emit q->dragStarted();
1926     }
1927 }
1928
1929 void QQuickFlickablePrivate::draggingEnding()
1930 {
1931     Q_Q(QQuickFlickable);
1932     bool wasDragging = hData.dragging || vData.dragging;
1933     if (hData.dragging) {
1934         hData.dragging = false;
1935         emit q->draggingHorizontallyChanged();
1936     }
1937     if (vData.dragging) {
1938         vData.dragging = false;
1939         emit q->draggingVerticallyChanged();
1940     }
1941     if (wasDragging && !hData.dragging && !vData.dragging) {
1942         emit q->draggingChanged();
1943         emit q->dragEnded();
1944     }
1945 }
1946
1947 /*!
1948     \qmlproperty int QtQuick2::Flickable::pressDelay
1949
1950     This property holds the time to delay (ms) delivering a press to
1951     children of the Flickable.  This can be useful where reacting
1952     to a press before a flicking action has undesirable effects.
1953
1954     If the flickable is dragged/flicked before the delay times out
1955     the press event will not be delivered.  If the button is released
1956     within the timeout, both the press and release will be delivered.
1957
1958     Note that for nested Flickables with pressDelay set, the pressDelay of
1959     inner Flickables is overridden by the outermost Flickable.
1960 */
1961 int QQuickFlickable::pressDelay() const
1962 {
1963     Q_D(const QQuickFlickable);
1964     return d->pressDelay;
1965 }
1966
1967 void QQuickFlickable::setPressDelay(int delay)
1968 {
1969     Q_D(QQuickFlickable);
1970     if (d->pressDelay == delay)
1971         return;
1972     d->pressDelay = delay;
1973     emit pressDelayChanged();
1974 }
1975
1976 /*!
1977     \qmlproperty bool QtQuick2::Flickable::moving
1978     \qmlproperty bool QtQuick2::Flickable::movingHorizontally
1979     \qmlproperty bool QtQuick2::Flickable::movingVertically
1980
1981     These properties describe whether the view is currently moving horizontally,
1982     vertically or in either direction, due to the user either dragging or
1983     flicking the view.
1984 */
1985
1986 bool QQuickFlickable::isMoving() const
1987 {
1988     Q_D(const QQuickFlickable);
1989     return d->hData.moving || d->vData.moving;
1990 }
1991
1992 bool QQuickFlickable::isMovingHorizontally() const
1993 {
1994     Q_D(const QQuickFlickable);
1995     return d->hData.moving;
1996 }
1997
1998 bool QQuickFlickable::isMovingVertically() const
1999 {
2000     Q_D(const QQuickFlickable);
2001     return d->vData.moving;
2002 }
2003
2004 void QQuickFlickable::movementStarting()
2005 {
2006     Q_D(QQuickFlickable);
2007     if (d->hMoved && !d->hData.moving) {
2008         d->hData.moving = true;
2009         emit movingChanged();
2010         emit movingHorizontallyChanged();
2011         if (!d->vData.moving)
2012             emit movementStarted();
2013     }
2014     else if (d->vMoved && !d->vData.moving) {
2015         d->vData.moving = true;
2016         emit movingChanged();
2017         emit movingVerticallyChanged();
2018         if (!d->hData.moving)
2019             emit movementStarted();
2020     }
2021 }
2022
2023 void QQuickFlickable::movementEnding()
2024 {
2025     Q_D(QQuickFlickable);
2026     movementXEnding();
2027     movementYEnding();
2028     d->hData.smoothVelocity.setValue(0);
2029     d->vData.smoothVelocity.setValue(0);
2030 }
2031
2032 void QQuickFlickable::movementXEnding()
2033 {
2034     Q_D(QQuickFlickable);
2035     if (d->hData.flicking) {
2036         d->hData.flicking = false;
2037         emit flickingChanged();
2038         emit flickingHorizontallyChanged();
2039         if (!d->vData.flicking)
2040            emit flickEnded();
2041     }
2042     if (!d->pressed && !d->stealMouse) {
2043         if (d->hData.moving) {
2044             d->hData.moving = false;
2045             d->hMoved = false;
2046             emit movingChanged();
2047             emit movingHorizontallyChanged();
2048             if (!d->vData.moving)
2049                 emit movementEnded();
2050         }
2051     }
2052     d->hData.fixingUp = false;
2053 }
2054
2055 void QQuickFlickable::movementYEnding()
2056 {
2057     Q_D(QQuickFlickable);
2058     if (d->vData.flicking) {
2059         d->vData.flicking = false;
2060         emit flickingChanged();
2061         emit flickingVerticallyChanged();
2062         if (!d->hData.flicking)
2063            emit flickEnded();
2064     }
2065     if (!d->pressed && !d->stealMouse) {
2066         if (d->vData.moving) {
2067             d->vData.moving = false;
2068             d->vMoved = false;
2069             emit movingChanged();
2070             emit movingVerticallyChanged();
2071             if (!d->hData.moving)
2072                 emit movementEnded();
2073         }
2074     }
2075     d->vData.fixingUp = false;
2076 }
2077
2078 void QQuickFlickablePrivate::updateVelocity()
2079 {
2080     Q_Q(QQuickFlickable);
2081     emit q->horizontalVelocityChanged();
2082     emit q->verticalVelocityChanged();
2083 }
2084
2085 QT_END_NAMESPACE