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 sufficient at the time the
583 touch/mouse button is released then a flick will start.
588 \instantiates QQuickFlickable
589 \inqmlmodule QtQuick 2
590 \ingroup qtquick-input
591 \ingroup qtquick-containers
593 \brief Provides a surface that can be "flicked"
596 The Flickable item places its children on a surface that can be dragged
597 and flicked, causing the view onto the child items to scroll. This
598 behavior forms the basis of Items that are designed to show large numbers
599 of child items, such as \l ListView and \l GridView.
601 In traditional user interfaces, views can be scrolled using standard
602 controls, such as scroll bars and arrow buttons. In some situations, it
603 is also possible to drag the view directly by pressing and holding a
604 mouse button while moving the cursor. In touch-based user interfaces,
605 this dragging action is often complemented with a flicking action, where
606 scrolling continues after the user has stopped touching the view.
608 Flickable does not automatically clip its contents. If it is not used as
609 a full-screen item, you should consider setting the \l{Item::}{clip} property
612 \section1 Example Usage
614 \div {class="float-right"}
615 \inlineimage flickable.gif
618 The following example shows a small view onto a large image in which the
619 user can drag or flick the image in order to view different parts of it.
621 \snippet qml/flickable.qml document
625 Items declared as children of a Flickable are automatically parented to the
626 Flickable's \l contentItem. This should be taken into account when
627 operating on the children of the Flickable; it is usually the children of
628 \c contentItem that are relevant. For example, the bound of Items added
629 to the Flickable will be available by \c contentItem.childrenRect
631 \section1 Limitations
633 \note Due to an implementation detail, items placed inside a Flickable cannot anchor to it by
634 \c id. Use \c parent instead.
638 \qmlsignal QtQuick2::Flickable::onMovementStarted()
640 This handler is called when the view begins moving due to user
645 \qmlsignal QtQuick2::Flickable::onMovementEnded()
647 This handler is called when the view stops moving due to user
648 interaction. If a flick was generated, this handler will
649 be triggered once the flick stops. If a flick was not
650 generated, the handler will be triggered when the
651 user stops dragging - i.e. a mouse or touch release.
655 \qmlsignal QtQuick2::Flickable::onFlickStarted()
657 This handler is called when the view is flicked. A flick
658 starts from the point that the mouse or touch is released,
659 while still in motion.
663 \qmlsignal QtQuick2::Flickable::onFlickEnded()
665 This handler is called when the view stops moving due to a flick.
669 \qmlproperty real QtQuick2::Flickable::visibleArea.xPosition
670 \qmlproperty real QtQuick2::Flickable::visibleArea.widthRatio
671 \qmlproperty real QtQuick2::Flickable::visibleArea.yPosition
672 \qmlproperty real QtQuick2::Flickable::visibleArea.heightRatio
674 These properties describe the position and size of the currently viewed area.
675 The size is defined as the percentage of the full view currently visible,
676 scaled to 0.0 - 1.0. The page position is usually in the range 0.0 (beginning) to
677 1.0 minus size ratio (end), i.e. \c yPosition is in the range 0.0 to 1.0-\c heightRatio.
678 However, it is possible for the contents to be dragged outside of the normal
679 range, resulting in the page positions also being outside the normal range.
681 These properties are typically used to draw a scrollbar. For example:
683 \snippet qml/flickableScrollbar.qml 0
685 \snippet qml/flickableScrollbar.qml 1
687 \sa {declarative/ui-components/scrollbar}{scrollbar example}
689 QQuickFlickable::QQuickFlickable(QQuickItem *parent)
690 : QQuickItem(*(new QQuickFlickablePrivate), parent)
692 Q_D(QQuickFlickable);
696 QQuickFlickable::QQuickFlickable(QQuickFlickablePrivate &dd, QQuickItem *parent)
697 : QQuickItem(dd, parent)
699 Q_D(QQuickFlickable);
703 QQuickFlickable::~QQuickFlickable()
708 \qmlproperty real QtQuick2::Flickable::contentX
709 \qmlproperty real QtQuick2::Flickable::contentY
711 These properties hold the surface coordinate currently at the top-left
712 corner of the Flickable. For example, if you flick an image up 100 pixels,
713 \c contentY will be 100.
715 qreal QQuickFlickable::contentX() const
717 Q_D(const QQuickFlickable);
718 return -d->contentItem->x();
721 void QQuickFlickable::setContentX(qreal pos)
723 Q_D(QQuickFlickable);
724 d->hData.explicitValue = true;
725 d->resetTimeline(d->hData);
726 d->hData.vTime = d->timeline.time();
727 movementEnding(true, false);
728 if (-pos != d->hData.move.value())
729 d->hData.move.setValue(-pos);
732 qreal QQuickFlickable::contentY() const
734 Q_D(const QQuickFlickable);
735 return -d->contentItem->y();
738 void QQuickFlickable::setContentY(qreal pos)
740 Q_D(QQuickFlickable);
741 d->vData.explicitValue = true;
742 d->resetTimeline(d->vData);
743 d->vData.vTime = d->timeline.time();
744 movementEnding(false, true);
745 if (-pos != d->vData.move.value())
746 d->vData.move.setValue(-pos);
750 \qmlproperty bool QtQuick2::Flickable::interactive
752 This property describes whether the user can interact with the Flickable.
753 A user cannot drag or flick a Flickable that is not interactive.
755 By default, this property is true.
757 This property is useful for temporarily disabling flicking. This allows
758 special interaction with Flickable's children; for example, you might want
759 to freeze a flickable map while scrolling through a pop-up dialog that
760 is a child of the Flickable.
762 bool QQuickFlickable::isInteractive() const
764 Q_D(const QQuickFlickable);
765 return d->interactive;
768 void QQuickFlickable::setInteractive(bool interactive)
770 Q_D(QQuickFlickable);
771 if (interactive != d->interactive) {
772 d->interactive = interactive;
773 if (!interactive && (d->hData.flicking || d->vData.flicking)) {
775 d->hData.vTime = d->vData.vTime = d->timeline.time();
776 d->hData.flicking = false;
777 d->vData.flicking = false;
778 emit flickingChanged();
779 emit flickingHorizontallyChanged();
780 emit flickingVerticallyChanged();
783 emit interactiveChanged();
788 \qmlproperty real QtQuick2::Flickable::horizontalVelocity
789 \qmlproperty real QtQuick2::Flickable::verticalVelocity
791 The instantaneous velocity of movement along the x and y axes, in pixels/sec.
793 The reported velocity is smoothed to avoid erratic output.
795 Note that for views with a large content size (more than 10 times the view size),
796 the velocity of the flick may exceed the velocity of the touch in the case
797 of multiple quick consecutive flicks. This allows the user to flick faster
798 through large content.
800 qreal QQuickFlickable::horizontalVelocity() const
802 Q_D(const QQuickFlickable);
803 return d->hData.smoothVelocity.value();
806 qreal QQuickFlickable::verticalVelocity() const
808 Q_D(const QQuickFlickable);
809 return d->vData.smoothVelocity.value();
813 \qmlproperty bool QtQuick2::Flickable::atXBeginning
814 \qmlproperty bool QtQuick2::Flickable::atXEnd
815 \qmlproperty bool QtQuick2::Flickable::atYBeginning
816 \qmlproperty bool QtQuick2::Flickable::atYEnd
818 These properties are true if the flickable view is positioned at the beginning,
821 bool QQuickFlickable::isAtXEnd() const
823 Q_D(const QQuickFlickable);
824 return d->hData.atEnd;
827 bool QQuickFlickable::isAtXBeginning() const
829 Q_D(const QQuickFlickable);
830 return d->hData.atBeginning;
833 bool QQuickFlickable::isAtYEnd() const
835 Q_D(const QQuickFlickable);
836 return d->vData.atEnd;
839 bool QQuickFlickable::isAtYBeginning() const
841 Q_D(const QQuickFlickable);
842 return d->vData.atBeginning;
846 \qmlproperty Item QtQuick2::Flickable::contentItem
848 The internal item that contains the Items to be moved in the Flickable.
850 Items declared as children of a Flickable are automatically parented to the Flickable's contentItem.
852 Items created dynamically need to be explicitly parented to the \e contentItem:
856 function addItem(file) {
857 var component = Qt.createComponent(file)
858 component.createObject(myFlickable.contentItem);
863 QQuickItem *QQuickFlickable::contentItem()
865 Q_D(QQuickFlickable);
866 return d->contentItem;
869 QQuickFlickableVisibleArea *QQuickFlickable::visibleArea()
871 Q_D(QQuickFlickable);
873 d->visibleArea = new QQuickFlickableVisibleArea(this);
874 return d->visibleArea;
878 \qmlproperty enumeration QtQuick2::Flickable::flickableDirection
880 This property determines which directions the view can be flicked.
883 \li Flickable.AutoFlickDirection (default) - allows flicking vertically if the
884 \e contentHeight is not equal to the \e height of the Flickable.
885 Allows flicking horizontally if the \e contentWidth is not equal
886 to the \e width of the Flickable.
887 \li Flickable.HorizontalFlick - allows flicking horizontally.
888 \li Flickable.VerticalFlick - allows flicking vertically.
889 \li Flickable.HorizontalAndVerticalFlick - allows flicking in both directions.
892 QQuickFlickable::FlickableDirection QQuickFlickable::flickableDirection() const
894 Q_D(const QQuickFlickable);
895 return d->flickableDirection;
898 void QQuickFlickable::setFlickableDirection(FlickableDirection direction)
900 Q_D(QQuickFlickable);
901 if (direction != d->flickableDirection) {
902 d->flickableDirection = direction;
903 emit flickableDirectionChanged();
907 bool QQuickFlickable::pixelAligned() const
909 Q_D(const QQuickFlickable);
910 return d->pixelAligned;
913 void QQuickFlickable::setPixelAligned(bool align)
915 Q_D(QQuickFlickable);
916 if (align != d->pixelAligned) {
917 d->pixelAligned = align;
918 emit pixelAlignedChanged();
922 qint64 QQuickFlickablePrivate::computeCurrentTime(QInputEvent *event)
924 if (0 != event->timestamp() && QQuickItemPrivate::consistentTime == -1)
925 return event->timestamp();
927 return QQuickItemPrivate::elapsed(timer);
930 void QQuickFlickablePrivate::handleMousePressEvent(QMouseEvent *event)
932 Q_Q(QQuickFlickable);
933 QQuickItemPrivate::start(timer);
934 if (interactive && timeline.isActive()
935 && ((qAbs(hData.smoothVelocity.value()) > RetainGrabVelocity && !hData.fixingUp && !hData.inOvershoot)
936 || (qAbs(vData.smoothVelocity.value()) > RetainGrabVelocity && !vData.fixingUp && !vData.inOvershoot))) {
937 stealMouse = true; // If we've been flicked then steal the click.
938 int flickTime = timeline.time();
939 if (flickTime > 600) {
940 // too long between flicks - cancel boost
941 hData.continuousFlickVelocity = 0;
942 vData.continuousFlickVelocity = 0;
945 hData.continuousFlickVelocity = -hData.smoothVelocity.value();
946 vData.continuousFlickVelocity = -vData.smoothVelocity.value();
947 if (flickTime > 300) // slower flicking - reduce boost
948 flickBoost = qMax(1.0, flickBoost - 0.5);
952 hData.continuousFlickVelocity = 0;
953 vData.continuousFlickVelocity = 0;
956 q->setKeepMouseGrab(stealMouse);
958 if (hData.transitionToBounds)
959 hData.transitionToBounds->stopTransition();
960 if (vData.transitionToBounds)
961 vData.transitionToBounds->stopTransition();
963 resetTimeline(hData);
965 resetTimeline(vData);
969 hData.dragMinBound = q->minXExtent();
970 vData.dragMinBound = q->minYExtent();
971 hData.dragMaxBound = q->maxXExtent();
972 vData.dragMaxBound = q->maxYExtent();
975 pressPos = event->localPos();
976 hData.pressPos = hData.move.value();
977 vData.pressPos = vData.move.value();
978 bool wasFlicking = hData.flicking || vData.flicking;
979 if (hData.flicking) {
980 hData.flicking = false;
981 emit q->flickingHorizontallyChanged();
983 if (vData.flicking) {
984 vData.flicking = false;
985 emit q->flickingVerticallyChanged();
988 emit q->flickingChanged();
989 lastPosTime = lastPressTime = computeCurrentTime(event);
990 QQuickItemPrivate::start(vData.velocityTime);
991 QQuickItemPrivate::start(hData.velocityTime);
994 void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event)
996 Q_Q(QQuickFlickable);
997 if (!interactive || lastPosTime == -1)
999 bool rejectY = false;
1000 bool rejectX = false;
1002 bool stealY = stealMouse;
1003 bool stealX = stealMouse;
1005 bool prevHMoved = hMoved;
1006 bool prevVMoved = vMoved;
1008 qint64 elapsedSincePress = computeCurrentTime(event) - lastPressTime;
1010 qreal dy = event->localPos().y() - pressPos.y();
1011 bool overThreshold = QQuickWindowPrivate::dragOverThreshold(dy, Qt::YAxis, event);
1012 if (overThreshold || elapsedSincePress > 200) {
1014 vData.dragStartOffset = dy;
1015 qreal newY = dy + vData.pressPos - vData.dragStartOffset;
1016 const qreal minY = vData.dragMinBound;
1017 const qreal maxY = vData.dragMaxBound;
1019 newY = minY + (newY - minY) / 2;
1020 if (newY < maxY && maxY - minY <= 0)
1021 newY = maxY + (newY - maxY) / 2;
1022 if (boundsBehavior == QQuickFlickable::StopAtBounds && (newY > minY || newY < maxY)) {
1033 if (!rejectY && stealMouse && dy != 0.0) {
1035 vData.move.setValue(newY);
1044 qreal dx = event->localPos().x() - pressPos.x();
1045 bool overThreshold = QQuickWindowPrivate::dragOverThreshold(dx, Qt::XAxis, event);
1046 if (overThreshold || elapsedSincePress > 200) {
1048 hData.dragStartOffset = dx;
1049 qreal newX = dx + hData.pressPos - hData.dragStartOffset;
1050 const qreal minX = hData.dragMinBound;
1051 const qreal maxX = hData.dragMaxBound;
1053 newX = minX + (newX - minX) / 2;
1054 if (newX < maxX && maxX - minX <= 0)
1055 newX = maxX + (newX - maxX) / 2;
1056 if (boundsBehavior == QQuickFlickable::StopAtBounds && (newX > minX || newX < maxX)) {
1067 if (!rejectX && stealMouse && dx != 0.0) {
1069 hData.move.setValue(newX);
1078 stealMouse = stealX || stealY;
1080 q->setKeepMouseGrab(true);
1083 vData.velocityBuffer.clear();
1087 hData.velocityBuffer.clear();
1091 if ((hMoved && !prevHMoved) || (vMoved && !prevVMoved)) {
1093 q->movementStarting();
1096 qint64 currentTimestamp = computeCurrentTime(event);
1097 qreal elapsed = qreal(currentTimestamp - (lastPos.isNull() ? lastPressTime : lastPosTime)) / 1000.;
1100 lastPosTime = currentTimestamp;
1101 if (q->yflick() && !rejectY) {
1102 if (QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity) {
1103 vData.addVelocitySample(QGuiApplicationPrivate::mouseEventVelocity(event).y(), maxVelocity);
1105 qreal dy = event->localPos().y() - (lastPos.isNull() ? pressPos.y() : lastPos.y());
1106 vData.addVelocitySample(dy/elapsed, maxVelocity);
1109 if (q->xflick() && !rejectX) {
1110 if (QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity) {
1111 hData.addVelocitySample(QGuiApplicationPrivate::mouseEventVelocity(event).x(), maxVelocity);
1113 qreal dx = event->localPos().x() - (lastPos.isNull() ? pressPos.x() : lastPos.x());
1114 hData.addVelocitySample(dx/elapsed, maxVelocity);
1118 lastPos = event->localPos();
1121 void QQuickFlickablePrivate::handleMouseReleaseEvent(QMouseEvent *event)
1123 Q_Q(QQuickFlickable);
1125 q->setKeepMouseGrab(false);
1128 // if we drag then pause before release we should not cause a flick.
1129 qint64 elapsed = computeCurrentTime(event) - lastPosTime;
1131 vData.updateVelocity();
1132 hData.updateVelocity();
1136 if (lastPosTime == -1)
1139 hData.vTime = vData.vTime = timeline.time();
1141 bool canBoost = false;
1143 qreal vVelocity = 0;
1144 if (elapsed < 100 && vData.velocity != 0.) {
1145 vVelocity = (QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity)
1146 ? QGuiApplicationPrivate::mouseEventVelocity(event).y() : vData.velocity;
1148 if ((vData.atBeginning && vVelocity > 0.) || (vData.atEnd && vVelocity < 0.)) {
1150 } else if (vData.continuousFlickVelocity != 0.0
1151 && vData.viewSize/q->height() > QML_FLICK_MULTIFLICK_RATIO
1152 && ((vVelocity > 0) == (vData.continuousFlickVelocity > 0))
1153 && qAbs(vVelocity) > QML_FLICK_MULTIFLICK_THRESHOLD) {
1154 // accelerate flick for large view flicked quickly
1158 qreal hVelocity = 0;
1159 if (elapsed < 100 && hData.velocity != 0.) {
1160 hVelocity = (QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity)
1161 ? QGuiApplicationPrivate::mouseEventVelocity(event).x() : hData.velocity;
1163 if ((hData.atBeginning && hVelocity > 0.) || (hData.atEnd && hVelocity < 0.)) {
1165 } else if (hData.continuousFlickVelocity != 0.0
1166 && hData.viewSize/q->width() > QML_FLICK_MULTIFLICK_RATIO
1167 && ((hVelocity > 0) == (hData.continuousFlickVelocity > 0))
1168 && qAbs(hVelocity) > QML_FLICK_MULTIFLICK_THRESHOLD) {
1169 // accelerate flick for large view flicked quickly
1173 flickBoost = canBoost ? qBound(1.0, flickBoost+0.25, QML_FLICK_MULTIFLICK_MAXBOOST) : 1.0;
1175 bool flickedV = false;
1176 vVelocity *= flickBoost;
1177 if (q->yflick() && qAbs(vVelocity) > MinimumFlickVelocity && qAbs(event->localPos().y() - pressPos.y()) > FlickThreshold) {
1178 velocityTimeline.reset(vData.smoothVelocity);
1179 vData.smoothVelocity.setValue(-vVelocity);
1180 flickedV = flickY(vVelocity);
1185 bool flickedH = false;
1186 hVelocity *= flickBoost;
1187 if (q->xflick() && qAbs(hVelocity) > MinimumFlickVelocity && qAbs(event->localPos().x() - pressPos.x()) > FlickThreshold) {
1188 velocityTimeline.reset(hData.smoothVelocity);
1189 hData.smoothVelocity.setValue(-hVelocity);
1190 flickedH = flickX(hVelocity);
1195 flickingStarted(flickedH, flickedV);
1196 if (!isViewMoving())
1197 q->movementEnding();
1200 void QQuickFlickable::mousePressEvent(QMouseEvent *event)
1202 Q_D(QQuickFlickable);
1203 if (d->interactive) {
1205 d->handleMousePressEvent(event);
1208 QQuickItem::mousePressEvent(event);
1212 void QQuickFlickable::mouseMoveEvent(QMouseEvent *event)
1214 Q_D(QQuickFlickable);
1215 if (d->interactive) {
1216 d->handleMouseMoveEvent(event);
1219 QQuickItem::mouseMoveEvent(event);
1223 void QQuickFlickable::mouseReleaseEvent(QMouseEvent *event)
1225 Q_D(QQuickFlickable);
1226 if (d->interactive) {
1227 d->clearDelayedPress();
1228 d->handleMouseReleaseEvent(event);
1230 if (window() && window()->mouseGrabberItem() == this)
1233 QQuickItem::mouseReleaseEvent(event);
1237 void QQuickFlickable::wheelEvent(QWheelEvent *event)
1239 Q_D(QQuickFlickable);
1240 if (!d->interactive) {
1241 QQuickItem::wheelEvent(event);
1245 int yDelta = event->angleDelta().y();
1246 int xDelta = event->angleDelta().x();
1247 if (yflick() && yDelta != 0) {
1249 if (yDelta > 0 && contentY() > -minYExtent()) {
1250 d->vData.velocity = qMax(yDelta*2 - d->vData.smoothVelocity.value(), qreal(d->maxVelocity/4));
1252 } else if (yDelta < 0 && contentY() < -maxYExtent()) {
1253 d->vData.velocity = qMin(yDelta*2 - d->vData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
1257 d->vData.flicking = false;
1258 d->flickY(d->vData.velocity);
1259 d->flickingStarted(false, true);
1260 if (d->vData.flicking) {
1267 if (xflick() && xDelta != 0) {
1269 if (xDelta > 0 && contentX() > -minXExtent()) {
1270 d->hData.velocity = qMax(xDelta*2 - d->hData.smoothVelocity.value(), qreal(d->maxVelocity/4));
1272 } else if (xDelta < 0 && contentX() < -maxXExtent()) {
1273 d->hData.velocity = qMin(xDelta*2 - d->hData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
1277 d->hData.flicking = false;
1278 d->flickX(d->hData.velocity);
1279 d->flickingStarted(true, false);
1280 if (d->hData.flicking) {
1288 if (!event->isAccepted())
1289 QQuickItem::wheelEvent(event);
1292 bool QQuickFlickablePrivate::isOutermostPressDelay() const
1294 Q_Q(const QQuickFlickable);
1295 QQuickItem *item = q->parentItem();
1297 QQuickFlickable *flick = qobject_cast<QQuickFlickable*>(item);
1298 if (flick && flick->pressDelay() > 0 && flick->isInteractive())
1300 item = item->parentItem();
1306 void QQuickFlickablePrivate::captureDelayedPress(QMouseEvent *event)
1308 Q_Q(QQuickFlickable);
1309 if (!q->window() || pressDelay <= 0)
1311 if (!isOutermostPressDelay())
1313 delayedPressTarget = q->window()->mouseGrabberItem();
1314 delayedPressEvent = QQuickWindowPrivate::cloneMouseEvent(event);
1315 delayedPressEvent->setAccepted(false);
1316 delayedPressTimer.start(pressDelay, q);
1319 void QQuickFlickablePrivate::clearDelayedPress()
1321 if (delayedPressEvent) {
1322 delayedPressTimer.stop();
1323 delete delayedPressEvent;
1324 delayedPressEvent = 0;
1328 //XXX pixelAligned ignores the global position of the Flickable, i.e. assumes Flickable itself is pixel aligned.
1329 void QQuickFlickablePrivate::setViewportX(qreal x)
1331 contentItem->setX(pixelAligned ? qRound(x) : x);
1334 void QQuickFlickablePrivate::setViewportY(qreal y)
1336 contentItem->setY(pixelAligned ? qRound(y) : y);
1339 void QQuickFlickable::timerEvent(QTimerEvent *event)
1341 Q_D(QQuickFlickable);
1342 if (event->timerId() == d->delayedPressTimer.timerId()) {
1343 d->delayedPressTimer.stop();
1344 if (d->delayedPressEvent) {
1345 QQuickItem *grabber = window() ? window()->mouseGrabberItem() : 0;
1346 if (!grabber || grabber != this) {
1347 // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
1348 // so we reset the grabber
1349 if (window()->mouseGrabberItem() == d->delayedPressTarget)
1350 d->delayedPressTarget->ungrabMouse();
1351 // Use the event handler that will take care of finding the proper item to propagate the event
1352 QQuickWindowPrivate::get(window())->deliverMouseEvent(d->delayedPressEvent);
1354 delete d->delayedPressEvent;
1355 d->delayedPressEvent = 0;
1360 qreal QQuickFlickable::minYExtent() const
1362 Q_D(const QQuickFlickable);
1363 return d->vData.startMargin;
1366 qreal QQuickFlickable::minXExtent() const
1368 Q_D(const QQuickFlickable);
1369 return d->hData.startMargin;
1373 qreal QQuickFlickable::maxXExtent() const
1375 Q_D(const QQuickFlickable);
1376 return width() - vWidth() - d->hData.endMargin;
1379 qreal QQuickFlickable::maxYExtent() const
1381 Q_D(const QQuickFlickable);
1382 return height() - vHeight() - d->vData.endMargin;
1385 void QQuickFlickable::componentComplete()
1387 Q_D(QQuickFlickable);
1388 QQuickItem::componentComplete();
1389 if (!d->hData.explicitValue && d->hData.startMargin != 0.)
1390 setContentX(-minXExtent());
1391 if (!d->vData.explicitValue && d->vData.startMargin != 0.)
1392 setContentY(-minYExtent());
1395 void QQuickFlickable::viewportMoved(Qt::Orientations orient)
1397 Q_D(QQuickFlickable);
1398 if (orient & Qt::Vertical)
1399 d->viewportAxisMoved(d->vData, minYExtent(), maxYExtent(), height(), d->fixupY_callback);
1400 if (orient & Qt::Horizontal)
1401 d->viewportAxisMoved(d->hData, minXExtent(), maxXExtent(), width(), d->fixupX_callback);
1402 d->updateBeginningEnd();
1405 void QQuickFlickablePrivate::viewportAxisMoved(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1406 QQuickTimeLineCallback::Callback fixupCallback)
1408 if (pressed || calcVelocity) {
1409 int elapsed = QQuickItemPrivate::restart(data.velocityTime);
1411 qreal velocity = (data.lastPos - data.move.value()) * 1000 / elapsed;
1412 if (qAbs(velocity) > 0) {
1413 velocityTimeline.reset(data.smoothVelocity);
1415 velocityTimeline.set(data.smoothVelocity, velocity);
1417 velocityTimeline.move(data.smoothVelocity, velocity, reportedVelocitySmoothing);
1418 velocityTimeline.move(data.smoothVelocity, 0, reportedVelocitySmoothing);
1422 if (timeline.time() > data.vTime) {
1423 velocityTimeline.reset(data.smoothVelocity);
1424 qreal velocity = (data.lastPos - data.move.value()) * 1000 / (timeline.time() - data.vTime);
1425 data.smoothVelocity.setValue(velocity);
1429 if (!data.inOvershoot && !data.fixingUp && data.flicking
1430 && (data.move.value() > minExtent || data.move.value() < maxExtent)
1431 && qAbs(data.smoothVelocity.value()) > 10) {
1432 // Increase deceleration if we've passed a bound
1433 qreal overBound = data.move.value() > minExtent
1434 ? data.move.value() - minExtent
1435 : maxExtent - data.move.value();
1436 data.inOvershoot = true;
1437 qreal maxDistance = overShootDistance(vSize) - overBound;
1438 resetTimeline(data);
1439 if (maxDistance > 0)
1440 timeline.accel(data.move, -data.smoothVelocity.value(), deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance);
1441 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
1444 data.lastPos = data.move.value();
1445 data.vTime = timeline.time();
1448 void QQuickFlickable::geometryChanged(const QRectF &newGeometry,
1449 const QRectF &oldGeometry)
1451 Q_D(QQuickFlickable);
1452 QQuickItem::geometryChanged(newGeometry, oldGeometry);
1454 bool changed = false;
1455 if (newGeometry.width() != oldGeometry.width()) {
1458 if (d->hData.viewSize < 0) {
1459 d->contentItem->setWidth(width());
1460 emit contentWidthChanged();
1462 // Make sure that we're entirely in view.
1463 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1464 d->fixupMode = QQuickFlickablePrivate::Immediate;
1468 if (newGeometry.height() != oldGeometry.height()) {
1471 if (d->vData.viewSize < 0) {
1472 d->contentItem->setHeight(height());
1473 emit contentHeightChanged();
1475 // Make sure that we're entirely in view.
1476 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1477 d->fixupMode = QQuickFlickablePrivate::Immediate;
1483 d->updateBeginningEnd();
1487 \qmlmethod QtQuick2::Flickable::flick(qreal xVelocity, qreal yVelocity)
1489 Flicks the content with \a xVelocity horizontally and \a yVelocity vertically in pixels/sec.
1492 void QQuickFlickable::flick(qreal xVelocity, qreal yVelocity)
1494 Q_D(QQuickFlickable);
1495 bool flickedX = d->flickX(xVelocity);
1496 bool flickedY = d->flickY(yVelocity);
1497 d->flickingStarted(flickedX, flickedY);
1500 void QQuickFlickablePrivate::flickingStarted(bool flickingH, bool flickingV)
1502 Q_Q(QQuickFlickable);
1503 if (!flickingH && !flickingV)
1506 bool wasFlicking = hData.flicking || vData.flicking;
1507 if (flickingH && !hData.flicking) {
1508 hData.flicking = true;
1509 emit q->flickingHorizontallyChanged();
1511 if (flickingV && !vData.flicking) {
1512 vData.flicking = true;
1513 emit q->flickingVerticallyChanged();
1515 if (!wasFlicking && (hData.flicking || vData.flicking)) {
1516 emit q->flickingChanged();
1517 emit q->flickStarted();
1522 \qmlmethod QtQuick2::Flickable::cancelFlick()
1524 Cancels the current flick animation.
1527 void QQuickFlickable::cancelFlick()
1529 Q_D(QQuickFlickable);
1530 d->resetTimeline(d->hData);
1531 d->resetTimeline(d->vData);
1535 void QQuickFlickablePrivate::data_append(QQmlListProperty<QObject> *prop, QObject *o)
1537 if (QQuickItem *i = qmlobject_cast<QQuickItem *>(o)) {
1538 i->setParentItem(static_cast<QQuickFlickablePrivate*>(prop->data)->contentItem);
1540 o->setParent(prop->object); // XXX todo - do we want this?
1544 int QQuickFlickablePrivate::data_count(QQmlListProperty<QObject> *)
1550 QObject *QQuickFlickablePrivate::data_at(QQmlListProperty<QObject> *, int)
1556 void QQuickFlickablePrivate::data_clear(QQmlListProperty<QObject> *)
1561 QQmlListProperty<QObject> QQuickFlickable::flickableData()
1563 Q_D(QQuickFlickable);
1564 return QQmlListProperty<QObject>(this, (void *)d, QQuickFlickablePrivate::data_append,
1565 QQuickFlickablePrivate::data_count,
1566 QQuickFlickablePrivate::data_at,
1567 QQuickFlickablePrivate::data_clear);
1570 QQmlListProperty<QQuickItem> QQuickFlickable::flickableChildren()
1572 Q_D(QQuickFlickable);
1573 return QQuickItemPrivate::get(d->contentItem)->children();
1577 \qmlproperty enumeration QtQuick2::Flickable::boundsBehavior
1578 This property holds whether the surface may be dragged
1579 beyond the Flickable's boundaries, or overshoot the
1580 Flickable's boundaries when flicked.
1582 This enables the feeling that the edges of the view are soft,
1583 rather than a hard physical boundary.
1585 The \c boundsBehavior can be one of:
1588 \li Flickable.StopAtBounds - the contents can not be dragged beyond the boundary
1589 of the flickable, and flicks will not overshoot.
1590 \li Flickable.DragOverBounds - the contents can be dragged beyond the boundary
1591 of the Flickable, but flicks will not overshoot.
1592 \li Flickable.DragAndOvershootBounds (default) - the contents can be dragged
1593 beyond the boundary of the Flickable, and can overshoot the
1594 boundary when flicked.
1597 QQuickFlickable::BoundsBehavior QQuickFlickable::boundsBehavior() const
1599 Q_D(const QQuickFlickable);
1600 return d->boundsBehavior;
1603 void QQuickFlickable::setBoundsBehavior(BoundsBehavior b)
1605 Q_D(QQuickFlickable);
1606 if (b == d->boundsBehavior)
1608 d->boundsBehavior = b;
1609 emit boundsBehaviorChanged();
1613 \qmlproperty Transition QtQuick2::Flickable::rebound
1615 This holds the transition to be applied to the content view when
1616 it snaps back to the bounds of the flickable. The transition is
1617 triggered when the view is flicked or dragged past the edge of the
1618 content area, or when returnToBounds() is called.
1624 width: 150; height: 150
1625 contentWidth: 300; contentHeight: 300
1627 rebound: Transition {
1631 easing.type: Easing.OutBounce
1636 width: 300; height: 300
1637 gradient: Gradient {
1638 GradientStop { position: 0.0; color: "lightsteelblue" }
1639 GradientStop { position: 1.0; color: "blue" }
1645 When the above view is flicked beyond its bounds, it will return to its
1646 bounds using the transition specified:
1648 \image flickable-rebound.gif
1650 If this property is not set, a default animation is applied.
1652 QQuickTransition *QQuickFlickable::rebound() const
1654 Q_D(const QQuickFlickable);
1658 void QQuickFlickable::setRebound(QQuickTransition *transition)
1660 Q_D(QQuickFlickable);
1662 if (!d->hData.transitionToBounds)
1663 d->hData.transitionToBounds = new QQuickFlickableReboundTransition(this, QLatin1String("x"));
1664 if (!d->vData.transitionToBounds)
1665 d->vData.transitionToBounds = new QQuickFlickableReboundTransition(this, QLatin1String("y"));
1667 if (d->rebound != transition) {
1668 d->rebound = transition;
1669 emit reboundChanged();
1674 \qmlproperty real QtQuick2::Flickable::contentWidth
1675 \qmlproperty real QtQuick2::Flickable::contentHeight
1677 The dimensions of the content (the surface controlled by Flickable).
1678 This should typically be set to the combined size of the items placed in the
1681 The following snippet shows how these properties are used to display
1682 an image that is larger than the Flickable item itself:
1684 \snippet qml/flickable.qml document
1686 In some cases, the content dimensions can be automatically set
1687 based on the \l {Item::childrenRect.width}{childrenRect.width}
1688 and \l {Item::childrenRect.height}{childrenRect.height} properties
1689 of the \l contentItem. For example, the previous snippet could be rewritten with:
1692 contentWidth: contentItem.childrenRect.width; contentHeight: contentItem.childrenRect.height
1695 Though this assumes that the origin of the childrenRect is 0,0.
1697 qreal QQuickFlickable::contentWidth() const
1699 Q_D(const QQuickFlickable);
1700 return d->hData.viewSize;
1703 void QQuickFlickable::setContentWidth(qreal w)
1705 Q_D(QQuickFlickable);
1706 if (d->hData.viewSize == w)
1708 d->hData.viewSize = w;
1710 d->contentItem->setWidth(width());
1712 d->contentItem->setWidth(w);
1713 d->hData.markExtentsDirty();
1714 // Make sure that we're entirely in view.
1715 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1716 d->fixupMode = QQuickFlickablePrivate::Immediate;
1718 } else if (!d->pressed && d->hData.fixingUp) {
1719 d->fixupMode = QQuickFlickablePrivate::ExtentChanged;
1722 emit contentWidthChanged();
1723 d->updateBeginningEnd();
1726 qreal QQuickFlickable::contentHeight() const
1728 Q_D(const QQuickFlickable);
1729 return d->vData.viewSize;
1732 void QQuickFlickable::setContentHeight(qreal h)
1734 Q_D(QQuickFlickable);
1735 if (d->vData.viewSize == h)
1737 d->vData.viewSize = h;
1739 d->contentItem->setHeight(height());
1741 d->contentItem->setHeight(h);
1742 d->vData.markExtentsDirty();
1743 // Make sure that we're entirely in view.
1744 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1745 d->fixupMode = QQuickFlickablePrivate::Immediate;
1747 } else if (!d->pressed && d->vData.fixingUp) {
1748 d->fixupMode = QQuickFlickablePrivate::ExtentChanged;
1751 emit contentHeightChanged();
1752 d->updateBeginningEnd();
1756 \qmlproperty real QtQuick2::Flickable::topMargin
1757 \qmlproperty real QtQuick2::Flickable::leftMargin
1758 \qmlproperty real QtQuick2::Flickable::bottomMargin
1759 \qmlproperty real QtQuick2::Flickable::rightMargin
1761 These properties hold the margins around the content. This space is reserved
1762 in addition to the contentWidth and contentHeight.
1766 qreal QQuickFlickable::topMargin() const
1768 Q_D(const QQuickFlickable);
1769 return d->vData.startMargin;
1772 void QQuickFlickable::setTopMargin(qreal m)
1774 Q_D(QQuickFlickable);
1775 if (d->vData.startMargin == m)
1777 d->vData.startMargin = m;
1778 d->vData.markExtentsDirty();
1779 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1780 d->fixupMode = QQuickFlickablePrivate::Immediate;
1783 emit topMarginChanged();
1784 d->updateBeginningEnd();
1787 qreal QQuickFlickable::bottomMargin() const
1789 Q_D(const QQuickFlickable);
1790 return d->vData.endMargin;
1793 void QQuickFlickable::setBottomMargin(qreal m)
1795 Q_D(QQuickFlickable);
1796 if (d->vData.endMargin == m)
1798 d->vData.endMargin = m;
1799 d->vData.markExtentsDirty();
1800 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1801 d->fixupMode = QQuickFlickablePrivate::Immediate;
1804 emit bottomMarginChanged();
1805 d->updateBeginningEnd();
1808 qreal QQuickFlickable::leftMargin() const
1810 Q_D(const QQuickFlickable);
1811 return d->hData.startMargin;
1814 void QQuickFlickable::setLeftMargin(qreal m)
1816 Q_D(QQuickFlickable);
1817 if (d->hData.startMargin == m)
1819 d->hData.startMargin = m;
1820 d->hData.markExtentsDirty();
1821 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1822 d->fixupMode = QQuickFlickablePrivate::Immediate;
1825 emit leftMarginChanged();
1826 d->updateBeginningEnd();
1829 qreal QQuickFlickable::rightMargin() const
1831 Q_D(const QQuickFlickable);
1832 return d->hData.endMargin;
1835 void QQuickFlickable::setRightMargin(qreal m)
1837 Q_D(QQuickFlickable);
1838 if (d->hData.endMargin == m)
1840 d->hData.endMargin = m;
1841 d->hData.markExtentsDirty();
1842 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1843 d->fixupMode = QQuickFlickablePrivate::Immediate;
1846 emit rightMarginChanged();
1847 d->updateBeginningEnd();
1851 \qmlproperty real QtQuick2::Flickable::originX
1852 \qmlproperty real QtQuick2::Flickable::originY
1854 These properties hold the origin of the content. This value always refers
1855 to the top-left position of the content regardless of layout direction.
1857 This is usually (0,0), however ListView and GridView may have an arbitrary
1858 origin due to delegate size variation, or item insertion/removal outside
1862 qreal QQuickFlickable::originY() const
1864 Q_D(const QQuickFlickable);
1865 return -minYExtent() + d->vData.startMargin;
1868 qreal QQuickFlickable::originX() const
1870 Q_D(const QQuickFlickable);
1871 return -minXExtent() + d->hData.startMargin;
1876 \qmlmethod QtQuick2::Flickable::resizeContent(real width, real height, QPointF center)
1878 Resizes the content to \a width x \a height about \a center.
1880 This does not scale the contents of the Flickable - it only resizes the \l contentWidth
1881 and \l contentHeight.
1883 Resizing the content may result in the content being positioned outside
1884 the bounds of the Flickable. Calling \l returnToBounds() will
1885 move the content back within legal bounds.
1887 void QQuickFlickable::resizeContent(qreal w, qreal h, QPointF center)
1889 Q_D(QQuickFlickable);
1890 if (w != d->hData.viewSize) {
1891 qreal oldSize = d->hData.viewSize;
1892 d->hData.viewSize = w;
1893 d->contentItem->setWidth(w);
1894 emit contentWidthChanged();
1895 if (center.x() != 0) {
1896 qreal pos = center.x() * w / oldSize;
1897 setContentX(contentX() + pos - center.x());
1900 if (h != d->vData.viewSize) {
1901 qreal oldSize = d->vData.viewSize;
1902 d->vData.viewSize = h;
1903 d->contentItem->setHeight(h);
1904 emit contentHeightChanged();
1905 if (center.y() != 0) {
1906 qreal pos = center.y() * h / oldSize;
1907 setContentY(contentY() + pos - center.y());
1910 d->updateBeginningEnd();
1914 \qmlmethod QtQuick2::Flickable::returnToBounds()
1916 Ensures the content is within legal bounds.
1918 This may be called to ensure that the content is within legal bounds
1919 after manually positioning the content.
1921 void QQuickFlickable::returnToBounds()
1923 Q_D(QQuickFlickable);
1928 qreal QQuickFlickable::vWidth() const
1930 Q_D(const QQuickFlickable);
1931 if (d->hData.viewSize < 0)
1934 return d->hData.viewSize;
1937 qreal QQuickFlickable::vHeight() const
1939 Q_D(const QQuickFlickable);
1940 if (d->vData.viewSize < 0)
1943 return d->vData.viewSize;
1946 bool QQuickFlickable::xflick() const
1948 Q_D(const QQuickFlickable);
1949 if (d->flickableDirection == QQuickFlickable::AutoFlickDirection)
1950 return floor(qAbs(vWidth() - width()));
1951 return d->flickableDirection & QQuickFlickable::HorizontalFlick;
1954 bool QQuickFlickable::yflick() const
1956 Q_D(const QQuickFlickable);
1957 if (d->flickableDirection == QQuickFlickable::AutoFlickDirection)
1958 return floor(qAbs(vHeight() - height()));
1959 return d->flickableDirection & QQuickFlickable::VerticalFlick;
1962 void QQuickFlickable::mouseUngrabEvent()
1964 Q_D(QQuickFlickable);
1966 // if our mouse grab has been removed (probably by another Flickable),
1968 d->clearDelayedPress();
1970 d->draggingEnding();
1971 d->stealMouse = false;
1972 setKeepMouseGrab(false);
1975 if (!d->isViewMoving())
1980 bool QQuickFlickable::sendMouseEvent(QMouseEvent *event)
1982 Q_D(QQuickFlickable);
1983 QPointF localPos = mapFromScene(event->windowPos());
1985 QQuickWindow *c = window();
1986 QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
1987 bool grabberDisabled = grabber && !grabber->isEnabled();
1988 bool stealThisEvent = d->stealMouse;
1989 if ((stealThisEvent || contains(localPos)) && (!grabber || !grabber->keepMouseGrab() || grabberDisabled)) {
1990 QScopedPointer<QMouseEvent> mouseEvent(QQuickWindowPrivate::cloneMouseEvent(event, &localPos));
1991 mouseEvent->setAccepted(false);
1993 switch (mouseEvent->type()) {
1994 case QEvent::MouseMove:
1995 d->handleMouseMoveEvent(mouseEvent.data());
1997 case QEvent::MouseButtonPress:
1998 if (d->pressed) // we are already pressed - this is a delayed replay
2001 d->handleMousePressEvent(mouseEvent.data());
2002 d->captureDelayedPress(event);
2003 stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
2005 case QEvent::MouseButtonRelease:
2006 if (d->delayedPressEvent) {
2007 // We replay the mouse press but the grabber we had might not be interested in the event (e.g. overlay)
2008 // so we reset the grabber
2009 if (c->mouseGrabberItem() == d->delayedPressTarget)
2010 d->delayedPressTarget->ungrabMouse();
2011 // Use the event handler that will take care of finding the proper item to propagate the event
2012 QQuickWindowPrivate::get(window())->deliverMouseEvent(d->delayedPressEvent);
2013 d->clearDelayedPress();
2014 // We send the release
2015 window()->sendEvent(c->mouseGrabberItem(), event);
2016 // And the event has been consumed
2017 d->stealMouse = false;
2021 d->handleMouseReleaseEvent(mouseEvent.data());
2026 grabber = qobject_cast<QQuickItem*>(c->mouseGrabberItem());
2027 if ((grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) || grabberDisabled) {
2028 d->clearDelayedPress();
2032 return stealThisEvent || d->delayedPressEvent || grabberDisabled;
2033 } else if (d->lastPosTime != -1) {
2034 d->lastPosTime = -1;
2037 if (event->type() == QEvent::MouseButtonRelease) {
2038 d->lastPosTime = -1;
2039 d->clearDelayedPress();
2040 d->stealMouse = false;
2047 bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e)
2049 Q_D(QQuickFlickable);
2050 if (!isVisible() || !isEnabled())
2051 return QQuickItem::childMouseEventFilter(i, e);
2052 switch (e->type()) {
2053 case QEvent::MouseButtonPress:
2054 case QEvent::MouseMove:
2055 case QEvent::MouseButtonRelease:
2056 return sendMouseEvent(static_cast<QMouseEvent *>(e));
2057 case QEvent::UngrabMouse:
2058 if (d->window && d->window->mouseGrabberItem() && d->window->mouseGrabberItem() != this) {
2059 // The grab has been taken away from a child and given to some other item.
2067 return QQuickItem::childMouseEventFilter(i, e);
2071 \qmlproperty real QtQuick2::Flickable::maximumFlickVelocity
2072 This property holds the maximum velocity that the user can flick the view in pixels/second.
2074 The default value is platform dependent.
2076 qreal QQuickFlickable::maximumFlickVelocity() const
2078 Q_D(const QQuickFlickable);
2079 return d->maxVelocity;
2082 void QQuickFlickable::setMaximumFlickVelocity(qreal v)
2084 Q_D(QQuickFlickable);
2085 if (v == d->maxVelocity)
2088 emit maximumFlickVelocityChanged();
2092 \qmlproperty real QtQuick2::Flickable::flickDeceleration
2093 This property holds the rate at which a flick will decelerate.
2095 The default value is platform dependent.
2097 qreal QQuickFlickable::flickDeceleration() const
2099 Q_D(const QQuickFlickable);
2100 return d->deceleration;
2103 void QQuickFlickable::setFlickDeceleration(qreal deceleration)
2105 Q_D(QQuickFlickable);
2106 if (deceleration == d->deceleration)
2108 d->deceleration = deceleration;
2109 emit flickDecelerationChanged();
2112 bool QQuickFlickable::isFlicking() const
2114 Q_D(const QQuickFlickable);
2115 return d->hData.flicking || d->vData.flicking;
2119 \qmlproperty bool QtQuick2::Flickable::flicking
2120 \qmlproperty bool QtQuick2::Flickable::flickingHorizontally
2121 \qmlproperty bool QtQuick2::Flickable::flickingVertically
2123 These properties describe whether the view is currently moving horizontally,
2124 vertically or in either direction, due to the user flicking the view.
2126 bool QQuickFlickable::isFlickingHorizontally() const
2128 Q_D(const QQuickFlickable);
2129 return d->hData.flicking;
2132 bool QQuickFlickable::isFlickingVertically() const
2134 Q_D(const QQuickFlickable);
2135 return d->vData.flicking;
2139 \qmlproperty bool QtQuick2::Flickable::dragging
2140 \qmlproperty bool QtQuick2::Flickable::draggingHorizontally
2141 \qmlproperty bool QtQuick2::Flickable::draggingVertically
2143 These properties describe whether the view is currently moving horizontally,
2144 vertically or in either direction, due to the user dragging the view.
2146 bool QQuickFlickable::isDragging() const
2148 Q_D(const QQuickFlickable);
2149 return d->hData.dragging || d->vData.dragging;
2152 bool QQuickFlickable::isDraggingHorizontally() const
2154 Q_D(const QQuickFlickable);
2155 return d->hData.dragging;
2158 bool QQuickFlickable::isDraggingVertically() const
2160 Q_D(const QQuickFlickable);
2161 return d->vData.dragging;
2164 void QQuickFlickablePrivate::draggingStarting()
2166 Q_Q(QQuickFlickable);
2167 bool wasDragging = hData.dragging || vData.dragging;
2168 if (hMoved && !hData.dragging) {
2169 hData.dragging = true;
2170 emit q->draggingHorizontallyChanged();
2172 if (vMoved && !vData.dragging) {
2173 vData.dragging = true;
2174 emit q->draggingVerticallyChanged();
2176 if (!wasDragging && (hData.dragging || vData.dragging)) {
2177 emit q->draggingChanged();
2178 emit q->dragStarted();
2182 void QQuickFlickablePrivate::draggingEnding()
2184 Q_Q(QQuickFlickable);
2185 bool wasDragging = hData.dragging || vData.dragging;
2186 if (hData.dragging) {
2187 hData.dragging = false;
2188 emit q->draggingHorizontallyChanged();
2190 if (vData.dragging) {
2191 vData.dragging = false;
2192 emit q->draggingVerticallyChanged();
2194 if (wasDragging && !hData.dragging && !vData.dragging) {
2195 emit q->draggingChanged();
2196 emit q->dragEnded();
2200 bool QQuickFlickablePrivate::isViewMoving() const
2202 if (timeline.isActive()
2203 || (hData.transitionToBounds && hData.transitionToBounds->isActive())
2204 || (vData.transitionToBounds && vData.transitionToBounds->isActive()) ) {
2211 \qmlproperty int QtQuick2::Flickable::pressDelay
2213 This property holds the time to delay (ms) delivering a press to
2214 children of the Flickable. This can be useful where reacting
2215 to a press before a flicking action has undesirable effects.
2217 If the flickable is dragged/flicked before the delay times out
2218 the press event will not be delivered. If the button is released
2219 within the timeout, both the press and release will be delivered.
2221 Note that for nested Flickables with pressDelay set, the pressDelay of
2222 inner Flickables is overridden by the outermost Flickable.
2224 int QQuickFlickable::pressDelay() const
2226 Q_D(const QQuickFlickable);
2227 return d->pressDelay;
2230 void QQuickFlickable::setPressDelay(int delay)
2232 Q_D(QQuickFlickable);
2233 if (d->pressDelay == delay)
2235 d->pressDelay = delay;
2236 emit pressDelayChanged();
2240 \qmlproperty bool QtQuick2::Flickable::moving
2241 \qmlproperty bool QtQuick2::Flickable::movingHorizontally
2242 \qmlproperty bool QtQuick2::Flickable::movingVertically
2244 These properties describe whether the view is currently moving horizontally,
2245 vertically or in either direction, due to the user either dragging or
2249 bool QQuickFlickable::isMoving() const
2251 Q_D(const QQuickFlickable);
2252 return d->hData.moving || d->vData.moving;
2255 bool QQuickFlickable::isMovingHorizontally() const
2257 Q_D(const QQuickFlickable);
2258 return d->hData.moving;
2261 bool QQuickFlickable::isMovingVertically() const
2263 Q_D(const QQuickFlickable);
2264 return d->vData.moving;
2267 void QQuickFlickable::timelineCompleted()
2269 Q_D(QQuickFlickable);
2270 if ( (d->hData.transitionToBounds && d->hData.transitionToBounds->isActive())
2271 || (d->vData.transitionToBounds && d->vData.transitionToBounds->isActive()) ) {
2277 void QQuickFlickable::movementStarting()
2279 Q_D(QQuickFlickable);
2280 bool wasMoving = d->hData.moving || d->vData.moving;
2281 if (d->hMoved && !d->hData.moving) {
2282 d->hData.moving = true;
2283 emit movingHorizontallyChanged();
2285 if (d->vMoved && !d->vData.moving) {
2286 d->vData.moving = true;
2287 emit movingVerticallyChanged();
2290 if (!wasMoving && (d->hData.moving || d->vData.moving)) {
2291 emit movingChanged();
2292 emit movementStarted();
2296 void QQuickFlickable::movementEnding()
2298 movementEnding(true, true);
2301 void QQuickFlickable::movementEnding(bool hMovementEnding, bool vMovementEnding)
2303 Q_D(QQuickFlickable);
2305 // emit flicking signals
2306 bool wasFlicking = d->hData.flicking || d->vData.flicking;
2307 if (hMovementEnding && d->hData.flicking) {
2308 d->hData.flicking = false;
2309 emit flickingHorizontallyChanged();
2311 if (vMovementEnding && d->vData.flicking) {
2312 d->vData.flicking = false;
2313 emit flickingVerticallyChanged();
2315 if (wasFlicking && (!d->hData.flicking || !d->vData.flicking)) {
2316 emit flickingChanged();
2320 // emit moving signals
2321 bool wasMoving = d->hData.moving || d->vData.moving;
2322 if (hMovementEnding && d->hData.moving
2323 && (!d->pressed && !d->stealMouse)) {
2324 d->hData.moving = false;
2326 emit movingHorizontallyChanged();
2328 if (vMovementEnding && d->vData.moving
2329 && (!d->pressed && !d->stealMouse)) {
2330 d->vData.moving = false;
2332 emit movingVerticallyChanged();
2334 if (wasMoving && (!d->hData.moving || !d->vData.moving)) {
2335 emit movingChanged();
2336 emit movementEnded();
2339 if (hMovementEnding) {
2340 d->hData.fixingUp = false;
2341 d->hData.smoothVelocity.setValue(0);
2343 if (vMovementEnding) {
2344 d->vData.fixingUp = false;
2345 d->vData.smoothVelocity.setValue(0);
2349 void QQuickFlickablePrivate::updateVelocity()
2351 Q_Q(QQuickFlickable);
2352 emit q->horizontalVelocityChanged();
2353 emit q->verticalVelocityChanged();