QQuickCanvas renames
[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 suffient at the time the
583     touch/mouse button is released then a flick will start.
584 */
585
586 /*!
587     \qmlclass Flickable QQuickFlickable
588     \inqmlmodule QtQuick 2
589     \ingroup qtquick-input
590     \ingroup qtquick-containers
591
592     \brief Provides a surface that can be "flicked"
593     \inherits Item
594
595     The Flickable item places its children on a surface that can be dragged
596     and flicked, causing the view onto the child items to scroll. This
597     behavior forms the basis of Items that are designed to show large numbers
598     of child items, such as \l ListView and \l GridView.
599
600     In traditional user interfaces, views can be scrolled using standard
601     controls, such as scroll bars and arrow buttons. In some situations, it
602     is also possible to drag the view directly by pressing and holding a
603     mouse button while moving the cursor. In touch-based user interfaces,
604     this dragging action is often complemented with a flicking action, where
605     scrolling continues after the user has stopped touching the view.
606
607     Flickable does not automatically clip its contents. If it is not used as
608     a full-screen item, you should consider setting the \l{Item::}{clip} property
609     to true.
610
611     \section1 Example Usage
612
613     \div {class="float-right"}
614     \inlineimage flickable.gif
615     \enddiv
616
617     The following example shows a small view onto a large image in which the
618     user can drag or flick the image in order to view different parts of it.
619
620     \snippet qml/flickable.qml document
621
622     \clearfloat
623
624     Items declared as children of a Flickable are automatically parented to the
625     Flickable's \l contentItem.  This should be taken into account when
626     operating on the children of the Flickable; it is usually the children of
627     \c contentItem that are relevant.  For example, the bound of Items added
628     to the Flickable will be available by \c contentItem.childrenRect
629
630     \section1 Limitations
631
632     \note Due to an implementation detail, items placed inside a Flickable cannot anchor to it by
633     \c id. Use \c parent instead.
634 */
635
636 /*!
637     \qmlsignal QtQuick2::Flickable::onMovementStarted()
638
639     This handler is called when the view begins moving due to user
640     interaction.
641 */
642
643 /*!
644     \qmlsignal QtQuick2::Flickable::onMovementEnded()
645
646     This handler is called when the view stops moving due to user
647     interaction.  If a flick was generated, this handler will
648     be triggered once the flick stops.  If a flick was not
649     generated, the handler will be triggered when the
650     user stops dragging - i.e. a mouse or touch release.
651 */
652
653 /*!
654     \qmlsignal QtQuick2::Flickable::onFlickStarted()
655
656     This handler is called when the view is flicked.  A flick
657     starts from the point that the mouse or touch is released,
658     while still in motion.
659 */
660
661 /*!
662     \qmlsignal QtQuick2::Flickable::onFlickEnded()
663
664     This handler is called when the view stops moving due to a flick.
665 */
666
667 /*!
668     \qmlproperty real QtQuick2::Flickable::visibleArea.xPosition
669     \qmlproperty real QtQuick2::Flickable::visibleArea.widthRatio
670     \qmlproperty real QtQuick2::Flickable::visibleArea.yPosition
671     \qmlproperty real QtQuick2::Flickable::visibleArea.heightRatio
672
673     These properties describe the position and size of the currently viewed area.
674     The size is defined as the percentage of the full view currently visible,
675     scaled to 0.0 - 1.0.  The page position is usually in the range 0.0 (beginning) to
676     1.0 minus size ratio (end), i.e. \c yPosition is in the range 0.0 to 1.0-\c heightRatio.
677     However, it is possible for the contents to be dragged outside of the normal
678     range, resulting in the page positions also being outside the normal range.
679
680     These properties are typically used to draw a scrollbar. For example:
681
682     \snippet qml/flickableScrollbar.qml 0
683     \dots 8
684     \snippet qml/flickableScrollbar.qml 1
685
686     \sa {declarative/ui-components/scrollbar}{scrollbar example}
687 */
688 QQuickFlickable::QQuickFlickable(QQuickItem *parent)
689   : QQuickItem(*(new QQuickFlickablePrivate), parent)
690 {
691     Q_D(QQuickFlickable);
692     d->init();
693 }
694
695 QQuickFlickable::QQuickFlickable(QQuickFlickablePrivate &dd, QQuickItem *parent)
696   : QQuickItem(dd, parent)
697 {
698     Q_D(QQuickFlickable);
699     d->init();
700 }
701
702 QQuickFlickable::~QQuickFlickable()
703 {
704 }
705
706 /*!
707     \qmlproperty real QtQuick2::Flickable::contentX
708     \qmlproperty real QtQuick2::Flickable::contentY
709
710     These properties hold the surface coordinate currently at the top-left
711     corner of the Flickable. For example, if you flick an image up 100 pixels,
712     \c contentY will be 100.
713 */
714 qreal QQuickFlickable::contentX() const
715 {
716     Q_D(const QQuickFlickable);
717     return -d->contentItem->x();
718 }
719
720 void QQuickFlickable::setContentX(qreal pos)
721 {
722     Q_D(QQuickFlickable);
723     d->hData.explicitValue = true;
724     d->resetTimeline(d->hData);
725     d->hData.vTime = d->timeline.time();
726     movementEnding(true, false);
727     if (-pos != d->hData.move.value())
728         d->hData.move.setValue(-pos);
729 }
730
731 qreal QQuickFlickable::contentY() const
732 {
733     Q_D(const QQuickFlickable);
734     return -d->contentItem->y();
735 }
736
737 void QQuickFlickable::setContentY(qreal pos)
738 {
739     Q_D(QQuickFlickable);
740     d->vData.explicitValue = true;
741     d->resetTimeline(d->vData);
742     d->vData.vTime = d->timeline.time();
743     movementEnding(false, true);
744     if (-pos != d->vData.move.value())
745         d->vData.move.setValue(-pos);
746 }
747
748 /*!
749     \qmlproperty bool QtQuick2::Flickable::interactive
750
751     This property describes whether the user can interact with the Flickable.
752     A user cannot drag or flick a Flickable that is not interactive.
753
754     By default, this property is true.
755
756     This property is useful for temporarily disabling flicking. This allows
757     special interaction with Flickable's children; for example, you might want
758     to freeze a flickable map while scrolling through a pop-up dialog that
759     is a child of the Flickable.
760 */
761 bool QQuickFlickable::isInteractive() const
762 {
763     Q_D(const QQuickFlickable);
764     return d->interactive;
765 }
766
767 void QQuickFlickable::setInteractive(bool interactive)
768 {
769     Q_D(QQuickFlickable);
770     if (interactive != d->interactive) {
771         d->interactive = interactive;
772         if (!interactive && (d->hData.flicking || d->vData.flicking)) {
773             d->clearTimeline();
774             d->hData.vTime = d->vData.vTime = d->timeline.time();
775             d->hData.flicking = false;
776             d->vData.flicking = false;
777             emit flickingChanged();
778             emit flickingHorizontallyChanged();
779             emit flickingVerticallyChanged();
780             emit flickEnded();
781         }
782         emit interactiveChanged();
783     }
784 }
785
786 /*!
787     \qmlproperty real QtQuick2::Flickable::horizontalVelocity
788     \qmlproperty real QtQuick2::Flickable::verticalVelocity
789
790     The instantaneous velocity of movement along the x and y axes, in pixels/sec.
791
792     The reported velocity is smoothed to avoid erratic output.
793
794     Note that for views with a large content size (more than 10 times the view size),
795     the velocity of the flick may exceed the velocity of the touch in the case
796     of multiple quick consecutive flicks.  This allows the user to flick faster
797     through large content.
798 */
799 qreal QQuickFlickable::horizontalVelocity() const
800 {
801     Q_D(const QQuickFlickable);
802     return d->hData.smoothVelocity.value();
803 }
804
805 qreal QQuickFlickable::verticalVelocity() const
806 {
807     Q_D(const QQuickFlickable);
808     return d->vData.smoothVelocity.value();
809 }
810
811 /*!
812     \qmlproperty bool QtQuick2::Flickable::atXBeginning
813     \qmlproperty bool QtQuick2::Flickable::atXEnd
814     \qmlproperty bool QtQuick2::Flickable::atYBeginning
815     \qmlproperty bool QtQuick2::Flickable::atYEnd
816
817     These properties are true if the flickable view is positioned at the beginning,
818     or end respectively.
819 */
820 bool QQuickFlickable::isAtXEnd() const
821 {
822     Q_D(const QQuickFlickable);
823     return d->hData.atEnd;
824 }
825
826 bool QQuickFlickable::isAtXBeginning() const
827 {
828     Q_D(const QQuickFlickable);
829     return d->hData.atBeginning;
830 }
831
832 bool QQuickFlickable::isAtYEnd() const
833 {
834     Q_D(const QQuickFlickable);
835     return d->vData.atEnd;
836 }
837
838 bool QQuickFlickable::isAtYBeginning() const
839 {
840     Q_D(const QQuickFlickable);
841     return d->vData.atBeginning;
842 }
843
844 /*!
845     \qmlproperty Item QtQuick2::Flickable::contentItem
846
847     The internal item that contains the Items to be moved in the Flickable.
848
849     Items declared as children of a Flickable are automatically parented to the Flickable's contentItem.
850
851     Items created dynamically need to be explicitly parented to the \e contentItem:
852     \code
853     Flickable {
854         id: myFlickable
855         function addItem(file) {
856             var component = Qt.createComponent(file)
857             component.createObject(myFlickable.contentItem);
858         }
859     }
860     \endcode
861 */
862 QQuickItem *QQuickFlickable::contentItem()
863 {
864     Q_D(QQuickFlickable);
865     return d->contentItem;
866 }
867
868 QQuickFlickableVisibleArea *QQuickFlickable::visibleArea()
869 {
870     Q_D(QQuickFlickable);
871     if (!d->visibleArea)
872         d->visibleArea = new QQuickFlickableVisibleArea(this);
873     return d->visibleArea;
874 }
875
876 /*!
877     \qmlproperty enumeration QtQuick2::Flickable::flickableDirection
878
879     This property determines which directions the view can be flicked.
880
881     \list
882     \li Flickable.AutoFlickDirection (default) - allows flicking vertically if the
883     \e contentHeight is not equal to the \e height of the Flickable.
884     Allows flicking horizontally if the \e contentWidth is not equal
885     to the \e width of the Flickable.
886     \li Flickable.HorizontalFlick - allows flicking horizontally.
887     \li Flickable.VerticalFlick - allows flicking vertically.
888     \li Flickable.HorizontalAndVerticalFlick - allows flicking in both directions.
889     \endlist
890 */
891 QQuickFlickable::FlickableDirection QQuickFlickable::flickableDirection() const
892 {
893     Q_D(const QQuickFlickable);
894     return d->flickableDirection;
895 }
896
897 void QQuickFlickable::setFlickableDirection(FlickableDirection direction)
898 {
899     Q_D(QQuickFlickable);
900     if (direction != d->flickableDirection) {
901         d->flickableDirection = direction;
902         emit flickableDirectionChanged();
903     }
904 }
905
906 bool QQuickFlickable::pixelAligned() const
907 {
908     Q_D(const QQuickFlickable);
909     return d->pixelAligned;
910 }
911
912 void QQuickFlickable::setPixelAligned(bool align)
913 {
914     Q_D(QQuickFlickable);
915     if (align != d->pixelAligned) {
916         d->pixelAligned = align;
917         emit pixelAlignedChanged();
918     }
919 }
920
921 qint64 QQuickFlickablePrivate::computeCurrentTime(QInputEvent *event)
922 {
923     if (0 != event->timestamp() && QQuickItemPrivate::consistentTime == -1)
924         return event->timestamp();
925
926     return QQuickItemPrivate::elapsed(timer);
927 }
928
929 void QQuickFlickablePrivate::handleMousePressEvent(QMouseEvent *event)
930 {
931     Q_Q(QQuickFlickable);
932     QQuickItemPrivate::start(timer);
933     if (interactive && timeline.isActive()
934         && ((qAbs(hData.smoothVelocity.value()) > RetainGrabVelocity && !hData.fixingUp && !hData.inOvershoot)
935             || (qAbs(vData.smoothVelocity.value()) > RetainGrabVelocity && !vData.fixingUp && !vData.inOvershoot))) {
936         stealMouse = true; // If we've been flicked then steal the click.
937         int flickTime = timeline.time();
938         if (flickTime > 600) {
939             // too long between flicks - cancel boost
940             hData.continuousFlickVelocity = 0;
941             vData.continuousFlickVelocity = 0;
942             flickBoost = 1.0;
943         } else {
944             hData.continuousFlickVelocity = -hData.smoothVelocity.value();
945             vData.continuousFlickVelocity = -vData.smoothVelocity.value();
946             if (flickTime > 300) // slower flicking - reduce boost
947                 flickBoost = qMax(1.0, flickBoost - 0.5);
948         }
949     } else {
950         stealMouse = false;
951         hData.continuousFlickVelocity = 0;
952         vData.continuousFlickVelocity = 0;
953         flickBoost = 1.0;
954     }
955     q->setKeepMouseGrab(stealMouse);
956     pressed = true;
957     if (hData.transitionToBounds)
958         hData.transitionToBounds->stopTransition();
959     if (vData.transitionToBounds)
960         vData.transitionToBounds->stopTransition();
961     if (!hData.fixingUp)
962         resetTimeline(hData);
963     if (!vData.fixingUp)
964         resetTimeline(vData);
965
966     hData.reset();
967     vData.reset();
968     hData.dragMinBound = q->minXExtent();
969     vData.dragMinBound = q->minYExtent();
970     hData.dragMaxBound = q->maxXExtent();
971     vData.dragMaxBound = q->maxYExtent();
972     fixupMode = Normal;
973     lastPos = QPointF();
974     pressPos = event->localPos();
975     hData.pressPos = hData.move.value();
976     vData.pressPos = vData.move.value();
977     bool wasFlicking = hData.flicking || vData.flicking;
978     if (hData.flicking) {
979         hData.flicking = false;
980         emit q->flickingHorizontallyChanged();
981     }
982     if (vData.flicking) {
983         vData.flicking = false;
984         emit q->flickingVerticallyChanged();
985     }
986     if (wasFlicking)
987         emit q->flickingChanged();
988     lastPosTime = lastPressTime = computeCurrentTime(event);
989     QQuickItemPrivate::start(vData.velocityTime);
990     QQuickItemPrivate::start(hData.velocityTime);
991 }
992
993 void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event)
994 {
995     Q_Q(QQuickFlickable);
996     if (!interactive || lastPosTime == -1)
997         return;
998     bool rejectY = false;
999     bool rejectX = false;
1000
1001     bool stealY = stealMouse;
1002     bool stealX = stealMouse;
1003
1004     bool prevHMoved = hMoved;
1005     bool prevVMoved = vMoved;
1006
1007     qint64 elapsedSincePress = computeCurrentTime(event) - lastPressTime;
1008     if (q->yflick()) {
1009         qreal dy = event->localPos().y() - pressPos.y();
1010         bool overThreshold = QQuickWindowPrivate::dragOverThreshold(dy, Qt::YAxis, event);
1011         if (overThreshold || elapsedSincePress > 200) {
1012             if (!vMoved)
1013                 vData.dragStartOffset = dy;
1014             qreal newY = dy + vData.pressPos - vData.dragStartOffset;
1015             const qreal minY = vData.dragMinBound;
1016             const qreal maxY = vData.dragMaxBound;
1017             if (newY > minY)
1018                 newY = minY + (newY - minY) / 2;
1019             if (newY < maxY && maxY - minY <= 0)
1020                 newY = maxY + (newY - maxY) / 2;
1021             if (boundsBehavior == QQuickFlickable::StopAtBounds && (newY > minY || newY < maxY)) {
1022                 rejectY = true;
1023                 if (newY < maxY) {
1024                     newY = maxY;
1025                     rejectY = false;
1026                 }
1027                 if (newY > minY) {
1028                     newY = minY;
1029                     rejectY = false;
1030                 }
1031             }
1032             if (!rejectY && stealMouse && dy != 0.0) {
1033                 clearTimeline();
1034                 vData.move.setValue(newY);
1035                 vMoved = true;
1036             }
1037             if (overThreshold)
1038                 stealY = true;
1039         }
1040     }
1041
1042     if (q->xflick()) {
1043         qreal dx = event->localPos().x() - pressPos.x();
1044         bool overThreshold = QQuickWindowPrivate::dragOverThreshold(dx, Qt::XAxis, event);
1045         if (overThreshold || elapsedSincePress > 200) {
1046             if (!hMoved)
1047                 hData.dragStartOffset = dx;
1048             qreal newX = dx + hData.pressPos - hData.dragStartOffset;
1049             const qreal minX = hData.dragMinBound;
1050             const qreal maxX = hData.dragMaxBound;
1051             if (newX > minX)
1052                 newX = minX + (newX - minX) / 2;
1053             if (newX < maxX && maxX - minX <= 0)
1054                 newX = maxX + (newX - maxX) / 2;
1055             if (boundsBehavior == QQuickFlickable::StopAtBounds && (newX > minX || newX < maxX)) {
1056                 rejectX = true;
1057                 if (newX < maxX) {
1058                     newX = maxX;
1059                     rejectX = false;
1060                 }
1061                 if (newX > minX) {
1062                     newX = minX;
1063                     rejectX = false;
1064                 }
1065             }
1066             if (!rejectX && stealMouse && dx != 0.0) {
1067                 clearTimeline();
1068                 hData.move.setValue(newX);
1069                 hMoved = true;
1070             }
1071
1072             if (overThreshold)
1073                 stealX = true;
1074         }
1075     }
1076
1077     stealMouse = stealX || stealY;
1078     if (stealMouse)
1079         q->setKeepMouseGrab(true);
1080
1081     if (rejectY) {
1082         vData.velocityBuffer.clear();
1083         vData.velocity = 0;
1084     }
1085     if (rejectX) {
1086         hData.velocityBuffer.clear();
1087         hData.velocity = 0;
1088     }
1089
1090     if ((hMoved && !prevHMoved) || (vMoved && !prevVMoved)) {
1091         draggingStarting();
1092         q->movementStarting();
1093     }
1094
1095     qint64 currentTimestamp = computeCurrentTime(event);
1096     qreal elapsed = qreal(currentTimestamp - (lastPos.isNull() ? lastPressTime : lastPosTime)) / 1000.;
1097     if (elapsed <= 0)
1098         return;
1099     lastPosTime = currentTimestamp;
1100     if (q->yflick() && !rejectY) {
1101         if (QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity) {
1102             vData.addVelocitySample(QGuiApplicationPrivate::mouseEventVelocity(event).y(), maxVelocity);
1103         } else {
1104             qreal dy = event->localPos().y() - (lastPos.isNull() ? pressPos.y() : lastPos.y());
1105             vData.addVelocitySample(dy/elapsed, maxVelocity);
1106         }
1107     }
1108     if (q->xflick() && !rejectX) {
1109         if (QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity) {
1110             hData.addVelocitySample(QGuiApplicationPrivate::mouseEventVelocity(event).x(), maxVelocity);
1111         } else {
1112             qreal dx = event->localPos().x() - (lastPos.isNull() ? pressPos.x() : lastPos.x());
1113             hData.addVelocitySample(dx/elapsed, maxVelocity);
1114         }
1115     }
1116
1117     lastPos = event->localPos();
1118 }
1119
1120 void QQuickFlickablePrivate::handleMouseReleaseEvent(QMouseEvent *event)
1121 {
1122     Q_Q(QQuickFlickable);
1123     stealMouse = false;
1124     q->setKeepMouseGrab(false);
1125     pressed = false;
1126
1127     // if we drag then pause before release we should not cause a flick.
1128     qint64 elapsed = computeCurrentTime(event) - lastPosTime;
1129
1130     vData.updateVelocity();
1131     hData.updateVelocity();
1132
1133     draggingEnding();
1134
1135     if (lastPosTime == -1)
1136         return;
1137
1138     hData.vTime = vData.vTime = timeline.time();
1139
1140     bool canBoost = false;
1141
1142     qreal vVelocity = 0;
1143     if (elapsed < 100 && vData.velocity != 0.) {
1144         vVelocity = (QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity)
1145                 ? QGuiApplicationPrivate::mouseEventVelocity(event).y() : vData.velocity;
1146     }
1147     if ((vData.atBeginning && vVelocity > 0.) || (vData.atEnd && vVelocity < 0.)) {
1148         vVelocity /= 2;
1149     } else if (vData.continuousFlickVelocity != 0.0
1150                && vData.viewSize/q->height() > QML_FLICK_MULTIFLICK_RATIO
1151                && ((vVelocity > 0) == (vData.continuousFlickVelocity > 0))
1152                && qAbs(vVelocity) > QML_FLICK_MULTIFLICK_THRESHOLD) {
1153         // accelerate flick for large view flicked quickly
1154         canBoost = true;
1155     }
1156
1157     qreal hVelocity = 0;
1158     if (elapsed < 100 && hData.velocity != 0.) {
1159         hVelocity = (QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity)
1160                 ? QGuiApplicationPrivate::mouseEventVelocity(event).x() : hData.velocity;
1161     }
1162     if ((hData.atBeginning && hVelocity > 0.) || (hData.atEnd && hVelocity < 0.)) {
1163         hVelocity /= 2;
1164     } else if (hData.continuousFlickVelocity != 0.0
1165                && hData.viewSize/q->width() > QML_FLICK_MULTIFLICK_RATIO
1166                && ((hVelocity > 0) == (hData.continuousFlickVelocity > 0))
1167                && qAbs(hVelocity) > QML_FLICK_MULTIFLICK_THRESHOLD) {
1168         // accelerate flick for large view flicked quickly
1169         canBoost = true;
1170     }
1171
1172     flickBoost = canBoost ? qBound(1.0, flickBoost+0.25, QML_FLICK_MULTIFLICK_MAXBOOST) : 1.0;
1173
1174     bool flickedV = false;
1175     vVelocity *= flickBoost;
1176     if (q->yflick() && qAbs(vVelocity) > MinimumFlickVelocity && qAbs(event->localPos().y() - pressPos.y()) > FlickThreshold) {
1177         velocityTimeline.reset(vData.smoothVelocity);
1178         vData.smoothVelocity.setValue(-vVelocity);
1179         flickedV = flickY(vVelocity);
1180     } else {
1181         fixupY();
1182     }
1183
1184     bool flickedH = false;
1185     hVelocity *= flickBoost;
1186     if (q->xflick() && qAbs(hVelocity) > MinimumFlickVelocity && qAbs(event->localPos().x() - pressPos.x()) > FlickThreshold) {
1187         velocityTimeline.reset(hData.smoothVelocity);
1188         hData.smoothVelocity.setValue(-hVelocity);
1189         flickedH = flickX(hVelocity);
1190     } else {
1191         fixupX();
1192     }
1193
1194     flickingStarted(flickedH, flickedV);
1195     if (!isViewMoving())
1196         q->movementEnding();
1197 }
1198
1199 void QQuickFlickable::mousePressEvent(QMouseEvent *event)
1200 {
1201     Q_D(QQuickFlickable);
1202     if (d->interactive) {
1203         if (!d->pressed)
1204             d->handleMousePressEvent(event);
1205         event->accept();
1206     } else {
1207         QQuickItem::mousePressEvent(event);
1208     }
1209 }
1210
1211 void QQuickFlickable::mouseMoveEvent(QMouseEvent *event)
1212 {
1213     Q_D(QQuickFlickable);
1214     if (d->interactive) {
1215         d->handleMouseMoveEvent(event);
1216         event->accept();
1217     } else {
1218         QQuickItem::mouseMoveEvent(event);
1219     }
1220 }
1221
1222 void QQuickFlickable::mouseReleaseEvent(QMouseEvent *event)
1223 {
1224     Q_D(QQuickFlickable);
1225     if (d->interactive) {
1226         d->clearDelayedPress();
1227         d->handleMouseReleaseEvent(event);
1228         event->accept();
1229         if (window() && window()->mouseGrabberItem() == this)
1230             ungrabMouse();
1231     } else {
1232         QQuickItem::mouseReleaseEvent(event);
1233     }
1234 }
1235
1236 void QQuickFlickable::wheelEvent(QWheelEvent *event)
1237 {
1238     Q_D(QQuickFlickable);
1239     if (!d->interactive) {
1240         QQuickItem::wheelEvent(event);
1241         return;
1242     }
1243
1244     int yDelta = event->angleDelta().y();
1245     int xDelta = event->angleDelta().x();
1246     if (yflick() && yDelta != 0) {
1247         bool valid = false;
1248         if (yDelta > 0 && contentY() > -minYExtent()) {
1249             d->vData.velocity = qMax(yDelta*2 - d->vData.smoothVelocity.value(), qreal(d->maxVelocity/4));
1250             valid = true;
1251         } else if (yDelta < 0 && contentY() < -maxYExtent()) {
1252             d->vData.velocity = qMin(yDelta*2 - d->vData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
1253             valid = true;
1254         }
1255         if (valid) {
1256             d->vData.flicking = false;
1257             d->flickY(d->vData.velocity);
1258             d->flickingStarted(false, true);
1259             if (d->vData.flicking) {
1260                 d->vMoved = true;
1261                 movementStarting();
1262             }
1263             event->accept();
1264         }
1265     }
1266     if (xflick() && xDelta != 0) {
1267         bool valid = false;
1268         if (xDelta > 0 && contentX() > -minXExtent()) {
1269             d->hData.velocity = qMax(xDelta*2 - d->hData.smoothVelocity.value(), qreal(d->maxVelocity/4));
1270             valid = true;
1271         } else if (xDelta < 0 && contentX() < -maxXExtent()) {
1272             d->hData.velocity = qMin(xDelta*2 - d->hData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
1273             valid = true;
1274         }
1275         if (valid) {
1276             d->hData.flicking = false;
1277             d->flickX(d->hData.velocity);
1278             d->flickingStarted(true, false);
1279             if (d->hData.flicking) {
1280                 d->hMoved = true;
1281                 movementStarting();
1282             }
1283             event->accept();
1284         }
1285     }
1286
1287     if (!event->isAccepted())
1288         QQuickItem::wheelEvent(event);
1289 }
1290
1291 bool QQuickFlickablePrivate::isOutermostPressDelay() const
1292 {
1293     Q_Q(const QQuickFlickable);
1294     QQuickItem *item = q->parentItem();
1295     while (item) {
1296         QQuickFlickable *flick = qobject_cast<QQuickFlickable*>(item);
1297         if (flick && flick->pressDelay() > 0 && flick->isInteractive())
1298             return false;
1299         item = item->parentItem();
1300     }
1301
1302     return true;
1303 }
1304
1305 void QQuickFlickablePrivate::captureDelayedPress(QMouseEvent *event)
1306 {
1307     Q_Q(QQuickFlickable);
1308     if (!q->window() || pressDelay <= 0)
1309         return;
1310     if (!isOutermostPressDelay())
1311         return;
1312     delayedPressTarget = q->window()->mouseGrabberItem();
1313     delayedPressEvent = QQuickWindowPrivate::cloneMouseEvent(event);
1314     delayedPressEvent->setAccepted(false);
1315     delayedPressTimer.start(pressDelay, q);
1316 }
1317
1318 void QQuickFlickablePrivate::clearDelayedPress()
1319 {
1320     if (delayedPressEvent) {
1321         delayedPressTimer.stop();
1322         delete delayedPressEvent;
1323         delayedPressEvent = 0;
1324     }
1325 }
1326
1327 //XXX pixelAligned ignores the global position of the Flickable, i.e. assumes Flickable itself is pixel aligned.
1328 void QQuickFlickablePrivate::setViewportX(qreal x)
1329 {
1330     contentItem->setX(pixelAligned ? qRound(x) : x);
1331 }
1332
1333 void QQuickFlickablePrivate::setViewportY(qreal y)
1334 {
1335     contentItem->setY(pixelAligned ? qRound(y) : y);
1336 }
1337
1338 void QQuickFlickable::timerEvent(QTimerEvent *event)
1339 {
1340     Q_D(QQuickFlickable);
1341     if (event->timerId() == d->delayedPressTimer.timerId()) {
1342         d->delayedPressTimer.stop();
1343         if (d->delayedPressEvent) {
1344             QQuickItem *grabber = window() ? window()->mouseGrabberItem() : 0;
1345             if (!grabber || grabber != this) {
1346                 // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
1347                 // so we reset the grabber
1348                 if (window()->mouseGrabberItem() == d->delayedPressTarget)
1349                     d->delayedPressTarget->ungrabMouse();
1350                 // Use the event handler that will take care of finding the proper item to propagate the event
1351                 QQuickWindowPrivate::get(window())->deliverMouseEvent(d->delayedPressEvent);
1352             }
1353             delete d->delayedPressEvent;
1354             d->delayedPressEvent = 0;
1355         }
1356     }
1357 }
1358
1359 qreal QQuickFlickable::minYExtent() const
1360 {
1361     Q_D(const QQuickFlickable);
1362     return d->vData.startMargin;
1363 }
1364
1365 qreal QQuickFlickable::minXExtent() const
1366 {
1367     Q_D(const QQuickFlickable);
1368     return d->hData.startMargin;
1369 }
1370
1371 /* returns -ve */
1372 qreal QQuickFlickable::maxXExtent() const
1373 {
1374     Q_D(const QQuickFlickable);
1375     return width() - vWidth() - d->hData.endMargin;
1376 }
1377 /* returns -ve */
1378 qreal QQuickFlickable::maxYExtent() const
1379 {
1380     Q_D(const QQuickFlickable);
1381     return height() - vHeight() - d->vData.endMargin;
1382 }
1383
1384 void QQuickFlickable::componentComplete()
1385 {
1386     Q_D(QQuickFlickable);
1387     QQuickItem::componentComplete();
1388     if (!d->hData.explicitValue && d->hData.startMargin != 0.)
1389         setContentX(-minXExtent());
1390     if (!d->vData.explicitValue && d->vData.startMargin != 0.)
1391         setContentY(-minYExtent());
1392 }
1393
1394 void QQuickFlickable::viewportMoved(Qt::Orientations orient)
1395 {
1396     Q_D(QQuickFlickable);
1397     if (orient & Qt::Vertical)
1398         d->viewportAxisMoved(d->vData, minYExtent(), maxYExtent(), height(), d->fixupY_callback);
1399     if (orient & Qt::Horizontal)
1400         d->viewportAxisMoved(d->hData, minXExtent(), maxXExtent(), width(), d->fixupX_callback);
1401     d->updateBeginningEnd();
1402 }
1403
1404 void QQuickFlickablePrivate::viewportAxisMoved(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1405                                            QQuickTimeLineCallback::Callback fixupCallback)
1406 {
1407     if (pressed || calcVelocity) {
1408         int elapsed = QQuickItemPrivate::restart(data.velocityTime);
1409         if (elapsed > 0) {
1410             qreal velocity = (data.lastPos - data.move.value()) * 1000 / elapsed;
1411             if (qAbs(velocity) > 0) {
1412                 velocityTimeline.reset(data.smoothVelocity);
1413                 if (calcVelocity)
1414                     velocityTimeline.set(data.smoothVelocity, velocity);
1415                 else
1416                     velocityTimeline.move(data.smoothVelocity, velocity, reportedVelocitySmoothing);
1417                 velocityTimeline.move(data.smoothVelocity, 0, reportedVelocitySmoothing);
1418             }
1419         }
1420     } else {
1421         if (timeline.time() > data.vTime) {
1422             velocityTimeline.reset(data.smoothVelocity);
1423             qreal velocity = (data.lastPos - data.move.value()) * 1000 / (timeline.time() - data.vTime);
1424             data.smoothVelocity.setValue(velocity);
1425         }
1426     }
1427
1428     if (!data.inOvershoot && !data.fixingUp && data.flicking
1429             && (data.move.value() > minExtent || data.move.value() < maxExtent)
1430             && qAbs(data.smoothVelocity.value()) > 10) {
1431         // Increase deceleration if we've passed a bound
1432         qreal overBound = data.move.value() > minExtent
1433                 ? data.move.value() - minExtent
1434                 : maxExtent - data.move.value();
1435         data.inOvershoot = true;
1436         qreal maxDistance = overShootDistance(vSize) - overBound;
1437         resetTimeline(data);
1438         if (maxDistance > 0)
1439             timeline.accel(data.move, -data.smoothVelocity.value(), deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance);
1440         timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
1441     }
1442
1443     data.lastPos = data.move.value();
1444     data.vTime = timeline.time();
1445 }
1446
1447 void QQuickFlickable::geometryChanged(const QRectF &newGeometry,
1448                              const QRectF &oldGeometry)
1449 {
1450     Q_D(QQuickFlickable);
1451     QQuickItem::geometryChanged(newGeometry, oldGeometry);
1452
1453     bool changed = false;
1454     if (newGeometry.width() != oldGeometry.width()) {
1455         if (xflick())
1456             changed = true;
1457         if (d->hData.viewSize < 0) {
1458             d->contentItem->setWidth(width());
1459             emit contentWidthChanged();
1460         }
1461         // Make sure that we're entirely in view.
1462         if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1463             d->fixupMode = QQuickFlickablePrivate::Immediate;
1464             d->fixupX();
1465         }
1466     }
1467     if (newGeometry.height() != oldGeometry.height()) {
1468         if (yflick())
1469             changed = true;
1470         if (d->vData.viewSize < 0) {
1471             d->contentItem->setHeight(height());
1472             emit contentHeightChanged();
1473         }
1474         // Make sure that we're entirely in view.
1475         if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1476             d->fixupMode = QQuickFlickablePrivate::Immediate;
1477             d->fixupY();
1478         }
1479     }
1480
1481     if (changed)
1482         d->updateBeginningEnd();
1483 }
1484
1485 /*!
1486     \qmlmethod QtQuick2::Flickable::flick(qreal xVelocity, qreal yVelocity)
1487
1488     Flicks the content with \a xVelocity horizontally and \a yVelocity vertically in pixels/sec.
1489 */
1490
1491 void QQuickFlickable::flick(qreal xVelocity, qreal yVelocity)
1492 {
1493     Q_D(QQuickFlickable);
1494     bool flickedX = d->flickX(xVelocity);
1495     bool flickedY = d->flickY(yVelocity);
1496     d->flickingStarted(flickedX, flickedY);
1497 }
1498
1499 void QQuickFlickablePrivate::flickingStarted(bool flickingH, bool flickingV)
1500 {
1501     Q_Q(QQuickFlickable);
1502     if (!flickingH && !flickingV)
1503         return;
1504
1505     bool wasFlicking = hData.flicking || vData.flicking;
1506     if (flickingH && !hData.flicking) {
1507         hData.flicking = true;
1508         emit q->flickingHorizontallyChanged();
1509     }
1510     if (flickingV && !vData.flicking) {
1511         vData.flicking = true;
1512         emit q->flickingVerticallyChanged();
1513     }
1514     if (!wasFlicking && (hData.flicking || vData.flicking)) {
1515         emit q->flickingChanged();
1516         emit q->flickStarted();
1517     }
1518 }
1519
1520 /*!
1521     \qmlmethod QtQuick2::Flickable::cancelFlick()
1522
1523     Cancels the current flick animation.
1524 */
1525
1526 void QQuickFlickable::cancelFlick()
1527 {
1528     Q_D(QQuickFlickable);
1529     d->resetTimeline(d->hData);
1530     d->resetTimeline(d->vData);
1531     movementEnding();
1532 }
1533
1534 void QQuickFlickablePrivate::data_append(QQmlListProperty<QObject> *prop, QObject *o)
1535 {
1536     if (QQuickItem *i = qmlobject_cast<QQuickItem *>(o)) {
1537         i->setParentItem(static_cast<QQuickFlickablePrivate*>(prop->data)->contentItem);
1538     } else {
1539         o->setParent(prop->object); // XXX todo - do we want this?
1540     }
1541 }
1542
1543 int QQuickFlickablePrivate::data_count(QQmlListProperty<QObject> *)
1544 {
1545     // XXX todo
1546     return 0;
1547 }
1548
1549 QObject *QQuickFlickablePrivate::data_at(QQmlListProperty<QObject> *, int)
1550 {
1551     // XXX todo
1552     return 0;
1553 }
1554
1555 void QQuickFlickablePrivate::data_clear(QQmlListProperty<QObject> *)
1556 {
1557     // XXX todo
1558 }
1559
1560 QQmlListProperty<QObject> QQuickFlickable::flickableData()
1561 {
1562     Q_D(QQuickFlickable);
1563     return QQmlListProperty<QObject>(this, (void *)d, QQuickFlickablePrivate::data_append,
1564                                              QQuickFlickablePrivate::data_count,
1565                                              QQuickFlickablePrivate::data_at,
1566                                              QQuickFlickablePrivate::data_clear);
1567 }
1568
1569 QQmlListProperty<QQuickItem> QQuickFlickable::flickableChildren()
1570 {
1571     Q_D(QQuickFlickable);
1572     return QQuickItemPrivate::get(d->contentItem)->children();
1573 }
1574
1575 /*!
1576     \qmlproperty enumeration QtQuick2::Flickable::boundsBehavior
1577     This property holds whether the surface may be dragged
1578     beyond the Flickable's boundaries, or overshoot the
1579     Flickable's boundaries when flicked.
1580
1581     This enables the feeling that the edges of the view are soft,
1582     rather than a hard physical boundary.
1583
1584     The \c boundsBehavior can be one of:
1585
1586     \list
1587     \li Flickable.StopAtBounds - the contents can not be dragged beyond the boundary
1588     of the flickable, and flicks will not overshoot.
1589     \li Flickable.DragOverBounds - the contents can be dragged beyond the boundary
1590     of the Flickable, but flicks will not overshoot.
1591     \li Flickable.DragAndOvershootBounds (default) - the contents can be dragged
1592     beyond the boundary of the Flickable, and can overshoot the
1593     boundary when flicked.
1594     \endlist
1595 */
1596 QQuickFlickable::BoundsBehavior QQuickFlickable::boundsBehavior() const
1597 {
1598     Q_D(const QQuickFlickable);
1599     return d->boundsBehavior;
1600 }
1601
1602 void QQuickFlickable::setBoundsBehavior(BoundsBehavior b)
1603 {
1604     Q_D(QQuickFlickable);
1605     if (b == d->boundsBehavior)
1606         return;
1607     d->boundsBehavior = b;
1608     emit boundsBehaviorChanged();
1609 }
1610
1611 /*!
1612     \qmlproperty Transition QtQuick2::Flickable::rebound
1613
1614     This holds the transition to be applied to the content view when
1615     it snaps back to the bounds of the flickable. The transition is
1616     triggered when the view is flicked or dragged past the edge of the
1617     content area, or when returnToBounds() is called.
1618
1619     \qml
1620     import QtQuick 2.0
1621
1622     Flickable {
1623         width: 150; height: 150
1624         contentWidth: 300; contentHeight: 300
1625
1626         rebound: Transition {
1627             NumberAnimation {
1628                 properties: "x,y"
1629                 duration: 1000
1630                 easing.type: Easing.OutBounce
1631             }
1632         }
1633
1634         Rectangle {
1635             width: 300; height: 300
1636             gradient: Gradient {
1637                 GradientStop { position: 0.0; color: "lightsteelblue" }
1638                 GradientStop { position: 1.0; color: "blue" }
1639             }
1640         }
1641     }
1642     \endqml
1643
1644     When the above view is flicked beyond its bounds, it will return to its
1645     bounds using the transition specified:
1646
1647     \image flickable-rebound.gif
1648
1649     If this property is not set, a default animation is applied.
1650   */
1651 QQuickTransition *QQuickFlickable::rebound() const
1652 {
1653     Q_D(const QQuickFlickable);
1654     return d->rebound;
1655 }
1656
1657 void QQuickFlickable::setRebound(QQuickTransition *transition)
1658 {
1659     Q_D(QQuickFlickable);
1660     if (transition) {
1661         if (!d->hData.transitionToBounds)
1662             d->hData.transitionToBounds = new QQuickFlickableReboundTransition(this, QLatin1String("x"));
1663         if (!d->vData.transitionToBounds)
1664             d->vData.transitionToBounds = new QQuickFlickableReboundTransition(this, QLatin1String("y"));
1665     }
1666     if (d->rebound != transition) {
1667         d->rebound = transition;
1668         emit reboundChanged();
1669     }
1670 }
1671
1672 /*!
1673     \qmlproperty real QtQuick2::Flickable::contentWidth
1674     \qmlproperty real QtQuick2::Flickable::contentHeight
1675
1676     The dimensions of the content (the surface controlled by Flickable).
1677     This should typically be set to the combined size of the items placed in the
1678     Flickable.
1679
1680     The following snippet shows how these properties are used to display
1681     an image that is larger than the Flickable item itself:
1682
1683     \snippet qml/flickable.qml document
1684
1685     In some cases, the content dimensions can be automatically set
1686     based on the \l {Item::childrenRect.width}{childrenRect.width}
1687     and \l {Item::childrenRect.height}{childrenRect.height} properties
1688     of the \l contentItem. For example, the previous snippet could be rewritten with:
1689
1690     \qml
1691     contentWidth: contentItem.childrenRect.width; contentHeight: contentItem.childrenRect.height
1692     \endqml
1693
1694     Though this assumes that the origin of the childrenRect is 0,0.
1695 */
1696 qreal QQuickFlickable::contentWidth() const
1697 {
1698     Q_D(const QQuickFlickable);
1699     return d->hData.viewSize;
1700 }
1701
1702 void QQuickFlickable::setContentWidth(qreal w)
1703 {
1704     Q_D(QQuickFlickable);
1705     if (d->hData.viewSize == w)
1706         return;
1707     d->hData.viewSize = w;
1708     if (w < 0)
1709         d->contentItem->setWidth(width());
1710     else
1711         d->contentItem->setWidth(w);
1712     d->hData.markExtentsDirty();
1713     // Make sure that we're entirely in view.
1714     if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1715         d->fixupMode = QQuickFlickablePrivate::Immediate;
1716         d->fixupX();
1717     } else if (!d->pressed && d->hData.fixingUp) {
1718         d->fixupMode = QQuickFlickablePrivate::ExtentChanged;
1719         d->fixupX();
1720     }
1721     emit contentWidthChanged();
1722     d->updateBeginningEnd();
1723 }
1724
1725 qreal QQuickFlickable::contentHeight() const
1726 {
1727     Q_D(const QQuickFlickable);
1728     return d->vData.viewSize;
1729 }
1730
1731 void QQuickFlickable::setContentHeight(qreal h)
1732 {
1733     Q_D(QQuickFlickable);
1734     if (d->vData.viewSize == h)
1735         return;
1736     d->vData.viewSize = h;
1737     if (h < 0)
1738         d->contentItem->setHeight(height());
1739     else
1740         d->contentItem->setHeight(h);
1741     d->vData.markExtentsDirty();
1742     // Make sure that we're entirely in view.
1743     if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1744         d->fixupMode = QQuickFlickablePrivate::Immediate;
1745         d->fixupY();
1746     } else if (!d->pressed && d->vData.fixingUp) {
1747         d->fixupMode = QQuickFlickablePrivate::ExtentChanged;
1748         d->fixupY();
1749     }
1750     emit contentHeightChanged();
1751     d->updateBeginningEnd();
1752 }
1753
1754 /*!
1755     \qmlproperty real QtQuick2::Flickable::topMargin
1756     \qmlproperty real QtQuick2::Flickable::leftMargin
1757     \qmlproperty real QtQuick2::Flickable::bottomMargin
1758     \qmlproperty real QtQuick2::Flickable::rightMargin
1759
1760     These properties hold the margins around the content.  This space is reserved
1761     in addition to the contentWidth and contentHeight.
1762 */
1763
1764
1765 qreal QQuickFlickable::topMargin() const
1766 {
1767     Q_D(const QQuickFlickable);
1768     return d->vData.startMargin;
1769 }
1770
1771 void QQuickFlickable::setTopMargin(qreal m)
1772 {
1773     Q_D(QQuickFlickable);
1774     if (d->vData.startMargin == m)
1775         return;
1776     d->vData.startMargin = m;
1777     d->vData.markExtentsDirty();
1778     if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1779         d->fixupMode = QQuickFlickablePrivate::Immediate;
1780         d->fixupY();
1781     }
1782     emit topMarginChanged();
1783     d->updateBeginningEnd();
1784 }
1785
1786 qreal QQuickFlickable::bottomMargin() const
1787 {
1788     Q_D(const QQuickFlickable);
1789     return d->vData.endMargin;
1790 }
1791
1792 void QQuickFlickable::setBottomMargin(qreal m)
1793 {
1794     Q_D(QQuickFlickable);
1795     if (d->vData.endMargin == m)
1796         return;
1797     d->vData.endMargin = m;
1798     d->vData.markExtentsDirty();
1799     if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1800         d->fixupMode = QQuickFlickablePrivate::Immediate;
1801         d->fixupY();
1802     }
1803     emit bottomMarginChanged();
1804     d->updateBeginningEnd();
1805 }
1806
1807 qreal QQuickFlickable::leftMargin() const
1808 {
1809     Q_D(const QQuickFlickable);
1810     return d->hData.startMargin;
1811 }
1812
1813 void QQuickFlickable::setLeftMargin(qreal m)
1814 {
1815     Q_D(QQuickFlickable);
1816     if (d->hData.startMargin == m)
1817         return;
1818     d->hData.startMargin = m;
1819     d->hData.markExtentsDirty();
1820     if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1821         d->fixupMode = QQuickFlickablePrivate::Immediate;
1822         d->fixupX();
1823     }
1824     emit leftMarginChanged();
1825     d->updateBeginningEnd();
1826 }
1827
1828 qreal QQuickFlickable::rightMargin() const
1829 {
1830     Q_D(const QQuickFlickable);
1831     return d->hData.endMargin;
1832 }
1833
1834 void QQuickFlickable::setRightMargin(qreal m)
1835 {
1836     Q_D(QQuickFlickable);
1837     if (d->hData.endMargin == m)
1838         return;
1839     d->hData.endMargin = m;
1840     d->hData.markExtentsDirty();
1841     if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1842         d->fixupMode = QQuickFlickablePrivate::Immediate;
1843         d->fixupX();
1844     }
1845     emit rightMarginChanged();
1846     d->updateBeginningEnd();
1847 }
1848
1849 /*!
1850     \qmlproperty real QtQuick2::Flickable::originX
1851     \qmlproperty real QtQuick2::Flickable::originY
1852
1853     These properties hold the origin of the content. This value always refers
1854     to the top-left position of the content regardless of layout direction.
1855
1856     This is usually (0,0), however ListView and GridView may have an arbitrary
1857     origin due to delegate size variation, or item insertion/removal outside
1858     the visible region.
1859 */
1860
1861 qreal QQuickFlickable::originY() const
1862 {
1863     Q_D(const QQuickFlickable);
1864     return -minYExtent() + d->vData.startMargin;
1865 }
1866
1867 qreal QQuickFlickable::originX() const
1868 {
1869     Q_D(const QQuickFlickable);
1870     return -minXExtent() + d->hData.startMargin;
1871 }
1872
1873
1874 /*!
1875     \qmlmethod QtQuick2::Flickable::resizeContent(real width, real height, QPointF center)
1876
1877     Resizes the content to \a width x \a height about \a center.
1878
1879     This does not scale the contents of the Flickable - it only resizes the \l contentWidth
1880     and \l contentHeight.
1881
1882     Resizing the content may result in the content being positioned outside
1883     the bounds of the Flickable.  Calling \l returnToBounds() will
1884     move the content back within legal bounds.
1885 */
1886 void QQuickFlickable::resizeContent(qreal w, qreal h, QPointF center)
1887 {
1888     Q_D(QQuickFlickable);
1889     if (w != d->hData.viewSize) {
1890         qreal oldSize = d->hData.viewSize;
1891         d->hData.viewSize = w;
1892         d->contentItem->setWidth(w);
1893         emit contentWidthChanged();
1894         if (center.x() != 0) {
1895             qreal pos = center.x() * w / oldSize;
1896             setContentX(contentX() + pos - center.x());
1897         }
1898     }
1899     if (h != d->vData.viewSize) {
1900         qreal oldSize = d->vData.viewSize;
1901         d->vData.viewSize = h;
1902         d->contentItem->setHeight(h);
1903         emit contentHeightChanged();
1904         if (center.y() != 0) {
1905             qreal pos = center.y() * h / oldSize;
1906             setContentY(contentY() + pos - center.y());
1907         }
1908     }
1909     d->updateBeginningEnd();
1910 }
1911
1912 /*!
1913     \qmlmethod QtQuick2::Flickable::returnToBounds()
1914
1915     Ensures the content is within legal bounds.
1916
1917     This may be called to ensure that the content is within legal bounds
1918     after manually positioning the content.
1919 */
1920 void QQuickFlickable::returnToBounds()
1921 {
1922     Q_D(QQuickFlickable);
1923     d->fixupX();
1924     d->fixupY();
1925 }
1926
1927 qreal QQuickFlickable::vWidth() const
1928 {
1929     Q_D(const QQuickFlickable);
1930     if (d->hData.viewSize < 0)
1931         return width();
1932     else
1933         return d->hData.viewSize;
1934 }
1935
1936 qreal QQuickFlickable::vHeight() const
1937 {
1938     Q_D(const QQuickFlickable);
1939     if (d->vData.viewSize < 0)
1940         return height();
1941     else
1942         return d->vData.viewSize;
1943 }
1944
1945 bool QQuickFlickable::xflick() const
1946 {
1947     Q_D(const QQuickFlickable);
1948     if (d->flickableDirection == QQuickFlickable::AutoFlickDirection)
1949         return floor(qAbs(vWidth() - width()));
1950     return d->flickableDirection & QQuickFlickable::HorizontalFlick;
1951 }
1952
1953 bool QQuickFlickable::yflick() const
1954 {
1955     Q_D(const QQuickFlickable);
1956     if (d->flickableDirection == QQuickFlickable::AutoFlickDirection)
1957         return floor(qAbs(vHeight() - height()));
1958     return d->flickableDirection & QQuickFlickable::VerticalFlick;
1959 }
1960
1961 void QQuickFlickable::mouseUngrabEvent()
1962 {
1963     Q_D(QQuickFlickable);
1964     if (d->pressed) {
1965         // if our mouse grab has been removed (probably by another Flickable),
1966         // fix our state
1967         d->clearDelayedPress();
1968         d->pressed = false;
1969         d->draggingEnding();
1970         d->stealMouse = false;
1971         setKeepMouseGrab(false);
1972         d->fixupX();
1973         d->fixupY();
1974         if (!d->isViewMoving())
1975             movementEnding();
1976     }
1977 }
1978
1979 bool QQuickFlickable::sendMouseEvent(QMouseEvent *event)
1980 {
1981     Q_D(QQuickFlickable);
1982     QPointF localPos = mapFromScene(event->windowPos());
1983
1984     QQuickWindow *c = window();
1985     QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
1986     bool grabberDisabled = grabber && !grabber->isEnabled();
1987     bool stealThisEvent = d->stealMouse;
1988     if ((stealThisEvent || contains(localPos)) && (!grabber || !grabber->keepMouseGrab() || grabberDisabled)) {
1989         QScopedPointer<QMouseEvent> mouseEvent(QQuickWindowPrivate::cloneMouseEvent(event, &localPos));
1990         mouseEvent->setAccepted(false);
1991
1992         switch (mouseEvent->type()) {
1993         case QEvent::MouseMove:
1994             d->handleMouseMoveEvent(mouseEvent.data());
1995             break;
1996         case QEvent::MouseButtonPress:
1997             if (d->pressed) // we are already pressed - this is a delayed replay
1998                 return false;
1999
2000             d->handleMousePressEvent(mouseEvent.data());
2001             d->captureDelayedPress(event);
2002             stealThisEvent = d->stealMouse;   // Update stealThisEvent in case changed by function call above
2003             break;
2004         case QEvent::MouseButtonRelease:
2005             if (d->delayedPressEvent) {
2006                 // We replay the mouse press but the grabber we had might not be interested in the event (e.g. overlay)
2007                 // so we reset the grabber
2008                 if (c->mouseGrabberItem() == d->delayedPressTarget)
2009                     d->delayedPressTarget->ungrabMouse();
2010                 // Use the event handler that will take care of finding the proper item to propagate the event
2011                 QQuickWindowPrivate::get(window())->deliverMouseEvent(d->delayedPressEvent);
2012                 d->clearDelayedPress();
2013                 // We send the release
2014                 window()->sendEvent(c->mouseGrabberItem(), event);
2015                 // And the event has been consumed
2016                 d->stealMouse = false;
2017                 d->pressed = false;
2018                 return true;
2019             }
2020             d->handleMouseReleaseEvent(mouseEvent.data());
2021             break;
2022         default:
2023             break;
2024         }
2025         grabber = qobject_cast<QQuickItem*>(c->mouseGrabberItem());
2026         if ((grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) || grabberDisabled) {
2027             d->clearDelayedPress();
2028             grabMouse();
2029         }
2030
2031         return stealThisEvent || d->delayedPressEvent || grabberDisabled;
2032     } else if (d->lastPosTime != -1) {
2033         d->lastPosTime = -1;
2034         returnToBounds();
2035     }
2036     if (event->type() == QEvent::MouseButtonRelease) {
2037         d->lastPosTime = -1;
2038         d->clearDelayedPress();
2039         d->stealMouse = false;
2040         d->pressed = false;
2041     }
2042     return false;
2043 }
2044
2045
2046 bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e)
2047 {
2048     Q_D(QQuickFlickable);
2049     if (!isVisible() || !isEnabled())
2050         return QQuickItem::childMouseEventFilter(i, e);
2051     switch (e->type()) {
2052     case QEvent::MouseButtonPress:
2053     case QEvent::MouseMove:
2054     case QEvent::MouseButtonRelease:
2055         return sendMouseEvent(static_cast<QMouseEvent *>(e));
2056     case QEvent::UngrabMouse:
2057         if (d->window && d->window->mouseGrabberItem() && d->window->mouseGrabberItem() != this) {
2058             // The grab has been taken away from a child and given to some other item.
2059             mouseUngrabEvent();
2060         }
2061         break;
2062     default:
2063         break;
2064     }
2065
2066     return QQuickItem::childMouseEventFilter(i, e);
2067 }
2068
2069 /*!
2070     \qmlproperty real QtQuick2::Flickable::maximumFlickVelocity
2071     This property holds the maximum velocity that the user can flick the view in pixels/second.
2072
2073     The default value is platform dependent.
2074 */
2075 qreal QQuickFlickable::maximumFlickVelocity() const
2076 {
2077     Q_D(const QQuickFlickable);
2078     return d->maxVelocity;
2079 }
2080
2081 void QQuickFlickable::setMaximumFlickVelocity(qreal v)
2082 {
2083     Q_D(QQuickFlickable);
2084     if (v == d->maxVelocity)
2085         return;
2086     d->maxVelocity = v;
2087     emit maximumFlickVelocityChanged();
2088 }
2089
2090 /*!
2091     \qmlproperty real QtQuick2::Flickable::flickDeceleration
2092     This property holds the rate at which a flick will decelerate.
2093
2094     The default value is platform dependent.
2095 */
2096 qreal QQuickFlickable::flickDeceleration() const
2097 {
2098     Q_D(const QQuickFlickable);
2099     return d->deceleration;
2100 }
2101
2102 void QQuickFlickable::setFlickDeceleration(qreal deceleration)
2103 {
2104     Q_D(QQuickFlickable);
2105     if (deceleration == d->deceleration)
2106         return;
2107     d->deceleration = deceleration;
2108     emit flickDecelerationChanged();
2109 }
2110
2111 bool QQuickFlickable::isFlicking() const
2112 {
2113     Q_D(const QQuickFlickable);
2114     return d->hData.flicking ||  d->vData.flicking;
2115 }
2116
2117 /*!
2118     \qmlproperty bool QtQuick2::Flickable::flicking
2119     \qmlproperty bool QtQuick2::Flickable::flickingHorizontally
2120     \qmlproperty bool QtQuick2::Flickable::flickingVertically
2121
2122     These properties describe whether the view is currently moving horizontally,
2123     vertically or in either direction, due to the user flicking the view.
2124 */
2125 bool QQuickFlickable::isFlickingHorizontally() const
2126 {
2127     Q_D(const QQuickFlickable);
2128     return d->hData.flicking;
2129 }
2130
2131 bool QQuickFlickable::isFlickingVertically() const
2132 {
2133     Q_D(const QQuickFlickable);
2134     return d->vData.flicking;
2135 }
2136
2137 /*!
2138     \qmlproperty bool QtQuick2::Flickable::dragging
2139     \qmlproperty bool QtQuick2::Flickable::draggingHorizontally
2140     \qmlproperty bool QtQuick2::Flickable::draggingVertically
2141
2142     These properties describe whether the view is currently moving horizontally,
2143     vertically or in either direction, due to the user dragging the view.
2144 */
2145 bool QQuickFlickable::isDragging() const
2146 {
2147     Q_D(const QQuickFlickable);
2148     return d->hData.dragging ||  d->vData.dragging;
2149 }
2150
2151 bool QQuickFlickable::isDraggingHorizontally() const
2152 {
2153     Q_D(const QQuickFlickable);
2154     return d->hData.dragging;
2155 }
2156
2157 bool QQuickFlickable::isDraggingVertically() const
2158 {
2159     Q_D(const QQuickFlickable);
2160     return d->vData.dragging;
2161 }
2162
2163 void QQuickFlickablePrivate::draggingStarting()
2164 {
2165     Q_Q(QQuickFlickable);
2166     bool wasDragging = hData.dragging || vData.dragging;
2167     if (hMoved && !hData.dragging) {
2168         hData.dragging = true;
2169         emit q->draggingHorizontallyChanged();
2170     }
2171     if (vMoved && !vData.dragging) {
2172         vData.dragging = true;
2173         emit q->draggingVerticallyChanged();
2174     }
2175     if (!wasDragging && (hData.dragging || vData.dragging)) {
2176         emit q->draggingChanged();
2177         emit q->dragStarted();
2178     }
2179 }
2180
2181 void QQuickFlickablePrivate::draggingEnding()
2182 {
2183     Q_Q(QQuickFlickable);
2184     bool wasDragging = hData.dragging || vData.dragging;
2185     if (hData.dragging) {
2186         hData.dragging = false;
2187         emit q->draggingHorizontallyChanged();
2188     }
2189     if (vData.dragging) {
2190         vData.dragging = false;
2191         emit q->draggingVerticallyChanged();
2192     }
2193     if (wasDragging && !hData.dragging && !vData.dragging) {
2194         emit q->draggingChanged();
2195         emit q->dragEnded();
2196     }
2197 }
2198
2199 bool QQuickFlickablePrivate::isViewMoving() const
2200 {
2201     if (timeline.isActive()
2202             || (hData.transitionToBounds && hData.transitionToBounds->isActive())
2203             || (vData.transitionToBounds && vData.transitionToBounds->isActive()) ) {
2204         return true;
2205     }
2206     return false;
2207 }
2208
2209 /*!
2210     \qmlproperty int QtQuick2::Flickable::pressDelay
2211
2212     This property holds the time to delay (ms) delivering a press to
2213     children of the Flickable.  This can be useful where reacting
2214     to a press before a flicking action has undesirable effects.
2215
2216     If the flickable is dragged/flicked before the delay times out
2217     the press event will not be delivered.  If the button is released
2218     within the timeout, both the press and release will be delivered.
2219
2220     Note that for nested Flickables with pressDelay set, the pressDelay of
2221     inner Flickables is overridden by the outermost Flickable.
2222 */
2223 int QQuickFlickable::pressDelay() const
2224 {
2225     Q_D(const QQuickFlickable);
2226     return d->pressDelay;
2227 }
2228
2229 void QQuickFlickable::setPressDelay(int delay)
2230 {
2231     Q_D(QQuickFlickable);
2232     if (d->pressDelay == delay)
2233         return;
2234     d->pressDelay = delay;
2235     emit pressDelayChanged();
2236 }
2237
2238 /*!
2239     \qmlproperty bool QtQuick2::Flickable::moving
2240     \qmlproperty bool QtQuick2::Flickable::movingHorizontally
2241     \qmlproperty bool QtQuick2::Flickable::movingVertically
2242
2243     These properties describe whether the view is currently moving horizontally,
2244     vertically or in either direction, due to the user either dragging or
2245     flicking the view.
2246 */
2247
2248 bool QQuickFlickable::isMoving() const
2249 {
2250     Q_D(const QQuickFlickable);
2251     return d->hData.moving || d->vData.moving;
2252 }
2253
2254 bool QQuickFlickable::isMovingHorizontally() const
2255 {
2256     Q_D(const QQuickFlickable);
2257     return d->hData.moving;
2258 }
2259
2260 bool QQuickFlickable::isMovingVertically() const
2261 {
2262     Q_D(const QQuickFlickable);
2263     return d->vData.moving;
2264 }
2265
2266 void QQuickFlickable::timelineCompleted()
2267 {
2268     Q_D(QQuickFlickable);
2269     if ( (d->hData.transitionToBounds && d->hData.transitionToBounds->isActive())
2270          || (d->vData.transitionToBounds && d->vData.transitionToBounds->isActive()) ) {
2271         return;
2272     }
2273     movementEnding();
2274 }
2275
2276 void QQuickFlickable::movementStarting()
2277 {
2278     Q_D(QQuickFlickable);
2279     bool wasMoving = d->hData.moving || d->vData.moving;
2280     if (d->hMoved && !d->hData.moving) {
2281         d->hData.moving = true;
2282         emit movingHorizontallyChanged();
2283     }
2284     if (d->vMoved && !d->vData.moving) {
2285         d->vData.moving = true;
2286         emit movingVerticallyChanged();
2287     }
2288
2289     if (!wasMoving && (d->hData.moving || d->vData.moving)) {
2290         emit movingChanged();
2291         emit movementStarted();
2292     }
2293 }
2294
2295 void QQuickFlickable::movementEnding()
2296 {
2297     movementEnding(true, true);
2298 }
2299
2300 void QQuickFlickable::movementEnding(bool hMovementEnding, bool vMovementEnding)
2301 {
2302     Q_D(QQuickFlickable);
2303
2304     // emit flicking signals
2305     bool wasFlicking = d->hData.flicking || d->vData.flicking;
2306     if (hMovementEnding && d->hData.flicking) {
2307         d->hData.flicking = false;
2308         emit flickingHorizontallyChanged();
2309     }
2310     if (vMovementEnding && d->vData.flicking) {
2311         d->vData.flicking = false;
2312         emit flickingVerticallyChanged();
2313     }
2314     if (wasFlicking && (!d->hData.flicking || !d->vData.flicking)) {
2315         emit flickingChanged();
2316         emit flickEnded();
2317     }
2318
2319     // emit moving signals
2320     bool wasMoving = d->hData.moving || d->vData.moving;
2321     if (hMovementEnding && d->hData.moving
2322             && (!d->pressed && !d->stealMouse)) {
2323         d->hData.moving = false;
2324         d->hMoved = false;
2325         emit movingHorizontallyChanged();
2326     }
2327     if (vMovementEnding && d->vData.moving
2328             && (!d->pressed && !d->stealMouse)) {
2329         d->vData.moving = false;
2330         d->vMoved = false;
2331         emit movingVerticallyChanged();
2332     }
2333     if (wasMoving && (!d->hData.moving || !d->vData.moving)) {
2334         emit movingChanged();
2335         emit movementEnded();
2336     }
2337
2338     if (hMovementEnding) {
2339         d->hData.fixingUp = false;
2340         d->hData.smoothVelocity.setValue(0);
2341     }
2342     if (vMovementEnding) {
2343         d->vData.fixingUp = false;
2344         d->vData.smoothVelocity.setValue(0);
2345     }
2346 }
2347
2348 void QQuickFlickablePrivate::updateVelocity()
2349 {
2350     Q_Q(QQuickFlickable);
2351     emit q->horizontalVelocityChanged();
2352     emit q->verticalVelocityChanged();
2353 }
2354
2355 QT_END_NAMESPACE