1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquickflickable_p.h"
43 #include "qquickflickable_p_p.h"
44 #include "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 // Multiflick acceleration minimum flick velocity threshold
87 #ifndef QML_FLICK_MULTIFLICK_THRESHOLD
88 #define QML_FLICK_MULTIFLICK_THRESHOLD 1250
91 // Multiflick acceleration minimum contentSize/viewSize ratio
92 #ifndef QML_FLICK_MULTIFLICK_RATIO
93 #define QML_FLICK_MULTIFLICK_RATIO 10
96 // Multiflick acceleration maximum velocity multiplier
97 #ifndef QML_FLICK_MULTIFLICK_MAXBOOST
98 #define QML_FLICK_MULTIFLICK_MAXBOOST 3.0
101 // FlickThreshold determines how far the "mouse" must have moved
102 // before we perform a flick.
103 static const int FlickThreshold = 20;
105 // RetainGrabVelocity is the maxmimum instantaneous velocity that
106 // will ensure the Flickable retains the grab on consecutive flicks.
107 static const int RetainGrabVelocity = 15;
109 QQuickFlickableVisibleArea::QQuickFlickableVisibleArea(QQuickFlickable *parent)
110 : QObject(parent), flickable(parent), m_xPosition(0.), m_widthRatio(0.)
111 , m_yPosition(0.), m_heightRatio(0.)
115 qreal QQuickFlickableVisibleArea::widthRatio() const
120 qreal QQuickFlickableVisibleArea::xPosition() const
125 qreal QQuickFlickableVisibleArea::heightRatio() const
127 return m_heightRatio;
130 qreal QQuickFlickableVisibleArea::yPosition() const
135 void QQuickFlickableVisibleArea::updateVisible()
137 QQuickFlickablePrivate *p = QQuickFlickablePrivate::get(flickable);
139 bool changeX = false;
140 bool changeY = false;
141 bool changeWidth = false;
142 bool changeHeight = false;
145 const qreal viewheight = flickable->height();
146 const qreal maxyextent = -flickable->maxYExtent() + flickable->minYExtent();
147 qreal pagePos = (-p->vData.move.value() + flickable->minYExtent()) / (maxyextent + viewheight);
148 qreal pageSize = viewheight / (maxyextent + viewheight);
150 if (pageSize != m_heightRatio) {
151 m_heightRatio = pageSize;
154 if (pagePos != m_yPosition) {
155 m_yPosition = pagePos;
160 const qreal viewwidth = flickable->width();
161 const qreal maxxextent = -flickable->maxXExtent() + flickable->minXExtent();
162 pagePos = (-p->hData.move.value() + flickable->minXExtent()) / (maxxextent + viewwidth);
163 pageSize = viewwidth / (maxxextent + viewwidth);
165 if (pageSize != m_widthRatio) {
166 m_widthRatio = pageSize;
169 if (pagePos != m_xPosition) {
170 m_xPosition = pagePos;
175 emit xPositionChanged(m_xPosition);
177 emit yPositionChanged(m_yPosition);
179 emit widthRatioChanged(m_widthRatio);
181 emit heightRatioChanged(m_heightRatio);
185 QQuickFlickablePrivate::QQuickFlickablePrivate()
186 : contentItem(new QQuickItem)
187 , hData(this, &QQuickFlickablePrivate::setViewportX)
188 , vData(this, &QQuickFlickablePrivate::setViewportY)
189 , hMoved(false), vMoved(false)
190 , stealMouse(false), pressed(false), interactive(true), calcVelocity(false)
191 , pixelAligned(false)
194 , deceleration(QML_FLICK_DEFAULTDECELERATION)
195 , maxVelocity(QML_FLICK_DEFAULTMAXVELOCITY), reportedVelocitySmoothing(100)
196 , delayedPressEvent(0), delayedPressTarget(0), pressDelay(0), fixupDuration(400)
197 , flickBoost(1.0), fixupMode(Normal), vTime(0), visibleArea(0)
198 , flickableDirection(QQuickFlickable::AutoFlickDirection)
199 , boundsBehavior(QQuickFlickable::DragAndOvershootBounds)
203 void QQuickFlickablePrivate::init()
205 Q_Q(QQuickFlickable);
206 QDeclarative_setParent_noEvent(contentItem, q);
207 contentItem->setParentItem(q);
208 FAST_CONNECT(&timeline, SIGNAL(completed()), q, SLOT(movementEnding()))
209 q->setAcceptedMouseButtons(Qt::LeftButton);
210 q->setFiltersChildMouseEvents(true);
211 QQuickItemPrivate *viewportPrivate = QQuickItemPrivate::get(contentItem);
212 viewportPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
216 Returns the amount to overshoot by given a velocity.
217 Will be roughly in range 0 - size/4
219 qreal QQuickFlickablePrivate::overShootDistance(qreal size)
221 if (maxVelocity <= 0)
224 return qMin(qreal(QML_FLICK_OVERSHOOT), size/3);
227 void QQuickFlickablePrivate::AxisData::addVelocitySample(qreal v, qreal maxVelocity)
231 else if (v < -maxVelocity)
233 velocityBuffer.append(v);
234 if (velocityBuffer.count() > QML_FLICK_SAMPLEBUFFER)
235 velocityBuffer.remove(0);
238 void QQuickFlickablePrivate::AxisData::updateVelocity()
241 if (velocityBuffer.count() > QML_FLICK_DISCARDSAMPLES) {
242 int count = velocityBuffer.count()-QML_FLICK_DISCARDSAMPLES;
243 for (int i = 0; i < count; ++i) {
244 qreal v = velocityBuffer.at(i);
251 void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeom, const QRectF &oldGeom)
253 Q_Q(QQuickFlickable);
254 if (item == contentItem) {
255 bool xChanged = newGeom.x() != oldGeom.x();
256 bool yChanged = newGeom.y() != oldGeom.y();
257 if (xChanged || yChanged)
260 emit q->contentXChanged();
262 emit q->contentYChanged();
266 void QQuickFlickablePrivate::flickX(qreal velocity)
268 Q_Q(QQuickFlickable);
269 flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, velocity);
272 void QQuickFlickablePrivate::flickY(qreal velocity)
274 Q_Q(QQuickFlickable);
275 flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, velocity);
278 void QQuickFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal,
279 QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
281 Q_Q(QQuickFlickable);
282 qreal maxDistance = -1;
283 data.fixingUp = false;
284 // -ve velocity means list is moving up
286 maxDistance = qAbs(minExtent - data.move.value());
287 data.flickTarget = minExtent;
289 maxDistance = qAbs(maxExtent - data.move.value());
290 data.flickTarget = maxExtent;
292 if (maxDistance > 0) {
294 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
300 timeline.reset(data.move);
301 if (boundsBehavior == QQuickFlickable::DragAndOvershootBounds)
302 timeline.accel(data.move, v, deceleration);
304 timeline.accel(data.move, v, deceleration, maxDistance);
305 timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
306 if (!hData.flicking && q->xflick()) {
307 hData.flicking = true;
308 emit q->flickingChanged();
309 emit q->flickingHorizontallyChanged();
311 emit q->flickStarted();
313 if (!vData.flicking && q->yflick()) {
314 vData.flicking = true;
315 emit q->flickingChanged();
316 emit q->flickingVerticallyChanged();
318 emit q->flickStarted();
321 timeline.reset(data.move);
322 fixup(data, minExtent, maxExtent);
326 void QQuickFlickablePrivate::fixupY_callback(void *data)
328 ((QQuickFlickablePrivate *)data)->fixupY();
331 void QQuickFlickablePrivate::fixupX_callback(void *data)
333 ((QQuickFlickablePrivate *)data)->fixupX();
336 void QQuickFlickablePrivate::fixupX()
338 Q_Q(QQuickFlickable);
339 fixup(hData, q->minXExtent(), q->maxXExtent());
342 void QQuickFlickablePrivate::fixupY()
344 Q_Q(QQuickFlickable);
345 fixup(vData, q->minYExtent(), q->maxYExtent());
348 void QQuickFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
350 if (data.move.value() > minExtent || maxExtent > minExtent) {
351 timeline.reset(data.move);
352 if (data.move.value() != minExtent) {
355 timeline.set(data.move, minExtent);
358 // The target has changed. Don't start from the beginning; just complete the
359 // second half of the animation using the new extent.
360 timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
361 data.fixingUp = true;
364 qreal dist = minExtent - data.move;
365 timeline.move(data.move, minExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4);
366 timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
367 data.fixingUp = true;
371 } else if (data.move.value() < maxExtent) {
372 timeline.reset(data.move);
375 timeline.set(data.move, maxExtent);
378 // The target has changed. Don't start from the beginning; just complete the
379 // second half of the animation using the new extent.
380 timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
381 data.fixingUp = true;
384 qreal dist = maxExtent - data.move;
385 timeline.move(data.move, maxExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4);
386 timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
387 data.fixingUp = true;
391 data.inOvershoot = false;
393 vTime = timeline.time();
396 void QQuickFlickablePrivate::updateBeginningEnd()
398 Q_Q(QQuickFlickable);
399 bool atBoundaryChange = false;
402 const int maxyextent = int(-q->maxYExtent());
403 const qreal ypos = -vData.move.value();
404 bool atBeginning = (ypos <= -q->minYExtent());
405 bool atEnd = (maxyextent <= ypos);
407 if (atBeginning != vData.atBeginning) {
408 vData.atBeginning = atBeginning;
409 atBoundaryChange = true;
411 if (atEnd != vData.atEnd) {
413 atBoundaryChange = true;
417 const int maxxextent = int(-q->maxXExtent());
418 const qreal xpos = -hData.move.value();
419 atBeginning = (xpos <= -q->minXExtent());
420 atEnd = (maxxextent <= xpos);
422 if (atBeginning != hData.atBeginning) {
423 hData.atBeginning = atBeginning;
424 atBoundaryChange = true;
426 if (atEnd != hData.atEnd) {
428 atBoundaryChange = true;
431 if (vData.extentsChanged) {
432 vData.extentsChanged = false;
433 emit q->yOriginChanged();
436 if (hData.extentsChanged) {
437 hData.extentsChanged = false;
438 emit q->xOriginChanged();
441 if (atBoundaryChange)
442 emit q->isAtBoundaryChanged();
445 visibleArea->updateVisible();
449 XXXTODO add docs describing moving, dragging, flicking properties, e.g.
451 When the user starts dragging the Flickable, the dragging and moving properties
454 If the velocity is sufficient when the drag is ended, flicking may begin.
456 The moving properties will remain true until all dragging and flicking
461 \qmlsignal QtQuick2::Flickable::onDragStarted()
463 This handler is called when the view starts to be dragged due to user
468 \qmlsignal QtQuick2::Flickable::onDragEnded()
470 This handler is called when the user stops dragging the view.
472 If the velocity of the drag is suffient at the time the
473 touch/mouse button is released then a flick will start.
477 \qmlclass Flickable QQuickFlickable
478 \inqmlmodule QtQuick 2
479 \ingroup qml-basic-interaction-elements
481 \brief The Flickable item provides a surface that can be "flicked".
484 The Flickable item places its children on a surface that can be dragged
485 and flicked, causing the view onto the child items to scroll. This
486 behavior forms the basis of Items that are designed to show large numbers
487 of child items, such as \l ListView and \l GridView.
489 In traditional user interfaces, views can be scrolled using standard
490 controls, such as scroll bars and arrow buttons. In some situations, it
491 is also possible to drag the view directly by pressing and holding a
492 mouse button while moving the cursor. In touch-based user interfaces,
493 this dragging action is often complemented with a flicking action, where
494 scrolling continues after the user has stopped touching the view.
496 Flickable does not automatically clip its contents. If it is not used as
497 a full-screen item, you should consider setting the \l{Item::}{clip} property
500 \section1 Example Usage
502 \div {class="float-right"}
503 \inlineimage flickable.gif
506 The following example shows a small view onto a large image in which the
507 user can drag or flick the image in order to view different parts of it.
509 \snippet doc/src/snippets/declarative/flickable.qml document
513 Items declared as children of a Flickable are automatically parented to the
514 Flickable's \l contentItem. This should be taken into account when
515 operating on the children of the Flickable; it is usually the children of
516 \c contentItem that are relevant. For example, the bound of Items added
517 to the Flickable will be available by \c contentItem.childrenRect
519 \section1 Limitations
521 \note Due to an implementation detail, items placed inside a Flickable cannot anchor to it by
522 \c id. Use \c parent instead.
526 \qmlsignal QtQuick2::Flickable::onMovementStarted()
528 This handler is called when the view begins moving due to user
533 \qmlsignal QtQuick2::Flickable::onMovementEnded()
535 This handler is called when the view stops moving due to user
536 interaction. If a flick was generated, this handler will
537 be triggered once the flick stops. If a flick was not
538 generated, the handler will be triggered when the
539 user stops dragging - i.e. a mouse or touch release.
543 \qmlsignal QtQuick2::Flickable::onFlickStarted()
545 This handler is called when the view is flicked. A flick
546 starts from the point that the mouse or touch is released,
547 while still in motion.
551 \qmlsignal QtQuick2::Flickable::onFlickEnded()
553 This handler is called when the view stops moving due to a flick.
557 \qmlproperty real QtQuick2::Flickable::visibleArea.xPosition
558 \qmlproperty real QtQuick2::Flickable::visibleArea.widthRatio
559 \qmlproperty real QtQuick2::Flickable::visibleArea.yPosition
560 \qmlproperty real QtQuick2::Flickable::visibleArea.heightRatio
562 These properties describe the position and size of the currently viewed area.
563 The size is defined as the percentage of the full view currently visible,
564 scaled to 0.0 - 1.0. The page position is usually in the range 0.0 (beginning) to
565 1.0 minus size ratio (end), i.e. \c yPosition is in the range 0.0 to 1.0-\c heightRatio.
566 However, it is possible for the contents to be dragged outside of the normal
567 range, resulting in the page positions also being outside the normal range.
569 These properties are typically used to draw a scrollbar. For example:
571 \snippet doc/src/snippets/declarative/flickableScrollbar.qml 0
573 \snippet doc/src/snippets/declarative/flickableScrollbar.qml 1
575 \sa {declarative/ui-components/scrollbar}{scrollbar example}
577 QQuickFlickable::QQuickFlickable(QQuickItem *parent)
578 : QQuickItem(*(new QQuickFlickablePrivate), parent)
580 Q_D(QQuickFlickable);
584 QQuickFlickable::QQuickFlickable(QQuickFlickablePrivate &dd, QQuickItem *parent)
585 : QQuickItem(dd, parent)
587 Q_D(QQuickFlickable);
591 QQuickFlickable::~QQuickFlickable()
596 \qmlproperty real QtQuick2::Flickable::contentX
597 \qmlproperty real QtQuick2::Flickable::contentY
599 These properties hold the surface coordinate currently at the top-left
600 corner of the Flickable. For example, if you flick an image up 100 pixels,
601 \c contentY will be 100.
603 qreal QQuickFlickable::contentX() const
605 Q_D(const QQuickFlickable);
606 return -d->contentItem->x();
609 void QQuickFlickable::setContentX(qreal pos)
611 Q_D(QQuickFlickable);
612 d->hData.explicitValue = true;
613 d->timeline.reset(d->hData.move);
614 d->vTime = d->timeline.time();
616 if (-pos != d->hData.move.value())
617 d->hData.move.setValue(-pos);
620 qreal QQuickFlickable::contentY() const
622 Q_D(const QQuickFlickable);
623 return -d->contentItem->y();
626 void QQuickFlickable::setContentY(qreal pos)
628 Q_D(QQuickFlickable);
629 d->vData.explicitValue = true;
630 d->timeline.reset(d->vData.move);
631 d->vTime = d->timeline.time();
633 if (-pos != d->vData.move.value())
634 d->vData.move.setValue(-pos);
638 \qmlproperty bool QtQuick2::Flickable::interactive
640 This property describes whether the user can interact with the Flickable.
641 A user cannot drag or flick a Flickable that is not interactive.
643 By default, this property is true.
645 This property is useful for temporarily disabling flicking. This allows
646 special interaction with Flickable's children; for example, you might want
647 to freeze a flickable map while scrolling through a pop-up dialog that
648 is a child of the Flickable.
650 bool QQuickFlickable::isInteractive() const
652 Q_D(const QQuickFlickable);
653 return d->interactive;
656 void QQuickFlickable::setInteractive(bool interactive)
658 Q_D(QQuickFlickable);
659 if (interactive != d->interactive) {
660 d->interactive = interactive;
661 if (!interactive && (d->hData.flicking || d->vData.flicking)) {
663 d->vTime = d->timeline.time();
664 d->hData.flicking = false;
665 d->vData.flicking = false;
666 emit flickingChanged();
667 emit flickingHorizontallyChanged();
668 emit flickingVerticallyChanged();
671 emit interactiveChanged();
676 \qmlproperty real QtQuick2::Flickable::horizontalVelocity
677 \qmlproperty real QtQuick2::Flickable::verticalVelocity
679 The instantaneous velocity of movement along the x and y axes, in pixels/sec.
681 The reported velocity is smoothed to avoid erratic output.
683 Note that for views with a large content size (more than 10 times the view size),
684 the velocity of the flick may exceed the velocity of the touch in the case
685 of multiple quick consecutive flicks. This allows the user to flick faster
686 through large content.
688 qreal QQuickFlickable::horizontalVelocity() const
690 Q_D(const QQuickFlickable);
691 return d->hData.smoothVelocity.value();
694 qreal QQuickFlickable::verticalVelocity() const
696 Q_D(const QQuickFlickable);
697 return d->vData.smoothVelocity.value();
701 \qmlproperty bool QtQuick2::Flickable::atXBeginning
702 \qmlproperty bool QtQuick2::Flickable::atXEnd
703 \qmlproperty bool QtQuick2::Flickable::atYBeginning
704 \qmlproperty bool QtQuick2::Flickable::atYEnd
706 These properties are true if the flickable view is positioned at the beginning,
709 bool QQuickFlickable::isAtXEnd() const
711 Q_D(const QQuickFlickable);
712 return d->hData.atEnd;
715 bool QQuickFlickable::isAtXBeginning() const
717 Q_D(const QQuickFlickable);
718 return d->hData.atBeginning;
721 bool QQuickFlickable::isAtYEnd() const
723 Q_D(const QQuickFlickable);
724 return d->vData.atEnd;
727 bool QQuickFlickable::isAtYBeginning() const
729 Q_D(const QQuickFlickable);
730 return d->vData.atBeginning;
734 \qmlproperty Item QtQuick2::Flickable::contentItem
736 The internal item that contains the Items to be moved in the Flickable.
738 Items declared as children of a Flickable are automatically parented to the Flickable's contentItem.
740 Items created dynamically need to be explicitly parented to the \e contentItem:
744 function addItem(file) {
745 var component = Qt.createComponent(file)
746 component.createObject(myFlickable.contentItem);
751 QQuickItem *QQuickFlickable::contentItem()
753 Q_D(QQuickFlickable);
754 return d->contentItem;
757 QQuickFlickableVisibleArea *QQuickFlickable::visibleArea()
759 Q_D(QQuickFlickable);
761 d->visibleArea = new QQuickFlickableVisibleArea(this);
762 return d->visibleArea;
766 \qmlproperty enumeration QtQuick2::Flickable::flickableDirection
768 This property determines which directions the view can be flicked.
771 \o Flickable.AutoFlickDirection (default) - allows flicking vertically if the
772 \e contentHeight is not equal to the \e height of the Flickable.
773 Allows flicking horizontally if the \e contentWidth is not equal
774 to the \e width of the Flickable.
775 \o Flickable.HorizontalFlick - allows flicking horizontally.
776 \o Flickable.VerticalFlick - allows flicking vertically.
777 \o Flickable.HorizontalAndVerticalFlick - allows flicking in both directions.
780 QQuickFlickable::FlickableDirection QQuickFlickable::flickableDirection() const
782 Q_D(const QQuickFlickable);
783 return d->flickableDirection;
786 void QQuickFlickable::setFlickableDirection(FlickableDirection direction)
788 Q_D(QQuickFlickable);
789 if (direction != d->flickableDirection) {
790 d->flickableDirection = direction;
791 emit flickableDirectionChanged();
795 bool QQuickFlickable::pixelAligned() const
797 Q_D(const QQuickFlickable);
798 return d->pixelAligned;
801 void QQuickFlickable::setPixelAligned(bool align)
803 Q_D(QQuickFlickable);
804 if (align != d->pixelAligned) {
805 d->pixelAligned = align;
806 emit pixelAlignedChanged();
810 qint64 QQuickFlickablePrivate::computeCurrentTime(QInputEvent *event)
812 if (0 != event->timestamp() && QQuickItemPrivate::consistentTime == -1)
813 return event->timestamp();
815 return QQuickItemPrivate::elapsed(timer);
818 void QQuickFlickablePrivate::handleMousePressEvent(QMouseEvent *event)
820 Q_Q(QQuickFlickable);
821 QQuickItemPrivate::start(timer);
822 if (interactive && timeline.isActive()
823 && (qAbs(hData.smoothVelocity.value()) > RetainGrabVelocity
824 || qAbs(vData.smoothVelocity.value()) > RetainGrabVelocity)) {
825 stealMouse = true; // If we've been flicked then steal the click.
826 int flickTime = timeline.time();
827 if (flickTime > 600) {
828 // too long between flicks - cancel boost
829 hData.continuousFlickVelocity = 0;
830 vData.continuousFlickVelocity = 0;
833 hData.continuousFlickVelocity = -hData.smoothVelocity.value();
834 vData.continuousFlickVelocity = -vData.smoothVelocity.value();
835 if (flickTime > 300) // slower flicking - reduce boost
836 flickBoost = qMax(1.0, flickBoost - 0.5);
840 hData.continuousFlickVelocity = 0;
841 vData.continuousFlickVelocity = 0;
844 q->setKeepMouseGrab(stealMouse);
849 hData.dragMinBound = q->minXExtent();
850 vData.dragMinBound = q->minYExtent();
851 hData.dragMaxBound = q->maxXExtent();
852 vData.dragMaxBound = q->maxYExtent();
855 pressPos = event->localPos();
856 hData.pressPos = hData.move.value();
857 vData.pressPos = vData.move.value();
858 hData.flicking = false;
859 vData.flicking = false;
860 lastPosTime = lastPressTime = computeCurrentTime(event);
861 QQuickItemPrivate::start(velocityTime);
864 void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event)
866 Q_Q(QQuickFlickable);
867 if (!interactive || lastPosTime == -1)
869 bool rejectY = false;
870 bool rejectX = false;
872 bool stealY = stealMouse;
873 bool stealX = stealMouse;
875 qint64 elapsed = computeCurrentTime(event) - lastPressTime;
878 qreal dy = event->localPos().y() - pressPos.y();
879 if (qAbs(dy) > qApp->styleHints()->startDragDistance() || elapsed > 200) {
881 vData.dragStartOffset = dy;
882 qreal newY = dy + vData.pressPos - vData.dragStartOffset;
883 const qreal minY = vData.dragMinBound;
884 const qreal maxY = vData.dragMaxBound;
886 newY = minY + (newY - minY) / 2;
887 if (newY < maxY && maxY - minY <= 0)
888 newY = maxY + (newY - maxY) / 2;
889 if (boundsBehavior == QQuickFlickable::StopAtBounds && (newY > minY || newY < maxY)) {
900 if (!rejectY && stealMouse) {
901 vData.move.setValue(qRound(newY));
904 if (qAbs(dy) > qApp->styleHints()->startDragDistance())
910 qreal dx = event->localPos().x() - pressPos.x();
911 if (qAbs(dx) > qApp->styleHints()->startDragDistance() || elapsed > 200) {
913 hData.dragStartOffset = dx;
914 qreal newX = dx + hData.pressPos - hData.dragStartOffset;
915 const qreal minX = hData.dragMinBound;
916 const qreal maxX = hData.dragMaxBound;
918 newX = minX + (newX - minX) / 2;
919 if (newX < maxX && maxX - minX <= 0)
920 newX = maxX + (newX - maxX) / 2;
921 if (boundsBehavior == QQuickFlickable::StopAtBounds && (newX > minX || newX < maxX)) {
932 if (!rejectX && stealMouse) {
933 hData.move.setValue(qRound(newX));
937 if (qAbs(dx) > qApp->styleHints()->startDragDistance())
942 stealMouse = stealX || stealY;
944 q->setKeepMouseGrab(true);
947 vData.velocityBuffer.clear();
951 hData.velocityBuffer.clear();
955 if (hMoved || vMoved) {
957 q->movementStarting();
960 if (!lastPos.isNull()) {
961 qint64 currentTimestamp = computeCurrentTime(event);
962 qreal elapsed = qreal(currentTimestamp - lastPosTime) / 1000.;
965 lastPosTime = currentTimestamp;
966 qreal dy = event->localPos().y()-lastPos.y();
967 if (q->yflick() && !rejectY)
968 vData.addVelocitySample(dy/elapsed, maxVelocity);
969 qreal dx = event->localPos().x()-lastPos.x();
970 if (q->xflick() && !rejectX)
971 hData.addVelocitySample(dx/elapsed, maxVelocity);
974 lastPos = event->localPos();
977 void QQuickFlickablePrivate::handleMouseReleaseEvent(QMouseEvent *event)
979 Q_Q(QQuickFlickable);
981 q->setKeepMouseGrab(false);
984 // if we drag then pause before release we should not cause a flick.
985 qint64 elapsed = computeCurrentTime(event) - lastPosTime;
987 vData.updateVelocity();
988 hData.updateVelocity();
992 if (lastPosTime == -1)
995 vTime = timeline.time();
997 bool canBoost = false;
999 qreal vVelocity = elapsed < 100 ? vData.velocity : 0;
1000 if (vData.atBeginning || vData.atEnd) {
1002 } else if (vData.continuousFlickVelocity != 0.0
1003 && vData.viewSize/q->height() > QML_FLICK_MULTIFLICK_RATIO
1004 && ((vVelocity > 0) == (vData.continuousFlickVelocity > 0))
1005 && qAbs(vVelocity) > QML_FLICK_MULTIFLICK_THRESHOLD) {
1006 // accelerate flick for large view flicked quickly
1010 qreal hVelocity = elapsed < 100 ? hData.velocity : 0;
1011 if (hData.atBeginning || hData.atEnd) {
1013 } else if (hData.continuousFlickVelocity != 0.0
1014 && hData.viewSize/q->width() > QML_FLICK_MULTIFLICK_RATIO
1015 && ((hVelocity > 0) == (hData.continuousFlickVelocity > 0))
1016 && qAbs(hVelocity) > QML_FLICK_MULTIFLICK_THRESHOLD) {
1017 // accelerate flick for large view flicked quickly
1021 flickBoost = canBoost ? qBound(1.0, flickBoost+0.25, QML_FLICK_MULTIFLICK_MAXBOOST) : 1.0;
1023 vVelocity *= flickBoost;
1024 if (q->yflick() && qAbs(vVelocity) > MinimumFlickVelocity && qAbs(event->localPos().y() - pressPos.y()) > FlickThreshold) {
1025 velocityTimeline.reset(vData.smoothVelocity);
1026 vData.smoothVelocity.setValue(-vVelocity);
1032 hVelocity *= flickBoost;
1033 if (q->xflick() && qAbs(hVelocity) > MinimumFlickVelocity && qAbs(event->localPos().x() - pressPos.x()) > FlickThreshold) {
1034 velocityTimeline.reset(hData.smoothVelocity);
1035 hData.smoothVelocity.setValue(-hVelocity);
1041 if (!timeline.isActive())
1042 q->movementEnding();
1045 void QQuickFlickable::mousePressEvent(QMouseEvent *event)
1047 Q_D(QQuickFlickable);
1048 if (d->interactive) {
1050 d->handleMousePressEvent(event);
1053 QQuickItem::mousePressEvent(event);
1057 void QQuickFlickable::mouseMoveEvent(QMouseEvent *event)
1059 Q_D(QQuickFlickable);
1060 if (d->interactive) {
1061 d->handleMouseMoveEvent(event);
1064 QQuickItem::mouseMoveEvent(event);
1068 void QQuickFlickable::mouseReleaseEvent(QMouseEvent *event)
1070 Q_D(QQuickFlickable);
1071 if (d->interactive) {
1072 d->clearDelayedPress();
1073 d->handleMouseReleaseEvent(event);
1077 QQuickItem::mouseReleaseEvent(event);
1081 void QQuickFlickable::wheelEvent(QWheelEvent *event)
1083 Q_D(QQuickFlickable);
1084 if (!d->interactive) {
1085 QQuickItem::wheelEvent(event);
1086 } else if (yflick() && event->orientation() == Qt::Vertical) {
1088 if (event->delta() > 0 && contentY() > -minYExtent()) {
1089 d->vData.velocity = qMax(event->delta()*2 - d->vData.smoothVelocity.value(), qreal(d->maxVelocity/4));
1091 } else if (event->delta() < 0 && contentY() < -maxYExtent()) {
1092 d->vData.velocity = qMin(event->delta()*2 - d->vData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
1096 d->vData.flicking = false;
1097 d->flickY(d->vData.velocity);
1098 if (d->vData.flicking) {
1104 } else if (xflick() && event->orientation() == Qt::Horizontal) {
1106 if (event->delta() > 0 && contentX() > -minXExtent()) {
1107 d->hData.velocity = qMax(event->delta()*2 - d->hData.smoothVelocity.value(), qreal(d->maxVelocity/4));
1109 } else if (event->delta() < 0 && contentX() < -maxXExtent()) {
1110 d->hData.velocity = qMin(event->delta()*2 - d->hData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
1114 d->hData.flicking = false;
1115 d->flickX(d->hData.velocity);
1116 if (d->hData.flicking) {
1123 QQuickItem::wheelEvent(event);
1127 bool QQuickFlickablePrivate::isOutermostPressDelay() const
1129 Q_Q(const QQuickFlickable);
1130 QQuickItem *item = q->parentItem();
1132 QQuickFlickable *flick = qobject_cast<QQuickFlickable*>(item);
1133 if (flick && flick->pressDelay() > 0 && flick->isInteractive())
1135 item = item->parentItem();
1141 void QQuickFlickablePrivate::captureDelayedPress(QMouseEvent *event)
1143 Q_Q(QQuickFlickable);
1144 if (!q->canvas() || pressDelay <= 0)
1146 if (!isOutermostPressDelay())
1148 delayedPressTarget = q->canvas()->mouseGrabberItem();
1149 delayedPressEvent = new QMouseEvent(*event);
1150 delayedPressEvent->setAccepted(false);
1151 delayedPressTimer.start(pressDelay, q);
1154 void QQuickFlickablePrivate::clearDelayedPress()
1156 if (delayedPressEvent) {
1157 delayedPressTimer.stop();
1158 delete delayedPressEvent;
1159 delayedPressEvent = 0;
1163 //XXX pixelAligned ignores the global position of the Flickable, i.e. assumes Flickable itself is pixel aligned.
1164 void QQuickFlickablePrivate::setViewportX(qreal x)
1166 contentItem->setX(pixelAligned ? qRound(x) : x);
1169 void QQuickFlickablePrivate::setViewportY(qreal y)
1171 contentItem->setY(pixelAligned ? qRound(y) : y);
1174 void QQuickFlickable::timerEvent(QTimerEvent *event)
1176 Q_D(QQuickFlickable);
1177 if (event->timerId() == d->delayedPressTimer.timerId()) {
1178 d->delayedPressTimer.stop();
1179 if (d->delayedPressEvent) {
1180 QQuickItem *grabber = canvas() ? canvas()->mouseGrabberItem() : 0;
1181 if (!grabber || grabber != this) {
1182 // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
1183 // so we reset the grabber
1184 if (canvas()->mouseGrabberItem() == d->delayedPressTarget)
1185 d->delayedPressTarget->ungrabMouse();
1186 // Use the event handler that will take care of finding the proper item to propagate the event
1187 QQuickCanvasPrivate::get(canvas())->deliverMouseEvent(d->delayedPressEvent);
1189 delete d->delayedPressEvent;
1190 d->delayedPressEvent = 0;
1195 qreal QQuickFlickable::minYExtent() const
1197 Q_D(const QQuickFlickable);
1198 return d->vData.startMargin;
1201 qreal QQuickFlickable::minXExtent() const
1203 Q_D(const QQuickFlickable);
1204 return d->hData.startMargin;
1208 qreal QQuickFlickable::maxXExtent() const
1210 Q_D(const QQuickFlickable);
1211 return width() - vWidth() - d->hData.endMargin;
1214 qreal QQuickFlickable::maxYExtent() const
1216 Q_D(const QQuickFlickable);
1217 return height() - vHeight() - d->vData.endMargin;
1220 void QQuickFlickable::componentComplete()
1222 Q_D(QQuickFlickable);
1223 QQuickItem::componentComplete();
1224 if (!d->hData.explicitValue && d->hData.startMargin != 0.)
1225 setContentX(-minXExtent());
1226 if (!d->vData.explicitValue && d->vData.startMargin != 0.)
1227 setContentY(-minYExtent());
1230 void QQuickFlickable::viewportMoved()
1232 Q_D(QQuickFlickable);
1234 qreal prevX = d->lastFlickablePosition.x();
1235 qreal prevY = d->lastFlickablePosition.y();
1236 if (d->pressed || d->calcVelocity) {
1237 int elapsed = QQuickItemPrivate::restart(d->velocityTime);
1239 qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / elapsed;
1240 if (qAbs(horizontalVelocity) > 0) {
1241 d->velocityTimeline.reset(d->hData.smoothVelocity);
1242 if (d->calcVelocity)
1243 d->velocityTimeline.set(d->hData.smoothVelocity, horizontalVelocity);
1245 d->velocityTimeline.move(d->hData.smoothVelocity, horizontalVelocity, d->reportedVelocitySmoothing);
1246 d->velocityTimeline.move(d->hData.smoothVelocity, 0, d->reportedVelocitySmoothing);
1248 qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / elapsed;
1249 if (qAbs(verticalVelocity) > 0) {
1250 d->velocityTimeline.reset(d->vData.smoothVelocity);
1251 if (d->calcVelocity)
1252 d->velocityTimeline.set(d->vData.smoothVelocity, verticalVelocity);
1254 d->velocityTimeline.move(d->vData.smoothVelocity, verticalVelocity, d->reportedVelocitySmoothing);
1255 d->velocityTimeline.move(d->vData.smoothVelocity, 0, d->reportedVelocitySmoothing);
1259 if (d->timeline.time() > d->vTime) {
1260 d->velocityTimeline.clear();
1261 qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / (d->timeline.time() - d->vTime);
1262 qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / (d->timeline.time() - d->vTime);
1263 d->hData.smoothVelocity.setValue(horizontalVelocity);
1264 d->vData.smoothVelocity.setValue(verticalVelocity);
1268 if (!d->vData.inOvershoot && !d->vData.fixingUp && d->vData.flicking
1269 && (d->vData.move.value() > minYExtent() || d->vData.move.value() < maxYExtent())
1270 && qAbs(d->vData.smoothVelocity.value()) > 100) {
1271 // Increase deceleration if we've passed a bound
1272 d->vData.inOvershoot = true;
1273 qreal maxDistance = d->overShootDistance(height());
1274 d->timeline.reset(d->vData.move);
1275 d->timeline.accel(d->vData.move, -d->vData.smoothVelocity.value(), d->deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance);
1276 d->timeline.callback(QDeclarativeTimeLineCallback(&d->vData.move, d->fixupY_callback, d));
1278 if (!d->hData.inOvershoot && !d->hData.fixingUp && d->hData.flicking
1279 && (d->hData.move.value() > minXExtent() || d->hData.move.value() < maxXExtent())
1280 && qAbs(d->hData.smoothVelocity.value()) > 100) {
1281 // Increase deceleration if we've passed a bound
1282 d->hData.inOvershoot = true;
1283 qreal maxDistance = d->overShootDistance(width());
1284 d->timeline.reset(d->hData.move);
1285 d->timeline.accel(d->hData.move, -d->hData.smoothVelocity.value(), d->deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance);
1286 d->timeline.callback(QDeclarativeTimeLineCallback(&d->hData.move, d->fixupX_callback, d));
1289 d->lastFlickablePosition = QPointF(d->hData.move.value(), d->vData.move.value());
1291 d->vTime = d->timeline.time();
1292 d->updateBeginningEnd();
1295 void QQuickFlickable::geometryChanged(const QRectF &newGeometry,
1296 const QRectF &oldGeometry)
1298 Q_D(QQuickFlickable);
1299 QQuickItem::geometryChanged(newGeometry, oldGeometry);
1301 bool changed = false;
1302 if (newGeometry.width() != oldGeometry.width()) {
1305 if (d->hData.viewSize < 0) {
1306 d->contentItem->setWidth(width());
1307 emit contentWidthChanged();
1309 // Make sure that we're entirely in view.
1310 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1311 d->fixupMode = QQuickFlickablePrivate::Immediate;
1315 if (newGeometry.height() != oldGeometry.height()) {
1318 if (d->vData.viewSize < 0) {
1319 d->contentItem->setHeight(height());
1320 emit contentHeightChanged();
1322 // Make sure that we're entirely in view.
1323 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1324 d->fixupMode = QQuickFlickablePrivate::Immediate;
1330 d->updateBeginningEnd();
1334 \qmlmethod QtQuick2::Flickable::flick(qreal xVelocity, qreal yVelocity)
1336 Flicks the content with \a xVelocity horizontally and \a yVelocity vertically in pixels/sec.
1339 void QQuickFlickable::flick(qreal xVelocity, qreal yVelocity)
1341 Q_D(QQuickFlickable);
1342 d->flickX(xVelocity);
1343 d->flickY(yVelocity);
1347 \qmlmethod QtQuick2::Flickable::cancelFlick()
1349 Cancels the current flick animation.
1352 void QQuickFlickable::cancelFlick()
1354 Q_D(QQuickFlickable);
1355 d->timeline.reset(d->hData.move);
1356 d->timeline.reset(d->vData.move);
1360 void QQuickFlickablePrivate::data_append(QDeclarativeListProperty<QObject> *prop, QObject *o)
1362 QQuickItem *i = qobject_cast<QQuickItem *>(o);
1364 i->setParentItem(static_cast<QQuickFlickablePrivate*>(prop->data)->contentItem);
1366 o->setParent(prop->object); // XXX todo - do we want this?
1370 int QQuickFlickablePrivate::data_count(QDeclarativeListProperty<QObject> *)
1376 QObject *QQuickFlickablePrivate::data_at(QDeclarativeListProperty<QObject> *, int)
1382 void QQuickFlickablePrivate::data_clear(QDeclarativeListProperty<QObject> *)
1387 QDeclarativeListProperty<QObject> QQuickFlickable::flickableData()
1389 Q_D(QQuickFlickable);
1390 return QDeclarativeListProperty<QObject>(this, (void *)d, QQuickFlickablePrivate::data_append,
1391 QQuickFlickablePrivate::data_count,
1392 QQuickFlickablePrivate::data_at,
1393 QQuickFlickablePrivate::data_clear);
1396 QDeclarativeListProperty<QQuickItem> QQuickFlickable::flickableChildren()
1398 Q_D(QQuickFlickable);
1399 return QQuickItemPrivate::get(d->contentItem)->children();
1403 \qmlproperty enumeration QtQuick2::Flickable::boundsBehavior
1404 This property holds whether the surface may be dragged
1405 beyond the Flickable's boundaries, or overshoot the
1406 Flickable's boundaries when flicked.
1408 This enables the feeling that the edges of the view are soft,
1409 rather than a hard physical boundary.
1411 The \c boundsBehavior can be one of:
1414 \o Flickable.StopAtBounds - the contents can not be dragged beyond the boundary
1415 of the flickable, and flicks will not overshoot.
1416 \o Flickable.DragOverBounds - the contents can be dragged beyond the boundary
1417 of the Flickable, but flicks will not overshoot.
1418 \o Flickable.DragAndOvershootBounds (default) - the contents can be dragged
1419 beyond the boundary of the Flickable, and can overshoot the
1420 boundary when flicked.
1423 QQuickFlickable::BoundsBehavior QQuickFlickable::boundsBehavior() const
1425 Q_D(const QQuickFlickable);
1426 return d->boundsBehavior;
1429 void QQuickFlickable::setBoundsBehavior(BoundsBehavior b)
1431 Q_D(QQuickFlickable);
1432 if (b == d->boundsBehavior)
1434 d->boundsBehavior = b;
1435 emit boundsBehaviorChanged();
1439 \qmlproperty real QtQuick2::Flickable::contentWidth
1440 \qmlproperty real QtQuick2::Flickable::contentHeight
1442 The dimensions of the content (the surface controlled by Flickable).
1443 This should typically be set to the combined size of the items placed in the
1446 The following snippet shows how these properties are used to display
1447 an image that is larger than the Flickable item itself:
1449 \snippet doc/src/snippets/declarative/flickable.qml document
1451 In some cases, the the content dimensions can be automatically set
1452 using the \l {Item::childrenRect.width}{childrenRect.width}
1453 and \l {Item::childrenRect.height}{childrenRect.height} properties.
1455 qreal QQuickFlickable::contentWidth() const
1457 Q_D(const QQuickFlickable);
1458 return d->hData.viewSize;
1461 void QQuickFlickable::setContentWidth(qreal w)
1463 Q_D(QQuickFlickable);
1464 if (d->hData.viewSize == w)
1466 d->hData.viewSize = w;
1468 d->contentItem->setWidth(width());
1470 d->contentItem->setWidth(w);
1471 d->hData.markExtentsDirty();
1472 // Make sure that we're entirely in view.
1473 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1474 d->fixupMode = QQuickFlickablePrivate::Immediate;
1476 } else if (!d->pressed && d->hData.fixingUp) {
1477 d->fixupMode = QQuickFlickablePrivate::ExtentChanged;
1480 emit contentWidthChanged();
1481 d->updateBeginningEnd();
1484 qreal QQuickFlickable::contentHeight() const
1486 Q_D(const QQuickFlickable);
1487 return d->vData.viewSize;
1490 void QQuickFlickable::setContentHeight(qreal h)
1492 Q_D(QQuickFlickable);
1493 if (d->vData.viewSize == h)
1495 d->vData.viewSize = h;
1497 d->contentItem->setHeight(height());
1499 d->contentItem->setHeight(h);
1500 d->vData.markExtentsDirty();
1501 // Make sure that we're entirely in view.
1502 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1503 d->fixupMode = QQuickFlickablePrivate::Immediate;
1505 } else if (!d->pressed && d->vData.fixingUp) {
1506 d->fixupMode = QQuickFlickablePrivate::ExtentChanged;
1509 emit contentHeightChanged();
1510 d->updateBeginningEnd();
1514 \qmlproperty real QtQuick2::Flickable::topMargin
1515 \qmlproperty real QtQuick2::Flickable::leftMargin
1516 \qmlproperty real QtQuick2::Flickable::bottomMargin
1517 \qmlproperty real QtQuick2::Flickable::rightMargin
1519 These properties hold the margins around the content. This space is reserved
1520 in addition to the contentWidth and contentHeight.
1524 qreal QQuickFlickable::topMargin() const
1526 Q_D(const QQuickFlickable);
1527 return d->vData.startMargin;
1530 void QQuickFlickable::setTopMargin(qreal m)
1532 Q_D(QQuickFlickable);
1533 if (d->vData.startMargin == m)
1535 d->vData.startMargin = m;
1536 d->vData.markExtentsDirty();
1537 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1538 d->fixupMode = QQuickFlickablePrivate::Immediate;
1541 emit topMarginChanged();
1542 d->updateBeginningEnd();
1545 qreal QQuickFlickable::bottomMargin() const
1547 Q_D(const QQuickFlickable);
1548 return d->vData.endMargin;
1551 void QQuickFlickable::setBottomMargin(qreal m)
1553 Q_D(QQuickFlickable);
1554 if (d->vData.endMargin == m)
1556 d->vData.endMargin = m;
1557 d->vData.markExtentsDirty();
1558 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1559 d->fixupMode = QQuickFlickablePrivate::Immediate;
1562 emit bottomMarginChanged();
1563 d->updateBeginningEnd();
1566 qreal QQuickFlickable::leftMargin() const
1568 Q_D(const QQuickFlickable);
1569 return d->hData.startMargin;
1572 void QQuickFlickable::setLeftMargin(qreal m)
1574 Q_D(QQuickFlickable);
1575 if (d->hData.startMargin == m)
1577 d->hData.startMargin = m;
1578 d->hData.markExtentsDirty();
1579 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1580 d->fixupMode = QQuickFlickablePrivate::Immediate;
1583 emit leftMarginChanged();
1584 d->updateBeginningEnd();
1587 qreal QQuickFlickable::rightMargin() const
1589 Q_D(const QQuickFlickable);
1590 return d->hData.endMargin;
1593 void QQuickFlickable::setRightMargin(qreal m)
1595 Q_D(QQuickFlickable);
1596 if (d->hData.endMargin == m)
1598 d->hData.endMargin = m;
1599 d->hData.markExtentsDirty();
1600 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1601 d->fixupMode = QQuickFlickablePrivate::Immediate;
1604 emit rightMarginChanged();
1605 d->updateBeginningEnd();
1609 \qmlproperty real QtQuick2::Flickable::xOrigin
1610 \qmlproperty real QtQuick2::Flickable::yOrigin
1612 These properties hold the origin of the content. This is usually (0,0), however
1613 ListView and GridView may have an arbitrary origin due to delegate size variation,
1614 or item insertion/removal outside the visible region.
1617 qreal QQuickFlickable::yOrigin() const
1619 Q_D(const QQuickFlickable);
1620 return -minYExtent() + d->vData.startMargin;
1623 qreal QQuickFlickable::xOrigin() const
1625 Q_D(const QQuickFlickable);
1626 return -minXExtent() + d->hData.startMargin;
1631 \qmlmethod QtQuick2::Flickable::resizeContent(real width, real height, QPointF center)
1633 Resizes the content to \a width x \a height about \a center.
1635 This does not scale the contents of the Flickable - it only resizes the \l contentWidth
1636 and \l contentHeight.
1638 Resizing the content may result in the content being positioned outside
1639 the bounds of the Flickable. Calling \l returnToBounds() will
1640 move the content back within legal bounds.
1642 void QQuickFlickable::resizeContent(qreal w, qreal h, QPointF center)
1644 Q_D(QQuickFlickable);
1645 if (w != d->hData.viewSize) {
1646 qreal oldSize = d->hData.viewSize;
1647 d->hData.viewSize = w;
1648 d->contentItem->setWidth(w);
1649 emit contentWidthChanged();
1650 if (center.x() != 0) {
1651 qreal pos = center.x() * w / oldSize;
1652 setContentX(contentX() + pos - center.x());
1655 if (h != d->vData.viewSize) {
1656 qreal oldSize = d->vData.viewSize;
1657 d->vData.viewSize = h;
1658 d->contentItem->setHeight(h);
1659 emit contentHeightChanged();
1660 if (center.y() != 0) {
1661 qreal pos = center.y() * h / oldSize;
1662 setContentY(contentY() + pos - center.y());
1665 d->updateBeginningEnd();
1669 \qmlmethod QtQuick2::Flickable::returnToBounds()
1671 Ensures the content is within legal bounds.
1673 This may be called to ensure that the content is within legal bounds
1674 after manually positioning the content.
1676 void QQuickFlickable::returnToBounds()
1678 Q_D(QQuickFlickable);
1683 qreal QQuickFlickable::vWidth() const
1685 Q_D(const QQuickFlickable);
1686 if (d->hData.viewSize < 0)
1689 return d->hData.viewSize;
1692 qreal QQuickFlickable::vHeight() const
1694 Q_D(const QQuickFlickable);
1695 if (d->vData.viewSize < 0)
1698 return d->vData.viewSize;
1701 bool QQuickFlickable::xflick() const
1703 Q_D(const QQuickFlickable);
1704 if (d->flickableDirection == QQuickFlickable::AutoFlickDirection)
1705 return vWidth() != width();
1706 return d->flickableDirection & QQuickFlickable::HorizontalFlick;
1709 bool QQuickFlickable::yflick() const
1711 Q_D(const QQuickFlickable);
1712 if (d->flickableDirection == QQuickFlickable::AutoFlickDirection)
1713 return vHeight() != height();
1714 return d->flickableDirection & QQuickFlickable::VerticalFlick;
1717 void QQuickFlickable::mouseUngrabEvent()
1719 Q_D(QQuickFlickable);
1721 // if our mouse grab has been removed (probably by another Flickable),
1724 d->draggingEnding();
1725 d->stealMouse = false;
1726 setKeepMouseGrab(false);
1730 bool QQuickFlickable::sendMouseEvent(QMouseEvent *event)
1732 Q_D(QQuickFlickable);
1733 QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
1735 QQuickCanvas *c = canvas();
1736 QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
1737 bool disabledItem = grabber && !grabber->isEnabled();
1738 bool stealThisEvent = d->stealMouse;
1739 if ((stealThisEvent || myRect.contains(event->windowPos())) && (!grabber || !grabber->keepMouseGrab() || disabledItem)) {
1740 QMouseEvent mouseEvent(event->type(), mapFromScene(event->windowPos()), event->windowPos(), event->screenPos(),
1741 event->button(), event->buttons(), event->modifiers());
1743 mouseEvent.setTimestamp(event->timestamp());
1744 mouseEvent.setAccepted(false);
1746 switch (mouseEvent.type()) {
1747 case QEvent::MouseMove:
1748 d->handleMouseMoveEvent(&mouseEvent);
1750 case QEvent::MouseButtonPress:
1751 if (d->pressed) // we are already pressed - this is a delayed replay
1754 d->handleMousePressEvent(&mouseEvent);
1755 d->captureDelayedPress(event);
1756 stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
1758 case QEvent::MouseButtonRelease:
1759 if (d->delayedPressEvent) {
1760 // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
1761 // so we reset the grabber
1762 if (c->mouseGrabberItem() == d->delayedPressTarget)
1763 d->delayedPressTarget->ungrabMouse();
1764 //Use the event handler that will take care of finding the proper item to propagate the event
1765 QQuickCanvasPrivate::get(canvas())->deliverMouseEvent(d->delayedPressEvent);
1766 d->clearDelayedPress();
1767 // We send the release
1768 canvas()->sendEvent(c->mouseGrabberItem(), event);
1769 // And the event has been consumed
1770 d->stealMouse = false;
1774 d->handleMouseReleaseEvent(&mouseEvent);
1779 grabber = qobject_cast<QQuickItem*>(c->mouseGrabberItem());
1780 if ((grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) || disabledItem) {
1781 d->clearDelayedPress();
1785 return stealThisEvent || d->delayedPressEvent || disabledItem;
1786 } else if (d->lastPosTime != -1) {
1787 d->lastPosTime = -1;
1790 if (event->type() == QEvent::MouseButtonRelease) {
1791 d->lastPosTime = -1;
1792 d->clearDelayedPress();
1793 d->stealMouse = false;
1800 bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e)
1802 Q_D(QQuickFlickable);
1803 if (!isVisible() || !d->interactive || !isEnabled())
1804 return QQuickItem::childMouseEventFilter(i, e);
1805 switch (e->type()) {
1806 case QEvent::MouseButtonPress:
1807 case QEvent::MouseMove:
1808 case QEvent::MouseButtonRelease:
1809 return sendMouseEvent(static_cast<QMouseEvent *>(e));
1814 return QQuickItem::childMouseEventFilter(i, e);
1818 \qmlproperty real QtQuick2::Flickable::maximumFlickVelocity
1819 This property holds the maximum velocity that the user can flick the view in pixels/second.
1821 The default value is platform dependent.
1823 qreal QQuickFlickable::maximumFlickVelocity() const
1825 Q_D(const QQuickFlickable);
1826 return d->maxVelocity;
1829 void QQuickFlickable::setMaximumFlickVelocity(qreal v)
1831 Q_D(QQuickFlickable);
1832 if (v == d->maxVelocity)
1835 emit maximumFlickVelocityChanged();
1839 \qmlproperty real QtQuick2::Flickable::flickDeceleration
1840 This property holds the rate at which a flick will decelerate.
1842 The default value is platform dependent.
1844 qreal QQuickFlickable::flickDeceleration() const
1846 Q_D(const QQuickFlickable);
1847 return d->deceleration;
1850 void QQuickFlickable::setFlickDeceleration(qreal deceleration)
1852 Q_D(QQuickFlickable);
1853 if (deceleration == d->deceleration)
1855 d->deceleration = deceleration;
1856 emit flickDecelerationChanged();
1859 bool QQuickFlickable::isFlicking() const
1861 Q_D(const QQuickFlickable);
1862 return d->hData.flicking || d->vData.flicking;
1866 \qmlproperty bool QtQuick2::Flickable::flicking
1867 \qmlproperty bool QtQuick2::Flickable::flickingHorizontally
1868 \qmlproperty bool QtQuick2::Flickable::flickingVertically
1870 These properties describe whether the view is currently moving horizontally,
1871 vertically or in either direction, due to the user flicking the view.
1873 bool QQuickFlickable::isFlickingHorizontally() const
1875 Q_D(const QQuickFlickable);
1876 return d->hData.flicking;
1879 bool QQuickFlickable::isFlickingVertically() const
1881 Q_D(const QQuickFlickable);
1882 return d->vData.flicking;
1886 \qmlproperty bool QtQuick2::Flickable::dragging
1887 \qmlproperty bool QtQuick2::Flickable::draggingHorizontally
1888 \qmlproperty bool QtQuick2::Flickable::draggingVertically
1890 These properties describe whether the view is currently moving horizontally,
1891 vertically or in either direction, due to the user dragging the view.
1893 bool QQuickFlickable::isDragging() const
1895 Q_D(const QQuickFlickable);
1896 return d->hData.dragging || d->vData.dragging;
1899 bool QQuickFlickable::isDraggingHorizontally() const
1901 Q_D(const QQuickFlickable);
1902 return d->hData.dragging;
1905 bool QQuickFlickable::isDraggingVertically() const
1907 Q_D(const QQuickFlickable);
1908 return d->vData.dragging;
1911 void QQuickFlickablePrivate::draggingStarting()
1913 Q_Q(QQuickFlickable);
1914 bool wasDragging = hData.dragging || vData.dragging;
1915 if (hMoved && !hData.dragging) {
1916 hData.dragging = true;
1917 emit q->draggingHorizontallyChanged();
1919 if (vMoved && !vData.dragging) {
1920 vData.dragging = true;
1921 emit q->draggingVerticallyChanged();
1923 if (!wasDragging && (hData.dragging || vData.dragging)) {
1924 emit q->draggingChanged();
1925 emit q->dragStarted();
1929 void QQuickFlickablePrivate::draggingEnding()
1931 Q_Q(QQuickFlickable);
1932 bool wasDragging = hData.dragging || vData.dragging;
1933 if (hData.dragging) {
1934 hData.dragging = false;
1935 emit q->draggingHorizontallyChanged();
1937 if (vData.dragging) {
1938 vData.dragging = false;
1939 emit q->draggingVerticallyChanged();
1941 if (wasDragging && !hData.dragging && !vData.dragging) {
1942 emit q->draggingChanged();
1943 emit q->dragEnded();
1948 \qmlproperty int QtQuick2::Flickable::pressDelay
1950 This property holds the time to delay (ms) delivering a press to
1951 children of the Flickable. This can be useful where reacting
1952 to a press before a flicking action has undesirable effects.
1954 If the flickable is dragged/flicked before the delay times out
1955 the press event will not be delivered. If the button is released
1956 within the timeout, both the press and release will be delivered.
1958 Note that for nested Flickables with pressDelay set, the pressDelay of
1959 inner Flickables is overridden by the outermost Flickable.
1961 int QQuickFlickable::pressDelay() const
1963 Q_D(const QQuickFlickable);
1964 return d->pressDelay;
1967 void QQuickFlickable::setPressDelay(int delay)
1969 Q_D(QQuickFlickable);
1970 if (d->pressDelay == delay)
1972 d->pressDelay = delay;
1973 emit pressDelayChanged();
1977 \qmlproperty bool QtQuick2::Flickable::moving
1978 \qmlproperty bool QtQuick2::Flickable::movingHorizontally
1979 \qmlproperty bool QtQuick2::Flickable::movingVertically
1981 These properties describe whether the view is currently moving horizontally,
1982 vertically or in either direction, due to the user either dragging or
1986 bool QQuickFlickable::isMoving() const
1988 Q_D(const QQuickFlickable);
1989 return d->hData.moving || d->vData.moving;
1992 bool QQuickFlickable::isMovingHorizontally() const
1994 Q_D(const QQuickFlickable);
1995 return d->hData.moving;
1998 bool QQuickFlickable::isMovingVertically() const
2000 Q_D(const QQuickFlickable);
2001 return d->vData.moving;
2004 void QQuickFlickable::movementStarting()
2006 Q_D(QQuickFlickable);
2007 if (d->hMoved && !d->hData.moving) {
2008 d->hData.moving = true;
2009 emit movingChanged();
2010 emit movingHorizontallyChanged();
2011 if (!d->vData.moving)
2012 emit movementStarted();
2014 else if (d->vMoved && !d->vData.moving) {
2015 d->vData.moving = true;
2016 emit movingChanged();
2017 emit movingVerticallyChanged();
2018 if (!d->hData.moving)
2019 emit movementStarted();
2023 void QQuickFlickable::movementEnding()
2025 Q_D(QQuickFlickable);
2028 d->hData.smoothVelocity.setValue(0);
2029 d->vData.smoothVelocity.setValue(0);
2032 void QQuickFlickable::movementXEnding()
2034 Q_D(QQuickFlickable);
2035 if (d->hData.flicking) {
2036 d->hData.flicking = false;
2037 emit flickingChanged();
2038 emit flickingHorizontallyChanged();
2039 if (!d->vData.flicking)
2042 if (!d->pressed && !d->stealMouse) {
2043 if (d->hData.moving) {
2044 d->hData.moving = false;
2046 emit movingChanged();
2047 emit movingHorizontallyChanged();
2048 if (!d->vData.moving)
2049 emit movementEnded();
2052 d->hData.fixingUp = false;
2055 void QQuickFlickable::movementYEnding()
2057 Q_D(QQuickFlickable);
2058 if (d->vData.flicking) {
2059 d->vData.flicking = false;
2060 emit flickingChanged();
2061 emit flickingVerticallyChanged();
2062 if (!d->hData.flicking)
2065 if (!d->pressed && !d->stealMouse) {
2066 if (d->vData.moving) {
2067 d->vData.moving = false;
2069 emit movingChanged();
2070 emit movingVerticallyChanged();
2071 if (!d->hData.moving)
2072 emit movementEnded();
2075 d->vData.fixingUp = false;
2078 void QQuickFlickablePrivate::updateVelocity()
2080 Q_Q(QQuickFlickable);
2081 emit q->horizontalVelocityChanged();
2082 emit q->verticalVelocityChanged();