1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquickflickable_p.h"
43 #include "qquickflickable_p_p.h"
44 #include "qquickcanvas.h"
45 #include "qquickcanvas_p.h"
47 #include <QtDeclarative/qdeclarativeinfo.h>
48 #include <QtGui/qevent.h>
49 #include <QtGui/qguiapplication.h>
50 #include <QtGui/qstylehints.h>
51 #include "qplatformdefs.h"
55 // The maximum number of pixels a flick can overshoot
56 #ifndef QML_FLICK_OVERSHOOT
57 #define QML_FLICK_OVERSHOOT 200
60 // The number of samples to use in calculating the velocity of a flick
61 #ifndef QML_FLICK_SAMPLEBUFFER
62 #define QML_FLICK_SAMPLEBUFFER 3
65 // The number of samples to discard when calculating the flick velocity.
66 // Touch panels often produce inaccurate results as the finger is lifted.
67 #ifndef QML_FLICK_DISCARDSAMPLES
68 #define QML_FLICK_DISCARDSAMPLES 1
71 // The default maximum velocity of a flick.
72 #ifndef QML_FLICK_DEFAULTMAXVELOCITY
73 #define QML_FLICK_DEFAULTMAXVELOCITY 2500
76 // The default deceleration of a flick.
77 #ifndef QML_FLICK_DEFAULTDECELERATION
78 #define QML_FLICK_DEFAULTDECELERATION 1500
81 // How much faster to decelerate when overshooting
82 #ifndef QML_FLICK_OVERSHOOTFRICTION
83 #define QML_FLICK_OVERSHOOTFRICTION 8
86 // FlickThreshold determines how far the "mouse" must have moved
87 // before we perform a flick.
88 static const int FlickThreshold = 20;
90 // RetainGrabVelocity is the maxmimum instantaneous velocity that
91 // will ensure the Flickable retains the grab on consecutive flicks.
92 static const int RetainGrabVelocity = 15;
94 QQuickFlickableVisibleArea::QQuickFlickableVisibleArea(QQuickFlickable *parent)
95 : QObject(parent), flickable(parent), m_xPosition(0.), m_widthRatio(0.)
96 , m_yPosition(0.), m_heightRatio(0.)
100 qreal QQuickFlickableVisibleArea::widthRatio() const
105 qreal QQuickFlickableVisibleArea::xPosition() const
110 qreal QQuickFlickableVisibleArea::heightRatio() const
112 return m_heightRatio;
115 qreal QQuickFlickableVisibleArea::yPosition() const
120 void QQuickFlickableVisibleArea::updateVisible()
122 QQuickFlickablePrivate *p = QQuickFlickablePrivate::get(flickable);
124 bool changeX = false;
125 bool changeY = false;
126 bool changeWidth = false;
127 bool changeHeight = false;
130 const qreal viewheight = flickable->height();
131 const qreal maxyextent = -flickable->maxYExtent() + flickable->minYExtent();
132 qreal pagePos = (-p->vData.move.value() + flickable->minYExtent()) / (maxyextent + viewheight);
133 qreal pageSize = viewheight / (maxyextent + viewheight);
135 if (pageSize != m_heightRatio) {
136 m_heightRatio = pageSize;
139 if (pagePos != m_yPosition) {
140 m_yPosition = pagePos;
145 const qreal viewwidth = flickable->width();
146 const qreal maxxextent = -flickable->maxXExtent() + flickable->minXExtent();
147 pagePos = (-p->hData.move.value() + flickable->minXExtent()) / (maxxextent + viewwidth);
148 pageSize = viewwidth / (maxxextent + viewwidth);
150 if (pageSize != m_widthRatio) {
151 m_widthRatio = pageSize;
154 if (pagePos != m_xPosition) {
155 m_xPosition = pagePos;
160 emit xPositionChanged(m_xPosition);
162 emit yPositionChanged(m_yPosition);
164 emit widthRatioChanged(m_widthRatio);
166 emit heightRatioChanged(m_heightRatio);
170 QQuickFlickablePrivate::QQuickFlickablePrivate()
171 : contentItem(new QQuickItem)
172 , hData(this, &QQuickFlickablePrivate::setViewportX)
173 , vData(this, &QQuickFlickablePrivate::setViewportY)
174 , hMoved(false), vMoved(false)
175 , stealMouse(false), pressed(false), interactive(true), calcVelocity(false)
176 , pixelAligned(false)
179 , deceleration(QML_FLICK_DEFAULTDECELERATION)
180 , maxVelocity(QML_FLICK_DEFAULTMAXVELOCITY), reportedVelocitySmoothing(100)
181 , delayedPressEvent(0), delayedPressTarget(0), pressDelay(0), fixupDuration(400)
182 , fixupMode(Normal), vTime(0), visibleArea(0)
183 , flickableDirection(QQuickFlickable::AutoFlickDirection)
184 , boundsBehavior(QQuickFlickable::DragAndOvershootBounds)
188 void QQuickFlickablePrivate::init()
190 Q_Q(QQuickFlickable);
191 QDeclarative_setParent_noEvent(contentItem, q);
192 contentItem->setParentItem(q);
193 FAST_CONNECT(&timeline, SIGNAL(completed()), q, SLOT(movementEnding()))
194 q->setAcceptedMouseButtons(Qt::LeftButton);
195 q->setFiltersChildMouseEvents(true);
196 QQuickItemPrivate *viewportPrivate = QQuickItemPrivate::get(contentItem);
197 viewportPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
201 Returns the amount to overshoot by given a velocity.
202 Will be roughly in range 0 - size/4
204 qreal QQuickFlickablePrivate::overShootDistance(qreal size)
206 if (maxVelocity <= 0)
209 return qMin(qreal(QML_FLICK_OVERSHOOT), size/3);
212 void QQuickFlickablePrivate::AxisData::addVelocitySample(qreal v, qreal maxVelocity)
216 else if (v < -maxVelocity)
218 velocityBuffer.append(v);
219 if (velocityBuffer.count() > QML_FLICK_SAMPLEBUFFER)
220 velocityBuffer.remove(0);
223 void QQuickFlickablePrivate::AxisData::updateVelocity()
226 if (velocityBuffer.count() > QML_FLICK_DISCARDSAMPLES) {
227 int count = velocityBuffer.count()-QML_FLICK_DISCARDSAMPLES;
228 for (int i = 0; i < count; ++i) {
229 qreal v = velocityBuffer.at(i);
236 void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeom, const QRectF &oldGeom)
238 Q_Q(QQuickFlickable);
239 if (item == contentItem) {
240 bool xChanged = newGeom.x() != oldGeom.x();
241 bool yChanged = newGeom.y() != oldGeom.y();
242 if (xChanged || yChanged)
245 emit q->contentXChanged();
247 emit q->contentYChanged();
251 void QQuickFlickablePrivate::flickX(qreal velocity)
253 Q_Q(QQuickFlickable);
254 flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, velocity);
257 void QQuickFlickablePrivate::flickY(qreal velocity)
259 Q_Q(QQuickFlickable);
260 flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, velocity);
263 void QQuickFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal,
264 QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
266 Q_Q(QQuickFlickable);
267 qreal maxDistance = -1;
268 data.fixingUp = false;
269 // -ve velocity means list is moving up
271 maxDistance = qAbs(minExtent - data.move.value());
272 data.flickTarget = minExtent;
274 maxDistance = qAbs(maxExtent - data.move.value());
275 data.flickTarget = maxExtent;
277 if (maxDistance > 0) {
279 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
285 timeline.reset(data.move);
286 if (boundsBehavior == QQuickFlickable::DragAndOvershootBounds)
287 timeline.accel(data.move, v, deceleration);
289 timeline.accel(data.move, v, deceleration, maxDistance);
290 timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
291 if (!hData.flicking && q->xflick()) {
292 hData.flicking = true;
293 emit q->flickingChanged();
294 emit q->flickingHorizontallyChanged();
296 emit q->flickStarted();
298 if (!vData.flicking && q->yflick()) {
299 vData.flicking = true;
300 emit q->flickingChanged();
301 emit q->flickingVerticallyChanged();
303 emit q->flickStarted();
306 timeline.reset(data.move);
307 fixup(data, minExtent, maxExtent);
311 void QQuickFlickablePrivate::fixupY_callback(void *data)
313 ((QQuickFlickablePrivate *)data)->fixupY();
316 void QQuickFlickablePrivate::fixupX_callback(void *data)
318 ((QQuickFlickablePrivate *)data)->fixupX();
321 void QQuickFlickablePrivate::fixupX()
323 Q_Q(QQuickFlickable);
324 fixup(hData, q->minXExtent(), q->maxXExtent());
327 void QQuickFlickablePrivate::fixupY()
329 Q_Q(QQuickFlickable);
330 fixup(vData, q->minYExtent(), q->maxYExtent());
333 void QQuickFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
335 if (data.move.value() > minExtent || maxExtent > minExtent) {
336 timeline.reset(data.move);
337 if (data.move.value() != minExtent) {
340 timeline.set(data.move, minExtent);
343 // The target has changed. Don't start from the beginning; just complete the
344 // second half of the animation using the new extent.
345 timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
346 data.fixingUp = true;
349 qreal dist = minExtent - data.move;
350 timeline.move(data.move, minExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4);
351 timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
352 data.fixingUp = true;
356 } else if (data.move.value() < maxExtent) {
357 timeline.reset(data.move);
360 timeline.set(data.move, maxExtent);
363 // The target has changed. Don't start from the beginning; just complete the
364 // second half of the animation using the new extent.
365 timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
366 data.fixingUp = true;
369 qreal dist = maxExtent - data.move;
370 timeline.move(data.move, maxExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4);
371 timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
372 data.fixingUp = true;
376 data.inOvershoot = false;
378 vTime = timeline.time();
381 void QQuickFlickablePrivate::updateBeginningEnd()
383 Q_Q(QQuickFlickable);
384 bool atBoundaryChange = false;
387 const int maxyextent = int(-q->maxYExtent());
388 const qreal ypos = -vData.move.value();
389 bool atBeginning = (ypos <= -q->minYExtent());
390 bool atEnd = (maxyextent <= ypos);
392 if (atBeginning != vData.atBeginning) {
393 vData.atBeginning = atBeginning;
394 atBoundaryChange = true;
396 if (atEnd != vData.atEnd) {
398 atBoundaryChange = true;
402 const int maxxextent = int(-q->maxXExtent());
403 const qreal xpos = -hData.move.value();
404 atBeginning = (xpos <= -q->minXExtent());
405 atEnd = (maxxextent <= xpos);
407 if (atBeginning != hData.atBeginning) {
408 hData.atBeginning = atBeginning;
409 atBoundaryChange = true;
411 if (atEnd != hData.atEnd) {
413 atBoundaryChange = true;
416 if (vData.extentsChanged) {
417 vData.extentsChanged = false;
418 emit q->yOriginChanged();
421 if (hData.extentsChanged) {
422 hData.extentsChanged = false;
423 emit q->xOriginChanged();
426 if (atBoundaryChange)
427 emit q->isAtBoundaryChanged();
430 visibleArea->updateVisible();
434 XXXTODO add docs describing moving, dragging, flicking properties, e.g.
436 When the user starts dragging the Flickable, the dragging and moving properties
439 If the velocity is sufficient when the drag is ended, flicking may begin.
441 The moving properties will remain true until all dragging and flicking
446 \qmlsignal QtQuick2::Flickable::onDragStarted()
448 This handler is called when the view starts to be dragged due to user
453 \qmlsignal QtQuick2::Flickable::onDragEnded()
455 This handler is called when the user stops dragging the view.
457 If the velocity of the drag is suffient at the time the
458 touch/mouse button is released then a flick will start.
462 \qmlclass Flickable QQuickFlickable
463 \inqmlmodule QtQuick 2
464 \ingroup qml-basic-interaction-elements
466 \brief The Flickable item provides a surface that can be "flicked".
469 The Flickable item places its children on a surface that can be dragged
470 and flicked, causing the view onto the child items to scroll. This
471 behavior forms the basis of Items that are designed to show large numbers
472 of child items, such as \l ListView and \l GridView.
474 In traditional user interfaces, views can be scrolled using standard
475 controls, such as scroll bars and arrow buttons. In some situations, it
476 is also possible to drag the view directly by pressing and holding a
477 mouse button while moving the cursor. In touch-based user interfaces,
478 this dragging action is often complemented with a flicking action, where
479 scrolling continues after the user has stopped touching the view.
481 Flickable does not automatically clip its contents. If it is not used as
482 a full-screen item, you should consider setting the \l{Item::}{clip} property
485 \section1 Example Usage
487 \div {class="float-right"}
488 \inlineimage flickable.gif
491 The following example shows a small view onto a large image in which the
492 user can drag or flick the image in order to view different parts of it.
494 \snippet doc/src/snippets/declarative/flickable.qml document
498 Items declared as children of a Flickable are automatically parented to the
499 Flickable's \l contentItem. This should be taken into account when
500 operating on the children of the Flickable; it is usually the children of
501 \c contentItem that are relevant. For example, the bound of Items added
502 to the Flickable will be available by \c contentItem.childrenRect
504 \section1 Limitations
506 \note Due to an implementation detail, items placed inside a Flickable cannot anchor to it by
507 \c id. Use \c parent instead.
511 \qmlsignal QtQuick2::Flickable::onMovementStarted()
513 This handler is called when the view begins moving due to user
518 \qmlsignal QtQuick2::Flickable::onMovementEnded()
520 This handler is called when the view stops moving due to user
521 interaction. If a flick was generated, this handler will
522 be triggered once the flick stops. If a flick was not
523 generated, the handler will be triggered when the
524 user stops dragging - i.e. a mouse or touch release.
528 \qmlsignal QtQuick2::Flickable::onFlickStarted()
530 This handler is called when the view is flicked. A flick
531 starts from the point that the mouse or touch is released,
532 while still in motion.
536 \qmlsignal QtQuick2::Flickable::onFlickEnded()
538 This handler is called when the view stops moving due to a flick.
542 \qmlproperty real QtQuick2::Flickable::visibleArea.xPosition
543 \qmlproperty real QtQuick2::Flickable::visibleArea.widthRatio
544 \qmlproperty real QtQuick2::Flickable::visibleArea.yPosition
545 \qmlproperty real QtQuick2::Flickable::visibleArea.heightRatio
547 These properties describe the position and size of the currently viewed area.
548 The size is defined as the percentage of the full view currently visible,
549 scaled to 0.0 - 1.0. The page position is usually in the range 0.0 (beginning) to
550 1.0 minus size ratio (end), i.e. \c yPosition is in the range 0.0 to 1.0-\c heightRatio.
551 However, it is possible for the contents to be dragged outside of the normal
552 range, resulting in the page positions also being outside the normal range.
554 These properties are typically used to draw a scrollbar. For example:
556 \snippet doc/src/snippets/declarative/flickableScrollbar.qml 0
558 \snippet doc/src/snippets/declarative/flickableScrollbar.qml 1
560 \sa {declarative/ui-components/scrollbar}{scrollbar example}
562 QQuickFlickable::QQuickFlickable(QQuickItem *parent)
563 : QQuickItem(*(new QQuickFlickablePrivate), parent)
565 Q_D(QQuickFlickable);
569 QQuickFlickable::QQuickFlickable(QQuickFlickablePrivate &dd, QQuickItem *parent)
570 : QQuickItem(dd, parent)
572 Q_D(QQuickFlickable);
576 QQuickFlickable::~QQuickFlickable()
581 \qmlproperty real QtQuick2::Flickable::contentX
582 \qmlproperty real QtQuick2::Flickable::contentY
584 These properties hold the surface coordinate currently at the top-left
585 corner of the Flickable. For example, if you flick an image up 100 pixels,
586 \c contentY will be 100.
588 qreal QQuickFlickable::contentX() const
590 Q_D(const QQuickFlickable);
591 return -d->contentItem->x();
594 void QQuickFlickable::setContentX(qreal pos)
596 Q_D(QQuickFlickable);
597 d->hData.explicitValue = true;
598 d->timeline.reset(d->hData.move);
599 d->vTime = d->timeline.time();
601 if (-pos != d->hData.move.value())
602 d->hData.move.setValue(-pos);
605 qreal QQuickFlickable::contentY() const
607 Q_D(const QQuickFlickable);
608 return -d->contentItem->y();
611 void QQuickFlickable::setContentY(qreal pos)
613 Q_D(QQuickFlickable);
614 d->vData.explicitValue = true;
615 d->timeline.reset(d->vData.move);
616 d->vTime = d->timeline.time();
618 if (-pos != d->vData.move.value())
619 d->vData.move.setValue(-pos);
623 \qmlproperty bool QtQuick2::Flickable::interactive
625 This property describes whether the user can interact with the Flickable.
626 A user cannot drag or flick a Flickable that is not interactive.
628 By default, this property is true.
630 This property is useful for temporarily disabling flicking. This allows
631 special interaction with Flickable's children; for example, you might want
632 to freeze a flickable map while scrolling through a pop-up dialog that
633 is a child of the Flickable.
635 bool QQuickFlickable::isInteractive() const
637 Q_D(const QQuickFlickable);
638 return d->interactive;
641 void QQuickFlickable::setInteractive(bool interactive)
643 Q_D(QQuickFlickable);
644 if (interactive != d->interactive) {
645 d->interactive = interactive;
646 if (!interactive && (d->hData.flicking || d->vData.flicking)) {
648 d->vTime = d->timeline.time();
649 d->hData.flicking = false;
650 d->vData.flicking = false;
651 emit flickingChanged();
652 emit flickingHorizontallyChanged();
653 emit flickingVerticallyChanged();
656 emit interactiveChanged();
661 \qmlproperty real QtQuick2::Flickable::horizontalVelocity
662 \qmlproperty real QtQuick2::Flickable::verticalVelocity
664 The instantaneous velocity of movement along the x and y axes, in pixels/sec.
666 The reported velocity is smoothed to avoid erratic output.
668 qreal QQuickFlickable::horizontalVelocity() const
670 Q_D(const QQuickFlickable);
671 return d->hData.smoothVelocity.value();
674 qreal QQuickFlickable::verticalVelocity() const
676 Q_D(const QQuickFlickable);
677 return d->vData.smoothVelocity.value();
681 \qmlproperty bool QtQuick2::Flickable::atXBeginning
682 \qmlproperty bool QtQuick2::Flickable::atXEnd
683 \qmlproperty bool QtQuick2::Flickable::atYBeginning
684 \qmlproperty bool QtQuick2::Flickable::atYEnd
686 These properties are true if the flickable view is positioned at the beginning,
689 bool QQuickFlickable::isAtXEnd() const
691 Q_D(const QQuickFlickable);
692 return d->hData.atEnd;
695 bool QQuickFlickable::isAtXBeginning() const
697 Q_D(const QQuickFlickable);
698 return d->hData.atBeginning;
701 bool QQuickFlickable::isAtYEnd() const
703 Q_D(const QQuickFlickable);
704 return d->vData.atEnd;
707 bool QQuickFlickable::isAtYBeginning() const
709 Q_D(const QQuickFlickable);
710 return d->vData.atBeginning;
714 \qmlproperty Item QtQuick2::Flickable::contentItem
716 The internal item that contains the Items to be moved in the Flickable.
718 Items declared as children of a Flickable are automatically parented to the Flickable's contentItem.
720 Items created dynamically need to be explicitly parented to the \e contentItem:
724 function addItem(file) {
725 var component = Qt.createComponent(file)
726 component.createObject(myFlickable.contentItem);
731 QQuickItem *QQuickFlickable::contentItem()
733 Q_D(QQuickFlickable);
734 return d->contentItem;
737 QQuickFlickableVisibleArea *QQuickFlickable::visibleArea()
739 Q_D(QQuickFlickable);
741 d->visibleArea = new QQuickFlickableVisibleArea(this);
742 return d->visibleArea;
746 \qmlproperty enumeration QtQuick2::Flickable::flickableDirection
748 This property determines which directions the view can be flicked.
751 \o Flickable.AutoFlickDirection (default) - allows flicking vertically if the
752 \e contentHeight is not equal to the \e height of the Flickable.
753 Allows flicking horizontally if the \e contentWidth is not equal
754 to the \e width of the Flickable.
755 \o Flickable.HorizontalFlick - allows flicking horizontally.
756 \o Flickable.VerticalFlick - allows flicking vertically.
757 \o Flickable.HorizontalAndVerticalFlick - allows flicking in both directions.
760 QQuickFlickable::FlickableDirection QQuickFlickable::flickableDirection() const
762 Q_D(const QQuickFlickable);
763 return d->flickableDirection;
766 void QQuickFlickable::setFlickableDirection(FlickableDirection direction)
768 Q_D(QQuickFlickable);
769 if (direction != d->flickableDirection) {
770 d->flickableDirection = direction;
771 emit flickableDirectionChanged();
775 bool QQuickFlickable::pixelAligned() const
777 Q_D(const QQuickFlickable);
778 return d->pixelAligned;
781 void QQuickFlickable::setPixelAligned(bool align)
783 Q_D(QQuickFlickable);
784 if (align != d->pixelAligned) {
785 d->pixelAligned = align;
786 emit pixelAlignedChanged();
790 qint64 QQuickFlickablePrivate::computeCurrentTime(QInputEvent *event)
792 if (0 != event->timestamp() && QQuickItemPrivate::consistentTime == -1)
793 return event->timestamp();
795 return QQuickItemPrivate::elapsed(timer);
798 void QQuickFlickablePrivate::handleMousePressEvent(QMouseEvent *event)
800 Q_Q(QQuickFlickable);
801 QQuickItemPrivate::start(timer);
802 if (interactive && timeline.isActive()
803 && (qAbs(hData.smoothVelocity.value()) > RetainGrabVelocity
804 || qAbs(vData.smoothVelocity.value()) > RetainGrabVelocity)) {
805 stealMouse = true; // If we've been flicked then steal the click.
809 q->setKeepMouseGrab(stealMouse);
814 hData.dragMinBound = q->minXExtent();
815 vData.dragMinBound = q->minYExtent();
816 hData.dragMaxBound = q->maxXExtent();
817 vData.dragMaxBound = q->maxYExtent();
820 lastPosTime = computeCurrentTime(event);
821 pressPos = event->localPos();
822 hData.pressPos = hData.move.value();
823 vData.pressPos = vData.move.value();
824 hData.flicking = false;
825 vData.flicking = false;
826 lastPressTime = computeCurrentTime(event);
827 QQuickItemPrivate::start(velocityTime);
830 void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event)
832 Q_Q(QQuickFlickable);
833 if (!interactive || lastPosTime == -1)
835 bool rejectY = false;
836 bool rejectX = false;
838 bool stealY = stealMouse;
839 bool stealX = stealMouse;
841 qint64 elapsed = computeCurrentTime(event) - lastPressTime;
844 qreal dy = event->localPos().y() - pressPos.y();
845 if (qAbs(dy) > qApp->styleHints()->startDragDistance() || elapsed > 200) {
847 vData.dragStartOffset = dy;
848 qreal newY = dy + vData.pressPos - vData.dragStartOffset;
849 const qreal minY = vData.dragMinBound;
850 const qreal maxY = vData.dragMaxBound;
852 newY = minY + (newY - minY) / 2;
853 if (newY < maxY && maxY - minY <= 0)
854 newY = maxY + (newY - maxY) / 2;
855 if (boundsBehavior == QQuickFlickable::StopAtBounds && (newY > minY || newY < maxY)) {
866 if (!rejectY && stealMouse) {
867 vData.move.setValue(qRound(newY));
870 if (qAbs(dy) > qApp->styleHints()->startDragDistance())
876 qreal dx = event->localPos().x() - pressPos.x();
877 if (qAbs(dx) > qApp->styleHints()->startDragDistance() || elapsed > 200) {
879 hData.dragStartOffset = dx;
880 qreal newX = dx + hData.pressPos - hData.dragStartOffset;
881 const qreal minX = hData.dragMinBound;
882 const qreal maxX = hData.dragMaxBound;
884 newX = minX + (newX - minX) / 2;
885 if (newX < maxX && maxX - minX <= 0)
886 newX = maxX + (newX - maxX) / 2;
887 if (boundsBehavior == QQuickFlickable::StopAtBounds && (newX > minX || newX < maxX)) {
898 if (!rejectX && stealMouse) {
899 hData.move.setValue(qRound(newX));
903 if (qAbs(dx) > qApp->styleHints()->startDragDistance())
908 stealMouse = stealX || stealY;
910 q->setKeepMouseGrab(true);
913 vData.velocityBuffer.clear();
917 hData.velocityBuffer.clear();
921 if (hMoved || vMoved) {
923 q->movementStarting();
926 if (!lastPos.isNull()) {
927 qint64 currentTimestamp = computeCurrentTime(event);
928 qreal elapsed = qreal(currentTimestamp - lastPosTime) / 1000.;
931 lastPosTime = currentTimestamp;
932 qreal dy = event->localPos().y()-lastPos.y();
933 if (q->yflick() && !rejectY)
934 vData.addVelocitySample(dy/elapsed, maxVelocity);
935 qreal dx = event->localPos().x()-lastPos.x();
936 if (q->xflick() && !rejectX)
937 hData.addVelocitySample(dx/elapsed, maxVelocity);
940 lastPos = event->localPos();
943 void QQuickFlickablePrivate::handleMouseReleaseEvent(QMouseEvent *event)
945 Q_Q(QQuickFlickable);
947 q->setKeepMouseGrab(false);
950 // if we drag then pause before release we should not cause a flick.
951 qint64 elapsed = computeCurrentTime(event) - lastPosTime;
953 vData.updateVelocity();
954 hData.updateVelocity();
958 if (lastPosTime == -1)
961 vTime = timeline.time();
963 qreal velocity = elapsed < 100 ? vData.velocity : 0;
964 if (vData.atBeginning || vData.atEnd)
966 if (q->yflick() && qAbs(velocity) > MinimumFlickVelocity && qAbs(event->localPos().y() - pressPos.y()) > FlickThreshold) {
967 velocityTimeline.reset(vData.smoothVelocity);
968 vData.smoothVelocity.setValue(-velocity);
974 velocity = elapsed < 100 ? hData.velocity : 0;
975 if (hData.atBeginning || hData.atEnd)
977 if (q->xflick() && qAbs(velocity) > MinimumFlickVelocity && qAbs(event->localPos().x() - pressPos.x()) > FlickThreshold) {
978 velocityTimeline.reset(hData.smoothVelocity);
979 hData.smoothVelocity.setValue(-velocity);
985 if (!timeline.isActive())
989 void QQuickFlickable::mousePressEvent(QMouseEvent *event)
991 Q_D(QQuickFlickable);
992 if (d->interactive) {
994 d->handleMousePressEvent(event);
997 QQuickItem::mousePressEvent(event);
1001 void QQuickFlickable::mouseMoveEvent(QMouseEvent *event)
1003 Q_D(QQuickFlickable);
1004 if (d->interactive) {
1005 d->handleMouseMoveEvent(event);
1008 QQuickItem::mouseMoveEvent(event);
1012 void QQuickFlickable::mouseReleaseEvent(QMouseEvent *event)
1014 Q_D(QQuickFlickable);
1015 if (d->interactive) {
1016 d->clearDelayedPress();
1017 d->handleMouseReleaseEvent(event);
1021 QQuickItem::mouseReleaseEvent(event);
1025 void QQuickFlickable::wheelEvent(QWheelEvent *event)
1027 Q_D(QQuickFlickable);
1028 if (!d->interactive) {
1029 QQuickItem::wheelEvent(event);
1030 } else if (yflick() && event->orientation() == Qt::Vertical) {
1032 if (event->delta() > 0 && contentY() > -minYExtent()) {
1033 d->vData.velocity = qMax(event->delta()*2 - d->vData.smoothVelocity.value(), qreal(d->maxVelocity/4));
1035 } else if (event->delta() < 0 && contentY() < -maxYExtent()) {
1036 d->vData.velocity = qMin(event->delta()*2 - d->vData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
1040 d->vData.flicking = false;
1041 d->flickY(d->vData.velocity);
1042 if (d->vData.flicking) {
1048 } else if (xflick() && event->orientation() == Qt::Horizontal) {
1050 if (event->delta() > 0 && contentX() > -minXExtent()) {
1051 d->hData.velocity = qMax(event->delta()*2 - d->hData.smoothVelocity.value(), qreal(d->maxVelocity/4));
1053 } else if (event->delta() < 0 && contentX() < -maxXExtent()) {
1054 d->hData.velocity = qMin(event->delta()*2 - d->hData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
1058 d->hData.flicking = false;
1059 d->flickX(d->hData.velocity);
1060 if (d->hData.flicking) {
1067 QQuickItem::wheelEvent(event);
1071 bool QQuickFlickablePrivate::isOutermostPressDelay() const
1073 Q_Q(const QQuickFlickable);
1074 QQuickItem *item = q->parentItem();
1076 QQuickFlickable *flick = qobject_cast<QQuickFlickable*>(item);
1077 if (flick && flick->pressDelay() > 0 && flick->isInteractive())
1079 item = item->parentItem();
1085 void QQuickFlickablePrivate::captureDelayedPress(QMouseEvent *event)
1087 Q_Q(QQuickFlickable);
1088 if (!q->canvas() || pressDelay <= 0)
1090 if (!isOutermostPressDelay())
1092 delayedPressTarget = q->canvas()->mouseGrabberItem();
1093 delayedPressEvent = new QMouseEvent(*event);
1094 delayedPressEvent->setAccepted(false);
1095 delayedPressTimer.start(pressDelay, q);
1098 void QQuickFlickablePrivate::clearDelayedPress()
1100 if (delayedPressEvent) {
1101 delayedPressTimer.stop();
1102 delete delayedPressEvent;
1103 delayedPressEvent = 0;
1107 //XXX pixelAligned ignores the global position of the Flickable, i.e. assumes Flickable itself is pixel aligned.
1108 void QQuickFlickablePrivate::setViewportX(qreal x)
1110 contentItem->setX(pixelAligned ? qRound(x) : x);
1113 void QQuickFlickablePrivate::setViewportY(qreal y)
1115 contentItem->setY(pixelAligned ? qRound(y) : y);
1118 void QQuickFlickable::timerEvent(QTimerEvent *event)
1120 Q_D(QQuickFlickable);
1121 if (event->timerId() == d->delayedPressTimer.timerId()) {
1122 d->delayedPressTimer.stop();
1123 if (d->delayedPressEvent) {
1124 QQuickItem *grabber = canvas() ? canvas()->mouseGrabberItem() : 0;
1125 if (!grabber || grabber != this) {
1126 // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
1127 // so we reset the grabber
1128 if (canvas()->mouseGrabberItem() == d->delayedPressTarget)
1129 d->delayedPressTarget->ungrabMouse();
1130 // Use the event handler that will take care of finding the proper item to propagate the event
1131 QQuickCanvasPrivate::get(canvas())->deliverMouseEvent(d->delayedPressEvent);
1133 delete d->delayedPressEvent;
1134 d->delayedPressEvent = 0;
1139 qreal QQuickFlickable::minYExtent() const
1141 Q_D(const QQuickFlickable);
1142 return d->vData.startMargin;
1145 qreal QQuickFlickable::minXExtent() const
1147 Q_D(const QQuickFlickable);
1148 return d->hData.startMargin;
1152 qreal QQuickFlickable::maxXExtent() const
1154 Q_D(const QQuickFlickable);
1155 return width() - vWidth() - d->hData.endMargin;
1158 qreal QQuickFlickable::maxYExtent() const
1160 Q_D(const QQuickFlickable);
1161 return height() - vHeight() - d->vData.endMargin;
1164 void QQuickFlickable::componentComplete()
1166 Q_D(QQuickFlickable);
1167 QQuickItem::componentComplete();
1168 if (!d->hData.explicitValue && d->hData.startMargin != 0.)
1169 setContentX(-minXExtent());
1170 if (!d->vData.explicitValue && d->vData.startMargin != 0.)
1171 setContentY(-minYExtent());
1174 void QQuickFlickable::viewportMoved()
1176 Q_D(QQuickFlickable);
1178 qreal prevX = d->lastFlickablePosition.x();
1179 qreal prevY = d->lastFlickablePosition.y();
1180 if (d->pressed || d->calcVelocity) {
1181 int elapsed = QQuickItemPrivate::restart(d->velocityTime);
1183 qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / elapsed;
1184 if (qAbs(horizontalVelocity) > 0) {
1185 d->velocityTimeline.reset(d->hData.smoothVelocity);
1186 if (d->calcVelocity)
1187 d->velocityTimeline.set(d->hData.smoothVelocity, horizontalVelocity);
1189 d->velocityTimeline.move(d->hData.smoothVelocity, horizontalVelocity, d->reportedVelocitySmoothing);
1190 d->velocityTimeline.move(d->hData.smoothVelocity, 0, d->reportedVelocitySmoothing);
1192 qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / elapsed;
1193 if (qAbs(verticalVelocity) > 0) {
1194 d->velocityTimeline.reset(d->vData.smoothVelocity);
1195 if (d->calcVelocity)
1196 d->velocityTimeline.set(d->vData.smoothVelocity, verticalVelocity);
1198 d->velocityTimeline.move(d->vData.smoothVelocity, verticalVelocity, d->reportedVelocitySmoothing);
1199 d->velocityTimeline.move(d->vData.smoothVelocity, 0, d->reportedVelocitySmoothing);
1203 if (d->timeline.time() > d->vTime) {
1204 d->velocityTimeline.clear();
1205 qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / (d->timeline.time() - d->vTime);
1206 qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / (d->timeline.time() - d->vTime);
1207 d->hData.smoothVelocity.setValue(horizontalVelocity);
1208 d->vData.smoothVelocity.setValue(verticalVelocity);
1212 if (!d->vData.inOvershoot && !d->vData.fixingUp && d->vData.flicking
1213 && (d->vData.move.value() > minYExtent() || d->vData.move.value() < maxYExtent())
1214 && qAbs(d->vData.smoothVelocity.value()) > 100) {
1215 // Increase deceleration if we've passed a bound
1216 d->vData.inOvershoot = true;
1217 qreal maxDistance = d->overShootDistance(height());
1218 d->timeline.reset(d->vData.move);
1219 d->timeline.accel(d->vData.move, -d->vData.smoothVelocity.value(), d->deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance);
1220 d->timeline.callback(QDeclarativeTimeLineCallback(&d->vData.move, d->fixupY_callback, d));
1222 if (!d->hData.inOvershoot && !d->hData.fixingUp && d->hData.flicking
1223 && (d->hData.move.value() > minXExtent() || d->hData.move.value() < maxXExtent())
1224 && qAbs(d->hData.smoothVelocity.value()) > 100) {
1225 // Increase deceleration if we've passed a bound
1226 d->hData.inOvershoot = true;
1227 qreal maxDistance = d->overShootDistance(width());
1228 d->timeline.reset(d->hData.move);
1229 d->timeline.accel(d->hData.move, -d->hData.smoothVelocity.value(), d->deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance);
1230 d->timeline.callback(QDeclarativeTimeLineCallback(&d->hData.move, d->fixupX_callback, d));
1233 d->lastFlickablePosition = QPointF(d->hData.move.value(), d->vData.move.value());
1235 d->vTime = d->timeline.time();
1236 d->updateBeginningEnd();
1239 void QQuickFlickable::geometryChanged(const QRectF &newGeometry,
1240 const QRectF &oldGeometry)
1242 Q_D(QQuickFlickable);
1243 QQuickItem::geometryChanged(newGeometry, oldGeometry);
1245 bool changed = false;
1246 if (newGeometry.width() != oldGeometry.width()) {
1249 if (d->hData.viewSize < 0) {
1250 d->contentItem->setWidth(width());
1251 emit contentWidthChanged();
1253 // Make sure that we're entirely in view.
1254 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1255 d->fixupMode = QQuickFlickablePrivate::Immediate;
1259 if (newGeometry.height() != oldGeometry.height()) {
1262 if (d->vData.viewSize < 0) {
1263 d->contentItem->setHeight(height());
1264 emit contentHeightChanged();
1266 // Make sure that we're entirely in view.
1267 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1268 d->fixupMode = QQuickFlickablePrivate::Immediate;
1274 d->updateBeginningEnd();
1277 void QQuickFlickable::cancelFlick()
1279 Q_D(QQuickFlickable);
1280 d->timeline.reset(d->hData.move);
1281 d->timeline.reset(d->vData.move);
1285 void QQuickFlickablePrivate::data_append(QDeclarativeListProperty<QObject> *prop, QObject *o)
1287 QQuickItem *i = qobject_cast<QQuickItem *>(o);
1289 i->setParentItem(static_cast<QQuickFlickablePrivate*>(prop->data)->contentItem);
1291 o->setParent(prop->object); // XXX todo - do we want this?
1295 int QQuickFlickablePrivate::data_count(QDeclarativeListProperty<QObject> *)
1301 QObject *QQuickFlickablePrivate::data_at(QDeclarativeListProperty<QObject> *, int)
1307 void QQuickFlickablePrivate::data_clear(QDeclarativeListProperty<QObject> *)
1312 QDeclarativeListProperty<QObject> QQuickFlickable::flickableData()
1314 Q_D(QQuickFlickable);
1315 return QDeclarativeListProperty<QObject>(this, (void *)d, QQuickFlickablePrivate::data_append,
1316 QQuickFlickablePrivate::data_count,
1317 QQuickFlickablePrivate::data_at,
1318 QQuickFlickablePrivate::data_clear);
1321 QDeclarativeListProperty<QQuickItem> QQuickFlickable::flickableChildren()
1323 Q_D(QQuickFlickable);
1324 return QQuickItemPrivate::get(d->contentItem)->children();
1328 \qmlproperty enumeration QtQuick2::Flickable::boundsBehavior
1329 This property holds whether the surface may be dragged
1330 beyond the Flickable's boundaries, or overshoot the
1331 Flickable's boundaries when flicked.
1333 This enables the feeling that the edges of the view are soft,
1334 rather than a hard physical boundary.
1336 The \c boundsBehavior can be one of:
1339 \o Flickable.StopAtBounds - the contents can not be dragged beyond the boundary
1340 of the flickable, and flicks will not overshoot.
1341 \o Flickable.DragOverBounds - the contents can be dragged beyond the boundary
1342 of the Flickable, but flicks will not overshoot.
1343 \o Flickable.DragAndOvershootBounds (default) - the contents can be dragged
1344 beyond the boundary of the Flickable, and can overshoot the
1345 boundary when flicked.
1348 QQuickFlickable::BoundsBehavior QQuickFlickable::boundsBehavior() const
1350 Q_D(const QQuickFlickable);
1351 return d->boundsBehavior;
1354 void QQuickFlickable::setBoundsBehavior(BoundsBehavior b)
1356 Q_D(QQuickFlickable);
1357 if (b == d->boundsBehavior)
1359 d->boundsBehavior = b;
1360 emit boundsBehaviorChanged();
1364 \qmlproperty real QtQuick2::Flickable::contentWidth
1365 \qmlproperty real QtQuick2::Flickable::contentHeight
1367 The dimensions of the content (the surface controlled by Flickable).
1368 This should typically be set to the combined size of the items placed in the
1371 The following snippet shows how these properties are used to display
1372 an image that is larger than the Flickable item itself:
1374 \snippet doc/src/snippets/declarative/flickable.qml document
1376 In some cases, the the content dimensions can be automatically set
1377 using the \l {Item::childrenRect.width}{childrenRect.width}
1378 and \l {Item::childrenRect.height}{childrenRect.height} properties.
1380 qreal QQuickFlickable::contentWidth() const
1382 Q_D(const QQuickFlickable);
1383 return d->hData.viewSize;
1386 void QQuickFlickable::setContentWidth(qreal w)
1388 Q_D(QQuickFlickable);
1389 if (d->hData.viewSize == w)
1391 d->hData.viewSize = w;
1393 d->contentItem->setWidth(width());
1395 d->contentItem->setWidth(w);
1396 d->hData.markExtentsDirty();
1397 // Make sure that we're entirely in view.
1398 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1399 d->fixupMode = QQuickFlickablePrivate::Immediate;
1401 } else if (!d->pressed && d->hData.fixingUp) {
1402 d->fixupMode = QQuickFlickablePrivate::ExtentChanged;
1405 emit contentWidthChanged();
1406 d->updateBeginningEnd();
1409 qreal QQuickFlickable::contentHeight() const
1411 Q_D(const QQuickFlickable);
1412 return d->vData.viewSize;
1415 void QQuickFlickable::setContentHeight(qreal h)
1417 Q_D(QQuickFlickable);
1418 if (d->vData.viewSize == h)
1420 d->vData.viewSize = h;
1422 d->contentItem->setHeight(height());
1424 d->contentItem->setHeight(h);
1425 d->vData.markExtentsDirty();
1426 // Make sure that we're entirely in view.
1427 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1428 d->fixupMode = QQuickFlickablePrivate::Immediate;
1430 } else if (!d->pressed && d->vData.fixingUp) {
1431 d->fixupMode = QQuickFlickablePrivate::ExtentChanged;
1434 emit contentHeightChanged();
1435 d->updateBeginningEnd();
1439 \qmlproperty real QtQuick2::Flickable::topMargin
1440 \qmlproperty real QtQuick2::Flickable::leftMargin
1441 \qmlproperty real QtQuick2::Flickable::bottomMargin
1442 \qmlproperty real QtQuick2::Flickable::rightMargin
1444 These properties hold the margins around the content. This space is reserved
1445 in addition to the contentWidth and contentHeight.
1449 qreal QQuickFlickable::topMargin() const
1451 Q_D(const QQuickFlickable);
1452 return d->vData.startMargin;
1455 void QQuickFlickable::setTopMargin(qreal m)
1457 Q_D(QQuickFlickable);
1458 if (d->vData.startMargin == m)
1460 d->vData.startMargin = m;
1461 d->vData.markExtentsDirty();
1462 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1463 d->fixupMode = QQuickFlickablePrivate::Immediate;
1466 emit topMarginChanged();
1467 d->updateBeginningEnd();
1470 qreal QQuickFlickable::bottomMargin() const
1472 Q_D(const QQuickFlickable);
1473 return d->vData.endMargin;
1476 void QQuickFlickable::setBottomMargin(qreal m)
1478 Q_D(QQuickFlickable);
1479 if (d->vData.endMargin == m)
1481 d->vData.endMargin = m;
1482 d->vData.markExtentsDirty();
1483 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1484 d->fixupMode = QQuickFlickablePrivate::Immediate;
1487 emit bottomMarginChanged();
1488 d->updateBeginningEnd();
1491 qreal QQuickFlickable::leftMargin() const
1493 Q_D(const QQuickFlickable);
1494 return d->hData.startMargin;
1497 void QQuickFlickable::setLeftMargin(qreal m)
1499 Q_D(QQuickFlickable);
1500 if (d->hData.startMargin == m)
1502 d->hData.startMargin = m;
1503 d->hData.markExtentsDirty();
1504 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1505 d->fixupMode = QQuickFlickablePrivate::Immediate;
1508 emit leftMarginChanged();
1509 d->updateBeginningEnd();
1512 qreal QQuickFlickable::rightMargin() const
1514 Q_D(const QQuickFlickable);
1515 return d->hData.endMargin;
1518 void QQuickFlickable::setRightMargin(qreal m)
1520 Q_D(QQuickFlickable);
1521 if (d->hData.endMargin == m)
1523 d->hData.endMargin = m;
1524 d->hData.markExtentsDirty();
1525 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1526 d->fixupMode = QQuickFlickablePrivate::Immediate;
1529 emit rightMarginChanged();
1530 d->updateBeginningEnd();
1534 \qmlproperty real QtQuick2::Flickable::xOrigin
1535 \qmlproperty real QtQuick2::Flickable::yOrigin
1537 These properties hold the origin of the content. This is usually (0,0), however
1538 ListView and GridView may have an arbitrary origin due to delegate size variation,
1539 or item insertion/removal outside the visible region.
1542 qreal QQuickFlickable::yOrigin() const
1544 Q_D(const QQuickFlickable);
1545 return -minYExtent() + d->vData.startMargin;
1548 qreal QQuickFlickable::xOrigin() const
1550 Q_D(const QQuickFlickable);
1551 return -minXExtent() + d->hData.startMargin;
1556 \qmlmethod QtQuick2::Flickable::resizeContent(real width, real height, QPointF center)
1558 Resizes the content to \a width x \a height about \a center.
1560 This does not scale the contents of the Flickable - it only resizes the \l contentWidth
1561 and \l contentHeight.
1563 Resizing the content may result in the content being positioned outside
1564 the bounds of the Flickable. Calling \l returnToBounds() will
1565 move the content back within legal bounds.
1567 void QQuickFlickable::resizeContent(qreal w, qreal h, QPointF center)
1569 Q_D(QQuickFlickable);
1570 if (w != d->hData.viewSize) {
1571 qreal oldSize = d->hData.viewSize;
1572 d->hData.viewSize = w;
1573 d->contentItem->setWidth(w);
1574 emit contentWidthChanged();
1575 if (center.x() != 0) {
1576 qreal pos = center.x() * w / oldSize;
1577 setContentX(contentX() + pos - center.x());
1580 if (h != d->vData.viewSize) {
1581 qreal oldSize = d->vData.viewSize;
1582 d->vData.viewSize = h;
1583 d->contentItem->setHeight(h);
1584 emit contentHeightChanged();
1585 if (center.y() != 0) {
1586 qreal pos = center.y() * h / oldSize;
1587 setContentY(contentY() + pos - center.y());
1590 d->updateBeginningEnd();
1594 \qmlmethod QtQuick2::Flickable::returnToBounds()
1596 Ensures the content is within legal bounds.
1598 This may be called to ensure that the content is within legal bounds
1599 after manually positioning the content.
1601 void QQuickFlickable::returnToBounds()
1603 Q_D(QQuickFlickable);
1608 qreal QQuickFlickable::vWidth() const
1610 Q_D(const QQuickFlickable);
1611 if (d->hData.viewSize < 0)
1614 return d->hData.viewSize;
1617 qreal QQuickFlickable::vHeight() const
1619 Q_D(const QQuickFlickable);
1620 if (d->vData.viewSize < 0)
1623 return d->vData.viewSize;
1626 bool QQuickFlickable::xflick() const
1628 Q_D(const QQuickFlickable);
1629 if (d->flickableDirection == QQuickFlickable::AutoFlickDirection)
1630 return vWidth() != width();
1631 return d->flickableDirection & QQuickFlickable::HorizontalFlick;
1634 bool QQuickFlickable::yflick() const
1636 Q_D(const QQuickFlickable);
1637 if (d->flickableDirection == QQuickFlickable::AutoFlickDirection)
1638 return vHeight() != height();
1639 return d->flickableDirection & QQuickFlickable::VerticalFlick;
1642 void QQuickFlickable::mouseUngrabEvent()
1644 Q_D(QQuickFlickable);
1646 // if our mouse grab has been removed (probably by another Flickable),
1649 d->draggingEnding();
1650 d->stealMouse = false;
1651 setKeepMouseGrab(false);
1655 bool QQuickFlickable::sendMouseEvent(QMouseEvent *event)
1657 Q_D(QQuickFlickable);
1658 QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
1660 QQuickCanvas *c = canvas();
1661 QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
1662 bool disabledItem = grabber && !grabber->isEnabled();
1663 bool stealThisEvent = d->stealMouse;
1664 if ((stealThisEvent || myRect.contains(event->windowPos())) && (!grabber || !grabber->keepMouseGrab() || disabledItem)) {
1665 QMouseEvent mouseEvent(event->type(), mapFromScene(event->windowPos()), event->windowPos(), event->screenPos(),
1666 event->button(), event->buttons(), event->modifiers());
1668 mouseEvent.setTimestamp(event->timestamp());
1669 mouseEvent.setAccepted(false);
1671 switch (mouseEvent.type()) {
1672 case QEvent::MouseMove:
1673 d->handleMouseMoveEvent(&mouseEvent);
1675 case QEvent::MouseButtonPress:
1676 if (d->pressed) // we are already pressed - this is a delayed replay
1679 d->handleMousePressEvent(&mouseEvent);
1680 d->captureDelayedPress(event);
1681 stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
1683 case QEvent::MouseButtonRelease:
1684 if (d->delayedPressEvent) {
1685 // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
1686 // so we reset the grabber
1687 if (c->mouseGrabberItem() == d->delayedPressTarget)
1688 d->delayedPressTarget->ungrabMouse();
1689 //Use the event handler that will take care of finding the proper item to propagate the event
1690 QQuickCanvasPrivate::get(canvas())->deliverMouseEvent(d->delayedPressEvent);
1691 d->clearDelayedPress();
1692 // We send the release
1693 canvas()->sendEvent(c->mouseGrabberItem(), event);
1694 // And the event has been consumed
1695 d->stealMouse = false;
1699 d->handleMouseReleaseEvent(&mouseEvent);
1704 grabber = qobject_cast<QQuickItem*>(c->mouseGrabberItem());
1705 if ((grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) || disabledItem) {
1706 d->clearDelayedPress();
1710 return stealThisEvent || d->delayedPressEvent || disabledItem;
1711 } else if (d->lastPosTime != -1) {
1712 d->lastPosTime = -1;
1715 if (event->type() == QEvent::MouseButtonRelease) {
1716 d->lastPosTime = -1;
1717 d->clearDelayedPress();
1718 d->stealMouse = false;
1725 bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e)
1727 Q_D(QQuickFlickable);
1728 if (!isVisible() || !d->interactive || !isEnabled())
1729 return QQuickItem::childMouseEventFilter(i, e);
1730 switch (e->type()) {
1731 case QEvent::MouseButtonPress:
1732 case QEvent::MouseMove:
1733 case QEvent::MouseButtonRelease:
1734 return sendMouseEvent(static_cast<QMouseEvent *>(e));
1739 return QQuickItem::childMouseEventFilter(i, e);
1743 \qmlproperty real QtQuick2::Flickable::maximumFlickVelocity
1744 This property holds the maximum velocity that the user can flick the view in pixels/second.
1746 The default value is platform dependent.
1748 qreal QQuickFlickable::maximumFlickVelocity() const
1750 Q_D(const QQuickFlickable);
1751 return d->maxVelocity;
1754 void QQuickFlickable::setMaximumFlickVelocity(qreal v)
1756 Q_D(QQuickFlickable);
1757 if (v == d->maxVelocity)
1760 emit maximumFlickVelocityChanged();
1764 \qmlproperty real QtQuick2::Flickable::flickDeceleration
1765 This property holds the rate at which a flick will decelerate.
1767 The default value is platform dependent.
1769 qreal QQuickFlickable::flickDeceleration() const
1771 Q_D(const QQuickFlickable);
1772 return d->deceleration;
1775 void QQuickFlickable::setFlickDeceleration(qreal deceleration)
1777 Q_D(QQuickFlickable);
1778 if (deceleration == d->deceleration)
1780 d->deceleration = deceleration;
1781 emit flickDecelerationChanged();
1784 bool QQuickFlickable::isFlicking() const
1786 Q_D(const QQuickFlickable);
1787 return d->hData.flicking || d->vData.flicking;
1791 \qmlproperty bool QtQuick2::Flickable::flicking
1792 \qmlproperty bool QtQuick2::Flickable::flickingHorizontally
1793 \qmlproperty bool QtQuick2::Flickable::flickingVertically
1795 These properties describe whether the view is currently moving horizontally,
1796 vertically or in either direction, due to the user flicking the view.
1798 bool QQuickFlickable::isFlickingHorizontally() const
1800 Q_D(const QQuickFlickable);
1801 return d->hData.flicking;
1804 bool QQuickFlickable::isFlickingVertically() const
1806 Q_D(const QQuickFlickable);
1807 return d->vData.flicking;
1811 \qmlproperty bool QtQuick2::Flickable::dragging
1812 \qmlproperty bool QtQuick2::Flickable::draggingHorizontally
1813 \qmlproperty bool QtQuick2::Flickable::draggingVertically
1815 These properties describe whether the view is currently moving horizontally,
1816 vertically or in either direction, due to the user dragging the view.
1818 bool QQuickFlickable::isDragging() const
1820 Q_D(const QQuickFlickable);
1821 return d->hData.dragging || d->vData.dragging;
1824 bool QQuickFlickable::isDraggingHorizontally() const
1826 Q_D(const QQuickFlickable);
1827 return d->hData.dragging;
1830 bool QQuickFlickable::isDraggingVertically() const
1832 Q_D(const QQuickFlickable);
1833 return d->vData.dragging;
1836 void QQuickFlickablePrivate::draggingStarting()
1838 Q_Q(QQuickFlickable);
1839 bool wasDragging = hData.dragging || vData.dragging;
1840 if (hMoved && !hData.dragging) {
1841 hData.dragging = true;
1842 emit q->draggingHorizontallyChanged();
1844 if (vMoved && !vData.dragging) {
1845 vData.dragging = true;
1846 emit q->draggingVerticallyChanged();
1848 if (!wasDragging && (hData.dragging || vData.dragging)) {
1849 emit q->draggingChanged();
1850 emit q->dragStarted();
1854 void QQuickFlickablePrivate::draggingEnding()
1856 Q_Q(QQuickFlickable);
1857 bool wasDragging = hData.dragging || vData.dragging;
1858 if (hData.dragging) {
1859 hData.dragging = false;
1860 emit q->draggingHorizontallyChanged();
1862 if (vData.dragging) {
1863 vData.dragging = false;
1864 emit q->draggingVerticallyChanged();
1866 if (wasDragging && !hData.dragging && !vData.dragging) {
1867 emit q->draggingChanged();
1868 emit q->dragEnded();
1873 \qmlproperty int QtQuick2::Flickable::pressDelay
1875 This property holds the time to delay (ms) delivering a press to
1876 children of the Flickable. This can be useful where reacting
1877 to a press before a flicking action has undesirable effects.
1879 If the flickable is dragged/flicked before the delay times out
1880 the press event will not be delivered. If the button is released
1881 within the timeout, both the press and release will be delivered.
1883 Note that for nested Flickables with pressDelay set, the pressDelay of
1884 inner Flickables is overridden by the outermost Flickable.
1886 int QQuickFlickable::pressDelay() const
1888 Q_D(const QQuickFlickable);
1889 return d->pressDelay;
1892 void QQuickFlickable::setPressDelay(int delay)
1894 Q_D(QQuickFlickable);
1895 if (d->pressDelay == delay)
1897 d->pressDelay = delay;
1898 emit pressDelayChanged();
1902 \qmlproperty bool QtQuick2::Flickable::moving
1903 \qmlproperty bool QtQuick2::Flickable::movingHorizontally
1904 \qmlproperty bool QtQuick2::Flickable::movingVertically
1906 These properties describe whether the view is currently moving horizontally,
1907 vertically or in either direction, due to the user either dragging or
1911 bool QQuickFlickable::isMoving() const
1913 Q_D(const QQuickFlickable);
1914 return d->hData.moving || d->vData.moving;
1917 bool QQuickFlickable::isMovingHorizontally() const
1919 Q_D(const QQuickFlickable);
1920 return d->hData.moving;
1923 bool QQuickFlickable::isMovingVertically() const
1925 Q_D(const QQuickFlickable);
1926 return d->vData.moving;
1929 void QQuickFlickable::movementStarting()
1931 Q_D(QQuickFlickable);
1932 if (d->hMoved && !d->hData.moving) {
1933 d->hData.moving = true;
1934 emit movingChanged();
1935 emit movingHorizontallyChanged();
1936 if (!d->vData.moving)
1937 emit movementStarted();
1939 else if (d->vMoved && !d->vData.moving) {
1940 d->vData.moving = true;
1941 emit movingChanged();
1942 emit movingVerticallyChanged();
1943 if (!d->hData.moving)
1944 emit movementStarted();
1948 void QQuickFlickable::movementEnding()
1950 Q_D(QQuickFlickable);
1953 d->hData.smoothVelocity.setValue(0);
1954 d->vData.smoothVelocity.setValue(0);
1957 void QQuickFlickable::movementXEnding()
1959 Q_D(QQuickFlickable);
1960 if (d->hData.flicking) {
1961 d->hData.flicking = false;
1962 emit flickingChanged();
1963 emit flickingHorizontallyChanged();
1964 if (!d->vData.flicking)
1967 if (!d->pressed && !d->stealMouse) {
1968 if (d->hData.moving) {
1969 d->hData.moving = false;
1971 emit movingChanged();
1972 emit movingHorizontallyChanged();
1973 if (!d->vData.moving)
1974 emit movementEnded();
1977 d->hData.fixingUp = false;
1980 void QQuickFlickable::movementYEnding()
1982 Q_D(QQuickFlickable);
1983 if (d->vData.flicking) {
1984 d->vData.flicking = false;
1985 emit flickingChanged();
1986 emit flickingVerticallyChanged();
1987 if (!d->hData.flicking)
1990 if (!d->pressed && !d->stealMouse) {
1991 if (d->vData.moving) {
1992 d->vData.moving = false;
1994 emit movingChanged();
1995 emit movingVerticallyChanged();
1996 if (!d->hData.moving)
1997 emit movementEnded();
2000 d->vData.fixingUp = false;
2003 void QQuickFlickablePrivate::updateVelocity()
2005 Q_Q(QQuickFlickable);
2006 emit q->horizontalVelocityChanged();
2007 emit q->verticalVelocityChanged();