1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the $MODULE$ of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtGui>
30#include <QtWidgets>
31#include <QtTest>
32#include <qpa/qwindowsysteminterface.h>
33
34// #include <QDebug>
35
36class tst_QScrollerWidget : public QWidget
37{
38public:
39 tst_QScrollerWidget()
40 : QWidget()
41 {
42 reset();
43 }
44
45 void reset()
46 {
47 receivedPrepare = false;
48 receivedScroll = false;
49 receivedFirst = false;
50 receivedLast = false;
51 receivedOvershoot = false;
52 }
53
54 bool event(QEvent *e)
55 {
56 switch (e->type()) {
57 case QEvent::Gesture:
58 e->setAccepted(false); // better reject the event or QGestureManager will make trouble
59 return false;
60
61 case QEvent::ScrollPrepare:
62 {
63 receivedPrepare = true;
64 QScrollPrepareEvent *se = static_cast<QScrollPrepareEvent *>(e);
65 se->setViewportSize(QSizeF(100,100));
66 se->setContentPosRange(scrollArea);
67 se->setContentPos(scrollPosition);
68 se->accept();
69 return true;
70 }
71
72 case QEvent::Scroll:
73 {
74 receivedScroll = true;
75 QScrollEvent *se = static_cast<QScrollEvent *>(e);
76 // qDebug() << "Scroll for"<<this<<"pos"<<se->scrollPos()<<"ov"<<se->overshoot()<<"first"<<se->isFirst()<<"last"<<se->isLast();
77
78 if (se->scrollState() == QScrollEvent::ScrollStarted)
79 receivedFirst = true;
80 if (se->scrollState() == QScrollEvent::ScrollFinished)
81 receivedLast = true;
82
83 currentPos = se->contentPos();
84 overshoot = se->overshootDistance();
85 if (!qFuzzyCompare(p1: overshoot.x() + 1.0, p2: 1.0) ||
86 !qFuzzyCompare(p1: overshoot.y() + 1.0, p2: 1.0))
87 receivedOvershoot = true;
88 return true;
89 }
90
91 default:
92 return QObject::event(event: e);
93 }
94 }
95
96
97 QRectF scrollArea;
98 QPointF scrollPosition;
99
100 bool receivedPrepare;
101 bool receivedScroll;
102 bool receivedFirst;
103 bool receivedLast;
104 bool receivedOvershoot;
105
106 QPointF currentPos;
107 QPointF overshoot;
108};
109
110
111class tst_QScroller : public QObject
112{
113 Q_OBJECT
114public:
115 tst_QScroller() { }
116 ~tst_QScroller() { }
117
118private:
119 void kineticScroll(tst_QScrollerWidget *sw, QPointF from, QPoint touchStart, QPoint touchUpdate, QPoint touchEnd);
120 void kineticScrollNoTest(tst_QScrollerWidget *sw, QPointF from, QPoint touchStart, QPoint touchUpdate, QPoint touchEnd);
121
122private slots:
123 void staticScrollers();
124 void scrollerProperties();
125 void scrollTo();
126 void scroll();
127 void overshoot();
128 void multipleWindows();
129
130private:
131 QTouchDevice *m_touchScreen = QTest::createTouchDevice();
132};
133
134/*! \internal
135 Generates touchBegin, touchUpdate and touchEnd events to trigger scrolling.
136 Tests some in between states but does not wait until scrolling is finished.
137*/
138void tst_QScroller::kineticScroll(tst_QScrollerWidget *sw, QPointF from, QPoint touchStart, QPoint touchUpdate, QPoint touchEnd)
139{
140 sw->scrollPosition = from;
141 sw->currentPos= from;
142
143 QScroller *s1 = QScroller::scroller(target: sw);
144 QCOMPARE(s1->state(), QScroller::Inactive);
145
146 QScrollerProperties sp1 = QScroller::scroller(target: sw)->scrollerProperties();
147
148 QTouchEvent::TouchPoint rawTouchPoint;
149 rawTouchPoint.setId(0);
150
151 // send the touch begin event
152 QTouchEvent::TouchPoint touchPoint(0);
153 touchPoint.setState(Qt::TouchPointPressed);
154 touchPoint.setPos(touchStart);
155 touchPoint.setScenePos(touchStart);
156 touchPoint.setScreenPos(touchStart);
157 QTouchEvent touchEvent1(QEvent::TouchBegin,
158 m_touchScreen,
159 Qt::NoModifier,
160 Qt::TouchPointPressed,
161 (QList<QTouchEvent::TouchPoint>() << touchPoint));
162 QApplication::sendEvent(receiver: sw, event: &touchEvent1);
163
164 QCOMPARE(s1->state(), QScroller::Pressed);
165
166 // send the touch update far enough to trigger a scroll
167 QTest::qWait(ms: 200); // we need to wait a little or else the speed would be infinite. now we have around 500 pixel per second.
168 touchPoint.setPos(touchUpdate);
169 touchPoint.setScenePos(touchUpdate);
170 touchPoint.setScreenPos(touchUpdate);
171 QTouchEvent touchEvent2(QEvent::TouchUpdate,
172 m_touchScreen,
173 Qt::NoModifier,
174 Qt::TouchPointMoved,
175 (QList<QTouchEvent::TouchPoint>() << touchPoint));
176 QApplication::sendEvent(receiver: sw, event: &touchEvent2);
177
178 QCOMPARE(s1->state(), QScroller::Dragging);
179 QCOMPARE(sw->receivedPrepare, true);
180
181
182 QTRY_COMPARE(sw->receivedFirst, true);
183 QCOMPARE(sw->receivedScroll, true);
184 QCOMPARE(sw->receivedOvershoot, false);
185
186 // note that the scrolling goes in a different direction than the mouse move
187 QPoint calculatedPos = from.toPoint() - touchUpdate - touchStart;
188 QVERIFY(qAbs(sw->currentPos.x() - calculatedPos.x()) < 1.0);
189 QVERIFY(qAbs(sw->currentPos.y() - calculatedPos.y()) < 1.0);
190
191 // send the touch end
192 touchPoint.setPos(touchEnd);
193 touchPoint.setScenePos(touchEnd);
194 touchPoint.setScreenPos(touchEnd);
195 QTouchEvent touchEvent5(QEvent::TouchEnd,
196 m_touchScreen,
197 Qt::NoModifier,
198 Qt::TouchPointReleased,
199 (QList<QTouchEvent::TouchPoint>() << touchPoint));
200 QApplication::sendEvent(receiver: sw, event: &touchEvent5);
201}
202
203/*! \internal
204 Generates touchBegin, touchUpdate and touchEnd events to trigger scrolling.
205 This function does not have any in between tests, it does not expect the scroller to actually scroll.
206*/
207void tst_QScroller::kineticScrollNoTest(tst_QScrollerWidget *sw, QPointF from, QPoint touchStart, QPoint touchUpdate, QPoint touchEnd)
208{
209 sw->scrollPosition = from;
210 sw->currentPos = from;
211
212 QScroller *s1 = QScroller::scroller(target: sw);
213 QCOMPARE(s1->state(), QScroller::Inactive);
214
215 QScrollerProperties sp1 = s1->scrollerProperties();
216 int fps = 60;
217
218 QTouchEvent::TouchPoint rawTouchPoint;
219 rawTouchPoint.setId(0);
220
221 // send the touch begin event
222 QTouchEvent::TouchPoint touchPoint(0);
223 touchPoint.setState(Qt::TouchPointPressed);
224 touchPoint.setPos(touchStart);
225 touchPoint.setScenePos(touchStart);
226 touchPoint.setScreenPos(touchStart);
227 QTouchEvent touchEvent1(QEvent::TouchBegin,
228 m_touchScreen,
229 Qt::NoModifier,
230 Qt::TouchPointPressed,
231 (QList<QTouchEvent::TouchPoint>() << touchPoint));
232 QApplication::sendEvent(receiver: sw, event: &touchEvent1);
233
234 // send the touch update far enough to trigger a scroll
235 QTest::qWait(ms: 200); // we need to wait a little or else the speed would be infinite. now we have around 500 pixel per second.
236 touchPoint.setPos(touchUpdate);
237 touchPoint.setScenePos(touchUpdate);
238 touchPoint.setScreenPos(touchUpdate);
239 QTouchEvent touchEvent2(QEvent::TouchUpdate,
240 m_touchScreen,
241 Qt::NoModifier,
242 Qt::TouchPointMoved,
243 (QList<QTouchEvent::TouchPoint>() << touchPoint));
244 QApplication::sendEvent(receiver: sw, event: &touchEvent2);
245
246 QTest::qWait(ms: 1000 / fps * 2); // wait until the first scroll move
247
248 // send the touch end
249 touchPoint.setPos(touchEnd);
250 touchPoint.setScenePos(touchEnd);
251 touchPoint.setScreenPos(touchEnd);
252 QTouchEvent touchEvent5(QEvent::TouchEnd,
253 m_touchScreen,
254 Qt::NoModifier,
255 Qt::TouchPointReleased,
256 (QList<QTouchEvent::TouchPoint>() << touchPoint));
257 QApplication::sendEvent(receiver: sw, event: &touchEvent5);
258}
259
260
261void tst_QScroller::staticScrollers()
262{
263 // scrollers
264 {
265 QObject *o1 = new QObject(this);
266 QObject *o2 = new QObject(this);
267
268 // get scroller for object
269 QScroller *s1 = QScroller::scroller(target: o1);
270 QScroller *s2 = QScroller::scroller(target: o2);
271
272 QVERIFY(s1);
273 QVERIFY(s2);
274 QVERIFY(s1 != s2);
275
276 QVERIFY(!QScroller::scroller(static_cast<const QObject*>(0)));
277 QCOMPARE(QScroller::scroller(o1), s1);
278
279 delete o1;
280 delete o2;
281 }
282
283 // the same for properties
284 {
285 QObject *o1 = new QObject(this);
286 QObject *o2 = new QObject(this);
287
288 // get scroller for object
289 QScrollerProperties sp1 = QScroller::scroller(target: o1)->scrollerProperties();
290 QScrollerProperties sp2 = QScroller::scroller(target: o2)->scrollerProperties();
291
292 // default properties should be the same
293 QCOMPARE(sp1, sp2);
294
295 QCOMPARE(QScroller::scroller(o1)->scrollerProperties(), sp1);
296
297 delete o1;
298 delete o2;
299 }
300}
301
302void tst_QScroller::scrollerProperties()
303{
304 QObject *o1 = new QObject(this);
305 QScrollerProperties sp1 = QScroller::scroller(target: o1)->scrollerProperties();
306
307 QScrollerProperties::ScrollMetric metrics[] =
308 {
309 QScrollerProperties::MousePressEventDelay, // qreal [s]
310 QScrollerProperties::DragStartDistance, // qreal [m]
311 QScrollerProperties::DragVelocitySmoothingFactor, // qreal [0..1/s] (complex calculation involving time) v = v_new* DASF + v_old * (1-DASF)
312 QScrollerProperties::AxisLockThreshold, // qreal [0..1] atan(|min(dx,dy)|/|max(dx,dy)|)
313
314 QScrollerProperties::DecelerationFactor, // slope of the curve
315
316 QScrollerProperties::MinimumVelocity, // qreal [m/s]
317 QScrollerProperties::MaximumVelocity, // qreal [m/s]
318 QScrollerProperties::MaximumClickThroughVelocity, // qreal [m/s]
319
320 QScrollerProperties::AcceleratingFlickMaximumTime, // qreal [s]
321 QScrollerProperties::AcceleratingFlickSpeedupFactor, // qreal [1..]
322
323 QScrollerProperties::SnapPositionRatio, // qreal [0..1]
324 QScrollerProperties::SnapTime, // qreal [s]
325
326 QScrollerProperties::OvershootDragResistanceFactor, // qreal [0..1]
327 QScrollerProperties::OvershootDragDistanceFactor, // qreal [0..1]
328 QScrollerProperties::OvershootScrollDistanceFactor, // qreal [0..1]
329 QScrollerProperties::OvershootScrollTime, // qreal [s]
330 };
331
332 for (unsigned int i = 0; i < sizeof(metrics) / sizeof(metrics[0]); i++) {
333 sp1.setScrollMetric(metric: metrics[i], value: 0.9);
334 QCOMPARE(sp1.scrollMetric(metrics[i]).toDouble(), 0.9);
335 }
336 sp1.setScrollMetric(metric: QScrollerProperties::ScrollingCurve, value: QEasingCurve(QEasingCurve::OutQuart));
337 QCOMPARE(sp1.scrollMetric(QScrollerProperties::ScrollingCurve).toEasingCurve().type(), QEasingCurve::OutQuart);
338
339 sp1.setScrollMetric(metric: QScrollerProperties::HorizontalOvershootPolicy, value: QVariant::fromValue(value: QScrollerProperties::OvershootAlwaysOff));
340 QCOMPARE(sp1.scrollMetric(QScrollerProperties::HorizontalOvershootPolicy).value<QScrollerProperties::OvershootPolicy>(), QScrollerProperties::OvershootAlwaysOff);
341
342 sp1.setScrollMetric(metric: QScrollerProperties::VerticalOvershootPolicy, value: QVariant::fromValue(value: QScrollerProperties::OvershootAlwaysOn));
343 QCOMPARE(sp1.scrollMetric(QScrollerProperties::VerticalOvershootPolicy).value<QScrollerProperties::OvershootPolicy>(), QScrollerProperties::OvershootAlwaysOn);
344
345 sp1.setScrollMetric(metric: QScrollerProperties::FrameRate, value: QVariant::fromValue(value: QScrollerProperties::Fps20));
346 QCOMPARE(sp1.scrollMetric(QScrollerProperties::FrameRate).value<QScrollerProperties::FrameRates>(), QScrollerProperties::Fps20);
347}
348
349void tst_QScroller::scrollTo()
350{
351 QScopedPointer<tst_QScrollerWidget> sw(new tst_QScrollerWidget);
352 sw->show();
353 QApplication::setActiveWindow(sw.data());
354 if (!QTest::qWaitForWindowExposed(widget: sw.data()) || !QTest::qWaitForWindowActive(widget: sw.data()))
355 QSKIP("Failed to show and activate window");
356
357 sw->scrollArea = QRectF(0, 0, 1000, 1000);
358 sw->scrollPosition = QPointF(500, 500);
359
360 QScroller *s1 = QScroller::scroller(target: sw.data());
361 QCOMPARE(s1->state(), QScroller::Inactive);
362
363 // a normal scroll
364 s1->scrollTo(pos: QPointF(100,100), scrollTime: 100);
365 QTest::qWait(ms: 200);
366
367 QTRY_COMPARE(sw->receivedPrepare, true);
368 QCOMPARE(sw->receivedScroll, true);
369 QCOMPARE(sw->receivedFirst, true);
370 QCOMPARE(sw->receivedLast, true);
371 QCOMPARE(sw->receivedOvershoot, false);
372 QTRY_VERIFY(qFuzzyCompare(sw->currentPos.x(), 100));
373 QVERIFY(qFuzzyCompare(sw->currentPos.y(), 100));
374}
375
376void tst_QScroller::scroll()
377{
378#if QT_CONFIG(gestures) && QT_CONFIG(scroller)
379 // -- good case. normal scroll
380 QScopedPointer<tst_QScrollerWidget> sw(new tst_QScrollerWidget());
381 sw->scrollArea = QRectF(0, 0, 1000, 1000);
382 QScroller::grabGesture(target: sw.data(), gestureType: QScroller::TouchGesture);
383 sw->setGeometry(ax: 100, ay: 100, aw: 400, ah: 300);
384 sw->show();
385 QApplication::setActiveWindow(sw.data());
386 if (!QTest::qWaitForWindowExposed(widget: sw.data()) || !QTest::qWaitForWindowActive(widget: sw.data()))
387 QSKIP("Failed to show and activate window");
388
389 QScroller *s1 = QScroller::scroller(target: sw.data());
390 kineticScroll(sw: sw.data(), from: QPointF(500, 500), touchStart: QPoint(0, 0), touchUpdate: QPoint(100, 100), touchEnd: QPoint(200, 200));
391 // now we should be scrolling
392 QTRY_COMPARE(s1->state(), QScroller::Scrolling);
393
394 // wait until finished, check that no further first scroll is sent
395 sw->receivedFirst = false;
396 sw->receivedScroll = false;
397 QTRY_VERIFY(s1->state() != QScroller::Scrolling);
398
399 QCOMPARE(sw->receivedFirst, false);
400 QCOMPARE(sw->receivedScroll, true);
401 QCOMPARE(sw->receivedLast, true);
402 QVERIFY(sw->currentPos.x() < 400);
403 QVERIFY(sw->currentPos.y() < 400);
404
405 // -- try to scroll when nothing to scroll
406
407 sw->reset();
408 sw->scrollArea = QRectF(0, 0, 0, 1000);
409 kineticScrollNoTest(sw: sw.data(), from: QPointF(0, 500), touchStart: QPoint(0, 0), touchUpdate: QPoint(100, 0), touchEnd: QPoint(200, 0));
410
411 QTRY_COMPARE(s1->state(), QScroller::Inactive);
412
413 QCOMPARE(sw->currentPos.x(), 0.0);
414 QCOMPARE(sw->currentPos.y(), 500.0);
415#endif
416}
417
418void tst_QScroller::overshoot()
419{
420#if QT_CONFIG(gestures) && QT_CONFIG(scroller)
421 QScopedPointer<tst_QScrollerWidget> sw(new tst_QScrollerWidget);
422 sw->scrollArea = QRectF(0, 0, 1000, 1000);
423 QScroller::grabGesture(target: sw.data(), gestureType: QScroller::TouchGesture);
424 sw->setGeometry(ax: 100, ay: 100, aw: 400, ah: 300);
425 sw->show();
426 QApplication::setActiveWindow(sw.data());
427 if (!QTest::qWaitForWindowExposed(widget: sw.data()) || !QTest::qWaitForWindowActive(widget: sw.data()))
428 QSKIP("Failed to show and activate window");
429
430 QScroller *s1 = QScroller::scroller(target: sw.data());
431 QScrollerProperties sp1 = s1->scrollerProperties();
432
433 sp1.setScrollMetric(metric: QScrollerProperties::OvershootDragResistanceFactor, value: 0.5);
434 sp1.setScrollMetric(metric: QScrollerProperties::OvershootDragDistanceFactor, value: 0.2);
435 sp1.setScrollMetric(metric: QScrollerProperties::OvershootScrollDistanceFactor, value: 0.2);
436
437 // -- try to scroll with overshoot (when scrollable good case)
438
439 sp1.setScrollMetric(metric: QScrollerProperties::HorizontalOvershootPolicy, value: QVariant::fromValue(value: QScrollerProperties::OvershootWhenScrollable));
440 s1->setScrollerProperties(sp1);
441 kineticScrollNoTest(sw: sw.data(), from: QPointF(500, 500), touchStart: QPoint(0, 0), touchUpdate: QPoint(400, 0), touchEnd: QPoint(490, 0));
442
443 QTRY_COMPARE(s1->state(), QScroller::Inactive);
444
445 //qDebug() << "Overshoot fuzzy: "<<sw->currentPos;
446 QVERIFY(qFuzzyCompare(sw->currentPos.x(), 0));
447 QVERIFY(qFuzzyCompare(sw->currentPos.y(), 500));
448 QCOMPARE(sw->receivedOvershoot, true);
449
450 // -- try to scroll with overshoot (when scrollable bad case)
451 sw->reset();
452 sw->scrollArea = QRectF(0, 0, 0, 1000);
453
454 sp1.setScrollMetric(metric: QScrollerProperties::HorizontalOvershootPolicy, value: QVariant::fromValue(value: QScrollerProperties::OvershootWhenScrollable));
455 s1->setScrollerProperties(sp1);
456 kineticScrollNoTest(sw: sw.data(), from: QPointF(0, 500), touchStart: QPoint(0, 0), touchUpdate: QPoint(400, 0), touchEnd: QPoint(490, 0));
457
458 QTRY_COMPARE(s1->state(), QScroller::Inactive);
459
460 //qDebug() << "Overshoot fuzzy: "<<sw->currentPos;
461 QVERIFY(qFuzzyCompare(sw->currentPos.x(), 0));
462 QVERIFY(qFuzzyCompare(sw->currentPos.y(), 500));
463 QCOMPARE(sw->receivedOvershoot, false);
464
465 // -- try to scroll with overshoot (always on)
466 sw->reset();
467 sw->scrollArea = QRectF(0, 0, 0, 1000);
468
469 sp1.setScrollMetric(metric: QScrollerProperties::HorizontalOvershootPolicy, value: QVariant::fromValue(value: QScrollerProperties::OvershootAlwaysOn));
470 s1->setScrollerProperties(sp1);
471 kineticScrollNoTest(sw: sw.data(), from: QPointF(0, 500), touchStart: QPoint(0, 0), touchUpdate: QPoint(400, 0), touchEnd: QPoint(490, 0));
472
473 QTRY_COMPARE(s1->state(), QScroller::Inactive);
474
475 //qDebug() << "Overshoot fuzzy: "<<sw->currentPos;
476
477 QVERIFY(qFuzzyCompare(sw->currentPos.x(), 0));
478 QVERIFY(qFuzzyCompare(sw->currentPos.y(), 500));
479 QCOMPARE(sw->receivedOvershoot, true);
480
481 // -- try to scroll with overshoot (always off)
482 sw->reset();
483 sw->scrollArea = QRectF(0, 0, 1000, 1000);
484
485 sp1.setScrollMetric(metric: QScrollerProperties::HorizontalOvershootPolicy, value: QVariant::fromValue(value: QScrollerProperties::OvershootAlwaysOff));
486 s1->setScrollerProperties(sp1);
487 kineticScrollNoTest(sw: sw.data(), from: QPointF(500, 500), touchStart: QPoint(0, 0), touchUpdate: QPoint(400, 0), touchEnd: QPoint(490, 0));
488
489 QTRY_COMPARE(s1->state(), QScroller::Inactive);
490
491 QVERIFY(qFuzzyCompare(sw->currentPos.x(), 0));
492 QVERIFY(qFuzzyCompare(sw->currentPos.y(), 500));
493 QCOMPARE(sw->receivedOvershoot, false);
494
495 // -- try to scroll with overshoot (always on but max overshoot = 0)
496 sp1.setScrollMetric(metric: QScrollerProperties::OvershootDragDistanceFactor, value: 0.0);
497 sp1.setScrollMetric(metric: QScrollerProperties::OvershootScrollDistanceFactor, value: 0.0);
498 sw->reset();
499 sw->scrollArea = QRectF(0, 0, 1000, 1000);
500
501 sp1.setScrollMetric(metric: QScrollerProperties::HorizontalOvershootPolicy, value: QVariant::fromValue(value: QScrollerProperties::OvershootAlwaysOn));
502 s1->setScrollerProperties(sp1);
503 kineticScrollNoTest(sw: sw.data(), from: QPointF(500, 500), touchStart: QPoint(0, 0), touchUpdate: QPoint(400, 0), touchEnd: QPoint(490, 0));
504
505 QTRY_COMPARE(s1->state(), QScroller::Inactive);
506
507 QVERIFY(qFuzzyCompare(sw->currentPos.x(), 0));
508 QVERIFY(qFuzzyCompare(sw->currentPos.y(), 500));
509 QCOMPARE(sw->receivedOvershoot, false);
510#endif
511}
512
513void tst_QScroller::multipleWindows()
514{
515#if QT_CONFIG(gestures) && QT_CONFIG(scroller)
516 QScopedPointer<tst_QScrollerWidget> sw1(new tst_QScrollerWidget);
517 sw1->scrollArea = QRectF(0, 0, 1000, 1000);
518 QScroller::grabGesture(target: sw1.data(), gestureType: QScroller::TouchGesture);
519 sw1->setGeometry(ax: 100, ay: 100, aw: 400, ah: 300);
520
521 QScroller *s1 = QScroller::scroller(target: sw1.data());
522 kineticScroll(sw: sw1.data(), from: QPointF(500, 500), touchStart: QPoint(0, 0), touchUpdate: QPoint(100, 100), touchEnd: QPoint(200, 200));
523 // now we should be scrolling
524 QTRY_COMPARE(s1->state(), QScroller::Scrolling);
525
526 // That was fun! Do it again!
527 QScopedPointer<tst_QScrollerWidget> sw2(new tst_QScrollerWidget());
528 sw2->scrollArea = QRectF(0, 0, 1000, 1000);
529 QScroller::grabGesture(target: sw2.data(), gestureType: QScroller::TouchGesture);
530 sw2->setGeometry(ax: 100, ay: 100, aw: 400, ah: 300);
531
532 QScroller *s2 = QScroller::scroller(target: sw2.data());
533 kineticScroll(sw: sw2.data(), from: QPointF(500, 500), touchStart: QPoint(0, 0), touchUpdate: QPoint(100, 100), touchEnd: QPoint(200, 200));
534 // now we should be scrolling
535 QTRY_COMPARE(s2->state(), QScroller::Scrolling);
536
537 // wait for both to stop
538 QTRY_VERIFY(s1->state() != QScroller::Scrolling);
539 QTRY_VERIFY(s2->state() != QScroller::Scrolling);
540
541 sw1.reset(); // destroy one window
542 sw2->reset(); // reset the other scroller's internal state
543 // make sure we can still scroll the remaining one without crashing (QTBUG-71232)
544 kineticScroll(sw: sw2.data(), from: QPointF(500, 500), touchStart: QPoint(0, 0), touchUpdate: QPoint(100, 100), touchEnd: QPoint(200, 200));
545#endif
546}
547
548QTEST_MAIN(tst_QScroller)
549
550#include "tst_qscroller.moc"
551

source code of qtbase/tests/auto/widgets/util/qscroller/tst_qscroller.cpp