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)
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;
118 bool forwardingEvent;
119 Gesture multitouchGesture;
121 unsigned int timeDelta;
127 : d(new KineticScrollingPrivate)
137 void KineticScrolling::duration( )
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)) {
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())) {
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>();
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>();
247 if ((d->contentsSize.width() < d->viewportGeometry.width()) &&
248 (d->contentsSize.height() < d->viewportGeometry.height())) {
249 d->kinMovement =
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();
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()));
352 SIGNAL(
stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)),
this,
353 SIGNAL(
stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)));
354 delete d->scrollAnimation;
362 connect(d->scrollAnimation, SIGNAL(finished()),
this, SLOT(overshoot()));
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())))) {
409 switch (event->
type()) {
410 case QEvent::GraphicsSceneMousePress:
413 case QEvent::GraphicsSceneMouseRelease:
416 case QEvent::GraphicsSceneMouseMove:
419 case QEvent::TouchBegin:
422 case QEvent::TouchUpdate: {
424 if (touchPoints.
count() == 2) {
428 const QLineF line1(touchPoint0.
pos(), touchPoint1.
pos());
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) {
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: {
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
virtual bool event(QEvent *e)
int count(const T &value) const
void setParent(QObject *parent)
Qt::Orientation orientation() const
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)