1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
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 "qsgflickable_p.h"
43 #include "qsgflickable_p_p.h"
44 #include "qsgcanvas.h"
45 #include "qsgcanvas_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 QSGFlickableVisibleArea::QSGFlickableVisibleArea(QSGFlickable *parent)
95 : QObject(parent), flickable(parent), m_xPosition(0.), m_widthRatio(0.)
96 , m_yPosition(0.), m_heightRatio(0.)
100 qreal QSGFlickableVisibleArea::widthRatio() const
105 qreal QSGFlickableVisibleArea::xPosition() const
110 qreal QSGFlickableVisibleArea::heightRatio() const
112 return m_heightRatio;
115 qreal QSGFlickableVisibleArea::yPosition() const
120 void QSGFlickableVisibleArea::updateVisible()
122 QSGFlickablePrivate *p = QSGFlickablePrivate::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 QSGFlickablePrivate::QSGFlickablePrivate()
171 : contentItem(new QSGItem)
172 , hData(this, &QSGFlickablePrivate::setViewportX)
173 , vData(this, &QSGFlickablePrivate::setViewportY)
174 , flickingHorizontally(false), flickingVertically(false)
175 , hMoved(false), vMoved(false)
176 , movingHorizontally(false), movingVertically(false)
177 , stealMouse(false), pressed(false), interactive(true), calcVelocity(false)
178 , pixelAligned(false)
179 , deceleration(QML_FLICK_DEFAULTDECELERATION)
180 , maxVelocity(QML_FLICK_DEFAULTMAXVELOCITY), reportedVelocitySmoothing(100)
181 , delayedPressEvent(0), delayedPressTarget(0), pressDelay(0), fixupDuration(400)
182 , fixupMode(Normal), vTime(0), visibleArea(0)
183 , flickableDirection(QSGFlickable::AutoFlickDirection)
184 , boundsBehavior(QSGFlickable::DragAndOvershootBounds)
188 void QSGFlickablePrivate::init()
191 QDeclarative_setParent_noEvent(contentItem, q);
192 contentItem->setParentItem(q);
193 static int timelineUpdatedIdx = -1;
194 static int timelineCompletedIdx = -1;
195 static int flickableTickedIdx = -1;
196 static int flickableMovementEndingIdx = -1;
197 if (timelineUpdatedIdx == -1) {
198 timelineUpdatedIdx = QDeclarativeTimeLine::staticMetaObject.indexOfSignal("updated()");
199 timelineCompletedIdx = QDeclarativeTimeLine::staticMetaObject.indexOfSignal("completed()");
200 flickableTickedIdx = QSGFlickable::staticMetaObject.indexOfSlot("ticked()");
201 flickableMovementEndingIdx = QSGFlickable::staticMetaObject.indexOfSlot("movementEnding()");
203 QMetaObject::connect(&timeline, timelineUpdatedIdx,
204 q, flickableTickedIdx, Qt::DirectConnection);
205 QMetaObject::connect(&timeline, timelineCompletedIdx,
206 q, flickableMovementEndingIdx, Qt::DirectConnection);
207 q->setAcceptedMouseButtons(Qt::LeftButton);
208 q->setFiltersChildMouseEvents(true);
209 QSGItemPrivate *viewportPrivate = QSGItemPrivate::get(contentItem);
210 viewportPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
211 lastPosTime.invalidate();
215 Returns the amount to overshoot by given a velocity.
216 Will be roughly in range 0 - size/4
218 qreal QSGFlickablePrivate::overShootDistance(qreal size)
220 if (maxVelocity <= 0)
223 return qMin(qreal(QML_FLICK_OVERSHOOT), size/3);
226 void QSGFlickablePrivate::AxisData::addVelocitySample(qreal v, qreal maxVelocity)
230 else if (v < -maxVelocity)
232 velocityBuffer.append(v);
233 if (velocityBuffer.count() > QML_FLICK_SAMPLEBUFFER)
234 velocityBuffer.remove(0);
237 void QSGFlickablePrivate::AxisData::updateVelocity()
240 if (velocityBuffer.count() > QML_FLICK_DISCARDSAMPLES) {
241 int count = velocityBuffer.count()-QML_FLICK_DISCARDSAMPLES;
242 for (int i = 0; i < count; ++i) {
243 qreal v = velocityBuffer.at(i);
250 void QSGFlickablePrivate::itemGeometryChanged(QSGItem *item, const QRectF &newGeom, const QRectF &oldGeom)
253 if (item == contentItem) {
254 if (newGeom.x() != oldGeom.x())
255 emit q->contentXChanged();
256 if (newGeom.y() != oldGeom.y())
257 emit q->contentYChanged();
261 void QSGFlickablePrivate::flickX(qreal velocity)
264 flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, velocity);
267 void QSGFlickablePrivate::flickY(qreal velocity)
270 flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, velocity);
273 void QSGFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal,
274 QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
277 qreal maxDistance = -1;
278 data.fixingUp = false;
279 // -ve velocity means list is moving up
281 maxDistance = qAbs(minExtent - data.move.value());
282 data.flickTarget = minExtent;
284 maxDistance = qAbs(maxExtent - data.move.value());
285 data.flickTarget = maxExtent;
287 if (maxDistance > 0) {
289 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
295 timeline.reset(data.move);
296 if (boundsBehavior == QSGFlickable::DragAndOvershootBounds)
297 timeline.accel(data.move, v, deceleration);
299 timeline.accel(data.move, v, deceleration, maxDistance);
300 timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
301 if (!flickingHorizontally && q->xflick()) {
302 flickingHorizontally = true;
303 emit q->flickingChanged();
304 emit q->flickingHorizontallyChanged();
305 if (!flickingVertically)
306 emit q->flickStarted();
308 if (!flickingVertically && q->yflick()) {
309 flickingVertically = true;
310 emit q->flickingChanged();
311 emit q->flickingVerticallyChanged();
312 if (!flickingHorizontally)
313 emit q->flickStarted();
316 timeline.reset(data.move);
317 fixup(data, minExtent, maxExtent);
321 void QSGFlickablePrivate::fixupY_callback(void *data)
323 ((QSGFlickablePrivate *)data)->fixupY();
326 void QSGFlickablePrivate::fixupX_callback(void *data)
328 ((QSGFlickablePrivate *)data)->fixupX();
331 void QSGFlickablePrivate::fixupX()
334 fixup(hData, q->minXExtent(), q->maxXExtent());
337 void QSGFlickablePrivate::fixupY()
340 fixup(vData, q->minYExtent(), q->maxYExtent());
343 void QSGFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
345 if (data.move.value() > minExtent || maxExtent > minExtent) {
346 timeline.reset(data.move);
347 if (data.move.value() != minExtent) {
350 timeline.set(data.move, minExtent);
353 // The target has changed. Don't start from the beginning; just complete the
354 // second half of the animation using the new extent.
355 timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
356 data.fixingUp = true;
359 qreal dist = minExtent - data.move;
360 timeline.move(data.move, minExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4);
361 timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
362 data.fixingUp = true;
366 } else if (data.move.value() < maxExtent) {
367 timeline.reset(data.move);
370 timeline.set(data.move, maxExtent);
373 // The target has changed. Don't start from the beginning; just complete the
374 // second half of the animation using the new extent.
375 timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
376 data.fixingUp = true;
379 qreal dist = maxExtent - data.move;
380 timeline.move(data.move, maxExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4);
381 timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
382 data.fixingUp = true;
386 data.inOvershoot = false;
388 vTime = timeline.time();
391 void QSGFlickablePrivate::updateBeginningEnd()
394 bool atBoundaryChange = false;
397 const int maxyextent = int(-q->maxYExtent());
398 const qreal ypos = -vData.move.value();
399 bool atBeginning = (ypos <= -q->minYExtent());
400 bool atEnd = (maxyextent <= ypos);
402 if (atBeginning != vData.atBeginning) {
403 vData.atBeginning = atBeginning;
404 atBoundaryChange = true;
406 if (atEnd != vData.atEnd) {
408 atBoundaryChange = true;
412 const int maxxextent = int(-q->maxXExtent());
413 const qreal xpos = -hData.move.value();
414 atBeginning = (xpos <= -q->minXExtent());
415 atEnd = (maxxextent <= xpos);
417 if (atBeginning != hData.atBeginning) {
418 hData.atBeginning = atBeginning;
419 atBoundaryChange = true;
421 if (atEnd != hData.atEnd) {
423 atBoundaryChange = true;
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 QSGFlickable
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 QSGFlickable::QSGFlickable(QSGItem *parent)
563 : QSGItem(*(new QSGFlickablePrivate), parent)
569 QSGFlickable::QSGFlickable(QSGFlickablePrivate &dd, QSGItem *parent)
570 : QSGItem(dd, parent)
576 QSGFlickable::~QSGFlickable()
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 QSGFlickable::contentX() const
590 Q_D(const QSGFlickable);
591 return -d->contentItem->x();
594 void QSGFlickable::setContentX(qreal pos)
597 d->timeline.reset(d->hData.move);
598 d->vTime = d->timeline.time();
600 if (-pos != d->hData.move.value()) {
601 d->hData.move.setValue(-pos);
606 qreal QSGFlickable::contentY() const
608 Q_D(const QSGFlickable);
609 return -d->contentItem->y();
612 void QSGFlickable::setContentY(qreal pos)
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);
625 \qmlproperty bool QtQuick2::Flickable::interactive
627 This property describes whether the user can interact with the Flickable.
628 A user cannot drag or flick a Flickable that is not interactive.
630 By default, this property is true.
632 This property is useful for temporarily disabling flicking. This allows
633 special interaction with Flickable's children; for example, you might want
634 to freeze a flickable map while scrolling through a pop-up dialog that
635 is a child of the Flickable.
637 bool QSGFlickable::isInteractive() const
639 Q_D(const QSGFlickable);
640 return d->interactive;
643 void QSGFlickable::setInteractive(bool interactive)
646 if (interactive != d->interactive) {
647 d->interactive = interactive;
648 if (!interactive && (d->flickingHorizontally || d->flickingVertically)) {
650 d->vTime = d->timeline.time();
651 d->flickingHorizontally = false;
652 d->flickingVertically = false;
653 emit flickingChanged();
654 emit flickingHorizontallyChanged();
655 emit flickingVerticallyChanged();
658 emit interactiveChanged();
663 \qmlproperty real QtQuick2::Flickable::horizontalVelocity
664 \qmlproperty real QtQuick2::Flickable::verticalVelocity
666 The instantaneous velocity of movement along the x and y axes, in pixels/sec.
668 The reported velocity is smoothed to avoid erratic output.
670 qreal QSGFlickable::horizontalVelocity() const
672 Q_D(const QSGFlickable);
673 return d->hData.smoothVelocity.value();
676 qreal QSGFlickable::verticalVelocity() const
678 Q_D(const QSGFlickable);
679 return d->vData.smoothVelocity.value();
683 \qmlproperty bool QtQuick2::Flickable::atXBeginning
684 \qmlproperty bool QtQuick2::Flickable::atXEnd
685 \qmlproperty bool QtQuick2::Flickable::atYBeginning
686 \qmlproperty bool QtQuick2::Flickable::atYEnd
688 These properties are true if the flickable view is positioned at the beginning,
691 bool QSGFlickable::isAtXEnd() const
693 Q_D(const QSGFlickable);
694 return d->hData.atEnd;
697 bool QSGFlickable::isAtXBeginning() const
699 Q_D(const QSGFlickable);
700 return d->hData.atBeginning;
703 bool QSGFlickable::isAtYEnd() const
705 Q_D(const QSGFlickable);
706 return d->vData.atEnd;
709 bool QSGFlickable::isAtYBeginning() const
711 Q_D(const QSGFlickable);
712 return d->vData.atBeginning;
715 void QSGFlickable::ticked()
721 \qmlproperty Item QtQuick2::Flickable::contentItem
723 The internal item that contains the Items to be moved in the Flickable.
725 Items declared as children of a Flickable are automatically parented to the Flickable's contentItem.
727 Items created dynamically need to be explicitly parented to the \e contentItem:
731 function addItem(file) {
732 var component = Qt.createComponent(file)
733 component.createObject(myFlickable.contentItem);
738 QSGItem *QSGFlickable::contentItem()
741 return d->contentItem;
744 QSGFlickableVisibleArea *QSGFlickable::visibleArea()
748 d->visibleArea = new QSGFlickableVisibleArea(this);
749 return d->visibleArea;
753 \qmlproperty enumeration QtQuick2::Flickable::flickableDirection
755 This property determines which directions the view can be flicked.
758 \o Flickable.AutoFlickDirection (default) - allows flicking vertically if the
759 \e contentHeight is not equal to the \e height of the Flickable.
760 Allows flicking horizontally if the \e contentWidth is not equal
761 to the \e width of the Flickable.
762 \o Flickable.HorizontalFlick - allows flicking horizontally.
763 \o Flickable.VerticalFlick - allows flicking vertically.
764 \o Flickable.HorizontalAndVerticalFlick - allows flicking in both directions.
767 QSGFlickable::FlickableDirection QSGFlickable::flickableDirection() const
769 Q_D(const QSGFlickable);
770 return d->flickableDirection;
773 void QSGFlickable::setFlickableDirection(FlickableDirection direction)
776 if (direction != d->flickableDirection) {
777 d->flickableDirection = direction;
778 emit flickableDirectionChanged();
782 bool QSGFlickable::pixelAligned() const
784 Q_D(const QSGFlickable);
785 return d->pixelAligned;
788 void QSGFlickable::setPixelAligned(bool align)
791 if (align != d->pixelAligned) {
792 d->pixelAligned = align;
793 emit pixelAlignedChanged();
797 void QSGFlickablePrivate::handleMousePressEvent(QMouseEvent *event)
800 if (interactive && timeline.isActive()
801 && (qAbs(hData.smoothVelocity.value()) > RetainGrabVelocity
802 || qAbs(vData.smoothVelocity.value()) > RetainGrabVelocity)) {
803 stealMouse = true; // If we've been flicked then steal the click.
807 q->setKeepMouseGrab(stealMouse);
812 hData.dragMinBound = q->minXExtent();
813 vData.dragMinBound = q->minYExtent();
814 hData.dragMaxBound = q->maxXExtent();
815 vData.dragMaxBound = q->maxYExtent();
818 QSGItemPrivate::start(lastPosTime);
819 pressPos = event->localPos();
820 hData.pressPos = hData.move.value();
821 vData.pressPos = vData.move.value();
822 flickingHorizontally = false;
823 flickingVertically = false;
824 QSGItemPrivate::start(pressTime);
825 QSGItemPrivate::start(velocityTime);
828 void QSGFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event)
831 if (!interactive || !lastPosTime.isValid())
833 bool rejectY = false;
834 bool rejectX = false;
836 bool stealY = stealMouse;
837 bool stealX = stealMouse;
840 int dy = int(event->localPos().y() - pressPos.y());
841 if (qAbs(dy) > qApp->styleHints()->startDragDistance() || QSGItemPrivate::elapsed(pressTime) > 200) {
843 vData.dragStartOffset = dy;
844 qreal newY = dy + vData.pressPos - vData.dragStartOffset;
845 const qreal minY = vData.dragMinBound;
846 const qreal maxY = vData.dragMaxBound;
848 newY = minY + (newY - minY) / 2;
849 if (newY < maxY && maxY - minY <= 0)
850 newY = maxY + (newY - maxY) / 2;
851 if (boundsBehavior == QSGFlickable::StopAtBounds && (newY > minY || newY < maxY)) {
862 if (!rejectY && stealMouse) {
863 vData.move.setValue(qRound(newY));
866 if (qAbs(dy) > qApp->styleHints()->startDragDistance())
872 int dx = int(event->localPos().x() - pressPos.x());
873 if (qAbs(dx) > qApp->styleHints()->startDragDistance() || QSGItemPrivate::elapsed(pressTime) > 200) {
875 hData.dragStartOffset = dx;
876 qreal newX = dx + hData.pressPos - hData.dragStartOffset;
877 const qreal minX = hData.dragMinBound;
878 const qreal maxX = hData.dragMaxBound;
880 newX = minX + (newX - minX) / 2;
881 if (newX < maxX && maxX - minX <= 0)
882 newX = maxX + (newX - maxX) / 2;
883 if (boundsBehavior == QSGFlickable::StopAtBounds && (newX > minX || newX < maxX)) {
894 if (!rejectX && stealMouse) {
895 hData.move.setValue(qRound(newX));
899 if (qAbs(dx) > qApp->styleHints()->startDragDistance())
904 stealMouse = stealX || stealY;
906 q->setKeepMouseGrab(true);
909 vData.velocityBuffer.clear();
913 hData.velocityBuffer.clear();
917 if (hMoved || vMoved) {
919 q->movementStarting();
923 if (!lastPos.isNull()) {
924 qreal elapsed = qreal(QSGItemPrivate::elapsed(lastPosTime)) / 1000.;
927 QSGItemPrivate::restart(lastPosTime);
928 qreal dy = event->localPos().y()-lastPos.y();
929 if (q->yflick() && !rejectY)
930 vData.addVelocitySample(dy/elapsed, maxVelocity);
931 qreal dx = event->localPos().x()-lastPos.x();
932 if (q->xflick() && !rejectX)
933 hData.addVelocitySample(dx/elapsed, maxVelocity);
936 lastPos = event->localPos();
939 void QSGFlickablePrivate::handleMouseReleaseEvent(QMouseEvent *event)
943 q->setKeepMouseGrab(false);
946 // if we drag then pause before release we should not cause a flick.
947 if (QSGItemPrivate::elapsed(lastPosTime) < 100) {
948 vData.updateVelocity();
949 hData.updateVelocity();
951 hData.velocity = 0.0;
952 vData.velocity = 0.0;
957 if (!lastPosTime.isValid())
960 vTime = timeline.time();
962 qreal velocity = vData.velocity;
963 if (vData.atBeginning || vData.atEnd)
965 if (qAbs(velocity) > MinimumFlickVelocity && qAbs(event->localPos().y() - pressPos.y()) > FlickThreshold)
970 velocity = hData.velocity;
971 if (hData.atBeginning || hData.atEnd)
973 if (qAbs(velocity) > MinimumFlickVelocity && qAbs(event->localPos().x() - pressPos.x()) > FlickThreshold)
978 if (!timeline.isActive())
982 void QSGFlickable::mousePressEvent(QMouseEvent *event)
985 if (d->interactive) {
987 d->handleMousePressEvent(event);
990 QSGItem::mousePressEvent(event);
994 void QSGFlickable::mouseMoveEvent(QMouseEvent *event)
997 if (d->interactive) {
998 d->handleMouseMoveEvent(event);
1001 QSGItem::mouseMoveEvent(event);
1005 void QSGFlickable::mouseReleaseEvent(QMouseEvent *event)
1008 if (d->interactive) {
1009 d->clearDelayedPress();
1010 d->handleMouseReleaseEvent(event);
1014 QSGItem::mouseReleaseEvent(event);
1018 void QSGFlickable::wheelEvent(QWheelEvent *event)
1021 if (!d->interactive) {
1022 QSGItem::wheelEvent(event);
1023 } else if (yflick() && event->orientation() == Qt::Vertical) {
1025 if (event->delta() > 0 && contentY() > -minYExtent()) {
1026 d->vData.velocity = qMax(event->delta()*2 - d->vData.smoothVelocity.value(), qreal(d->maxVelocity/4));
1028 } else if (event->delta() < 0 && contentY() < -maxYExtent()) {
1029 d->vData.velocity = qMin(event->delta()*2 - d->vData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
1033 d->flickingVertically = false;
1034 d->flickY(d->vData.velocity);
1035 if (d->flickingVertically) {
1041 } else if (xflick() && event->orientation() == Qt::Horizontal) {
1043 if (event->delta() > 0 && contentX() > -minXExtent()) {
1044 d->hData.velocity = qMax(event->delta()*2 - d->hData.smoothVelocity.value(), qreal(d->maxVelocity/4));
1046 } else if (event->delta() < 0 && contentX() < -maxXExtent()) {
1047 d->hData.velocity = qMin(event->delta()*2 - d->hData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
1051 d->flickingHorizontally = false;
1052 d->flickX(d->hData.velocity);
1053 if (d->flickingHorizontally) {
1060 QSGItem::wheelEvent(event);
1064 bool QSGFlickablePrivate::isOutermostPressDelay() const
1066 Q_Q(const QSGFlickable);
1067 QSGItem *item = q->parentItem();
1069 QSGFlickable *flick = qobject_cast<QSGFlickable*>(item);
1070 if (flick && flick->pressDelay() > 0 && flick->isInteractive())
1072 item = item->parentItem();
1078 void QSGFlickablePrivate::captureDelayedPress(QMouseEvent *event)
1081 if (!q->canvas() || pressDelay <= 0)
1083 if (!isOutermostPressDelay())
1085 delayedPressTarget = q->canvas()->mouseGrabberItem();
1086 delayedPressEvent = new QMouseEvent(*event);
1087 delayedPressEvent->setAccepted(false);
1088 delayedPressTimer.start(pressDelay, q);
1091 void QSGFlickablePrivate::clearDelayedPress()
1093 if (delayedPressEvent) {
1094 delayedPressTimer.stop();
1095 delete delayedPressEvent;
1096 delayedPressEvent = 0;
1100 //XXX pixelAligned ignores the global position of the Flickable, i.e. assumes Flickable itself is pixel aligned.
1101 void QSGFlickablePrivate::setViewportX(qreal x)
1103 contentItem->setX(pixelAligned ? qRound(x) : x);
1106 void QSGFlickablePrivate::setViewportY(qreal y)
1108 contentItem->setY(pixelAligned ? qRound(y) : y);
1111 void QSGFlickable::timerEvent(QTimerEvent *event)
1114 if (event->timerId() == d->delayedPressTimer.timerId()) {
1115 d->delayedPressTimer.stop();
1116 if (d->delayedPressEvent) {
1117 QSGItem *grabber = canvas() ? canvas()->mouseGrabberItem() : 0;
1118 if (!grabber || grabber != this) {
1119 // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
1120 // so we reset the grabber
1121 if (canvas()->mouseGrabberItem() == d->delayedPressTarget)
1122 d->delayedPressTarget->ungrabMouse();
1123 // Use the event handler that will take care of finding the proper item to propagate the event
1124 QSGCanvasPrivate::get(canvas())->deliverMouseEvent(d->delayedPressEvent);
1126 delete d->delayedPressEvent;
1127 d->delayedPressEvent = 0;
1132 qreal QSGFlickable::minYExtent() const
1137 qreal QSGFlickable::minXExtent() const
1143 qreal QSGFlickable::maxXExtent() const
1145 return width() - vWidth();
1148 qreal QSGFlickable::maxYExtent() const
1150 return height() - vHeight();
1153 void QSGFlickable::viewportMoved()
1157 qreal prevX = d->lastFlickablePosition.x();
1158 qreal prevY = d->lastFlickablePosition.y();
1159 d->velocityTimeline.clear();
1160 if (d->pressed || d->calcVelocity) {
1161 int elapsed = QSGItemPrivate::restart(d->velocityTime);
1163 qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / elapsed;
1164 qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / elapsed;
1165 d->velocityTimeline.move(d->hData.smoothVelocity, horizontalVelocity, d->reportedVelocitySmoothing);
1166 d->velocityTimeline.move(d->hData.smoothVelocity, 0, d->reportedVelocitySmoothing);
1167 d->velocityTimeline.move(d->vData.smoothVelocity, verticalVelocity, d->reportedVelocitySmoothing);
1168 d->velocityTimeline.move(d->vData.smoothVelocity, 0, d->reportedVelocitySmoothing);
1171 if (d->timeline.time() > d->vTime) {
1172 qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / (d->timeline.time() - d->vTime);
1173 qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / (d->timeline.time() - d->vTime);
1174 d->hData.smoothVelocity.setValue(horizontalVelocity);
1175 d->vData.smoothVelocity.setValue(verticalVelocity);
1179 if (!d->vData.inOvershoot && !d->vData.fixingUp && d->flickingVertically
1180 && (d->vData.move.value() > minYExtent() || d->vData.move.value() < maxYExtent())
1181 && qAbs(d->vData.smoothVelocity.value()) > 100) {
1182 // Increase deceleration if we've passed a bound
1183 d->vData.inOvershoot = true;
1184 qreal maxDistance = d->overShootDistance(height());
1185 d->timeline.reset(d->vData.move);
1186 d->timeline.accel(d->vData.move, -d->vData.smoothVelocity.value(), d->deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance);
1187 d->timeline.callback(QDeclarativeTimeLineCallback(&d->vData.move, d->fixupY_callback, d));
1189 if (!d->hData.inOvershoot && !d->hData.fixingUp && d->flickingHorizontally
1190 && (d->hData.move.value() > minXExtent() || d->hData.move.value() < maxXExtent())
1191 && qAbs(d->hData.smoothVelocity.value()) > 100) {
1192 // Increase deceleration if we've passed a bound
1193 d->hData.inOvershoot = true;
1194 qreal maxDistance = d->overShootDistance(width());
1195 d->timeline.reset(d->hData.move);
1196 d->timeline.accel(d->hData.move, -d->hData.smoothVelocity.value(), d->deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance);
1197 d->timeline.callback(QDeclarativeTimeLineCallback(&d->hData.move, d->fixupX_callback, d));
1200 d->lastFlickablePosition = QPointF(d->hData.move.value(), d->vData.move.value());
1202 d->vTime = d->timeline.time();
1203 d->updateBeginningEnd();
1206 void QSGFlickable::geometryChanged(const QRectF &newGeometry,
1207 const QRectF &oldGeometry)
1210 QSGItem::geometryChanged(newGeometry, oldGeometry);
1212 bool changed = false;
1213 if (newGeometry.width() != oldGeometry.width()) {
1216 if (d->hData.viewSize < 0) {
1217 d->contentItem->setWidth(width());
1218 emit contentWidthChanged();
1220 // Make sure that we're entirely in view.
1221 if (!d->pressed && !d->movingHorizontally && !d->movingVertically) {
1222 d->fixupMode = QSGFlickablePrivate::Immediate;
1226 if (newGeometry.height() != oldGeometry.height()) {
1229 if (d->vData.viewSize < 0) {
1230 d->contentItem->setHeight(height());
1231 emit contentHeightChanged();
1233 // Make sure that we're entirely in view.
1234 if (!d->pressed && !d->movingHorizontally && !d->movingVertically) {
1235 d->fixupMode = QSGFlickablePrivate::Immediate;
1241 d->updateBeginningEnd();
1244 void QSGFlickable::cancelFlick()
1247 d->timeline.reset(d->hData.move);
1248 d->timeline.reset(d->vData.move);
1252 void QSGFlickablePrivate::data_append(QDeclarativeListProperty<QObject> *prop, QObject *o)
1254 QSGItem *i = qobject_cast<QSGItem *>(o);
1256 i->setParentItem(static_cast<QSGFlickablePrivate*>(prop->data)->contentItem);
1258 o->setParent(prop->object); // XXX todo - do we want this?
1262 int QSGFlickablePrivate::data_count(QDeclarativeListProperty<QObject> *)
1268 QObject *QSGFlickablePrivate::data_at(QDeclarativeListProperty<QObject> *, int)
1274 void QSGFlickablePrivate::data_clear(QDeclarativeListProperty<QObject> *)
1279 QDeclarativeListProperty<QObject> QSGFlickable::flickableData()
1282 return QDeclarativeListProperty<QObject>(this, (void *)d, QSGFlickablePrivate::data_append,
1283 QSGFlickablePrivate::data_count,
1284 QSGFlickablePrivate::data_at,
1285 QSGFlickablePrivate::data_clear);
1288 QDeclarativeListProperty<QSGItem> QSGFlickable::flickableChildren()
1291 return QSGItemPrivate::get(d->contentItem)->children();
1295 \qmlproperty enumeration QtQuick2::Flickable::boundsBehavior
1296 This property holds whether the surface may be dragged
1297 beyond the Fickable's boundaries, or overshoot the
1298 Flickable's boundaries when flicked.
1300 This enables the feeling that the edges of the view are soft,
1301 rather than a hard physical boundary.
1303 The \c boundsBehavior can be one of:
1306 \o Flickable.StopAtBounds - the contents can not be dragged beyond the boundary
1307 of the flickable, and flicks will not overshoot.
1308 \o Flickable.DragOverBounds - the contents can be dragged beyond the boundary
1309 of the Flickable, but flicks will not overshoot.
1310 \o Flickable.DragAndOvershootBounds (default) - the contents can be dragged
1311 beyond the boundary of the Flickable, and can overshoot the
1312 boundary when flicked.
1315 QSGFlickable::BoundsBehavior QSGFlickable::boundsBehavior() const
1317 Q_D(const QSGFlickable);
1318 return d->boundsBehavior;
1321 void QSGFlickable::setBoundsBehavior(BoundsBehavior b)
1324 if (b == d->boundsBehavior)
1326 d->boundsBehavior = b;
1327 emit boundsBehaviorChanged();
1331 \qmlproperty real QtQuick2::Flickable::contentWidth
1332 \qmlproperty real QtQuick2::Flickable::contentHeight
1334 The dimensions of the content (the surface controlled by Flickable).
1335 This should typically be set to the combined size of the items placed in the
1338 The following snippet shows how these properties are used to display
1339 an image that is larger than the Flickable item itself:
1341 \snippet doc/src/snippets/declarative/flickable.qml document
1343 In some cases, the the content dimensions can be automatically set
1344 using the \l {Item::childrenRect.width}{childrenRect.width}
1345 and \l {Item::childrenRect.height}{childrenRect.height} properties.
1347 qreal QSGFlickable::contentWidth() const
1349 Q_D(const QSGFlickable);
1350 return d->hData.viewSize;
1353 void QSGFlickable::setContentWidth(qreal w)
1356 if (d->hData.viewSize == w)
1358 d->hData.viewSize = w;
1360 d->contentItem->setWidth(width());
1362 d->contentItem->setWidth(w);
1363 // Make sure that we're entirely in view.
1364 if (!d->pressed && !d->movingHorizontally && !d->movingVertically) {
1365 d->fixupMode = QSGFlickablePrivate::Immediate;
1367 } else if (!d->pressed && d->hData.fixingUp) {
1368 d->fixupMode = QSGFlickablePrivate::ExtentChanged;
1371 emit contentWidthChanged();
1372 d->updateBeginningEnd();
1375 qreal QSGFlickable::contentHeight() const
1377 Q_D(const QSGFlickable);
1378 return d->vData.viewSize;
1381 void QSGFlickable::setContentHeight(qreal h)
1384 if (d->vData.viewSize == h)
1386 d->vData.viewSize = h;
1388 d->contentItem->setHeight(height());
1390 d->contentItem->setHeight(h);
1391 // Make sure that we're entirely in view.
1392 if (!d->pressed && !d->movingHorizontally && !d->movingVertically) {
1393 d->fixupMode = QSGFlickablePrivate::Immediate;
1395 } else if (!d->pressed && d->vData.fixingUp) {
1396 d->fixupMode = QSGFlickablePrivate::ExtentChanged;
1399 emit contentHeightChanged();
1400 d->updateBeginningEnd();
1404 \qmlmethod QtQuick2::Flickable::resizeContent(real width, real height, QPointF center)
1407 Resizes the content to \a width x \a height about \a center.
1409 This does not scale the contents of the Flickable - it only resizes the \l contentWidth
1410 and \l contentHeight.
1412 Resizing the content may result in the content being positioned outside
1413 the bounds of the Flickable. Calling \l returnToBounds() will
1414 move the content back within legal bounds.
1416 void QSGFlickable::resizeContent(qreal w, qreal h, QPointF center)
1419 if (w != d->hData.viewSize) {
1420 qreal oldSize = d->hData.viewSize;
1421 d->hData.viewSize = w;
1422 d->contentItem->setWidth(w);
1423 emit contentWidthChanged();
1424 if (center.x() != 0) {
1425 qreal pos = center.x() * w / oldSize;
1426 setContentX(contentX() + pos - center.x());
1429 if (h != d->vData.viewSize) {
1430 qreal oldSize = d->vData.viewSize;
1431 d->vData.viewSize = h;
1432 d->contentItem->setHeight(h);
1433 emit contentHeightChanged();
1434 if (center.y() != 0) {
1435 qreal pos = center.y() * h / oldSize;
1436 setContentY(contentY() + pos - center.y());
1439 d->updateBeginningEnd();
1443 \qmlmethod QtQuick2::Flickable::returnToBounds()
1446 Ensures the content is within legal bounds.
1448 This may be called to ensure that the content is within legal bounds
1449 after manually positioning the content.
1451 void QSGFlickable::returnToBounds()
1458 qreal QSGFlickable::vWidth() const
1460 Q_D(const QSGFlickable);
1461 if (d->hData.viewSize < 0)
1464 return d->hData.viewSize;
1467 qreal QSGFlickable::vHeight() const
1469 Q_D(const QSGFlickable);
1470 if (d->vData.viewSize < 0)
1473 return d->vData.viewSize;
1476 bool QSGFlickable::xflick() const
1478 Q_D(const QSGFlickable);
1479 if (d->flickableDirection == QSGFlickable::AutoFlickDirection)
1480 return vWidth() != width();
1481 return d->flickableDirection & QSGFlickable::HorizontalFlick;
1484 bool QSGFlickable::yflick() const
1486 Q_D(const QSGFlickable);
1487 if (d->flickableDirection == QSGFlickable::AutoFlickDirection)
1488 return vHeight() != height();
1489 return d->flickableDirection & QSGFlickable::VerticalFlick;
1492 void QSGFlickable::mouseUngrabEvent()
1496 // if our mouse grab has been removed (probably by another Flickable),
1499 d->draggingEnding();
1500 d->stealMouse = false;
1501 setKeepMouseGrab(false);
1505 bool QSGFlickable::sendMouseEvent(QMouseEvent *event)
1508 QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
1510 QSGCanvas *c = canvas();
1511 QSGItem *grabber = c ? c->mouseGrabberItem() : 0;
1512 bool disabledItem = grabber && !grabber->isEnabled();
1513 bool stealThisEvent = d->stealMouse;
1514 if ((stealThisEvent || myRect.contains(event->windowPos())) && (!grabber || !grabber->keepMouseGrab() || disabledItem)) {
1515 QMouseEvent mouseEvent(event->type(), mapFromScene(event->windowPos()), event->windowPos(), event->screenPos(),
1516 event->button(), event->buttons(), event->modifiers());
1518 mouseEvent.setAccepted(false);
1520 switch(mouseEvent.type()) {
1521 case QEvent::MouseMove:
1522 d->handleMouseMoveEvent(&mouseEvent);
1524 case QEvent::MouseButtonPress:
1525 if (d->pressed) // we are already pressed - this is a delayed replay
1528 d->handleMousePressEvent(&mouseEvent);
1529 d->captureDelayedPress(event);
1530 stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
1532 case QEvent::MouseButtonRelease:
1533 if (d->delayedPressEvent) {
1534 // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
1535 // so we reset the grabber
1536 if (c->mouseGrabberItem() == d->delayedPressTarget)
1537 d->delayedPressTarget->ungrabMouse();
1538 //Use the event handler that will take care of finding the proper item to propagate the event
1539 QSGCanvasPrivate::get(canvas())->deliverMouseEvent(d->delayedPressEvent);
1540 d->clearDelayedPress();
1541 // We send the release
1542 canvas()->sendEvent(c->mouseGrabberItem(), event);
1543 // And the event has been consumed
1544 d->stealMouse = false;
1548 d->handleMouseReleaseEvent(&mouseEvent);
1553 grabber = qobject_cast<QSGItem*>(c->mouseGrabberItem());
1554 if ((grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) || disabledItem) {
1555 d->clearDelayedPress();
1559 return stealThisEvent || d->delayedPressEvent || disabledItem;
1560 } else if (d->lastPosTime.isValid()) {
1561 d->lastPosTime.invalidate();
1564 if (event->type() == QEvent::MouseButtonRelease) {
1565 d->lastPosTime.invalidate();
1566 d->clearDelayedPress();
1567 d->stealMouse = false;
1574 bool QSGFlickable::childMouseEventFilter(QSGItem *i, QEvent *e)
1577 if (!isVisible() || !d->interactive || !isEnabled())
1578 return QSGItem::childMouseEventFilter(i, e);
1579 switch (e->type()) {
1580 case QEvent::MouseButtonPress:
1581 case QEvent::MouseMove:
1582 case QEvent::MouseButtonRelease:
1583 return sendMouseEvent(static_cast<QMouseEvent *>(e));
1588 return QSGItem::childMouseEventFilter(i, e);
1592 \qmlproperty real QtQuick2::Flickable::maximumFlickVelocity
1593 This property holds the maximum velocity that the user can flick the view in pixels/second.
1595 The default value is platform dependent.
1597 qreal QSGFlickable::maximumFlickVelocity() const
1599 Q_D(const QSGFlickable);
1600 return d->maxVelocity;
1603 void QSGFlickable::setMaximumFlickVelocity(qreal v)
1606 if (v == d->maxVelocity)
1609 emit maximumFlickVelocityChanged();
1613 \qmlproperty real QtQuick2::Flickable::flickDeceleration
1614 This property holds the rate at which a flick will decelerate.
1616 The default value is platform dependent.
1618 qreal QSGFlickable::flickDeceleration() const
1620 Q_D(const QSGFlickable);
1621 return d->deceleration;
1624 void QSGFlickable::setFlickDeceleration(qreal deceleration)
1627 if (deceleration == d->deceleration)
1629 d->deceleration = deceleration;
1630 emit flickDecelerationChanged();
1633 bool QSGFlickable::isFlicking() const
1635 Q_D(const QSGFlickable);
1636 return d->flickingHorizontally || d->flickingVertically;
1640 \qmlproperty bool QtQuick2::Flickable::flicking
1641 \qmlproperty bool QtQuick2::Flickable::flickingHorizontally
1642 \qmlproperty bool QtQuick2::Flickable::flickingVertically
1644 These properties describe whether the view is currently moving horizontally,
1645 vertically or in either direction, due to the user flicking the view.
1647 bool QSGFlickable::isFlickingHorizontally() const
1649 Q_D(const QSGFlickable);
1650 return d->flickingHorizontally;
1653 bool QSGFlickable::isFlickingVertically() const
1655 Q_D(const QSGFlickable);
1656 return d->flickingVertically;
1660 \qmlproperty bool QtQuick2::Flickable::dragging
1661 \qmlproperty bool QtQuick2::Flickable::draggingHorizontally
1662 \qmlproperty bool QtQuick2::Flickable::draggingVertically
1664 These properties describe whether the view is currently moving horizontally,
1665 vertically or in either direction, due to the user dragging the view.
1667 bool QSGFlickable::isDragging() const
1669 Q_D(const QSGFlickable);
1670 return d->hData.dragging || d->vData.dragging;
1673 bool QSGFlickable::isDraggingHorizontally() const
1675 Q_D(const QSGFlickable);
1676 return d->hData.dragging;
1679 bool QSGFlickable::isDraggingVertically() const
1681 Q_D(const QSGFlickable);
1682 return d->vData.dragging;
1685 void QSGFlickablePrivate::draggingStarting()
1688 bool wasDragging = hData.dragging || vData.dragging;
1689 if (hMoved && !hData.dragging) {
1690 hData.dragging = true;
1691 emit q->draggingHorizontallyChanged();
1693 if (vMoved && !vData.dragging) {
1694 vData.dragging = true;
1695 emit q->draggingVerticallyChanged();
1697 if (!wasDragging && (hData.dragging || vData.dragging)) {
1698 emit q->draggingChanged();
1699 emit q->dragStarted();
1703 void QSGFlickablePrivate::draggingEnding()
1706 bool wasDragging = hData.dragging || vData.dragging;
1707 if (hData.dragging) {
1708 hData.dragging = false;
1709 emit q->draggingHorizontallyChanged();
1711 if (vData.dragging) {
1712 vData.dragging = false;
1713 emit q->draggingVerticallyChanged();
1715 if (wasDragging && !hData.dragging && !vData.dragging) {
1716 emit q->draggingChanged();
1717 emit q->dragEnded();
1722 \qmlproperty int QtQuick2::Flickable::pressDelay
1724 This property holds the time to delay (ms) delivering a press to
1725 children of the Flickable. This can be useful where reacting
1726 to a press before a flicking action has undesirable effects.
1728 If the flickable is dragged/flicked before the delay times out
1729 the press event will not be delivered. If the button is released
1730 within the timeout, both the press and release will be delivered.
1732 Note that for nested Flickables with pressDelay set, the pressDelay of
1733 inner Flickables is overridden by the outermost Flickable.
1735 int QSGFlickable::pressDelay() const
1737 Q_D(const QSGFlickable);
1738 return d->pressDelay;
1741 void QSGFlickable::setPressDelay(int delay)
1744 if (d->pressDelay == delay)
1746 d->pressDelay = delay;
1747 emit pressDelayChanged();
1751 \qmlproperty bool QtQuick2::Flickable::moving
1752 \qmlproperty bool QtQuick2::Flickable::movingHorizontally
1753 \qmlproperty bool QtQuick2::Flickable::movingVertically
1755 These properties describe whether the view is currently moving horizontally,
1756 vertically or in either direction, due to the user either dragging or
1760 bool QSGFlickable::isMoving() const
1762 Q_D(const QSGFlickable);
1763 return d->movingHorizontally || d->movingVertically;
1766 bool QSGFlickable::isMovingHorizontally() const
1768 Q_D(const QSGFlickable);
1769 return d->movingHorizontally;
1772 bool QSGFlickable::isMovingVertically() const
1774 Q_D(const QSGFlickable);
1775 return d->movingVertically;
1778 void QSGFlickable::movementStarting()
1781 if (d->hMoved && !d->movingHorizontally) {
1782 d->movingHorizontally = true;
1783 emit movingChanged();
1784 emit movingHorizontallyChanged();
1785 if (!d->movingVertically)
1786 emit movementStarted();
1788 else if (d->vMoved && !d->movingVertically) {
1789 d->movingVertically = true;
1790 emit movingChanged();
1791 emit movingVerticallyChanged();
1792 if (!d->movingHorizontally)
1793 emit movementStarted();
1797 void QSGFlickable::movementEnding()
1802 d->hData.smoothVelocity.setValue(0);
1803 d->vData.smoothVelocity.setValue(0);
1806 void QSGFlickable::movementXEnding()
1809 if (d->flickingHorizontally) {
1810 d->flickingHorizontally = false;
1811 emit flickingChanged();
1812 emit flickingHorizontallyChanged();
1813 if (!d->flickingVertically)
1816 if (!d->pressed && !d->stealMouse) {
1817 if (d->movingHorizontally) {
1818 d->movingHorizontally = false;
1820 emit movingChanged();
1821 emit movingHorizontallyChanged();
1822 if (!d->movingVertically)
1823 emit movementEnded();
1826 d->hData.fixingUp = false;
1829 void QSGFlickable::movementYEnding()
1832 if (d->flickingVertically) {
1833 d->flickingVertically = false;
1834 emit flickingChanged();
1835 emit flickingVerticallyChanged();
1836 if (!d->flickingHorizontally)
1839 if (!d->pressed && !d->stealMouse) {
1840 if (d->movingVertically) {
1841 d->movingVertically = false;
1843 emit movingChanged();
1844 emit movingVerticallyChanged();
1845 if (!d->movingHorizontally)
1846 emit movementEnded();
1849 d->vData.fixingUp = false;
1852 void QSGFlickablePrivate::updateVelocity()
1855 emit q->horizontalVelocityChanged();
1856 emit q->verticalVelocityChanged();