1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the $MODULE$ of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
45 #include <qpa/qwindowsysteminterface.h>
49 class tst_QScrollerWidget : public QWidget
60 receivedPrepare = false;
61 receivedScroll = false;
62 receivedFirst = false;
64 receivedOvershoot = false;
71 e->setAccepted(false); // better reject the event or QGestureManager will make trouble
74 case QEvent::ScrollPrepare:
76 receivedPrepare = true;
77 QScrollPrepareEvent *se = static_cast<QScrollPrepareEvent *>(e);
78 se->setViewportSize(QSizeF(100,100));
79 se->setContentPosRange(scrollArea);
80 se->setContentPos(scrollPosition);
87 receivedScroll = true;
88 QScrollEvent *se = static_cast<QScrollEvent *>(e);
89 // qDebug() << "Scroll for"<<this<<"pos"<<se->scrollPos()<<"ov"<<se->overshoot()<<"first"<<se->isFirst()<<"last"<<se->isLast();
91 if (se->scrollState() == QScrollEvent::ScrollStarted)
93 if (se->scrollState() == QScrollEvent::ScrollFinished)
96 currentPos = se->contentPos();
97 overshoot = se->overshootDistance();
98 if (!qFuzzyCompare( overshoot.x() + 1.0, 1.0 ) ||
99 !qFuzzyCompare( overshoot.y() + 1.0, 1.0 ))
100 receivedOvershoot = true;
105 return QObject::event(e);
111 QPointF scrollPosition;
113 bool receivedPrepare;
117 bool receivedOvershoot;
124 class tst_QScroller : public QObject
132 void kineticScroll( tst_QScrollerWidget *sw, QPointF from, QPoint touchStart, QPoint touchUpdate, QPoint touchEnd);
133 void kineticScrollNoTest( tst_QScrollerWidget *sw, QPointF from, QPoint touchStart, QPoint touchUpdate, QPoint touchEnd);
136 void staticScrollers();
137 void scrollerProperties();
144 Generates touchBegin, touchUpdate and touchEnd events to trigger scrolling.
145 Tests some in between states but does not wait until scrolling is finished.
147 void tst_QScroller::kineticScroll( tst_QScrollerWidget *sw, QPointF from, QPoint touchStart, QPoint touchUpdate, QPoint touchEnd)
149 sw->scrollPosition = from;
150 sw->currentPos= from;
152 QScroller *s1 = QScroller::scroller(sw);
153 QCOMPARE( s1->state(), QScroller::Inactive );
155 QScrollerProperties sp1 = QScroller::scroller(sw)->scrollerProperties();
158 QTouchEvent::TouchPoint rawTouchPoint;
159 rawTouchPoint.setId(0);
161 // send the touch begin event
162 QTouchEvent::TouchPoint touchPoint(0);
163 touchPoint.setState(Qt::TouchPointPressed);
164 touchPoint.setPos(touchStart);
165 touchPoint.setScenePos(touchStart);
166 touchPoint.setScreenPos(touchStart);
167 QTouchDevice *device = new QTouchDevice;
168 device->setType(QTouchDevice::TouchScreen);
169 QWindowSystemInterface::registerTouchDevice(device);
170 QTouchEvent touchEvent1(QEvent::TouchBegin,
173 Qt::TouchPointPressed,
174 (QList<QTouchEvent::TouchPoint>() << touchPoint));
175 QApplication::sendEvent(sw, &touchEvent1);
177 QCOMPARE( s1->state(), QScroller::Pressed );
179 // send the touch update far enough to trigger a scroll
180 QTest::qWait(200); // we need to wait a little or else the speed would be infinite. now we have around 500 pixel per second.
181 touchPoint.setPos(touchUpdate);
182 touchPoint.setScenePos(touchUpdate);
183 touchPoint.setScreenPos(touchUpdate);
184 QTouchEvent touchEvent2(QEvent::TouchUpdate,
188 (QList<QTouchEvent::TouchPoint>() << touchPoint));
189 QApplication::sendEvent(sw, &touchEvent2);
191 QCOMPARE( s1->state(), QScroller::Dragging );
192 QCOMPARE( sw->receivedPrepare, true );
195 QTest::qWait(1000 / fps * 2); // wait until the first scroll move
196 QCOMPARE( sw->receivedFirst, true );
197 QCOMPARE( sw->receivedScroll, true );
198 QCOMPARE( sw->receivedOvershoot, false );
200 // note that the scrolling goes in a different direction than the mouse move
201 QPoint calculatedPos = from.toPoint() - touchUpdate - touchStart;
202 QVERIFY(qAbs(sw->currentPos.x() - calculatedPos.x()) < 1.0);
203 QVERIFY(qAbs(sw->currentPos.y() - calculatedPos.y()) < 1.0);
205 // send the touch end
206 touchPoint.setPos(touchEnd);
207 touchPoint.setScenePos(touchEnd);
208 touchPoint.setScreenPos(touchEnd);
209 QTouchEvent touchEvent5(QEvent::TouchEnd,
212 Qt::TouchPointReleased,
213 (QList<QTouchEvent::TouchPoint>() << touchPoint));
214 QApplication::sendEvent(sw, &touchEvent5);
218 Generates touchBegin, touchUpdate and touchEnd events to trigger scrolling.
219 This function does not have any in between tests, it does not expect the scroller to actually scroll.
221 void tst_QScroller::kineticScrollNoTest( tst_QScrollerWidget *sw, QPointF from, QPoint touchStart, QPoint touchUpdate, QPoint touchEnd)
223 sw->scrollPosition = from;
224 sw->currentPos = from;
226 QScroller *s1 = QScroller::scroller(sw);
227 QCOMPARE( s1->state(), QScroller::Inactive );
229 QScrollerProperties sp1 = s1->scrollerProperties();
232 QTouchEvent::TouchPoint rawTouchPoint;
233 rawTouchPoint.setId(0);
235 // send the touch begin event
236 QTouchEvent::TouchPoint touchPoint(0);
237 touchPoint.setState(Qt::TouchPointPressed);
238 touchPoint.setPos(touchStart);
239 touchPoint.setScenePos(touchStart);
240 touchPoint.setScreenPos(touchStart);
241 QTouchDevice *device = new QTouchDevice;
242 device->setType(QTouchDevice::TouchScreen);
243 QWindowSystemInterface::registerTouchDevice(device);
244 QTouchEvent touchEvent1(QEvent::TouchBegin,
247 Qt::TouchPointPressed,
248 (QList<QTouchEvent::TouchPoint>() << touchPoint));
249 QApplication::sendEvent(sw, &touchEvent1);
251 // send the touch update far enough to trigger a scroll
252 QTest::qWait(200); // we need to wait a little or else the speed would be infinite. now we have around 500 pixel per second.
253 touchPoint.setPos(touchUpdate);
254 touchPoint.setScenePos(touchUpdate);
255 touchPoint.setScreenPos(touchUpdate);
256 QTouchEvent touchEvent2(QEvent::TouchUpdate,
260 (QList<QTouchEvent::TouchPoint>() << touchPoint));
261 QApplication::sendEvent(sw, &touchEvent2);
263 QTest::qWait(1000 / fps * 2); // wait until the first scroll move
265 // send the touch end
266 touchPoint.setPos(touchEnd);
267 touchPoint.setScenePos(touchEnd);
268 touchPoint.setScreenPos(touchEnd);
269 QTouchEvent touchEvent5(QEvent::TouchEnd,
272 Qt::TouchPointReleased,
273 (QList<QTouchEvent::TouchPoint>() << touchPoint));
274 QApplication::sendEvent(sw, &touchEvent5);
278 void tst_QScroller::staticScrollers()
282 QObject *o1 = new QObject(this);
283 QObject *o2 = new QObject(this);
285 // get scroller for object
286 QScroller *s1 = QScroller::scroller(o1);
287 QScroller *s2 = QScroller::scroller(o2);
293 QVERIFY(!QScroller::scroller(static_cast<const QObject*>(0)));
294 QCOMPARE(QScroller::scroller(o1), s1);
300 // the same for properties
302 QObject *o1 = new QObject(this);
303 QObject *o2 = new QObject(this);
305 // get scroller for object
306 QScrollerProperties sp1 = QScroller::scroller(o1)->scrollerProperties();
307 QScrollerProperties sp2 = QScroller::scroller(o2)->scrollerProperties();
309 // default properties should be the same
312 QCOMPARE(QScroller::scroller(o1)->scrollerProperties(), sp1);
319 void tst_QScroller::scrollerProperties()
321 QObject *o1 = new QObject(this);
322 QScrollerProperties sp1 = QScroller::scroller(o1)->scrollerProperties();
324 QScrollerProperties::ScrollMetric metrics[] =
326 QScrollerProperties::MousePressEventDelay, // qreal [s]
327 QScrollerProperties::DragStartDistance, // qreal [m]
328 QScrollerProperties::DragVelocitySmoothingFactor, // qreal [0..1/s] (complex calculation involving time) v = v_new* DASF + v_old * (1-DASF)
329 QScrollerProperties::AxisLockThreshold, // qreal [0..1] atan(|min(dx,dy)|/|max(dx,dy)|)
331 QScrollerProperties::DecelerationFactor, // slope of the curve
333 QScrollerProperties::MinimumVelocity, // qreal [m/s]
334 QScrollerProperties::MaximumVelocity, // qreal [m/s]
335 QScrollerProperties::MaximumClickThroughVelocity, // qreal [m/s]
337 QScrollerProperties::AcceleratingFlickMaximumTime, // qreal [s]
338 QScrollerProperties::AcceleratingFlickSpeedupFactor, // qreal [1..]
340 QScrollerProperties::SnapPositionRatio, // qreal [0..1]
341 QScrollerProperties::SnapTime, // qreal [s]
343 QScrollerProperties::OvershootDragResistanceFactor, // qreal [0..1]
344 QScrollerProperties::OvershootDragDistanceFactor, // qreal [0..1]
345 QScrollerProperties::OvershootScrollDistanceFactor, // qreal [0..1]
346 QScrollerProperties::OvershootScrollTime, // qreal [s]
349 for (unsigned int i = 0; i < sizeof(metrics) / sizeof(metrics[0]); i++) {
350 sp1.setScrollMetric(metrics[i], 0.9);
351 QCOMPARE(sp1.scrollMetric(metrics[i]).toDouble(), 0.9);
353 sp1.setScrollMetric(QScrollerProperties::ScrollingCurve, QEasingCurve(QEasingCurve::OutQuart));
354 QCOMPARE(sp1.scrollMetric(QScrollerProperties::ScrollingCurve).toEasingCurve().type(), QEasingCurve::OutQuart);
356 sp1.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QVariant::fromValue(QScrollerProperties::OvershootAlwaysOff));
357 QCOMPARE(sp1.scrollMetric(QScrollerProperties::HorizontalOvershootPolicy).value<QScrollerProperties::OvershootPolicy>(), QScrollerProperties::OvershootAlwaysOff);
359 sp1.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy, QVariant::fromValue(QScrollerProperties::OvershootAlwaysOn));
360 QCOMPARE(sp1.scrollMetric(QScrollerProperties::VerticalOvershootPolicy).value<QScrollerProperties::OvershootPolicy>(), QScrollerProperties::OvershootAlwaysOn);
362 sp1.setScrollMetric(QScrollerProperties::FrameRate, QVariant::fromValue(QScrollerProperties::Fps20));
363 QCOMPARE(sp1.scrollMetric(QScrollerProperties::FrameRate).value<QScrollerProperties::FrameRates>(), QScrollerProperties::Fps20);
366 void tst_QScroller::scrollTo()
369 tst_QScrollerWidget *sw = new tst_QScrollerWidget();
370 sw->scrollArea = QRectF( 0, 0, 1000, 1000 );
371 sw->scrollPosition = QPointF( 500, 500 );
373 QScroller *s1 = QScroller::scroller(sw);
374 QCOMPARE( s1->state(), QScroller::Inactive );
377 s1->scrollTo(QPointF(100,100), 100);
380 QCOMPARE( sw->receivedPrepare, true );
381 QCOMPARE( sw->receivedScroll, true );
382 QCOMPARE( sw->receivedFirst, true );
383 QCOMPARE( sw->receivedLast, true );
384 QCOMPARE( sw->receivedOvershoot, false );
385 QVERIFY(qFuzzyCompare( sw->currentPos.x(), 100 ));
386 QVERIFY(qFuzzyCompare( sw->currentPos.y(), 100 ));
392 void tst_QScroller::scroll()
394 #if defined(Q_OS_MACX) && (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6)
395 QSKIP("Mac OS X < 10.6 does not support QTouchEvents");
399 #ifndef QT_NO_GESTURES
400 // -- good case. normal scroll
401 tst_QScrollerWidget *sw = new tst_QScrollerWidget();
402 sw->scrollArea = QRectF(0, 0, 1000, 1000);
403 QScroller::grabGesture(sw, QScroller::TouchGesture);
404 sw->setGeometry(100, 100, 400, 300);
406 QScroller *s1 = QScroller::scroller(sw);
407 kineticScroll(sw, QPointF(500, 500), QPoint(0, 0), QPoint(100, 100), QPoint(200, 200));
408 // now we should be scrolling
409 QCOMPARE( s1->state(), QScroller::Scrolling );
411 // wait until finished, check that no further first scroll is send
412 sw->receivedFirst = false;
413 sw->receivedScroll = false;
414 while (s1->state() == QScroller::Scrolling)
417 QCOMPARE( sw->receivedFirst, false );
418 QCOMPARE( sw->receivedScroll, true );
419 QCOMPARE( sw->receivedLast, true );
420 QVERIFY(sw->currentPos.x() < 400);
421 QVERIFY(sw->currentPos.y() < 400);
423 // -- try to scroll when nothing to scroll
426 sw->scrollArea = QRectF(0, 0, 0, 1000);
427 kineticScrollNoTest(sw, QPointF(0, 500), QPoint(0, 0), QPoint(100, 0), QPoint(200, 0));
429 while (s1->state() != QScroller::Inactive)
432 QCOMPARE(sw->currentPos.x(), 0.0);
433 QCOMPARE(sw->currentPos.y(), 500.0);
439 void tst_QScroller::overshoot()
441 #if defined(Q_OS_MACX) && (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6)
442 QSKIP("Mac OS X < 10.6 does not support QTouchEvents");
446 #ifndef QT_NO_GESTURES
447 tst_QScrollerWidget *sw = new tst_QScrollerWidget();
448 sw->scrollArea = QRectF(0, 0, 1000, 1000);
449 QScroller::grabGesture(sw, QScroller::TouchGesture);
450 sw->setGeometry(100, 100, 400, 300);
452 QScroller *s1 = QScroller::scroller(sw);
453 QScrollerProperties sp1 = s1->scrollerProperties();
455 sp1.setScrollMetric(QScrollerProperties::OvershootDragResistanceFactor, 0.5);
456 sp1.setScrollMetric(QScrollerProperties::OvershootDragDistanceFactor, 0.2);
457 sp1.setScrollMetric(QScrollerProperties::OvershootScrollDistanceFactor, 0.2);
459 // -- try to scroll with overshoot (when scrollable good case)
461 sp1.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QVariant::fromValue(QScrollerProperties::OvershootWhenScrollable));
462 s1->setScrollerProperties(sp1);
463 kineticScrollNoTest(sw, QPointF(500, 500), QPoint(0, 0), QPoint(400, 0), QPoint(490, 0));
465 while (s1->state() != QScroller::Inactive)
468 //qDebug() << "Overshoot fuzzy: "<<sw->currentPos;
469 QVERIFY(qFuzzyCompare( sw->currentPos.x(), 0 ));
470 QVERIFY(qFuzzyCompare( sw->currentPos.y(), 500 ));
471 QCOMPARE( sw->receivedOvershoot, true );
473 // -- try to scroll with overshoot (when scrollable bad case)
475 sw->scrollArea = QRectF(0, 0, 0, 1000);
477 sp1.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QVariant::fromValue(QScrollerProperties::OvershootWhenScrollable));
478 s1->setScrollerProperties(sp1);
479 kineticScrollNoTest(sw, QPointF(0, 500), QPoint(0, 0), QPoint(400, 0), QPoint(490, 0));
481 while (s1->state() != QScroller::Inactive)
484 //qDebug() << "Overshoot fuzzy: "<<sw->currentPos;
485 QVERIFY(qFuzzyCompare( sw->currentPos.x(), 0 ));
486 QVERIFY(qFuzzyCompare( sw->currentPos.y(), 500 ));
487 QCOMPARE( sw->receivedOvershoot, false );
489 // -- try to scroll with overshoot (always on)
491 sw->scrollArea = QRectF(0, 0, 0, 1000);
493 sp1.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QVariant::fromValue(QScrollerProperties::OvershootAlwaysOn));
494 s1->setScrollerProperties(sp1);
495 kineticScrollNoTest(sw, QPointF(0, 500), QPoint(0, 0), QPoint(400, 0), QPoint(490, 0));
497 while (s1->state() != QScroller::Inactive)
500 //qDebug() << "Overshoot fuzzy: "<<sw->currentPos;
502 QVERIFY(qFuzzyCompare( sw->currentPos.x(), 0 ));
503 QVERIFY(qFuzzyCompare( sw->currentPos.y(), 500 ));
504 QCOMPARE( sw->receivedOvershoot, true );
506 // -- try to scroll with overshoot (always off)
508 sw->scrollArea = QRectF(0, 0, 1000, 1000);
510 sp1.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QVariant::fromValue(QScrollerProperties::OvershootAlwaysOff));
511 s1->setScrollerProperties(sp1);
512 kineticScrollNoTest(sw, QPointF(500, 500), QPoint(0, 0), QPoint(400, 0), QPoint(490, 0));
514 while (s1->state() != QScroller::Inactive)
517 QVERIFY(qFuzzyCompare( sw->currentPos.x(), 0 ));
518 QVERIFY(qFuzzyCompare( sw->currentPos.y(), 500 ));
519 QCOMPARE( sw->receivedOvershoot, false );
521 // -- try to scroll with overshoot (always on but max overshoot = 0)
522 sp1.setScrollMetric(QScrollerProperties::OvershootDragDistanceFactor, 0.0);
523 sp1.setScrollMetric(QScrollerProperties::OvershootScrollDistanceFactor, 0.0);
525 sw->scrollArea = QRectF(0, 0, 1000, 1000);
527 sp1.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QVariant::fromValue(QScrollerProperties::OvershootAlwaysOn));
528 s1->setScrollerProperties(sp1);
529 kineticScrollNoTest(sw, QPointF(500, 500), QPoint(0, 0), QPoint(400, 0), QPoint(490, 0));
531 while (s1->state() != QScroller::Inactive)
534 QVERIFY(qFuzzyCompare( sw->currentPos.x(), 0 ));
535 QVERIFY(qFuzzyCompare( sw->currentPos.y(), 500 ));
536 QCOMPARE( sw->receivedOvershoot, false );
542 QTEST_MAIN(tst_QScroller)
544 #include "tst_qscroller.moc"