diff options
author | Axel Spoerl <[email protected]> | 2024-05-22 16:23:27 +0200 |
---|---|---|
committer | Axel Spoerl <[email protected]> | 2024-10-19 10:23:37 +0000 |
commit | 5ac4f04325a56d47812f528fe31ceb0d4932f664 (patch) | |
tree | 0601e1080fa0419e6683fded56961ab3a7b3be87 | |
parent | 3c495f9b93d71dda5b787f76112261aae37ebdc9 (diff) |
Implement QtGuiTest namespace
Squish uses QGuiApplicationPrivate and QWindowSystemInterface to
synthesize mouse-, key-, and wheel events.
Signature and behavior changes in private headers have required Squish
developers to adapt calls, depending on the wrapper's supported Qt
version.
Implement compatibility namespace, depending on FEATURE_test_gui=true.
Simplify signatures and add categorized logging.
Fixes: QTBUG-125595
Change-Id: Iadb54eaef34744be35416147d800e7ba2e763cf7
Reviewed-by: Volker Hilsheimer <[email protected]>
-rw-r--r-- | src/gui/kernel/qtestsupport_gui.cpp | 334 | ||||
-rw-r--r-- | src/gui/kernel/qtestsupport_gui.h | 57 |
2 files changed, 390 insertions, 1 deletions
diff --git a/src/gui/kernel/qtestsupport_gui.cpp b/src/gui/kernel/qtestsupport_gui.cpp index 869eddce494..85f791dd450 100644 --- a/src/gui/kernel/qtestsupport_gui.cpp +++ b/src/gui/kernel/qtestsupport_gui.cpp @@ -13,6 +13,12 @@ #include <QtCore/qthread.h> #include <QtCore/QDebug> +#if QT_CONFIG(test_gui) +#include <private/qinputdevicemanager_p.h> +#include <private/qeventpoint_p.h> +#include <private/qhighdpiscaling_p.h> +#endif // #if QT_CONFIG(test_gui) + QT_BEGIN_NAMESPACE /*! @@ -166,4 +172,332 @@ QEventPoint &QTouchEventSequence::pointOrPreviousPoint(int touchId) } // namespace QTest +// +// W A R N I N G +// ------------- +// +// The QtGuiTest namespace is not part of the Qt API. It exists purely as an +// implementation detail. It may change from version to version without notice, +// or even be removed. +// +// We mean it. +// +#if QT_CONFIG(test_gui) +Q_LOGGING_CATEGORY(lcQtGuiTest, "qt.gui.test"); +#define deb qCDebug(lcQtGuiTest) + +/*! + \internal + \return the application's input device manager. + \return nullptr and log error, if the application hasn't been initialized. + */ +static QInputDeviceManager *inputDeviceManager() +{ + if (auto *idm = QGuiApplicationPrivate::inputDeviceManager()) + return idm; + + deb << "No input device manager present."; + return nullptr; +} + +/*! + \internal + Synthesize keyboard modifier action by passing \a modifiers + to the application's input device manager. + */ +void QtGuiTest::setKeyboardModifiers(Qt::KeyboardModifiers modifiers) +{ + auto *idm = inputDeviceManager(); + if (Q_UNLIKELY(!idm)) + return; + + idm->setKeyboardModifiers(modifiers); + deb << "Keyboard modifiers synthesized:" << modifiers; +} + +/*! + \internal + Synthesize user-initiated mouse positioning by passing \a position + to the application's input device manager. + */ +void QtGuiTest::setCursorPosition(const QPoint &position) +{ + auto *idm = inputDeviceManager(); + if (Q_UNLIKELY(!idm)) + return; + + idm->setCursorPos(position); + deb << "Mouse curser set to" << position; +} + +/*! + \internal + Synthesize an extended \a key event of \a type, with \a modifiers, \a nativeScanCode, + \a nativeVirtualKey and \a text on application level. + Log whether the synthesizing has been successful. + + \note + The application is expected to propagate the extended key event to its focus window, + if one exists. + */ +void QtGuiTest::synthesizeExtendedKeyEvent(QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, + quint32 nativeScanCode, quint32 nativeVirtualKey, + const QString &text) +{ + Q_ASSERT_X((type == QEvent::KeyPress + || type == QEvent::KeyRelease), + Q_FUNC_INFO, + "called with invalid QEvent type"); + + deb << "Synthesizing key event:" << type << Qt::Key(key) << modifiers << text; + + if (QWindowSystemInterface::handleExtendedKeyEvent(nullptr, type, key, modifiers, + nativeScanCode, nativeVirtualKey, + modifiers, text, /* autorep = */ false, + /* count = */ 0)) { + + // If the key event is a shortcut, it may cause other events to be posted. + // => process those. + QCoreApplication::sendPostedEvents(); + deb << "(success)"; + } else { + deb << "(failure)"; + } +} + +/*! + \internal + Synthesize a key event \a k of type \a t, with modifiers \a mods, \a text, + \a autorep and \a count on application level. + Log whether the synthesizing has been successful. + + \note + The application is expected to propagate the key event to its focus window, + if one exists. + */ +bool QtGuiTest::synthesizeKeyEvent(QWindow *window, QEvent::Type t, int k, Qt::KeyboardModifiers mods, + const QString & text, bool autorep, + ushort count) +{ + Q_ASSERT_X((t == QEvent::KeyPress + || t == QEvent::KeyRelease), + Q_FUNC_INFO, + "called with invalid QEvent type"); + + deb << "Synthesizing key event:" << t << Qt::Key(k) << mods << text; + + bool result = QWindowSystemInterface::handleKeyEvent(window, t, k, mods, text, autorep, count); + if (result) { + // If the key event is a shortcut, it may cause other events to be posted. + // => process those. + QCoreApplication::sendPostedEvents(); + deb << "(success)"; + } else { + deb << "(failure)"; + } + + return result; +} + +/*! + \internal + Synthesize a mouse event of \a type, with \a button at \a position at application level. + Respect \a state and \a modifiers. + + The application is expected to + \list + \li propagate the mouse event to its focus window, + if one exists. + \li convert a click/release squence into a double click. + \endlist + + \note + QEvent::MouseButtonDoubleClick can't be explicitly synthesized. + */ +void QtGuiTest::synthesizeMouseEvent(const QPointF &position, Qt::MouseButtons state, + Qt::MouseButton button, QEvent::Type type, + Qt::KeyboardModifiers modifiers) +{ + Q_ASSERT_X((type == QEvent::MouseButtonPress + || type == QEvent::MouseButtonRelease + || type == QEvent::MouseMove), + Q_FUNC_INFO, + "called with invalid QEvent type"); + + deb << "Synthesizing mouse event:" << type << position << button << modifiers; + + if (QWindowSystemInterface::handleMouseEvent(nullptr, position, position, state, button, + type, modifiers, Qt::MouseEventNotSynthesized)) { + // If the mouse event reacts to a shortcut, it may cause other events to be posted. + // => process those. + QCoreApplication::processEvents(); + QCoreApplication::sendPostedEvents(); + + deb << "(success)"; + } else { + deb << "(failure)"; + } +} + +/*! + \internal + Synthesize a wheel event with \a modifiers and \a rollCount representing the number of + roll unit on application level. + + \note + The application is expected to handle the wheel event, or propagate it + to its focus window, if one exists. + */ +void QtGuiTest::synthesizeWheelEvent(int rollCount, Qt::KeyboardModifiers modifiers) +{ + deb << "Synthesizing wheel event:" << rollCount << modifiers; + + QPoint position = QCursor::pos(); + if (QWindowSystemInterface::handleWheelEvent(nullptr, position, position, + QPoint(), QPoint(0, -rollCount), modifiers)) { + + // It's unlikely that a shortcut relates to a subsequent wheel event. + // But it's not harmful, to send posted events here. + QCoreApplication::sendPostedEvents(); + deb << "(success)"; + } else { + deb << "(failure)"; + } +} + +/*! + \internal + \return the number of milliseconds since the QElapsedTimer + eventTime was last started. +*/ +qint64 QtGuiTest::eventTimeElapsed() +{ + return QWindowSystemInterfacePrivate::eventTime.elapsed(); +} + +/*! + \internal + Post fake window activation with \a window representing the + fake window being activated. +*/ +void QtGuiTest::postFakeWindowActivation(QWindow *window) +{ + Q_ASSERT_X(window, + Q_FUNC_INFO, + "called with nullptr"); + + deb << "Posting fake window activation:" << window; + + QWindowSystemInterfacePrivate::FocusWindowEvent e(window, Qt::OtherFocusReason); + QGuiApplicationPrivate::processWindowSystemEvent(&e); + QWindowSystemInterface::handleFocusWindowChanged(window); +} + +/*! + \internal + \return native \a window position from \a value. +*/ +QPoint QtGuiTest::toNativePixels(const QPoint &value, const QWindow *window) +{ + Q_ASSERT_X(window, + Q_FUNC_INFO, + "called with nullptr"); + + deb << "Calculating native pixels: " << value << window; + return QHighDpi::toNativePixels<QPoint, QWindow>(value, window); +} + +/*! + \internal + \return native \a window rectangle from \a value. +*/ +QRect QtGuiTest::toNativePixels(const QRect &value, const QWindow *window) +{ + Q_ASSERT_X(window, + Q_FUNC_INFO, + "called with nullptr"); + + deb << "Calculating native pixels: " << value << window; + return QHighDpi::toNativePixels<QRect, QWindow>(value, window); +} + +/*! + \internal + \return scaling factor of \a window relative to Qt. +*/ +qreal QtGuiTest::factor(const QWindow *window) +{ + Q_ASSERT_X(window, + Q_FUNC_INFO, + "called with nullptr"); + + deb << "Calculating scaling factor: " << window; + return QHighDpiScaling::factor(window); +} + +/*! + \internal + Set the id of \a p to \a arg. +*/ +void QtGuiTest::setEventPointId(QEventPoint &p, int arg) +{ + QMutableEventPoint::setId(p, arg); +} + +/*! + \internal + Set the pressure of \a p to \a arg. +*/ +void QtGuiTest::setEventPointPressure(QEventPoint &p, qreal arg) +{ + QMutableEventPoint::setPressure(p, arg); +} + +/*! + \internal + Set the state of \a p to \a arg. +*/ +void QtGuiTest::setEventPointState(QEventPoint &p, QEventPoint::State arg) +{ + QMutableEventPoint::setState(p, arg); +} + +/*! + \internal + Set the position of \a p to \a arg. +*/ +void QtGuiTest::setEventPointPosition(QEventPoint &p, QPointF arg) +{ + QMutableEventPoint::setPosition(p, arg); +} + +/*! + \internal + Set the global position of \a p to \a arg. +*/ +void QtGuiTest::setEventPointGlobalPosition(QEventPoint &p, QPointF arg) +{ + QMutableEventPoint::setGlobalPosition(p, arg); +} + +/*! + \internal + Set the scene position of \a p to \a arg. +*/ +void QtGuiTest::setEventPointScenePosition(QEventPoint &p, QPointF arg) +{ + QMutableEventPoint::setScenePosition(p, arg); +} + +/*! + \internal + Set the ellipse diameters of \a p to \a arg. +*/ +void QtGuiTest::setEventPointEllipseDiameters(QEventPoint &p, QSizeF arg) +{ + QMutableEventPoint::setEllipseDiameters(p, arg); +} + +#undef deb +#endif // #if QT_CONFIG(test_gui) QT_END_NAMESPACE diff --git a/src/gui/kernel/qtestsupport_gui.h b/src/gui/kernel/qtestsupport_gui.h index c39af318b82..349c48ef6e7 100644 --- a/src/gui/kernel/qtestsupport_gui.h +++ b/src/gui/kernel/qtestsupport_gui.h @@ -8,6 +8,10 @@ #include <QtGui/qevent.h> #include <QtCore/qmap.h> +#if QT_CONFIG(test_gui) +#include <QtCore/qloggingcategory.h> +#endif // #if QT_CONFIG(test_gui) + QT_BEGIN_NAMESPACE class QWindow; @@ -59,6 +63,57 @@ protected: } // namespace QTest +// +// W A R N I N G +// ------------- +// +// The QtGuiTest namespace is not part of the Qt API. It exists purely as an +// implementation detail. It may change from version to version without notice, +// or even be removed. +// +// We mean it. +// + +#if QT_CONFIG(test_gui) +Q_DECLARE_LOGGING_CATEGORY(lcQtGuiTest) +namespace QtGuiTest +{ + Q_NAMESPACE_EXPORT(Q_GUI_EXPORT) + + void setKeyboardModifiers(Qt::KeyboardModifiers modifiers); + void setCursorPosition(const QPoint &position); + void synthesizeExtendedKeyEvent(QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, + quint32 nativeScanCode, quint32 nativeVirtualKey, + const QString &text); + bool synthesizeKeyEvent(QWindow *window, QEvent::Type t, int k, Qt::KeyboardModifiers mods, + const QString & text = QString(), bool autorep = false, + ushort count = 1); + + void synthesizeMouseEvent(const QPointF &position, Qt::MouseButtons state, + Qt::MouseButton button, QEvent::Type type, + Qt::KeyboardModifiers modifiers); + + void synthesizeWheelEvent(int rollCount, Qt::KeyboardModifiers modifiers); + + qint64 eventTimeElapsed(); + + void postFakeWindowActivation(QWindow *window); + + QPoint toNativePixels(const QPoint &value, const QWindow *window); + QRect toNativePixels(const QRect &value, const QWindow *window); + qreal factor(const QWindow *window); + + void setEventPointId(QEventPoint &p, int arg); + void setEventPointPressure(QEventPoint &p, qreal arg); + void setEventPointState(QEventPoint &p, QEventPoint::State arg); + void setEventPointPosition(QEventPoint &p, QPointF arg); + void setEventPointGlobalPosition(QEventPoint &p, QPointF arg); + void setEventPointScenePosition(QEventPoint &p, QPointF arg); + void setEventPointEllipseDiameters(QEventPoint &p, QSizeF arg); +} // namespace QtGuiTest + +#endif // #if QT_CONFIG(test_gui) + QT_END_NAMESPACE -#endif +#endif // #ifndef QTESTSUPPORT_GUI_H |