summaryrefslogtreecommitdiffstats
path: root/src/widgets
diff options
context:
space:
mode:
authorShawn Rutledge <shawn.rutledge@qt.io>2020-09-03 21:34:31 +0200
committerShawn Rutledge <shawn.rutledge@qt.io>2020-09-16 11:33:03 +0200
commit2692237bb1b0c0f50b7cc5d920eb8ab065063d47 (patch)
tree8f114b0313fcf04f128e265efaaabc8a130b2f6e /src/widgets
parent4ceef8a3a6d64474344cac4a6ed4b32b09d38367 (diff)
Track grab state in QPointingDevicePrivate::activePoints
QQuickEventPoint instances were very long-lived and got reused from one event to the next. That was initially done because they were "heavy" QObjects; but it also became useful to store state in them between events. But this is in conflict with the ubiquitous event replay code that assumes it's OK to hold an event instance (especially a QMouseEvent) for any length of time, and then send it to some widget, item or window. Clearly QEventPoints must be stored in the QPointerEvent, if we are to avoid the need for workarounds to keep such old code working. And now they have d-pointers, so copying is cheap. But replay code will need to detach() their QEventPoints now. QEventPoint is useful as an object to hold state, but we now store the truly persistent state separately in an EventPointData struct, in QPointingDevicePrivate::activePoints. Incoming events merely update the persistent points, then we deliver those instead. Thus when event handler code modifies state, it will be remembered even when the delivery is done and the QPA event is destroyed. This gets us a step closer to supporting multiple simultaneous mice. Within pointer events, the points are moved up to QPointerEvent itself: QList<QEventPoint> m_points; This means pointCount(), point(int i) and points() can be non-virtual. However in any QSinglePointEvent, the list only contains one point. We hope that pessimization is worthwhile for the sake of removing virtual functions, simplifying code in event classes themselves, and enabling the use of the range-for loop over points() with any kind of QPointerEvent, not just QTouchEvent. points() is a nicer API for the sake of range-for looping; but point() is more suited to being non-const. In QML it's expected to be OK to emit a signal with a QPointerEvent by value: that will involve copying the event. But QEventPoint instances are explicitly shared, so calling setAccepted() modifies the instance in activePoints (EventPointData.eventPoint.d->accept); and the grabbers are stored separately and thus preserved between events. In code such as MouseArea { onPressed: mouse.accepted = false } we can either continue to emit the QQuickMouseEvent wrapper or perhaps QEvent::setAccepted() could become virtual and set the eventpoint's accepted flag instead, so that it will survive after the event copy that QML sees is discarded. The grabChanged() signal is useful to keep QQuickWindow informed when items or handlers change exclusive or passive grabbers. When a release happens at a different location than the last move event, Qt synthesizes an additional move. But it would be "boring" if QEventPoint::lastXPosition() accessors in any released eventpoint always returned the same as the current QEventPoint::xPosition()s just because of that; and it would mean that the velocity() must always be zero on release, which would make it hard to use the final velocity to drive an animation. So now we expect the lastPositions to be different than current positions in a released eventpoint. De-inline some functions whose implementations might be subject to change later on. Improve documentation. Since we have an accessor for pressTimestamp(), we might as well add one for timestamp() too. That way users get enough information to calculate instantaneous velocity, since the plan is for velocity() to be somewhat smoothed. Change-Id: I2733d847139a1b1bea33c00275459dcd2a145ffc Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Diffstat (limited to 'src/widgets')
-rw-r--r--src/widgets/graphicsview/qgraphicsscene.cpp4
-rw-r--r--src/widgets/graphicsview/qgraphicsview.cpp3
-rw-r--r--src/widgets/kernel/qapplication.cpp92
-rw-r--r--src/widgets/kernel/qapplication_p.h2
-rw-r--r--src/widgets/kernel/qwidgetwindow.cpp4
5 files changed, 51 insertions, 54 deletions
diff --git a/src/widgets/graphicsview/qgraphicsscene.cpp b/src/widgets/graphicsview/qgraphicsscene.cpp
index 220221a5e9..f272720150 100644
--- a/src/widgets/graphicsview/qgraphicsscene.cpp
+++ b/src/widgets/graphicsview/qgraphicsscene.cpp
@@ -5829,8 +5829,10 @@ void QGraphicsScenePrivate::updateTouchPointsForItem(QGraphicsItem *item, QTouch
const QTransform mapFromScene =
item->d_ptr->genericMapFromSceneTransform(static_cast<const QWidget *>(touchEvent->target()));
- for (QEventPoint &pt : QMutableTouchEvent::from(touchEvent)->touchPoints())
+ for (int i = 0; i < touchEvent->pointCount(); ++i) {
+ auto &pt = QMutableEventPoint::from(touchEvent->point(i));
QMutableEventPoint::from(pt).setPosition(mapFromScene.map(pt.scenePosition()));
+ }
}
int QGraphicsScenePrivate::findClosestTouchPointId(const QPointF &scenePos)
diff --git a/src/widgets/graphicsview/qgraphicsview.cpp b/src/widgets/graphicsview/qgraphicsview.cpp
index 83d1259740..1db7bf222d 100644
--- a/src/widgets/graphicsview/qgraphicsview.cpp
+++ b/src/widgets/graphicsview/qgraphicsview.cpp
@@ -310,7 +310,8 @@ inline int q_round_bound(qreal d) //### (int)(qreal) INT_MAX != INT_MAX for sing
void QGraphicsViewPrivate::translateTouchEvent(QGraphicsViewPrivate *d, QTouchEvent *touchEvent)
{
- for (QEventPoint &pt : QMutableTouchEvent::from(touchEvent)->touchPoints()) {
+ for (int i = 0; i < touchEvent->pointCount(); ++i) {
+ auto &pt = touchEvent->point(i);
// the scene will set the item local pos, startPos, lastPos, and rect before delivering to
// an item, but for now those functions are returning the view's local coordinates
QMutableEventPoint::from(pt).setScenePosition(d->mapToScene(pt.position()));
diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp
index a8590c7903..c230a71550 100644
--- a/src/widgets/kernel/qapplication.cpp
+++ b/src/widgets/kernel/qapplication.cpp
@@ -80,6 +80,7 @@
#include <QtGui/qinputmethod.h>
#include <QtGui/private/qwindow_p.h>
#include <QtGui/qpointingdevice.h>
+#include <QtGui/private/qpointingdevice_p.h>
#include <qpa/qplatformtheme.h>
#if QT_CONFIG(whatsthis)
#include <QtWidgets/QWhatsThis>
@@ -3190,7 +3191,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e)
bool acceptTouchEvents = widget->testAttribute(Qt::WA_AcceptTouchEvents);
if (acceptTouchEvents && e->spontaneous()) {
- const QPoint localPos = touchEvent->touchPoints()[0].position().toPoint();
+ const QPoint localPos = touchEvent->points()[0].position().toPoint();
QApplicationPrivate::giveFocusAccordingToFocusPolicy(widget, e, localPos);
}
@@ -3228,8 +3229,10 @@ bool QApplication::notify(QObject *receiver, QEvent *e)
QPoint offset = widget->pos();
widget = widget->parentWidget();
touchEvent->setTarget(widget);
- for (QEventPoint &pt : touchEvent->touchPoints())
- QMutableEventPoint::from(pt).setPosition(pt.position() + offset);
+ for (int i = 0; i < touchEvent->pointCount(); ++i) {
+ auto &pt = QMutableEventPoint::from(touchEvent->point(i));
+ pt.setPosition(pt.position() + offset);
+ }
}
#ifndef QT_NO_GESTURES
@@ -3487,8 +3490,10 @@ void QApplicationPrivate::closePopup(QWidget *popup)
if (popupGrabOk) {
popupGrabOk = false;
- if (popup->geometry().contains(QPoint(QGuiApplicationPrivate::mousePressX,
- QGuiApplicationPrivate::mousePressY))
+ // TODO on multi-seat window systems, we have to know which mouse
+ auto devPriv = QPointingDevicePrivate::get(QPointingDevice::primaryPointingDevice());
+ auto mousePressPos = devPriv->pointById(0)->eventPoint.globalPressPosition();
+ if (popup->geometry().contains(mousePressPos.toPoint())
|| popup->testAttribute(Qt::WA_NoMouseReplay)) {
// mouse release event or inside
qt_replay_popup_mouse_event = false;
@@ -3876,9 +3881,9 @@ bool QApplicationPrivate::updateTouchPointsForWidget(QWidget *widget, QTouchEven
{
bool containsPress = false;
- for (QEventPoint &pt : QMutableTouchEvent::from(touchEvent)->touchPoints()) {
- const QPointF screenPos = pt.globalPosition();
- QMutableEventPoint::from(pt).setPosition(widget->mapFromGlobal(screenPos));
+ for (int i = 0; i < touchEvent->pointCount(); ++i) {
+ auto &pt = QMutableEventPoint::from(touchEvent->point(i));
+ pt.setPosition(widget->mapFromGlobal(pt.globalPosition()));
if (pt.state() == QEventPoint::State::Pressed)
containsPress = true;
@@ -3906,25 +3911,23 @@ void QApplicationPrivate::cleanupMultitouch_sys()
QWidget *QApplicationPrivate::findClosestTouchPointTarget(const QPointingDevice *device, const QEventPoint &touchPoint)
{
- const QPointF screenPos = touchPoint.globalPosition();
+ const QPointF globalPos = touchPoint.globalPosition();
int closestTouchPointId = -1;
QObject *closestTarget = nullptr;
- qreal closestDistance = qreal(0.);
- QHash<ActiveTouchPointsKey, ActiveTouchPointsValue>::const_iterator it = activeTouchPoints.constBegin(),
- ite = activeTouchPoints.constEnd();
- while (it != ite) {
- if (it.key().device == device && it.key().touchPointId != touchPoint.id()) {
- const QEventPoint &touchPoint = it->touchPoint;
- qreal dx = screenPos.x() - touchPoint.globalPosition().x();
- qreal dy = screenPos.y() - touchPoint.globalPosition().y();
+ qreal closestDistance = 0;
+ const QPointingDevicePrivate *devPriv = QPointingDevicePrivate::get(device);
+ for (const auto &pair : devPriv->activePoints) {
+ const auto &pt = pair.second.eventPoint;
+ if (pt.id() != touchPoint.id()) {
+ qreal dx = globalPos.x() - pt.globalPosition().x();
+ qreal dy = globalPos.y() - pt.globalPosition().y();
qreal distance = dx * dx + dy * dy;
if (closestTouchPointId == -1 || distance < closestDistance) {
- closestTouchPointId = touchPoint.id();
+ closestTouchPointId = pt.id();
closestDistance = distance;
- closestTarget = it.value().target.data();
+ closestTarget = static_cast<const QMutableEventPoint &>(pt).target();
}
}
- ++it;
}
return static_cast<QWidget *>(closestTarget);
}
@@ -3934,16 +3937,14 @@ void QApplicationPrivate::activateImplicitTouchGrab(QWidget *widget, QTouchEvent
if (touchEvent->type() != QEvent::TouchBegin)
return;
- for (int i = 0, tc = touchEvent->touchPoints().count(); i < tc; ++i) {
- const QEventPoint &touchPoint = touchEvent->touchPoints().at(i);
- activeTouchPoints[QGuiApplicationPrivate::ActiveTouchPointsKey(
- touchEvent->pointingDevice(), touchPoint.id())].target = widget;
- }
+ for (int i = 0; i < touchEvent->pointCount(); ++i)
+ QMutableEventPoint::from(touchEvent->point(i)).setTarget(widget);
+ // TODO setExclusiveGrabber() to be consistent with Qt Quick?
}
bool QApplicationPrivate::translateRawTouchEvent(QWidget *window,
const QPointingDevice *device,
- const QList<QEventPoint> &touchPoints,
+ QList<QEventPoint> &touchPoints,
ulong timestamp)
{
QApplicationPrivate *d = self;
@@ -3951,26 +3952,17 @@ bool QApplicationPrivate::translateRawTouchEvent(QWidget *window,
typedef QPair<QEventPoint::State, QList<QEventPoint> > StatesAndTouchPoints;
QHash<QWidget *, StatesAndTouchPoints> widgetsNeedingEvents;
- for (int i = 0; i < touchPoints.count(); ++i) {
- QEventPoint touchPoint = touchPoints.at(i);
-
+ for (auto &touchPoint : touchPoints) {
// update state
QPointer<QObject> target;
- ActiveTouchPointsKey touchInfoKey(device, touchPoint.id());
- ActiveTouchPointsValue &touchInfo = d->activeTouchPoints[touchInfoKey];
if (touchPoint.state() == QEventPoint::State::Pressed) {
- if (device->type() == QInputDevice::DeviceType::TouchPad && !d->activeTouchPoints.isEmpty()) {
- // on touch-pads, send all touch points to the same widget
+ if (device->type() == QInputDevice::DeviceType::TouchPad) {
+ // on touchpads, send all touch points to the same widget:
// pick the first non-null target if possible
- for (const auto &a : d->activeTouchPoints.values()) {
- if (a.target) {
- target = a.target;
- break;
- }
- }
+ target = QPointingDevicePrivate::get(device)->firstActiveTarget();
}
- if (!target) {
+ if (target.isNull()) {
// determine which widget this event will go to
if (!window)
window = QApplication::topLevelAt(touchPoint.globalPosition().toPoint());
@@ -3990,13 +3982,13 @@ bool QApplicationPrivate::translateRawTouchEvent(QWidget *window,
}
}
- touchInfo.target = target;
+ QMutableEventPoint::from(touchPoint).setTarget(target);
} else {
- target = touchInfo.target;
+ target = QMutableEventPoint::from(touchPoint).target();
if (!target)
continue;
}
- Q_ASSERT(target.data() != nullptr);
+ Q_ASSERT(!target.isNull());
QWidget *targetWidget = static_cast<QWidget *>(target.data());
@@ -4084,14 +4076,14 @@ void QApplicationPrivate::translateTouchCancel(const QPointingDevice *device, ul
{
QMutableTouchEvent touchEvent(QEvent::TouchCancel, device, QGuiApplication::keyboardModifiers());
touchEvent.setTimestamp(timestamp);
- QHash<ActiveTouchPointsKey, ActiveTouchPointsValue>::const_iterator it
- = self->activeTouchPoints.constBegin(), ite = self->activeTouchPoints.constEnd();
+
QSet<QWidget *> widgetsNeedingCancel;
- while (it != ite) {
- QWidget *widget = static_cast<QWidget *>(it->target.data());
- if (widget)
- widgetsNeedingCancel.insert(widget);
- ++it;
+ const QPointingDevicePrivate *devPriv = QPointingDevicePrivate::get(device);
+ for (const auto &pair : devPriv->activePoints) {
+ const auto &pt = pair.second.eventPoint;
+ QObject *target = static_cast<const QMutableEventPoint &>(pt).target();
+ if (target && target->isWidgetType())
+ widgetsNeedingCancel.insert(static_cast<QWidget *>(target));
}
for (QSet<QWidget *>::const_iterator widIt = widgetsNeedingCancel.constBegin(),
widItEnd = widgetsNeedingCancel.constEnd(); widIt != widItEnd; ++widIt) {
diff --git a/src/widgets/kernel/qapplication_p.h b/src/widgets/kernel/qapplication_p.h
index 971ade7284..354cac0c9a 100644
--- a/src/widgets/kernel/qapplication_p.h
+++ b/src/widgets/kernel/qapplication_p.h
@@ -252,7 +252,7 @@ public:
void activateImplicitTouchGrab(QWidget *widget, QTouchEvent *touchBeginEvent);
static bool translateRawTouchEvent(QWidget *widget,
const QPointingDevice *device,
- const QList<QEventPoint> &touchPoints,
+ QList<QEventPoint> &touchPoints,
ulong timestamp);
static void translateTouchCancel(const QPointingDevice *device, ulong timestamp);
diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp
index a9bf8bee6a..9f5ba27e9e 100644
--- a/src/widgets/kernel/qwidgetwindow.cpp
+++ b/src/widgets/kernel/qwidgetwindow.cpp
@@ -696,7 +696,9 @@ void QWidgetWindow::handleTouchEvent(QTouchEvent *event)
// events instead, which QWidgetWindow::handleMouseEvent will forward correctly:
event->ignore();
} else {
- event->setAccepted(QApplicationPrivate::translateRawTouchEvent(m_widget, event->pointingDevice(), event->touchPoints(), event->timestamp()));
+ event->setAccepted(QApplicationPrivate::translateRawTouchEvent(m_widget, event->pointingDevice(),
+ const_cast<QList<QEventPoint> &>(event->points()),
+ event->timestamp()));
}
}