21 #include <QtCore/qglobal.h>
22 #include <QtCore/qmetatype.h>
23 #include <QGraphicsScene>
24 #include <QGraphicsSceneMouseEvent>
25 #include <QGraphicsSceneWheelEvent>
27 #include <QGraphicsWidget>
29 #include <QPropertyAnimation>
42 class KineticScrollingPrivate
60 KineticScrollingPrivate()
64 forwardingEvent(
false),
65 multitouchGesture(GestureNone)
71 t = QTime::currentTime();
74 void syncViewportRect()
76 contentsSize = parent->property(
"contentsSize").toSizeF();
77 viewportGeometry = parent->property(
"viewportGeometry").toRectF();
80 bool canScroll(Direction direction,
bool hasOvershoot =
false)
const
82 QPointF scrollPosition = -parent->property(
"scrollPosition").value<QPointF>();
83 int offset = (hasOvershoot?overshoot*2:0);
87 return (scrollPosition.y() < offset);
89 return (scrollPosition.y() + contentsSize.height() + offset >= viewportGeometry.bottom());
91 return (scrollPosition.x() < offset);
93 return (scrollPosition.x() + contentsSize.width() + offset >= viewportGeometry.right());
107 BounceStatus bounceStatus;
109 QPropertyAnimation *scrollAnimation;
115 QRectF viewportGeometry;
117 QPointF maximum, minimum;
118 bool forwardingEvent;
119 Gesture multitouchGesture;
121 unsigned int timeDelta;
127 : d(new KineticScrollingPrivate)
137 void KineticScrolling::duration( )
139 d->timeDelta = d->t.msecsTo(QTime::currentTime());
142 void KineticScrolling::overshoot()
144 QPointF scrollPosition = -d->parent->property(
"scrollPosition").value<QPointF>();
146 if (!d->canScroll(KineticScrollingPrivate::Down) &&
147 !d->canScroll(KineticScrollingPrivate::Up)) {
151 if (d->bounceStatus != KineticScrollingPrivate::Running) {
152 if ((d->cposition.y() > 0 ) || (d->cposition.y() <= d->minimum.y() + d->overshoot)) {
153 QPointF finalPosition;
154 d->scrollAnimation->setEasingCurve( QEasingCurve::OutBounce );
156 if (d->cposition.y() > 0) {
157 finalPosition = QPointF(scrollPosition.x(), 0);
159 finalPosition = QPointF(d->cposition.x(),
160 -d->contentsSize.height( ) + d->parent->size().height());
163 resetAnimation(-finalPosition, 900);
164 d->bounceStatus = KineticScrollingPrivate::Running;
167 d->bounceStatus = KineticScrollingPrivate::Finished;
168 d->scrollAnimation->setEasingCurve(QEasingCurve::OutCirc);
172 void KineticScrolling::setScrollValue(QPointF value)
174 const QPointF pos = thresholdPosition(value);
176 d->parent->setProperty(
"scrollPosition", posf);
178 if ((pos.y() == d->overshoot) || (pos.y() == d->minimum.y())) {
183 QPointF KineticScrolling::thresholdPosition(QPointF value)
const
185 d->minimum.setX(-d->contentsSize.width() + d->viewportGeometry.width());
186 d->minimum.setY(-d->contentsSize.height() + d->viewportGeometry.height()
189 d->minimum.setY(qMin((qreal)d->overshoot, d->minimum.y()));
192 if(d->minimum.x() >= 0) {
193 d->cposition.setX(value.x());
195 d->cposition.setX(qBound(d->minimum.x(), d->maximum.x(), qreal(0)));
198 if((-d->contentsSize.height() + d->viewportGeometry.height() - d->overshoot) >= 0) {
199 d->cposition.setY(value.y());
201 d->cposition.setY(qBound(d->minimum.y(), d->maximum.y(), qreal(d->overshoot)));
207 void KineticScrolling::resetAnimation(QPointF finalPosition,
int duration)
209 if (d->scrollAnimation->state() != QAbstractAnimation::Stopped) {
210 d->scrollAnimation->stop();
213 d->cposition = -finalPosition;
214 QPointF tmpPosition = d->parent->property(
"scrollPosition").value<QPointF>();
215 d->scrollAnimation->setStartValue(tmpPosition);
217 tmpPosition = finalPosition;
219 d->scrollAnimation->setEndValue(tmpPosition);
220 d->scrollAnimation->setDuration(duration);
221 d->scrollAnimation->start();
229 if (d->scrollAnimation->state() != QAbstractAnimation::Stopped) {
230 d->scrollAnimation->stop();
233 d->syncViewportRect();
234 d->cposition = -d->parent->property(
"scrollPosition").value<QPointF>();
237 d->kinMovement = QPointF(0,0);
238 d->scrollAnimation->setEasingCurve(QEasingCurve::OutCirc);
243 QPointF movement =
event->lastPos().toPoint() -
event->pos().toPoint();
244 const QPointF scrollPosition = -d->parent->property(
"scrollPosition").value<QPointF>();
246 if (!movement.isNull()) {
247 if ((d->contentsSize.width() < d->viewportGeometry.width()) &&
248 (d->contentsSize.height() < d->viewportGeometry.height())) {
249 d->kinMovement = QPointF(0, 0);
250 movement = QPointF(0, 0);
251 }
else if (d->contentsSize.height() < d->viewportGeometry.height()) {
252 d->kinMovement += QPointF(movement.x(), 0);
254 }
else if (d->contentsSize.width() < d->viewportGeometry.width()) {
255 d->kinMovement = QPointF(0, movement.y());
258 d->kinMovement += movement;
260 setScrollValue(scrollPosition - movement);
268 if (d->scrollAnimation->state() != QAbstractAnimation::Running) {
271 if (d->kinMovement != QPointF(0, 0)) {
272 const QPointF scrollPosition = -d->parent->property(
"scrollPosition").toPointF();
273 d->kinMovement = QPointF(d->kinMovement.x()*3, d->kinMovement.y()*3);
274 const QPointF finalPos = thresholdPosition(scrollPosition - d->kinMovement);
275 resetAnimation( -finalPos, d->timeDelta*8 );
283 d->syncViewportRect();
284 d->kinMovement = QPointF(0,0);
286 if((event->orientation() == Qt::Vertical) &&
287 (((event->delta() < 0) && d->canScroll(KineticScrollingPrivate::Down)) ||
288 ((event->delta() > 0) && d->canScroll(KineticScrollingPrivate::Up)))) {
289 d->kinMovement.setY(d->kinMovement.y() -
event->delta());
290 }
else if ((event->orientation() == Qt::Vertical) ||
291 (!d->canScroll(KineticScrollingPrivate::Down) &&
292 !d->canScroll(KineticScrollingPrivate::Up))) {
293 if (((event->delta() < 0) &&
294 d->canScroll(KineticScrollingPrivate::Right)) ||
295 (event->delta() > 0 && d->canScroll(KineticScrollingPrivate::Left))) {
296 d->kinMovement.setX(d->kinMovement.x() -
event->delta());
305 const QPointF scrollPosition = -d->parent->property(
"scrollPosition").value<QPointF>();
306 const QPointF pos = scrollPosition - d->kinMovement*2;
307 const QPointF finalPos = thresholdPosition(pos);
309 d->scrollAnimation->setEasingCurve(QEasingCurve::OutCirc);
310 resetAnimation(-finalPos, 900);
315 const int movement = 30;
316 const int duration = 900;
318 QPointF scrollPosition = -d->parent->property(
"scrollPosition").value<QPointF>();
320 switch (event->key()) {
322 scrollPosition.setX(scrollPosition.x() + movement);
323 finalPos = thresholdPosition(scrollPosition);
324 resetAnimation(-finalPos, duration);
327 scrollPosition.setX(scrollPosition.x() - movement);
328 finalPos = thresholdPosition(scrollPosition);
329 resetAnimation(-finalPos, duration);
332 scrollPosition.setY(scrollPosition.y() + movement);
333 finalPos = thresholdPosition(scrollPosition);
334 resetAnimation(-finalPos, duration);
337 scrollPosition.setY(scrollPosition.y() - movement);
338 finalPos = thresholdPosition(scrollPosition);
339 resetAnimation(-finalPos, duration);
349 d->parent->removeEventFilter(
this);
350 disconnect(d->scrollAnimation, SIGNAL(finished()),
this, SLOT(overshoot()));
351 disconnect(d->scrollAnimation,
352 SIGNAL(
stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)),
this,
353 SIGNAL(
stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)));
354 delete d->scrollAnimation;
361 d->scrollAnimation =
new QPropertyAnimation(parent,
"scrollPosition", parent);
362 connect(d->scrollAnimation, SIGNAL(finished()),
this, SLOT(overshoot()));
363 connect(d->scrollAnimation,
364 SIGNAL(
stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)),
this,
365 SIGNAL(
stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)));
366 d->scrollAnimation->setEasingCurve(QEasingCurve::OutCirc);
369 d->parent->installEventFilter(
this);
376 d->scrollAnimation->stop();
385 if (d->forwardingEvent) {
389 bool notBlocked =
true;
390 if (d->multitouchGesture == KineticScrollingPrivate::GestureNone &&
391 d->parent && d->parent->scene()) {
392 d->forwardingEvent =
true;
393 notBlocked = d->parent->scene()->sendEvent(d->parent, event);
394 d->forwardingEvent =
false;
397 if (event->type() != QEvent::TouchBegin &&
398 event->type() != QEvent::TouchUpdate &&
399 event->type() != QEvent::TouchEnd &&
401 ((
event->type() != QEvent::GraphicsSceneMousePress &&
event->isAccepted()) &&
402 (event->type() != QEvent::GraphicsSceneWheel &&
event->isAccepted())))) {
406 QGraphicsSceneMouseEvent *me =
static_cast<QGraphicsSceneMouseEvent *
>(event);
407 QGraphicsSceneWheelEvent *we =
static_cast<QGraphicsSceneWheelEvent *
>(event);
409 switch (event->type()) {
410 case QEvent::GraphicsSceneMousePress:
413 case QEvent::GraphicsSceneMouseRelease:
416 case QEvent::GraphicsSceneMouseMove:
419 case QEvent::TouchBegin:
422 case QEvent::TouchUpdate: {
423 QList<QTouchEvent::TouchPoint> touchPoints =
static_cast<QTouchEvent *
>(event)->touchPoints();
424 if (touchPoints.count() == 2) {
425 const QTouchEvent::TouchPoint &touchPoint0 = touchPoints.first();
426 const QTouchEvent::TouchPoint &touchPoint1 = touchPoints.last();
427 const QLineF line0(touchPoint0.lastPos(), touchPoint1.lastPos());
428 const QLineF line1(touchPoint0.pos(), touchPoint1.pos());
429 const QLineF startLine(touchPoint0.startPos(), touchPoint1.startPos());
430 const QPointF point = line1.pointAt(0.5);
431 const QPointF lastPoint = line0.pointAt(0.5);
433 if (d->multitouchGesture == KineticScrollingPrivate::GestureNone) {
434 d->multitouchGesture = KineticScrollingPrivate::GestureUndefined;
436 if (d->multitouchGesture == KineticScrollingPrivate::GestureUndefined) {
437 const int zoomDistance = qAbs(line1.length() - startLine.length());
438 const int dragDistance = (startLine.pointAt(0.5) - point).manhattanLength();
440 if (zoomDistance - dragDistance > 30) {
441 d->multitouchGesture = KineticScrollingPrivate::GestureZoom;
442 }
else if (dragDistance - zoomDistance > 30) {
443 d->multitouchGesture = KineticScrollingPrivate::GestureScroll;
447 if (d->multitouchGesture == KineticScrollingPrivate::GestureScroll) {
448 QGraphicsSceneMouseEvent fakeEvent;
449 fakeEvent.setPos(point);
450 fakeEvent.setLastPos(lastPoint);
452 }
else if (d->multitouchGesture == KineticScrollingPrivate::GestureZoom) {
453 qreal scaleFactor = 1;
454 if (line0.length() > 0) {
455 scaleFactor = line1.length() / line0.length();
458 qreal zoom = d->parent->property(
"zoomFactor").toReal();
459 d->parent->setProperty(
"zoomFactor", zoom * scaleFactor);
464 case QEvent::TouchEnd:
466 d->multitouchGesture = KineticScrollingPrivate::GestureNone;
469 case QEvent::GraphicsSceneWheel:
472 case QEvent::KeyPress: {
473 QKeyEvent *ke =
static_cast<QKeyEvent *
>(event);