1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
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"
48 #include <QtQuick/private/qquicktransition_p.h>
49 #include <private/qqmlglobal_p.h>
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"
60 // The maximum number of pixels a flick can overshoot
61 #ifndef QML_FLICK_OVERSHOOT
62 #define QML_FLICK_OVERSHOOT 150
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
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
76 // The default maximum velocity of a flick.
77 #ifndef QML_FLICK_DEFAULTMAXVELOCITY
78 #define QML_FLICK_DEFAULTMAXVELOCITY 2500
81 // The default deceleration of a flick.
82 #ifndef QML_FLICK_DEFAULTDECELERATION
83 #define QML_FLICK_DEFAULTDECELERATION 1500
86 // How much faster to decelerate when overshooting
87 #ifndef QML_FLICK_OVERSHOOTFRICTION
88 #define QML_FLICK_OVERSHOOTFRICTION 8
91 // Multiflick acceleration minimum flick velocity threshold
92 #ifndef QML_FLICK_MULTIFLICK_THRESHOLD
93 #define QML_FLICK_MULTIFLICK_THRESHOLD 1250
96 // Multiflick acceleration minimum contentSize/viewSize ratio
97 #ifndef QML_FLICK_MULTIFLICK_RATIO
98 #define QML_FLICK_MULTIFLICK_RATIO 10
101 // Multiflick acceleration maximum velocity multiplier
102 #ifndef QML_FLICK_MULTIFLICK_MAXBOOST
103 #define QML_FLICK_MULTIFLICK_MAXBOOST 3.0
106 // FlickThreshold determines how far the "mouse" must have moved
107 // before we perform a flick.
108 static const int FlickThreshold = 15;
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;
114 QQuickFlickableVisibleArea::QQuickFlickableVisibleArea(QQuickFlickable *parent)
115 : QObject(parent), flickable(parent), m_xPosition(0.), m_widthRatio(0.)
116 , m_yPosition(0.), m_heightRatio(0.)
120 qreal QQuickFlickableVisibleArea::widthRatio() const
125 qreal QQuickFlickableVisibleArea::xPosition() const
130 qreal QQuickFlickableVisibleArea::heightRatio() const
132 return m_heightRatio;
135 qreal QQuickFlickableVisibleArea::yPosition() const
140 void QQuickFlickableVisibleArea::updateVisible()
142 QQuickFlickablePrivate *p = QQuickFlickablePrivate::get(flickable);
144 bool changeX = false;
145 bool changeY = false;
146 bool changeWidth = false;
147 bool changeHeight = false;
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);
155 if (pageSize != m_heightRatio) {
156 m_heightRatio = pageSize;
159 if (pagePos != m_yPosition) {
160 m_yPosition = pagePos;
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);
170 if (pageSize != m_widthRatio) {
171 m_widthRatio = pageSize;
174 if (pagePos != m_xPosition) {
175 m_xPosition = pagePos;
180 emit xPositionChanged(m_xPosition);
182 emit yPositionChanged(m_yPosition);
184 emit widthRatioChanged(m_widthRatio);
186 emit heightRatioChanged(m_heightRatio);
190 class QQuickFlickableReboundTransition : public QQuickTransitionManager
193 QQuickFlickableReboundTransition(QQuickFlickable *f, const QString &name)
194 : flickable(f), axisData(0), propName(name), active(false)
198 ~QQuickFlickableReboundTransition()
203 bool startTransition(QQuickFlickablePrivate::AxisData *data, qreal toPos) {
204 QQuickFlickablePrivate *fp = QQuickFlickablePrivate::get(flickable);
205 if (!fp->rebound || !fp->rebound->enabled())
209 axisData->transitionTo = toPos;
210 axisData->transitionToSet = true;
213 actions << QQuickAction(fp->contentItem, propName, toPos);
214 QQuickTransitionManager::transition(actions, fp->rebound, fp->contentItem);
218 bool isActive() const {
222 void stopTransition() {
223 if (!flickable || !isRunning())
225 QQuickFlickablePrivate *fp = QQuickFlickablePrivate::get(flickable);
226 if (axisData == &fp->hData)
227 axisData->move.setValue(-flickable->contentX());
229 axisData->move.setValue(-flickable->contentY());
235 virtual void finished() {
238 axisData->move.setValue(axisData->transitionTo);
239 QQuickFlickablePrivate *fp = QQuickFlickablePrivate::get(flickable);
242 if (!fp->hData.transitionToBounds->isActive()
243 && !fp->vData.transitionToBounds->isActive()) {
244 flickable->movementEnding();
249 QQuickStateOperation::ActionList actions;
250 QQuickFlickable *flickable;
251 QQuickFlickablePrivate::AxisData *axisData;
256 QQuickFlickablePrivate::AxisData::~AxisData()
258 delete transitionToBounds;
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)
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)
281 void QQuickFlickablePrivate::init()
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);
295 Returns the amount to overshoot by given a velocity.
296 Will be roughly in range 0 - size/4
298 qreal QQuickFlickablePrivate::overShootDistance(qreal size)
300 if (maxVelocity <= 0)
303 return qMin(qreal(QML_FLICK_OVERSHOOT), size/3);
306 void QQuickFlickablePrivate::AxisData::addVelocitySample(qreal v, qreal maxVelocity)
310 else if (v < -maxVelocity)
312 velocityBuffer.append(v);
313 if (velocityBuffer.count() > QML_FLICK_SAMPLEBUFFER)
314 velocityBuffer.remove(0);
317 void QQuickFlickablePrivate::AxisData::updateVelocity()
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);
330 void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeom, const QRectF &oldGeom)
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;
340 q->viewportMoved(orient);
341 if (orient & Qt::Horizontal)
342 emit q->contentXChanged();
343 if (orient & Qt::Vertical)
344 emit q->contentYChanged();
348 bool QQuickFlickablePrivate::flickX(qreal velocity)
350 Q_Q(QQuickFlickable);
351 return flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, velocity);
354 bool QQuickFlickablePrivate::flickY(qreal velocity)
356 Q_Q(QQuickFlickable);
357 return flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, velocity);
360 bool QQuickFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal,
361 QQuickTimeLineCallback::Callback fixupCallback, qreal velocity)
363 Q_Q(QQuickFlickable);
364 qreal maxDistance = -1;
365 data.fixingUp = false;
366 // -ve velocity means list is moving up
368 maxDistance = qAbs(minExtent - data.move.value());
369 data.flickTarget = minExtent;
371 maxDistance = qAbs(maxExtent - data.move.value());
372 data.flickTarget = maxExtent;
374 if (maxDistance > 0) {
376 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
383 // adjust accel so that we hit a full pixel
384 qreal accel = deceleration;
386 qreal dist = v2 / (accel * 2.0);
389 qreal target = qRound(data.move.value() - dist);
390 dist = -target + data.move.value();
391 accel = v2 / (2.0f * qAbs(dist));
394 if (boundsBehavior == QQuickFlickable::DragAndOvershootBounds)
395 timeline.accel(data.move, v, accel);
397 timeline.accel(data.move, v, accel, maxDistance);
398 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
401 return !hData.flicking && q->xflick();
402 else if (&data == &vData)
403 return !vData.flicking && q->yflick();
407 fixup(data, minExtent, maxExtent);
412 void QQuickFlickablePrivate::fixupY_callback(void *data)
414 ((QQuickFlickablePrivate *)data)->fixupY();
417 void QQuickFlickablePrivate::fixupX_callback(void *data)
419 ((QQuickFlickablePrivate *)data)->fixupX();
422 void QQuickFlickablePrivate::fixupX()
424 Q_Q(QQuickFlickable);
425 fixup(hData, q->minXExtent(), q->maxXExtent());
428 void QQuickFlickablePrivate::fixupY()
430 Q_Q(QQuickFlickable);
431 fixup(vData, q->minYExtent(), q->maxYExtent());
434 void QQuickFlickablePrivate::adjustContentPos(AxisData &data, qreal toPos)
436 Q_Q(QQuickFlickable);
439 timeline.set(data.move, toPos);
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;
448 if (data.transitionToBounds && data.transitionToBounds->startTransition(&data, toPos)) {
449 q->movementStarting();
450 data.fixingUp = true;
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;
461 void QQuickFlickablePrivate::resetTimeline(AxisData &data)
463 timeline.reset(data.move);
464 if (data.transitionToBounds)
465 data.transitionToBounds->stopTransition();
468 void QQuickFlickablePrivate::clearTimeline()
471 if (hData.transitionToBounds)
472 hData.transitionToBounds->stopTransition();
473 if (vData.transitionToBounds)
474 vData.transitionToBounds->stopTransition();
477 void QQuickFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
479 if (data.move.value() > minExtent || maxExtent > minExtent) {
481 if (data.move.value() != minExtent) {
482 adjustContentPos(data, minExtent);
484 } else if (data.move.value() < maxExtent) {
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.
490 qreal val = data.move.value();
491 if (qAbs(qRound(val) - val) < 0.25) // round small differences
493 else if (data.smoothVelocity.value() > 0) // continue direction of motion for larger
495 else if (data.smoothVelocity.value() < 0)
497 else // otherwise round
499 timeline.set(data.move, val);
501 data.inOvershoot = false;
503 data.vTime = timeline.time();
506 void QQuickFlickablePrivate::updateBeginningEnd()
508 Q_Q(QQuickFlickable);
509 bool atBoundaryChange = false;
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);
517 if (atBeginning != vData.atBeginning) {
518 vData.atBeginning = atBeginning;
519 atBoundaryChange = true;
521 if (atEnd != vData.atEnd) {
523 atBoundaryChange = true;
527 const int maxxextent = int(-q->maxXExtent());
528 const qreal xpos = -hData.move.value();
529 atBeginning = (xpos <= -q->minXExtent());
530 atEnd = (maxxextent <= xpos);
532 if (atBeginning != hData.atBeginning) {
533 hData.atBeginning = atBeginning;
534 atBoundaryChange = true;
536 if (atEnd != hData.atEnd) {
538 atBoundaryChange = true;
541 if (vData.extentsChanged) {
542 vData.extentsChanged = false;
543 qreal originY = q->originY();
544 if (vData.origin != originY) {
545 vData.origin = originY;
546 emit q->originYChanged();
550 if (hData.extentsChanged) {
551 hData.extentsChanged = false;
552 qreal originX = q->originX();
553 if (hData.origin != originX) {
554 hData.origin = originX;
555 emit q->originXChanged();
559 if (atBoundaryChange)
560 emit q->isAtBoundaryChanged();
563 visibleArea->updateVisible();
567 XXXTODO add docs describing moving, dragging, flicking properties, e.g.
569 When the user starts dragging the Flickable, the dragging and moving properties
572 If the velocity is sufficient when the drag is ended, flicking may begin.
574 The moving properties will remain true until all dragging and flicking
579 \qmlsignal QtQuick2::Flickable::onDragStarted()
581 This handler is called when the view starts to be dragged due to user
586 \qmlsignal QtQuick2::Flickable::onDragEnded()
588 This handler is called when the user stops dragging the view.
590 If the velocity of the drag is sufficient at the time the
591 touch/mouse button is released then a flick will start.
596 \instantiates QQuickFlickable
597 \inqmlmodule QtQuick 2
598 \ingroup qtquick-input
599 \ingroup qtquick-containers
601 \brief Provides a surface that can be "flicked"
604 The Flickable item places its children on a surface that can be dragged
605 and flicked, causing the view onto the child items to scroll. This
606 behavior forms the basis of Items that are designed to show large numbers
607 of child items, such as \l ListView and \l GridView.
609 In traditional user interfaces, views can be scrolled using standard
610 controls, such as scroll bars and arrow buttons. In some situations, it
611 is also possible to drag the view directly by pressing and holding a
612 mouse button while moving the cursor. In touch-based user interfaces,
613 this dragging action is often complemented with a flicking action, where
614 scrolling continues after the user has stopped touching the view.
616 Flickable does not automatically clip its contents. If it is not used as
617 a full-screen item, you should consider setting the \l{Item::}{clip} property
620 \section1 Example Usage
622 \div {class="float-right"}
623 \inlineimage flickable.gif
626 The following example shows a small view onto a large image in which the
627 user can drag or flick the image in order to view different parts of it.
629 \snippet qml/flickable.qml document
633 Items declared as children of a Flickable are automatically parented to the
634 Flickable's \l contentItem. This should be taken into account when
635 operating on the children of the Flickable; it is usually the children of
636 \c contentItem that are relevant. For example, the bound of Items added
637 to the Flickable will be available by \c contentItem.childrenRect
639 \section1 Limitations
641 \note Due to an implementation detail, items placed inside a Flickable cannot anchor to it by
642 \c id. Use \c parent instead.
646 \qmlsignal QtQuick2::Flickable::onMovementStarted()
648 This handler is called when the view begins moving due to user
653 \qmlsignal QtQuick2::Flickable::onMovementEnded()
655 This handler is called when the view stops moving due to user
656 interaction. If a flick was generated, this handler will
657 be triggered once the flick stops. If a flick was not
658 generated, the handler will be triggered when the
659 user stops dragging - i.e. a mouse or touch release.
663 \qmlsignal QtQuick2::Flickable::onFlickStarted()
665 This handler is called when the view is flicked. A flick
666 starts from the point that the mouse or touch is released,
667 while still in motion.
671 \qmlsignal QtQuick2::Flickable::onFlickEnded()
673 This handler is called when the view stops moving due to a flick.
677 \qmlproperty real QtQuick2::Flickable::visibleArea.xPosition
678 \qmlproperty real QtQuick2::Flickable::visibleArea.widthRatio
679 \qmlproperty real QtQuick2::Flickable::visibleArea.yPosition
680 \qmlproperty real QtQuick2::Flickable::visibleArea.heightRatio
682 These properties describe the position and size of the currently viewed area.
683 The size is defined as the percentage of the full view currently visible,
684 scaled to 0.0 - 1.0. The page position is usually in the range 0.0 (beginning) to
685 1.0 minus size ratio (end), i.e. \c yPosition is in the range 0.0 to 1.0-\c heightRatio.
686 However, it is possible for the contents to be dragged outside of the normal
687 range, resulting in the page positions also being outside the normal range.
689 These properties are typically used to draw a scrollbar. For example:
691 \snippet qml/flickableScrollbar.qml 0
693 \snippet qml/flickableScrollbar.qml 1
695 \sa {declarative/ui-components/scrollbar}{scrollbar example}
697 QQuickFlickable::QQuickFlickable(QQuickItem *parent)
698 : QQuickItem(*(new QQuickFlickablePrivate), parent)
700 Q_D(QQuickFlickable);
704 QQuickFlickable::QQuickFlickable(QQuickFlickablePrivate &dd, QQuickItem *parent)
705 : QQuickItem(dd, parent)
707 Q_D(QQuickFlickable);
711 QQuickFlickable::~QQuickFlickable()
716 \qmlproperty real QtQuick2::Flickable::contentX
717 \qmlproperty real QtQuick2::Flickable::contentY
719 These properties hold the surface coordinate currently at the top-left
720 corner of the Flickable. For example, if you flick an image up 100 pixels,
721 \c contentY will be 100.
723 qreal QQuickFlickable::contentX() const
725 Q_D(const QQuickFlickable);
726 return -d->contentItem->x();
729 void QQuickFlickable::setContentX(qreal pos)
731 Q_D(QQuickFlickable);
732 d->hData.explicitValue = true;
733 d->resetTimeline(d->hData);
734 d->hData.vTime = d->timeline.time();
735 movementEnding(true, false);
736 if (-pos != d->hData.move.value())
737 d->hData.move.setValue(-pos);
740 qreal QQuickFlickable::contentY() const
742 Q_D(const QQuickFlickable);
743 return -d->contentItem->y();
746 void QQuickFlickable::setContentY(qreal pos)
748 Q_D(QQuickFlickable);
749 d->vData.explicitValue = true;
750 d->resetTimeline(d->vData);
751 d->vData.vTime = d->timeline.time();
752 movementEnding(false, true);
753 if (-pos != d->vData.move.value())
754 d->vData.move.setValue(-pos);
758 \qmlproperty bool QtQuick2::Flickable::interactive
760 This property describes whether the user can interact with the Flickable.
761 A user cannot drag or flick a Flickable that is not interactive.
763 By default, this property is true.
765 This property is useful for temporarily disabling flicking. This allows
766 special interaction with Flickable's children; for example, you might want
767 to freeze a flickable map while scrolling through a pop-up dialog that
768 is a child of the Flickable.
770 bool QQuickFlickable::isInteractive() const
772 Q_D(const QQuickFlickable);
773 return d->interactive;
776 void QQuickFlickable::setInteractive(bool interactive)
778 Q_D(QQuickFlickable);
779 if (interactive != d->interactive) {
780 d->interactive = interactive;
781 if (!interactive && (d->hData.flicking || d->vData.flicking)) {
783 d->hData.vTime = d->vData.vTime = d->timeline.time();
784 d->hData.flicking = false;
785 d->vData.flicking = false;
786 emit flickingChanged();
787 emit flickingHorizontallyChanged();
788 emit flickingVerticallyChanged();
791 emit interactiveChanged();
796 \qmlproperty real QtQuick2::Flickable::horizontalVelocity
797 \qmlproperty real QtQuick2::Flickable::verticalVelocity
799 The instantaneous velocity of movement along the x and y axes, in pixels/sec.
801 The reported velocity is smoothed to avoid erratic output.
803 Note that for views with a large content size (more than 10 times the view size),
804 the velocity of the flick may exceed the velocity of the touch in the case
805 of multiple quick consecutive flicks. This allows the user to flick faster
806 through large content.
808 qreal QQuickFlickable::horizontalVelocity() const
810 Q_D(const QQuickFlickable);
811 return d->hData.smoothVelocity.value();
814 qreal QQuickFlickable::verticalVelocity() const
816 Q_D(const QQuickFlickable);
817 return d->vData.smoothVelocity.value();
821 \qmlproperty bool QtQuick2::Flickable::atXBeginning
822 \qmlproperty bool QtQuick2::Flickable::atXEnd
823 \qmlproperty bool QtQuick2::Flickable::atYBeginning
824 \qmlproperty bool QtQuick2::Flickable::atYEnd
826 These properties are true if the flickable view is positioned at the beginning,
829 bool QQuickFlickable::isAtXEnd() const
831 Q_D(const QQuickFlickable);
832 return d->hData.atEnd;
835 bool QQuickFlickable::isAtXBeginning() const
837 Q_D(const QQuickFlickable);
838 return d->hData.atBeginning;
841 bool QQuickFlickable::isAtYEnd() const
843 Q_D(const QQuickFlickable);
844 return d->vData.atEnd;
847 bool QQuickFlickable::isAtYBeginning() const
849 Q_D(const QQuickFlickable);
850 return d->vData.atBeginning;
854 \qmlproperty Item QtQuick2::Flickable::contentItem
856 The internal item that contains the Items to be moved in the Flickable.
858 Items declared as children of a Flickable are automatically parented to the Flickable's contentItem.
860 Items created dynamically need to be explicitly parented to the \e contentItem:
864 function addItem(file) {
865 var component = Qt.createComponent(file)
866 component.createObject(myFlickable.contentItem);
871 QQuickItem *QQuickFlickable::contentItem()
873 Q_D(QQuickFlickable);
874 return d->contentItem;
877 QQuickFlickableVisibleArea *QQuickFlickable::visibleArea()
879 Q_D(QQuickFlickable);
881 d->visibleArea = new QQuickFlickableVisibleArea(this);
882 return d->visibleArea;
886 \qmlproperty enumeration QtQuick2::Flickable::flickableDirection
888 This property determines which directions the view can be flicked.
891 \li Flickable.AutoFlickDirection (default) - allows flicking vertically if the
892 \e contentHeight is not equal to the \e height of the Flickable.
893 Allows flicking horizontally if the \e contentWidth is not equal
894 to the \e width of the Flickable.
895 \li Flickable.HorizontalFlick - allows flicking horizontally.
896 \li Flickable.VerticalFlick - allows flicking vertically.
897 \li Flickable.HorizontalAndVerticalFlick - allows flicking in both directions.
900 QQuickFlickable::FlickableDirection QQuickFlickable::flickableDirection() const
902 Q_D(const QQuickFlickable);
903 return d->flickableDirection;
906 void QQuickFlickable::setFlickableDirection(FlickableDirection direction)
908 Q_D(QQuickFlickable);
909 if (direction != d->flickableDirection) {
910 d->flickableDirection = direction;
911 emit flickableDirectionChanged();
915 bool QQuickFlickable::pixelAligned() const
917 Q_D(const QQuickFlickable);
918 return d->pixelAligned;
921 void QQuickFlickable::setPixelAligned(bool align)
923 Q_D(QQuickFlickable);
924 if (align != d->pixelAligned) {
925 d->pixelAligned = align;
926 emit pixelAlignedChanged();
930 qint64 QQuickFlickablePrivate::computeCurrentTime(QInputEvent *event)
932 if (0 != event->timestamp() && QQuickItemPrivate::consistentTime == -1)
933 return event->timestamp();
935 return QQuickItemPrivate::elapsed(timer);
938 void QQuickFlickablePrivate::handleMousePressEvent(QMouseEvent *event)
940 Q_Q(QQuickFlickable);
941 QQuickItemPrivate::start(timer);
942 if (interactive && timeline.isActive()
943 && ((qAbs(hData.smoothVelocity.value()) > RetainGrabVelocity && !hData.fixingUp && !hData.inOvershoot)
944 || (qAbs(vData.smoothVelocity.value()) > RetainGrabVelocity && !vData.fixingUp && !vData.inOvershoot))) {
945 stealMouse = true; // If we've been flicked then steal the click.
946 int flickTime = timeline.time();
947 if (flickTime > 600) {
948 // too long between flicks - cancel boost
949 hData.continuousFlickVelocity = 0;
950 vData.continuousFlickVelocity = 0;
953 hData.continuousFlickVelocity = -hData.smoothVelocity.value();
954 vData.continuousFlickVelocity = -vData.smoothVelocity.value();
955 if (flickTime > 300) // slower flicking - reduce boost
956 flickBoost = qMax(1.0, flickBoost - 0.5);
960 hData.continuousFlickVelocity = 0;
961 vData.continuousFlickVelocity = 0;
964 q->setKeepMouseGrab(stealMouse);
966 if (hData.transitionToBounds)
967 hData.transitionToBounds->stopTransition();
968 if (vData.transitionToBounds)
969 vData.transitionToBounds->stopTransition();
971 resetTimeline(hData);
973 resetTimeline(vData);
977 hData.dragMinBound = q->minXExtent();
978 vData.dragMinBound = q->minYExtent();
979 hData.dragMaxBound = q->maxXExtent();
980 vData.dragMaxBound = q->maxYExtent();
983 pressPos = event->localPos();
984 hData.pressPos = hData.move.value();
985 vData.pressPos = vData.move.value();
986 bool wasFlicking = hData.flicking || vData.flicking;
987 if (hData.flicking) {
988 hData.flicking = false;
989 emit q->flickingHorizontallyChanged();
991 if (vData.flicking) {
992 vData.flicking = false;
993 emit q->flickingVerticallyChanged();
996 emit q->flickingChanged();
997 lastPosTime = lastPressTime = computeCurrentTime(event);
998 QQuickItemPrivate::start(vData.velocityTime);
999 QQuickItemPrivate::start(hData.velocityTime);
1002 void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event)
1004 Q_Q(QQuickFlickable);
1005 if (!interactive || lastPosTime == -1)
1007 bool rejectY = false;
1008 bool rejectX = false;
1010 bool stealY = stealMouse;
1011 bool stealX = stealMouse;
1013 bool prevHMoved = hMoved;
1014 bool prevVMoved = vMoved;
1016 qint64 elapsedSincePress = computeCurrentTime(event) - lastPressTime;
1018 qreal dy = event->localPos().y() - pressPos.y();
1019 bool overThreshold = QQuickWindowPrivate::dragOverThreshold(dy, Qt::YAxis, event);
1020 if (overThreshold || elapsedSincePress > 200) {
1022 vData.dragStartOffset = dy;
1023 qreal newY = dy + vData.pressPos - vData.dragStartOffset;
1024 const qreal minY = vData.dragMinBound;
1025 const qreal maxY = vData.dragMaxBound;
1027 newY = minY + (newY - minY) / 2;
1028 if (newY < maxY && maxY - minY <= 0)
1029 newY = maxY + (newY - maxY) / 2;
1030 if (boundsBehavior == QQuickFlickable::StopAtBounds && (newY > minY || newY < maxY)) {
1041 if (!rejectY && stealMouse && dy != 0.0) {
1043 vData.move.setValue(newY);
1052 qreal dx = event->localPos().x() - pressPos.x();
1053 bool overThreshold = QQuickWindowPrivate::dragOverThreshold(dx, Qt::XAxis, event);
1054 if (overThreshold || elapsedSincePress > 200) {
1056 hData.dragStartOffset = dx;
1057 qreal newX = dx + hData.pressPos - hData.dragStartOffset;
1058 const qreal minX = hData.dragMinBound;
1059 const qreal maxX = hData.dragMaxBound;
1061 newX = minX + (newX - minX) / 2;
1062 if (newX < maxX && maxX - minX <= 0)
1063 newX = maxX + (newX - maxX) / 2;
1064 if (boundsBehavior == QQuickFlickable::StopAtBounds && (newX > minX || newX < maxX)) {
1075 if (!rejectX && stealMouse && dx != 0.0) {
1077 hData.move.setValue(newX);
1086 stealMouse = stealX || stealY;
1088 q->setKeepMouseGrab(true);
1091 vData.velocityBuffer.clear();
1095 hData.velocityBuffer.clear();
1099 if ((hMoved && !prevHMoved) || (vMoved && !prevVMoved)) {
1101 q->movementStarting();
1104 qint64 currentTimestamp = computeCurrentTime(event);
1105 qreal elapsed = qreal(currentTimestamp - (lastPos.isNull() ? lastPressTime : lastPosTime)) / 1000.;
1108 lastPosTime = currentTimestamp;
1109 if (q->yflick() && !rejectY) {
1110 if (QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity) {
1111 vData.addVelocitySample(QGuiApplicationPrivate::mouseEventVelocity(event).y(), maxVelocity);
1113 qreal dy = event->localPos().y() - (lastPos.isNull() ? pressPos.y() : lastPos.y());
1114 vData.addVelocitySample(dy/elapsed, maxVelocity);
1117 if (q->xflick() && !rejectX) {
1118 if (QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity) {
1119 hData.addVelocitySample(QGuiApplicationPrivate::mouseEventVelocity(event).x(), maxVelocity);
1121 qreal dx = event->localPos().x() - (lastPos.isNull() ? pressPos.x() : lastPos.x());
1122 hData.addVelocitySample(dx/elapsed, maxVelocity);
1126 lastPos = event->localPos();
1129 void QQuickFlickablePrivate::handleMouseReleaseEvent(QMouseEvent *event)
1131 Q_Q(QQuickFlickable);
1133 q->setKeepMouseGrab(false);
1136 // if we drag then pause before release we should not cause a flick.
1137 qint64 elapsed = computeCurrentTime(event) - lastPosTime;
1139 vData.updateVelocity();
1140 hData.updateVelocity();
1144 if (lastPosTime == -1)
1147 hData.vTime = vData.vTime = timeline.time();
1149 bool canBoost = false;
1151 qreal vVelocity = 0;
1152 if (elapsed < 100 && vData.velocity != 0.) {
1153 vVelocity = (QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity)
1154 ? QGuiApplicationPrivate::mouseEventVelocity(event).y() : vData.velocity;
1156 if ((vData.atBeginning && vVelocity > 0.) || (vData.atEnd && vVelocity < 0.)) {
1158 } else if (vData.continuousFlickVelocity != 0.0
1159 && vData.viewSize/q->height() > QML_FLICK_MULTIFLICK_RATIO
1160 && ((vVelocity > 0) == (vData.continuousFlickVelocity > 0))
1161 && qAbs(vVelocity) > QML_FLICK_MULTIFLICK_THRESHOLD) {
1162 // accelerate flick for large view flicked quickly
1166 qreal hVelocity = 0;
1167 if (elapsed < 100 && hData.velocity != 0.) {
1168 hVelocity = (QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity)
1169 ? QGuiApplicationPrivate::mouseEventVelocity(event).x() : hData.velocity;
1171 if ((hData.atBeginning && hVelocity > 0.) || (hData.atEnd && hVelocity < 0.)) {
1173 } else if (hData.continuousFlickVelocity != 0.0
1174 && hData.viewSize/q->width() > QML_FLICK_MULTIFLICK_RATIO
1175 && ((hVelocity > 0) == (hData.continuousFlickVelocity > 0))
1176 && qAbs(hVelocity) > QML_FLICK_MULTIFLICK_THRESHOLD) {
1177 // accelerate flick for large view flicked quickly
1181 flickBoost = canBoost ? qBound(1.0, flickBoost+0.25, QML_FLICK_MULTIFLICK_MAXBOOST) : 1.0;
1183 bool flickedVertically = false;
1184 vVelocity *= flickBoost;
1185 bool isVerticalFlickAllowed = q->yflick() && qAbs(vVelocity) > MinimumFlickVelocity && qAbs(event->localPos().y() - pressPos.y()) > FlickThreshold;
1186 if (isVerticalFlickAllowed) {
1187 velocityTimeline.reset(vData.smoothVelocity);
1188 vData.smoothVelocity.setValue(-vVelocity);
1189 flickedVertically = flickY(vVelocity);
1192 bool flickedHorizontally = false;
1193 hVelocity *= flickBoost;
1194 bool isHorizontalFlickAllowed = q->xflick() && qAbs(hVelocity) > MinimumFlickVelocity && qAbs(event->localPos().x() - pressPos.x()) > FlickThreshold;
1195 if (isHorizontalFlickAllowed) {
1196 velocityTimeline.reset(hData.smoothVelocity);
1197 hData.smoothVelocity.setValue(-hVelocity);
1198 flickedHorizontally = flickX(hVelocity);
1201 if (!isVerticalFlickAllowed)
1204 if (!isHorizontalFlickAllowed)
1207 flickingStarted(flickedHorizontally, flickedVertically);
1208 if (!isViewMoving())
1209 q->movementEnding();
1212 void QQuickFlickable::mousePressEvent(QMouseEvent *event)
1214 Q_D(QQuickFlickable);
1215 if (d->interactive) {
1217 d->handleMousePressEvent(event);
1220 QQuickItem::mousePressEvent(event);
1224 void QQuickFlickable::mouseMoveEvent(QMouseEvent *event)
1226 Q_D(QQuickFlickable);
1227 if (d->interactive) {
1228 d->handleMouseMoveEvent(event);
1231 QQuickItem::mouseMoveEvent(event);
1235 void QQuickFlickable::mouseReleaseEvent(QMouseEvent *event)
1237 Q_D(QQuickFlickable);
1238 if (d->interactive) {
1239 d->clearDelayedPress();
1240 d->handleMouseReleaseEvent(event);
1242 if (window() && window()->mouseGrabberItem() == this)
1245 QQuickItem::mouseReleaseEvent(event);
1249 void QQuickFlickable::wheelEvent(QWheelEvent *event)
1251 Q_D(QQuickFlickable);
1252 if (!d->interactive) {
1253 QQuickItem::wheelEvent(event);
1257 int yDelta = event->angleDelta().y();
1258 int xDelta = event->angleDelta().x();
1259 if (yflick() && yDelta != 0) {
1261 if (yDelta > 0 && contentY() > -minYExtent()) {
1262 d->vData.velocity = qMax(yDelta*2 - d->vData.smoothVelocity.value(), qreal(d->maxVelocity/4));
1264 } else if (yDelta < 0 && contentY() < -maxYExtent()) {
1265 d->vData.velocity = qMin(yDelta*2 - d->vData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
1269 d->vData.flicking = false;
1270 d->flickY(d->vData.velocity);
1271 d->flickingStarted(false, true);
1272 if (d->vData.flicking) {
1279 if (xflick() && xDelta != 0) {
1281 if (xDelta > 0 && contentX() > -minXExtent()) {
1282 d->hData.velocity = qMax(xDelta*2 - d->hData.smoothVelocity.value(), qreal(d->maxVelocity/4));
1284 } else if (xDelta < 0 && contentX() < -maxXExtent()) {
1285 d->hData.velocity = qMin(xDelta*2 - d->hData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
1289 d->hData.flicking = false;
1290 d->flickX(d->hData.velocity);
1291 d->flickingStarted(true, false);
1292 if (d->hData.flicking) {
1300 if (!event->isAccepted())
1301 QQuickItem::wheelEvent(event);
1304 bool QQuickFlickablePrivate::isOutermostPressDelay() const
1306 Q_Q(const QQuickFlickable);
1307 QQuickItem *item = q->parentItem();
1309 QQuickFlickable *flick = qobject_cast<QQuickFlickable*>(item);
1310 if (flick && flick->pressDelay() > 0 && flick->isInteractive())
1312 item = item->parentItem();
1318 void QQuickFlickablePrivate::captureDelayedPress(QMouseEvent *event)
1320 Q_Q(QQuickFlickable);
1321 if (!q->window() || pressDelay <= 0)
1323 if (!isOutermostPressDelay())
1325 delayedPressTarget = q->window()->mouseGrabberItem();
1326 delayedPressEvent = QQuickWindowPrivate::cloneMouseEvent(event);
1327 delayedPressEvent->setAccepted(false);
1328 delayedPressTimer.start(pressDelay, q);
1331 void QQuickFlickablePrivate::clearDelayedPress()
1333 if (delayedPressEvent) {
1334 delayedPressTimer.stop();
1335 delete delayedPressEvent;
1336 delayedPressEvent = 0;
1340 //XXX pixelAligned ignores the global position of the Flickable, i.e. assumes Flickable itself is pixel aligned.
1341 void QQuickFlickablePrivate::setViewportX(qreal x)
1343 contentItem->setX(pixelAligned ? qRound(x) : x);
1346 void QQuickFlickablePrivate::setViewportY(qreal y)
1348 contentItem->setY(pixelAligned ? qRound(y) : y);
1351 void QQuickFlickable::timerEvent(QTimerEvent *event)
1353 Q_D(QQuickFlickable);
1354 if (event->timerId() == d->delayedPressTimer.timerId()) {
1355 d->delayedPressTimer.stop();
1356 if (d->delayedPressEvent) {
1357 QQuickItem *grabber = window() ? window()->mouseGrabberItem() : 0;
1358 if (!grabber || grabber != this) {
1359 // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
1360 // so we reset the grabber
1361 if (window()->mouseGrabberItem() == d->delayedPressTarget)
1362 d->delayedPressTarget->ungrabMouse();
1363 // Use the event handler that will take care of finding the proper item to propagate the event
1364 QQuickWindowPrivate::get(window())->deliverMouseEvent(d->delayedPressEvent);
1366 delete d->delayedPressEvent;
1367 d->delayedPressEvent = 0;
1372 qreal QQuickFlickable::minYExtent() const
1374 Q_D(const QQuickFlickable);
1375 return d->vData.startMargin;
1378 qreal QQuickFlickable::minXExtent() const
1380 Q_D(const QQuickFlickable);
1381 return d->hData.startMargin;
1385 qreal QQuickFlickable::maxXExtent() const
1387 Q_D(const QQuickFlickable);
1388 return width() - vWidth() - d->hData.endMargin;
1391 qreal QQuickFlickable::maxYExtent() const
1393 Q_D(const QQuickFlickable);
1394 return height() - vHeight() - d->vData.endMargin;
1397 void QQuickFlickable::componentComplete()
1399 Q_D(QQuickFlickable);
1400 QQuickItem::componentComplete();
1401 if (!d->hData.explicitValue && d->hData.startMargin != 0.)
1402 setContentX(-minXExtent());
1403 if (!d->vData.explicitValue && d->vData.startMargin != 0.)
1404 setContentY(-minYExtent());
1407 void QQuickFlickable::viewportMoved(Qt::Orientations orient)
1409 Q_D(QQuickFlickable);
1410 if (orient & Qt::Vertical)
1411 d->viewportAxisMoved(d->vData, minYExtent(), maxYExtent(), height(), d->fixupY_callback);
1412 if (orient & Qt::Horizontal)
1413 d->viewportAxisMoved(d->hData, minXExtent(), maxXExtent(), width(), d->fixupX_callback);
1414 d->updateBeginningEnd();
1417 void QQuickFlickablePrivate::viewportAxisMoved(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1418 QQuickTimeLineCallback::Callback fixupCallback)
1420 if (pressed || calcVelocity) {
1421 int elapsed = QQuickItemPrivate::restart(data.velocityTime);
1423 qreal velocity = (data.lastPos - data.move.value()) * 1000 / elapsed;
1424 if (qAbs(velocity) > 0) {
1425 velocityTimeline.reset(data.smoothVelocity);
1427 velocityTimeline.set(data.smoothVelocity, velocity);
1429 velocityTimeline.move(data.smoothVelocity, velocity, reportedVelocitySmoothing);
1430 velocityTimeline.move(data.smoothVelocity, 0, reportedVelocitySmoothing);
1434 if (timeline.time() > data.vTime) {
1435 velocityTimeline.reset(data.smoothVelocity);
1436 qreal velocity = (data.lastPos - data.move.value()) * 1000 / (timeline.time() - data.vTime);
1437 data.smoothVelocity.setValue(velocity);
1441 if (!data.inOvershoot && !data.fixingUp && data.flicking
1442 && (data.move.value() > minExtent || data.move.value() < maxExtent)
1443 && qAbs(data.smoothVelocity.value()) > 10) {
1444 // Increase deceleration if we've passed a bound
1445 qreal overBound = data.move.value() > minExtent
1446 ? data.move.value() - minExtent
1447 : maxExtent - data.move.value();
1448 data.inOvershoot = true;
1449 qreal maxDistance = overShootDistance(vSize) - overBound;
1450 resetTimeline(data);
1451 if (maxDistance > 0)
1452 timeline.accel(data.move, -data.smoothVelocity.value(), deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance);
1453 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
1456 data.lastPos = data.move.value();
1457 data.vTime = timeline.time();
1460 void QQuickFlickable::geometryChanged(const QRectF &newGeometry,
1461 const QRectF &oldGeometry)
1463 Q_D(QQuickFlickable);
1464 QQuickItem::geometryChanged(newGeometry, oldGeometry);
1466 bool changed = false;
1467 if (newGeometry.width() != oldGeometry.width()) {
1470 if (d->hData.viewSize < 0) {
1471 d->contentItem->setWidth(width());
1472 emit contentWidthChanged();
1474 // Make sure that we're entirely in view.
1475 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1476 d->fixupMode = QQuickFlickablePrivate::Immediate;
1480 if (newGeometry.height() != oldGeometry.height()) {
1483 if (d->vData.viewSize < 0) {
1484 d->contentItem->setHeight(height());
1485 emit contentHeightChanged();
1487 // Make sure that we're entirely in view.
1488 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1489 d->fixupMode = QQuickFlickablePrivate::Immediate;
1495 d->updateBeginningEnd();
1499 \qmlmethod QtQuick2::Flickable::flick(qreal xVelocity, qreal yVelocity)
1501 Flicks the content with \a xVelocity horizontally and \a yVelocity vertically in pixels/sec.
1504 void QQuickFlickable::flick(qreal xVelocity, qreal yVelocity)
1506 Q_D(QQuickFlickable);
1507 bool flickedX = d->flickX(xVelocity);
1508 bool flickedY = d->flickY(yVelocity);
1509 d->flickingStarted(flickedX, flickedY);
1512 void QQuickFlickablePrivate::flickingStarted(bool flickingH, bool flickingV)
1514 Q_Q(QQuickFlickable);
1515 if (!flickingH && !flickingV)
1518 bool wasFlicking = hData.flicking || vData.flicking;
1519 if (flickingH && !hData.flicking) {
1520 hData.flicking = true;
1521 emit q->flickingHorizontallyChanged();
1523 if (flickingV && !vData.flicking) {
1524 vData.flicking = true;
1525 emit q->flickingVerticallyChanged();
1527 if (!wasFlicking && (hData.flicking || vData.flicking)) {
1528 emit q->flickingChanged();
1529 emit q->flickStarted();
1534 \qmlmethod QtQuick2::Flickable::cancelFlick()
1536 Cancels the current flick animation.
1539 void QQuickFlickable::cancelFlick()
1541 Q_D(QQuickFlickable);
1542 d->resetTimeline(d->hData);
1543 d->resetTimeline(d->vData);
1547 void QQuickFlickablePrivate::data_append(QQmlListProperty<QObject> *prop, QObject *o)
1549 if (QQuickItem *i = qmlobject_cast<QQuickItem *>(o)) {
1550 i->setParentItem(static_cast<QQuickFlickablePrivate*>(prop->data)->contentItem);
1552 o->setParent(prop->object); // XXX todo - do we want this?
1556 int QQuickFlickablePrivate::data_count(QQmlListProperty<QObject> *)
1562 QObject *QQuickFlickablePrivate::data_at(QQmlListProperty<QObject> *, int)
1568 void QQuickFlickablePrivate::data_clear(QQmlListProperty<QObject> *)
1573 QQmlListProperty<QObject> QQuickFlickable::flickableData()
1575 Q_D(QQuickFlickable);
1576 return QQmlListProperty<QObject>(this, (void *)d, QQuickFlickablePrivate::data_append,
1577 QQuickFlickablePrivate::data_count,
1578 QQuickFlickablePrivate::data_at,
1579 QQuickFlickablePrivate::data_clear);
1582 QQmlListProperty<QQuickItem> QQuickFlickable::flickableChildren()
1584 Q_D(QQuickFlickable);
1585 return QQuickItemPrivate::get(d->contentItem)->children();
1589 \qmlproperty enumeration QtQuick2::Flickable::boundsBehavior
1590 This property holds whether the surface may be dragged
1591 beyond the Flickable's boundaries, or overshoot the
1592 Flickable's boundaries when flicked.
1594 This enables the feeling that the edges of the view are soft,
1595 rather than a hard physical boundary.
1597 The \c boundsBehavior can be one of:
1600 \li Flickable.StopAtBounds - the contents can not be dragged beyond the boundary
1601 of the flickable, and flicks will not overshoot.
1602 \li Flickable.DragOverBounds - the contents can be dragged beyond the boundary
1603 of the Flickable, but flicks will not overshoot.
1604 \li Flickable.DragAndOvershootBounds (default) - the contents can be dragged
1605 beyond the boundary of the Flickable, and can overshoot the
1606 boundary when flicked.
1609 QQuickFlickable::BoundsBehavior QQuickFlickable::boundsBehavior() const
1611 Q_D(const QQuickFlickable);
1612 return d->boundsBehavior;
1615 void QQuickFlickable::setBoundsBehavior(BoundsBehavior b)
1617 Q_D(QQuickFlickable);
1618 if (b == d->boundsBehavior)
1620 d->boundsBehavior = b;
1621 emit boundsBehaviorChanged();
1625 \qmlproperty Transition QtQuick2::Flickable::rebound
1627 This holds the transition to be applied to the content view when
1628 it snaps back to the bounds of the flickable. The transition is
1629 triggered when the view is flicked or dragged past the edge of the
1630 content area, or when returnToBounds() is called.
1636 width: 150; height: 150
1637 contentWidth: 300; contentHeight: 300
1639 rebound: Transition {
1643 easing.type: Easing.OutBounce
1648 width: 300; height: 300
1649 gradient: Gradient {
1650 GradientStop { position: 0.0; color: "lightsteelblue" }
1651 GradientStop { position: 1.0; color: "blue" }
1657 When the above view is flicked beyond its bounds, it will return to its
1658 bounds using the transition specified:
1660 \image flickable-rebound.gif
1662 If this property is not set, a default animation is applied.
1664 QQuickTransition *QQuickFlickable::rebound() const
1666 Q_D(const QQuickFlickable);
1670 void QQuickFlickable::setRebound(QQuickTransition *transition)
1672 Q_D(QQuickFlickable);
1674 if (!d->hData.transitionToBounds)
1675 d->hData.transitionToBounds = new QQuickFlickableReboundTransition(this, QLatin1String("x"));
1676 if (!d->vData.transitionToBounds)
1677 d->vData.transitionToBounds = new QQuickFlickableReboundTransition(this, QLatin1String("y"));
1679 if (d->rebound != transition) {
1680 d->rebound = transition;
1681 emit reboundChanged();
1686 \qmlproperty real QtQuick2::Flickable::contentWidth
1687 \qmlproperty real QtQuick2::Flickable::contentHeight
1689 The dimensions of the content (the surface controlled by Flickable).
1690 This should typically be set to the combined size of the items placed in the
1693 The following snippet shows how these properties are used to display
1694 an image that is larger than the Flickable item itself:
1696 \snippet qml/flickable.qml document
1698 In some cases, the content dimensions can be automatically set
1699 based on the \l {Item::childrenRect.width}{childrenRect.width}
1700 and \l {Item::childrenRect.height}{childrenRect.height} properties
1701 of the \l contentItem. For example, the previous snippet could be rewritten with:
1704 contentWidth: contentItem.childrenRect.width; contentHeight: contentItem.childrenRect.height
1707 Though this assumes that the origin of the childrenRect is 0,0.
1709 qreal QQuickFlickable::contentWidth() const
1711 Q_D(const QQuickFlickable);
1712 return d->hData.viewSize;
1715 void QQuickFlickable::setContentWidth(qreal w)
1717 Q_D(QQuickFlickable);
1718 if (d->hData.viewSize == w)
1720 d->hData.viewSize = w;
1722 d->contentItem->setWidth(width());
1724 d->contentItem->setWidth(w);
1725 d->hData.markExtentsDirty();
1726 // Make sure that we're entirely in view.
1727 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1728 d->fixupMode = QQuickFlickablePrivate::Immediate;
1730 } else if (!d->pressed && d->hData.fixingUp) {
1731 d->fixupMode = QQuickFlickablePrivate::ExtentChanged;
1734 emit contentWidthChanged();
1735 d->updateBeginningEnd();
1738 qreal QQuickFlickable::contentHeight() const
1740 Q_D(const QQuickFlickable);
1741 return d->vData.viewSize;
1744 void QQuickFlickable::setContentHeight(qreal h)
1746 Q_D(QQuickFlickable);
1747 if (d->vData.viewSize == h)
1749 d->vData.viewSize = h;
1751 d->contentItem->setHeight(height());
1753 d->contentItem->setHeight(h);
1754 d->vData.markExtentsDirty();
1755 // Make sure that we're entirely in view.
1756 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1757 d->fixupMode = QQuickFlickablePrivate::Immediate;
1759 } else if (!d->pressed && d->vData.fixingUp) {
1760 d->fixupMode = QQuickFlickablePrivate::ExtentChanged;
1763 emit contentHeightChanged();
1764 d->updateBeginningEnd();
1768 \qmlproperty real QtQuick2::Flickable::topMargin
1769 \qmlproperty real QtQuick2::Flickable::leftMargin
1770 \qmlproperty real QtQuick2::Flickable::bottomMargin
1771 \qmlproperty real QtQuick2::Flickable::rightMargin
1773 These properties hold the margins around the content. This space is reserved
1774 in addition to the contentWidth and contentHeight.
1778 qreal QQuickFlickable::topMargin() const
1780 Q_D(const QQuickFlickable);
1781 return d->vData.startMargin;
1784 void QQuickFlickable::setTopMargin(qreal m)
1786 Q_D(QQuickFlickable);
1787 if (d->vData.startMargin == m)
1789 d->vData.startMargin = m;
1790 d->vData.markExtentsDirty();
1791 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1792 d->fixupMode = QQuickFlickablePrivate::Immediate;
1795 emit topMarginChanged();
1796 d->updateBeginningEnd();
1799 qreal QQuickFlickable::bottomMargin() const
1801 Q_D(const QQuickFlickable);
1802 return d->vData.endMargin;
1805 void QQuickFlickable::setBottomMargin(qreal m)
1807 Q_D(QQuickFlickable);
1808 if (d->vData.endMargin == m)
1810 d->vData.endMargin = m;
1811 d->vData.markExtentsDirty();
1812 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1813 d->fixupMode = QQuickFlickablePrivate::Immediate;
1816 emit bottomMarginChanged();
1817 d->updateBeginningEnd();
1820 qreal QQuickFlickable::leftMargin() const
1822 Q_D(const QQuickFlickable);
1823 return d->hData.startMargin;
1826 void QQuickFlickable::setLeftMargin(qreal m)
1828 Q_D(QQuickFlickable);
1829 if (d->hData.startMargin == m)
1831 d->hData.startMargin = m;
1832 d->hData.markExtentsDirty();
1833 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1834 d->fixupMode = QQuickFlickablePrivate::Immediate;
1837 emit leftMarginChanged();
1838 d->updateBeginningEnd();
1841 qreal QQuickFlickable::rightMargin() const
1843 Q_D(const QQuickFlickable);
1844 return d->hData.endMargin;
1847 void QQuickFlickable::setRightMargin(qreal m)
1849 Q_D(QQuickFlickable);
1850 if (d->hData.endMargin == m)
1852 d->hData.endMargin = m;
1853 d->hData.markExtentsDirty();
1854 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1855 d->fixupMode = QQuickFlickablePrivate::Immediate;
1858 emit rightMarginChanged();
1859 d->updateBeginningEnd();
1863 \qmlproperty real QtQuick2::Flickable::originX
1864 \qmlproperty real QtQuick2::Flickable::originY
1866 These properties hold the origin of the content. This value always refers
1867 to the top-left position of the content regardless of layout direction.
1869 This is usually (0,0), however ListView and GridView may have an arbitrary
1870 origin due to delegate size variation, or item insertion/removal outside
1874 qreal QQuickFlickable::originY() const
1876 Q_D(const QQuickFlickable);
1877 return -minYExtent() + d->vData.startMargin;
1880 qreal QQuickFlickable::originX() const
1882 Q_D(const QQuickFlickable);
1883 return -minXExtent() + d->hData.startMargin;
1888 \qmlmethod QtQuick2::Flickable::resizeContent(real width, real height, QPointF center)
1890 Resizes the content to \a width x \a height about \a center.
1892 This does not scale the contents of the Flickable - it only resizes the \l contentWidth
1893 and \l contentHeight.
1895 Resizing the content may result in the content being positioned outside
1896 the bounds of the Flickable. Calling \l returnToBounds() will
1897 move the content back within legal bounds.
1899 void QQuickFlickable::resizeContent(qreal w, qreal h, QPointF center)
1901 Q_D(QQuickFlickable);
1902 if (w != d->hData.viewSize) {
1903 qreal oldSize = d->hData.viewSize;
1904 d->hData.viewSize = w;
1905 d->contentItem->setWidth(w);
1906 emit contentWidthChanged();
1907 if (center.x() != 0) {
1908 qreal pos = center.x() * w / oldSize;
1909 setContentX(contentX() + pos - center.x());
1912 if (h != d->vData.viewSize) {
1913 qreal oldSize = d->vData.viewSize;
1914 d->vData.viewSize = h;
1915 d->contentItem->setHeight(h);
1916 emit contentHeightChanged();
1917 if (center.y() != 0) {
1918 qreal pos = center.y() * h / oldSize;
1919 setContentY(contentY() + pos - center.y());
1922 d->updateBeginningEnd();
1926 \qmlmethod QtQuick2::Flickable::returnToBounds()
1928 Ensures the content is within legal bounds.
1930 This may be called to ensure that the content is within legal bounds
1931 after manually positioning the content.
1933 void QQuickFlickable::returnToBounds()
1935 Q_D(QQuickFlickable);
1940 qreal QQuickFlickable::vWidth() const
1942 Q_D(const QQuickFlickable);
1943 if (d->hData.viewSize < 0)
1946 return d->hData.viewSize;
1949 qreal QQuickFlickable::vHeight() const
1951 Q_D(const QQuickFlickable);
1952 if (d->vData.viewSize < 0)
1955 return d->vData.viewSize;
1958 bool QQuickFlickable::xflick() const
1960 Q_D(const QQuickFlickable);
1961 if (d->flickableDirection == QQuickFlickable::AutoFlickDirection)
1962 return floor(qAbs(vWidth() - width()));
1963 return d->flickableDirection & QQuickFlickable::HorizontalFlick;
1966 bool QQuickFlickable::yflick() const
1968 Q_D(const QQuickFlickable);
1969 if (d->flickableDirection == QQuickFlickable::AutoFlickDirection)
1970 return floor(qAbs(vHeight() - height()));
1971 return d->flickableDirection & QQuickFlickable::VerticalFlick;
1974 void QQuickFlickable::mouseUngrabEvent()
1976 Q_D(QQuickFlickable);
1978 // if our mouse grab has been removed (probably by another Flickable),
1980 d->clearDelayedPress();
1982 d->draggingEnding();
1983 d->stealMouse = false;
1984 setKeepMouseGrab(false);
1987 if (!d->isViewMoving())
1992 bool QQuickFlickable::sendMouseEvent(QMouseEvent *event)
1994 Q_D(QQuickFlickable);
1995 QPointF localPos = mapFromScene(event->windowPos());
1997 QQuickWindow *c = window();
1998 QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
1999 bool grabberDisabled = grabber && !grabber->isEnabled();
2000 bool stealThisEvent = d->stealMouse;
2001 if ((stealThisEvent || contains(localPos)) && (!grabber || !grabber->keepMouseGrab() || grabberDisabled)) {
2002 QScopedPointer<QMouseEvent> mouseEvent(QQuickWindowPrivate::cloneMouseEvent(event, &localPos));
2003 mouseEvent->setAccepted(false);
2005 switch (mouseEvent->type()) {
2006 case QEvent::MouseMove:
2007 d->handleMouseMoveEvent(mouseEvent.data());
2009 case QEvent::MouseButtonPress:
2010 if (d->pressed) // we are already pressed - this is a delayed replay
2013 d->handleMousePressEvent(mouseEvent.data());
2014 d->captureDelayedPress(event);
2015 stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
2017 case QEvent::MouseButtonRelease:
2018 if (d->delayedPressEvent) {
2019 // We replay the mouse press but the grabber we had might not be interested in the event (e.g. overlay)
2020 // so we reset the grabber
2021 if (c->mouseGrabberItem() == d->delayedPressTarget)
2022 d->delayedPressTarget->ungrabMouse();
2023 // Use the event handler that will take care of finding the proper item to propagate the event
2024 QQuickWindowPrivate::get(window())->deliverMouseEvent(d->delayedPressEvent);
2025 d->clearDelayedPress();
2026 // We send the release
2027 window()->sendEvent(c->mouseGrabberItem(), event);
2028 // And the event has been consumed
2029 d->stealMouse = false;
2033 d->handleMouseReleaseEvent(mouseEvent.data());
2038 grabber = qobject_cast<QQuickItem*>(c->mouseGrabberItem());
2039 if ((grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) || grabberDisabled) {
2040 d->clearDelayedPress();
2044 return stealThisEvent || d->delayedPressEvent || grabberDisabled;
2045 } else if (d->lastPosTime != -1) {
2046 d->lastPosTime = -1;
2049 if (event->type() == QEvent::MouseButtonRelease) {
2050 d->lastPosTime = -1;
2051 d->clearDelayedPress();
2052 d->stealMouse = false;
2059 bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e)
2061 Q_D(QQuickFlickable);
2062 if (!isVisible() || !isEnabled())
2063 return QQuickItem::childMouseEventFilter(i, e);
2064 switch (e->type()) {
2065 case QEvent::MouseButtonPress:
2066 case QEvent::MouseMove:
2067 case QEvent::MouseButtonRelease:
2068 return sendMouseEvent(static_cast<QMouseEvent *>(e));
2069 case QEvent::UngrabMouse:
2070 if (d->window && d->window->mouseGrabberItem() && d->window->mouseGrabberItem() != this) {
2071 // The grab has been taken away from a child and given to some other item.
2079 return QQuickItem::childMouseEventFilter(i, e);
2083 \qmlproperty real QtQuick2::Flickable::maximumFlickVelocity
2084 This property holds the maximum velocity that the user can flick the view in pixels/second.
2086 The default value is platform dependent.
2088 qreal QQuickFlickable::maximumFlickVelocity() const
2090 Q_D(const QQuickFlickable);
2091 return d->maxVelocity;
2094 void QQuickFlickable::setMaximumFlickVelocity(qreal v)
2096 Q_D(QQuickFlickable);
2097 if (v == d->maxVelocity)
2100 emit maximumFlickVelocityChanged();
2104 \qmlproperty real QtQuick2::Flickable::flickDeceleration
2105 This property holds the rate at which a flick will decelerate.
2107 The default value is platform dependent.
2109 qreal QQuickFlickable::flickDeceleration() const
2111 Q_D(const QQuickFlickable);
2112 return d->deceleration;
2115 void QQuickFlickable::setFlickDeceleration(qreal deceleration)
2117 Q_D(QQuickFlickable);
2118 if (deceleration == d->deceleration)
2120 d->deceleration = deceleration;
2121 emit flickDecelerationChanged();
2124 bool QQuickFlickable::isFlicking() const
2126 Q_D(const QQuickFlickable);
2127 return d->hData.flicking || d->vData.flicking;
2131 \qmlproperty bool QtQuick2::Flickable::flicking
2132 \qmlproperty bool QtQuick2::Flickable::flickingHorizontally
2133 \qmlproperty bool QtQuick2::Flickable::flickingVertically
2135 These properties describe whether the view is currently moving horizontally,
2136 vertically or in either direction, due to the user flicking the view.
2138 bool QQuickFlickable::isFlickingHorizontally() const
2140 Q_D(const QQuickFlickable);
2141 return d->hData.flicking;
2144 bool QQuickFlickable::isFlickingVertically() const
2146 Q_D(const QQuickFlickable);
2147 return d->vData.flicking;
2151 \qmlproperty bool QtQuick2::Flickable::dragging
2152 \qmlproperty bool QtQuick2::Flickable::draggingHorizontally
2153 \qmlproperty bool QtQuick2::Flickable::draggingVertically
2155 These properties describe whether the view is currently moving horizontally,
2156 vertically or in either direction, due to the user dragging the view.
2158 bool QQuickFlickable::isDragging() const
2160 Q_D(const QQuickFlickable);
2161 return d->hData.dragging || d->vData.dragging;
2164 bool QQuickFlickable::isDraggingHorizontally() const
2166 Q_D(const QQuickFlickable);
2167 return d->hData.dragging;
2170 bool QQuickFlickable::isDraggingVertically() const
2172 Q_D(const QQuickFlickable);
2173 return d->vData.dragging;
2176 void QQuickFlickablePrivate::draggingStarting()
2178 Q_Q(QQuickFlickable);
2179 bool wasDragging = hData.dragging || vData.dragging;
2180 if (hMoved && !hData.dragging) {
2181 hData.dragging = true;
2182 emit q->draggingHorizontallyChanged();
2184 if (vMoved && !vData.dragging) {
2185 vData.dragging = true;
2186 emit q->draggingVerticallyChanged();
2188 if (!wasDragging && (hData.dragging || vData.dragging)) {
2189 emit q->draggingChanged();
2190 emit q->dragStarted();
2194 void QQuickFlickablePrivate::draggingEnding()
2196 Q_Q(QQuickFlickable);
2197 bool wasDragging = hData.dragging || vData.dragging;
2198 if (hData.dragging) {
2199 hData.dragging = false;
2200 emit q->draggingHorizontallyChanged();
2202 if (vData.dragging) {
2203 vData.dragging = false;
2204 emit q->draggingVerticallyChanged();
2206 if (wasDragging && !hData.dragging && !vData.dragging) {
2207 emit q->draggingChanged();
2208 emit q->dragEnded();
2212 bool QQuickFlickablePrivate::isViewMoving() const
2214 if (timeline.isActive()
2215 || (hData.transitionToBounds && hData.transitionToBounds->isActive())
2216 || (vData.transitionToBounds && vData.transitionToBounds->isActive()) ) {
2223 \qmlproperty int QtQuick2::Flickable::pressDelay
2225 This property holds the time to delay (ms) delivering a press to
2226 children of the Flickable. This can be useful where reacting
2227 to a press before a flicking action has undesirable effects.
2229 If the flickable is dragged/flicked before the delay times out
2230 the press event will not be delivered. If the button is released
2231 within the timeout, both the press and release will be delivered.
2233 Note that for nested Flickables with pressDelay set, the pressDelay of
2234 inner Flickables is overridden by the outermost Flickable.
2236 int QQuickFlickable::pressDelay() const
2238 Q_D(const QQuickFlickable);
2239 return d->pressDelay;
2242 void QQuickFlickable::setPressDelay(int delay)
2244 Q_D(QQuickFlickable);
2245 if (d->pressDelay == delay)
2247 d->pressDelay = delay;
2248 emit pressDelayChanged();
2252 \qmlproperty bool QtQuick2::Flickable::moving
2253 \qmlproperty bool QtQuick2::Flickable::movingHorizontally
2254 \qmlproperty bool QtQuick2::Flickable::movingVertically
2256 These properties describe whether the view is currently moving horizontally,
2257 vertically or in either direction, due to the user either dragging or
2261 bool QQuickFlickable::isMoving() const
2263 Q_D(const QQuickFlickable);
2264 return d->hData.moving || d->vData.moving;
2267 bool QQuickFlickable::isMovingHorizontally() const
2269 Q_D(const QQuickFlickable);
2270 return d->hData.moving;
2273 bool QQuickFlickable::isMovingVertically() const
2275 Q_D(const QQuickFlickable);
2276 return d->vData.moving;
2279 void QQuickFlickable::timelineCompleted()
2281 Q_D(QQuickFlickable);
2282 if ( (d->hData.transitionToBounds && d->hData.transitionToBounds->isActive())
2283 || (d->vData.transitionToBounds && d->vData.transitionToBounds->isActive()) ) {
2289 void QQuickFlickable::movementStarting()
2291 Q_D(QQuickFlickable);
2292 bool wasMoving = d->hData.moving || d->vData.moving;
2293 if (d->hMoved && !d->hData.moving) {
2294 d->hData.moving = true;
2295 emit movingHorizontallyChanged();
2297 if (d->vMoved && !d->vData.moving) {
2298 d->vData.moving = true;
2299 emit movingVerticallyChanged();
2302 if (!wasMoving && (d->hData.moving || d->vData.moving)) {
2303 emit movingChanged();
2304 emit movementStarted();
2308 void QQuickFlickable::movementEnding()
2310 movementEnding(true, true);
2313 void QQuickFlickable::movementEnding(bool hMovementEnding, bool vMovementEnding)
2315 Q_D(QQuickFlickable);
2317 // emit flicking signals
2318 bool wasFlicking = d->hData.flicking || d->vData.flicking;
2319 if (hMovementEnding && d->hData.flicking) {
2320 d->hData.flicking = false;
2321 emit flickingHorizontallyChanged();
2323 if (vMovementEnding && d->vData.flicking) {
2324 d->vData.flicking = false;
2325 emit flickingVerticallyChanged();
2327 if (wasFlicking && (!d->hData.flicking || !d->vData.flicking)) {
2328 emit flickingChanged();
2332 // emit moving signals
2333 bool wasMoving = d->hData.moving || d->vData.moving;
2334 if (hMovementEnding && d->hData.moving
2335 && (!d->pressed && !d->stealMouse)) {
2336 d->hData.moving = false;
2338 emit movingHorizontallyChanged();
2340 if (vMovementEnding && d->vData.moving
2341 && (!d->pressed && !d->stealMouse)) {
2342 d->vData.moving = false;
2344 emit movingVerticallyChanged();
2346 if (wasMoving && (!d->hData.moving || !d->vData.moving)) {
2347 emit movingChanged();
2348 emit movementEnded();
2351 if (hMovementEnding) {
2352 d->hData.fixingUp = false;
2353 d->hData.smoothVelocity.setValue(0);
2355 if (vMovementEnding) {
2356 d->vData.fixingUp = false;
2357 d->vData.smoothVelocity.setValue(0);
2361 void QQuickFlickablePrivate::updateVelocity()
2363 Q_Q(QQuickFlickable);
2364 emit q->horizontalVelocityChanged();
2365 emit q->verticalVelocityChanged();