1 // Commit: ee767e8c16742316068e83323374ea54f2b939cb
2 /****************************************************************************
4 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
5 ** All rights reserved.
6 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 ** This file is part of the QtDeclarative module of the Qt Toolkit.
10 ** $QT_BEGIN_LICENSE:LGPL$
11 ** No Commercial Usage
12 ** This file contains pre-release code and may not be distributed.
13 ** You may use this file in accordance with the terms and conditions
14 ** contained in the Technology Preview License Agreement accompanying
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
41 ****************************************************************************/
43 #include "qsgflickable_p.h"
44 #include "qsgflickable_p_p.h"
45 #include "qsgcanvas.h"
46 #include "qsgcanvas_p.h"
48 #include <QtDeclarative/qdeclarativeinfo.h>
49 #include <QtGui/qgraphicssceneevent.h>
50 #include <QtGui/qapplication.h>
54 // FlickThreshold determines how far the "mouse" must have moved
55 // before we perform a flick.
56 static const int FlickThreshold = 20;
58 // RetainGrabVelocity is the maxmimum instantaneous velocity that
59 // will ensure the Flickable retains the grab on consecutive flicks.
60 static const int RetainGrabVelocity = 15;
62 QSGFlickableVisibleArea::QSGFlickableVisibleArea(QSGFlickable *parent)
63 : QObject(parent), flickable(parent), m_xPosition(0.), m_widthRatio(0.)
64 , m_yPosition(0.), m_heightRatio(0.)
68 qreal QSGFlickableVisibleArea::widthRatio() const
73 qreal QSGFlickableVisibleArea::xPosition() const
78 qreal QSGFlickableVisibleArea::heightRatio() const
83 qreal QSGFlickableVisibleArea::yPosition() const
88 void QSGFlickableVisibleArea::updateVisible()
90 QSGFlickablePrivate *p = QSGFlickablePrivate::get(flickable);
94 bool changeWidth = false;
95 bool changeHeight = false;
98 const qreal viewheight = flickable->height();
99 const qreal maxyextent = -flickable->maxYExtent() + flickable->minYExtent();
100 qreal pagePos = (-p->vData.move.value() + flickable->minYExtent()) / (maxyextent + viewheight);
101 qreal pageSize = viewheight / (maxyextent + viewheight);
103 if (pageSize != m_heightRatio) {
104 m_heightRatio = pageSize;
107 if (pagePos != m_yPosition) {
108 m_yPosition = pagePos;
113 const qreal viewwidth = flickable->width();
114 const qreal maxxextent = -flickable->maxXExtent() + flickable->minXExtent();
115 pagePos = (-p->hData.move.value() + flickable->minXExtent()) / (maxxextent + viewwidth);
116 pageSize = viewwidth / (maxxextent + viewwidth);
118 if (pageSize != m_widthRatio) {
119 m_widthRatio = pageSize;
122 if (pagePos != m_xPosition) {
123 m_xPosition = pagePos;
128 emit xPositionChanged(m_xPosition);
130 emit yPositionChanged(m_yPosition);
132 emit widthRatioChanged(m_widthRatio);
134 emit heightRatioChanged(m_heightRatio);
138 QSGFlickablePrivate::QSGFlickablePrivate()
139 : contentItem(new QSGItem)
140 , hData(this, &QSGFlickablePrivate::setRoundedViewportX)
141 , vData(this, &QSGFlickablePrivate::setRoundedViewportY)
142 , flickingHorizontally(false), flickingVertically(false)
143 , hMoved(false), vMoved(false)
144 , movingHorizontally(false), movingVertically(false)
145 , stealMouse(false), pressed(false), interactive(true), calcVelocity(false)
146 , deceleration(500), maxVelocity(2000), reportedVelocitySmoothing(100)
147 , delayedPressEvent(0), delayedPressTarget(0), pressDelay(0), fixupDuration(600)
148 , fixupMode(Normal), vTime(0), visibleArea(0)
149 , flickableDirection(QSGFlickable::AutoFlickDirection)
150 , boundsBehavior(QSGFlickable::DragAndOvershootBounds)
154 void QSGFlickablePrivate::init()
157 QDeclarative_setParent_noEvent(contentItem, q);
158 contentItem->setParentItem(q);
159 static int timelineUpdatedIdx = -1;
160 static int timelineCompletedIdx = -1;
161 static int flickableTickedIdx = -1;
162 static int flickableMovementEndingIdx = -1;
163 if (timelineUpdatedIdx == -1) {
164 timelineUpdatedIdx = QDeclarativeTimeLine::staticMetaObject.indexOfSignal("updated()");
165 timelineCompletedIdx = QDeclarativeTimeLine::staticMetaObject.indexOfSignal("completed()");
166 flickableTickedIdx = QSGFlickable::staticMetaObject.indexOfSlot("ticked()");
167 flickableMovementEndingIdx = QSGFlickable::staticMetaObject.indexOfSlot("movementEnding()");
169 QMetaObject::connect(&timeline, timelineUpdatedIdx,
170 q, flickableTickedIdx, Qt::DirectConnection);
171 QMetaObject::connect(&timeline, timelineCompletedIdx,
172 q, flickableMovementEndingIdx, Qt::DirectConnection);
173 q->setAcceptedMouseButtons(Qt::LeftButton);
174 q->setFiltersChildMouseEvents(true);
175 QSGItemPrivate *viewportPrivate = QSGItemPrivate::get(contentItem);
176 viewportPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
177 lastPosTime.invalidate();
181 Returns the amount to overshoot by given a velocity.
182 Will be roughly in range 0 - size/4
184 qreal QSGFlickablePrivate::overShootDistance(qreal velocity, qreal size)
186 if (maxVelocity <= 0)
189 velocity = qAbs(velocity);
190 if (velocity > maxVelocity)
191 velocity = maxVelocity;
192 qreal dist = size / 4 * velocity / maxVelocity;
196 void QSGFlickablePrivate::itemGeometryChanged(QSGItem *item, const QRectF &newGeom, const QRectF &oldGeom)
199 if (item == contentItem) {
200 if (newGeom.x() != oldGeom.x())
201 emit q->contentXChanged();
202 if (newGeom.y() != oldGeom.y())
203 emit q->contentYChanged();
207 void QSGFlickablePrivate::flickX(qreal velocity)
210 flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, velocity);
213 void QSGFlickablePrivate::flickY(qreal velocity)
216 flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, velocity);
219 void QSGFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
220 QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
223 qreal maxDistance = -1;
224 data.fixingUp = false;
225 bool overShoot = boundsBehavior == QSGFlickable::DragAndOvershootBounds;
226 // -ve velocity means list is moving up
228 if (data.move.value() < minExtent)
229 maxDistance = qAbs(minExtent - data.move.value() + (overShoot?overShootDistance(velocity,vSize):0));
230 data.flickTarget = minExtent;
232 if (data.move.value() > maxExtent)
233 maxDistance = qAbs(maxExtent - data.move.value()) + (overShoot?overShootDistance(velocity,vSize):0);
234 data.flickTarget = maxExtent;
236 if (maxDistance > 0) {
238 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
244 timeline.reset(data.move);
245 timeline.accel(data.move, v, deceleration, maxDistance);
246 timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
247 if (!flickingHorizontally && q->xflick()) {
248 flickingHorizontally = true;
249 emit q->flickingChanged();
250 emit q->flickingHorizontallyChanged();
251 if (!flickingVertically)
252 emit q->flickStarted();
254 if (!flickingVertically && q->yflick()) {
255 flickingVertically = true;
256 emit q->flickingChanged();
257 emit q->flickingVerticallyChanged();
258 if (!flickingHorizontally)
259 emit q->flickStarted();
262 timeline.reset(data.move);
263 fixup(data, minExtent, maxExtent);
267 void QSGFlickablePrivate::fixupY_callback(void *data)
269 ((QSGFlickablePrivate *)data)->fixupY();
272 void QSGFlickablePrivate::fixupX_callback(void *data)
274 ((QSGFlickablePrivate *)data)->fixupX();
277 void QSGFlickablePrivate::fixupX()
280 fixup(hData, q->minXExtent(), q->maxXExtent());
283 void QSGFlickablePrivate::fixupY()
286 fixup(vData, q->minYExtent(), q->maxYExtent());
289 void QSGFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
291 if (data.move.value() > minExtent || maxExtent > minExtent) {
292 timeline.reset(data.move);
293 if (data.move.value() != minExtent) {
296 timeline.set(data.move, minExtent);
299 // The target has changed. Don't start from the beginning; just complete the
300 // second half of the animation using the new extent.
301 timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
302 data.fixingUp = true;
305 qreal dist = minExtent - data.move;
306 timeline.move(data.move, minExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4);
307 timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
308 data.fixingUp = true;
312 } else if (data.move.value() < maxExtent) {
313 timeline.reset(data.move);
316 timeline.set(data.move, maxExtent);
319 // The target has changed. Don't start from the beginning; just complete the
320 // second half of the animation using the new extent.
321 timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
322 data.fixingUp = true;
325 qreal dist = maxExtent - data.move;
326 timeline.move(data.move, maxExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4);
327 timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
328 data.fixingUp = true;
333 vTime = timeline.time();
336 void QSGFlickablePrivate::updateBeginningEnd()
339 bool atBoundaryChange = false;
342 const int maxyextent = int(-q->maxYExtent());
343 const qreal ypos = -vData.move.value();
344 bool atBeginning = (ypos <= -q->minYExtent());
345 bool atEnd = (maxyextent <= ypos);
347 if (atBeginning != vData.atBeginning) {
348 vData.atBeginning = atBeginning;
349 atBoundaryChange = true;
351 if (atEnd != vData.atEnd) {
353 atBoundaryChange = true;
357 const int maxxextent = int(-q->maxXExtent());
358 const qreal xpos = -hData.move.value();
359 atBeginning = (xpos <= -q->minXExtent());
360 atEnd = (maxxextent <= xpos);
362 if (atBeginning != hData.atBeginning) {
363 hData.atBeginning = atBeginning;
364 atBoundaryChange = true;
366 if (atEnd != hData.atEnd) {
368 atBoundaryChange = true;
371 if (atBoundaryChange)
372 emit q->isAtBoundaryChanged();
375 visibleArea->updateVisible();
378 QSGFlickable::QSGFlickable(QSGItem *parent)
379 : QSGItem(*(new QSGFlickablePrivate), parent)
385 QSGFlickable::QSGFlickable(QSGFlickablePrivate &dd, QSGItem *parent)
386 : QSGItem(dd, parent)
392 QSGFlickable::~QSGFlickable()
396 qreal QSGFlickable::contentX() const
398 Q_D(const QSGFlickable);
399 return -d->contentItem->x();
402 void QSGFlickable::setContentX(qreal pos)
405 d->timeline.reset(d->hData.move);
406 d->vTime = d->timeline.time();
408 if (-pos != d->hData.move.value()) {
409 d->hData.move.setValue(-pos);
414 qreal QSGFlickable::contentY() const
416 Q_D(const QSGFlickable);
417 return -d->contentItem->y();
420 void QSGFlickable::setContentY(qreal pos)
423 d->timeline.reset(d->vData.move);
424 d->vTime = d->timeline.time();
426 if (-pos != d->vData.move.value()) {
427 d->vData.move.setValue(-pos);
432 bool QSGFlickable::isInteractive() const
434 Q_D(const QSGFlickable);
435 return d->interactive;
438 void QSGFlickable::setInteractive(bool interactive)
441 if (interactive != d->interactive) {
442 d->interactive = interactive;
443 if (!interactive && (d->flickingHorizontally || d->flickingVertically)) {
445 d->vTime = d->timeline.time();
446 d->flickingHorizontally = false;
447 d->flickingVertically = false;
448 emit flickingChanged();
449 emit flickingHorizontallyChanged();
450 emit flickingVerticallyChanged();
453 emit interactiveChanged();
457 qreal QSGFlickable::horizontalVelocity() const
459 Q_D(const QSGFlickable);
460 return d->hData.smoothVelocity.value();
463 qreal QSGFlickable::verticalVelocity() const
465 Q_D(const QSGFlickable);
466 return d->vData.smoothVelocity.value();
469 bool QSGFlickable::isAtXEnd() const
471 Q_D(const QSGFlickable);
472 return d->hData.atEnd;
475 bool QSGFlickable::isAtXBeginning() const
477 Q_D(const QSGFlickable);
478 return d->hData.atBeginning;
481 bool QSGFlickable::isAtYEnd() const
483 Q_D(const QSGFlickable);
484 return d->vData.atEnd;
487 bool QSGFlickable::isAtYBeginning() const
489 Q_D(const QSGFlickable);
490 return d->vData.atBeginning;
493 void QSGFlickable::ticked()
498 QSGItem *QSGFlickable::contentItem()
501 return d->contentItem;
504 QSGFlickableVisibleArea *QSGFlickable::visibleArea()
508 d->visibleArea = new QSGFlickableVisibleArea(this);
509 return d->visibleArea;
512 QSGFlickable::FlickableDirection QSGFlickable::flickableDirection() const
514 Q_D(const QSGFlickable);
515 return d->flickableDirection;
518 void QSGFlickable::setFlickableDirection(FlickableDirection direction)
521 if (direction != d->flickableDirection) {
522 d->flickableDirection = direction;
523 emit flickableDirectionChanged();
527 void QSGFlickablePrivate::handleMousePressEvent(QGraphicsSceneMouseEvent *event)
530 if (interactive && timeline.isActive()
531 && (qAbs(hData.smoothVelocity.value()) > RetainGrabVelocity
532 || qAbs(vData.smoothVelocity.value()) > RetainGrabVelocity)) {
533 stealMouse = true; // If we've been flicked then steal the click.
537 q->setKeepMouseGrab(stealMouse);
542 hData.dragStartOffset = 0;
543 vData.dragStartOffset = 0;
544 hData.dragMinBound = q->minXExtent();
545 vData.dragMinBound = q->minYExtent();
546 hData.dragMaxBound = q->maxXExtent();
547 vData.dragMaxBound = q->maxYExtent();
548 hData.fixingUp = false;
549 vData.fixingUp = false;
551 QSGItemPrivate::start(lastPosTime);
552 pressPos = event->pos();
553 hData.pressPos = hData.move.value();
554 vData.pressPos = vData.move.value();
555 flickingHorizontally = false;
556 flickingVertically = false;
557 QSGItemPrivate::start(pressTime);
558 QSGItemPrivate::start(velocityTime);
561 void QSGFlickablePrivate::handleMouseMoveEvent(QGraphicsSceneMouseEvent *event)
564 if (!interactive || !lastPosTime.isValid())
566 bool rejectY = false;
567 bool rejectX = false;
569 bool stealY = stealMouse;
570 bool stealX = stealMouse;
573 int dy = int(event->pos().y() - pressPos.y());
574 if (qAbs(dy) > QApplication::startDragDistance() || QSGItemPrivate::elapsed(pressTime) > 200) {
576 vData.dragStartOffset = dy;
577 qreal newY = dy + vData.pressPos - vData.dragStartOffset;
578 const qreal minY = vData.dragMinBound;
579 const qreal maxY = vData.dragMaxBound;
581 newY = minY + (newY - minY) / 2;
582 if (newY < maxY && maxY - minY <= 0)
583 newY = maxY + (newY - maxY) / 2;
584 if (boundsBehavior == QSGFlickable::StopAtBounds && (newY > minY || newY < maxY)) {
595 if (!rejectY && stealMouse) {
596 vData.move.setValue(qRound(newY));
599 if (qAbs(dy) > QApplication::startDragDistance())
605 int dx = int(event->pos().x() - pressPos.x());
606 if (qAbs(dx) > QApplication::startDragDistance() || QSGItemPrivate::elapsed(pressTime) > 200) {
608 hData.dragStartOffset = dx;
609 qreal newX = dx + hData.pressPos - hData.dragStartOffset;
610 const qreal minX = hData.dragMinBound;
611 const qreal maxX = hData.dragMaxBound;
613 newX = minX + (newX - minX) / 2;
614 if (newX < maxX && maxX - minX <= 0)
615 newX = maxX + (newX - maxX) / 2;
616 if (boundsBehavior == QSGFlickable::StopAtBounds && (newX > minX || newX < maxX)) {
627 if (!rejectX && stealMouse) {
628 hData.move.setValue(qRound(newX));
632 if (qAbs(dx) > QApplication::startDragDistance())
637 stealMouse = stealX || stealY;
639 q->setKeepMouseGrab(true);
641 if (!lastPos.isNull()) {
642 qreal elapsed = qreal(QSGItemPrivate::restart(lastPosTime)) / 1000.;
646 qreal diff = event->pos().y() - lastPos.y();
647 // average to reduce the effect of spurious moves
648 vData.velocity += diff / elapsed;
653 qreal diff = event->pos().x() - lastPos.x();
654 // average to reduce the effect of spurious moves
655 hData.velocity += diff / elapsed;
660 if (rejectY) vData.velocity = 0;
661 if (rejectX) hData.velocity = 0;
663 if (hMoved || vMoved) {
664 q->movementStarting();
668 lastPos = event->pos();
671 void QSGFlickablePrivate::handleMouseReleaseEvent(QGraphicsSceneMouseEvent *event)
675 q->setKeepMouseGrab(false);
677 if (!lastPosTime.isValid())
680 if (QSGItemPrivate::elapsed(lastPosTime) > 100) {
681 // if we drag then pause before release we should not cause a flick.
682 hData.velocity = 0.0;
683 vData.velocity = 0.0;
686 vTime = timeline.time();
687 if (qAbs(vData.velocity) > MinimumFlickVelocity && qAbs(event->pos().y() - pressPos.y()) > FlickThreshold)
688 flickY(vData.velocity);
692 if (qAbs(hData.velocity) > MinimumFlickVelocity && qAbs(event->pos().x() - pressPos.x()) > FlickThreshold)
693 flickX(hData.velocity);
697 lastPosTime.invalidate();
699 if (!timeline.isActive())
703 void QSGFlickable::mousePressEvent(QGraphicsSceneMouseEvent *event)
706 if (d->interactive) {
708 d->handleMousePressEvent(event);
711 QSGItem::mousePressEvent(event);
715 void QSGFlickable::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
718 if (d->interactive) {
719 d->handleMouseMoveEvent(event);
722 QSGItem::mouseMoveEvent(event);
726 void QSGFlickable::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
729 if (d->interactive) {
730 d->clearDelayedPress();
731 d->handleMouseReleaseEvent(event);
735 QSGItem::mouseReleaseEvent(event);
739 void QSGFlickable::wheelEvent(QGraphicsSceneWheelEvent *event)
742 if (!d->interactive) {
743 QSGItem::wheelEvent(event);
744 } else if (yflick() && event->orientation() == Qt::Vertical) {
745 if (event->delta() > 0)
746 d->vData.velocity = qMax(event->delta() - d->vData.smoothVelocity.value(), qreal(250.0));
748 d->vData.velocity = qMin(event->delta() - d->vData.smoothVelocity.value(), qreal(-250.0));
749 d->flickingVertically = false;
750 d->flickY(d->vData.velocity);
751 if (d->flickingVertically) {
756 } else if (xflick() && event->orientation() == Qt::Horizontal) {
757 if (event->delta() > 0)
758 d->hData.velocity = qMax(event->delta() - d->hData.smoothVelocity.value(), qreal(250.0));
760 d->hData.velocity = qMin(event->delta() - d->hData.smoothVelocity.value(), qreal(-250.0));
761 d->flickingHorizontally = false;
762 d->flickX(d->hData.velocity);
763 if (d->flickingHorizontally) {
769 QSGItem::wheelEvent(event);
773 bool QSGFlickablePrivate::isOutermostPressDelay() const
775 Q_Q(const QSGFlickable);
776 QSGItem *item = q->parentItem();
778 QSGFlickable *flick = qobject_cast<QSGFlickable*>(item);
779 if (flick && flick->pressDelay() > 0 && flick->isInteractive())
781 item = item->parentItem();
787 void QSGFlickablePrivate::captureDelayedPress(QGraphicsSceneMouseEvent *event)
790 if (!q->canvas() || pressDelay <= 0)
792 if (!isOutermostPressDelay())
794 delayedPressTarget = q->canvas()->mouseGrabberItem();
795 delayedPressEvent = new QGraphicsSceneMouseEvent(event->type());
796 delayedPressEvent->setAccepted(false);
797 for (int i = 0x1; i <= 0x10; i <<= 1) {
798 if (event->buttons() & i) {
799 Qt::MouseButton button = Qt::MouseButton(i);
800 delayedPressEvent->setButtonDownPos(button, event->buttonDownPos(button));
801 delayedPressEvent->setButtonDownScenePos(button, event->buttonDownScenePos(button));
802 delayedPressEvent->setButtonDownScreenPos(button, event->buttonDownScreenPos(button));
805 delayedPressEvent->setButtons(event->buttons());
806 delayedPressEvent->setButton(event->button());
807 delayedPressEvent->setPos(event->pos());
808 delayedPressEvent->setScenePos(event->scenePos());
809 delayedPressEvent->setScreenPos(event->screenPos());
810 delayedPressEvent->setLastPos(event->lastPos());
811 delayedPressEvent->setLastScenePos(event->lastScenePos());
812 delayedPressEvent->setLastScreenPos(event->lastScreenPos());
813 delayedPressEvent->setModifiers(event->modifiers());
814 delayedPressTimer.start(pressDelay, q);
817 void QSGFlickablePrivate::clearDelayedPress()
819 if (delayedPressEvent) {
820 delayedPressTimer.stop();
821 delete delayedPressEvent;
822 delayedPressEvent = 0;
826 void QSGFlickablePrivate::setRoundedViewportX(qreal x)
828 contentItem->setX(qRound(x));
831 void QSGFlickablePrivate::setRoundedViewportY(qreal y)
833 contentItem->setY(qRound(y));
836 void QSGFlickable::timerEvent(QTimerEvent *event)
839 if (event->timerId() == d->delayedPressTimer.timerId()) {
840 d->delayedPressTimer.stop();
841 if (d->delayedPressEvent) {
842 QSGItem *grabber = canvas() ? canvas()->mouseGrabberItem() : 0;
843 if (!grabber || grabber != this) {
844 // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
845 // so we reset the grabber
846 if (canvas()->mouseGrabberItem() == d->delayedPressTarget)
847 d->delayedPressTarget->ungrabMouse();
848 // Use the event handler that will take care of finding the proper item to propagate the event
849 QSGCanvasPrivate::get(canvas())->deliverMouseEvent(d->delayedPressEvent);
851 delete d->delayedPressEvent;
852 d->delayedPressEvent = 0;
857 qreal QSGFlickable::minYExtent() const
862 qreal QSGFlickable::minXExtent() const
868 qreal QSGFlickable::maxXExtent() const
870 return width() - vWidth();
873 qreal QSGFlickable::maxYExtent() const
875 return height() - vHeight();
878 void QSGFlickable::viewportMoved()
882 qreal prevX = d->lastFlickablePosition.x();
883 qreal prevY = d->lastFlickablePosition.y();
884 d->velocityTimeline.clear();
885 if (d->pressed || d->calcVelocity) {
886 int elapsed = QSGItemPrivate::restart(d->velocityTime);
888 qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / elapsed;
889 qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / elapsed;
890 d->velocityTimeline.move(d->hData.smoothVelocity, horizontalVelocity, d->reportedVelocitySmoothing);
891 d->velocityTimeline.move(d->hData.smoothVelocity, 0, d->reportedVelocitySmoothing);
892 d->velocityTimeline.move(d->vData.smoothVelocity, verticalVelocity, d->reportedVelocitySmoothing);
893 d->velocityTimeline.move(d->vData.smoothVelocity, 0, d->reportedVelocitySmoothing);
896 if (d->timeline.time() > d->vTime) {
897 qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / (d->timeline.time() - d->vTime);
898 qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / (d->timeline.time() - d->vTime);
899 d->hData.smoothVelocity.setValue(horizontalVelocity);
900 d->vData.smoothVelocity.setValue(verticalVelocity);
904 d->lastFlickablePosition = QPointF(d->hData.move.value(), d->vData.move.value());
906 d->vTime = d->timeline.time();
907 d->updateBeginningEnd();
910 void QSGFlickable::geometryChanged(const QRectF &newGeometry,
911 const QRectF &oldGeometry)
914 QSGItem::geometryChanged(newGeometry, oldGeometry);
916 bool changed = false;
917 if (newGeometry.width() != oldGeometry.width()) {
920 if (d->hData.viewSize < 0) {
921 d->contentItem->setWidth(width());
922 emit contentWidthChanged();
924 // Make sure that we're entirely in view.
925 if (!d->pressed && !d->movingHorizontally && !d->movingVertically) {
926 d->fixupMode = QSGFlickablePrivate::Immediate;
930 if (newGeometry.height() != oldGeometry.height()) {
933 if (d->vData.viewSize < 0) {
934 d->contentItem->setHeight(height());
935 emit contentHeightChanged();
937 // Make sure that we're entirely in view.
938 if (!d->pressed && !d->movingHorizontally && !d->movingVertically) {
939 d->fixupMode = QSGFlickablePrivate::Immediate;
945 d->updateBeginningEnd();
948 void QSGFlickable::cancelFlick()
951 d->timeline.reset(d->hData.move);
952 d->timeline.reset(d->vData.move);
956 void QSGFlickablePrivate::data_append(QDeclarativeListProperty<QObject> *prop, QObject *o)
958 QSGItem *i = qobject_cast<QSGItem *>(o);
960 i->setParentItem(static_cast<QSGFlickablePrivate*>(prop->data)->contentItem);
962 o->setParent(prop->object); // XXX todo - do we want this?
966 int QSGFlickablePrivate::data_count(QDeclarativeListProperty<QObject> *)
972 QObject *QSGFlickablePrivate::data_at(QDeclarativeListProperty<QObject> *, int)
978 void QSGFlickablePrivate::data_clear(QDeclarativeListProperty<QObject> *)
983 QDeclarativeListProperty<QObject> QSGFlickable::flickableData()
986 return QDeclarativeListProperty<QObject>(this, (void *)d, QSGFlickablePrivate::data_append,
987 QSGFlickablePrivate::data_count,
988 QSGFlickablePrivate::data_at,
989 QSGFlickablePrivate::data_clear);
992 QDeclarativeListProperty<QSGItem> QSGFlickable::flickableChildren()
995 return QSGItemPrivate::get(d->contentItem)->children();
998 QSGFlickable::BoundsBehavior QSGFlickable::boundsBehavior() const
1000 Q_D(const QSGFlickable);
1001 return d->boundsBehavior;
1004 void QSGFlickable::setBoundsBehavior(BoundsBehavior b)
1007 if (b == d->boundsBehavior)
1009 d->boundsBehavior = b;
1010 emit boundsBehaviorChanged();
1013 qreal QSGFlickable::contentWidth() const
1015 Q_D(const QSGFlickable);
1016 return d->hData.viewSize;
1019 void QSGFlickable::setContentWidth(qreal w)
1022 if (d->hData.viewSize == w)
1024 d->hData.viewSize = w;
1026 d->contentItem->setWidth(width());
1028 d->contentItem->setWidth(w);
1029 // Make sure that we're entirely in view.
1030 if (!d->pressed && !d->movingHorizontally && !d->movingVertically) {
1031 d->fixupMode = QSGFlickablePrivate::Immediate;
1033 } else if (!d->pressed && d->hData.fixingUp) {
1034 d->fixupMode = QSGFlickablePrivate::ExtentChanged;
1037 emit contentWidthChanged();
1038 d->updateBeginningEnd();
1041 qreal QSGFlickable::contentHeight() const
1043 Q_D(const QSGFlickable);
1044 return d->vData.viewSize;
1047 void QSGFlickable::setContentHeight(qreal h)
1050 if (d->vData.viewSize == h)
1052 d->vData.viewSize = h;
1054 d->contentItem->setHeight(height());
1056 d->contentItem->setHeight(h);
1057 // Make sure that we're entirely in view.
1058 if (!d->pressed && !d->movingHorizontally && !d->movingVertically) {
1059 d->fixupMode = QSGFlickablePrivate::Immediate;
1061 } else if (!d->pressed && d->vData.fixingUp) {
1062 d->fixupMode = QSGFlickablePrivate::ExtentChanged;
1065 emit contentHeightChanged();
1066 d->updateBeginningEnd();
1069 void QSGFlickable::resizeContent(qreal w, qreal h, QPointF center)
1072 if (w != d->hData.viewSize) {
1073 qreal oldSize = d->hData.viewSize;
1075 if (center.x() != 0) {
1076 qreal pos = center.x() * w / oldSize;
1077 setContentX(contentX() + pos - center.x());
1080 if (h != d->vData.viewSize) {
1081 qreal oldSize = d->vData.viewSize;
1082 setContentHeight(h);
1083 if (center.y() != 0) {
1084 qreal pos = center.y() * h / oldSize;
1085 setContentY(contentY() + pos - center.y());
1090 void QSGFlickable::returnToBounds()
1097 qreal QSGFlickable::vWidth() const
1099 Q_D(const QSGFlickable);
1100 if (d->hData.viewSize < 0)
1103 return d->hData.viewSize;
1106 qreal QSGFlickable::vHeight() const
1108 Q_D(const QSGFlickable);
1109 if (d->vData.viewSize < 0)
1112 return d->vData.viewSize;
1115 bool QSGFlickable::xflick() const
1117 Q_D(const QSGFlickable);
1118 if (d->flickableDirection == QSGFlickable::AutoFlickDirection)
1119 return vWidth() != width();
1120 return d->flickableDirection & QSGFlickable::HorizontalFlick;
1123 bool QSGFlickable::yflick() const
1125 Q_D(const QSGFlickable);
1126 if (d->flickableDirection == QSGFlickable::AutoFlickDirection)
1127 return vHeight() != height();
1128 return d->flickableDirection & QSGFlickable::VerticalFlick;
1131 void QSGFlickable::mouseUngrabEvent()
1135 // if our mouse grab has been removed (probably by another Flickable),
1138 d->stealMouse = false;
1139 setKeepMouseGrab(false);
1143 bool QSGFlickable::sendMouseEvent(QGraphicsSceneMouseEvent *event)
1146 QGraphicsSceneMouseEvent mouseEvent(event->type());
1147 QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
1149 QSGCanvas *c = canvas();
1150 QSGItem *grabber = c ? c->mouseGrabberItem() : 0;
1151 bool stealThisEvent = d->stealMouse;
1152 if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) {
1153 mouseEvent.setAccepted(false);
1154 for (int i = 0x1; i <= 0x10; i <<= 1) {
1155 if (event->buttons() & i) {
1156 Qt::MouseButton button = Qt::MouseButton(i);
1157 mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button)));
1160 mouseEvent.setScenePos(event->scenePos());
1161 mouseEvent.setLastScenePos(event->lastScenePos());
1162 mouseEvent.setPos(mapFromScene(event->scenePos()));
1163 mouseEvent.setLastPos(mapFromScene(event->lastScenePos()));
1165 switch(mouseEvent.type()) {
1166 case QEvent::GraphicsSceneMouseMove:
1167 d->handleMouseMoveEvent(&mouseEvent);
1169 case QEvent::GraphicsSceneMousePress:
1170 if (d->pressed) // we are already pressed - this is a delayed replay
1173 d->handleMousePressEvent(&mouseEvent);
1174 d->captureDelayedPress(event);
1175 stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
1177 case QEvent::GraphicsSceneMouseRelease:
1178 if (d->delayedPressEvent) {
1179 // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
1180 // so we reset the grabber
1181 if (c->mouseGrabberItem() == d->delayedPressTarget)
1182 d->delayedPressTarget->ungrabMouse();
1183 //Use the event handler that will take care of finding the proper item to propagate the event
1184 QSGCanvasPrivate::get(canvas())->deliverMouseEvent(d->delayedPressEvent);
1185 d->clearDelayedPress();
1186 // We send the release
1187 canvas()->sendEvent(c->mouseGrabberItem(), event);
1188 // And the event has been consumed
1189 d->stealMouse = false;
1193 d->handleMouseReleaseEvent(&mouseEvent);
1198 grabber = qobject_cast<QSGItem*>(c->mouseGrabberItem());
1199 if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) {
1200 d->clearDelayedPress();
1204 return stealThisEvent || d->delayedPressEvent;
1205 } else if (d->lastPosTime.isValid()) {
1206 d->lastPosTime.invalidate();
1208 if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease) {
1209 d->clearDelayedPress();
1210 d->stealMouse = false;
1217 bool QSGFlickable::childMouseEventFilter(QSGItem *i, QEvent *e)
1220 if (!isVisible() || !d->interactive)
1221 return QSGItem::childMouseEventFilter(i, e);
1222 switch (e->type()) {
1223 case QEvent::GraphicsSceneMousePress:
1224 case QEvent::GraphicsSceneMouseMove:
1225 case QEvent::GraphicsSceneMouseRelease:
1226 return sendMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e));
1231 return QSGItem::childMouseEventFilter(i, e);
1234 qreal QSGFlickable::maximumFlickVelocity() const
1236 Q_D(const QSGFlickable);
1237 return d->maxVelocity;
1240 void QSGFlickable::setMaximumFlickVelocity(qreal v)
1243 if (v == d->maxVelocity)
1246 emit maximumFlickVelocityChanged();
1249 qreal QSGFlickable::flickDeceleration() const
1251 Q_D(const QSGFlickable);
1252 return d->deceleration;
1255 void QSGFlickable::setFlickDeceleration(qreal deceleration)
1258 if (deceleration == d->deceleration)
1260 d->deceleration = deceleration;
1261 emit flickDecelerationChanged();
1264 bool QSGFlickable::isFlicking() const
1266 Q_D(const QSGFlickable);
1267 return d->flickingHorizontally || d->flickingVertically;
1270 bool QSGFlickable::isFlickingHorizontally() const
1272 Q_D(const QSGFlickable);
1273 return d->flickingHorizontally;
1276 bool QSGFlickable::isFlickingVertically() const
1278 Q_D(const QSGFlickable);
1279 return d->flickingVertically;
1282 int QSGFlickable::pressDelay() const
1284 Q_D(const QSGFlickable);
1285 return d->pressDelay;
1288 void QSGFlickable::setPressDelay(int delay)
1291 if (d->pressDelay == delay)
1293 d->pressDelay = delay;
1294 emit pressDelayChanged();
1298 bool QSGFlickable::isMoving() const
1300 Q_D(const QSGFlickable);
1301 return d->movingHorizontally || d->movingVertically;
1304 bool QSGFlickable::isMovingHorizontally() const
1306 Q_D(const QSGFlickable);
1307 return d->movingHorizontally;
1310 bool QSGFlickable::isMovingVertically() const
1312 Q_D(const QSGFlickable);
1313 return d->movingVertically;
1316 void QSGFlickable::movementStarting()
1319 if (d->hMoved && !d->movingHorizontally) {
1320 d->movingHorizontally = true;
1321 emit movingChanged();
1322 emit movingHorizontallyChanged();
1323 if (!d->movingVertically)
1324 emit movementStarted();
1326 else if (d->vMoved && !d->movingVertically) {
1327 d->movingVertically = true;
1328 emit movingChanged();
1329 emit movingVerticallyChanged();
1330 if (!d->movingHorizontally)
1331 emit movementStarted();
1335 void QSGFlickable::movementEnding()
1340 d->hData.smoothVelocity.setValue(0);
1341 d->vData.smoothVelocity.setValue(0);
1344 void QSGFlickable::movementXEnding()
1347 if (d->flickingHorizontally) {
1348 d->flickingHorizontally = false;
1349 emit flickingChanged();
1350 emit flickingHorizontallyChanged();
1351 if (!d->flickingVertically)
1354 if (!d->pressed && !d->stealMouse) {
1355 if (d->movingHorizontally) {
1356 d->movingHorizontally = false;
1358 emit movingChanged();
1359 emit movingHorizontallyChanged();
1360 if (!d->movingVertically)
1361 emit movementEnded();
1364 d->hData.fixingUp = false;
1367 void QSGFlickable::movementYEnding()
1370 if (d->flickingVertically) {
1371 d->flickingVertically = false;
1372 emit flickingChanged();
1373 emit flickingVerticallyChanged();
1374 if (!d->flickingHorizontally)
1377 if (!d->pressed && !d->stealMouse) {
1378 if (d->movingVertically) {
1379 d->movingVertically = false;
1381 emit movingChanged();
1382 emit movingVerticallyChanged();
1383 if (!d->movingHorizontally)
1384 emit movementEnded();
1387 d->vData.fixingUp = false;
1390 void QSGFlickablePrivate::updateVelocity()
1393 emit q->horizontalVelocityChanged();
1394 emit q->verticalVelocityChanged();