Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
quicktestevent.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6#include <QtTest/qtestkeyboard.h>
7#include <QtQml/qqml.h>
8#include <QtQuick/qquickitem.h>
9#include <QtQuick/qquickwindow.h>
10#include <qpa/qwindowsysteminterface.h>
11
13
14namespace QTest {
15 extern int Q_TESTLIB_EXPORT defaultMouseDelay();
16}
17
18QuickTestEvent::QuickTestEvent(QObject *parent)
19 : QObject(parent)
20{
21}
22
23QuickTestEvent::~QuickTestEvent()
24{
25}
26
27int QuickTestEvent::defaultMouseDelay() const
28{
29 return QTest::defaultMouseDelay();
30}
31
32bool QuickTestEvent::keyPress(int key, int modifiers, int delay)
33{
34 QWindow *window = activeWindow();
35 if (!window)
36 return false;
37 QTest::keyPress(window, Qt::Key(key), Qt::KeyboardModifiers(modifiers), delay);
38 return true;
39}
40
41bool QuickTestEvent::keyRelease(int key, int modifiers, int delay)
42{
43 QWindow *window = activeWindow();
44 if (!window)
45 return false;
46 QTest::keyRelease(window, Qt::Key(key), Qt::KeyboardModifiers(modifiers), delay);
47 return true;
48}
49
50bool QuickTestEvent::keyClick(int key, int modifiers, int delay)
51{
52 QWindow *window = activeWindow();
53 if (!window)
54 return false;
55 QTest::keyClick(window, Qt::Key(key), Qt::KeyboardModifiers(modifiers), delay);
56 return true;
57}
58
59bool QuickTestEvent::keyPressChar(const QString &character, int modifiers, int delay)
60{
61 QTEST_ASSERT(character.size() == 1);
62 QWindow *window = activeWindow();
63 if (!window)
64 return false;
65 QTest::keyPress(window, character[0].toLatin1(), Qt::KeyboardModifiers(modifiers), delay);
66 return true;
67}
68
69bool QuickTestEvent::keyReleaseChar(const QString &character, int modifiers, int delay)
70{
71 QTEST_ASSERT(character.size() == 1);
72 QWindow *window = activeWindow();
73 if (!window)
74 return false;
75 QTest::keyRelease(window, character[0].toLatin1(), Qt::KeyboardModifiers(modifiers), delay);
76 return true;
77}
78
79bool QuickTestEvent::keyClickChar(const QString &character, int modifiers, int delay)
80{
81 QTEST_ASSERT(character.size() == 1);
82 QWindow *window = activeWindow();
83 if (!window)
84 return false;
85 QTest::keyClick(window, character[0].toLatin1(), Qt::KeyboardModifiers(modifiers), delay);
86 return true;
87}
88
89#if QT_CONFIG(shortcut)
90// valueToKeySequence() is copied from qquickshortcut.cpp
91static QKeySequence valueToKeySequence(const QVariant &value)
92{
93 if (value.userType() == QMetaType::Int)
94 return QKeySequence(static_cast<QKeySequence::StandardKey>(value.toInt()));
95 return QKeySequence::fromString(value.toString());
96}
97#endif
98
99bool QuickTestEvent::keySequence(const QVariant &keySequence)
100{
101 QWindow *window = activeWindow();
102 if (!window)
103 return false;
104#if QT_CONFIG(shortcut)
105 QTest::keySequence(window, valueToKeySequence(keySequence));
106#else
107 Q_UNUSED(keySequence);
108#endif
109 return true;
110}
111
112namespace QtQuickTest
113{
115
117
118 // TODO should be Qt::MouseButtons buttons in case multiple buttons are pressed
119 static void mouseEvent(MouseAction action, QWindow *window,
120 QObject *item, Qt::MouseButton button,
121 Qt::KeyboardModifiers stateKey, const QPointF &_pos, int delay=-1)
122 {
123 QTEST_ASSERT(window);
124 QTEST_ASSERT(item);
125
126 if (delay == -1 || delay < QTest::defaultMouseDelay())
127 delay = QTest::defaultMouseDelay();
128 if (delay > 0) {
129 QTest::qWait(delay);
130 lastMouseTimestamp += delay;
131 }
132
133 if (action == MouseClick) {
134 mouseEvent(MousePress, window, item, button, stateKey, _pos);
135 mouseEvent(MouseRelease, window, item, button, stateKey, _pos);
136 return;
137 }
138
139 if (action == MouseDoubleClickSequence) {
140 mouseEvent(MousePress, window, item, button, stateKey, _pos);
141 mouseEvent(MouseRelease, window, item, button, stateKey, _pos);
142 mouseEvent(MousePress, window, item, button, stateKey, _pos);
143 mouseEvent(MouseDoubleClick, window, item, button, stateKey, _pos);
144 mouseEvent(MouseRelease, window, item, button, stateKey, _pos);
145 return;
146 }
147
148 QPoint pos = _pos.toPoint();
149 QQuickItem *sgitem = qobject_cast<QQuickItem *>(item);
150 if (sgitem)
151 pos = sgitem->mapToScene(_pos).toPoint();
152 QTEST_ASSERT(button == Qt::NoButton || button & Qt::MouseButtonMask);
153 QTEST_ASSERT(stateKey == 0 || stateKey & Qt::KeyboardModifierMask);
154
155 stateKey &= static_cast<unsigned int>(Qt::KeyboardModifierMask);
156
157 QEvent::Type meType;
158 Qt::MouseButton meButton;
159 Qt::MouseButtons meButtons;
160 switch (action)
161 {
162 case MousePress:
163 meType = QEvent::MouseButtonPress;
164 meButton = button;
165 meButtons = button;
166 break;
167 case MouseRelease:
168 meType = QEvent::MouseButtonRelease;
169 meButton = button;
170 meButtons = Qt::MouseButton();
171 break;
172 case MouseDoubleClick:
173 meType = QEvent::MouseButtonDblClick;
174 meButton = button;
175 meButtons = button;
176 break;
177 case MouseMove:
178 meType = QEvent::MouseMove;
179 meButton = Qt::NoButton;
180 meButtons = button;
181 break;
182 default:
183 QTEST_ASSERT(false);
184 }
185 QMouseEvent me(meType, pos, window->mapToGlobal(pos), meButton, meButtons, stateKey);
186 me.setTimestamp(++lastMouseTimestamp);
187 if (action == MouseRelease) // avoid double clicks being generated
188 lastMouseTimestamp += 500;
189
190 QSpontaneKeyEvent::setSpontaneous(&me);
191 if (!qApp->notify(window, &me)) {
192 static const char *mouseActionNames[] =
193 { "MousePress", "MouseRelease", "MouseClick", "MouseDoubleClick", "MouseMove",
194 "MouseDoubleClickSequence" };
195 qWarning("Mouse event \"%s\" not accepted by receiving window",
196 mouseActionNames[static_cast<int>(action)]);
197 }
198 }
199
200#if QT_CONFIG(wheelevent)
203 QPointF _pos, int xDelta, int yDelta, int delay = -1)
204 {
207 if (delay == -1 || delay < QTest::defaultMouseDelay())
209 if (delay > 0) {
210 QTest::qWait(delay);
212 }
213
214 QPoint pos;
216 if (sgitem)
218
221
222 stateKey &= static_cast<unsigned int>(Qt::KeyboardModifierMask);
224 stateKey, Qt::NoScrollPhase, false);
226
228 if (!qApp->notify(window, &we))
229 qWarning("Wheel event not accepted by receiving window");
230 }
231#endif
232};
233
234bool QuickTestEvent::mousePress
235 (QObject *item, qreal x, qreal y, int button,
236 int modifiers, int delay)
237{
238 QWindow *view = eventWindow(item);
239 if (!view)
240 return false;
241 m_pressedButtons.setFlag(Qt::MouseButton(button), true);
242 QtQuickTest::mouseEvent(QtQuickTest::MousePress, view, item,
243 Qt::MouseButton(button),
244 Qt::KeyboardModifiers(modifiers),
245 QPointF(x, y), delay);
246 return true;
247}
248
249#if QT_CONFIG(wheelevent)
250bool QuickTestEvent::mouseWheel(
251 QObject *item, qreal x, qreal y, int buttons,
252 int modifiers, int xDelta, int yDelta, int delay)
253{
254 QWindow *view = eventWindow(item);
255 if (!view)
256 return false;
257 QtQuickTest::mouseWheel(view, item, Qt::MouseButtons(buttons),
258 Qt::KeyboardModifiers(modifiers),
259 QPointF(x, y), xDelta, yDelta, delay);
260 return true;
261}
262#endif
263
264bool QuickTestEvent::mouseRelease
265 (QObject *item, qreal x, qreal y, int button,
266 int modifiers, int delay)
267{
268 QWindow *view = eventWindow(item);
269 if (!view)
270 return false;
271 m_pressedButtons.setFlag(Qt::MouseButton(button), false);
272 QtQuickTest::mouseEvent(QtQuickTest::MouseRelease, view, item,
273 Qt::MouseButton(button),
274 Qt::KeyboardModifiers(modifiers),
275 QPointF(x, y), delay);
276 return true;
277}
278
279bool QuickTestEvent::mouseClick
280 (QObject *item, qreal x, qreal y, int button,
281 int modifiers, int delay)
282{
283 QWindow *view = eventWindow(item);
284 if (!view)
285 return false;
286 QtQuickTest::mouseEvent(QtQuickTest::MouseClick, view, item,
287 Qt::MouseButton(button),
288 Qt::KeyboardModifiers(modifiers),
289 QPointF(x, y), delay);
290 return true;
291}
292
293bool QuickTestEvent::mouseDoubleClick
294 (QObject *item, qreal x, qreal y, int button,
295 int modifiers, int delay)
296{
297 QWindow *view = eventWindow(item);
298 if (!view)
299 return false;
300 QtQuickTest::mouseEvent(QtQuickTest::MouseDoubleClick, view, item,
301 Qt::MouseButton(button),
302 Qt::KeyboardModifiers(modifiers),
303 QPointF(x, y), delay);
304 return true;
305}
306
307bool QuickTestEvent::mouseDoubleClickSequence
308 (QObject *item, qreal x, qreal y, int button,
309 int modifiers, int delay)
310{
311 QWindow *view = eventWindow(item);
312 if (!view)
313 return false;
314 QtQuickTest::mouseEvent(QtQuickTest::MouseDoubleClickSequence, view, item,
315 Qt::MouseButton(button),
316 Qt::KeyboardModifiers(modifiers),
317 QPointF(x, y), delay);
318 return true;
319}
320
321bool QuickTestEvent::mouseMove
322 (QObject *item, qreal x, qreal y, int delay, int buttons, int modifiers)
323{
324 QWindow *view = eventWindow(item);
325 if (!view)
326 return false;
327 const Qt::MouseButtons effectiveButtons = buttons ? Qt::MouseButtons(buttons) : m_pressedButtons;
328 QtQuickTest::mouseEvent(QtQuickTest::MouseMove, view, item,
329 Qt::MouseButton(int(effectiveButtons)), Qt::KeyboardModifiers(modifiers),
330 QPointF(x, y), delay);
331 return true;
332}
333
334QWindow *QuickTestEvent::eventWindow(QObject *item)
335{
336 QWindow * window = qobject_cast<QWindow *>(item);
337 if (window)
338 return window;
339
340 QQuickItem *quickItem = qobject_cast<QQuickItem *>(item);
341 if (quickItem)
342 return quickItem->window();
343
344 QQuickItem *testParentitem = qobject_cast<QQuickItem *>(parent());
345 if (testParentitem)
346 return testParentitem->window();
347 return nullptr;
348}
349
350QWindow *QuickTestEvent::activeWindow()
351{
352 if (QWindow *window = QGuiApplication::focusWindow())
353 return window;
354 return eventWindow();
355}
356
357QQuickTouchEventSequence::QQuickTouchEventSequence(QuickTestEvent *testEvent, QObject *item)
358 : QObject(testEvent)
359 , m_sequence(QTest::touchEvent(testEvent->eventWindow(item), testEvent->touchDevice()))
360 , m_testEvent(testEvent)
361{
362}
363
364QObject *QQuickTouchEventSequence::press(int touchId, QObject *item, qreal x, qreal y)
365{
366 QWindow *view = m_testEvent->eventWindow(item);
367 if (view) {
368 QPointF pos(x, y);
369 QQuickItem *quickItem = qobject_cast<QQuickItem *>(item);
370 if (quickItem) {
371 pos = quickItem->mapToScene(pos);
372 }
373 m_sequence.press(touchId, pos.toPoint(), view);
374 }
375 return this;
376}
377
378QObject *QQuickTouchEventSequence::move(int touchId, QObject *item, qreal x, qreal y)
379{
380 QWindow *view = m_testEvent->eventWindow(item);
381 if (view) {
382 QPointF pos(x, y);
383 QQuickItem *quickItem = qobject_cast<QQuickItem *>(item);
384 if (quickItem) {
385 pos = quickItem->mapToScene(pos);
386 }
387 m_sequence.move(touchId, pos.toPoint(), view);
388 }
389 return this;
390}
391
392QObject *QQuickTouchEventSequence::release(int touchId, QObject *item, qreal x, qreal y)
393{
394 QWindow *view = m_testEvent->eventWindow(item);
395 if (view) {
396 QPointF pos(x, y);
397 QQuickItem *quickItem = qobject_cast<QQuickItem *>(item);
398 if (quickItem) {
399 pos = quickItem->mapToScene(pos);
400 }
401 m_sequence.release(touchId, pos.toPoint(), view);
402 }
403 return this;
404}
405
406QObject *QQuickTouchEventSequence::stationary(int touchId)
407{
408 m_sequence.stationary(touchId);
409 return this;
410}
411
412QObject *QQuickTouchEventSequence::commit()
413{
414 m_sequence.commit();
415 return this;
416}
417
418/*!
419 Return a simulated touchscreen, creating one if necessary
420
421 \internal
422*/
423
424QPointingDevice *QuickTestEvent::touchDevice()
425{
426 static QPointingDevice *device(nullptr);
427
428 if (!device) {
429 device = new QPointingDevice(QLatin1String("test touchscreen"), 42,
430 QInputDevice::DeviceType::TouchScreen, QPointingDevice::PointerType::Finger,
431 QInputDevice::Capability::Position, 10, 0);
432 QWindowSystemInterface::registerInputDevice(device);
433 }
434 return device;
435}
436
437/*!
438 Creates a new QQuickTouchEventSequence.
439
440 If valid, \a item determines the QWindow that touch events are sent to.
441 Test code should use touchEvent() from the QML TestCase type.
442
443 \internal
444*/
445QQuickTouchEventSequence *QuickTestEvent::touchEvent(QObject *item)
446{
447 return new QQuickTouchEventSequence(this, item);
448}
449
450QT_END_NAMESPACE
451
452#include "moc_quicktestevent_p.cpp"
static void mouseEvent(MouseAction action, QWindow *window, QObject *item, Qt::MouseButton button, Qt::KeyboardModifiers stateKey, const QPointF &_pos, int delay=-1)