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