67117eb51afd209ec979237161b32309075fefcf
[profile/ivi/qtbase.git] / tests / auto / widgets / util / qscroller / tst_qscroller.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the $MODULE$ of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <QtGui>
43 #include <QtWidgets>
44 #include <QtTest>
45 #include <QtGui/QWindowSystemInterface>
46
47 // #include <QDebug>
48
49 class tst_QScrollerWidget : public QWidget
50 {
51 public:
52     tst_QScrollerWidget()
53         : QWidget()
54     {
55         reset();
56     }
57
58     void reset()
59     {
60         receivedPrepare = false;
61         receivedScroll = false;
62         receivedFirst = false;
63         receivedLast = false;
64         receivedOvershoot = false;
65     }
66
67     bool event(QEvent *e)
68     {
69         switch (e->type()) {
70         case QEvent::Gesture:
71             e->setAccepted(false); // better reject the event or QGestureManager will make trouble
72             return false;
73
74         case QEvent::ScrollPrepare:
75             {
76                 receivedPrepare = true;
77                 QScrollPrepareEvent *se = static_cast<QScrollPrepareEvent *>(e);
78                 se->setViewportSize(QSizeF(100,100));
79                 se->setContentPosRange(scrollArea);
80                 se->setContentPos(scrollPosition);
81                 se->accept();
82                 return true;
83             }
84
85         case QEvent::Scroll:
86             {
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();
90
91                 if (se->scrollState() == QScrollEvent::ScrollStarted)
92                     receivedFirst = true;
93                 if (se->scrollState() == QScrollEvent::ScrollFinished)
94                     receivedLast = true;
95
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;
101                 return true;
102             }
103
104         default:
105             return QObject::event(e);
106         }
107     }
108
109
110     QRectF scrollArea;
111     QPointF scrollPosition;
112
113     bool receivedPrepare;
114     bool receivedScroll;
115     bool receivedFirst;
116     bool receivedLast;
117     bool receivedOvershoot;
118
119     QPointF currentPos;
120     QPointF overshoot;
121 };
122
123
124 class tst_QScroller : public QObject
125 {
126     Q_OBJECT
127 public:
128     tst_QScroller() { }
129     ~tst_QScroller() { }
130
131 private:
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);
134
135 private slots:
136     void staticScrollers();
137     void scrollerProperties();
138     void scrollTo();
139     void scroll();
140     void overshoot();
141 };
142
143 /*! \internal
144     Generates touchBegin, touchUpdate and touchEnd events to trigger scrolling.
145     Tests some in between states but does not wait until scrolling is finished.
146 */
147 void tst_QScroller::kineticScroll( tst_QScrollerWidget *sw, QPointF from, QPoint touchStart, QPoint touchUpdate, QPoint touchEnd)
148 {
149     sw->scrollPosition = from;
150     sw->currentPos= from;
151
152     QScroller *s1 = QScroller::scroller(sw);
153     QCOMPARE( s1->state(), QScroller::Inactive );
154
155     QScrollerProperties sp1 = QScroller::scroller(sw)->scrollerProperties();
156     int fps = 60;
157
158     QTouchEvent::TouchPoint rawTouchPoint;
159     rawTouchPoint.setId(0);
160
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,
171                             device,
172                             Qt::NoModifier,
173                             Qt::TouchPointPressed,
174                             (QList<QTouchEvent::TouchPoint>() << touchPoint));
175     QApplication::sendEvent(sw, &touchEvent1);
176
177     QCOMPARE( s1->state(), QScroller::Pressed );
178
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,
185                             device,
186                             Qt::NoModifier,
187                             Qt::TouchPointMoved,
188                             (QList<QTouchEvent::TouchPoint>() << touchPoint));
189     QApplication::sendEvent(sw, &touchEvent2);
190
191     QCOMPARE( s1->state(), QScroller::Dragging );
192     QCOMPARE( sw->receivedPrepare, true );
193
194
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 );
199
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);
204
205     // send the touch end
206     touchPoint.setPos(touchEnd);
207     touchPoint.setScenePos(touchEnd);
208     touchPoint.setScreenPos(touchEnd);
209     QTouchEvent touchEvent5(QEvent::TouchEnd,
210                             device,
211                             Qt::NoModifier,
212                             Qt::TouchPointReleased,
213                             (QList<QTouchEvent::TouchPoint>() << touchPoint));
214     QApplication::sendEvent(sw, &touchEvent5);
215 }
216
217 /*! \internal
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.
220 */
221 void tst_QScroller::kineticScrollNoTest( tst_QScrollerWidget *sw, QPointF from, QPoint touchStart, QPoint touchUpdate, QPoint touchEnd)
222 {
223     sw->scrollPosition = from;
224     sw->currentPos = from;
225
226     QScroller *s1 = QScroller::scroller(sw);
227     QCOMPARE( s1->state(), QScroller::Inactive );
228
229     QScrollerProperties sp1 = s1->scrollerProperties();
230     int fps = 60;
231
232     QTouchEvent::TouchPoint rawTouchPoint;
233     rawTouchPoint.setId(0);
234
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,
245                             device,
246                             Qt::NoModifier,
247                             Qt::TouchPointPressed,
248                             (QList<QTouchEvent::TouchPoint>() << touchPoint));
249     QApplication::sendEvent(sw, &touchEvent1);
250
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,
257                             device,
258                             Qt::NoModifier,
259                             Qt::TouchPointMoved,
260                             (QList<QTouchEvent::TouchPoint>() << touchPoint));
261     QApplication::sendEvent(sw, &touchEvent2);
262
263     QTest::qWait(1000 / fps * 2); // wait until the first scroll move
264
265     // send the touch end
266     touchPoint.setPos(touchEnd);
267     touchPoint.setScenePos(touchEnd);
268     touchPoint.setScreenPos(touchEnd);
269     QTouchEvent touchEvent5(QEvent::TouchEnd,
270                             device,
271                             Qt::NoModifier,
272                             Qt::TouchPointReleased,
273                             (QList<QTouchEvent::TouchPoint>() << touchPoint));
274     QApplication::sendEvent(sw, &touchEvent5);
275 }
276
277
278 void tst_QScroller::staticScrollers()
279 {
280     // scrollers
281     {
282         QObject *o1 = new QObject(this);
283         QObject *o2 = new QObject(this);
284
285         // get scroller for object
286         QScroller *s1 = QScroller::scroller(o1);
287         QScroller *s2 = QScroller::scroller(o2);
288
289         QVERIFY(s1);
290         QVERIFY(s2);
291         QVERIFY(s1 != s2);
292
293         QVERIFY(!QScroller::scroller(static_cast<const QObject*>(0)));
294         QCOMPARE(QScroller::scroller(o1), s1);
295
296         delete o1;
297         delete o2;
298     }
299
300     // the same for properties
301     {
302         QObject *o1 = new QObject(this);
303         QObject *o2 = new QObject(this);
304
305         // get scroller for object
306         QScrollerProperties sp1 = QScroller::scroller(o1)->scrollerProperties();
307         QScrollerProperties sp2 = QScroller::scroller(o2)->scrollerProperties();
308
309         // default properties should be the same
310         QVERIFY(sp1 == sp2);
311
312         QCOMPARE(QScroller::scroller(o1)->scrollerProperties(), sp1);
313
314         delete o1;
315         delete o2;
316     }
317 }
318
319 void tst_QScroller::scrollerProperties()
320 {
321     QObject *o1 = new QObject(this);
322     QScrollerProperties sp1 = QScroller::scroller(o1)->scrollerProperties();
323
324     QScrollerProperties::ScrollMetric metrics[] =
325     {
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)|)
330
331         QScrollerProperties::DecelerationFactor,                      // slope of the curve
332
333         QScrollerProperties::MinimumVelocity,                         // qreal [m/s]
334         QScrollerProperties::MaximumVelocity,                         // qreal [m/s]
335         QScrollerProperties::MaximumClickThroughVelocity,             // qreal [m/s]
336
337         QScrollerProperties::AcceleratingFlickMaximumTime,            // qreal [s]
338         QScrollerProperties::AcceleratingFlickSpeedupFactor,          // qreal [1..]
339
340         QScrollerProperties::SnapPositionRatio,                       // qreal [0..1]
341         QScrollerProperties::SnapTime,                                // qreal [s]
342
343         QScrollerProperties::OvershootDragResistanceFactor,           // qreal [0..1]
344         QScrollerProperties::OvershootDragDistanceFactor,             // qreal [0..1]
345         QScrollerProperties::OvershootScrollDistanceFactor,           // qreal [0..1]
346         QScrollerProperties::OvershootScrollTime,                     // qreal [s]
347     };
348
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);
352     }
353     sp1.setScrollMetric(QScrollerProperties::ScrollingCurve, QEasingCurve(QEasingCurve::OutQuart));
354     QCOMPARE(sp1.scrollMetric(QScrollerProperties::ScrollingCurve).toEasingCurve().type(), QEasingCurve::OutQuart);
355
356     sp1.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QVariant::fromValue(QScrollerProperties::OvershootAlwaysOff));
357     QCOMPARE(sp1.scrollMetric(QScrollerProperties::HorizontalOvershootPolicy).value<QScrollerProperties::OvershootPolicy>(), QScrollerProperties::OvershootAlwaysOff);
358
359     sp1.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy, QVariant::fromValue(QScrollerProperties::OvershootAlwaysOn));
360     QCOMPARE(sp1.scrollMetric(QScrollerProperties::VerticalOvershootPolicy).value<QScrollerProperties::OvershootPolicy>(), QScrollerProperties::OvershootAlwaysOn);
361
362     sp1.setScrollMetric(QScrollerProperties::FrameRate, QVariant::fromValue(QScrollerProperties::Fps20));
363     QCOMPARE(sp1.scrollMetric(QScrollerProperties::FrameRate).value<QScrollerProperties::FrameRates>(), QScrollerProperties::Fps20);
364 }
365
366 void tst_QScroller::scrollTo()
367 {
368     {
369         tst_QScrollerWidget *sw = new tst_QScrollerWidget();
370         sw->scrollArea = QRectF( 0, 0, 1000, 1000 );
371         sw->scrollPosition = QPointF( 500, 500 );
372
373         QScroller *s1 = QScroller::scroller(sw);
374         QCOMPARE( s1->state(), QScroller::Inactive );
375
376         // a normal scroll
377         s1->scrollTo(QPointF(100,100), 100);
378         QTest::qWait(200);
379
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 ));
387
388         delete sw;
389     }
390 }
391
392 void tst_QScroller::scroll()
393 {
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");
396     return;
397 #endif
398
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);
405
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 );
410
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)
415         QTest::qWait(100);
416
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);
422
423     // -- try to scroll when nothing to scroll
424
425     sw->reset();
426     sw->scrollArea = QRectF(0, 0, 0, 1000);
427     kineticScrollNoTest(sw, QPointF(0, 500), QPoint(0, 0), QPoint(100, 0), QPoint(200, 0));
428
429     while (s1->state() != QScroller::Inactive)
430         QTest::qWait(20);
431
432     QCOMPARE(sw->currentPos.x(), 0.0);
433     QCOMPARE(sw->currentPos.y(), 500.0);
434
435     delete sw;
436 #endif
437 }
438
439 void tst_QScroller::overshoot()
440 {
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");
443     return;
444 #endif
445
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);
451
452     QScroller *s1 = QScroller::scroller(sw);
453     QScrollerProperties sp1 = s1->scrollerProperties();
454
455     sp1.setScrollMetric(QScrollerProperties::OvershootDragResistanceFactor, 0.5);
456     sp1.setScrollMetric(QScrollerProperties::OvershootDragDistanceFactor, 0.2);
457     sp1.setScrollMetric(QScrollerProperties::OvershootScrollDistanceFactor, 0.2);
458
459     // -- try to scroll with overshoot (when scrollable good case)
460
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));
464
465     while (s1->state() != QScroller::Inactive)
466         QTest::qWait(20);
467
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 );
472
473     // -- try to scroll with overshoot (when scrollable bad case)
474     sw->reset();
475     sw->scrollArea = QRectF(0, 0, 0, 1000);
476
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));
480
481     while (s1->state() != QScroller::Inactive)
482         QTest::qWait(20);
483
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 );
488
489     // -- try to scroll with overshoot (always on)
490     sw->reset();
491     sw->scrollArea = QRectF(0, 0, 0, 1000);
492
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));
496
497     while (s1->state() != QScroller::Inactive)
498         QTest::qWait(20);
499
500     //qDebug() << "Overshoot fuzzy: "<<sw->currentPos;
501
502     QVERIFY(qFuzzyCompare( sw->currentPos.x(), 0 ));
503     QVERIFY(qFuzzyCompare( sw->currentPos.y(), 500 ));
504     QCOMPARE( sw->receivedOvershoot, true );
505
506     // -- try to scroll with overshoot (always off)
507     sw->reset();
508     sw->scrollArea = QRectF(0, 0, 1000, 1000);
509
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));
513
514     while (s1->state() != QScroller::Inactive)
515         QTest::qWait(20);
516
517     QVERIFY(qFuzzyCompare( sw->currentPos.x(), 0 ));
518     QVERIFY(qFuzzyCompare( sw->currentPos.y(), 500 ));
519     QCOMPARE( sw->receivedOvershoot, false );
520
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);
524     sw->reset();
525     sw->scrollArea = QRectF(0, 0, 1000, 1000);
526
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));
530
531     while (s1->state() != QScroller::Inactive)
532         QTest::qWait(20);
533
534     QVERIFY(qFuzzyCompare( sw->currentPos.x(), 0 ));
535     QVERIFY(qFuzzyCompare( sw->currentPos.y(), 500 ));
536     QCOMPARE( sw->receivedOvershoot, false );
537
538     delete sw;
539 #endif
540 }
541
542 QTEST_MAIN(tst_QScroller)
543
544 #include "tst_qscroller.moc"