1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
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 emit q->originYChanged();
546 if (hData.extentsChanged) {
547 hData.extentsChanged = false;
548 emit q->originXChanged();
551 if (atBoundaryChange)
552 emit q->isAtBoundaryChanged();
555 visibleArea->updateVisible();
559 XXXTODO add docs describing moving, dragging, flicking properties, e.g.
561 When the user starts dragging the Flickable, the dragging and moving properties
564 If the velocity is sufficient when the drag is ended, flicking may begin.
566 The moving properties will remain true until all dragging and flicking
571 \qmlsignal QtQuick2::Flickable::onDragStarted()
573 This handler is called when the view starts to be dragged due to user
578 \qmlsignal QtQuick2::Flickable::onDragEnded()
580 This handler is called when the user stops dragging the view.
582 If the velocity of the drag is suffient at the time the
583 touch/mouse button is released then a flick will start.
587 \qmlclass Flickable QQuickFlickable
588 \inqmlmodule QtQuick 2
589 \ingroup qtquick-input
590 \ingroup qtquick-containers
592 \brief Provides a surface that can be "flicked"
595 The Flickable item places its children on a surface that can be dragged
596 and flicked, causing the view onto the child items to scroll. This
597 behavior forms the basis of Items that are designed to show large numbers
598 of child items, such as \l ListView and \l GridView.
600 In traditional user interfaces, views can be scrolled using standard
601 controls, such as scroll bars and arrow buttons. In some situations, it
602 is also possible to drag the view directly by pressing and holding a
603 mouse button while moving the cursor. In touch-based user interfaces,
604 this dragging action is often complemented with a flicking action, where
605 scrolling continues after the user has stopped touching the view.
607 Flickable does not automatically clip its contents. If it is not used as
608 a full-screen item, you should consider setting the \l{Item::}{clip} property
611 \section1 Example Usage
613 \div {class="float-right"}
614 \inlineimage flickable.gif
617 The following example shows a small view onto a large image in which the
618 user can drag or flick the image in order to view different parts of it.
620 \snippet qml/flickable.qml document
624 Items declared as children of a Flickable are automatically parented to the
625 Flickable's \l contentItem. This should be taken into account when
626 operating on the children of the Flickable; it is usually the children of
627 \c contentItem that are relevant. For example, the bound of Items added
628 to the Flickable will be available by \c contentItem.childrenRect
630 \section1 Limitations
632 \note Due to an implementation detail, items placed inside a Flickable cannot anchor to it by
633 \c id. Use \c parent instead.
637 \qmlsignal QtQuick2::Flickable::onMovementStarted()
639 This handler is called when the view begins moving due to user
644 \qmlsignal QtQuick2::Flickable::onMovementEnded()
646 This handler is called when the view stops moving due to user
647 interaction. If a flick was generated, this handler will
648 be triggered once the flick stops. If a flick was not
649 generated, the handler will be triggered when the
650 user stops dragging - i.e. a mouse or touch release.
654 \qmlsignal QtQuick2::Flickable::onFlickStarted()
656 This handler is called when the view is flicked. A flick
657 starts from the point that the mouse or touch is released,
658 while still in motion.
662 \qmlsignal QtQuick2::Flickable::onFlickEnded()
664 This handler is called when the view stops moving due to a flick.
668 \qmlproperty real QtQuick2::Flickable::visibleArea.xPosition
669 \qmlproperty real QtQuick2::Flickable::visibleArea.widthRatio
670 \qmlproperty real QtQuick2::Flickable::visibleArea.yPosition
671 \qmlproperty real QtQuick2::Flickable::visibleArea.heightRatio
673 These properties describe the position and size of the currently viewed area.
674 The size is defined as the percentage of the full view currently visible,
675 scaled to 0.0 - 1.0. The page position is usually in the range 0.0 (beginning) to
676 1.0 minus size ratio (end), i.e. \c yPosition is in the range 0.0 to 1.0-\c heightRatio.
677 However, it is possible for the contents to be dragged outside of the normal
678 range, resulting in the page positions also being outside the normal range.
680 These properties are typically used to draw a scrollbar. For example:
682 \snippet qml/flickableScrollbar.qml 0
684 \snippet qml/flickableScrollbar.qml 1
686 \sa {declarative/ui-components/scrollbar}{scrollbar example}
688 QQuickFlickable::QQuickFlickable(QQuickItem *parent)
689 : QQuickItem(*(new QQuickFlickablePrivate), parent)
691 Q_D(QQuickFlickable);
695 QQuickFlickable::QQuickFlickable(QQuickFlickablePrivate &dd, QQuickItem *parent)
696 : QQuickItem(dd, parent)
698 Q_D(QQuickFlickable);
702 QQuickFlickable::~QQuickFlickable()
707 \qmlproperty real QtQuick2::Flickable::contentX
708 \qmlproperty real QtQuick2::Flickable::contentY
710 These properties hold the surface coordinate currently at the top-left
711 corner of the Flickable. For example, if you flick an image up 100 pixels,
712 \c contentY will be 100.
714 qreal QQuickFlickable::contentX() const
716 Q_D(const QQuickFlickable);
717 return -d->contentItem->x();
720 void QQuickFlickable::setContentX(qreal pos)
722 Q_D(QQuickFlickable);
723 d->hData.explicitValue = true;
724 d->resetTimeline(d->hData);
725 d->hData.vTime = d->timeline.time();
726 movementEnding(true, false);
727 if (-pos != d->hData.move.value())
728 d->hData.move.setValue(-pos);
731 qreal QQuickFlickable::contentY() const
733 Q_D(const QQuickFlickable);
734 return -d->contentItem->y();
737 void QQuickFlickable::setContentY(qreal pos)
739 Q_D(QQuickFlickable);
740 d->vData.explicitValue = true;
741 d->resetTimeline(d->vData);
742 d->vData.vTime = d->timeline.time();
743 movementEnding(false, true);
744 if (-pos != d->vData.move.value())
745 d->vData.move.setValue(-pos);
749 \qmlproperty bool QtQuick2::Flickable::interactive
751 This property describes whether the user can interact with the Flickable.
752 A user cannot drag or flick a Flickable that is not interactive.
754 By default, this property is true.
756 This property is useful for temporarily disabling flicking. This allows
757 special interaction with Flickable's children; for example, you might want
758 to freeze a flickable map while scrolling through a pop-up dialog that
759 is a child of the Flickable.
761 bool QQuickFlickable::isInteractive() const
763 Q_D(const QQuickFlickable);
764 return d->interactive;
767 void QQuickFlickable::setInteractive(bool interactive)
769 Q_D(QQuickFlickable);
770 if (interactive != d->interactive) {
771 d->interactive = interactive;
772 if (!interactive && (d->hData.flicking || d->vData.flicking)) {
774 d->hData.vTime = d->vData.vTime = d->timeline.time();
775 d->hData.flicking = false;
776 d->vData.flicking = false;
777 emit flickingChanged();
778 emit flickingHorizontallyChanged();
779 emit flickingVerticallyChanged();
782 emit interactiveChanged();
787 \qmlproperty real QtQuick2::Flickable::horizontalVelocity
788 \qmlproperty real QtQuick2::Flickable::verticalVelocity
790 The instantaneous velocity of movement along the x and y axes, in pixels/sec.
792 The reported velocity is smoothed to avoid erratic output.
794 Note that for views with a large content size (more than 10 times the view size),
795 the velocity of the flick may exceed the velocity of the touch in the case
796 of multiple quick consecutive flicks. This allows the user to flick faster
797 through large content.
799 qreal QQuickFlickable::horizontalVelocity() const
801 Q_D(const QQuickFlickable);
802 return d->hData.smoothVelocity.value();
805 qreal QQuickFlickable::verticalVelocity() const
807 Q_D(const QQuickFlickable);
808 return d->vData.smoothVelocity.value();
812 \qmlproperty bool QtQuick2::Flickable::atXBeginning
813 \qmlproperty bool QtQuick2::Flickable::atXEnd
814 \qmlproperty bool QtQuick2::Flickable::atYBeginning
815 \qmlproperty bool QtQuick2::Flickable::atYEnd
817 These properties are true if the flickable view is positioned at the beginning,
820 bool QQuickFlickable::isAtXEnd() const
822 Q_D(const QQuickFlickable);
823 return d->hData.atEnd;
826 bool QQuickFlickable::isAtXBeginning() const
828 Q_D(const QQuickFlickable);
829 return d->hData.atBeginning;
832 bool QQuickFlickable::isAtYEnd() const
834 Q_D(const QQuickFlickable);
835 return d->vData.atEnd;
838 bool QQuickFlickable::isAtYBeginning() const
840 Q_D(const QQuickFlickable);
841 return d->vData.atBeginning;
845 \qmlproperty Item QtQuick2::Flickable::contentItem
847 The internal item that contains the Items to be moved in the Flickable.
849 Items declared as children of a Flickable are automatically parented to the Flickable's contentItem.
851 Items created dynamically need to be explicitly parented to the \e contentItem:
855 function addItem(file) {
856 var component = Qt.createComponent(file)
857 component.createObject(myFlickable.contentItem);
862 QQuickItem *QQuickFlickable::contentItem()
864 Q_D(QQuickFlickable);
865 return d->contentItem;
868 QQuickFlickableVisibleArea *QQuickFlickable::visibleArea()
870 Q_D(QQuickFlickable);
872 d->visibleArea = new QQuickFlickableVisibleArea(this);
873 return d->visibleArea;
877 \qmlproperty enumeration QtQuick2::Flickable::flickableDirection
879 This property determines which directions the view can be flicked.
882 \li Flickable.AutoFlickDirection (default) - allows flicking vertically if the
883 \e contentHeight is not equal to the \e height of the Flickable.
884 Allows flicking horizontally if the \e contentWidth is not equal
885 to the \e width of the Flickable.
886 \li Flickable.HorizontalFlick - allows flicking horizontally.
887 \li Flickable.VerticalFlick - allows flicking vertically.
888 \li Flickable.HorizontalAndVerticalFlick - allows flicking in both directions.
891 QQuickFlickable::FlickableDirection QQuickFlickable::flickableDirection() const
893 Q_D(const QQuickFlickable);
894 return d->flickableDirection;
897 void QQuickFlickable::setFlickableDirection(FlickableDirection direction)
899 Q_D(QQuickFlickable);
900 if (direction != d->flickableDirection) {
901 d->flickableDirection = direction;
902 emit flickableDirectionChanged();
906 bool QQuickFlickable::pixelAligned() const
908 Q_D(const QQuickFlickable);
909 return d->pixelAligned;
912 void QQuickFlickable::setPixelAligned(bool align)
914 Q_D(QQuickFlickable);
915 if (align != d->pixelAligned) {
916 d->pixelAligned = align;
917 emit pixelAlignedChanged();
921 qint64 QQuickFlickablePrivate::computeCurrentTime(QInputEvent *event)
923 if (0 != event->timestamp() && QQuickItemPrivate::consistentTime == -1)
924 return event->timestamp();
926 return QQuickItemPrivate::elapsed(timer);
929 void QQuickFlickablePrivate::handleMousePressEvent(QMouseEvent *event)
931 Q_Q(QQuickFlickable);
932 QQuickItemPrivate::start(timer);
933 if (interactive && timeline.isActive()
934 && ((qAbs(hData.smoothVelocity.value()) > RetainGrabVelocity && !hData.fixingUp && !hData.inOvershoot)
935 || (qAbs(vData.smoothVelocity.value()) > RetainGrabVelocity && !vData.fixingUp && !vData.inOvershoot))) {
936 stealMouse = true; // If we've been flicked then steal the click.
937 int flickTime = timeline.time();
938 if (flickTime > 600) {
939 // too long between flicks - cancel boost
940 hData.continuousFlickVelocity = 0;
941 vData.continuousFlickVelocity = 0;
944 hData.continuousFlickVelocity = -hData.smoothVelocity.value();
945 vData.continuousFlickVelocity = -vData.smoothVelocity.value();
946 if (flickTime > 300) // slower flicking - reduce boost
947 flickBoost = qMax(1.0, flickBoost - 0.5);
951 hData.continuousFlickVelocity = 0;
952 vData.continuousFlickVelocity = 0;
955 q->setKeepMouseGrab(stealMouse);
957 if (hData.transitionToBounds)
958 hData.transitionToBounds->stopTransition();
959 if (vData.transitionToBounds)
960 vData.transitionToBounds->stopTransition();
962 resetTimeline(hData);
964 resetTimeline(vData);
968 hData.dragMinBound = q->minXExtent();
969 vData.dragMinBound = q->minYExtent();
970 hData.dragMaxBound = q->maxXExtent();
971 vData.dragMaxBound = q->maxYExtent();
974 pressPos = event->localPos();
975 hData.pressPos = hData.move.value();
976 vData.pressPos = vData.move.value();
977 bool wasFlicking = hData.flicking || vData.flicking;
978 if (hData.flicking) {
979 hData.flicking = false;
980 emit q->flickingHorizontallyChanged();
982 if (vData.flicking) {
983 vData.flicking = false;
984 emit q->flickingVerticallyChanged();
987 emit q->flickingChanged();
988 lastPosTime = lastPressTime = computeCurrentTime(event);
989 QQuickItemPrivate::start(vData.velocityTime);
990 QQuickItemPrivate::start(hData.velocityTime);
993 void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event)
995 Q_Q(QQuickFlickable);
996 if (!interactive || lastPosTime == -1)
998 bool rejectY = false;
999 bool rejectX = false;
1001 bool stealY = stealMouse;
1002 bool stealX = stealMouse;
1004 bool prevHMoved = hMoved;
1005 bool prevVMoved = vMoved;
1007 qint64 elapsedSincePress = computeCurrentTime(event) - lastPressTime;
1009 qreal dy = event->localPos().y() - pressPos.y();
1010 bool overThreshold = QQuickWindowPrivate::dragOverThreshold(dy, Qt::YAxis, event);
1011 if (overThreshold || elapsedSincePress > 200) {
1013 vData.dragStartOffset = dy;
1014 qreal newY = dy + vData.pressPos - vData.dragStartOffset;
1015 const qreal minY = vData.dragMinBound;
1016 const qreal maxY = vData.dragMaxBound;
1018 newY = minY + (newY - minY) / 2;
1019 if (newY < maxY && maxY - minY <= 0)
1020 newY = maxY + (newY - maxY) / 2;
1021 if (boundsBehavior == QQuickFlickable::StopAtBounds && (newY > minY || newY < maxY)) {
1032 if (!rejectY && stealMouse && dy != 0.0) {
1034 vData.move.setValue(newY);
1043 qreal dx = event->localPos().x() - pressPos.x();
1044 bool overThreshold = QQuickWindowPrivate::dragOverThreshold(dx, Qt::XAxis, event);
1045 if (overThreshold || elapsedSincePress > 200) {
1047 hData.dragStartOffset = dx;
1048 qreal newX = dx + hData.pressPos - hData.dragStartOffset;
1049 const qreal minX = hData.dragMinBound;
1050 const qreal maxX = hData.dragMaxBound;
1052 newX = minX + (newX - minX) / 2;
1053 if (newX < maxX && maxX - minX <= 0)
1054 newX = maxX + (newX - maxX) / 2;
1055 if (boundsBehavior == QQuickFlickable::StopAtBounds && (newX > minX || newX < maxX)) {
1066 if (!rejectX && stealMouse && dx != 0.0) {
1068 hData.move.setValue(newX);
1077 stealMouse = stealX || stealY;
1079 q->setKeepMouseGrab(true);
1082 vData.velocityBuffer.clear();
1086 hData.velocityBuffer.clear();
1090 if ((hMoved && !prevHMoved) || (vMoved && !prevVMoved)) {
1092 q->movementStarting();
1095 qint64 currentTimestamp = computeCurrentTime(event);
1096 qreal elapsed = qreal(currentTimestamp - (lastPos.isNull() ? lastPressTime : lastPosTime)) / 1000.;
1099 lastPosTime = currentTimestamp;
1100 if (q->yflick() && !rejectY) {
1101 if (QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity) {
1102 vData.addVelocitySample(QGuiApplicationPrivate::mouseEventVelocity(event).y(), maxVelocity);
1104 qreal dy = event->localPos().y() - (lastPos.isNull() ? pressPos.y() : lastPos.y());
1105 vData.addVelocitySample(dy/elapsed, maxVelocity);
1108 if (q->xflick() && !rejectX) {
1109 if (QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity) {
1110 hData.addVelocitySample(QGuiApplicationPrivate::mouseEventVelocity(event).x(), maxVelocity);
1112 qreal dx = event->localPos().x() - (lastPos.isNull() ? pressPos.x() : lastPos.x());
1113 hData.addVelocitySample(dx/elapsed, maxVelocity);
1117 lastPos = event->localPos();
1120 void QQuickFlickablePrivate::handleMouseReleaseEvent(QMouseEvent *event)
1122 Q_Q(QQuickFlickable);
1124 q->setKeepMouseGrab(false);
1127 // if we drag then pause before release we should not cause a flick.
1128 qint64 elapsed = computeCurrentTime(event) - lastPosTime;
1130 vData.updateVelocity();
1131 hData.updateVelocity();
1135 if (lastPosTime == -1)
1138 hData.vTime = vData.vTime = timeline.time();
1140 bool canBoost = false;
1142 qreal vVelocity = 0;
1143 if (elapsed < 100 && vData.velocity != 0.) {
1144 vVelocity = (QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity)
1145 ? QGuiApplicationPrivate::mouseEventVelocity(event).y() : vData.velocity;
1147 if ((vData.atBeginning && vVelocity > 0.) || (vData.atEnd && vVelocity < 0.)) {
1149 } else if (vData.continuousFlickVelocity != 0.0
1150 && vData.viewSize/q->height() > QML_FLICK_MULTIFLICK_RATIO
1151 && ((vVelocity > 0) == (vData.continuousFlickVelocity > 0))
1152 && qAbs(vVelocity) > QML_FLICK_MULTIFLICK_THRESHOLD) {
1153 // accelerate flick for large view flicked quickly
1157 qreal hVelocity = 0;
1158 if (elapsed < 100 && hData.velocity != 0.) {
1159 hVelocity = (QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity)
1160 ? QGuiApplicationPrivate::mouseEventVelocity(event).x() : hData.velocity;
1162 if ((hData.atBeginning && hVelocity > 0.) || (hData.atEnd && hVelocity < 0.)) {
1164 } else if (hData.continuousFlickVelocity != 0.0
1165 && hData.viewSize/q->width() > QML_FLICK_MULTIFLICK_RATIO
1166 && ((hVelocity > 0) == (hData.continuousFlickVelocity > 0))
1167 && qAbs(hVelocity) > QML_FLICK_MULTIFLICK_THRESHOLD) {
1168 // accelerate flick for large view flicked quickly
1172 flickBoost = canBoost ? qBound(1.0, flickBoost+0.25, QML_FLICK_MULTIFLICK_MAXBOOST) : 1.0;
1174 bool flickedV = false;
1175 vVelocity *= flickBoost;
1176 if (q->yflick() && qAbs(vVelocity) > MinimumFlickVelocity && qAbs(event->localPos().y() - pressPos.y()) > FlickThreshold) {
1177 velocityTimeline.reset(vData.smoothVelocity);
1178 vData.smoothVelocity.setValue(-vVelocity);
1179 flickedV = flickY(vVelocity);
1184 bool flickedH = false;
1185 hVelocity *= flickBoost;
1186 if (q->xflick() && qAbs(hVelocity) > MinimumFlickVelocity && qAbs(event->localPos().x() - pressPos.x()) > FlickThreshold) {
1187 velocityTimeline.reset(hData.smoothVelocity);
1188 hData.smoothVelocity.setValue(-hVelocity);
1189 flickedH = flickX(hVelocity);
1194 flickingStarted(flickedH, flickedV);
1195 if (!isViewMoving())
1196 q->movementEnding();
1199 void QQuickFlickable::mousePressEvent(QMouseEvent *event)
1201 Q_D(QQuickFlickable);
1202 if (d->interactive) {
1204 d->handleMousePressEvent(event);
1207 QQuickItem::mousePressEvent(event);
1211 void QQuickFlickable::mouseMoveEvent(QMouseEvent *event)
1213 Q_D(QQuickFlickable);
1214 if (d->interactive) {
1215 d->handleMouseMoveEvent(event);
1218 QQuickItem::mouseMoveEvent(event);
1222 void QQuickFlickable::mouseReleaseEvent(QMouseEvent *event)
1224 Q_D(QQuickFlickable);
1225 if (d->interactive) {
1226 d->clearDelayedPress();
1227 d->handleMouseReleaseEvent(event);
1229 if (window() && window()->mouseGrabberItem() == this)
1232 QQuickItem::mouseReleaseEvent(event);
1236 void QQuickFlickable::wheelEvent(QWheelEvent *event)
1238 Q_D(QQuickFlickable);
1239 if (!d->interactive) {
1240 QQuickItem::wheelEvent(event);
1244 int yDelta = event->angleDelta().y();
1245 int xDelta = event->angleDelta().x();
1246 if (yflick() && yDelta != 0) {
1248 if (yDelta > 0 && contentY() > -minYExtent()) {
1249 d->vData.velocity = qMax(yDelta*2 - d->vData.smoothVelocity.value(), qreal(d->maxVelocity/4));
1251 } else if (yDelta < 0 && contentY() < -maxYExtent()) {
1252 d->vData.velocity = qMin(yDelta*2 - d->vData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
1256 d->vData.flicking = false;
1257 d->flickY(d->vData.velocity);
1258 d->flickingStarted(false, true);
1259 if (d->vData.flicking) {
1266 if (xflick() && xDelta != 0) {
1268 if (xDelta > 0 && contentX() > -minXExtent()) {
1269 d->hData.velocity = qMax(xDelta*2 - d->hData.smoothVelocity.value(), qreal(d->maxVelocity/4));
1271 } else if (xDelta < 0 && contentX() < -maxXExtent()) {
1272 d->hData.velocity = qMin(xDelta*2 - d->hData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
1276 d->hData.flicking = false;
1277 d->flickX(d->hData.velocity);
1278 d->flickingStarted(true, false);
1279 if (d->hData.flicking) {
1287 if (!event->isAccepted())
1288 QQuickItem::wheelEvent(event);
1291 bool QQuickFlickablePrivate::isOutermostPressDelay() const
1293 Q_Q(const QQuickFlickable);
1294 QQuickItem *item = q->parentItem();
1296 QQuickFlickable *flick = qobject_cast<QQuickFlickable*>(item);
1297 if (flick && flick->pressDelay() > 0 && flick->isInteractive())
1299 item = item->parentItem();
1305 void QQuickFlickablePrivate::captureDelayedPress(QMouseEvent *event)
1307 Q_Q(QQuickFlickable);
1308 if (!q->window() || pressDelay <= 0)
1310 if (!isOutermostPressDelay())
1312 delayedPressTarget = q->window()->mouseGrabberItem();
1313 delayedPressEvent = QQuickWindowPrivate::cloneMouseEvent(event);
1314 delayedPressEvent->setAccepted(false);
1315 delayedPressTimer.start(pressDelay, q);
1318 void QQuickFlickablePrivate::clearDelayedPress()
1320 if (delayedPressEvent) {
1321 delayedPressTimer.stop();
1322 delete delayedPressEvent;
1323 delayedPressEvent = 0;
1327 //XXX pixelAligned ignores the global position of the Flickable, i.e. assumes Flickable itself is pixel aligned.
1328 void QQuickFlickablePrivate::setViewportX(qreal x)
1330 contentItem->setX(pixelAligned ? qRound(x) : x);
1333 void QQuickFlickablePrivate::setViewportY(qreal y)
1335 contentItem->setY(pixelAligned ? qRound(y) : y);
1338 void QQuickFlickable::timerEvent(QTimerEvent *event)
1340 Q_D(QQuickFlickable);
1341 if (event->timerId() == d->delayedPressTimer.timerId()) {
1342 d->delayedPressTimer.stop();
1343 if (d->delayedPressEvent) {
1344 QQuickItem *grabber = window() ? window()->mouseGrabberItem() : 0;
1345 if (!grabber || grabber != this) {
1346 // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
1347 // so we reset the grabber
1348 if (window()->mouseGrabberItem() == d->delayedPressTarget)
1349 d->delayedPressTarget->ungrabMouse();
1350 // Use the event handler that will take care of finding the proper item to propagate the event
1351 QQuickWindowPrivate::get(window())->deliverMouseEvent(d->delayedPressEvent);
1353 delete d->delayedPressEvent;
1354 d->delayedPressEvent = 0;
1359 qreal QQuickFlickable::minYExtent() const
1361 Q_D(const QQuickFlickable);
1362 return d->vData.startMargin;
1365 qreal QQuickFlickable::minXExtent() const
1367 Q_D(const QQuickFlickable);
1368 return d->hData.startMargin;
1372 qreal QQuickFlickable::maxXExtent() const
1374 Q_D(const QQuickFlickable);
1375 return width() - vWidth() - d->hData.endMargin;
1378 qreal QQuickFlickable::maxYExtent() const
1380 Q_D(const QQuickFlickable);
1381 return height() - vHeight() - d->vData.endMargin;
1384 void QQuickFlickable::componentComplete()
1386 Q_D(QQuickFlickable);
1387 QQuickItem::componentComplete();
1388 if (!d->hData.explicitValue && d->hData.startMargin != 0.)
1389 setContentX(-minXExtent());
1390 if (!d->vData.explicitValue && d->vData.startMargin != 0.)
1391 setContentY(-minYExtent());
1394 void QQuickFlickable::viewportMoved(Qt::Orientations orient)
1396 Q_D(QQuickFlickable);
1397 if (orient & Qt::Vertical)
1398 d->viewportAxisMoved(d->vData, minYExtent(), maxYExtent(), height(), d->fixupY_callback);
1399 if (orient & Qt::Horizontal)
1400 d->viewportAxisMoved(d->hData, minXExtent(), maxXExtent(), width(), d->fixupX_callback);
1401 d->updateBeginningEnd();
1404 void QQuickFlickablePrivate::viewportAxisMoved(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1405 QQuickTimeLineCallback::Callback fixupCallback)
1407 if (pressed || calcVelocity) {
1408 int elapsed = QQuickItemPrivate::restart(data.velocityTime);
1410 qreal velocity = (data.lastPos - data.move.value()) * 1000 / elapsed;
1411 if (qAbs(velocity) > 0) {
1412 velocityTimeline.reset(data.smoothVelocity);
1414 velocityTimeline.set(data.smoothVelocity, velocity);
1416 velocityTimeline.move(data.smoothVelocity, velocity, reportedVelocitySmoothing);
1417 velocityTimeline.move(data.smoothVelocity, 0, reportedVelocitySmoothing);
1421 if (timeline.time() > data.vTime) {
1422 velocityTimeline.reset(data.smoothVelocity);
1423 qreal velocity = (data.lastPos - data.move.value()) * 1000 / (timeline.time() - data.vTime);
1424 data.smoothVelocity.setValue(velocity);
1428 if (!data.inOvershoot && !data.fixingUp && data.flicking
1429 && (data.move.value() > minExtent || data.move.value() < maxExtent)
1430 && qAbs(data.smoothVelocity.value()) > 10) {
1431 // Increase deceleration if we've passed a bound
1432 qreal overBound = data.move.value() > minExtent
1433 ? data.move.value() - minExtent
1434 : maxExtent - data.move.value();
1435 data.inOvershoot = true;
1436 qreal maxDistance = overShootDistance(vSize) - overBound;
1437 resetTimeline(data);
1438 if (maxDistance > 0)
1439 timeline.accel(data.move, -data.smoothVelocity.value(), deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance);
1440 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
1443 data.lastPos = data.move.value();
1444 data.vTime = timeline.time();
1447 void QQuickFlickable::geometryChanged(const QRectF &newGeometry,
1448 const QRectF &oldGeometry)
1450 Q_D(QQuickFlickable);
1451 QQuickItem::geometryChanged(newGeometry, oldGeometry);
1453 bool changed = false;
1454 if (newGeometry.width() != oldGeometry.width()) {
1457 if (d->hData.viewSize < 0) {
1458 d->contentItem->setWidth(width());
1459 emit contentWidthChanged();
1461 // Make sure that we're entirely in view.
1462 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1463 d->fixupMode = QQuickFlickablePrivate::Immediate;
1467 if (newGeometry.height() != oldGeometry.height()) {
1470 if (d->vData.viewSize < 0) {
1471 d->contentItem->setHeight(height());
1472 emit contentHeightChanged();
1474 // Make sure that we're entirely in view.
1475 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1476 d->fixupMode = QQuickFlickablePrivate::Immediate;
1482 d->updateBeginningEnd();
1486 \qmlmethod QtQuick2::Flickable::flick(qreal xVelocity, qreal yVelocity)
1488 Flicks the content with \a xVelocity horizontally and \a yVelocity vertically in pixels/sec.
1491 void QQuickFlickable::flick(qreal xVelocity, qreal yVelocity)
1493 Q_D(QQuickFlickable);
1494 bool flickedX = d->flickX(xVelocity);
1495 bool flickedY = d->flickY(yVelocity);
1496 d->flickingStarted(flickedX, flickedY);
1499 void QQuickFlickablePrivate::flickingStarted(bool flickingH, bool flickingV)
1501 Q_Q(QQuickFlickable);
1502 if (!flickingH && !flickingV)
1505 bool wasFlicking = hData.flicking || vData.flicking;
1506 if (flickingH && !hData.flicking) {
1507 hData.flicking = true;
1508 emit q->flickingHorizontallyChanged();
1510 if (flickingV && !vData.flicking) {
1511 vData.flicking = true;
1512 emit q->flickingVerticallyChanged();
1514 if (!wasFlicking && (hData.flicking || vData.flicking)) {
1515 emit q->flickingChanged();
1516 emit q->flickStarted();
1521 \qmlmethod QtQuick2::Flickable::cancelFlick()
1523 Cancels the current flick animation.
1526 void QQuickFlickable::cancelFlick()
1528 Q_D(QQuickFlickable);
1529 d->resetTimeline(d->hData);
1530 d->resetTimeline(d->vData);
1534 void QQuickFlickablePrivate::data_append(QQmlListProperty<QObject> *prop, QObject *o)
1536 if (QQuickItem *i = qmlobject_cast<QQuickItem *>(o)) {
1537 i->setParentItem(static_cast<QQuickFlickablePrivate*>(prop->data)->contentItem);
1539 o->setParent(prop->object); // XXX todo - do we want this?
1543 int QQuickFlickablePrivate::data_count(QQmlListProperty<QObject> *)
1549 QObject *QQuickFlickablePrivate::data_at(QQmlListProperty<QObject> *, int)
1555 void QQuickFlickablePrivate::data_clear(QQmlListProperty<QObject> *)
1560 QQmlListProperty<QObject> QQuickFlickable::flickableData()
1562 Q_D(QQuickFlickable);
1563 return QQmlListProperty<QObject>(this, (void *)d, QQuickFlickablePrivate::data_append,
1564 QQuickFlickablePrivate::data_count,
1565 QQuickFlickablePrivate::data_at,
1566 QQuickFlickablePrivate::data_clear);
1569 QQmlListProperty<QQuickItem> QQuickFlickable::flickableChildren()
1571 Q_D(QQuickFlickable);
1572 return QQuickItemPrivate::get(d->contentItem)->children();
1576 \qmlproperty enumeration QtQuick2::Flickable::boundsBehavior
1577 This property holds whether the surface may be dragged
1578 beyond the Flickable's boundaries, or overshoot the
1579 Flickable's boundaries when flicked.
1581 This enables the feeling that the edges of the view are soft,
1582 rather than a hard physical boundary.
1584 The \c boundsBehavior can be one of:
1587 \li Flickable.StopAtBounds - the contents can not be dragged beyond the boundary
1588 of the flickable, and flicks will not overshoot.
1589 \li Flickable.DragOverBounds - the contents can be dragged beyond the boundary
1590 of the Flickable, but flicks will not overshoot.
1591 \li Flickable.DragAndOvershootBounds (default) - the contents can be dragged
1592 beyond the boundary of the Flickable, and can overshoot the
1593 boundary when flicked.
1596 QQuickFlickable::BoundsBehavior QQuickFlickable::boundsBehavior() const
1598 Q_D(const QQuickFlickable);
1599 return d->boundsBehavior;
1602 void QQuickFlickable::setBoundsBehavior(BoundsBehavior b)
1604 Q_D(QQuickFlickable);
1605 if (b == d->boundsBehavior)
1607 d->boundsBehavior = b;
1608 emit boundsBehaviorChanged();
1612 \qmlproperty Transition QtQuick2::Flickable::rebound
1614 This holds the transition to be applied to the content view when
1615 it snaps back to the bounds of the flickable. The transition is
1616 triggered when the view is flicked or dragged past the edge of the
1617 content area, or when returnToBounds() is called.
1623 width: 150; height: 150
1624 contentWidth: 300; contentHeight: 300
1626 rebound: Transition {
1630 easing.type: Easing.OutBounce
1635 width: 300; height: 300
1636 gradient: Gradient {
1637 GradientStop { position: 0.0; color: "lightsteelblue" }
1638 GradientStop { position: 1.0; color: "blue" }
1644 When the above view is flicked beyond its bounds, it will return to its
1645 bounds using the transition specified:
1647 \image flickable-rebound.gif
1649 If this property is not set, a default animation is applied.
1651 QQuickTransition *QQuickFlickable::rebound() const
1653 Q_D(const QQuickFlickable);
1657 void QQuickFlickable::setRebound(QQuickTransition *transition)
1659 Q_D(QQuickFlickable);
1661 if (!d->hData.transitionToBounds)
1662 d->hData.transitionToBounds = new QQuickFlickableReboundTransition(this, QLatin1String("x"));
1663 if (!d->vData.transitionToBounds)
1664 d->vData.transitionToBounds = new QQuickFlickableReboundTransition(this, QLatin1String("y"));
1666 if (d->rebound != transition) {
1667 d->rebound = transition;
1668 emit reboundChanged();
1673 \qmlproperty real QtQuick2::Flickable::contentWidth
1674 \qmlproperty real QtQuick2::Flickable::contentHeight
1676 The dimensions of the content (the surface controlled by Flickable).
1677 This should typically be set to the combined size of the items placed in the
1680 The following snippet shows how these properties are used to display
1681 an image that is larger than the Flickable item itself:
1683 \snippet qml/flickable.qml document
1685 In some cases, the content dimensions can be automatically set
1686 based on the \l {Item::childrenRect.width}{childrenRect.width}
1687 and \l {Item::childrenRect.height}{childrenRect.height} properties
1688 of the \l contentItem. For example, the previous snippet could be rewritten with:
1691 contentWidth: contentItem.childrenRect.width; contentHeight: contentItem.childrenRect.height
1694 Though this assumes that the origin of the childrenRect is 0,0.
1696 qreal QQuickFlickable::contentWidth() const
1698 Q_D(const QQuickFlickable);
1699 return d->hData.viewSize;
1702 void QQuickFlickable::setContentWidth(qreal w)
1704 Q_D(QQuickFlickable);
1705 if (d->hData.viewSize == w)
1707 d->hData.viewSize = w;
1709 d->contentItem->setWidth(width());
1711 d->contentItem->setWidth(w);
1712 d->hData.markExtentsDirty();
1713 // Make sure that we're entirely in view.
1714 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1715 d->fixupMode = QQuickFlickablePrivate::Immediate;
1717 } else if (!d->pressed && d->hData.fixingUp) {
1718 d->fixupMode = QQuickFlickablePrivate::ExtentChanged;
1721 emit contentWidthChanged();
1722 d->updateBeginningEnd();
1725 qreal QQuickFlickable::contentHeight() const
1727 Q_D(const QQuickFlickable);
1728 return d->vData.viewSize;
1731 void QQuickFlickable::setContentHeight(qreal h)
1733 Q_D(QQuickFlickable);
1734 if (d->vData.viewSize == h)
1736 d->vData.viewSize = h;
1738 d->contentItem->setHeight(height());
1740 d->contentItem->setHeight(h);
1741 d->vData.markExtentsDirty();
1742 // Make sure that we're entirely in view.
1743 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1744 d->fixupMode = QQuickFlickablePrivate::Immediate;
1746 } else if (!d->pressed && d->vData.fixingUp) {
1747 d->fixupMode = QQuickFlickablePrivate::ExtentChanged;
1750 emit contentHeightChanged();
1751 d->updateBeginningEnd();
1755 \qmlproperty real QtQuick2::Flickable::topMargin
1756 \qmlproperty real QtQuick2::Flickable::leftMargin
1757 \qmlproperty real QtQuick2::Flickable::bottomMargin
1758 \qmlproperty real QtQuick2::Flickable::rightMargin
1760 These properties hold the margins around the content. This space is reserved
1761 in addition to the contentWidth and contentHeight.
1765 qreal QQuickFlickable::topMargin() const
1767 Q_D(const QQuickFlickable);
1768 return d->vData.startMargin;
1771 void QQuickFlickable::setTopMargin(qreal m)
1773 Q_D(QQuickFlickable);
1774 if (d->vData.startMargin == m)
1776 d->vData.startMargin = m;
1777 d->vData.markExtentsDirty();
1778 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1779 d->fixupMode = QQuickFlickablePrivate::Immediate;
1782 emit topMarginChanged();
1783 d->updateBeginningEnd();
1786 qreal QQuickFlickable::bottomMargin() const
1788 Q_D(const QQuickFlickable);
1789 return d->vData.endMargin;
1792 void QQuickFlickable::setBottomMargin(qreal m)
1794 Q_D(QQuickFlickable);
1795 if (d->vData.endMargin == m)
1797 d->vData.endMargin = m;
1798 d->vData.markExtentsDirty();
1799 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1800 d->fixupMode = QQuickFlickablePrivate::Immediate;
1803 emit bottomMarginChanged();
1804 d->updateBeginningEnd();
1807 qreal QQuickFlickable::leftMargin() const
1809 Q_D(const QQuickFlickable);
1810 return d->hData.startMargin;
1813 void QQuickFlickable::setLeftMargin(qreal m)
1815 Q_D(QQuickFlickable);
1816 if (d->hData.startMargin == m)
1818 d->hData.startMargin = m;
1819 d->hData.markExtentsDirty();
1820 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1821 d->fixupMode = QQuickFlickablePrivate::Immediate;
1824 emit leftMarginChanged();
1825 d->updateBeginningEnd();
1828 qreal QQuickFlickable::rightMargin() const
1830 Q_D(const QQuickFlickable);
1831 return d->hData.endMargin;
1834 void QQuickFlickable::setRightMargin(qreal m)
1836 Q_D(QQuickFlickable);
1837 if (d->hData.endMargin == m)
1839 d->hData.endMargin = m;
1840 d->hData.markExtentsDirty();
1841 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1842 d->fixupMode = QQuickFlickablePrivate::Immediate;
1845 emit rightMarginChanged();
1846 d->updateBeginningEnd();
1850 \qmlproperty real QtQuick2::Flickable::originX
1851 \qmlproperty real QtQuick2::Flickable::originY
1853 These properties hold the origin of the content. This value always refers
1854 to the top-left position of the content regardless of layout direction.
1856 This is usually (0,0), however ListView and GridView may have an arbitrary
1857 origin due to delegate size variation, or item insertion/removal outside
1861 qreal QQuickFlickable::originY() const
1863 Q_D(const QQuickFlickable);
1864 return -minYExtent() + d->vData.startMargin;
1867 qreal QQuickFlickable::originX() const
1869 Q_D(const QQuickFlickable);
1870 return -minXExtent() + d->hData.startMargin;
1875 \qmlmethod QtQuick2::Flickable::resizeContent(real width, real height, QPointF center)
1877 Resizes the content to \a width x \a height about \a center.
1879 This does not scale the contents of the Flickable - it only resizes the \l contentWidth
1880 and \l contentHeight.
1882 Resizing the content may result in the content being positioned outside
1883 the bounds of the Flickable. Calling \l returnToBounds() will
1884 move the content back within legal bounds.
1886 void QQuickFlickable::resizeContent(qreal w, qreal h, QPointF center)
1888 Q_D(QQuickFlickable);
1889 if (w != d->hData.viewSize) {
1890 qreal oldSize = d->hData.viewSize;
1891 d->hData.viewSize = w;
1892 d->contentItem->setWidth(w);
1893 emit contentWidthChanged();
1894 if (center.x() != 0) {
1895 qreal pos = center.x() * w / oldSize;
1896 setContentX(contentX() + pos - center.x());
1899 if (h != d->vData.viewSize) {
1900 qreal oldSize = d->vData.viewSize;
1901 d->vData.viewSize = h;
1902 d->contentItem->setHeight(h);
1903 emit contentHeightChanged();
1904 if (center.y() != 0) {
1905 qreal pos = center.y() * h / oldSize;
1906 setContentY(contentY() + pos - center.y());
1909 d->updateBeginningEnd();
1913 \qmlmethod QtQuick2::Flickable::returnToBounds()
1915 Ensures the content is within legal bounds.
1917 This may be called to ensure that the content is within legal bounds
1918 after manually positioning the content.
1920 void QQuickFlickable::returnToBounds()
1922 Q_D(QQuickFlickable);
1927 qreal QQuickFlickable::vWidth() const
1929 Q_D(const QQuickFlickable);
1930 if (d->hData.viewSize < 0)
1933 return d->hData.viewSize;
1936 qreal QQuickFlickable::vHeight() const
1938 Q_D(const QQuickFlickable);
1939 if (d->vData.viewSize < 0)
1942 return d->vData.viewSize;
1945 bool QQuickFlickable::xflick() const
1947 Q_D(const QQuickFlickable);
1948 if (d->flickableDirection == QQuickFlickable::AutoFlickDirection)
1949 return floor(qAbs(vWidth() - width()));
1950 return d->flickableDirection & QQuickFlickable::HorizontalFlick;
1953 bool QQuickFlickable::yflick() const
1955 Q_D(const QQuickFlickable);
1956 if (d->flickableDirection == QQuickFlickable::AutoFlickDirection)
1957 return floor(qAbs(vHeight() - height()));
1958 return d->flickableDirection & QQuickFlickable::VerticalFlick;
1961 void QQuickFlickable::mouseUngrabEvent()
1963 Q_D(QQuickFlickable);
1965 // if our mouse grab has been removed (probably by another Flickable),
1967 d->clearDelayedPress();
1969 d->draggingEnding();
1970 d->stealMouse = false;
1971 setKeepMouseGrab(false);
1974 if (!d->isViewMoving())
1979 bool QQuickFlickable::sendMouseEvent(QMouseEvent *event)
1981 Q_D(QQuickFlickable);
1982 QPointF localPos = mapFromScene(event->windowPos());
1984 QQuickWindow *c = window();
1985 QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
1986 bool grabberDisabled = grabber && !grabber->isEnabled();
1987 bool stealThisEvent = d->stealMouse;
1988 if ((stealThisEvent || contains(localPos)) && (!grabber || !grabber->keepMouseGrab() || grabberDisabled)) {
1989 QScopedPointer<QMouseEvent> mouseEvent(QQuickWindowPrivate::cloneMouseEvent(event, &localPos));
1990 mouseEvent->setAccepted(false);
1992 switch (mouseEvent->type()) {
1993 case QEvent::MouseMove:
1994 d->handleMouseMoveEvent(mouseEvent.data());
1996 case QEvent::MouseButtonPress:
1997 if (d->pressed) // we are already pressed - this is a delayed replay
2000 d->handleMousePressEvent(mouseEvent.data());
2001 d->captureDelayedPress(event);
2002 stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
2004 case QEvent::MouseButtonRelease:
2005 if (d->delayedPressEvent) {
2006 // We replay the mouse press but the grabber we had might not be interested in the event (e.g. overlay)
2007 // so we reset the grabber
2008 if (c->mouseGrabberItem() == d->delayedPressTarget)
2009 d->delayedPressTarget->ungrabMouse();
2010 // Use the event handler that will take care of finding the proper item to propagate the event
2011 QQuickWindowPrivate::get(window())->deliverMouseEvent(d->delayedPressEvent);
2012 d->clearDelayedPress();
2013 // We send the release
2014 window()->sendEvent(c->mouseGrabberItem(), event);
2015 // And the event has been consumed
2016 d->stealMouse = false;
2020 d->handleMouseReleaseEvent(mouseEvent.data());
2025 grabber = qobject_cast<QQuickItem*>(c->mouseGrabberItem());
2026 if ((grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) || grabberDisabled) {
2027 d->clearDelayedPress();
2031 return stealThisEvent || d->delayedPressEvent || grabberDisabled;
2032 } else if (d->lastPosTime != -1) {
2033 d->lastPosTime = -1;
2036 if (event->type() == QEvent::MouseButtonRelease) {
2037 d->lastPosTime = -1;
2038 d->clearDelayedPress();
2039 d->stealMouse = false;
2046 bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e)
2048 Q_D(QQuickFlickable);
2049 if (!isVisible() || !isEnabled())
2050 return QQuickItem::childMouseEventFilter(i, e);
2051 switch (e->type()) {
2052 case QEvent::MouseButtonPress:
2053 case QEvent::MouseMove:
2054 case QEvent::MouseButtonRelease:
2055 return sendMouseEvent(static_cast<QMouseEvent *>(e));
2056 case QEvent::UngrabMouse:
2057 if (d->window && d->window->mouseGrabberItem() && d->window->mouseGrabberItem() != this) {
2058 // The grab has been taken away from a child and given to some other item.
2066 return QQuickItem::childMouseEventFilter(i, e);
2070 \qmlproperty real QtQuick2::Flickable::maximumFlickVelocity
2071 This property holds the maximum velocity that the user can flick the view in pixels/second.
2073 The default value is platform dependent.
2075 qreal QQuickFlickable::maximumFlickVelocity() const
2077 Q_D(const QQuickFlickable);
2078 return d->maxVelocity;
2081 void QQuickFlickable::setMaximumFlickVelocity(qreal v)
2083 Q_D(QQuickFlickable);
2084 if (v == d->maxVelocity)
2087 emit maximumFlickVelocityChanged();
2091 \qmlproperty real QtQuick2::Flickable::flickDeceleration
2092 This property holds the rate at which a flick will decelerate.
2094 The default value is platform dependent.
2096 qreal QQuickFlickable::flickDeceleration() const
2098 Q_D(const QQuickFlickable);
2099 return d->deceleration;
2102 void QQuickFlickable::setFlickDeceleration(qreal deceleration)
2104 Q_D(QQuickFlickable);
2105 if (deceleration == d->deceleration)
2107 d->deceleration = deceleration;
2108 emit flickDecelerationChanged();
2111 bool QQuickFlickable::isFlicking() const
2113 Q_D(const QQuickFlickable);
2114 return d->hData.flicking || d->vData.flicking;
2118 \qmlproperty bool QtQuick2::Flickable::flicking
2119 \qmlproperty bool QtQuick2::Flickable::flickingHorizontally
2120 \qmlproperty bool QtQuick2::Flickable::flickingVertically
2122 These properties describe whether the view is currently moving horizontally,
2123 vertically or in either direction, due to the user flicking the view.
2125 bool QQuickFlickable::isFlickingHorizontally() const
2127 Q_D(const QQuickFlickable);
2128 return d->hData.flicking;
2131 bool QQuickFlickable::isFlickingVertically() const
2133 Q_D(const QQuickFlickable);
2134 return d->vData.flicking;
2138 \qmlproperty bool QtQuick2::Flickable::dragging
2139 \qmlproperty bool QtQuick2::Flickable::draggingHorizontally
2140 \qmlproperty bool QtQuick2::Flickable::draggingVertically
2142 These properties describe whether the view is currently moving horizontally,
2143 vertically or in either direction, due to the user dragging the view.
2145 bool QQuickFlickable::isDragging() const
2147 Q_D(const QQuickFlickable);
2148 return d->hData.dragging || d->vData.dragging;
2151 bool QQuickFlickable::isDraggingHorizontally() const
2153 Q_D(const QQuickFlickable);
2154 return d->hData.dragging;
2157 bool QQuickFlickable::isDraggingVertically() const
2159 Q_D(const QQuickFlickable);
2160 return d->vData.dragging;
2163 void QQuickFlickablePrivate::draggingStarting()
2165 Q_Q(QQuickFlickable);
2166 bool wasDragging = hData.dragging || vData.dragging;
2167 if (hMoved && !hData.dragging) {
2168 hData.dragging = true;
2169 emit q->draggingHorizontallyChanged();
2171 if (vMoved && !vData.dragging) {
2172 vData.dragging = true;
2173 emit q->draggingVerticallyChanged();
2175 if (!wasDragging && (hData.dragging || vData.dragging)) {
2176 emit q->draggingChanged();
2177 emit q->dragStarted();
2181 void QQuickFlickablePrivate::draggingEnding()
2183 Q_Q(QQuickFlickable);
2184 bool wasDragging = hData.dragging || vData.dragging;
2185 if (hData.dragging) {
2186 hData.dragging = false;
2187 emit q->draggingHorizontallyChanged();
2189 if (vData.dragging) {
2190 vData.dragging = false;
2191 emit q->draggingVerticallyChanged();
2193 if (wasDragging && !hData.dragging && !vData.dragging) {
2194 emit q->draggingChanged();
2195 emit q->dragEnded();
2199 bool QQuickFlickablePrivate::isViewMoving() const
2201 if (timeline.isActive()
2202 || (hData.transitionToBounds && hData.transitionToBounds->isActive())
2203 || (vData.transitionToBounds && vData.transitionToBounds->isActive()) ) {
2210 \qmlproperty int QtQuick2::Flickable::pressDelay
2212 This property holds the time to delay (ms) delivering a press to
2213 children of the Flickable. This can be useful where reacting
2214 to a press before a flicking action has undesirable effects.
2216 If the flickable is dragged/flicked before the delay times out
2217 the press event will not be delivered. If the button is released
2218 within the timeout, both the press and release will be delivered.
2220 Note that for nested Flickables with pressDelay set, the pressDelay of
2221 inner Flickables is overridden by the outermost Flickable.
2223 int QQuickFlickable::pressDelay() const
2225 Q_D(const QQuickFlickable);
2226 return d->pressDelay;
2229 void QQuickFlickable::setPressDelay(int delay)
2231 Q_D(QQuickFlickable);
2232 if (d->pressDelay == delay)
2234 d->pressDelay = delay;
2235 emit pressDelayChanged();
2239 \qmlproperty bool QtQuick2::Flickable::moving
2240 \qmlproperty bool QtQuick2::Flickable::movingHorizontally
2241 \qmlproperty bool QtQuick2::Flickable::movingVertically
2243 These properties describe whether the view is currently moving horizontally,
2244 vertically or in either direction, due to the user either dragging or
2248 bool QQuickFlickable::isMoving() const
2250 Q_D(const QQuickFlickable);
2251 return d->hData.moving || d->vData.moving;
2254 bool QQuickFlickable::isMovingHorizontally() const
2256 Q_D(const QQuickFlickable);
2257 return d->hData.moving;
2260 bool QQuickFlickable::isMovingVertically() const
2262 Q_D(const QQuickFlickable);
2263 return d->vData.moving;
2266 void QQuickFlickable::timelineCompleted()
2268 Q_D(QQuickFlickable);
2269 if ( (d->hData.transitionToBounds && d->hData.transitionToBounds->isActive())
2270 || (d->vData.transitionToBounds && d->vData.transitionToBounds->isActive()) ) {
2276 void QQuickFlickable::movementStarting()
2278 Q_D(QQuickFlickable);
2279 bool wasMoving = d->hData.moving || d->vData.moving;
2280 if (d->hMoved && !d->hData.moving) {
2281 d->hData.moving = true;
2282 emit movingHorizontallyChanged();
2284 if (d->vMoved && !d->vData.moving) {
2285 d->vData.moving = true;
2286 emit movingVerticallyChanged();
2289 if (!wasMoving && (d->hData.moving || d->vData.moving)) {
2290 emit movingChanged();
2291 emit movementStarted();
2295 void QQuickFlickable::movementEnding()
2297 movementEnding(true, true);
2300 void QQuickFlickable::movementEnding(bool hMovementEnding, bool vMovementEnding)
2302 Q_D(QQuickFlickable);
2304 // emit flicking signals
2305 bool wasFlicking = d->hData.flicking || d->vData.flicking;
2306 if (hMovementEnding && d->hData.flicking) {
2307 d->hData.flicking = false;
2308 emit flickingHorizontallyChanged();
2310 if (vMovementEnding && d->vData.flicking) {
2311 d->vData.flicking = false;
2312 emit flickingVerticallyChanged();
2314 if (wasFlicking && (!d->hData.flicking || !d->vData.flicking)) {
2315 emit flickingChanged();
2319 // emit moving signals
2320 bool wasMoving = d->hData.moving || d->vData.moving;
2321 if (hMovementEnding && d->hData.moving
2322 && (!d->pressed && !d->stealMouse)) {
2323 d->hData.moving = false;
2325 emit movingHorizontallyChanged();
2327 if (vMovementEnding && d->vData.moving
2328 && (!d->pressed && !d->stealMouse)) {
2329 d->vData.moving = false;
2331 emit movingVerticallyChanged();
2333 if (wasMoving && (!d->hData.moving || !d->vData.moving)) {
2334 emit movingChanged();
2335 emit movementEnded();
2338 if (hMovementEnding) {
2339 d->hData.fixingUp = false;
2340 d->hData.smoothVelocity.setValue(0);
2342 if (vMovementEnding) {
2343 d->vData.fixingUp = false;
2344 d->vData.smoothVelocity.setValue(0);
2348 void QQuickFlickablePrivate::updateVelocity()
2350 Q_Q(QQuickFlickable);
2351 emit q->horizontalVelocityChanged();
2352 emit q->verticalVelocityChanged();