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 test suite 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
30#include <QtTest/QTest>
31#include <QtWidgets/QApplication>
32#include <QtWidgets/QWidget>
33#include <QtWidgets/QGestureEvent>
34#include <QtGui/QScreen>
35#include <QtGui/QTouchDevice>
36#include <QtCore/QVector>
37#include <QtCore/QString>
38#include <QtCore/QHash>
39#include <QtCore/QDebug>
40
41class tst_QGestureRecognizer : public QObject
42{
43 Q_OBJECT
44public:
45 tst_QGestureRecognizer();
46
47private Q_SLOTS:
48 void initTestCase();
49#ifndef QT_NO_GESTURES
50 void panGesture_data();
51 void panGesture();
52 void pinchGesture_data();
53 void pinchGesture();
54 void swipeGesture_data();
55 void swipeGesture();
56#endif // !QT_NO_GESTURES
57
58private:
59 const int m_fingerDistance;
60 QTouchDevice *m_touchDevice;
61};
62
63tst_QGestureRecognizer::tst_QGestureRecognizer()
64 : m_fingerDistance(qRound(d: QGuiApplication::primaryScreen()->physicalDotsPerInch() / 2.0))
65 , m_touchDevice(QTest::createTouchDevice())
66{
67 qputenv(varName: "QT_PAN_TOUCHPOINTS", value: "2"); // Prevent device detection of pan touch point count.
68}
69
70void tst_QGestureRecognizer::initTestCase()
71{
72}
73
74#ifndef QT_NO_GESTURES
75
76typedef QVector<Qt::GestureType> GestureTypeVector;
77
78class TestWidget : public QWidget
79{
80public:
81 explicit TestWidget(const GestureTypeVector &gestureTypes);
82
83 bool gestureReceived(Qt::GestureType gestureType) const
84 { return m_receivedGestures.value(akey: gestureType); }
85
86protected:
87 bool event(QEvent * event) override;
88
89private:
90 typedef QHash<Qt::GestureType, bool> GestureTypeHash;
91 GestureTypeHash m_receivedGestures;
92};
93
94TestWidget::TestWidget(const GestureTypeVector &gestureTypes)
95{
96 setAttribute(Qt::WA_AcceptTouchEvents);
97
98 foreach (Qt::GestureType gestureType, gestureTypes) {
99 grabGesture(type: gestureType);
100 m_receivedGestures.insert(akey: gestureType, avalue: false);
101 }
102
103 const QRect geometry = QGuiApplication::primaryScreen()->availableGeometry();
104 const QSize size = geometry.size() / 2;
105 resize(size);
106 move(geometry.center() - QPoint(size.width() / 2, size.height() / 2));
107}
108
109bool TestWidget::event(QEvent * event)
110{
111 switch (event->type()) {
112 case QEvent::Gesture: {
113 const QGestureEvent *gestureEvent = static_cast<QGestureEvent *>(event);
114 const GestureTypeHash::iterator hend = m_receivedGestures.end();
115 for (GestureTypeHash::iterator it = m_receivedGestures.begin(); it != hend; ++it) {
116 if (const QGesture *gesture = gestureEvent->gesture(type: it.key())) {
117 if (gesture->state() == Qt::GestureFinished)
118 it.value() = true;
119 }
120 }
121 }
122 break;
123 default:
124 break;
125 }
126 return QWidget::event(event);
127}
128
129static void pressSequence(QTest::QTouchEventSequence &sequence,
130 QVector<QPoint> &points,
131 QWidget *widget)
132{
133 const int pointCount = points.size();
134 for (int p = 0; p < pointCount; ++p)
135 sequence.press(touchId: p, pt: points.at(i: p), widget);
136 sequence.commit();
137}
138
139static void linearSequence(int n, const QPoint &delta,
140 QTest::QTouchEventSequence &sequence,
141 QVector<QPoint> &points,
142 QWidget *widget)
143{
144 const int pointCount = points.size();
145 for (int s = 0; s < n; ++s) {
146 for (int p = 0; p < pointCount; ++p) {
147 points[p] += delta;
148 sequence.move(touchId: p, pt: points[p], widget);
149 }
150 sequence.commit();
151 }
152}
153
154static void releaseSequence(QTest::QTouchEventSequence &sequence,
155 QVector<QPoint> &points,
156 QWidget *widget)
157{
158 const int pointCount = points.size();
159 for (int p = 0; p < pointCount; ++p)
160 sequence.release(touchId: p, pt: points[p], widget);
161 sequence.commit();
162}
163
164// --- Pan
165
166enum PanSubTest {
167 TwoFingerPanSubTest
168};
169
170void tst_QGestureRecognizer::panGesture_data()
171{
172 QTest::addColumn<int>(name: "panSubTest");
173 QTest::addColumn<bool>(name: "gestureExpected");
174 QTest::newRow(dataTag: "Two finger") << int(TwoFingerPanSubTest) << true;
175}
176
177void tst_QGestureRecognizer::panGesture()
178{
179 QFETCH(int, panSubTest);
180 QFETCH(bool, gestureExpected);
181
182 Q_UNUSED(panSubTest) // Single finger pan will be added later.
183
184 const int panPoints = 2;
185 const Qt::GestureType gestureType = Qt::PanGesture;
186 TestWidget widget(GestureTypeVector(1, gestureType));
187 widget.setWindowTitle(QTest::currentTestFunction());
188 widget.show();
189 QVERIFY(QTest::qWaitForWindowExposed(&widget));
190
191 QVector<QPoint> points;
192 for (int i = 0; i < panPoints; ++i)
193 points.append(t: QPoint(10 + i *20, 10 + i *20));
194
195 QTest::QTouchEventSequence panSequence = QTest::touchEvent(widget: &widget, device: m_touchDevice);
196 pressSequence(sequence&: panSequence, points, widget: &widget);
197 linearSequence(n: 5, delta: QPoint(20, 20), sequence&: panSequence, points, widget: &widget);
198 releaseSequence(sequence&: panSequence, points, widget: &widget);
199
200 if (gestureExpected) {
201 QTRY_VERIFY(widget.gestureReceived(gestureType));
202 } else {
203 QCoreApplication::processEvents();
204 QVERIFY(!widget.gestureReceived(gestureType));
205 }
206}
207
208// --- Pinch
209
210enum PinchSubTest {
211 StandardPinchSubTest
212};
213
214void tst_QGestureRecognizer::pinchGesture_data()
215{
216 QTest::addColumn<int>(name: "pinchSubTest");
217 QTest::addColumn<bool>(name: "gestureExpected");
218 QTest::newRow(dataTag: "Standard") << int(StandardPinchSubTest) << true;
219}
220
221void tst_QGestureRecognizer::pinchGesture()
222{
223 QFETCH(int, pinchSubTest);
224 QFETCH(bool, gestureExpected);
225
226 Q_UNUSED(pinchSubTest)
227
228 const Qt::GestureType gestureType = Qt::PinchGesture;
229 TestWidget widget(GestureTypeVector(1, gestureType));
230 widget.setWindowTitle(QTest::currentTestFunction());
231 widget.show();
232 QVERIFY(QTest::qWaitForWindowExposed(&widget));
233
234 QVector<QPoint> points;
235 points.append(t: widget.rect().center());
236 points.append(t: points.front() + QPoint(0, 20));
237
238 QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(widget: &widget, device: m_touchDevice);
239 pressSequence(sequence&: pinchSequence, points, widget: &widget);
240
241 for (int s = 0; s < 5; ++s) {
242 points[0] += QPoint(5, 30);
243 pinchSequence.move(touchId: 0, pt: points[0], widget: &widget);
244 points[1] += QPoint(5, -30);
245 pinchSequence.move(touchId: 1, pt: points[1], widget: &widget);
246 pinchSequence.commit();
247 }
248
249 releaseSequence(sequence&: pinchSequence, points, widget: &widget);
250
251 if (gestureExpected) {
252 QTRY_VERIFY(widget.gestureReceived(gestureType));
253 } else {
254 QCoreApplication::processEvents();
255 QVERIFY(!widget.gestureReceived(gestureType));
256 }
257}
258
259// --- Swipe
260
261enum SwipeSubTest {
262 SwipeLineSubTest,
263 SwipeDirectionChangeSubTest,
264 SwipeSmallDirectionChangeSubTest
265};
266
267void tst_QGestureRecognizer::swipeGesture_data()
268{
269 QTest::addColumn<int>(name: "swipeSubTest");
270 QTest::addColumn<bool>(name: "gestureExpected");
271 QTest::newRow(dataTag: "Line") << int(SwipeLineSubTest) << true;
272 QTest::newRow(dataTag: "DirectionChange") << int(SwipeDirectionChangeSubTest) << false;
273 QTest::newRow(dataTag: "SmallDirectionChange") << int(SwipeSmallDirectionChangeSubTest) << true;
274}
275
276void tst_QGestureRecognizer::swipeGesture()
277{
278 enum { swipePoints = 3 };
279
280 QFETCH(int, swipeSubTest);
281 QFETCH(bool, gestureExpected);
282
283 const Qt::GestureType gestureType = Qt::SwipeGesture;
284 TestWidget widget(GestureTypeVector(1, gestureType));
285 widget.setWindowTitle(QTest::currentTestFunction());
286 widget.show();
287 QVERIFY(QTest::qWaitForWindowExposed(&widget));
288
289 // Start a swipe sequence with 2 points (QTBUG-15768)
290 const QPoint fingerDistance(m_fingerDistance, m_fingerDistance);
291 QVector<QPoint> points;
292 for (int i = 0; i < swipePoints - 1; ++i)
293 points.append(t: fingerDistance + i * fingerDistance);
294
295 QTest::QTouchEventSequence swipeSequence = QTest::touchEvent(widget: &widget, device: m_touchDevice);
296 pressSequence(sequence&: swipeSequence, points, widget: &widget);
297
298 // Press point #3
299 points.append(t: points.last() + fingerDistance);
300 swipeSequence.press(touchId: points.size() - 1, pt: points.last(), widget: &widget);
301 swipeSequence.commit();
302 Q_ASSERT(points.size() == swipePoints);
303
304 // Move.
305 const QPoint moveDelta(60, 20);
306 switch (swipeSubTest) {
307 case SwipeLineSubTest:
308 linearSequence(n: 5, delta: moveDelta, sequence&: swipeSequence, points, widget: &widget);
309 break;
310 case SwipeDirectionChangeSubTest:
311 linearSequence(n: 5, delta: moveDelta, sequence&: swipeSequence, points, widget: &widget);
312 linearSequence(n: 3, delta: QPoint(-moveDelta.x(), moveDelta.y()), sequence&: swipeSequence, points, widget: &widget);
313 break;
314 case SwipeSmallDirectionChangeSubTest: { // QTBUG-46195, small changes in direction should not cause the gesture to be canceled.
315 const QPoint smallChangeMoveDelta(50, 1);
316 linearSequence(n: 5, delta: smallChangeMoveDelta, sequence&: swipeSequence, points, widget: &widget);
317 linearSequence(n: 1, delta: QPoint(smallChangeMoveDelta.x(), -3), sequence&: swipeSequence, points, widget: &widget);
318 linearSequence(n: 5, delta: smallChangeMoveDelta, sequence&: swipeSequence, points, widget: &widget);
319 }
320 break;
321 }
322
323 releaseSequence(sequence&: swipeSequence, points, widget: &widget);
324
325 if (gestureExpected) {
326 QTRY_VERIFY(widget.gestureReceived(gestureType));
327 } else {
328 QCoreApplication::processEvents();
329 QVERIFY(!widget.gestureReceived(gestureType));
330 }
331}
332
333#endif // !QT_NO_GESTURES
334
335QTEST_MAIN(tst_QGestureRecognizer)
336
337#include "tst_qgesturerecognizer.moc"
338

source code of qtbase/tests/auto/widgets/kernel/qgesturerecognizer/tst_qgesturerecognizer.cpp