1 | // Copyright (C) 2021 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 |
3 | |
4 | #include "viewtestutils_p.h" |
5 | |
6 | #include <QtCore/QRandomGenerator> |
7 | #include <QtCore/QTimer> |
8 | #include <QtQuick/QQuickView> |
9 | #include <QtQuick/QQuickView> |
10 | #include <QtGui/QScreen> |
11 | #include <QtGui/qpa/qwindowsysteminterface.h> |
12 | |
13 | #include <QtTest/QTest> |
14 | |
15 | #include <QtQuick/private/qquickdeliveryagent_p_p.h> |
16 | #if QT_CONFIG(quick_itemview) |
17 | #include <QtQuick/private/qquickitemview_p_p.h> |
18 | #endif |
19 | #include <QtQuick/private/qquickwindow_p.h> |
20 | |
21 | #include <QtQuickTestUtils/private/visualtestutils_p.h> |
22 | |
23 | QT_BEGIN_NAMESPACE |
24 | |
25 | QQuickView *QQuickViewTestUtils::createView() |
26 | { |
27 | QQuickView *window = new QQuickView(0); |
28 | const QSize size(240, 320); |
29 | window->resize(newSize: size); |
30 | QQuickViewTestUtils::centerOnScreen(window, size); |
31 | return window; |
32 | } |
33 | |
34 | void QQuickViewTestUtils::centerOnScreen(QQuickView *window, const QSize &size) |
35 | { |
36 | const QRect screenGeometry = window->screen()->availableGeometry(); |
37 | const QPoint offset = QPoint(size.width() / 2, size.height() / 2); |
38 | window->setFramePosition(screenGeometry.center() - offset); |
39 | } |
40 | |
41 | void QQuickViewTestUtils::centerOnScreen(QQuickView *window) |
42 | { |
43 | QQuickViewTestUtils::centerOnScreen(window, size: window->size()); |
44 | } |
45 | |
46 | void QQuickViewTestUtils::moveMouseAway(QQuickView *window) |
47 | { |
48 | #if QT_CONFIG(cursor) // Get the cursor out of the way. |
49 | QCursor::setPos(window->geometry().topRight() + QPoint(100, 100)); |
50 | #else |
51 | Q_UNUSED(window); |
52 | #endif |
53 | } |
54 | |
55 | void QQuickViewTestUtils::moveAndRelease(QQuickView *window, const QPoint &position) |
56 | { |
57 | QTest::mouseMove(window, pos: position); |
58 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: {}, pos: position); |
59 | } |
60 | |
61 | void QQuickViewTestUtils::moveAndPress(QQuickView *window, const QPoint &position) |
62 | { |
63 | QTest::mouseMove(window, pos: position); |
64 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: {}, pos: position); |
65 | } |
66 | |
67 | void QQuickViewTestUtils::flick(QQuickView *window, const QPoint &from, const QPoint &to, int duration) |
68 | { |
69 | const int pointCount = 5; |
70 | QPoint diff = to - from; |
71 | |
72 | // send press, five equally spaced moves, and release. |
73 | moveAndPress(window, position: from); |
74 | |
75 | for (int i = 0; i < pointCount; ++i) |
76 | QTest::mouseMove(window, pos: from + (i+1)*diff/pointCount, delay: duration / pointCount); |
77 | |
78 | moveAndRelease(window, position: to); |
79 | QTest::qWait(ms: 50); |
80 | } |
81 | |
82 | QList<int> QQuickViewTestUtils::adjustIndexesForAddDisplaced(const QList<int> &indexes, int index, int count) |
83 | { |
84 | QList<int> result; |
85 | for (int i=0; i<indexes.size(); i++) { |
86 | int num = indexes[i]; |
87 | if (num >= index) { |
88 | num += count; |
89 | } |
90 | result << num; |
91 | } |
92 | return result; |
93 | } |
94 | |
95 | QList<int> QQuickViewTestUtils::adjustIndexesForMove(const QList<int> &indexes, int from, int to, int count) |
96 | { |
97 | QList<int> result; |
98 | for (int i=0; i<indexes.size(); i++) { |
99 | int num = indexes[i]; |
100 | if (from < to) { |
101 | if (num >= from && num < from + count) |
102 | num += (to - from); // target |
103 | else if (num >= from && num < to + count) |
104 | num -= count; // displaced |
105 | } else if (from > to) { |
106 | if (num >= from && num < from + count) |
107 | num -= (from - to); // target |
108 | else if (num >= to && num < from + count) |
109 | num += count; // displaced |
110 | } |
111 | result << num; |
112 | } |
113 | return result; |
114 | } |
115 | |
116 | QList<int> QQuickViewTestUtils::adjustIndexesForRemoveDisplaced(const QList<int> &indexes, int index, int count) |
117 | { |
118 | QList<int> result; |
119 | for (int i=0; i<indexes.size(); i++) { |
120 | int num = indexes[i]; |
121 | if (num >= index) |
122 | num -= count; |
123 | result << num; |
124 | } |
125 | return result; |
126 | } |
127 | |
128 | QQuickViewTestUtils::QaimModel::QaimModel(QObject *parent) |
129 | : QAbstractListModel(parent) |
130 | { |
131 | } |
132 | |
133 | int QQuickViewTestUtils::QaimModel::rowCount(const QModelIndex &parent) const |
134 | { |
135 | Q_UNUSED(parent); |
136 | return list.size(); |
137 | } |
138 | |
139 | int QQuickViewTestUtils::QaimModel::columnCount(const QModelIndex &parent) const |
140 | { |
141 | Q_UNUSED(parent); |
142 | return columns; |
143 | } |
144 | |
145 | QHash<int,QByteArray> QQuickViewTestUtils::QaimModel::roleNames() const |
146 | { |
147 | QHash<int,QByteArray> roles = QAbstractListModel::roleNames(); |
148 | roles.insert(key: Name, value: "name" ); |
149 | roles.insert(key: Number, value: "number" ); |
150 | return roles; |
151 | } |
152 | |
153 | QVariant QQuickViewTestUtils::QaimModel::data(const QModelIndex &index, int role) const |
154 | { |
155 | QVariant rv; |
156 | if (role == Name) |
157 | rv = list.at(i: index.row()).first; |
158 | else if (role == Number) |
159 | rv = list.at(i: index.row()).second; |
160 | |
161 | return rv; |
162 | } |
163 | |
164 | int QQuickViewTestUtils::QaimModel::count() const |
165 | { |
166 | return rowCount() * columnCount(); |
167 | } |
168 | |
169 | QString QQuickViewTestUtils::QaimModel::name(int index) const |
170 | { |
171 | return list.at(i: index).first; |
172 | } |
173 | |
174 | QString QQuickViewTestUtils::QaimModel::number(int index) const |
175 | { |
176 | return list.at(i: index).second; |
177 | } |
178 | |
179 | void QQuickViewTestUtils::QaimModel::addItem(const QString &name, const QString &number) |
180 | { |
181 | emit beginInsertRows(parent: QModelIndex(), first: list.size(), last: list.size()); |
182 | list.append(t: QPair<QString,QString>(name, number)); |
183 | emit endInsertRows(); |
184 | } |
185 | |
186 | void QQuickViewTestUtils::QaimModel::addItems(const QList<QPair<QString, QString> > &items) |
187 | { |
188 | emit beginInsertRows(parent: QModelIndex(), first: list.size(), last: list.size()+items.size()-1); |
189 | for (int i=0; i<items.size(); i++) |
190 | list.append(t: QPair<QString,QString>(items[i].first, items[i].second)); |
191 | emit endInsertRows(); |
192 | } |
193 | |
194 | void QQuickViewTestUtils::QaimModel::insertItem(int index, const QString &name, const QString &number) |
195 | { |
196 | emit beginInsertRows(parent: QModelIndex(), first: index, last: index); |
197 | list.insert(i: index, t: QPair<QString,QString>(name, number)); |
198 | emit endInsertRows(); |
199 | } |
200 | |
201 | void QQuickViewTestUtils::QaimModel::insertItems(int index, const QList<QPair<QString, QString> > &items) |
202 | { |
203 | emit beginInsertRows(parent: QModelIndex(), first: index, last: index+items.size()-1); |
204 | for (int i=0; i<items.size(); i++) |
205 | list.insert(i: index + i, t: QPair<QString,QString>(items[i].first, items[i].second)); |
206 | emit endInsertRows(); |
207 | } |
208 | |
209 | void QQuickViewTestUtils::QaimModel::removeItem(int index) |
210 | { |
211 | emit beginRemoveRows(parent: QModelIndex(), first: index, last: index); |
212 | list.removeAt(i: index); |
213 | emit endRemoveRows(); |
214 | } |
215 | |
216 | void QQuickViewTestUtils::QaimModel::removeItems(int index, int count) |
217 | { |
218 | emit beginRemoveRows(parent: QModelIndex(), first: index, last: index+count-1); |
219 | while (count--) |
220 | list.removeAt(i: index); |
221 | emit endRemoveRows(); |
222 | } |
223 | |
224 | void QQuickViewTestUtils::QaimModel::moveItem(int from, int to) |
225 | { |
226 | emit beginMoveRows(sourceParent: QModelIndex(), sourceFirst: from, sourceLast: from, destinationParent: QModelIndex(), destinationRow: to); |
227 | list.move(from, to); |
228 | emit endMoveRows(); |
229 | } |
230 | |
231 | void QQuickViewTestUtils::QaimModel::moveItems(int from, int to, int count) |
232 | { |
233 | emit beginMoveRows(sourceParent: QModelIndex(), sourceFirst: from, sourceLast: from+count-1, destinationParent: QModelIndex(), destinationRow: to > from ? to+count : to); |
234 | qquickmodelviewstestutil_move(from, to, n: count, items: &list); |
235 | emit endMoveRows(); |
236 | } |
237 | |
238 | void QQuickViewTestUtils::QaimModel::modifyItem(int idx, const QString &name, const QString &number) |
239 | { |
240 | list[idx] = QPair<QString,QString>(name, number); |
241 | emit dataChanged(topLeft: index(row: idx,column: 0), bottomRight: index(row: idx,column: 0)); |
242 | } |
243 | |
244 | void QQuickViewTestUtils::QaimModel::clear() |
245 | { |
246 | int count = list.size(); |
247 | if (count > 0) { |
248 | beginRemoveRows(parent: QModelIndex(), first: 0, last: count-1); |
249 | list.clear(); |
250 | endRemoveRows(); |
251 | } |
252 | } |
253 | |
254 | void QQuickViewTestUtils::QaimModel::reset() |
255 | { |
256 | emit beginResetModel(); |
257 | emit endResetModel(); |
258 | } |
259 | |
260 | void QQuickViewTestUtils::QaimModel::resetItems(const QList<QPair<QString, QString> > &items) |
261 | { |
262 | beginResetModel(); |
263 | list = items; |
264 | endResetModel(); |
265 | } |
266 | |
267 | class ScopedPrintable |
268 | { |
269 | Q_DISABLE_COPY_MOVE(ScopedPrintable) |
270 | |
271 | public: |
272 | ScopedPrintable(const QString &string) : data(QTest::toString(str: string)) {} |
273 | ~ScopedPrintable() { delete[] data; } |
274 | |
275 | operator const char*() const { return data; } |
276 | |
277 | private: |
278 | const char *data; |
279 | }; |
280 | |
281 | void QQuickViewTestUtils::QaimModel::matchAgainst(const QList<QPair<QString, QString> > &other, const QString &error1, const QString &error2) { |
282 | for (int i=0; i<other.size(); i++) { |
283 | QVERIFY2(list.contains(other[i]), |
284 | ScopedPrintable(other[i].first + QLatin1Char(' ') + other[i].second + QLatin1Char(' ') + error1)); |
285 | } |
286 | for (int i=0; i<list.size(); i++) { |
287 | QVERIFY2(other.contains(list[i]), |
288 | ScopedPrintable(list[i].first + QLatin1Char(' ') + list[i].second + QLatin1Char(' ') + error2)); |
289 | } |
290 | } |
291 | |
292 | |
293 | |
294 | QQuickViewTestUtils::ListRange::ListRange() |
295 | : valid(false) |
296 | { |
297 | } |
298 | |
299 | QQuickViewTestUtils::ListRange::ListRange(const ListRange &other) |
300 | : valid(other.valid) |
301 | { |
302 | indexes = other.indexes; |
303 | } |
304 | |
305 | QQuickViewTestUtils::ListRange::ListRange(int start, int end) |
306 | : valid(true) |
307 | { |
308 | for (int i=start; i<=end; i++) |
309 | indexes << i; |
310 | } |
311 | |
312 | QQuickViewTestUtils::ListRange::~ListRange() |
313 | { |
314 | } |
315 | |
316 | QQuickViewTestUtils::ListRange QQuickViewTestUtils::ListRange::operator+(const ListRange &other) const |
317 | { |
318 | if (other == *this) |
319 | return *this; |
320 | ListRange a(*this); |
321 | a.indexes.append(l: other.indexes); |
322 | return a; |
323 | } |
324 | |
325 | bool QQuickViewTestUtils::ListRange::operator==(const ListRange &other) const |
326 | { |
327 | return QSet<int>(indexes.cbegin(), indexes.cend()) |
328 | == QSet<int>(other.indexes.cbegin(), other.indexes.cend()); |
329 | } |
330 | |
331 | bool QQuickViewTestUtils::ListRange::operator!=(const ListRange &other) const |
332 | { |
333 | return !(*this == other); |
334 | } |
335 | |
336 | bool QQuickViewTestUtils::ListRange::isValid() const |
337 | { |
338 | return valid; |
339 | } |
340 | |
341 | int QQuickViewTestUtils::ListRange::count() const |
342 | { |
343 | return indexes.size(); |
344 | } |
345 | |
346 | QList<QPair<QString,QString> > QQuickViewTestUtils::ListRange::getModelDataValues(const QaimModel &model) |
347 | { |
348 | QList<QPair<QString,QString> > data; |
349 | if (!valid) |
350 | return data; |
351 | for (int i=0; i<indexes.size(); i++) |
352 | data.append(t: qMakePair(value1: model.name(index: indexes[i]), value2: model.number(index: indexes[i]))); |
353 | return data; |
354 | } |
355 | |
356 | QQuickViewTestUtils::StressTestModel::StressTestModel() |
357 | : QAbstractListModel() |
358 | , m_rowCount(20) |
359 | { |
360 | QTimer *t = new QTimer(this); |
361 | t->setInterval(500); |
362 | t->start(); |
363 | |
364 | connect(sender: t, signal: &QTimer::timeout, context: this, slot: &StressTestModel::updateModel); |
365 | } |
366 | |
367 | int QQuickViewTestUtils::StressTestModel::rowCount(const QModelIndex &) const |
368 | { |
369 | return m_rowCount; |
370 | } |
371 | |
372 | QVariant QQuickViewTestUtils::StressTestModel::data(const QModelIndex &, int) const |
373 | { |
374 | return QVariant(); |
375 | } |
376 | |
377 | void QQuickViewTestUtils::StressTestModel::updateModel() |
378 | { |
379 | if (m_rowCount > 10) { |
380 | for (int i = 0; i < 10; ++i) { |
381 | int rnum = QRandomGenerator::global()->bounded(highest: m_rowCount); |
382 | beginRemoveRows(parent: QModelIndex(), first: rnum, last: rnum); |
383 | m_rowCount--; |
384 | endRemoveRows(); |
385 | } |
386 | } |
387 | if (m_rowCount < 20) { |
388 | for (int i = 0; i < 10; ++i) { |
389 | int rnum = QRandomGenerator::global()->bounded(highest: m_rowCount); |
390 | beginInsertRows(parent: QModelIndex(), first: rnum, last: rnum); |
391 | m_rowCount++; |
392 | endInsertRows(); |
393 | } |
394 | } |
395 | } |
396 | |
397 | #if QT_CONFIG(quick_itemview) |
398 | bool QQuickViewTestUtils::testVisibleItems(const QQuickItemViewPrivate *priv, bool *nonUnique, FxViewItem **failItem, int *expectedIdx) |
399 | { |
400 | QHash<QQuickItem*, int> uniqueItems; |
401 | |
402 | int skip = 0; |
403 | for (int i = 0; i < priv->visibleItems.size(); ++i) { |
404 | FxViewItem *item = priv->visibleItems.at(i); |
405 | if (!item) { |
406 | *failItem = nullptr; |
407 | return false; |
408 | } |
409 | #if 0 |
410 | qDebug() << "\t" << item->index |
411 | << item->item |
412 | << item->position() |
413 | << (!item->item || QQuickItemPrivate::get(item->item)->culled ? "hidden" : "visible" ); |
414 | #endif |
415 | if (item->index == -1) { |
416 | ++skip; |
417 | } else if (item->index != priv->visibleIndex + i - skip) { |
418 | *nonUnique = false; |
419 | *failItem = item; |
420 | *expectedIdx = priv->visibleIndex + i - skip; |
421 | return false; |
422 | } else if (uniqueItems.contains(key: item->item)) { |
423 | *nonUnique = true; |
424 | *failItem = item; |
425 | *expectedIdx = uniqueItems.find(key: item->item).value(); |
426 | return false; |
427 | } |
428 | |
429 | uniqueItems.insert(key: item->item, value: item->index); |
430 | } |
431 | |
432 | return true; |
433 | } |
434 | #endif |
435 | |
436 | namespace QQuickTouchUtils { |
437 | |
438 | /* QQuickWindow does event compression and only delivers events just |
439 | * before it is about to render the next frame. Since some tests |
440 | * rely on events being delivered immediately AND that no other |
441 | * event processing has occurred in the meanwhile, we flush the |
442 | * event manually and immediately. |
443 | */ |
444 | void flush(QQuickWindow *window) { |
445 | if (!window) |
446 | return; |
447 | QQuickDeliveryAgentPrivate *da = QQuickWindowPrivate::get(c: window)->deliveryAgentPrivate(); |
448 | if (!da || !da->delayedTouch) |
449 | return; |
450 | da->deliverDelayedTouchEvent(); |
451 | } |
452 | |
453 | } |
454 | |
455 | namespace QTest { |
456 | int Q_TESTLIB_EXPORT defaultMouseDelay(); |
457 | } |
458 | |
459 | namespace QQuickTest { |
460 | |
461 | /*! \internal |
462 | Initialize \a view, set \a url, center in available geometry, move mouse away if desired. |
463 | If \a errorMessage is given, QQuickView::errors() will be concatenated into it; |
464 | otherwise, the QWARN messages are generally enough to debug the test. |
465 | |
466 | Returns \c false if the view fails to load the QML. That should be fatal in most tests, |
467 | so normally the return value should be checked with QVERIFY. |
468 | */ |
469 | bool initView(QQuickView &view, const QUrl &url, bool moveMouseOut, QByteArray *errorMessage) |
470 | { |
471 | view.setSource(url); |
472 | while (view.status() == QQuickView::Loading) |
473 | QTest::qWait(ms: 10); |
474 | if (view.status() != QQuickView::Ready) { |
475 | if (errorMessage) { |
476 | for (const QQmlError &e : view.errors()) |
477 | errorMessage->append(a: e.toString().toLocal8Bit() + '\n'); |
478 | } |
479 | return false; |
480 | } |
481 | const QRect screenGeometry = view.screen()->availableGeometry(); |
482 | const QSize size = view.size(); |
483 | if (view.width() == 0) |
484 | view.setWidth(100); |
485 | if (view.height() == 0) |
486 | view.setHeight(100); |
487 | const QPoint offset = QPoint(size.width() / 2, size.height() / 2); |
488 | view.setFramePosition(screenGeometry.center() - offset); |
489 | #if QT_CONFIG(cursor) // Get the cursor out of the way. |
490 | if (moveMouseOut) |
491 | QCursor::setPos(view.geometry().topRight() + QPoint(100, 100)); |
492 | #else |
493 | Q_UNUSED(moveMouseOut); |
494 | #endif |
495 | return true; |
496 | } |
497 | |
498 | /*! \internal |
499 | Initialize \a view, set \a url, center in available geometry, move mouse away, |
500 | show the \a view, wait for it to be exposed, and verify that its rootObject is not null. |
501 | |
502 | Returns \c false if anything fails, which should be fatal in most tests. |
503 | The usual way to call this function is |
504 | \code |
505 | QQuickView window; |
506 | QVERIFY(QQuickTest::showView(window, testFileUrl("myitems.qml"))); |
507 | \endcode |
508 | */ |
509 | bool showView(QQuickView &view, const QUrl &url) |
510 | { |
511 | if (!initView(view, url)) |
512 | return false; |
513 | view.show(); |
514 | if (!QTest::qWaitForWindowExposed(window: &view)) |
515 | return false; |
516 | if (!view.rootObject()) |
517 | return false; |
518 | return true; |
519 | } |
520 | |
521 | // TODO maybe move the generic pointerPress/Move/Release functions to QTestLib later on |
522 | |
523 | static Qt::MouseButton pressedTabletButton = Qt::NoButton; |
524 | static Qt::KeyboardModifiers pressedTabletModifiers = Qt::NoModifier; |
525 | |
526 | void pointerPress(const QPointingDevice *dev, QQuickWindow *window, int pointId, const QPoint &p, |
527 | Qt::MouseButton button, Qt::KeyboardModifiers modifiers) |
528 | { |
529 | switch (dev->type()) { |
530 | case QPointingDevice::DeviceType::Mouse: |
531 | case QPointingDevice::DeviceType::TouchPad: |
532 | QTest::mousePress(window, button, stateKey: modifiers, pos: p); |
533 | break; |
534 | case QPointingDevice::DeviceType::TouchScreen: |
535 | QTest::touchEvent(window, device: const_cast<QPointingDevice *>(dev)).press(touchId: pointId, pt: p, window); |
536 | QQuickTouchUtils::flush(window); |
537 | break; |
538 | case QPointingDevice::DeviceType::Puck: |
539 | case QPointingDevice::DeviceType::Stylus: |
540 | case QPointingDevice::DeviceType::Airbrush: |
541 | QTest::lastMouseTimestamp += QTest::defaultMouseDelay(); |
542 | pressedTabletButton = button; |
543 | pressedTabletModifiers = modifiers; |
544 | QWindowSystemInterface::handleTabletEvent(window, timestamp: QTest::lastMouseTimestamp, device: dev, local: p, global: window->mapToGlobal(pos: p), |
545 | buttons: button, pressure: 0.8, xTilt: 0, yTilt: 0, tangentialPressure: 0, rotation: 0, z: 0, modifiers); |
546 | break; |
547 | default: |
548 | qWarning() << "can't send a press event from" << dev; |
549 | break; |
550 | } |
551 | } |
552 | |
553 | void pointerMove(const QPointingDevice *dev, QQuickWindow *window, int pointId, const QPoint &p) |
554 | { |
555 | switch (dev->type()) { |
556 | case QPointingDevice::DeviceType::Mouse: |
557 | case QPointingDevice::DeviceType::TouchPad: |
558 | QTest::mouseMove(window, pos: p); |
559 | break; |
560 | case QPointingDevice::DeviceType::TouchScreen: |
561 | QTest::touchEvent(window, device: const_cast<QPointingDevice *>(dev)).move(touchId: pointId, pt: p, window); |
562 | QQuickTouchUtils::flush(window); |
563 | break; |
564 | case QPointingDevice::DeviceType::Puck: |
565 | case QPointingDevice::DeviceType::Stylus: |
566 | case QPointingDevice::DeviceType::Airbrush: |
567 | QTest::lastMouseTimestamp += QTest::defaultMouseDelay(); |
568 | QWindowSystemInterface::handleTabletEvent(window, timestamp: QTest::lastMouseTimestamp, device: dev, local: p, global: window->mapToGlobal(pos: p), |
569 | buttons: pressedTabletButton, pressure: 0, xTilt: 0, yTilt: 0, tangentialPressure: 0, rotation: 0, z: 0, modifiers: pressedTabletModifiers); |
570 | break; |
571 | default: |
572 | qWarning() << "can't send a move event from" << dev; |
573 | break; |
574 | } |
575 | } |
576 | |
577 | void pointerRelease(const QPointingDevice *dev, QQuickWindow *window, int pointId, const QPoint &p, |
578 | Qt::MouseButton button, Qt::KeyboardModifiers modifiers) |
579 | { |
580 | switch (dev->type()) { |
581 | case QPointingDevice::DeviceType::Mouse: |
582 | case QPointingDevice::DeviceType::TouchPad: |
583 | QTest::mouseRelease(window, button, stateKey: modifiers, pos: p); |
584 | break; |
585 | case QPointingDevice::DeviceType::TouchScreen: |
586 | QTest::touchEvent(window, device: const_cast<QPointingDevice *>(dev)).release(touchId: pointId, pt: p, window); |
587 | QQuickTouchUtils::flush(window); |
588 | break; |
589 | case QPointingDevice::DeviceType::Puck: |
590 | case QPointingDevice::DeviceType::Stylus: |
591 | case QPointingDevice::DeviceType::Airbrush: |
592 | QTest::lastMouseTimestamp += QTest::defaultMouseDelay(); |
593 | QWindowSystemInterface::handleTabletEvent(window, timestamp: QTest::lastMouseTimestamp, device: dev, local: p, global: window->mapToGlobal(pos: p), |
594 | buttons: Qt::NoButton, pressure: 0, xTilt: 0, yTilt: 0, tangentialPressure: 0, rotation: 0, z: 0, modifiers); |
595 | break; |
596 | default: |
597 | qWarning() << "can't send a press event from" << dev; |
598 | break; |
599 | } |
600 | } |
601 | |
602 | } |
603 | |
604 | QT_END_NAMESPACE |
605 | |
606 | #include "moc_viewtestutils_p.cpp" |
607 | |