Cleanup the mouse event handling in testlib

Calling QCursor::setPos() to emulate mouse move events
is a rather bad idea, as it creates round trips through
the server, leading to timing issues etc.

In addition, we should not call qapp->notify(), but rather
route the events through the proper QPA interface. This
is required to properly generate all other events such
as enter/leave etc. As this breaks existing tests,
put the new behavior behind an #ifdef for now. Like this,
we can fix tests one by one, and then turn on the define by
default for 5.6 (with a changelog message).

We emulate timestamps to avoid creating double clicks
by mistake. In addition, fix QGuiApplication::processMouseEvent
to not push events back into the QPA event queue (as this is
a bad hack and breaks the new testing system).

Change-Id: I71774cb56674d7fb66b9a7cf1e1ada1629536408
Reviewed-by: Simon Hausmann <simon.hausmann@theqtcompany.com>
This commit is contained in:
Lars Knoll 2015-06-02 08:43:17 +02:00 committed by Simon Hausmann
parent d83bd9c6f5
commit beef975f92
3 changed files with 46 additions and 40 deletions

View File

@ -1695,12 +1695,14 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo
// A mouse event should not change both position and buttons at the same time. Instead we
// should first send a move event followed by a button changed event. Since this is not the case
// with the current event, we split it in two.
QWindowSystemInterfacePrivate::MouseEvent *mouseButtonEvent = new QWindowSystemInterfacePrivate::MouseEvent(
QWindowSystemInterfacePrivate::MouseEvent mouseButtonEvent(
e->window.data(), e->timestamp, e->type, e->localPos, e->globalPos, e->buttons, e->modifiers);
if (e->flags & QWindowSystemInterfacePrivate::WindowSystemEvent::Synthetic)
mouseButtonEvent->flags |= QWindowSystemInterfacePrivate::WindowSystemEvent::Synthetic;
QWindowSystemInterfacePrivate::windowSystemEventQueue.prepend(mouseButtonEvent);
stateChange = Qt::NoButton;
mouseButtonEvent.flags |= QWindowSystemInterfacePrivate::WindowSystemEvent::Synthetic;
e->buttons = buttons;
processMouseEvent(e);
processMouseEvent(&mouseButtonEvent);
return;
}
QWindow *window = e->window.data();

View File

@ -810,8 +810,8 @@ Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QWindowSystemInterface::TouchPo
}
#endif
Q_GUI_EXPORT void qt_handleMouseEvent(QWindow *w, const QPointF & local, const QPointF & global, Qt::MouseButtons b, Qt::KeyboardModifiers mods = Qt::NoModifier) {
unsigned long timestamp = QWindowSystemInterfacePrivate::eventTime.elapsed();
Q_GUI_EXPORT void qt_handleMouseEvent(QWindow *w, const QPointF &local, const QPointF &global, Qt::MouseButtons b, Qt::KeyboardModifiers mods, int timestamp)
{
QWindowSystemInterfacePrivate::MouseEvent e(w, timestamp, local, global, b, mods, Qt::MouseEventNotSynthesized);
QGuiApplicationPrivate::processWindowSystemEvent(&e);
}

View File

@ -45,6 +45,7 @@
#include <QtTest/qtestspontaneevent.h>
#include <QtCore/qpoint.h>
#include <QtCore/qstring.h>
#include <QtCore/qpointer.h>
#include <QtGui/qevent.h>
#include <QtGui/qwindow.h>
@ -57,7 +58,7 @@
QT_BEGIN_NAMESPACE
Q_GUI_EXPORT void qt_handleMouseEvent(QWindow *w, const QPointF & local, const QPointF & global, Qt::MouseButtons b, Qt::KeyboardModifiers mods = Qt::NoModifier);
Q_GUI_EXPORT void qt_handleMouseEvent(QWindow *w, const QPointF &local, const QPointF &global, Qt::MouseButtons b, Qt::KeyboardModifiers mods, int timestamp);
namespace QTest
{
@ -83,7 +84,8 @@ namespace QTest
QTest::qWarn("Mouse event occurs outside of target window.");
}
static Qt::MouseButton lastButton = Qt::NoButton;
static Qt::MouseButton lastButton = Qt::NoButton;
static int timestamp = 0;
if (delay == -1 || delay < defaultMouseDelay())
delay = defaultMouseDelay();
@ -93,42 +95,38 @@ namespace QTest
if (pos.isNull())
pos = window->geometry().center();
if (action == MouseClick) {
mouseEvent(MousePress, window, button, stateKey, pos);
mouseEvent(MouseRelease, window, button, stateKey, pos);
return;
}
QTEST_ASSERT(uint(stateKey) == 0 || stateKey & Qt::KeyboardModifierMask);
stateKey &= static_cast<unsigned int>(Qt::KeyboardModifierMask);
QPointF global = window->mapToGlobal(pos);
QPointer<QWindow> w(window);
switch (action)
{
case MousePress:
qt_handleMouseEvent(window,pos,window->mapToGlobal(pos),button,stateKey);
lastButton = button;
case MouseDClick:
qt_handleMouseEvent(w, pos, global, button, stateKey, timestamp);
qt_handleMouseEvent(w, pos, global, Qt::NoButton, stateKey, ++timestamp);
// fall through
case MousePress:
case MouseClick:
qt_handleMouseEvent(w, pos, global, button, stateKey, ++timestamp);
lastButton = button;
if (action == MousePress)
break;
case MouseRelease:
qt_handleMouseEvent(window,pos,window->mapToGlobal(pos),Qt::NoButton,stateKey);
lastButton = Qt::NoButton;
break;
case MouseDClick:
qt_handleMouseEvent(window,pos,window->mapToGlobal(pos),button,stateKey);
qWait(10);
qt_handleMouseEvent(window,pos,window->mapToGlobal(pos),Qt::NoButton,stateKey);
qWait(20);
qt_handleMouseEvent(window,pos,window->mapToGlobal(pos),button,stateKey);
qWait(10);
qt_handleMouseEvent(window,pos,window->mapToGlobal(pos),Qt::NoButton,stateKey);
break;
case MouseMove:
qt_handleMouseEvent(window,pos,window->mapToGlobal(pos),lastButton,stateKey);
// No QCursor::setPos() call here. That could potentially result in mouse events sent by the windowing system
// which is highly undesired here. Tests must avoid relying on QCursor.
break;
default:
QTEST_ASSERT(false);
// fall through
case MouseRelease:
qt_handleMouseEvent(w, pos, global, Qt::NoButton, stateKey, ++timestamp);
timestamp += 500; // avoid double clicks being generated
lastButton = Qt::NoButton;
break;
case MouseMove:
qt_handleMouseEvent(w, pos, global, lastButton, stateKey, ++timestamp);
// No QCursor::setPos() call here. That could potentially result in mouse events sent by the windowing system
// which is highly undesired here. Tests must avoid relying on QCursor.
break;
default:
QTEST_ASSERT(false);
}
waitForEvents();
}
@ -153,6 +151,15 @@ namespace QTest
Qt::KeyboardModifiers stateKey, QPoint pos, int delay=-1)
{
QTEST_ASSERT(widget);
if (pos.isNull())
pos = widget->rect().center();
#ifdef QTEST_QPA_MOUSE_HANDLING
QWindow *w = widget->window()->windowHandle();
QTEST_ASSERT(w);
mouseEvent(action, w, button, stateKey, w->mapFromGlobal(widget->mapToGlobal(pos)), delay);
#else
extern int Q_TESTLIB_EXPORT defaultMouseDelay();
if (delay == -1 || delay < defaultMouseDelay())
@ -160,9 +167,6 @@ namespace QTest
if (delay > 0)
QTest::qWait(delay);
if (pos.isNull())
pos = widget->rect().center();
if (action == MouseClick) {
mouseEvent(MousePress, widget, button, stateKey, pos);
mouseEvent(MouseRelease, widget, button, stateKey, pos);
@ -203,7 +207,7 @@ namespace QTest
QString warning = QString::fromLatin1("Mouse event \"%1\" not accepted by receiving widget");
QTest::qWarn(warning.arg(QString::fromLatin1(mouseActionNames[static_cast<int>(action)])).toLatin1().data());
}
#endif
}
inline void mousePress(QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers stateKey = 0,