Refactor testlib touch functions into qtestsupport_gui and _widgets

Because we removed public setters from QTouchEvent and QEventPoint in
4e400369c0 and now it's proposed to give
QEventPoint a d-pointer again, the implementation of QTouchEventSequence
needs to start using QMutableEventPoint: being a friend will no longer
be enough, because the member variables won't be accessible in the future.
But because we have separate test libs for Gui and Widgets, it needs to
be further refactored into two classes.

Change-Id: I0bfc0978fc4187348ac872e1330d95259d557b69
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Shawn Rutledge 2020-09-03 13:32:55 +02:00
parent 2f0f74498a
commit 0e475eeea6
7 changed files with 233 additions and 173 deletions

View File

@ -45,6 +45,7 @@
#include "qwindow.h"
#include <QtCore/qtestsupport_core.h>
#include <QtCore/qthread.h>
#include <QtCore/QDebug>
QT_BEGIN_NAMESPACE
@ -91,4 +92,84 @@ Q_GUI_EXPORT bool QTest::qWaitForWindowExposed(QWindow *window, int timeout)
return QTest::qWaitFor([&]() { return window->isExposed(); }, timeout);
}
namespace QTest {
QTouchEventSequence::~QTouchEventSequence()
{
if (commitWhenDestroyed)
commit();
}
QTouchEventSequence& QTouchEventSequence::press(int touchId, const QPoint &pt, QWindow *window)
{
auto &p = QMutableEventPoint::from(point(touchId));
p.setGlobalPosition(mapToScreen(window, pt));
p.setState(QEventPoint::State::Pressed);
return *this;
}
QTouchEventSequence& QTouchEventSequence::move(int touchId, const QPoint &pt, QWindow *window)
{
auto &p = QMutableEventPoint::from(point(touchId));
p.setGlobalPosition(mapToScreen(window, pt));
p.setState(QEventPoint::State::Updated);
return *this;
}
QTouchEventSequence& QTouchEventSequence::release(int touchId, const QPoint &pt, QWindow *window)
{
auto &p = QMutableEventPoint::from(point(touchId));
p.setGlobalPosition(mapToScreen(window, pt));
p.setState(QEventPoint::State::Released);
return *this;
}
QTouchEventSequence& QTouchEventSequence::stationary(int touchId)
{
auto &p = QMutableEventPoint::from(pointOrPreviousPoint(touchId));
p.setState(QEventPoint::State::Stationary);
return *this;
}
void QTouchEventSequence::commit(bool processEvents)
{
if (points.isEmpty())
return;
QThread::msleep(1);
if (targetWindow)
qt_handleTouchEvent(targetWindow, device, points.values());
if (processEvents)
QCoreApplication::processEvents();
previousPoints = points;
points.clear();
}
QTouchEventSequence::QTouchEventSequence(QWindow *window, QPointingDevice *aDevice, bool autoCommit)
: targetWindow(window), device(aDevice), commitWhenDestroyed(autoCommit)
{
}
QPoint QTouchEventSequence::mapToScreen(QWindow *window, const QPoint &pt)
{
if (window)
return window->mapToGlobal(pt);
return targetWindow ? targetWindow->mapToGlobal(pt) : pt;
}
QEventPoint &QTouchEventSequence::point(int touchId)
{
if (!points.contains(touchId))
points[touchId] = QEventPoint(touchId);
return points[touchId];
}
QEventPoint &QTouchEventSequence::pointOrPreviousPoint(int touchId)
{
if (!points.contains(touchId)) {
if (previousPoints.contains(touchId))
points[touchId] = previousPoints.value(touchId);
else
points[touchId] = QEventPoint(touchId);
}
return points[touchId];
}
} // namespace QTest
QT_END_NAMESPACE

View File

@ -41,15 +41,54 @@
#define QTESTSUPPORT_GUI_H
#include <QtGui/qtguiglobal.h>
#include <QtGui/qevent.h>
#include <QtCore/qmap.h>
QT_BEGIN_NAMESPACE
class QWindow;
Q_GUI_EXPORT void qt_handleTouchEvent(QWindow *w, const QPointingDevice *device,
const QList<QEventPoint> &points,
Qt::KeyboardModifiers mods = Qt::NoModifier);
namespace QTest {
Q_REQUIRED_RESULT Q_GUI_EXPORT bool qWaitForWindowActive(QWindow *window, int timeout = 5000);
Q_REQUIRED_RESULT Q_GUI_EXPORT bool qWaitForWindowExposed(QWindow *window, int timeout = 5000);
}
Q_GUI_EXPORT QPointingDevice * createTouchDevice(QInputDevice::DeviceType devType = QInputDevice::DeviceType::TouchScreen,
QInputDevice::Capabilities caps = QInputDevice::Capability::Position);
class Q_GUI_EXPORT QTouchEventSequence
{
public:
virtual ~QTouchEventSequence();
QTouchEventSequence& press(int touchId, const QPoint &pt, QWindow *window = nullptr);
QTouchEventSequence& move(int touchId, const QPoint &pt, QWindow *window = nullptr);
QTouchEventSequence& release(int touchId, const QPoint &pt, QWindow *window = nullptr);
virtual QTouchEventSequence& stationary(int touchId);
virtual void commit(bool processEvents = true);
protected:
QTouchEventSequence(QWindow *window, QPointingDevice *aDevice, bool autoCommit);
QPoint mapToScreen(QWindow *window, const QPoint &pt);
QEventPoint &point(int touchId);
QEventPoint &pointOrPreviousPoint(int touchId);
QMap<int, QEventPoint> previousPoints;
QMap<int, QEventPoint> points;
QWindow *targetWindow;
QPointingDevice *device;
bool commitWhenDestroyed;
friend QTouchEventSequence touchEvent(QWindow *window, QPointingDevice *device, bool autoCommit);
};
} // namespace QTest
QT_END_NAMESPACE

View File

@ -56,177 +56,22 @@
#include <QtGui/qpointingdevice.h>
#ifdef QT_WIDGETS_LIB
#include <QtWidgets/qwidget.h>
#include <QtWidgets/qtestsupport_widgets.h>
#else
#include <QtGui/qtestsupport_gui.h>
#endif
QT_BEGIN_NAMESPACE
Q_GUI_EXPORT void qt_handleTouchEvent(QWindow *w, const QPointingDevice *device,
const QList<QEventPoint> &points,
Qt::KeyboardModifiers mods = Qt::NoModifier);
namespace QTest
{
Q_GUI_EXPORT QPointingDevice * createTouchDevice(QInputDevice::DeviceType devType = QInputDevice::DeviceType::TouchScreen,
QInputDevice::Capabilities caps = QInputDevice::Capability::Position);
class QTouchEventSequence
{
public:
~QTouchEventSequence()
{
if (commitWhenDestroyed)
commit();
}
QTouchEventSequence& press(int touchId, const QPoint &pt, QWindow *window = nullptr)
{
QEventPoint &p = point(touchId);
p.m_globalPos = mapToScreen(window, pt);
p.m_state = QEventPoint::State::Pressed;
return *this;
}
QTouchEventSequence& move(int touchId, const QPoint &pt, QWindow *window = nullptr)
{
QEventPoint &p = point(touchId);
p.m_globalPos = mapToScreen(window, pt);
p.m_state = QEventPoint::State::Updated;
return *this;
}
QTouchEventSequence& release(int touchId, const QPoint &pt, QWindow *window = nullptr)
{
QEventPoint &p = point(touchId);
p.m_globalPos = mapToScreen(window, pt);
p.m_state = QEventPoint::State::Released;
return *this;
}
QTouchEventSequence& stationary(int touchId)
{
QEventPoint &p = pointOrPreviousPoint(touchId);
p.m_state = QEventPoint::State::Stationary;
return *this;
}
#ifdef QT_WIDGETS_LIB
QTouchEventSequence& press(int touchId, const QPoint &pt, QWidget *widget = nullptr)
{
QEventPoint &p = point(touchId);
p.m_globalPos = mapToScreen(widget, pt);
p.m_state = QEventPoint::State::Pressed;
return *this;
}
QTouchEventSequence& move(int touchId, const QPoint &pt, QWidget *widget = nullptr)
{
QEventPoint &p = point(touchId);
p.m_globalPos = mapToScreen(widget, pt);
p.m_state = QEventPoint::State::Updated;
return *this;
}
QTouchEventSequence& release(int touchId, const QPoint &pt, QWidget *widget = nullptr)
{
QEventPoint &p = point(touchId);
p.m_globalPos = mapToScreen(widget, pt);
p.m_state = QEventPoint::State::Released;
return *this;
}
#endif
void commit(bool processEvents = true)
{
if (!points.isEmpty()) {
qSleep(1);
if (targetWindow)
{
qt_handleTouchEvent(targetWindow, device, points.values());
}
#ifdef QT_WIDGETS_LIB
else if (targetWidget)
{
qt_handleTouchEvent(targetWidget->windowHandle(), device, points.values());
}
#endif
}
if (processEvents)
QCoreApplication::processEvents();
previousPoints = points;
points.clear();
}
private:
#ifdef QT_WIDGETS_LIB
QTouchEventSequence(QWidget *widget, QPointingDevice *aDevice, bool autoCommit)
: targetWidget(widget), targetWindow(nullptr), device(aDevice), commitWhenDestroyed(autoCommit)
{
}
#endif
QTouchEventSequence(QWindow *window, QPointingDevice *aDevice, bool autoCommit)
:
#ifdef QT_WIDGETS_LIB
targetWidget(nullptr),
#endif
targetWindow(window), device(aDevice), commitWhenDestroyed(autoCommit)
{
}
#ifdef QT_WIDGETS_LIB
QPoint mapToScreen(QWidget *widget, const QPoint &pt)
{
if (widget)
return widget->mapToGlobal(pt);
return targetWidget ? targetWidget->mapToGlobal(pt) : pt;
}
#endif
QPoint mapToScreen(QWindow *window, const QPoint &pt)
{
if(window)
return window->mapToGlobal(pt);
return targetWindow ? targetWindow->mapToGlobal(pt) : pt;
}
QMap<int, QEventPoint> previousPoints;
QMap<int, QEventPoint> points;
#ifdef QT_WIDGETS_LIB
QWidget *targetWidget;
#endif
QWindow *targetWindow;
QPointingDevice *device;
bool commitWhenDestroyed;
#if defined(QT_WIDGETS_LIB) || defined(Q_CLANG_QDOC)
friend QTouchEventSequence touchEvent(QWidget *widget, QPointingDevice *device, bool autoCommit);
#endif
friend QTouchEventSequence touchEvent(QWindow *window, QPointingDevice *device, bool autoCommit);
protected:
// These don't make sense for public testing API,
// because we are getting rid of most public setters in QEventPoint.
// Each of these constructs a QEventPoint with null parent; in normal usage,
// the QTouchEvent constructor will set the points' parents to itself, later on.
QEventPoint &point(int touchId)
{
if (!points.contains(touchId))
points[touchId] = QEventPoint(touchId);
return points[touchId];
}
QEventPoint &pointOrPreviousPoint(int touchId)
{
if (!points.contains(touchId)) {
if (previousPoints.contains(touchId))
points[touchId] = previousPoints.value(touchId);
else
points[touchId] = QEventPoint(touchId);
}
return points[touchId];
}
};
#if defined(QT_WIDGETS_LIB) || defined(Q_CLANG_QDOC)
inline
QTouchEventSequence touchEvent(QWidget *widget,
QTouchEventWidgetSequence touchEvent(QWidget *widget,
QPointingDevice *device,
bool autoCommit = true)
{
return QTouchEventSequence(widget, device, autoCommit);
return QTouchEventWidgetSequence(widget, device, autoCommit);
}
#endif
inline

View File

@ -42,7 +42,10 @@
#include "qwidget.h"
#include <QtGui/qwindow.h>
#include <QtCore/qtestsupport_core.h>
#include <QtCore/qthread.h>
#include <QtGui/qtestsupport_gui.h>
#include <QtGui/private/qevent_p.h>
QT_BEGIN_NAMESPACE
@ -87,4 +90,71 @@ Q_WIDGETS_EXPORT bool QTest::qWaitForWindowExposed(QWidget *widget, int timeout)
return false;
}
namespace QTest {
QTouchEventWidgetSequence::~QTouchEventWidgetSequence()
{
if (commitWhenDestroyed)
commit();
}
QTouchEventWidgetSequence& QTouchEventWidgetSequence::press(int touchId, const QPoint &pt, QWidget *widget)
{
auto &p = QMutableEventPoint::from(point(touchId));
p.setGlobalPosition(mapToScreen(widget, pt));
p.setState(QEventPoint::State::Pressed);
return *this;
}
QTouchEventWidgetSequence& QTouchEventWidgetSequence::move(int touchId, const QPoint &pt, QWidget *widget)
{
auto &p = QMutableEventPoint::from(point(touchId));
p.setGlobalPosition(mapToScreen(widget, pt));
p.setState(QEventPoint::State::Updated);
return *this;
}
QTouchEventWidgetSequence& QTouchEventWidgetSequence::release(int touchId, const QPoint &pt, QWidget *widget)
{
auto &p = QMutableEventPoint::from(point(touchId));
p.setGlobalPosition(mapToScreen(widget, pt));
p.setState(QEventPoint::State::Released);
return *this;
}
QTouchEventWidgetSequence& QTouchEventWidgetSequence::stationary(int touchId)
{
auto &p = QMutableEventPoint::from(pointOrPreviousPoint(touchId));
p.setState(QEventPoint::State::Stationary);
return *this;
}
void QTouchEventWidgetSequence::commit(bool processEvents)
{
if (points.isEmpty())
return;
QThread::msleep(1);
if (targetWindow) {
qt_handleTouchEvent(targetWindow, device, points.values());
} else if (targetWidget) {
qt_handleTouchEvent(targetWidget->windowHandle(), device, points.values());
}
if (processEvents)
QCoreApplication::processEvents();
previousPoints = points;
points.clear();
}
QTest::QTouchEventWidgetSequence::QTouchEventWidgetSequence(QWidget *widget, QPointingDevice *aDevice, bool autoCommit)
: QTouchEventSequence(nullptr, aDevice, autoCommit), targetWidget(widget)
{
}
QPoint QTouchEventWidgetSequence::mapToScreen(QWidget *widget, const QPoint &pt)
{
if (widget)
return widget->mapToGlobal(pt);
return targetWidget ? targetWidget->mapToGlobal(pt) : pt;
}
} // namespace QTest
QT_END_NAMESPACE

View File

@ -41,15 +41,40 @@
#define QTESTSUPPORT_WIDGETS_H
#include <QtWidgets/qtwidgetsglobal.h>
#include <QtGui/qtestsupport_gui.h>
QT_BEGIN_NAMESPACE
class QPointingDevice;
class QWidget;
namespace QTest {
Q_REQUIRED_RESULT Q_WIDGETS_EXPORT bool qWaitForWindowActive(QWidget *widget, int timeout = 5000);
Q_REQUIRED_RESULT Q_WIDGETS_EXPORT bool qWaitForWindowExposed(QWidget *widget, int timeout = 5000);
}
class Q_WIDGETS_EXPORT QTouchEventWidgetSequence : public QTouchEventSequence
{
public:
~QTouchEventWidgetSequence() override;
QTouchEventWidgetSequence& press(int touchId, const QPoint &pt, QWidget *widget = nullptr);
QTouchEventWidgetSequence& move(int touchId, const QPoint &pt, QWidget *widget = nullptr);
QTouchEventWidgetSequence& release(int touchId, const QPoint &pt, QWidget *widget = nullptr);
QTouchEventWidgetSequence& stationary(int touchId) override;
void commit(bool processEvents = true) override;
private:
QTouchEventWidgetSequence(QWidget *widget, QPointingDevice *aDevice, bool autoCommit);
QPoint mapToScreen(QWidget *widget, const QPoint &pt);
QWidget *targetWidget = nullptr;
friend QTouchEventWidgetSequence touchEvent(QWidget *widget, QPointingDevice *device, bool autoCommit);
};
} // namespace QTest
QT_END_NAMESPACE

View File

@ -32,7 +32,7 @@
#include <QtWidgets/QGraphicsView>
#include <QtWidgets/QGraphicsWidget>
#include <QtWidgets/QWidget>
#include <QtTest>
#include <QtTest/QtTest>
#include <qpa/qwindowsysteminterface.h>
#include <qpa/qwindowsysteminterface_p.h>
#include <private/qevent_p.h>
@ -1655,11 +1655,11 @@ void tst_QTouchEvent::crashInQGraphicsSceneAfterNotHandlingTouchBegin()
QPoint centerPos = view.mapFromScene(rect->boundingRect().center());
// Touch the button
QTest::touchEvent(view.viewport(), touchScreenDevice).press(0, centerPos, static_cast<QWindow *>(0));
QTest::touchEvent(view.viewport(), touchScreenDevice).release(0, centerPos, static_cast<QWindow *>(0));
QTest::touchEvent(view.viewport(), touchScreenDevice).press(0, centerPos, nullptr);
QTest::touchEvent(view.viewport(), touchScreenDevice).release(0, centerPos, nullptr);
// Touch outside of the button
QTest::touchEvent(view.viewport(), touchScreenDevice).press(0, view.mapFromScene(QPoint(10, 10)), static_cast<QWindow *>(0));
QTest::touchEvent(view.viewport(), touchScreenDevice).release(0, view.mapFromScene(QPoint(10, 10)), static_cast<QWindow *>(0));
QTest::touchEvent(view.viewport(), touchScreenDevice).press(0, view.mapFromScene(QPoint(10, 10)), nullptr);
QTest::touchEvent(view.viewport(), touchScreenDevice).release(0, view.mapFromScene(QPoint(10, 10)), nullptr);
}
void tst_QTouchEvent::touchBeginWithGraphicsWidget()

View File

@ -126,7 +126,7 @@ bool TestWidget::event(QEvent * event)
return QWidget::event(event);
}
static void pressSequence(QTest::QTouchEventSequence &sequence, QList<QPoint> &points,
static void pressSequence(QTest::QTouchEventWidgetSequence &sequence, QList<QPoint> &points,
QWidget *widget)
{
const int pointCount = points.size();
@ -135,7 +135,7 @@ static void pressSequence(QTest::QTouchEventSequence &sequence, QList<QPoint> &p
sequence.commit();
}
static void linearSequence(int n, const QPoint &delta, QTest::QTouchEventSequence &sequence,
static void linearSequence(int n, const QPoint &delta, QTest::QTouchEventWidgetSequence &sequence,
QList<QPoint> &points, QWidget *widget)
{
const int pointCount = points.size();
@ -148,7 +148,7 @@ static void linearSequence(int n, const QPoint &delta, QTest::QTouchEventSequenc
}
}
static void releaseSequence(QTest::QTouchEventSequence &sequence, QList<QPoint> &points,
static void releaseSequence(QTest::QTouchEventWidgetSequence &sequence, QList<QPoint> &points,
QWidget *widget)
{
const int pointCount = points.size();
@ -188,7 +188,7 @@ void tst_QGestureRecognizer::panGesture()
for (int i = 0; i < panPoints; ++i)
points.append(QPoint(10 + i *20, 10 + i *20));
QTest::QTouchEventSequence panSequence = QTest::touchEvent(&widget, m_touchDevice);
QTest::QTouchEventWidgetSequence panSequence = QTest::touchEvent(&widget, m_touchDevice);
pressSequence(panSequence, points, &widget);
linearSequence(5, QPoint(20, 20), panSequence, points, &widget);
releaseSequence(panSequence, points, &widget);
@ -231,7 +231,7 @@ void tst_QGestureRecognizer::pinchGesture()
points.append(widget.rect().center());
points.append(points.front() + QPoint(0, 20));
QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(&widget, m_touchDevice);
QTest::QTouchEventWidgetSequence pinchSequence = QTest::touchEvent(&widget, m_touchDevice);
pressSequence(pinchSequence, points, &widget);
for (int s = 0; s < 5; ++s) {
@ -288,7 +288,7 @@ void tst_QGestureRecognizer::swipeGesture()
for (int i = 0; i < swipePoints - 1; ++i)
points.append(fingerDistance + i * fingerDistance);
QTest::QTouchEventSequence swipeSequence = QTest::touchEvent(&widget, m_touchDevice);
QTest::QTouchEventWidgetSequence swipeSequence = QTest::touchEvent(&widget, m_touchDevice);
pressSequence(swipeSequence, points, &widget);
// Press point #3