summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAxel Spoerl <[email protected]>2024-05-22 16:23:27 +0200
committerAxel Spoerl <[email protected]>2024-10-19 10:23:37 +0000
commit5ac4f04325a56d47812f528fe31ceb0d4932f664 (patch)
tree0601e1080fa0419e6683fded56961ab3a7b3be87
parent3c495f9b93d71dda5b787f76112261aae37ebdc9 (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.cpp334
-rw-r--r--src/gui/kernel/qtestsupport_gui.h57
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