1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquickflickable_p.h"
43 #include "qquickflickable_p_p.h"
44 #include "qquickcanvas.h"
45 #include "qquickcanvas_p.h"
46 #include "qquickevents_p_p.h"
48 #include <private/qqmlglobal_p.h>
50 #include <QtQml/qqmlinfo.h>
51 #include <QtGui/qevent.h>
52 #include <QtGui/qguiapplication.h>
53 #include <QtGui/qstylehints.h>
54 #include "qplatformdefs.h"
58 // The maximum number of pixels a flick can overshoot
59 #ifndef QML_FLICK_OVERSHOOT
60 #define QML_FLICK_OVERSHOOT 200
63 // The number of samples to use in calculating the velocity of a flick
64 #ifndef QML_FLICK_SAMPLEBUFFER
65 #define QML_FLICK_SAMPLEBUFFER 3
68 // The number of samples to discard when calculating the flick velocity.
69 // Touch panels often produce inaccurate results as the finger is lifted.
70 #ifndef QML_FLICK_DISCARDSAMPLES
71 #define QML_FLICK_DISCARDSAMPLES 0
74 // The default maximum velocity of a flick.
75 #ifndef QML_FLICK_DEFAULTMAXVELOCITY
76 #define QML_FLICK_DEFAULTMAXVELOCITY 2500
79 // The default deceleration of a flick.
80 #ifndef QML_FLICK_DEFAULTDECELERATION
81 #define QML_FLICK_DEFAULTDECELERATION 1500
84 // How much faster to decelerate when overshooting
85 #ifndef QML_FLICK_OVERSHOOTFRICTION
86 #define QML_FLICK_OVERSHOOTFRICTION 8
89 // Multiflick acceleration minimum flick velocity threshold
90 #ifndef QML_FLICK_MULTIFLICK_THRESHOLD
91 #define QML_FLICK_MULTIFLICK_THRESHOLD 1250
94 // Multiflick acceleration minimum contentSize/viewSize ratio
95 #ifndef QML_FLICK_MULTIFLICK_RATIO
96 #define QML_FLICK_MULTIFLICK_RATIO 10
99 // Multiflick acceleration maximum velocity multiplier
100 #ifndef QML_FLICK_MULTIFLICK_MAXBOOST
101 #define QML_FLICK_MULTIFLICK_MAXBOOST 3.0
104 // FlickThreshold determines how far the "mouse" must have moved
105 // before we perform a flick.
106 static const int FlickThreshold = 15;
108 // RetainGrabVelocity is the maxmimum instantaneous velocity that
109 // will ensure the Flickable retains the grab on consecutive flicks.
110 static const int RetainGrabVelocity = 100;
112 QQuickFlickableVisibleArea::QQuickFlickableVisibleArea(QQuickFlickable *parent)
113 : QObject(parent), flickable(parent), m_xPosition(0.), m_widthRatio(0.)
114 , m_yPosition(0.), m_heightRatio(0.)
118 qreal QQuickFlickableVisibleArea::widthRatio() const
123 qreal QQuickFlickableVisibleArea::xPosition() const
128 qreal QQuickFlickableVisibleArea::heightRatio() const
130 return m_heightRatio;
133 qreal QQuickFlickableVisibleArea::yPosition() const
138 void QQuickFlickableVisibleArea::updateVisible()
140 QQuickFlickablePrivate *p = QQuickFlickablePrivate::get(flickable);
142 bool changeX = false;
143 bool changeY = false;
144 bool changeWidth = false;
145 bool changeHeight = false;
148 const qreal viewheight = flickable->height();
149 const qreal maxyextent = -flickable->maxYExtent() + flickable->minYExtent();
150 qreal pagePos = (-p->vData.move.value() + flickable->minYExtent()) / (maxyextent + viewheight);
151 qreal pageSize = viewheight / (maxyextent + viewheight);
153 if (pageSize != m_heightRatio) {
154 m_heightRatio = pageSize;
157 if (pagePos != m_yPosition) {
158 m_yPosition = pagePos;
163 const qreal viewwidth = flickable->width();
164 const qreal maxxextent = -flickable->maxXExtent() + flickable->minXExtent();
165 pagePos = (-p->hData.move.value() + flickable->minXExtent()) / (maxxextent + viewwidth);
166 pageSize = viewwidth / (maxxextent + viewwidth);
168 if (pageSize != m_widthRatio) {
169 m_widthRatio = pageSize;
172 if (pagePos != m_xPosition) {
173 m_xPosition = pagePos;
178 emit xPositionChanged(m_xPosition);
180 emit yPositionChanged(m_yPosition);
182 emit widthRatioChanged(m_widthRatio);
184 emit heightRatioChanged(m_heightRatio);
188 QQuickFlickablePrivate::QQuickFlickablePrivate()
189 : contentItem(new QQuickItem)
190 , hData(this, &QQuickFlickablePrivate::setViewportX)
191 , vData(this, &QQuickFlickablePrivate::setViewportY)
192 , hMoved(false), vMoved(false)
193 , stealMouse(false), pressed(false), interactive(true), calcVelocity(false)
194 , pixelAligned(false)
197 , deceleration(QML_FLICK_DEFAULTDECELERATION)
198 , maxVelocity(QML_FLICK_DEFAULTMAXVELOCITY), reportedVelocitySmoothing(100)
199 , delayedPressEvent(0), delayedPressTarget(0), pressDelay(0), fixupDuration(400)
200 , flickBoost(1.0), fixupMode(Normal), vTime(0), visibleArea(0)
201 , flickableDirection(QQuickFlickable::AutoFlickDirection)
202 , boundsBehavior(QQuickFlickable::DragAndOvershootBounds)
206 void QQuickFlickablePrivate::init()
208 Q_Q(QQuickFlickable);
209 QQml_setParent_noEvent(contentItem, q);
210 contentItem->setParentItem(q);
211 FAST_CONNECT(&timeline, SIGNAL(completed()), q, SLOT(movementEnding()))
212 q->setAcceptedMouseButtons(Qt::LeftButton);
213 q->setFiltersChildMouseEvents(true);
214 QQuickItemPrivate *viewportPrivate = QQuickItemPrivate::get(contentItem);
215 viewportPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
219 Returns the amount to overshoot by given a velocity.
220 Will be roughly in range 0 - size/4
222 qreal QQuickFlickablePrivate::overShootDistance(qreal size)
224 if (maxVelocity <= 0)
227 return qMin(qreal(QML_FLICK_OVERSHOOT), size/3);
230 void QQuickFlickablePrivate::AxisData::addVelocitySample(qreal v, qreal maxVelocity)
234 else if (v < -maxVelocity)
236 velocityBuffer.append(v);
237 if (velocityBuffer.count() > QML_FLICK_SAMPLEBUFFER)
238 velocityBuffer.remove(0);
241 void QQuickFlickablePrivate::AxisData::updateVelocity()
244 if (velocityBuffer.count() > QML_FLICK_DISCARDSAMPLES) {
245 int count = velocityBuffer.count()-QML_FLICK_DISCARDSAMPLES;
246 for (int i = 0; i < count; ++i) {
247 qreal v = velocityBuffer.at(i);
254 void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeom, const QRectF &oldGeom)
256 Q_Q(QQuickFlickable);
257 if (item == contentItem) {
258 bool xChanged = newGeom.x() != oldGeom.x();
259 bool yChanged = newGeom.y() != oldGeom.y();
260 if (xChanged || yChanged)
263 emit q->contentXChanged();
265 emit q->contentYChanged();
269 void QQuickFlickablePrivate::flickX(qreal velocity)
271 Q_Q(QQuickFlickable);
272 flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, velocity);
275 void QQuickFlickablePrivate::flickY(qreal velocity)
277 Q_Q(QQuickFlickable);
278 flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, velocity);
281 void QQuickFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal,
282 QQuickTimeLineCallback::Callback fixupCallback, qreal velocity)
284 Q_Q(QQuickFlickable);
285 qreal maxDistance = -1;
286 data.fixingUp = false;
287 // -ve velocity means list is moving up
289 maxDistance = qAbs(minExtent - data.move.value());
290 data.flickTarget = minExtent;
292 maxDistance = qAbs(maxExtent - data.move.value());
293 data.flickTarget = maxExtent;
295 if (maxDistance > 0) {
297 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
303 timeline.reset(data.move);
304 if (boundsBehavior == QQuickFlickable::DragAndOvershootBounds)
305 timeline.accel(data.move, v, deceleration);
307 timeline.accel(data.move, v, deceleration, maxDistance);
308 timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
309 if (!hData.flicking && q->xflick() && (&data == &hData)) {
310 hData.flicking = true;
311 emit q->flickingChanged();
312 emit q->flickingHorizontallyChanged();
314 emit q->flickStarted();
316 if (!vData.flicking && q->yflick() && (&data == &vData)) {
317 vData.flicking = true;
318 emit q->flickingChanged();
319 emit q->flickingVerticallyChanged();
321 emit q->flickStarted();
324 timeline.reset(data.move);
325 fixup(data, minExtent, maxExtent);
329 void QQuickFlickablePrivate::fixupY_callback(void *data)
331 ((QQuickFlickablePrivate *)data)->fixupY();
334 void QQuickFlickablePrivate::fixupX_callback(void *data)
336 ((QQuickFlickablePrivate *)data)->fixupX();
339 void QQuickFlickablePrivate::fixupX()
341 Q_Q(QQuickFlickable);
342 fixup(hData, q->minXExtent(), q->maxXExtent());
345 void QQuickFlickablePrivate::fixupY()
347 Q_Q(QQuickFlickable);
348 fixup(vData, q->minYExtent(), q->maxYExtent());
351 void QQuickFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
353 if (data.move.value() > minExtent || maxExtent > minExtent) {
354 timeline.reset(data.move);
355 if (data.move.value() != minExtent) {
358 timeline.set(data.move, minExtent);
361 // The target has changed. Don't start from the beginning; just complete the
362 // second half of the animation using the new extent.
363 timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
364 data.fixingUp = true;
367 qreal dist = minExtent - data.move;
368 timeline.move(data.move, minExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4);
369 timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
370 data.fixingUp = true;
374 } else if (data.move.value() < maxExtent) {
375 timeline.reset(data.move);
378 timeline.set(data.move, maxExtent);
381 // The target has changed. Don't start from the beginning; just complete the
382 // second half of the animation using the new extent.
383 timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
384 data.fixingUp = true;
387 qreal dist = maxExtent - data.move;
388 timeline.move(data.move, maxExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4);
389 timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
390 data.fixingUp = true;
394 data.inOvershoot = false;
396 vTime = timeline.time();
399 void QQuickFlickablePrivate::updateBeginningEnd()
401 Q_Q(QQuickFlickable);
402 bool atBoundaryChange = false;
405 const int maxyextent = int(-q->maxYExtent());
406 const qreal ypos = -vData.move.value();
407 bool atBeginning = (ypos <= -q->minYExtent());
408 bool atEnd = (maxyextent <= ypos);
410 if (atBeginning != vData.atBeginning) {
411 vData.atBeginning = atBeginning;
412 atBoundaryChange = true;
414 if (atEnd != vData.atEnd) {
416 atBoundaryChange = true;
420 const int maxxextent = int(-q->maxXExtent());
421 const qreal xpos = -hData.move.value();
422 atBeginning = (xpos <= -q->minXExtent());
423 atEnd = (maxxextent <= xpos);
425 if (atBeginning != hData.atBeginning) {
426 hData.atBeginning = atBeginning;
427 atBoundaryChange = true;
429 if (atEnd != hData.atEnd) {
431 atBoundaryChange = true;
434 if (vData.extentsChanged) {
435 vData.extentsChanged = false;
436 emit q->yOriginChanged();
439 if (hData.extentsChanged) {
440 hData.extentsChanged = false;
441 emit q->xOriginChanged();
444 if (atBoundaryChange)
445 emit q->isAtBoundaryChanged();
448 visibleArea->updateVisible();
452 XXXTODO add docs describing moving, dragging, flicking properties, e.g.
454 When the user starts dragging the Flickable, the dragging and moving properties
457 If the velocity is sufficient when the drag is ended, flicking may begin.
459 The moving properties will remain true until all dragging and flicking
464 \qmlsignal QtQuick2::Flickable::onDragStarted()
466 This handler is called when the view starts to be dragged due to user
471 \qmlsignal QtQuick2::Flickable::onDragEnded()
473 This handler is called when the user stops dragging the view.
475 If the velocity of the drag is suffient at the time the
476 touch/mouse button is released then a flick will start.
480 \qmlclass Flickable QQuickFlickable
481 \inqmlmodule QtQuick 2
482 \ingroup qml-basic-interaction-elements
484 \brief The Flickable item provides a surface that can be "flicked".
487 The Flickable item places its children on a surface that can be dragged
488 and flicked, causing the view onto the child items to scroll. This
489 behavior forms the basis of Items that are designed to show large numbers
490 of child items, such as \l ListView and \l GridView.
492 In traditional user interfaces, views can be scrolled using standard
493 controls, such as scroll bars and arrow buttons. In some situations, it
494 is also possible to drag the view directly by pressing and holding a
495 mouse button while moving the cursor. In touch-based user interfaces,
496 this dragging action is often complemented with a flicking action, where
497 scrolling continues after the user has stopped touching the view.
499 Flickable does not automatically clip its contents. If it is not used as
500 a full-screen item, you should consider setting the \l{Item::}{clip} property
503 \section1 Example Usage
505 \div {class="float-right"}
506 \inlineimage flickable.gif
509 The following example shows a small view onto a large image in which the
510 user can drag or flick the image in order to view different parts of it.
512 \snippet doc/src/snippets/qml/flickable.qml document
516 Items declared as children of a Flickable are automatically parented to the
517 Flickable's \l contentItem. This should be taken into account when
518 operating on the children of the Flickable; it is usually the children of
519 \c contentItem that are relevant. For example, the bound of Items added
520 to the Flickable will be available by \c contentItem.childrenRect
522 \section1 Limitations
524 \note Due to an implementation detail, items placed inside a Flickable cannot anchor to it by
525 \c id. Use \c parent instead.
529 \qmlsignal QtQuick2::Flickable::onMovementStarted()
531 This handler is called when the view begins moving due to user
536 \qmlsignal QtQuick2::Flickable::onMovementEnded()
538 This handler is called when the view stops moving due to user
539 interaction. If a flick was generated, this handler will
540 be triggered once the flick stops. If a flick was not
541 generated, the handler will be triggered when the
542 user stops dragging - i.e. a mouse or touch release.
546 \qmlsignal QtQuick2::Flickable::onFlickStarted()
548 This handler is called when the view is flicked. A flick
549 starts from the point that the mouse or touch is released,
550 while still in motion.
554 \qmlsignal QtQuick2::Flickable::onFlickEnded()
556 This handler is called when the view stops moving due to a flick.
560 \qmlproperty real QtQuick2::Flickable::visibleArea.xPosition
561 \qmlproperty real QtQuick2::Flickable::visibleArea.widthRatio
562 \qmlproperty real QtQuick2::Flickable::visibleArea.yPosition
563 \qmlproperty real QtQuick2::Flickable::visibleArea.heightRatio
565 These properties describe the position and size of the currently viewed area.
566 The size is defined as the percentage of the full view currently visible,
567 scaled to 0.0 - 1.0. The page position is usually in the range 0.0 (beginning) to
568 1.0 minus size ratio (end), i.e. \c yPosition is in the range 0.0 to 1.0-\c heightRatio.
569 However, it is possible for the contents to be dragged outside of the normal
570 range, resulting in the page positions also being outside the normal range.
572 These properties are typically used to draw a scrollbar. For example:
574 \snippet doc/src/snippets/qml/flickableScrollbar.qml 0
576 \snippet doc/src/snippets/qml/flickableScrollbar.qml 1
578 \sa {declarative/ui-components/scrollbar}{scrollbar example}
580 QQuickFlickable::QQuickFlickable(QQuickItem *parent)
581 : QQuickItem(*(new QQuickFlickablePrivate), parent)
583 Q_D(QQuickFlickable);
587 QQuickFlickable::QQuickFlickable(QQuickFlickablePrivate &dd, QQuickItem *parent)
588 : QQuickItem(dd, parent)
590 Q_D(QQuickFlickable);
594 QQuickFlickable::~QQuickFlickable()
599 \qmlproperty real QtQuick2::Flickable::contentX
600 \qmlproperty real QtQuick2::Flickable::contentY
602 These properties hold the surface coordinate currently at the top-left
603 corner of the Flickable. For example, if you flick an image up 100 pixels,
604 \c contentY will be 100.
606 qreal QQuickFlickable::contentX() const
608 Q_D(const QQuickFlickable);
609 return -d->contentItem->x();
612 void QQuickFlickable::setContentX(qreal pos)
614 Q_D(QQuickFlickable);
615 d->hData.explicitValue = true;
616 d->timeline.reset(d->hData.move);
617 d->vTime = d->timeline.time();
619 if (-pos != d->hData.move.value())
620 d->hData.move.setValue(-pos);
623 qreal QQuickFlickable::contentY() const
625 Q_D(const QQuickFlickable);
626 return -d->contentItem->y();
629 void QQuickFlickable::setContentY(qreal pos)
631 Q_D(QQuickFlickable);
632 d->vData.explicitValue = true;
633 d->timeline.reset(d->vData.move);
634 d->vTime = d->timeline.time();
636 if (-pos != d->vData.move.value())
637 d->vData.move.setValue(-pos);
641 \qmlproperty bool QtQuick2::Flickable::interactive
643 This property describes whether the user can interact with the Flickable.
644 A user cannot drag or flick a Flickable that is not interactive.
646 By default, this property is true.
648 This property is useful for temporarily disabling flicking. This allows
649 special interaction with Flickable's children; for example, you might want
650 to freeze a flickable map while scrolling through a pop-up dialog that
651 is a child of the Flickable.
653 bool QQuickFlickable::isInteractive() const
655 Q_D(const QQuickFlickable);
656 return d->interactive;
659 void QQuickFlickable::setInteractive(bool interactive)
661 Q_D(QQuickFlickable);
662 if (interactive != d->interactive) {
663 d->interactive = interactive;
664 if (!interactive && (d->hData.flicking || d->vData.flicking)) {
666 d->vTime = d->timeline.time();
667 d->hData.flicking = false;
668 d->vData.flicking = false;
669 emit flickingChanged();
670 emit flickingHorizontallyChanged();
671 emit flickingVerticallyChanged();
674 emit interactiveChanged();
679 \qmlproperty real QtQuick2::Flickable::horizontalVelocity
680 \qmlproperty real QtQuick2::Flickable::verticalVelocity
682 The instantaneous velocity of movement along the x and y axes, in pixels/sec.
684 The reported velocity is smoothed to avoid erratic output.
686 Note that for views with a large content size (more than 10 times the view size),
687 the velocity of the flick may exceed the velocity of the touch in the case
688 of multiple quick consecutive flicks. This allows the user to flick faster
689 through large content.
691 qreal QQuickFlickable::horizontalVelocity() const
693 Q_D(const QQuickFlickable);
694 return d->hData.smoothVelocity.value();
697 qreal QQuickFlickable::verticalVelocity() const
699 Q_D(const QQuickFlickable);
700 return d->vData.smoothVelocity.value();
704 \qmlproperty bool QtQuick2::Flickable::atXBeginning
705 \qmlproperty bool QtQuick2::Flickable::atXEnd
706 \qmlproperty bool QtQuick2::Flickable::atYBeginning
707 \qmlproperty bool QtQuick2::Flickable::atYEnd
709 These properties are true if the flickable view is positioned at the beginning,
712 bool QQuickFlickable::isAtXEnd() const
714 Q_D(const QQuickFlickable);
715 return d->hData.atEnd;
718 bool QQuickFlickable::isAtXBeginning() const
720 Q_D(const QQuickFlickable);
721 return d->hData.atBeginning;
724 bool QQuickFlickable::isAtYEnd() const
726 Q_D(const QQuickFlickable);
727 return d->vData.atEnd;
730 bool QQuickFlickable::isAtYBeginning() const
732 Q_D(const QQuickFlickable);
733 return d->vData.atBeginning;
737 \qmlproperty Item QtQuick2::Flickable::contentItem
739 The internal item that contains the Items to be moved in the Flickable.
741 Items declared as children of a Flickable are automatically parented to the Flickable's contentItem.
743 Items created dynamically need to be explicitly parented to the \e contentItem:
747 function addItem(file) {
748 var component = Qt.createComponent(file)
749 component.createObject(myFlickable.contentItem);
754 QQuickItem *QQuickFlickable::contentItem()
756 Q_D(QQuickFlickable);
757 return d->contentItem;
760 QQuickFlickableVisibleArea *QQuickFlickable::visibleArea()
762 Q_D(QQuickFlickable);
764 d->visibleArea = new QQuickFlickableVisibleArea(this);
765 return d->visibleArea;
769 \qmlproperty enumeration QtQuick2::Flickable::flickableDirection
771 This property determines which directions the view can be flicked.
774 \li Flickable.AutoFlickDirection (default) - allows flicking vertically if the
775 \e contentHeight is not equal to the \e height of the Flickable.
776 Allows flicking horizontally if the \e contentWidth is not equal
777 to the \e width of the Flickable.
778 \li Flickable.HorizontalFlick - allows flicking horizontally.
779 \li Flickable.VerticalFlick - allows flicking vertically.
780 \li Flickable.HorizontalAndVerticalFlick - allows flicking in both directions.
783 QQuickFlickable::FlickableDirection QQuickFlickable::flickableDirection() const
785 Q_D(const QQuickFlickable);
786 return d->flickableDirection;
789 void QQuickFlickable::setFlickableDirection(FlickableDirection direction)
791 Q_D(QQuickFlickable);
792 if (direction != d->flickableDirection) {
793 d->flickableDirection = direction;
794 emit flickableDirectionChanged();
798 bool QQuickFlickable::pixelAligned() const
800 Q_D(const QQuickFlickable);
801 return d->pixelAligned;
804 void QQuickFlickable::setPixelAligned(bool align)
806 Q_D(QQuickFlickable);
807 if (align != d->pixelAligned) {
808 d->pixelAligned = align;
809 emit pixelAlignedChanged();
813 qint64 QQuickFlickablePrivate::computeCurrentTime(QInputEvent *event)
815 if (0 != event->timestamp() && QQuickItemPrivate::consistentTime == -1)
816 return event->timestamp();
818 return QQuickItemPrivate::elapsed(timer);
821 void QQuickFlickablePrivate::handleMousePressEvent(QMouseEvent *event)
823 Q_Q(QQuickFlickable);
824 QQuickItemPrivate::start(timer);
825 if (interactive && timeline.isActive()
826 && ((qAbs(hData.smoothVelocity.value()) > RetainGrabVelocity && !hData.fixingUp && !hData.inOvershoot)
827 || (qAbs(vData.smoothVelocity.value()) > RetainGrabVelocity && !vData.fixingUp && !vData.inOvershoot))) {
828 stealMouse = true; // If we've been flicked then steal the click.
829 int flickTime = timeline.time();
830 if (flickTime > 600) {
831 // too long between flicks - cancel boost
832 hData.continuousFlickVelocity = 0;
833 vData.continuousFlickVelocity = 0;
836 hData.continuousFlickVelocity = -hData.smoothVelocity.value();
837 vData.continuousFlickVelocity = -vData.smoothVelocity.value();
838 if (flickTime > 300) // slower flicking - reduce boost
839 flickBoost = qMax(1.0, flickBoost - 0.5);
843 hData.continuousFlickVelocity = 0;
844 vData.continuousFlickVelocity = 0;
847 q->setKeepMouseGrab(stealMouse);
850 timeline.reset(hData.move);
852 timeline.reset(vData.move);
855 hData.dragMinBound = q->minXExtent();
856 vData.dragMinBound = q->minYExtent();
857 hData.dragMaxBound = q->maxXExtent();
858 vData.dragMaxBound = q->maxYExtent();
861 pressPos = event->localPos();
862 hData.pressPos = hData.move.value();
863 vData.pressPos = vData.move.value();
864 bool wasFlicking = hData.flicking || vData.flicking;
865 if (hData.flicking) {
866 hData.flicking = false;
867 emit q->flickingHorizontallyChanged();
869 if (vData.flicking) {
870 vData.flicking = false;
871 emit q->flickingVerticallyChanged();
874 emit q->flickingChanged();
875 lastPosTime = lastPressTime = computeCurrentTime(event);
876 QQuickItemPrivate::start(velocityTime);
879 void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event)
881 Q_Q(QQuickFlickable);
882 if (!interactive || lastPosTime == -1)
884 bool rejectY = false;
885 bool rejectX = false;
887 bool stealY = stealMouse;
888 bool stealX = stealMouse;
890 qint64 elapsedSincePress = computeCurrentTime(event) - lastPressTime;
893 qreal dy = event->localPos().y() - pressPos.y();
894 if (qAbs(dy) > qApp->styleHints()->startDragDistance() || elapsedSincePress > 200) {
896 vData.dragStartOffset = dy;
897 qreal newY = dy + vData.pressPos - vData.dragStartOffset;
898 const qreal minY = vData.dragMinBound;
899 const qreal maxY = vData.dragMaxBound;
901 newY = minY + (newY - minY) / 2;
902 if (newY < maxY && maxY - minY <= 0)
903 newY = maxY + (newY - maxY) / 2;
904 if (boundsBehavior == QQuickFlickable::StopAtBounds && (newY > minY || newY < maxY)) {
915 if (!rejectY && stealMouse && dy != 0.0) {
917 vData.move.setValue(newY);
920 if (qAbs(dy) > qApp->styleHints()->startDragDistance())
926 qreal dx = event->localPos().x() - pressPos.x();
927 if (qAbs(dx) > qApp->styleHints()->startDragDistance() || elapsedSincePress > 200) {
929 hData.dragStartOffset = dx;
930 qreal newX = dx + hData.pressPos - hData.dragStartOffset;
931 const qreal minX = hData.dragMinBound;
932 const qreal maxX = hData.dragMaxBound;
934 newX = minX + (newX - minX) / 2;
935 if (newX < maxX && maxX - minX <= 0)
936 newX = maxX + (newX - maxX) / 2;
937 if (boundsBehavior == QQuickFlickable::StopAtBounds && (newX > minX || newX < maxX)) {
948 if (!rejectX && stealMouse && dx != 0.0) {
950 hData.move.setValue(newX);
954 if (qAbs(dx) > qApp->styleHints()->startDragDistance())
959 stealMouse = stealX || stealY;
961 q->setKeepMouseGrab(true);
964 vData.velocityBuffer.clear();
968 hData.velocityBuffer.clear();
972 if (hMoved || vMoved) {
974 q->movementStarting();
977 qint64 currentTimestamp = computeCurrentTime(event);
978 qreal elapsed = qreal(currentTimestamp - (lastPos.isNull() ? lastPressTime : lastPosTime)) / 1000.;
981 lastPosTime = currentTimestamp;
982 QQuickMouseEventEx *extended = QQuickMouseEventEx::extended(event);
983 if (q->yflick() && !rejectY) {
984 if (extended && extended->capabilities().testFlag(QTouchDevice::Velocity)) {
985 vData.addVelocitySample(extended->velocity().y(), maxVelocity);
987 qreal dy = event->localPos().y() - (lastPos.isNull() ? pressPos.y() : lastPos.y());
988 vData.addVelocitySample(dy/elapsed, maxVelocity);
991 if (q->xflick() && !rejectX) {
992 if (extended && extended->capabilities().testFlag(QTouchDevice::Velocity)) {
993 hData.addVelocitySample(extended->velocity().x(), maxVelocity);
995 qreal dx = event->localPos().x() - (lastPos.isNull() ? pressPos.x() : lastPos.x());
996 hData.addVelocitySample(dx/elapsed, maxVelocity);
1000 lastPos = event->localPos();
1003 void QQuickFlickablePrivate::handleMouseReleaseEvent(QMouseEvent *event)
1005 Q_Q(QQuickFlickable);
1007 q->setKeepMouseGrab(false);
1010 // if we drag then pause before release we should not cause a flick.
1011 qint64 elapsed = computeCurrentTime(event) - lastPosTime;
1013 vData.updateVelocity();
1014 hData.updateVelocity();
1018 if (lastPosTime == -1)
1021 vTime = timeline.time();
1023 bool canBoost = false;
1025 qreal vVelocity = 0;
1026 if (elapsed < 100 && vData.velocity != 0.) {
1027 QQuickMouseEventEx *extended = QQuickMouseEventEx::extended(event);
1028 vVelocity = (extended && extended->capabilities().testFlag(QTouchDevice::Velocity))
1029 ? extended->velocity().y() : vData.velocity;
1031 if ((vData.atBeginning && vVelocity > 0.) || (vData.atEnd && vVelocity < 0.)) {
1033 } else if (vData.continuousFlickVelocity != 0.0
1034 && vData.viewSize/q->height() > QML_FLICK_MULTIFLICK_RATIO
1035 && ((vVelocity > 0) == (vData.continuousFlickVelocity > 0))
1036 && qAbs(vVelocity) > QML_FLICK_MULTIFLICK_THRESHOLD) {
1037 // accelerate flick for large view flicked quickly
1041 qreal hVelocity = 0;
1042 if (elapsed < 100 && hData.velocity != 0.) {
1043 QQuickMouseEventEx *extended = QQuickMouseEventEx::extended(event);
1044 hVelocity = (extended && extended->capabilities().testFlag(QTouchDevice::Velocity))
1045 ? extended->velocity().x() : hData.velocity;
1047 if ((hData.atBeginning && hVelocity > 0.) || (hData.atEnd && hVelocity < 0.)) {
1049 } else if (hData.continuousFlickVelocity != 0.0
1050 && hData.viewSize/q->width() > QML_FLICK_MULTIFLICK_RATIO
1051 && ((hVelocity > 0) == (hData.continuousFlickVelocity > 0))
1052 && qAbs(hVelocity) > QML_FLICK_MULTIFLICK_THRESHOLD) {
1053 // accelerate flick for large view flicked quickly
1057 flickBoost = canBoost ? qBound(1.0, flickBoost+0.25, QML_FLICK_MULTIFLICK_MAXBOOST) : 1.0;
1059 vVelocity *= flickBoost;
1060 if (q->yflick() && qAbs(vVelocity) > MinimumFlickVelocity && qAbs(event->localPos().y() - pressPos.y()) > FlickThreshold) {
1061 velocityTimeline.reset(vData.smoothVelocity);
1062 vData.smoothVelocity.setValue(-vVelocity);
1068 hVelocity *= flickBoost;
1069 if (q->xflick() && qAbs(hVelocity) > MinimumFlickVelocity && qAbs(event->localPos().x() - pressPos.x()) > FlickThreshold) {
1070 velocityTimeline.reset(hData.smoothVelocity);
1071 hData.smoothVelocity.setValue(-hVelocity);
1077 if (!timeline.isActive())
1078 q->movementEnding();
1081 void QQuickFlickable::mousePressEvent(QMouseEvent *event)
1083 Q_D(QQuickFlickable);
1084 if (d->interactive) {
1086 d->handleMousePressEvent(event);
1089 QQuickItem::mousePressEvent(event);
1093 void QQuickFlickable::mouseMoveEvent(QMouseEvent *event)
1095 Q_D(QQuickFlickable);
1096 if (d->interactive) {
1097 d->handleMouseMoveEvent(event);
1100 QQuickItem::mouseMoveEvent(event);
1104 void QQuickFlickable::mouseReleaseEvent(QMouseEvent *event)
1106 Q_D(QQuickFlickable);
1107 if (d->interactive) {
1108 d->clearDelayedPress();
1109 d->handleMouseReleaseEvent(event);
1111 if (canvas() && canvas()->mouseGrabberItem() == this)
1114 QQuickItem::mouseReleaseEvent(event);
1118 void QQuickFlickable::wheelEvent(QWheelEvent *event)
1120 Q_D(QQuickFlickable);
1121 if (!d->interactive) {
1122 QQuickItem::wheelEvent(event);
1123 } else if (yflick() && event->orientation() == Qt::Vertical) {
1125 if (event->delta() > 0 && contentY() > -minYExtent()) {
1126 d->vData.velocity = qMax(event->delta()*2 - d->vData.smoothVelocity.value(), qreal(d->maxVelocity/4));
1128 } else if (event->delta() < 0 && contentY() < -maxYExtent()) {
1129 d->vData.velocity = qMin(event->delta()*2 - d->vData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
1133 d->vData.flicking = false;
1134 d->flickY(d->vData.velocity);
1135 if (d->vData.flicking) {
1141 } else if (xflick() && event->orientation() == Qt::Horizontal) {
1143 if (event->delta() > 0 && contentX() > -minXExtent()) {
1144 d->hData.velocity = qMax(event->delta()*2 - d->hData.smoothVelocity.value(), qreal(d->maxVelocity/4));
1146 } else if (event->delta() < 0 && contentX() < -maxXExtent()) {
1147 d->hData.velocity = qMin(event->delta()*2 - d->hData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
1151 d->hData.flicking = false;
1152 d->flickX(d->hData.velocity);
1153 if (d->hData.flicking) {
1160 QQuickItem::wheelEvent(event);
1164 bool QQuickFlickablePrivate::isOutermostPressDelay() const
1166 Q_Q(const QQuickFlickable);
1167 QQuickItem *item = q->parentItem();
1169 QQuickFlickable *flick = qobject_cast<QQuickFlickable*>(item);
1170 if (flick && flick->pressDelay() > 0 && flick->isInteractive())
1172 item = item->parentItem();
1178 void QQuickFlickablePrivate::captureDelayedPress(QMouseEvent *event)
1180 Q_Q(QQuickFlickable);
1181 if (!q->canvas() || pressDelay <= 0)
1183 if (!isOutermostPressDelay())
1185 delayedPressTarget = q->canvas()->mouseGrabberItem();
1186 delayedPressEvent = new QQuickMouseEventEx(*event);
1187 delayedPressEvent->setAccepted(false);
1188 delayedPressTimer.start(pressDelay, q);
1191 void QQuickFlickablePrivate::clearDelayedPress()
1193 if (delayedPressEvent) {
1194 delayedPressTimer.stop();
1195 delete delayedPressEvent;
1196 delayedPressEvent = 0;
1200 //XXX pixelAligned ignores the global position of the Flickable, i.e. assumes Flickable itself is pixel aligned.
1201 void QQuickFlickablePrivate::setViewportX(qreal x)
1203 contentItem->setX(pixelAligned ? qRound(x) : x);
1206 void QQuickFlickablePrivate::setViewportY(qreal y)
1208 contentItem->setY(pixelAligned ? qRound(y) : y);
1211 void QQuickFlickable::timerEvent(QTimerEvent *event)
1213 Q_D(QQuickFlickable);
1214 if (event->timerId() == d->delayedPressTimer.timerId()) {
1215 d->delayedPressTimer.stop();
1216 if (d->delayedPressEvent) {
1217 QQuickItem *grabber = canvas() ? canvas()->mouseGrabberItem() : 0;
1218 if (!grabber || grabber != this) {
1219 // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
1220 // so we reset the grabber
1221 if (canvas()->mouseGrabberItem() == d->delayedPressTarget)
1222 d->delayedPressTarget->ungrabMouse();
1223 // Use the event handler that will take care of finding the proper item to propagate the event
1224 QQuickCanvasPrivate::get(canvas())->deliverMouseEvent(d->delayedPressEvent);
1226 delete d->delayedPressEvent;
1227 d->delayedPressEvent = 0;
1232 qreal QQuickFlickable::minYExtent() const
1234 Q_D(const QQuickFlickable);
1235 return d->vData.startMargin;
1238 qreal QQuickFlickable::minXExtent() const
1240 Q_D(const QQuickFlickable);
1241 return d->hData.startMargin;
1245 qreal QQuickFlickable::maxXExtent() const
1247 Q_D(const QQuickFlickable);
1248 return width() - vWidth() - d->hData.endMargin;
1251 qreal QQuickFlickable::maxYExtent() const
1253 Q_D(const QQuickFlickable);
1254 return height() - vHeight() - d->vData.endMargin;
1257 void QQuickFlickable::componentComplete()
1259 Q_D(QQuickFlickable);
1260 QQuickItem::componentComplete();
1261 if (!d->hData.explicitValue && d->hData.startMargin != 0.)
1262 setContentX(-minXExtent());
1263 if (!d->vData.explicitValue && d->vData.startMargin != 0.)
1264 setContentY(-minYExtent());
1267 void QQuickFlickable::viewportMoved()
1269 Q_D(QQuickFlickable);
1271 qreal prevX = d->lastFlickablePosition.x();
1272 qreal prevY = d->lastFlickablePosition.y();
1273 if (d->pressed || d->calcVelocity) {
1274 int elapsed = QQuickItemPrivate::restart(d->velocityTime);
1276 qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / elapsed;
1277 if (qAbs(horizontalVelocity) > 0) {
1278 d->velocityTimeline.reset(d->hData.smoothVelocity);
1279 if (d->calcVelocity)
1280 d->velocityTimeline.set(d->hData.smoothVelocity, horizontalVelocity);
1282 d->velocityTimeline.move(d->hData.smoothVelocity, horizontalVelocity, d->reportedVelocitySmoothing);
1283 d->velocityTimeline.move(d->hData.smoothVelocity, 0, d->reportedVelocitySmoothing);
1285 qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / elapsed;
1286 if (qAbs(verticalVelocity) > 0) {
1287 d->velocityTimeline.reset(d->vData.smoothVelocity);
1288 if (d->calcVelocity)
1289 d->velocityTimeline.set(d->vData.smoothVelocity, verticalVelocity);
1291 d->velocityTimeline.move(d->vData.smoothVelocity, verticalVelocity, d->reportedVelocitySmoothing);
1292 d->velocityTimeline.move(d->vData.smoothVelocity, 0, d->reportedVelocitySmoothing);
1296 if (d->timeline.time() > d->vTime) {
1297 d->velocityTimeline.clear();
1298 qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / (d->timeline.time() - d->vTime);
1299 qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / (d->timeline.time() - d->vTime);
1300 d->hData.smoothVelocity.setValue(horizontalVelocity);
1301 d->vData.smoothVelocity.setValue(verticalVelocity);
1305 if (!d->vData.inOvershoot && !d->vData.fixingUp && d->vData.flicking
1306 && (d->vData.move.value() > minYExtent() || d->vData.move.value() < maxYExtent())
1307 && qAbs(d->vData.smoothVelocity.value()) > 100) {
1308 // Increase deceleration if we've passed a bound
1309 d->vData.inOvershoot = true;
1310 qreal maxDistance = d->overShootDistance(height());
1311 d->timeline.reset(d->vData.move);
1312 d->timeline.accel(d->vData.move, -d->vData.smoothVelocity.value(), d->deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance);
1313 d->timeline.callback(QQuickTimeLineCallback(&d->vData.move, d->fixupY_callback, d));
1315 if (!d->hData.inOvershoot && !d->hData.fixingUp && d->hData.flicking
1316 && (d->hData.move.value() > minXExtent() || d->hData.move.value() < maxXExtent())
1317 && qAbs(d->hData.smoothVelocity.value()) > 100) {
1318 // Increase deceleration if we've passed a bound
1319 d->hData.inOvershoot = true;
1320 qreal maxDistance = d->overShootDistance(width());
1321 d->timeline.reset(d->hData.move);
1322 d->timeline.accel(d->hData.move, -d->hData.smoothVelocity.value(), d->deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance);
1323 d->timeline.callback(QQuickTimeLineCallback(&d->hData.move, d->fixupX_callback, d));
1326 d->lastFlickablePosition = QPointF(d->hData.move.value(), d->vData.move.value());
1328 d->vTime = d->timeline.time();
1329 d->updateBeginningEnd();
1332 void QQuickFlickable::geometryChanged(const QRectF &newGeometry,
1333 const QRectF &oldGeometry)
1335 Q_D(QQuickFlickable);
1336 QQuickItem::geometryChanged(newGeometry, oldGeometry);
1338 bool changed = false;
1339 if (newGeometry.width() != oldGeometry.width()) {
1342 if (d->hData.viewSize < 0) {
1343 d->contentItem->setWidth(width());
1344 emit contentWidthChanged();
1346 // Make sure that we're entirely in view.
1347 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1348 d->fixupMode = QQuickFlickablePrivate::Immediate;
1352 if (newGeometry.height() != oldGeometry.height()) {
1355 if (d->vData.viewSize < 0) {
1356 d->contentItem->setHeight(height());
1357 emit contentHeightChanged();
1359 // Make sure that we're entirely in view.
1360 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1361 d->fixupMode = QQuickFlickablePrivate::Immediate;
1367 d->updateBeginningEnd();
1371 \qmlmethod QtQuick2::Flickable::flick(qreal xVelocity, qreal yVelocity)
1373 Flicks the content with \a xVelocity horizontally and \a yVelocity vertically in pixels/sec.
1376 void QQuickFlickable::flick(qreal xVelocity, qreal yVelocity)
1378 Q_D(QQuickFlickable);
1379 d->flickX(xVelocity);
1380 d->flickY(yVelocity);
1384 \qmlmethod QtQuick2::Flickable::cancelFlick()
1386 Cancels the current flick animation.
1389 void QQuickFlickable::cancelFlick()
1391 Q_D(QQuickFlickable);
1392 d->timeline.reset(d->hData.move);
1393 d->timeline.reset(d->vData.move);
1397 void QQuickFlickablePrivate::data_append(QQmlListProperty<QObject> *prop, QObject *o)
1399 QQuickItem *i = qobject_cast<QQuickItem *>(o);
1401 i->setParentItem(static_cast<QQuickFlickablePrivate*>(prop->data)->contentItem);
1403 o->setParent(prop->object); // XXX todo - do we want this?
1407 int QQuickFlickablePrivate::data_count(QQmlListProperty<QObject> *)
1413 QObject *QQuickFlickablePrivate::data_at(QQmlListProperty<QObject> *, int)
1419 void QQuickFlickablePrivate::data_clear(QQmlListProperty<QObject> *)
1424 QQmlListProperty<QObject> QQuickFlickable::flickableData()
1426 Q_D(QQuickFlickable);
1427 return QQmlListProperty<QObject>(this, (void *)d, QQuickFlickablePrivate::data_append,
1428 QQuickFlickablePrivate::data_count,
1429 QQuickFlickablePrivate::data_at,
1430 QQuickFlickablePrivate::data_clear);
1433 QQmlListProperty<QQuickItem> QQuickFlickable::flickableChildren()
1435 Q_D(QQuickFlickable);
1436 return QQuickItemPrivate::get(d->contentItem)->children();
1440 \qmlproperty enumeration QtQuick2::Flickable::boundsBehavior
1441 This property holds whether the surface may be dragged
1442 beyond the Flickable's boundaries, or overshoot the
1443 Flickable's boundaries when flicked.
1445 This enables the feeling that the edges of the view are soft,
1446 rather than a hard physical boundary.
1448 The \c boundsBehavior can be one of:
1451 \li Flickable.StopAtBounds - the contents can not be dragged beyond the boundary
1452 of the flickable, and flicks will not overshoot.
1453 \li Flickable.DragOverBounds - the contents can be dragged beyond the boundary
1454 of the Flickable, but flicks will not overshoot.
1455 \li Flickable.DragAndOvershootBounds (default) - the contents can be dragged
1456 beyond the boundary of the Flickable, and can overshoot the
1457 boundary when flicked.
1460 QQuickFlickable::BoundsBehavior QQuickFlickable::boundsBehavior() const
1462 Q_D(const QQuickFlickable);
1463 return d->boundsBehavior;
1466 void QQuickFlickable::setBoundsBehavior(BoundsBehavior b)
1468 Q_D(QQuickFlickable);
1469 if (b == d->boundsBehavior)
1471 d->boundsBehavior = b;
1472 emit boundsBehaviorChanged();
1476 \qmlproperty real QtQuick2::Flickable::contentWidth
1477 \qmlproperty real QtQuick2::Flickable::contentHeight
1479 The dimensions of the content (the surface controlled by Flickable).
1480 This should typically be set to the combined size of the items placed in the
1483 The following snippet shows how these properties are used to display
1484 an image that is larger than the Flickable item itself:
1486 \snippet doc/src/snippets/qml/flickable.qml document
1488 In some cases, the the content dimensions can be automatically set
1489 using the \l {Item::childrenRect.width}{childrenRect.width}
1490 and \l {Item::childrenRect.height}{childrenRect.height} properties.
1492 qreal QQuickFlickable::contentWidth() const
1494 Q_D(const QQuickFlickable);
1495 return d->hData.viewSize;
1498 void QQuickFlickable::setContentWidth(qreal w)
1500 Q_D(QQuickFlickable);
1501 if (d->hData.viewSize == w)
1503 d->hData.viewSize = w;
1505 d->contentItem->setWidth(width());
1507 d->contentItem->setWidth(w);
1508 d->hData.markExtentsDirty();
1509 // Make sure that we're entirely in view.
1510 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1511 d->fixupMode = QQuickFlickablePrivate::Immediate;
1513 } else if (!d->pressed && d->hData.fixingUp) {
1514 d->fixupMode = QQuickFlickablePrivate::ExtentChanged;
1517 emit contentWidthChanged();
1518 d->updateBeginningEnd();
1521 qreal QQuickFlickable::contentHeight() const
1523 Q_D(const QQuickFlickable);
1524 return d->vData.viewSize;
1527 void QQuickFlickable::setContentHeight(qreal h)
1529 Q_D(QQuickFlickable);
1530 if (d->vData.viewSize == h)
1532 d->vData.viewSize = h;
1534 d->contentItem->setHeight(height());
1536 d->contentItem->setHeight(h);
1537 d->vData.markExtentsDirty();
1538 // Make sure that we're entirely in view.
1539 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1540 d->fixupMode = QQuickFlickablePrivate::Immediate;
1542 } else if (!d->pressed && d->vData.fixingUp) {
1543 d->fixupMode = QQuickFlickablePrivate::ExtentChanged;
1546 emit contentHeightChanged();
1547 d->updateBeginningEnd();
1551 \qmlproperty real QtQuick2::Flickable::topMargin
1552 \qmlproperty real QtQuick2::Flickable::leftMargin
1553 \qmlproperty real QtQuick2::Flickable::bottomMargin
1554 \qmlproperty real QtQuick2::Flickable::rightMargin
1556 These properties hold the margins around the content. This space is reserved
1557 in addition to the contentWidth and contentHeight.
1561 qreal QQuickFlickable::topMargin() const
1563 Q_D(const QQuickFlickable);
1564 return d->vData.startMargin;
1567 void QQuickFlickable::setTopMargin(qreal m)
1569 Q_D(QQuickFlickable);
1570 if (d->vData.startMargin == m)
1572 d->vData.startMargin = m;
1573 d->vData.markExtentsDirty();
1574 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1575 d->fixupMode = QQuickFlickablePrivate::Immediate;
1578 emit topMarginChanged();
1579 d->updateBeginningEnd();
1582 qreal QQuickFlickable::bottomMargin() const
1584 Q_D(const QQuickFlickable);
1585 return d->vData.endMargin;
1588 void QQuickFlickable::setBottomMargin(qreal m)
1590 Q_D(QQuickFlickable);
1591 if (d->vData.endMargin == m)
1593 d->vData.endMargin = m;
1594 d->vData.markExtentsDirty();
1595 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1596 d->fixupMode = QQuickFlickablePrivate::Immediate;
1599 emit bottomMarginChanged();
1600 d->updateBeginningEnd();
1603 qreal QQuickFlickable::leftMargin() const
1605 Q_D(const QQuickFlickable);
1606 return d->hData.startMargin;
1609 void QQuickFlickable::setLeftMargin(qreal m)
1611 Q_D(QQuickFlickable);
1612 if (d->hData.startMargin == m)
1614 d->hData.startMargin = m;
1615 d->hData.markExtentsDirty();
1616 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1617 d->fixupMode = QQuickFlickablePrivate::Immediate;
1620 emit leftMarginChanged();
1621 d->updateBeginningEnd();
1624 qreal QQuickFlickable::rightMargin() const
1626 Q_D(const QQuickFlickable);
1627 return d->hData.endMargin;
1630 void QQuickFlickable::setRightMargin(qreal m)
1632 Q_D(QQuickFlickable);
1633 if (d->hData.endMargin == m)
1635 d->hData.endMargin = m;
1636 d->hData.markExtentsDirty();
1637 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1638 d->fixupMode = QQuickFlickablePrivate::Immediate;
1641 emit rightMarginChanged();
1642 d->updateBeginningEnd();
1646 \qmlproperty real QtQuick2::Flickable::xOrigin
1647 \qmlproperty real QtQuick2::Flickable::yOrigin
1649 These properties hold the origin of the content. This is usually (0,0), however
1650 ListView and GridView may have an arbitrary origin due to delegate size variation,
1651 or item insertion/removal outside the visible region.
1654 qreal QQuickFlickable::yOrigin() const
1656 Q_D(const QQuickFlickable);
1657 return -minYExtent() + d->vData.startMargin;
1660 qreal QQuickFlickable::xOrigin() const
1662 Q_D(const QQuickFlickable);
1663 return -minXExtent() + d->hData.startMargin;
1668 \qmlmethod QtQuick2::Flickable::resizeContent(real width, real height, QPointF center)
1670 Resizes the content to \a width x \a height about \a center.
1672 This does not scale the contents of the Flickable - it only resizes the \l contentWidth
1673 and \l contentHeight.
1675 Resizing the content may result in the content being positioned outside
1676 the bounds of the Flickable. Calling \l returnToBounds() will
1677 move the content back within legal bounds.
1679 void QQuickFlickable::resizeContent(qreal w, qreal h, QPointF center)
1681 Q_D(QQuickFlickable);
1682 if (w != d->hData.viewSize) {
1683 qreal oldSize = d->hData.viewSize;
1684 d->hData.viewSize = w;
1685 d->contentItem->setWidth(w);
1686 emit contentWidthChanged();
1687 if (center.x() != 0) {
1688 qreal pos = center.x() * w / oldSize;
1689 setContentX(contentX() + pos - center.x());
1692 if (h != d->vData.viewSize) {
1693 qreal oldSize = d->vData.viewSize;
1694 d->vData.viewSize = h;
1695 d->contentItem->setHeight(h);
1696 emit contentHeightChanged();
1697 if (center.y() != 0) {
1698 qreal pos = center.y() * h / oldSize;
1699 setContentY(contentY() + pos - center.y());
1702 d->updateBeginningEnd();
1706 \qmlmethod QtQuick2::Flickable::returnToBounds()
1708 Ensures the content is within legal bounds.
1710 This may be called to ensure that the content is within legal bounds
1711 after manually positioning the content.
1713 void QQuickFlickable::returnToBounds()
1715 Q_D(QQuickFlickable);
1720 qreal QQuickFlickable::vWidth() const
1722 Q_D(const QQuickFlickable);
1723 if (d->hData.viewSize < 0)
1726 return d->hData.viewSize;
1729 qreal QQuickFlickable::vHeight() const
1731 Q_D(const QQuickFlickable);
1732 if (d->vData.viewSize < 0)
1735 return d->vData.viewSize;
1738 bool QQuickFlickable::xflick() const
1740 Q_D(const QQuickFlickable);
1741 if (d->flickableDirection == QQuickFlickable::AutoFlickDirection)
1742 return vWidth() != width();
1743 return d->flickableDirection & QQuickFlickable::HorizontalFlick;
1746 bool QQuickFlickable::yflick() const
1748 Q_D(const QQuickFlickable);
1749 if (d->flickableDirection == QQuickFlickable::AutoFlickDirection)
1750 return vHeight() != height();
1751 return d->flickableDirection & QQuickFlickable::VerticalFlick;
1754 void QQuickFlickable::mouseUngrabEvent()
1756 Q_D(QQuickFlickable);
1758 // if our mouse grab has been removed (probably by another Flickable),
1760 d->clearDelayedPress();
1762 d->draggingEnding();
1763 d->stealMouse = false;
1764 setKeepMouseGrab(false);
1767 if (!d->timeline.isActive())
1772 bool QQuickFlickable::sendMouseEvent(QMouseEvent *event)
1774 Q_D(QQuickFlickable);
1775 QPointF localPos = mapFromScene(event->windowPos());
1777 QQuickCanvas *c = canvas();
1778 QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
1779 bool disabledItem = grabber && !grabber->isEnabled();
1780 bool stealThisEvent = d->stealMouse;
1781 if ((stealThisEvent || contains(localPos)) && (!grabber || !grabber->keepMouseGrab() || disabledItem)) {
1782 QQuickMouseEventEx mouseEvent(event->type(), localPos,
1783 event->windowPos(), event->screenPos(),
1784 event->button(), event->buttons(), event->modifiers());
1785 QQuickMouseEventEx *eventEx = QQuickMouseEventEx::extended(event);
1787 mouseEvent.setVelocity(eventEx->velocity());
1788 mouseEvent.setCapabilities(eventEx->capabilities());
1790 mouseEvent.setTimestamp(event->timestamp());
1791 mouseEvent.setAccepted(false);
1793 switch (mouseEvent.type()) {
1794 case QEvent::MouseMove:
1795 d->handleMouseMoveEvent(&mouseEvent);
1797 case QEvent::MouseButtonPress:
1798 if (d->pressed) // we are already pressed - this is a delayed replay
1801 d->handleMousePressEvent(&mouseEvent);
1802 d->captureDelayedPress(event);
1803 stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
1805 case QEvent::MouseButtonRelease:
1806 if (d->delayedPressEvent) {
1807 // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
1808 // so we reset the grabber
1809 if (c->mouseGrabberItem() == d->delayedPressTarget)
1810 d->delayedPressTarget->ungrabMouse();
1811 //Use the event handler that will take care of finding the proper item to propagate the event
1812 QQuickCanvasPrivate::get(canvas())->deliverMouseEvent(d->delayedPressEvent);
1813 d->clearDelayedPress();
1814 // We send the release
1815 canvas()->sendEvent(c->mouseGrabberItem(), event);
1816 // And the event has been consumed
1817 d->stealMouse = false;
1821 d->handleMouseReleaseEvent(&mouseEvent);
1826 grabber = qobject_cast<QQuickItem*>(c->mouseGrabberItem());
1827 if ((grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) || disabledItem) {
1828 d->clearDelayedPress();
1832 return stealThisEvent || d->delayedPressEvent || disabledItem;
1833 } else if (d->lastPosTime != -1) {
1834 d->lastPosTime = -1;
1837 if (event->type() == QEvent::MouseButtonRelease) {
1838 d->lastPosTime = -1;
1839 d->clearDelayedPress();
1840 d->stealMouse = false;
1847 bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e)
1849 Q_D(QQuickFlickable);
1850 if (!isVisible() || !isEnabled())
1851 return QQuickItem::childMouseEventFilter(i, e);
1852 switch (e->type()) {
1853 case QEvent::MouseButtonPress:
1854 case QEvent::MouseMove:
1855 case QEvent::MouseButtonRelease:
1856 return sendMouseEvent(static_cast<QMouseEvent *>(e));
1857 case QEvent::UngrabMouse:
1858 if (d->canvas && d->canvas->mouseGrabberItem() && d->canvas->mouseGrabberItem() != this) {
1859 // The grab has been taken away from a child and given to some other item.
1867 return QQuickItem::childMouseEventFilter(i, e);
1871 \qmlproperty real QtQuick2::Flickable::maximumFlickVelocity
1872 This property holds the maximum velocity that the user can flick the view in pixels/second.
1874 The default value is platform dependent.
1876 qreal QQuickFlickable::maximumFlickVelocity() const
1878 Q_D(const QQuickFlickable);
1879 return d->maxVelocity;
1882 void QQuickFlickable::setMaximumFlickVelocity(qreal v)
1884 Q_D(QQuickFlickable);
1885 if (v == d->maxVelocity)
1888 emit maximumFlickVelocityChanged();
1892 \qmlproperty real QtQuick2::Flickable::flickDeceleration
1893 This property holds the rate at which a flick will decelerate.
1895 The default value is platform dependent.
1897 qreal QQuickFlickable::flickDeceleration() const
1899 Q_D(const QQuickFlickable);
1900 return d->deceleration;
1903 void QQuickFlickable::setFlickDeceleration(qreal deceleration)
1905 Q_D(QQuickFlickable);
1906 if (deceleration == d->deceleration)
1908 d->deceleration = deceleration;
1909 emit flickDecelerationChanged();
1912 bool QQuickFlickable::isFlicking() const
1914 Q_D(const QQuickFlickable);
1915 return d->hData.flicking || d->vData.flicking;
1919 \qmlproperty bool QtQuick2::Flickable::flicking
1920 \qmlproperty bool QtQuick2::Flickable::flickingHorizontally
1921 \qmlproperty bool QtQuick2::Flickable::flickingVertically
1923 These properties describe whether the view is currently moving horizontally,
1924 vertically or in either direction, due to the user flicking the view.
1926 bool QQuickFlickable::isFlickingHorizontally() const
1928 Q_D(const QQuickFlickable);
1929 return d->hData.flicking;
1932 bool QQuickFlickable::isFlickingVertically() const
1934 Q_D(const QQuickFlickable);
1935 return d->vData.flicking;
1939 \qmlproperty bool QtQuick2::Flickable::dragging
1940 \qmlproperty bool QtQuick2::Flickable::draggingHorizontally
1941 \qmlproperty bool QtQuick2::Flickable::draggingVertically
1943 These properties describe whether the view is currently moving horizontally,
1944 vertically or in either direction, due to the user dragging the view.
1946 bool QQuickFlickable::isDragging() const
1948 Q_D(const QQuickFlickable);
1949 return d->hData.dragging || d->vData.dragging;
1952 bool QQuickFlickable::isDraggingHorizontally() const
1954 Q_D(const QQuickFlickable);
1955 return d->hData.dragging;
1958 bool QQuickFlickable::isDraggingVertically() const
1960 Q_D(const QQuickFlickable);
1961 return d->vData.dragging;
1964 void QQuickFlickablePrivate::draggingStarting()
1966 Q_Q(QQuickFlickable);
1967 bool wasDragging = hData.dragging || vData.dragging;
1968 if (hMoved && !hData.dragging) {
1969 hData.dragging = true;
1970 emit q->draggingHorizontallyChanged();
1972 if (vMoved && !vData.dragging) {
1973 vData.dragging = true;
1974 emit q->draggingVerticallyChanged();
1976 if (!wasDragging && (hData.dragging || vData.dragging)) {
1977 emit q->draggingChanged();
1978 emit q->dragStarted();
1982 void QQuickFlickablePrivate::draggingEnding()
1984 Q_Q(QQuickFlickable);
1985 bool wasDragging = hData.dragging || vData.dragging;
1986 if (hData.dragging) {
1987 hData.dragging = false;
1988 emit q->draggingHorizontallyChanged();
1990 if (vData.dragging) {
1991 vData.dragging = false;
1992 emit q->draggingVerticallyChanged();
1994 if (wasDragging && !hData.dragging && !vData.dragging) {
1995 emit q->draggingChanged();
1996 emit q->dragEnded();
2001 \qmlproperty int QtQuick2::Flickable::pressDelay
2003 This property holds the time to delay (ms) delivering a press to
2004 children of the Flickable. This can be useful where reacting
2005 to a press before a flicking action has undesirable effects.
2007 If the flickable is dragged/flicked before the delay times out
2008 the press event will not be delivered. If the button is released
2009 within the timeout, both the press and release will be delivered.
2011 Note that for nested Flickables with pressDelay set, the pressDelay of
2012 inner Flickables is overridden by the outermost Flickable.
2014 int QQuickFlickable::pressDelay() const
2016 Q_D(const QQuickFlickable);
2017 return d->pressDelay;
2020 void QQuickFlickable::setPressDelay(int delay)
2022 Q_D(QQuickFlickable);
2023 if (d->pressDelay == delay)
2025 d->pressDelay = delay;
2026 emit pressDelayChanged();
2030 \qmlproperty bool QtQuick2::Flickable::moving
2031 \qmlproperty bool QtQuick2::Flickable::movingHorizontally
2032 \qmlproperty bool QtQuick2::Flickable::movingVertically
2034 These properties describe whether the view is currently moving horizontally,
2035 vertically or in either direction, due to the user either dragging or
2039 bool QQuickFlickable::isMoving() const
2041 Q_D(const QQuickFlickable);
2042 return d->hData.moving || d->vData.moving;
2045 bool QQuickFlickable::isMovingHorizontally() const
2047 Q_D(const QQuickFlickable);
2048 return d->hData.moving;
2051 bool QQuickFlickable::isMovingVertically() const
2053 Q_D(const QQuickFlickable);
2054 return d->vData.moving;
2057 void QQuickFlickable::movementStarting()
2059 Q_D(QQuickFlickable);
2060 if (d->hMoved && !d->hData.moving) {
2061 d->hData.moving = true;
2062 emit movingChanged();
2063 emit movingHorizontallyChanged();
2064 if (!d->vData.moving)
2065 emit movementStarted();
2067 else if (d->vMoved && !d->vData.moving) {
2068 d->vData.moving = true;
2069 emit movingChanged();
2070 emit movingVerticallyChanged();
2071 if (!d->hData.moving)
2072 emit movementStarted();
2076 void QQuickFlickable::movementEnding()
2078 Q_D(QQuickFlickable);
2081 d->hData.smoothVelocity.setValue(0);
2082 d->vData.smoothVelocity.setValue(0);
2085 void QQuickFlickable::movementXEnding()
2087 Q_D(QQuickFlickable);
2088 if (d->hData.flicking) {
2089 d->hData.flicking = false;
2090 emit flickingChanged();
2091 emit flickingHorizontallyChanged();
2092 if (!d->vData.flicking)
2095 if (!d->pressed && !d->stealMouse) {
2096 if (d->hData.moving) {
2097 d->hData.moving = false;
2099 emit movingChanged();
2100 emit movingHorizontallyChanged();
2101 if (!d->vData.moving)
2102 emit movementEnded();
2105 d->hData.fixingUp = false;
2108 void QQuickFlickable::movementYEnding()
2110 Q_D(QQuickFlickable);
2111 if (d->vData.flicking) {
2112 d->vData.flicking = false;
2113 emit flickingChanged();
2114 emit flickingVerticallyChanged();
2115 if (!d->hData.flicking)
2118 if (!d->pressed && !d->stealMouse) {
2119 if (d->vData.moving) {
2120 d->vData.moving = false;
2122 emit movingChanged();
2123 emit movingVerticallyChanged();
2124 if (!d->hData.moving)
2125 emit movementEnded();
2128 d->vData.fixingUp = false;
2131 void QQuickFlickablePrivate::updateVelocity()
2133 Q_Q(QQuickFlickable);
2134 emit q->horizontalVelocityChanged();
2135 emit q->verticalVelocityChanged();