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
qtestsupport_gui.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 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
4#include <private/qguiapplication_p.h>
5#include <private/qeventpoint_p.h>
6
7#include <qpa/qplatformintegration.h>
8
10#include "qwindow.h"
11
12#include <QtCore/qtestsupport_core.h>
13#include <QtCore/qthread.h>
14#include <QtCore/QDebug>
15
16#if QT_CONFIG(test_gui)
17#include <QtCore/qloggingcategory.h>
18#include <private/qinputdevicemanager_p.h>
19#include <private/qeventpoint_p.h>
20#include <private/qhighdpiscaling_p.h>
21#endif // #if QT_CONFIG(test_gui)
22
24
25/*!
26 \since 5.0
27
28 Returns \c true, if \a window is active within \a timeout milliseconds. Otherwise returns \c false.
29
30 The method is useful in tests that call QWindow::show() and rely on the window actually being
31 active (i.e. being visible and having focus) before proceeding.
32
33 \note The method will time out and return \c false if another window prevents \a window from
34 becoming active.
35
36 \note Since focus is an exclusive property, \a window may loose its focus to another window at
37 any time - even after the method has returned \c true.
38
39 \sa qWaitForWindowExposed(), qWaitForWindowFocused(), QWindow::isActive()
40*/
41Q_GUI_EXPORT bool QTest::qWaitForWindowActive(QWindow *window, int timeout)
42{
43 if (Q_UNLIKELY(!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))) {
44 qWarning() << "qWaitForWindowActive was called on a platform that doesn't support window"
45 << "activation. This means there is an error in the test and it should either"
46 << "check for the WindowActivation platform capability before calling"
47 << "qWaitForWindowActivate, use qWaitForWindowExposed instead, or skip the test."
48 << "Falling back to qWaitForWindowExposed.";
49 return qWaitForWindowExposed(window, timeout);
50 }
51 return QTest::qWaitFor([&]() { return window->isActive(); }, timeout);
52}
53
54/*!
55 \since 6.7
56
57 Returns \c true, if \a window is the focus window within \a timeout. Otherwise returns \c false.
58
59 The method is useful in tests that call QWindow::show() and rely on the window
60 having focus (for receiving keyboard events e.g.) before proceeding.
61
62 \note The method will time out and return \c false if another window prevents \a window from
63 becoming focused.
64
65 \note Since focus is an exclusive property, \a window may loose its focus to another window at
66 any time - even after the method has returned \c true.
67
68 \sa qWaitForWindowExposed(), qWaitForWindowActive(), QGuiApplication::focusWindow()
69*/
70Q_GUI_EXPORT bool QTest::qWaitForWindowFocused(QWindow *window, QDeadlineTimer timeout)
71{
72 return QTest::qWaitFor([&]() { return qGuiApp->focusWindow() == window; }, timeout);
73}
74
75/*!
76 \since 5.0
77
78 Returns \c true, if \a window is exposed within \a timeout milliseconds. Otherwise returns \c false.
79
80 The method is useful in tests that call QWindow::show() and rely on the window actually being
81 being visible before proceeding.
82
83 \note A window mapped to screen may still not be considered exposed, if the window client area is
84 not visible, e.g. because it is completely covered by other windows.
85 In such cases, the method will time out and return \c false.
86
87 \sa qWaitForWindowActive(), QWindow::isExposed()
88*/
89Q_GUI_EXPORT bool QTest::qWaitForWindowExposed(QWindow *window, int timeout)
90{
91 return QTest::qWaitFor([&]() { return window->isExposed(); }, timeout);
92}
93
94namespace QTest {
95
128
130{
131 if (points.isEmpty())
132 return false;
134 bool ret = false;
135 if (targetWindow)
137 if (processEvents)
140 points.clear();
141 return ret;
142}
143
148
155
162
173
174} // namespace QTest
175
176//
177// W A R N I N G
178// -------------
179//
180// The QtGuiTest namespace is not part of the Qt API. It exists purely as an
181// implementation detail. It may change from version to version without notice,
182// or even be removed.
183//
184// We mean it.
185//
186#if QT_CONFIG(test_gui)
187Q_STATIC_LOGGING_CATEGORY(lcQtGuiTest, "qt.gui.test");
188#define deb qCDebug(lcQtGuiTest)
189
190/*!
191 \internal
192 \return the application's input device manager.
193 \return nullptr and log error, if the application hasn't been initialized.
194 */
195static QInputDeviceManager *inputDeviceManager()
196{
197 if (auto *idm = QGuiApplicationPrivate::inputDeviceManager())
198 return idm;
199
200 deb << "No input device manager present.";
201 return nullptr;
202}
203
204/*!
205 \internal
206 Synthesize keyboard modifier action by passing \a modifiers
207 to the application's input device manager.
208 */
209void QtGuiTest::setKeyboardModifiers(Qt::KeyboardModifiers modifiers)
210{
211 auto *idm = inputDeviceManager();
212 if (Q_UNLIKELY(!idm))
213 return;
214
215 idm->setKeyboardModifiers(modifiers);
216 deb << "Keyboard modifiers synthesized:" << modifiers;
217}
218
219/*!
220 \internal
221 Synthesize user-initiated mouse positioning by passing \a position
222 to the application's input device manager.
223 */
224void QtGuiTest::setCursorPosition(const QPoint &position)
225{
226 auto *idm = inputDeviceManager();
227 if (Q_UNLIKELY(!idm))
228 return;
229
230 idm->setCursorPos(position);
231 deb << "Mouse curser set to" << position;
232}
233
234/*!
235 \internal
236 Synthesize an extended \a key event of \a type, with \a modifiers, \a nativeScanCode,
237 \a nativeVirtualKey and \a text on application level.
238 Log whether the synthesizing has been successful.
239
240 \note
241 The application is expected to propagate the extended key event to its focus window,
242 if one exists.
243 */
244void QtGuiTest::synthesizeExtendedKeyEvent(QEvent::Type type, int key, Qt::KeyboardModifiers modifiers,
245 quint32 nativeScanCode, quint32 nativeVirtualKey,
246 const QString &text)
247{
248 Q_ASSERT_X((type == QEvent::KeyPress
249 || type == QEvent::KeyRelease),
250 Q_FUNC_INFO,
251 "called with invalid QEvent type");
252
253 deb << "Synthesizing key event:" << type << Qt::Key(key) << modifiers << text;
254
255 if (QWindowSystemInterface::handleExtendedKeyEvent(nullptr, type, key, modifiers,
256 nativeScanCode, nativeVirtualKey,
257 modifiers, text, /* autorep = */ false,
258 /* count = */ 0)) {
259
260 // If the key event is a shortcut, it may cause other events to be posted.
261 // => process those.
262 QCoreApplication::sendPostedEvents();
263 deb << "(success)";
264 } else {
265 deb << "(failure)";
266 }
267}
268
269/*!
270 \internal
271 Synthesize a key event \a k of type \a t, with modifiers \a mods, \a text,
272 \a autorep and \a count on application level.
273 Log whether the synthesizing has been successful.
274
275 \note
276 The application is expected to propagate the key event to its focus window,
277 if one exists.
278 */
279bool QtGuiTest::synthesizeKeyEvent(QWindow *window, QEvent::Type t, int k, Qt::KeyboardModifiers mods,
280 const QString & text, bool autorep,
281 ushort count)
282{
283 Q_ASSERT_X((t == QEvent::KeyPress
284 || t == QEvent::KeyRelease),
285 Q_FUNC_INFO,
286 "called with invalid QEvent type");
287
288 deb << "Synthesizing key event:" << t << Qt::Key(k) << mods << text;
289
290 bool result = QWindowSystemInterface::handleKeyEvent(window, t, k, mods, text, autorep, count);
291 if (result) {
292 // If the key event is a shortcut, it may cause other events to be posted.
293 // => process those.
294 QCoreApplication::sendPostedEvents();
295 deb << "(success)";
296 } else {
297 deb << "(failure)";
298 }
299
300 return result;
301}
302
303/*!
304 \internal
305 Synthesize a mouse event of \a type, with \a button at \a position at application level.
306 Respect \a state and \a modifiers.
307
308 The application is expected to
309 \list
310 \li propagate the mouse event to its focus window,
311 if one exists.
312 \li convert a click/release squence into a double click.
313 \endlist
314
315 \note
316 QEvent::MouseButtonDoubleClick can't be explicitly synthesized.
317 */
318void QtGuiTest::synthesizeMouseEvent(const QPointF &position, Qt::MouseButtons state,
319 Qt::MouseButton button, QEvent::Type type,
320 Qt::KeyboardModifiers modifiers)
321{
322 Q_ASSERT_X((type == QEvent::MouseButtonPress
323 || type == QEvent::MouseButtonRelease
324 || type == QEvent::MouseMove),
325 Q_FUNC_INFO,
326 "called with invalid QEvent type");
327
328 deb << "Synthesizing mouse event:" << type << position << button << modifiers;
329
330 if (QWindowSystemInterface::handleMouseEvent(nullptr, position, position, state, button,
331 type, modifiers, Qt::MouseEventNotSynthesized)) {
332 // If the mouse event reacts to a shortcut, it may cause other events to be posted.
333 // => process those.
334 QCoreApplication::processEvents();
335 QCoreApplication::sendPostedEvents();
336
337 deb << "(success)";
338 } else {
339 deb << "(failure)";
340 }
341}
342
343/*!
344 \internal
345 Synthesize a wheel event with \a modifiers and \a rollCount representing the number of
346 roll unit on application level.
347
348 \note
349 The application is expected to handle the wheel event, or propagate it
350 to its focus window, if one exists.
351 */
352void QtGuiTest::synthesizeWheelEvent(int rollCount, Qt::KeyboardModifiers modifiers)
353{
354 deb << "Synthesizing wheel event:" << rollCount << modifiers;
355
356 QPoint position = QCursor::pos();
357 if (QWindowSystemInterface::handleWheelEvent(nullptr, position, position,
358 QPoint(), QPoint(0, -rollCount), modifiers)) {
359
360 // It's unlikely that a shortcut relates to a subsequent wheel event.
361 // But it's not harmful, to send posted events here.
362 QCoreApplication::sendPostedEvents();
363 deb << "(success)";
364 } else {
365 deb << "(failure)";
366 }
367}
368
369/*!
370 \internal
371 \return the number of milliseconds since the QElapsedTimer
372 eventTime was last started.
373*/
374qint64 QtGuiTest::eventTimeElapsed()
375{
376 return QWindowSystemInterfacePrivate::eventTime.elapsed();
377}
378
379/*!
380 \internal
381 Post fake window activation with \a window representing the
382 fake window being activated.
383*/
384void QtGuiTest::postFakeWindowActivation(QWindow *window)
385{
386 Q_ASSERT_X(window,
387 Q_FUNC_INFO,
388 "called with nullptr");
389
390 deb << "Posting fake window activation:" << window;
391
392 QWindowSystemInterfacePrivate::FocusWindowEvent e(window, Qt::OtherFocusReason);
393 QGuiApplicationPrivate::processWindowSystemEvent(&e);
394 QWindowSystemInterface::handleFocusWindowChanged(window);
395}
396
397/*!
398 \internal
399 \return native \a window position from \a value.
400*/
401QPoint QtGuiTest::toNativePixels(const QPoint &value, const QWindow *window)
402{
403 Q_ASSERT_X(window,
404 Q_FUNC_INFO,
405 "called with nullptr");
406
407 deb << "Calculating native pixels: " << value << window;
408 return QHighDpi::toNativePixels<QPoint, QWindow>(value, window);
409}
410
411/*!
412 \internal
413 \return native \a window rectangle from \a value.
414*/
415QRect QtGuiTest::toNativePixels(const QRect &value, const QWindow *window)
416{
417 Q_ASSERT_X(window,
418 Q_FUNC_INFO,
419 "called with nullptr");
420
421 deb << "Calculating native pixels: " << value << window;
422 return QHighDpi::toNativePixels<QRect, QWindow>(value, window);
423}
424
425/*!
426 \internal
427 \return scaling factor of \a window relative to Qt.
428*/
429qreal QtGuiTest::scaleFactor(const QWindow *window)
430{
431 Q_ASSERT_X(window,
432 Q_FUNC_INFO,
433 "called with nullptr");
434
435 deb << "Calculating scaling factor: " << window;
436 return QHighDpiScaling::factor(window);
437}
438
439/*!
440 \internal
441 Set the id of \a p to \a arg.
442*/
443void QtGuiTest::setEventPointId(QEventPoint &p, int arg)
444{
445 QMutableEventPoint::setId(p, arg);
446}
447
448/*!
449 \internal
450 Set the pressure of \a p to \a arg.
451*/
452void QtGuiTest::setEventPointPressure(QEventPoint &p, qreal arg)
453{
454 QMutableEventPoint::setPressure(p, arg);
455}
456
457/*!
458 \internal
459 Set the state of \a p to \a arg.
460*/
461void QtGuiTest::setEventPointState(QEventPoint &p, QEventPoint::State arg)
462{
463 QMutableEventPoint::setState(p, arg);
464}
465
466/*!
467 \internal
468 Set the position of \a p to \a arg.
469*/
470void QtGuiTest::setEventPointPosition(QEventPoint &p, QPointF arg)
471{
472 QMutableEventPoint::setPosition(p, arg);
473}
474
475/*!
476 \internal
477 Set the global position of \a p to \a arg.
478*/
479void QtGuiTest::setEventPointGlobalPosition(QEventPoint &p, QPointF arg)
480{
481 QMutableEventPoint::setGlobalPosition(p, arg);
482}
483
484/*!
485 \internal
486 Set the scene position of \a p to \a arg.
487*/
488void QtGuiTest::setEventPointScenePosition(QEventPoint &p, QPointF arg)
489{
490 QMutableEventPoint::setScenePosition(p, arg);
491}
492
493/*!
494 \internal
495 Set the ellipse diameters of \a p to \a arg.
496*/
497void QtGuiTest::setEventPointEllipseDiameters(QEventPoint &p, QSizeF arg)
498{
499 QMutableEventPoint::setEllipseDiameters(p, arg);
500}
501
502#undef deb
503#endif // #if QT_CONFIG(test_gui)
504QT_END_NAMESPACE
Combined button and popup list for selecting options.
Q_GUI_EXPORT bool qWaitForWindowFocused(QWindow *window, QDeadlineTimer timeout=std::chrono::seconds{5})
Q_GUI_EXPORT bool qWaitForWindowActive(QWindow *window, int timeout=5000)
Q_GUI_EXPORT bool qWaitForWindowExposed(QWindow *window, int timeout=5000)