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