1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "private/qdeclarativeflickable_p.h"
43 #include "private/qdeclarativeflickable_p_p.h"
44 #include <qdeclarativeinfo.h>
45 #include <QGraphicsSceneMouseEvent>
48 #include "qplatformdefs.h"
52 // The maximum number of pixels a flick can overshoot
53 #ifndef QML_FLICK_OVERSHOOT
54 #define QML_FLICK_OVERSHOOT 200
57 // The number of samples to use in calculating the velocity of a flick
58 #ifndef QML_FLICK_SAMPLEBUFFER
59 #define QML_FLICK_SAMPLEBUFFER 3
62 // The number of samples to discard when calculating the flick velocity.
63 // Touch panels often produce inaccurate results as the finger is lifted.
64 #ifndef QML_FLICK_DISCARDSAMPLES
65 #define QML_FLICK_DISCARDSAMPLES 1
68 // The default maximum velocity of a flick.
69 #ifndef QML_FLICK_DEFAULTMAXVELOCITY
70 #define QML_FLICK_DEFAULTMAXVELOCITY 2500
73 // The default deceleration of a flick.
74 #ifndef QML_FLICK_DEFAULTDECELERATION
75 #define QML_FLICK_DEFAULTDECELERATION 1750
78 // How much faster to decelerate when overshooting
79 #ifndef QML_FLICK_OVERSHOOTFRICTION
80 #define QML_FLICK_OVERSHOOTFRICTION 8
83 // FlickThreshold determines how far the "mouse" must have moved
84 // before we perform a flick.
85 static const int FlickThreshold = 20;
87 // RetainGrabVelocity is the maxmimum instantaneous velocity that
88 // will ensure the Flickable retains the grab on consecutive flicks.
89 static const int RetainGrabVelocity = 15;
91 QDeclarativeFlickableVisibleArea::QDeclarativeFlickableVisibleArea(QDeclarativeFlickable *parent)
92 : QObject(parent), flickable(parent), m_xPosition(0.), m_widthRatio(0.)
93 , m_yPosition(0.), m_heightRatio(0.)
97 qreal QDeclarativeFlickableVisibleArea::widthRatio() const
102 qreal QDeclarativeFlickableVisibleArea::xPosition() const
107 qreal QDeclarativeFlickableVisibleArea::heightRatio() const
109 return m_heightRatio;
112 qreal QDeclarativeFlickableVisibleArea::yPosition() const
117 void QDeclarativeFlickableVisibleArea::updateVisible()
119 QDeclarativeFlickablePrivate *p = static_cast<QDeclarativeFlickablePrivate *>(QGraphicsItemPrivate::get(flickable));
121 bool changeX = false;
122 bool changeY = false;
123 bool changeWidth = false;
124 bool changeHeight = false;
127 const qreal viewheight = flickable->height();
128 const qreal maxyextent = -flickable->maxYExtent() + flickable->minYExtent();
129 qreal pagePos = (-p->vData.move.value() + flickable->minYExtent()) / (maxyextent + viewheight);
130 qreal pageSize = viewheight / (maxyextent + viewheight);
132 if (pageSize != m_heightRatio) {
133 m_heightRatio = pageSize;
136 if (pagePos != m_yPosition) {
137 m_yPosition = pagePos;
142 const qreal viewwidth = flickable->width();
143 const qreal maxxextent = -flickable->maxXExtent() + flickable->minXExtent();
144 pagePos = (-p->hData.move.value() + flickable->minXExtent()) / (maxxextent + viewwidth);
145 pageSize = viewwidth / (maxxextent + viewwidth);
147 if (pageSize != m_widthRatio) {
148 m_widthRatio = pageSize;
151 if (pagePos != m_xPosition) {
152 m_xPosition = pagePos;
157 emit xPositionChanged(m_xPosition);
159 emit yPositionChanged(m_yPosition);
161 emit widthRatioChanged(m_widthRatio);
163 emit heightRatioChanged(m_heightRatio);
167 QDeclarativeFlickablePrivate::QDeclarativeFlickablePrivate()
168 : contentItem(new QDeclarativeItem)
169 , hData(this, &QDeclarativeFlickablePrivate::setRoundedViewportX)
170 , vData(this, &QDeclarativeFlickablePrivate::setRoundedViewportY)
171 , hMoved(false), vMoved(false)
172 , stealMouse(false), pressed(false), interactive(true), calcVelocity(false)
173 , deceleration(QML_FLICK_DEFAULTDECELERATION)
174 , maxVelocity(QML_FLICK_DEFAULTMAXVELOCITY), reportedVelocitySmoothing(100)
175 , delayedPressEvent(0), delayedPressTarget(0), pressDelay(0), fixupDuration(400)
176 , fixupMode(Normal), vTime(0), visibleArea(0)
177 , flickableDirection(QDeclarativeFlickable::AutoFlickDirection)
178 , boundsBehavior(QDeclarativeFlickable::DragAndOvershootBounds)
182 void QDeclarativeFlickablePrivate::init()
184 Q_Q(QDeclarativeFlickable);
185 QDeclarative_setParent_noEvent(contentItem, q);
186 contentItem->setParentItem(q);
187 static int timelineUpdatedIdx = -1;
188 static int timelineCompletedIdx = -1;
189 static int flickableTickedIdx = -1;
190 static int flickableMovementEndingIdx = -1;
191 if (timelineUpdatedIdx == -1) {
192 timelineUpdatedIdx = QDeclarativeTimeLine::staticMetaObject.indexOfSignal("updated()");
193 timelineCompletedIdx = QDeclarativeTimeLine::staticMetaObject.indexOfSignal("completed()");
194 flickableTickedIdx = QDeclarativeFlickable::staticMetaObject.indexOfSlot("ticked()");
195 flickableMovementEndingIdx = QDeclarativeFlickable::staticMetaObject.indexOfSlot("movementEnding()");
197 QMetaObject::connect(&timeline, timelineUpdatedIdx,
198 q, flickableTickedIdx, Qt::DirectConnection);
199 QMetaObject::connect(&timeline, timelineCompletedIdx,
200 q, flickableMovementEndingIdx, Qt::DirectConnection);
201 q->setAcceptedMouseButtons(Qt::LeftButton);
202 q->setFiltersChildEvents(true);
203 QDeclarativeItemPrivate *viewportPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(contentItem));
204 viewportPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
205 lastPosTime.invalidate();
209 Returns the amount to overshoot by given a view size.
210 Will be up to the lesser of 1/3 of the view size or QML_FLICK_OVERSHOOT
212 qreal QDeclarativeFlickablePrivate::overShootDistance(qreal size)
214 if (maxVelocity <= 0)
217 return qMin(qreal(QML_FLICK_OVERSHOOT), size/3);
220 void QDeclarativeFlickablePrivate::AxisData::addVelocitySample(qreal v, qreal maxVelocity)
224 else if (v < -maxVelocity)
226 velocityBuffer.append(v);
227 if (velocityBuffer.count() > QML_FLICK_SAMPLEBUFFER)
228 velocityBuffer.remove(0);
231 void QDeclarativeFlickablePrivate::AxisData::updateVelocity()
234 if (velocityBuffer.count() > QML_FLICK_DISCARDSAMPLES) {
235 int count = velocityBuffer.count()-QML_FLICK_DISCARDSAMPLES;
236 for (int i = 0; i < count; ++i) {
237 qreal v = velocityBuffer.at(i);
244 void QDeclarativeFlickablePrivate::itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeom, const QRectF &oldGeom)
246 Q_Q(QDeclarativeFlickable);
247 if (item == contentItem) {
248 if (newGeom.x() != oldGeom.x())
249 emit q->contentXChanged();
250 if (newGeom.y() != oldGeom.y())
251 emit q->contentYChanged();
255 void QDeclarativeFlickablePrivate::flickX(qreal velocity)
257 Q_Q(QDeclarativeFlickable);
258 flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, velocity);
261 void QDeclarativeFlickablePrivate::flickY(qreal velocity)
263 Q_Q(QDeclarativeFlickable);
264 flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, velocity);
267 void QDeclarativeFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal,
268 QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
270 Q_Q(QDeclarativeFlickable);
271 qreal maxDistance = -1;
272 data.fixingUp = false;
273 // -ve velocity means list is moving up
275 maxDistance = qAbs(minExtent - data.move.value());
276 data.flickTarget = minExtent;
278 maxDistance = qAbs(maxExtent - data.move.value());
279 data.flickTarget = maxExtent;
281 if (maxDistance > 0) {
283 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
289 timeline.reset(data.move);
290 if (boundsBehavior == QDeclarativeFlickable::DragAndOvershootBounds)
291 timeline.accel(data.move, v, deceleration);
293 timeline.accel(data.move, v, deceleration, maxDistance);
294 timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
295 if (!hData.flicking && q->xflick()) {
296 hData.flicking = true;
297 emit q->flickingChanged();
298 emit q->flickingHorizontallyChanged();
300 emit q->flickStarted();
302 if (!vData.flicking && q->yflick()) {
303 vData.flicking = true;
304 emit q->flickingChanged();
305 emit q->flickingVerticallyChanged();
307 emit q->flickStarted();
310 timeline.reset(data.move);
311 fixup(data, minExtent, maxExtent);
315 void QDeclarativeFlickablePrivate::fixupY_callback(void *data)
317 ((QDeclarativeFlickablePrivate *)data)->fixupY();
320 void QDeclarativeFlickablePrivate::fixupX_callback(void *data)
322 ((QDeclarativeFlickablePrivate *)data)->fixupX();
325 void QDeclarativeFlickablePrivate::fixupX()
327 Q_Q(QDeclarativeFlickable);
328 fixup(hData, q->minXExtent(), q->maxXExtent());
331 void QDeclarativeFlickablePrivate::fixupY()
333 Q_Q(QDeclarativeFlickable);
334 fixup(vData, q->minYExtent(), q->maxYExtent());
337 void QDeclarativeFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
339 if (data.move.value() > minExtent || maxExtent > minExtent) {
340 timeline.reset(data.move);
341 if (data.move.value() != minExtent) {
344 timeline.set(data.move, minExtent);
347 // The target has changed. Don't start from the beginning; just complete the
348 // second half of the animation using the new extent.
349 timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
350 data.fixingUp = true;
353 qreal dist = minExtent - data.move;
354 timeline.move(data.move, minExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4);
355 timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
356 data.fixingUp = true;
360 } else if (data.move.value() < maxExtent) {
361 timeline.reset(data.move);
364 timeline.set(data.move, maxExtent);
367 // The target has changed. Don't start from the beginning; just complete the
368 // second half of the animation using the new extent.
369 timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
370 data.fixingUp = true;
373 qreal dist = maxExtent - data.move;
374 timeline.move(data.move, maxExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4);
375 timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
376 data.fixingUp = true;
380 data.inOvershoot = false;
382 vTime = timeline.time();
385 void QDeclarativeFlickablePrivate::updateBeginningEnd()
387 Q_Q(QDeclarativeFlickable);
388 bool atBoundaryChange = false;
391 const int maxyextent = int(-q->maxYExtent());
392 const qreal ypos = -vData.move.value();
393 bool atBeginning = (ypos <= -q->minYExtent());
394 bool atEnd = (maxyextent <= ypos);
396 if (atBeginning != vData.atBeginning) {
397 vData.atBeginning = atBeginning;
398 atBoundaryChange = true;
400 if (atEnd != vData.atEnd) {
402 atBoundaryChange = true;
406 const int maxxextent = int(-q->maxXExtent());
407 const qreal xpos = -hData.move.value();
408 atBeginning = (xpos <= -q->minXExtent());
409 atEnd = (maxxextent <= xpos);
411 if (atBeginning != hData.atBeginning) {
412 hData.atBeginning = atBeginning;
413 atBoundaryChange = true;
415 if (atEnd != hData.atEnd) {
417 atBoundaryChange = true;
420 if (atBoundaryChange)
421 emit q->isAtBoundaryChanged();
424 visibleArea->updateVisible();
428 \qmlclass Flickable QDeclarativeFlickable
430 \ingroup qml-basic-interaction-elements
432 \brief The Flickable item provides a surface that can be "flicked".
435 The Flickable item places its children on a surface that can be dragged
436 and flicked, causing the view onto the child items to scroll. This
437 behavior forms the basis of Items that are designed to show large numbers
438 of child items, such as \l ListView and \l GridView.
440 In traditional user interfaces, views can be scrolled using standard
441 controls, such as scroll bars and arrow buttons. In some situations, it
442 is also possible to drag the view directly by pressing and holding a
443 mouse button while moving the cursor. In touch-based user interfaces,
444 this dragging action is often complemented with a flicking action, where
445 scrolling continues after the user has stopped touching the view.
447 Flickable does not automatically clip its contents. If it is not used as
448 a full-screen item, you should consider setting the \l{Item::}{clip} property
451 \section1 Example Usage
453 \div {class="float-right"}
454 \inlineimage flickable.gif
457 The following example shows a small view onto a large image in which the
458 user can drag or flick the image in order to view different parts of it.
460 \snippet doc/src/snippets/declarative/flickable.qml document
464 Items declared as children of a Flickable are automatically parented to the
465 Flickable's \l contentItem. This should be taken into account when
466 operating on the children of the Flickable; it is usually the children of
467 \c contentItem that are relevant. For example, the bound of Items added
468 to the Flickable will be available by \c contentItem.childrenRect
470 \section1 Limitations
472 \note Due to an implementation detail, items placed inside a Flickable cannot anchor to it by
473 \c id. Use \c parent instead.
477 \qmlsignal Flickable::onMovementStarted()
479 This handler is called when the view begins moving due to user
484 \qmlsignal Flickable::onMovementEnded()
486 This handler is called when the view stops moving due to user
487 interaction. If a flick was generated, this handler will
488 be triggered once the flick stops. If a flick was not
489 generated, the handler will be triggered when the
490 user stops dragging - i.e. a mouse or touch release.
494 \qmlsignal Flickable::onFlickStarted()
496 This handler is called when the view is flicked. A flick
497 starts from the point that the mouse or touch is released,
498 while still in motion.
502 \qmlsignal Flickable::onFlickEnded()
504 This handler is called when the view stops moving due to a flick.
508 \qmlproperty real Flickable::visibleArea.xPosition
509 \qmlproperty real Flickable::visibleArea.widthRatio
510 \qmlproperty real Flickable::visibleArea.yPosition
511 \qmlproperty real Flickable::visibleArea.heightRatio
513 These properties describe the position and size of the currently viewed area.
514 The size is defined as the percentage of the full view currently visible,
515 scaled to 0.0 - 1.0. The page position is usually in the range 0.0 (beginning) to
516 1.0 minus size ratio (end), i.e. \c yPosition is in the range 0.0 to 1.0-\c heightRatio.
517 However, it is possible for the contents to be dragged outside of the normal
518 range, resulting in the page positions also being outside the normal range.
520 These properties are typically used to draw a scrollbar. For example:
522 \snippet doc/src/snippets/declarative/flickableScrollbar.qml 0
524 \snippet doc/src/snippets/declarative/flickableScrollbar.qml 1
526 \sa {declarative/ui-components/scrollbar}{scrollbar example}
529 QDeclarativeFlickable::QDeclarativeFlickable(QDeclarativeItem *parent)
530 : QDeclarativeItem(*(new QDeclarativeFlickablePrivate), parent)
532 Q_D(QDeclarativeFlickable);
536 QDeclarativeFlickable::QDeclarativeFlickable(QDeclarativeFlickablePrivate &dd, QDeclarativeItem *parent)
537 : QDeclarativeItem(dd, parent)
539 Q_D(QDeclarativeFlickable);
543 QDeclarativeFlickable::~QDeclarativeFlickable()
548 \qmlproperty real Flickable::contentX
549 \qmlproperty real Flickable::contentY
551 These properties hold the surface coordinate currently at the top-left
552 corner of the Flickable. For example, if you flick an image up 100 pixels,
553 \c contentY will be 100.
555 qreal QDeclarativeFlickable::contentX() const
557 Q_D(const QDeclarativeFlickable);
558 return -d->contentItem->x();
561 void QDeclarativeFlickable::setContentX(qreal pos)
563 Q_D(QDeclarativeFlickable);
564 d->timeline.reset(d->hData.move);
565 d->vTime = d->timeline.time();
567 if (-pos != d->hData.move.value()) {
568 d->hData.move.setValue(-pos);
573 qreal QDeclarativeFlickable::contentY() const
575 Q_D(const QDeclarativeFlickable);
576 return -d->contentItem->y();
579 void QDeclarativeFlickable::setContentY(qreal pos)
581 Q_D(QDeclarativeFlickable);
582 d->timeline.reset(d->vData.move);
583 d->vTime = d->timeline.time();
585 if (-pos != d->vData.move.value()) {
586 d->vData.move.setValue(-pos);
592 \qmlproperty bool Flickable::interactive
594 This property describes whether the user can interact with the Flickable.
595 A user cannot drag or flick a Flickable that is not interactive.
597 By default, this property is true.
599 This property is useful for temporarily disabling flicking. This allows
600 special interaction with Flickable's children; for example, you might want
601 to freeze a flickable map while scrolling through a pop-up dialog that
602 is a child of the Flickable.
604 bool QDeclarativeFlickable::isInteractive() const
606 Q_D(const QDeclarativeFlickable);
607 return d->interactive;
610 void QDeclarativeFlickable::setInteractive(bool interactive)
612 Q_D(QDeclarativeFlickable);
613 if (interactive != d->interactive) {
614 d->interactive = interactive;
615 if (!interactive && (d->hData.flicking || d->vData.flicking)) {
617 d->vTime = d->timeline.time();
618 d->hData.flicking = false;
619 d->vData.flicking = false;
620 emit flickingChanged();
621 emit flickingHorizontallyChanged();
622 emit flickingVerticallyChanged();
625 emit interactiveChanged();
630 \qmlproperty real Flickable::horizontalVelocity
631 \qmlproperty real Flickable::verticalVelocity
633 The instantaneous velocity of movement along the x and y axes, in pixels/sec.
635 The reported velocity is smoothed to avoid erratic output.
637 qreal QDeclarativeFlickable::horizontalVelocity() const
639 Q_D(const QDeclarativeFlickable);
640 return d->hData.smoothVelocity.value();
643 qreal QDeclarativeFlickable::verticalVelocity() const
645 Q_D(const QDeclarativeFlickable);
646 return d->vData.smoothVelocity.value();
650 \qmlproperty bool Flickable::atXBeginning
651 \qmlproperty bool Flickable::atXEnd
652 \qmlproperty bool Flickable::atYBeginning
653 \qmlproperty bool Flickable::atYEnd
655 These properties are true if the flickable view is positioned at the beginning,
658 bool QDeclarativeFlickable::isAtXEnd() const
660 Q_D(const QDeclarativeFlickable);
661 return d->hData.atEnd;
664 bool QDeclarativeFlickable::isAtXBeginning() const
666 Q_D(const QDeclarativeFlickable);
667 return d->hData.atBeginning;
670 bool QDeclarativeFlickable::isAtYEnd() const
672 Q_D(const QDeclarativeFlickable);
673 return d->vData.atEnd;
676 bool QDeclarativeFlickable::isAtYBeginning() const
678 Q_D(const QDeclarativeFlickable);
679 return d->vData.atBeginning;
682 void QDeclarativeFlickable::ticked()
688 \qmlproperty Item Flickable::contentItem
690 The internal item that contains the Items to be moved in the Flickable.
692 Items declared as children of a Flickable are automatically parented to the Flickable's contentItem.
694 Items created dynamically need to be explicitly parented to the \e contentItem:
698 function addItem(file) {
699 var component = Qt.createComponent(file)
700 component.createObject(myFlickable.contentItem);
705 QDeclarativeItem *QDeclarativeFlickable::contentItem()
707 Q_D(QDeclarativeFlickable);
708 return d->contentItem;
711 QDeclarativeFlickableVisibleArea *QDeclarativeFlickable::visibleArea()
713 Q_D(QDeclarativeFlickable);
715 d->visibleArea = new QDeclarativeFlickableVisibleArea(this);
716 return d->visibleArea;
720 \qmlproperty enumeration Flickable::flickableDirection
722 This property determines which directions the view can be flicked.
725 \o Flickable.AutoFlickDirection (default) - allows flicking vertically if the
726 \e contentHeight is not equal to the \e height of the Flickable.
727 Allows flicking horizontally if the \e contentWidth is not equal
728 to the \e width of the Flickable.
729 \o Flickable.HorizontalFlick - allows flicking horizontally.
730 \o Flickable.VerticalFlick - allows flicking vertically.
731 \o Flickable.HorizontalAndVerticalFlick - allows flicking in both directions.
734 QDeclarativeFlickable::FlickableDirection QDeclarativeFlickable::flickableDirection() const
736 Q_D(const QDeclarativeFlickable);
737 return d->flickableDirection;
740 void QDeclarativeFlickable::setFlickableDirection(FlickableDirection direction)
742 Q_D(QDeclarativeFlickable);
743 if (direction != d->flickableDirection) {
744 d->flickableDirection = direction;
745 emit flickableDirectionChanged();
749 void QDeclarativeFlickablePrivate::handleMousePressEvent(QGraphicsSceneMouseEvent *event)
751 Q_Q(QDeclarativeFlickable);
752 if (interactive && timeline.isActive()
753 && (qAbs(hData.smoothVelocity.value()) > RetainGrabVelocity || qAbs(vData.smoothVelocity.value()) > RetainGrabVelocity))
754 stealMouse = true; // If we've been flicked then steal the click.
757 q->setKeepMouseGrab(stealMouse);
762 hData.dragMinBound = q->minXExtent();
763 vData.dragMinBound = q->minYExtent();
764 hData.dragMaxBound = q->maxXExtent();
765 vData.dragMaxBound = q->maxYExtent();
768 QDeclarativeItemPrivate::start(lastPosTime);
769 pressPos = event->pos();
770 hData.pressPos = hData.move.value();
771 vData.pressPos = vData.move.value();
772 hData.flicking = false;
773 vData.flicking = false;
774 QDeclarativeItemPrivate::start(pressTime);
775 QDeclarativeItemPrivate::start(velocityTime);
778 void QDeclarativeFlickablePrivate::handleMouseMoveEvent(QGraphicsSceneMouseEvent *event)
780 Q_Q(QDeclarativeFlickable);
781 if (!interactive || !lastPosTime.isValid())
783 bool rejectY = false;
784 bool rejectX = false;
786 bool stealY = stealMouse;
787 bool stealX = stealMouse;
790 int dy = int(event->pos().y() - pressPos.y());
791 if (qAbs(dy) > QApplication::startDragDistance() || QDeclarativeItemPrivate::elapsed(pressTime) > 200) {
793 vData.dragStartOffset = dy;
794 qreal newY = dy + vData.pressPos - vData.dragStartOffset;
795 const qreal minY = vData.dragMinBound;
796 const qreal maxY = vData.dragMaxBound;
798 newY = minY + (newY - minY) / 2;
799 if (newY < maxY && maxY - minY <= 0)
800 newY = maxY + (newY - maxY) / 2;
801 if (boundsBehavior == QDeclarativeFlickable::StopAtBounds && (newY > minY || newY < maxY)) {
812 if (!rejectY && stealMouse) {
813 vData.move.setValue(qRound(newY));
816 if (qAbs(dy) > QApplication::startDragDistance())
822 int dx = int(event->pos().x() - pressPos.x());
823 if (qAbs(dx) > QApplication::startDragDistance() || QDeclarativeItemPrivate::elapsed(pressTime) > 200) {
825 hData.dragStartOffset = dx;
826 qreal newX = dx + hData.pressPos - hData.dragStartOffset;
827 const qreal minX = hData.dragMinBound;
828 const qreal maxX = hData.dragMaxBound;
830 newX = minX + (newX - minX) / 2;
831 if (newX < maxX && maxX - minX <= 0)
832 newX = maxX + (newX - maxX) / 2;
833 if (boundsBehavior == QDeclarativeFlickable::StopAtBounds && (newX > minX || newX < maxX)) {
844 if (!rejectX && stealMouse) {
845 hData.move.setValue(qRound(newX));
849 if (qAbs(dx) > QApplication::startDragDistance())
854 stealMouse = stealX || stealY;
856 q->setKeepMouseGrab(true);
859 vData.velocityBuffer.clear();
863 hData.velocityBuffer.clear();
867 if (hMoved || vMoved) {
868 q->movementStarting();
872 if (!lastPos.isNull()) {
873 qreal elapsed = qreal(QDeclarativeItemPrivate::elapsed(lastPosTime)) / 1000.;
876 QDeclarativeItemPrivate::restart(lastPosTime);
877 qreal dy = event->pos().y()-lastPos.y();
878 if (q->yflick() && !rejectY)
879 vData.addVelocitySample(dy/elapsed, maxVelocity);
880 qreal dx = event->pos().x()-lastPos.x();
881 if (q->xflick() && !rejectX)
882 hData.addVelocitySample(dx/elapsed, maxVelocity);
885 lastPos = event->pos();
888 void QDeclarativeFlickablePrivate::handleMouseReleaseEvent(QGraphicsSceneMouseEvent *event)
890 Q_Q(QDeclarativeFlickable);
892 q->setKeepMouseGrab(false);
894 if (!lastPosTime.isValid())
897 // if we drag then pause before release we should not cause a flick.
898 qint64 elapsed = QDeclarativeItemPrivate::elapsed(lastPosTime);
900 vData.updateVelocity();
901 hData.updateVelocity();
902 vTime = timeline.time();
904 qreal velocity = elapsed < 100 ? vData.velocity : 0;
905 if (vData.atBeginning || vData.atEnd)
907 if (q->yflick() && qAbs(velocity) > MinimumFlickVelocity && qAbs(event->pos().y() - pressPos.y()) > FlickThreshold) {
908 velocityTimeline.reset(vData.smoothVelocity);
909 vData.smoothVelocity.setValue(-velocity);
915 velocity = elapsed < 100 ? hData.velocity : 0;
916 if (hData.atBeginning || hData.atEnd)
918 if (q->xflick() && qAbs(velocity) > MinimumFlickVelocity && qAbs(event->pos().x() - pressPos.x()) > FlickThreshold) {
919 velocityTimeline.reset(hData.smoothVelocity);
920 hData.smoothVelocity.setValue(-velocity);
926 if (!timeline.isActive())
930 void QDeclarativeFlickable::mousePressEvent(QGraphicsSceneMouseEvent *event)
932 Q_D(QDeclarativeFlickable);
933 if (d->interactive) {
935 d->handleMousePressEvent(event);
938 QDeclarativeItem::mousePressEvent(event);
942 void QDeclarativeFlickable::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
944 Q_D(QDeclarativeFlickable);
945 if (d->interactive) {
946 d->handleMouseMoveEvent(event);
949 QDeclarativeItem::mouseMoveEvent(event);
953 void QDeclarativeFlickable::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
955 Q_D(QDeclarativeFlickable);
956 if (d->interactive) {
957 d->clearDelayedPress();
958 d->handleMouseReleaseEvent(event);
962 QDeclarativeItem::mouseReleaseEvent(event);
966 void QDeclarativeFlickable::wheelEvent(QGraphicsSceneWheelEvent *event)
968 Q_D(QDeclarativeFlickable);
969 if (!d->interactive) {
970 QDeclarativeItem::wheelEvent(event);
971 } else if (yflick() && event->orientation() == Qt::Vertical) {
973 if (event->delta() > 0 && contentY() > -minYExtent()) {
974 d->vData.velocity = qMax(event->delta()*2 - d->vData.smoothVelocity.value(), qreal(d->maxVelocity/4));
976 } else if (event->delta() < 0 && contentY() < -maxYExtent()) {
977 d->vData.velocity = qMin(event->delta()*2 - d->vData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
981 d->vData.flicking = false;
982 d->flickY(d->vData.velocity);
983 if (d->vData.flicking) {
989 } else if (xflick() && event->orientation() == Qt::Horizontal) {
991 if (event->delta() > 0 && contentX() > -minXExtent()) {
992 d->hData.velocity = qMax(event->delta()*2 - d->hData.smoothVelocity.value(), qreal(d->maxVelocity/4));
994 } else if (event->delta() < 0 && contentX() < -maxXExtent()) {
995 d->hData.velocity = qMin(event->delta()*2 - d->hData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
999 d->hData.flicking = false;
1000 d->flickX(d->hData.velocity);
1001 if (d->hData.flicking) {
1008 QDeclarativeItem::wheelEvent(event);
1012 bool QDeclarativeFlickablePrivate::isOutermostPressDelay() const
1014 Q_Q(const QDeclarativeFlickable);
1015 QDeclarativeItem *item = q->parentItem();
1017 QDeclarativeFlickable *flick = qobject_cast<QDeclarativeFlickable*>(item);
1018 if (flick && flick->pressDelay() > 0 && flick->isInteractive())
1020 item = item->parentItem();
1026 void QDeclarativeFlickablePrivate::captureDelayedPress(QGraphicsSceneMouseEvent *event)
1028 Q_Q(QDeclarativeFlickable);
1029 if (!q->scene() || pressDelay <= 0)
1031 if (!isOutermostPressDelay())
1033 delayedPressTarget = q->scene()->mouseGrabberItem();
1034 delayedPressEvent = new QGraphicsSceneMouseEvent(event->type());
1035 delayedPressEvent->setAccepted(false);
1036 for (int i = 0x1; i <= 0x10; i <<= 1) {
1037 if (event->buttons() & i) {
1038 Qt::MouseButton button = Qt::MouseButton(i);
1039 delayedPressEvent->setButtonDownPos(button, event->buttonDownPos(button));
1040 delayedPressEvent->setButtonDownScenePos(button, event->buttonDownScenePos(button));
1041 delayedPressEvent->setButtonDownScreenPos(button, event->buttonDownScreenPos(button));
1044 delayedPressEvent->setButtons(event->buttons());
1045 delayedPressEvent->setButton(event->button());
1046 delayedPressEvent->setPos(event->pos());
1047 delayedPressEvent->setScenePos(event->scenePos());
1048 delayedPressEvent->setScreenPos(event->screenPos());
1049 delayedPressEvent->setLastPos(event->lastPos());
1050 delayedPressEvent->setLastScenePos(event->lastScenePos());
1051 delayedPressEvent->setLastScreenPos(event->lastScreenPos());
1052 delayedPressEvent->setModifiers(event->modifiers());
1053 delayedPressTimer.start(pressDelay, q);
1056 void QDeclarativeFlickablePrivate::clearDelayedPress()
1058 if (delayedPressEvent) {
1059 delayedPressTimer.stop();
1060 delete delayedPressEvent;
1061 delayedPressEvent = 0;
1065 void QDeclarativeFlickablePrivate::setRoundedViewportX(qreal x)
1067 contentItem->setX(qRound(x));
1070 void QDeclarativeFlickablePrivate::setRoundedViewportY(qreal y)
1072 contentItem->setY(qRound(y));
1075 void QDeclarativeFlickable::timerEvent(QTimerEvent *event)
1077 Q_D(QDeclarativeFlickable);
1078 if (event->timerId() == d->delayedPressTimer.timerId()) {
1079 d->delayedPressTimer.stop();
1080 if (d->delayedPressEvent) {
1081 QDeclarativeItem *grabber = scene() ? qobject_cast<QDeclarativeItem*>(scene()->mouseGrabberItem()) : 0;
1082 if (!grabber || grabber != this) {
1083 // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
1084 // so we reset the grabber
1085 if (scene()->mouseGrabberItem() == d->delayedPressTarget)
1086 d->delayedPressTarget->ungrabMouse();
1087 //Use the event handler that will take care of finding the proper item to propagate the event
1088 QApplication::postEvent(scene(), d->delayedPressEvent);
1090 delete d->delayedPressEvent;
1092 d->delayedPressEvent = 0;
1097 qreal QDeclarativeFlickable::minYExtent() const
1102 qreal QDeclarativeFlickable::minXExtent() const
1108 qreal QDeclarativeFlickable::maxXExtent() const
1110 return width() - vWidth();
1113 qreal QDeclarativeFlickable::maxYExtent() const
1115 return height() - vHeight();
1118 void QDeclarativeFlickable::viewportMoved()
1120 Q_D(QDeclarativeFlickable);
1122 qreal prevX = d->lastFlickablePosition.x();
1123 qreal prevY = d->lastFlickablePosition.y();
1124 if (d->pressed || d->calcVelocity) {
1125 int elapsed = QDeclarativeItemPrivate::restart(d->velocityTime);
1127 qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / elapsed;
1128 if (qAbs(horizontalVelocity) > 0) {
1129 d->velocityTimeline.reset(d->hData.smoothVelocity);
1130 d->velocityTimeline.move(d->hData.smoothVelocity, horizontalVelocity, d->reportedVelocitySmoothing);
1131 d->velocityTimeline.move(d->hData.smoothVelocity, 0, d->reportedVelocitySmoothing);
1133 qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / elapsed;
1134 if (qAbs(verticalVelocity) > 0) {
1135 d->velocityTimeline.reset(d->vData.smoothVelocity);
1136 d->velocityTimeline.move(d->vData.smoothVelocity, verticalVelocity, d->reportedVelocitySmoothing);
1137 d->velocityTimeline.move(d->vData.smoothVelocity, 0, d->reportedVelocitySmoothing);
1141 if (d->timeline.time() > d->vTime) {
1142 d->velocityTimeline.clear();
1143 qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / (d->timeline.time() - d->vTime);
1144 qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / (d->timeline.time() - d->vTime);
1145 d->hData.smoothVelocity.setValue(horizontalVelocity);
1146 d->vData.smoothVelocity.setValue(verticalVelocity);
1150 if (!d->vData.inOvershoot && !d->vData.fixingUp && d->vData.flicking
1151 && (d->vData.move.value() > minYExtent() || d->vData.move.value() < maxYExtent())
1152 && qAbs(d->vData.smoothVelocity.value()) > 100) {
1153 // Increase deceleration if we've passed a bound
1154 d->vData.inOvershoot = true;
1155 qreal maxDistance = d->overShootDistance(height());
1156 d->timeline.reset(d->vData.move);
1157 d->timeline.accel(d->vData.move, -d->vData.smoothVelocity.value(), d->deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance);
1158 d->timeline.callback(QDeclarativeTimeLineCallback(&d->vData.move, d->fixupY_callback, d));
1160 if (!d->hData.inOvershoot && !d->hData.fixingUp && d->hData.flicking
1161 && (d->hData.move.value() > minXExtent() || d->hData.move.value() < maxXExtent())
1162 && qAbs(d->hData.smoothVelocity.value()) > 100) {
1163 // Increase deceleration if we've passed a bound
1164 d->hData.inOvershoot = true;
1165 qreal maxDistance = d->overShootDistance(width());
1166 d->timeline.reset(d->hData.move);
1167 d->timeline.accel(d->hData.move, -d->hData.smoothVelocity.value(), d->deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance);
1168 d->timeline.callback(QDeclarativeTimeLineCallback(&d->hData.move, d->fixupX_callback, d));
1171 d->lastFlickablePosition = QPointF(d->hData.move.value(), d->vData.move.value());
1173 d->vTime = d->timeline.time();
1174 d->updateBeginningEnd();
1177 void QDeclarativeFlickable::geometryChanged(const QRectF &newGeometry,
1178 const QRectF &oldGeometry)
1180 Q_D(QDeclarativeFlickable);
1181 QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
1183 bool changed = false;
1184 if (newGeometry.width() != oldGeometry.width()) {
1187 if (d->hData.viewSize < 0) {
1188 d->contentItem->setWidth(width());
1189 emit contentWidthChanged();
1191 // Make sure that we're entirely in view.
1192 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1193 d->fixupMode = QDeclarativeFlickablePrivate::Immediate;
1197 if (newGeometry.height() != oldGeometry.height()) {
1200 if (d->vData.viewSize < 0) {
1201 d->contentItem->setHeight(height());
1202 emit contentHeightChanged();
1204 // Make sure that we're entirely in view.
1205 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1206 d->fixupMode = QDeclarativeFlickablePrivate::Immediate;
1212 d->updateBeginningEnd();
1215 void QDeclarativeFlickable::cancelFlick()
1217 Q_D(QDeclarativeFlickable);
1218 d->timeline.reset(d->hData.move);
1219 d->timeline.reset(d->vData.move);
1223 void QDeclarativeFlickablePrivate::data_append(QDeclarativeListProperty<QObject> *prop, QObject *o)
1225 QGraphicsObject *i = qobject_cast<QGraphicsObject *>(o);
1227 QGraphicsItemPrivate *d = QGraphicsItemPrivate::get(i);
1228 if (static_cast<QDeclarativeItemPrivate*>(d)->componentComplete) {
1229 i->setParentItem(static_cast<QDeclarativeFlickablePrivate*>(prop->data)->contentItem);
1231 d->setParentItemHelper(static_cast<QDeclarativeFlickablePrivate*>(prop->data)->contentItem, 0, 0);
1234 o->setParent(prop->object);
1238 int QDeclarativeFlickablePrivate::data_count(QDeclarativeListProperty<QObject> *property)
1240 QDeclarativeItem *contentItem= static_cast<QDeclarativeFlickablePrivate*>(property->data)->contentItem;
1241 return contentItem->childItems().count() + contentItem->children().count();
1244 QObject *QDeclarativeFlickablePrivate::data_at(QDeclarativeListProperty<QObject> *property, int index)
1246 QDeclarativeItem *contentItem = static_cast<QDeclarativeFlickablePrivate*>(property->data)->contentItem;
1248 int childItemCount = contentItem->childItems().count();
1253 if (index < childItemCount) {
1254 return contentItem->childItems().at(index)->toGraphicsObject();
1256 return contentItem->children().at(index - childItemCount);
1262 void QDeclarativeFlickablePrivate::data_clear(QDeclarativeListProperty<QObject> *property)
1264 QDeclarativeItem *contentItem = static_cast<QDeclarativeFlickablePrivate*>(property->data)->contentItem;
1266 const QList<QGraphicsItem*> graphicsItems = contentItem->childItems();
1267 for (int i = 0; i < graphicsItems.count(); i++)
1268 contentItem->scene()->removeItem(graphicsItems[i]);
1270 const QList<QObject*> objects = contentItem->children();
1271 for (int i = 0; i < objects.count(); i++)
1272 objects[i]->setParent(0);
1275 QDeclarativeListProperty<QObject> QDeclarativeFlickable::flickableData()
1277 Q_D(QDeclarativeFlickable);
1278 return QDeclarativeListProperty<QObject>(this, (void *)d, QDeclarativeFlickablePrivate::data_append,
1279 QDeclarativeFlickablePrivate::data_count,
1280 QDeclarativeFlickablePrivate::data_at,
1281 QDeclarativeFlickablePrivate::data_clear);
1284 QDeclarativeListProperty<QGraphicsObject> QDeclarativeFlickable::flickableChildren()
1286 Q_D(QDeclarativeFlickable);
1287 return QGraphicsItemPrivate::get(d->contentItem)->childrenList();
1291 \qmlproperty enumeration Flickable::boundsBehavior
1292 This property holds whether the surface may be dragged
1293 beyond the Fickable's boundaries, or overshoot the
1294 Flickable's boundaries when flicked.
1296 This enables the feeling that the edges of the view are soft,
1297 rather than a hard physical boundary.
1299 The \c boundsBehavior can be one of:
1302 \o Flickable.StopAtBounds - the contents can not be dragged beyond the boundary
1303 of the flickable, and flicks will not overshoot.
1304 \o Flickable.DragOverBounds - the contents can be dragged beyond the boundary
1305 of the Flickable, but flicks will not overshoot.
1306 \o Flickable.DragAndOvershootBounds (default) - the contents can be dragged
1307 beyond the boundary of the Flickable, and can overshoot the
1308 boundary when flicked.
1311 QDeclarativeFlickable::BoundsBehavior QDeclarativeFlickable::boundsBehavior() const
1313 Q_D(const QDeclarativeFlickable);
1314 return d->boundsBehavior;
1317 void QDeclarativeFlickable::setBoundsBehavior(BoundsBehavior b)
1319 Q_D(QDeclarativeFlickable);
1320 if (b == d->boundsBehavior)
1322 d->boundsBehavior = b;
1323 emit boundsBehaviorChanged();
1327 \qmlproperty real Flickable::contentWidth
1328 \qmlproperty real Flickable::contentHeight
1330 The dimensions of the content (the surface controlled by Flickable).
1331 This should typically be set to the combined size of the items placed in the
1334 The following snippet shows how these properties are used to display
1335 an image that is larger than the Flickable item itself:
1337 \snippet doc/src/snippets/declarative/flickable.qml document
1339 In some cases, the the content dimensions can be automatically set
1340 using the \l {Item::childrenRect.width}{childrenRect.width}
1341 and \l {Item::childrenRect.height}{childrenRect.height} properties.
1343 qreal QDeclarativeFlickable::contentWidth() const
1345 Q_D(const QDeclarativeFlickable);
1346 return d->hData.viewSize;
1349 void QDeclarativeFlickable::setContentWidth(qreal w)
1351 Q_D(QDeclarativeFlickable);
1352 if (d->hData.viewSize == w)
1354 d->hData.viewSize = w;
1356 d->contentItem->setWidth(width());
1358 d->contentItem->setWidth(w);
1359 // Make sure that we're entirely in view.
1360 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1361 d->fixupMode = QDeclarativeFlickablePrivate::Immediate;
1363 } else if (!d->pressed && d->hData.fixingUp) {
1364 d->fixupMode = QDeclarativeFlickablePrivate::ExtentChanged;
1367 emit contentWidthChanged();
1368 d->updateBeginningEnd();
1371 qreal QDeclarativeFlickable::contentHeight() const
1373 Q_D(const QDeclarativeFlickable);
1374 return d->vData.viewSize;
1377 void QDeclarativeFlickable::setContentHeight(qreal h)
1379 Q_D(QDeclarativeFlickable);
1380 if (d->vData.viewSize == h)
1382 d->vData.viewSize = h;
1384 d->contentItem->setHeight(height());
1386 d->contentItem->setHeight(h);
1387 // Make sure that we're entirely in view.
1388 if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1389 d->fixupMode = QDeclarativeFlickablePrivate::Immediate;
1391 } else if (!d->pressed && d->vData.fixingUp) {
1392 d->fixupMode = QDeclarativeFlickablePrivate::ExtentChanged;
1395 emit contentHeightChanged();
1396 d->updateBeginningEnd();
1400 \qmlmethod Flickable::resizeContent(real width, real height, QPointF center)
1404 Resizes the content to \a width x \a height about \a center.
1406 This does not scale the contents of the Flickable - it only resizes the \l contentWidth
1407 and \l contentHeight.
1409 Resizing the content may result in the content being positioned outside
1410 the bounds of the Flickable. Calling \l returnToBounds() will
1411 move the content back within legal bounds.
1413 void QDeclarativeFlickable::resizeContent(qreal w, qreal h, QPointF center)
1415 Q_D(QDeclarativeFlickable);
1416 if (w != d->hData.viewSize) {
1417 qreal oldSize = d->hData.viewSize;
1418 d->hData.viewSize = w;
1419 d->contentItem->setWidth(w);
1420 emit contentWidthChanged();
1421 if (center.x() != 0) {
1422 qreal pos = center.x() * w / oldSize;
1423 setContentX(contentX() + pos - center.x());
1426 if (h != d->vData.viewSize) {
1427 qreal oldSize = d->vData.viewSize;
1428 d->vData.viewSize = h;
1429 d->contentItem->setHeight(h);
1430 emit contentHeightChanged();
1431 if (center.y() != 0) {
1432 qreal pos = center.y() * h / oldSize;
1433 setContentY(contentY() + pos - center.y());
1436 d->updateBeginningEnd();
1440 \qmlmethod Flickable::returnToBounds()
1444 Ensures the content is within legal bounds.
1446 This may be called to ensure that the content is within legal bounds
1447 after manually positioning the content.
1449 void QDeclarativeFlickable::returnToBounds()
1451 Q_D(QDeclarativeFlickable);
1456 qreal QDeclarativeFlickable::vWidth() const
1458 Q_D(const QDeclarativeFlickable);
1459 if (d->hData.viewSize < 0)
1462 return d->hData.viewSize;
1465 qreal QDeclarativeFlickable::vHeight() const
1467 Q_D(const QDeclarativeFlickable);
1468 if (d->vData.viewSize < 0)
1471 return d->vData.viewSize;
1474 bool QDeclarativeFlickable::xflick() const
1476 Q_D(const QDeclarativeFlickable);
1477 if (d->flickableDirection == QDeclarativeFlickable::AutoFlickDirection)
1478 return vWidth() != width();
1479 return d->flickableDirection & QDeclarativeFlickable::HorizontalFlick;
1482 bool QDeclarativeFlickable::yflick() const
1484 Q_D(const QDeclarativeFlickable);
1485 if (d->flickableDirection == QDeclarativeFlickable::AutoFlickDirection)
1486 return vHeight() != height();
1487 return d->flickableDirection & QDeclarativeFlickable::VerticalFlick;
1490 bool QDeclarativeFlickable::sceneEvent(QEvent *event)
1492 bool rv = QDeclarativeItem::sceneEvent(event);
1493 if (event->type() == QEvent::UngrabMouse) {
1494 Q_D(QDeclarativeFlickable);
1496 // if our mouse grab has been removed (probably by another Flickable),
1499 d->stealMouse = false;
1500 setKeepMouseGrab(false);
1506 bool QDeclarativeFlickable::sendMouseEvent(QGraphicsSceneMouseEvent *event)
1508 Q_D(QDeclarativeFlickable);
1509 QGraphicsSceneMouseEvent mouseEvent(event->type());
1510 QRectF myRect = mapToScene(QRectF(0, 0, width(), height())).boundingRect();
1512 QGraphicsScene *s = scene();
1513 QDeclarativeItem *grabber = s ? qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem()) : 0;
1514 QGraphicsItem *grabberItem = s ? s->mouseGrabberItem() : 0;
1515 bool disabledItem = grabberItem && !grabberItem->isEnabled();
1516 bool stealThisEvent = d->stealMouse;
1517 if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab() || disabledItem)) {
1518 mouseEvent.setAccepted(false);
1519 for (int i = 0x1; i <= 0x10; i <<= 1) {
1520 if (event->buttons() & i) {
1521 Qt::MouseButton button = Qt::MouseButton(i);
1522 mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button)));
1525 mouseEvent.setScenePos(event->scenePos());
1526 mouseEvent.setLastScenePos(event->lastScenePos());
1527 mouseEvent.setPos(mapFromScene(event->scenePos()));
1528 mouseEvent.setLastPos(mapFromScene(event->lastScenePos()));
1530 switch(mouseEvent.type()) {
1531 case QEvent::GraphicsSceneMouseMove:
1532 d->handleMouseMoveEvent(&mouseEvent);
1534 case QEvent::GraphicsSceneMousePress:
1535 if (d->pressed && !event->spontaneous()) // we are already pressed - this is a delayed replay
1538 d->handleMousePressEvent(&mouseEvent);
1539 d->captureDelayedPress(event);
1540 stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
1542 case QEvent::GraphicsSceneMouseRelease:
1543 if (d->delayedPressEvent) {
1544 // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
1545 // so we reset the grabber
1546 if (s->mouseGrabberItem() == d->delayedPressTarget)
1547 d->delayedPressTarget->ungrabMouse();
1548 //Use the event handler that will take care of finding the proper item to propagate the event
1549 QApplication::sendEvent(scene(), d->delayedPressEvent);
1550 d->clearDelayedPress();
1551 // We send the release
1552 scene()->sendEvent(s->mouseGrabberItem(), event);
1553 // And the event has been consumed
1554 d->stealMouse = false;
1558 d->handleMouseReleaseEvent(&mouseEvent);
1563 grabber = qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem());
1564 if ((grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) || disabledItem) {
1565 d->clearDelayedPress();
1569 return stealThisEvent || d->delayedPressEvent || disabledItem;
1570 } else if (d->lastPosTime.isValid()) {
1571 d->lastPosTime.invalidate();
1573 if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease) {
1574 d->clearDelayedPress();
1575 d->stealMouse = false;
1582 bool QDeclarativeFlickable::sceneEventFilter(QGraphicsItem *i, QEvent *e)
1584 Q_D(QDeclarativeFlickable);
1585 if (!isVisible() || !d->interactive)
1586 return QDeclarativeItem::sceneEventFilter(i, e);
1587 switch (e->type()) {
1588 case QEvent::GraphicsSceneMousePress:
1589 case QEvent::GraphicsSceneMouseMove:
1590 case QEvent::GraphicsSceneMouseRelease:
1591 return sendMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e));
1596 return QDeclarativeItem::sceneEventFilter(i, e);
1600 \qmlproperty real Flickable::maximumFlickVelocity
1601 This property holds the maximum velocity that the user can flick the view in pixels/second.
1603 The default value is platform dependent.
1605 qreal QDeclarativeFlickable::maximumFlickVelocity() const
1607 Q_D(const QDeclarativeFlickable);
1608 return d->maxVelocity;
1611 void QDeclarativeFlickable::setMaximumFlickVelocity(qreal v)
1613 Q_D(QDeclarativeFlickable);
1614 if (v == d->maxVelocity)
1617 emit maximumFlickVelocityChanged();
1621 \qmlproperty real Flickable::flickDeceleration
1622 This property holds the rate at which a flick will decelerate.
1624 The default value is platform dependent.
1626 qreal QDeclarativeFlickable::flickDeceleration() const
1628 Q_D(const QDeclarativeFlickable);
1629 return d->deceleration;
1632 void QDeclarativeFlickable::setFlickDeceleration(qreal deceleration)
1634 Q_D(QDeclarativeFlickable);
1635 if (deceleration == d->deceleration)
1637 d->deceleration = deceleration;
1638 emit flickDecelerationChanged();
1641 bool QDeclarativeFlickable::isFlicking() const
1643 Q_D(const QDeclarativeFlickable);
1644 return d->hData.flicking || d->vData.flicking;
1648 \qmlproperty bool Flickable::flicking
1649 \qmlproperty bool Flickable::flickingHorizontally
1650 \qmlproperty bool Flickable::flickingVertically
1652 These properties describe whether the view is currently moving horizontally,
1653 vertically or in either direction, due to the user flicking the view.
1655 bool QDeclarativeFlickable::isFlickingHorizontally() const
1657 Q_D(const QDeclarativeFlickable);
1658 return d->hData.flicking;
1661 bool QDeclarativeFlickable::isFlickingVertically() const
1663 Q_D(const QDeclarativeFlickable);
1664 return d->vData.flicking;
1668 \qmlproperty int Flickable::pressDelay
1670 This property holds the time to delay (ms) delivering a press to
1671 children of the Flickable. This can be useful where reacting
1672 to a press before a flicking action has undesirable effects.
1674 If the flickable is dragged/flicked before the delay times out
1675 the press event will not be delivered. If the button is released
1676 within the timeout, both the press and release will be delivered.
1678 Note that for nested Flickables with pressDelay set, the pressDelay of
1679 inner Flickables is overridden by the outermost Flickable.
1681 int QDeclarativeFlickable::pressDelay() const
1683 Q_D(const QDeclarativeFlickable);
1684 return d->pressDelay;
1687 void QDeclarativeFlickable::setPressDelay(int delay)
1689 Q_D(QDeclarativeFlickable);
1690 if (d->pressDelay == delay)
1692 d->pressDelay = delay;
1693 emit pressDelayChanged();
1697 bool QDeclarativeFlickable::isMoving() const
1699 Q_D(const QDeclarativeFlickable);
1700 return d->hData.moving || d->vData.moving;
1704 \qmlproperty bool Flickable::moving
1705 \qmlproperty bool Flickable::movingHorizontally
1706 \qmlproperty bool Flickable::movingVertically
1708 These properties describe whether the view is currently moving horizontally,
1709 vertically or in either direction, due to the user either dragging or
1712 bool QDeclarativeFlickable::isMovingHorizontally() const
1714 Q_D(const QDeclarativeFlickable);
1715 return d->hData.moving;
1718 bool QDeclarativeFlickable::isMovingVertically() const
1720 Q_D(const QDeclarativeFlickable);
1721 return d->vData.moving;
1724 void QDeclarativeFlickable::movementStarting()
1726 Q_D(QDeclarativeFlickable);
1727 if (d->hMoved && !d->hData.moving) {
1728 d->hData.moving = true;
1729 emit movingChanged();
1730 emit movingHorizontallyChanged();
1731 if (!d->vData.moving)
1732 emit movementStarted();
1734 else if (d->vMoved && !d->vData.moving) {
1735 d->vData.moving = true;
1736 emit movingChanged();
1737 emit movingVerticallyChanged();
1738 if (!d->hData.moving)
1739 emit movementStarted();
1743 void QDeclarativeFlickable::movementEnding()
1745 Q_D(QDeclarativeFlickable);
1748 d->hData.smoothVelocity.setValue(0);
1749 d->vData.smoothVelocity.setValue(0);
1752 void QDeclarativeFlickable::movementXEnding()
1754 Q_D(QDeclarativeFlickable);
1755 if (d->hData.flicking) {
1756 d->hData.flicking = false;
1757 emit flickingChanged();
1758 emit flickingHorizontallyChanged();
1759 if (!d->vData.flicking)
1762 if (!d->pressed && !d->stealMouse) {
1763 if (d->hData.moving) {
1764 d->hData.moving = false;
1766 emit movingChanged();
1767 emit movingHorizontallyChanged();
1768 if (!d->vData.moving)
1769 emit movementEnded();
1772 d->hData.fixingUp = false;
1775 void QDeclarativeFlickable::movementYEnding()
1777 Q_D(QDeclarativeFlickable);
1778 if (d->vData.flicking) {
1779 d->vData.flicking = false;
1780 emit flickingChanged();
1781 emit flickingVerticallyChanged();
1782 if (!d->hData.flicking)
1785 if (!d->pressed && !d->stealMouse) {
1786 if (d->vData.moving) {
1787 d->vData.moving = false;
1789 emit movingChanged();
1790 emit movingVerticallyChanged();
1791 if (!d->hData.moving)
1792 emit movementEnded();
1795 d->vData.fixingUp = false;
1798 void QDeclarativeFlickablePrivate::updateVelocity()
1800 Q_Q(QDeclarativeFlickable);
1801 emit q->horizontalVelocityChanged();
1802 emit q->verticalVelocityChanged();