aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorShawn Rutledge <shawn.rutledge@qt.io>2020-09-30 19:35:26 +0200
committerShawn Rutledge <shawn.rutledge@qt.io>2020-11-11 12:16:15 +0100
commitd7623d79ef4bc9533fced027bf1d173d68b4eba6 (patch)
tree9595924e962c340d8efb9c74c66f053f09c379da /src
parent25c5ace02747753c524960abbf6fb81de1d11c6d (diff)
Teach flickable to handle and replay touch as it does mouse
QQuickWindowPrivate::cloneMouseEvent() renamed to clonePointerEvent() and generalized to be able to clone any of the kinds of QPointerEvent that we're interested in replaying. Now it is used only in QQuickFlickablePrivate::captureDelayedPress(). Reverts f278bb7c66bb00c9f81b7a3aceeb94cb9b3a1b66 and 012a4528a515af8d7ec7dbc05a38d8fd0d6d4d1b (don't skip tst_TouchMouse::buttonOnDelayedPressFlickable). Some test changes from f128b5dee8a2a03ebc55ed0cd1e749a6599282c3 also get reverted. QEventPoint should always have valid velocity now, so Flickable no longer has to calculate it for itself. Removing that became necessary to fix the movingAndFlicking test. Adds logging categories qt.quick.flickable.filter and .replay. Fixes: QTBUG-85607 Task-number: QTBUG-83437 Task-number: QTBUG-78818 Task-number: QTBUG-61144 Task-number: QTBUG-88038 Task-number: QTBUG-88138 Change-Id: I0ed6802dff5e5d1595adddc389642925f1f2c93d Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/quick/items/qquickflickable.cpp223
-rw-r--r--src/quick/items/qquickflickable_p.h3
-rw-r--r--src/quick/items/qquickflickable_p_p.h10
-rw-r--r--src/quick/items/qquickitem.cpp42
-rw-r--r--src/quick/items/qquickwindow.cpp66
-rw-r--r--src/quick/items/qquickwindow_p.h10
6 files changed, 241 insertions, 113 deletions
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index 960b02ea9c..a6171ade78 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -62,6 +62,8 @@
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcHandlerParent)
+Q_LOGGING_CATEGORY(lcFilter, "qt.quick.flickable.filter")
+Q_LOGGING_CATEGORY(lcReplay, "qt.quick.flickable.replay")
// FlickThreshold determines how far the "mouse" must have moved
// before we perform a flick.
@@ -281,7 +283,7 @@ void QQuickFlickablePrivate::init()
qmlobject_connect(&velocityTimeline, QQuickTimeLine, SIGNAL(completed()),
q, QQuickFlickable, SLOT(velocityTimelineCompleted()));
q->setAcceptedMouseButtons(Qt::LeftButton);
- q->setAcceptTouchEvents(false); // rely on mouse events synthesized from touch
+ q->setAcceptTouchEvents(true);
q->setFiltersChildMouseEvents(true);
QQuickItemPrivate *viewportPrivate = QQuickItemPrivate::get(contentItem);
viewportPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
@@ -1032,7 +1034,7 @@ qreal QQuickFlickablePrivate::devicePixelRatio() const
return (window ? window->effectiveDevicePixelRatio() : qApp->devicePixelRatio());
}
-void QQuickFlickablePrivate::handleMousePressEvent(QMouseEvent *event)
+void QQuickFlickablePrivate::handlePressEvent(QPointerEvent *event)
{
Q_Q(QQuickFlickable);
timer.start();
@@ -1060,7 +1062,7 @@ void QQuickFlickablePrivate::handleMousePressEvent(QMouseEvent *event)
}
q->setKeepMouseGrab(stealMouse);
- maybeBeginDrag(computeCurrentTime(event), event->position());
+ maybeBeginDrag(computeCurrentTime(event), event->points().first().position());
}
void QQuickFlickablePrivate::maybeBeginDrag(qint64 currentTimestamp, const QPointF &pressPosn)
@@ -1311,34 +1313,29 @@ void QQuickFlickablePrivate::drag(qint64 currentTimestamp, QEvent::Type eventTyp
lastPos = localPos;
}
-void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event)
+void QQuickFlickablePrivate::handleMoveEvent(QPointerEvent *event)
{
Q_Q(QQuickFlickable);
- if (!interactive || lastPosTime == -1 || event->buttons() == Qt::NoButton)
+ if (!interactive || lastPosTime == -1 ||
+ (event->isSinglePointEvent() && static_cast<QSinglePointEvent *>(event)->buttons() == Qt::NoButton))
return;
qint64 currentTimestamp = computeCurrentTime(event);
- QVector2D deltas = QVector2D(event->position() - pressPos);
+ const auto &firstPoint = event->points().first();
+ const auto &pos = firstPoint.position();
+ QVector2D deltas = QVector2D(pos - q->mapFromGlobal(firstPoint.globalPressPosition()));
bool overThreshold = false;
QVector2D velocity = event->point(0).velocity();
- // TODO guarantee that events always have velocity so that it never needs to be computed here
- if (!event->device()->capabilities().testFlag(QInputDevice::Capability::Velocity)) {
- qint64 lastTimestamp = (lastPos.isNull() ? lastPressTime : lastPosTime);
- if (currentTimestamp == lastTimestamp)
- return; // events are too close together: velocity would be infinite
- qreal elapsed = qreal(currentTimestamp - lastTimestamp) / 1000.;
- velocity = QVector2D(event->position() - (lastPos.isNull() ? pressPos : lastPos)) / elapsed;
- }
if (q->yflick())
- overThreshold |= QQuickWindowPrivate::dragOverThreshold(deltas.y(), Qt::YAxis, event);
+ overThreshold |= QQuickWindowPrivate::dragOverThreshold(deltas.y(), Qt::YAxis, firstPoint);
if (q->xflick())
- overThreshold |= QQuickWindowPrivate::dragOverThreshold(deltas.x(), Qt::XAxis, event);
+ overThreshold |= QQuickWindowPrivate::dragOverThreshold(deltas.x(), Qt::XAxis, firstPoint);
- drag(currentTimestamp, event->type(), event->position(), deltas, overThreshold, false, false, velocity);
+ drag(currentTimestamp, event->type(), pos, deltas, overThreshold, false, false, velocity);
}
-void QQuickFlickablePrivate::handleMouseReleaseEvent(QMouseEvent *event)
+void QQuickFlickablePrivate::handleReleaseEvent(QPointerEvent *event)
{
Q_Q(QQuickFlickable);
stealMouse = false;
@@ -1359,6 +1356,8 @@ void QQuickFlickablePrivate::handleMouseReleaseEvent(QMouseEvent *event)
hData.vTime = vData.vTime = timeline.time();
bool canBoost = false;
+ const auto pos = event->points().first().position();
+ const auto pressPos = event->points().first().pressPosition();
qreal vVelocity = 0;
if (elapsed < 100 && vData.velocity != 0.) {
@@ -1394,7 +1393,7 @@ void QQuickFlickablePrivate::handleMouseReleaseEvent(QMouseEvent *event)
bool flickedVertically = false;
vVelocity *= flickBoost;
- bool isVerticalFlickAllowed = q->yflick() && qAbs(vVelocity) > MinimumFlickVelocity && qAbs(event->position().y() - pressPos.y()) > FlickThreshold;
+ bool isVerticalFlickAllowed = q->yflick() && qAbs(vVelocity) > MinimumFlickVelocity && qAbs(pos.y() - pressPos.y()) > FlickThreshold;
if (isVerticalFlickAllowed) {
velocityTimeline.reset(vData.smoothVelocity);
vData.smoothVelocity.setValue(-vVelocity);
@@ -1403,7 +1402,7 @@ void QQuickFlickablePrivate::handleMouseReleaseEvent(QMouseEvent *event)
bool flickedHorizontally = false;
hVelocity *= flickBoost;
- bool isHorizontalFlickAllowed = q->xflick() && qAbs(hVelocity) > MinimumFlickVelocity && qAbs(event->position().x() - pressPos.x()) > FlickThreshold;
+ bool isHorizontalFlickAllowed = q->xflick() && qAbs(hVelocity) > MinimumFlickVelocity && qAbs(pos.x() - pressPos.x()) > FlickThreshold;
if (isHorizontalFlickAllowed) {
velocityTimeline.reset(hData.smoothVelocity);
hData.smoothVelocity.setValue(-hVelocity);
@@ -1424,9 +1423,9 @@ void QQuickFlickablePrivate::handleMouseReleaseEvent(QMouseEvent *event)
void QQuickFlickable::mousePressEvent(QMouseEvent *event)
{
Q_D(QQuickFlickable);
- if (d->interactive && d->wantsPointerEvent(event)) {
+ if (d->interactive && !d->replayingPressEvent && d->wantsPointerEvent(event)) {
if (!d->pressed)
- d->handleMousePressEvent(event);
+ d->handlePressEvent(event);
event->accept();
} else {
QQuickItem::mousePressEvent(event);
@@ -1437,7 +1436,7 @@ void QQuickFlickable::mouseMoveEvent(QMouseEvent *event)
{
Q_D(QQuickFlickable);
if (d->interactive && d->wantsPointerEvent(event)) {
- d->handleMouseMoveEvent(event);
+ d->handleMoveEvent(event);
event->accept();
} else {
QQuickItem::mouseMoveEvent(event);
@@ -1452,10 +1451,11 @@ void QQuickFlickable::mouseReleaseEvent(QMouseEvent *event)
d->replayDelayedPress();
// Now send the release
- if (window() && window()->mouseGrabberItem()) {
- QPointF localPos = window()->mouseGrabberItem()->mapFromScene(event->scenePosition());
- QScopedPointer<QMouseEvent> mouseEvent(QQuickWindowPrivate::cloneMouseEvent(event, &localPos));
- QCoreApplication::sendEvent(window(), mouseEvent.data());
+ auto &firstPoint = event->point(0);
+ if (auto grabber = qmlobject_cast<QQuickItem *>(event->exclusiveGrabber(firstPoint))) {
+ QMouseEvent localized(*event);
+ QMutableEventPoint::from(firstPoint).setPosition(grabber->mapFromScene(event->scenePosition()));
+ QCoreApplication::sendEvent(window(), &localized);
}
// And the event has been consumed
@@ -1464,13 +1464,69 @@ void QQuickFlickable::mouseReleaseEvent(QMouseEvent *event)
return;
}
- d->handleMouseReleaseEvent(event);
+ d->handleReleaseEvent(event);
event->accept();
} else {
QQuickItem::mouseReleaseEvent(event);
}
}
+void QQuickFlickable::touchEvent(QTouchEvent *event)
+{
+ Q_D(QQuickFlickable);
+ bool unhandled = false;
+ const auto &firstPoint = event->points().first();
+ switch (firstPoint.state()) {
+ case QEventPoint::State::Pressed:
+ if (d->interactive && !d->replayingPressEvent && d->wantsPointerEvent(event)) {
+ if (!d->pressed)
+ d->handlePressEvent(event);
+ event->accept();
+ } else {
+ unhandled = true;
+ }
+ break;
+ case QEventPoint::State::Updated:
+ if (d->interactive && d->wantsPointerEvent(event)) {
+ d->handleMoveEvent(event);
+ event->accept();
+ } else {
+ unhandled = true;
+ }
+ break;
+ case QEventPoint::State::Released:
+ if (d->interactive && d->wantsPointerEvent(event)) {
+ if (d->delayedPressEvent) {
+ d->replayDelayedPress();
+
+ // Now send the release
+ auto &firstPoint = event->point(0);
+ if (auto grabber = qmlobject_cast<QQuickItem *>(event->exclusiveGrabber(firstPoint))) {
+ const auto localPos = grabber->mapFromScene(firstPoint.scenePosition());
+ QScopedPointer<QPointerEvent> localizedEvent(QQuickWindowPrivate::clonePointerEvent(event, localPos));
+ QCoreApplication::sendEvent(window(), localizedEvent.data());
+ }
+
+ // And the event has been consumed
+ d->stealMouse = false;
+ d->pressed = false;
+ return;
+ }
+
+ d->handleReleaseEvent(event);
+ event->accept();
+ } else {
+ unhandled = true;
+ }
+ break;
+ case QEventPoint::State::Stationary:
+ case QEventPoint::State::Unknown:
+ break;
+ }
+ if (unhandled)
+ QQuickItem::touchEvent(event);
+}
+
#if QT_CONFIG(wheelevent)
void QQuickFlickable::wheelEvent(QWheelEvent *event)
{
@@ -1595,7 +1651,7 @@ bool QQuickFlickablePrivate::isInnermostPressDelay(QQuickItem *i) const
return false;
}
-void QQuickFlickablePrivate::captureDelayedPress(QQuickItem *item, QMouseEvent *event)
+void QQuickFlickablePrivate::captureDelayedPress(QQuickItem *item, QPointerEvent *event)
{
Q_Q(QQuickFlickable);
if (!q->window() || pressDelay <= 0)
@@ -1606,15 +1662,17 @@ void QQuickFlickablePrivate::captureDelayedPress(QQuickItem *item, QMouseEvent *
if (!isInnermostPressDelay(item))
return;
- delayedPressEvent = QQuickWindowPrivate::cloneMouseEvent(event);
+ delayedPressEvent = QQuickWindowPrivate::clonePointerEvent(event);
delayedPressEvent->setAccepted(false);
delayedPressTimer.start(pressDelay, q);
+ qCDebug(lcReplay) << "begin press delay" << pressDelay << "ms with" << delayedPressEvent;
}
void QQuickFlickablePrivate::clearDelayedPress()
{
if (delayedPressEvent) {
delayedPressTimer.stop();
+ qCDebug(lcReplay) << "clear delayed press" << delayedPressEvent;
delete delayedPressEvent;
delayedPressEvent = nullptr;
}
@@ -1625,20 +1683,35 @@ void QQuickFlickablePrivate::replayDelayedPress()
Q_Q(QQuickFlickable);
if (delayedPressEvent) {
// Losing the grab will clear the delayed press event; take control of it here
- QScopedPointer<QMouseEvent> mouseEvent(delayedPressEvent);
+ QScopedPointer<QPointerEvent> event(delayedPressEvent);
delayedPressEvent = nullptr;
delayedPressTimer.stop();
// If we have the grab, release before delivering the event
- if (QQuickWindow *w = q->window()) {
- QQuickWindowPrivate *wpriv = QQuickWindowPrivate::get(w);
+ if (QQuickWindow *window = q->window()) {
+ QQuickWindowPrivate *wpriv = QQuickWindowPrivate::get(window);
wpriv->allowChildEventFiltering = false; // don't allow re-filtering during replay
replayingPressEvent = true;
- if (w->mouseGrabberItem() == q)
- q->ungrabMouse();
-
- // Use the event handler that will take care of finding the proper item to propagate the event
- QCoreApplication::sendEvent(w, mouseEvent.data());
+ auto &firstPoint = event->point(0);
+ // At first glance, it's weird for delayedPressEvent to already have a grabber;
+ // but on press, filterMouseEvent() took the exclusive grab, and that's stored
+ // in the device-specific EventPointData instance in QPointingDevicePrivate::activePoints,
+ // not in the event itself. If this Flickable is still the grabber of that point on that device,
+ // that's the reason; but now it doesn't need that grab anymore.
+ if (event->exclusiveGrabber(firstPoint) == q)
+ event->setExclusiveGrabber(firstPoint, nullptr);
+
+ qCDebug(lcReplay) << "replaying" << event.data();
+ // Put scenePosition into position, for the sake of QQuickWindowPrivate::translateTouchEvent()
+ // TODO remove this if we remove QQuickWindowPrivate::translateTouchEvent()
+ QMutableEventPoint::from(firstPoint).setPosition(firstPoint.scenePosition());
+ // Send it through like a fresh press event, and let QQuickWindow
+ // (more specifically, QQuickWindowPrivate::deliverPressOrReleaseEvent)
+ // find the item or handler that should receive it, as usual.
+ QCoreApplication::sendEvent(window, event.data());
+ qCDebug(lcReplay) << "replay done";
+
+ // We're done with replay, go back to normal delivery behavior
replayingPressEvent = false;
wpriv->allowChildEventFiltering = true;
}
@@ -2372,47 +2445,53 @@ void QQuickFlickablePrivate::addPointerHandler(QQuickPointerHandler *h)
QQuickItemPrivate::get(contentItem)->addPointerHandler(h);
}
-/*!
- QQuickFlickable::filterMouseEvent checks filtered mouse events and potentially steals them.
+/*! \internal
+ QQuickFlickable::filterPointerEvent filters pointer events intercepted on the way
+ to the child \a receiver, and potentially steals the exclusive grab.
- This is how flickable takes over events from other items (\a receiver) that are on top of it.
- It filters their events and may take over (grab) the \a event.
- Return true if the mouse event will be stolen.
- \internal
+ This is how flickable takes over the handling of events from child items.
+
+ Returns true if the event will be stolen and should <em>not</em> be delivered to the \a receiver.
*/
-bool QQuickFlickable::filterMouseEvent(QQuickItem *receiver, QMouseEvent *event)
+bool QQuickFlickable::filterPointerEvent(QQuickItem *receiver, QPointerEvent *event)
{
+ if (!(QQuickWindowPrivate::isMouseEvent(event) ||
+ QQuickWindowPrivate::isTouchEvent(event) ||
+ QQuickWindowPrivate::isTabletEvent(event)))
+ return false; // don't filter hover events or wheel events, for example
Q_ASSERT_X(receiver != this, "", "Flickable received a filter event for itself");
+ qCDebug(lcFilter) << objectName() << "filtering" << event << "for" << receiver;
Q_D(QQuickFlickable);
- QPointF localPos = mapFromScene(event->scenePosition());
+ const auto &firstPoint = event->points().first();
+ QPointF localPos = mapFromScene(firstPoint.scenePosition());
bool receiverDisabled = receiver && !receiver->isEnabled();
bool stealThisEvent = d->stealMouse;
bool receiverKeepsGrab = receiver && (receiver->keepMouseGrab() || receiver->keepTouchGrab());
if ((stealThisEvent || contains(localPos)) && (!receiver || !receiverKeepsGrab || receiverDisabled)) {
- QScopedPointer<QMouseEvent> mouseEvent(QQuickWindowPrivate::cloneMouseEvent(event, &localPos));
- mouseEvent->setAccepted(false);
-
- switch (mouseEvent->type()) {
- case QEvent::MouseMove:
- d->handleMouseMoveEvent(mouseEvent.data());
+ QScopedPointer<QPointerEvent> localizedEvent(QQuickWindowPrivate::clonePointerEvent(event, localPos));
+ localizedEvent->setAccepted(false);
+ switch (firstPoint.state()) {
+ case QEventPoint::State::Updated:
+ d->handleMoveEvent(localizedEvent.data());
break;
- case QEvent::MouseButtonPress:
- d->handleMousePressEvent(mouseEvent.data());
+ case QEventPoint::State::Pressed:
+ d->handlePressEvent(localizedEvent.data());
d->captureDelayedPress(receiver, event);
stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
break;
- case QEvent::MouseButtonRelease:
- d->handleMouseReleaseEvent(mouseEvent.data());
+ case QEventPoint::State::Released:
+ d->handleReleaseEvent(localizedEvent.data());
stealThisEvent = d->stealMouse;
break;
- default:
+ case QEventPoint::State::Stationary:
+ case QEventPoint::State::Unknown:
break;
}
if ((receiver && stealThisEvent && !receiverKeepsGrab && receiver != this) || receiverDisabled) {
d->clearDelayedPress();
- grabMouse();
+ event->setExclusiveGrabber(firstPoint, this);
} else if (d->delayedPressEvent) {
- grabMouse();
+ event->setExclusiveGrabber(firstPoint, this);
}
const bool filtered = stealThisEvent || d->delayedPressEvent || receiverDisabled;
@@ -2424,7 +2503,7 @@ bool QQuickFlickable::filterMouseEvent(QQuickItem *receiver, QMouseEvent *event)
d->lastPosTime = -1;
returnToBounds();
}
- if (event->type() == QEvent::MouseButtonRelease || (receiverKeepsGrab && !receiverDisabled)) {
+ if (firstPoint.state() == QEventPoint::State::Released || (receiverKeepsGrab && !receiverDisabled)) {
// mouse released, or another item has claimed the grab
d->lastPosTime = -1;
d->clearDelayedPress();
@@ -2434,7 +2513,10 @@ bool QQuickFlickable::filterMouseEvent(QQuickItem *receiver, QMouseEvent *event)
return false;
}
-
+/*! \internal
+ Despite the name, this function filters all pointer events on their way to any child within.
+ Returns true if the event will be stolen and should <em>not</em> be delivered to the \a receiver.
+*/
bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e)
{
Q_D(QQuickFlickable);
@@ -2444,19 +2526,12 @@ bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e)
return QQuickItem::childMouseEventFilter(i, e);
}
- switch (e->type()) {
- case QEvent::MouseButtonPress:
- case QEvent::MouseMove:
- case QEvent::MouseButtonRelease:
- return filterMouseEvent(i, static_cast<QMouseEvent *>(e));
- case QEvent::UngrabMouse:
- if (d->window && d->window->mouseGrabberItem() && d->window->mouseGrabberItem() != this) {
- // The grab has been taken away from a child and given to some other item.
- mouseUngrabEvent();
- }
- break;
- default:
- break;
+ if (e->isPointerEvent()) {
+ return filterPointerEvent(i, static_cast<QPointerEvent *>(e));
+ } else if (e->type() == QEvent::UngrabMouse && d->window &&
+ d->window->mouseGrabberItem() && d->window->mouseGrabberItem() != this) {
+ // The grab has been taken away from a child and given to some other item.
+ mouseUngrabEvent();
}
return QQuickItem::childMouseEventFilter(i, e);
diff --git a/src/quick/items/qquickflickable_p.h b/src/quick/items/qquickflickable_p.h
index d0fea9d5c5..7a12f5287b 100644
--- a/src/quick/items/qquickflickable_p.h
+++ b/src/quick/items/qquickflickable_p.h
@@ -280,6 +280,7 @@ protected:
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
+ void touchEvent(QTouchEvent *event) override;
#if QT_CONFIG(wheelevent)
void wheelEvent(QWheelEvent *event) override;
#endif
@@ -305,7 +306,7 @@ protected:
virtual void viewportMoved(Qt::Orientations orient);
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
void mouseUngrabEvent() override;
- bool filterMouseEvent(QQuickItem *receiver, QMouseEvent *event);
+ bool filterPointerEvent(QQuickItem *receiver, QPointerEvent *event);
bool xflick() const;
bool yflick() const;
diff --git a/src/quick/items/qquickflickable_p_p.h b/src/quick/items/qquickflickable_p_p.h
index 4d47943702..f9f5ce7628 100644
--- a/src/quick/items/qquickflickable_p_p.h
+++ b/src/quick/items/qquickflickable_p_p.h
@@ -188,7 +188,7 @@ public:
void updateBeginningEnd();
bool isInnermostPressDelay(QQuickItem *item) const;
- void captureDelayedPress(QQuickItem *item, QMouseEvent *event);
+ void captureDelayedPress(QQuickItem *item, QPointerEvent *event);
void clearDelayedPress();
void replayDelayedPress();
@@ -235,7 +235,7 @@ public:
qreal deceleration;
qreal maxVelocity;
qreal reportedVelocitySmoothing;
- QMouseEvent *delayedPressEvent;
+ QPointerEvent *delayedPressEvent;
QBasicTimer delayedPressTimer;
int pressDelay;
int fixupDuration;
@@ -259,9 +259,9 @@ public:
void viewportAxisMoved(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
QQuickTimeLineCallback::Callback fixupCallback);
- void handleMousePressEvent(QMouseEvent *);
- void handleMouseMoveEvent(QMouseEvent *);
- void handleMouseReleaseEvent(QMouseEvent *);
+ void handlePressEvent(QPointerEvent *);
+ void handleMoveEvent(QPointerEvent *);
+ void handleReleaseEvent(QPointerEvent *);
void maybeBeginDrag(qint64 currentTimestamp, const QPointF &pressPosn);
void drag(qint64 currentTimestamp, QEvent::Type eventType, const QPointF &localPos,
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index 547c825502..e71ae85c54 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -4198,13 +4198,28 @@ void QQuickItem::dropEvent(QDropEvent *event)
#endif // quick_draganddrop
/*!
- Reimplement this method to filter the mouse events that are received by
+ Reimplement this method to filter the pointer events that are received by
this item's children.
- This method will only be called if filtersChildMouseEvents() is true.
+ This method will only be called if filtersChildMouseEvents() is \c true.
- Return true if the specified \a event should not be passed onto the
- specified child \a item, and false otherwise.
+ Return \c true if the specified \a event should not be passed on to the
+ specified child \a item, and \c false otherwise.
+
+ \note Despite the name, this function filters all QPointerEvent instances
+ during delivery to all children (typically mouse, touch, and tablet
+ events). When overriding this function in a subclass, we suggest writing
+ generic event-handling code using only the accessors found in
+ QPointerEvent. Alternatively you can switch on \c event->type() and/or
+ \c event->device()->type() to handle different event types in different ways.
+
+ \note Filtering is just one way to share responsibility in case of gestural
+ ambiguity (for example on press, you don't know whether the user will tap
+ or drag). Another way is to call QPointerEvent::addPassiveGrabber() on
+ press, so as to non-exclusively monitor the progress of the QEventPoint.
+ In either case, the item or pointer handler that is monitoring can steal
+ the exclusive grab later on, when it becomes clear that the gesture fits
+ the pattern that it is expecting.
\sa setFiltersChildMouseEvents()
*/
@@ -7331,8 +7346,13 @@ void QQuickItem::setAcceptedMouseButtons(Qt::MouseButtons buttons)
}
/*!
- Returns whether mouse and touch events of this item's children should be filtered
- through this item.
+ Returns whether pointer events intended for this item's children should be
+ filtered through this item.
+
+ If both this item and a child item have acceptTouchEvents() \c true, then
+ when a touch interaction occurs, this item will filter the touch event.
+ But if either this item or the child cannot handle touch events,
+ childMouseEventFilter() will be called with a synthesized mouse event.
\sa setFiltersChildMouseEvents(), childMouseEventFilter()
*/
@@ -7343,11 +7363,11 @@ bool QQuickItem::filtersChildMouseEvents() const
}
/*!
- Sets whether mouse and touch events of this item's children should be filtered
- through this item.
+ Sets whether pointer events intended for this item's children should be
+ filtered through this item.
If \a filter is true, childMouseEventFilter() will be called when
- a mouse event is triggered for a child item.
+ a pointer event is triggered for a child item.
\sa filtersChildMouseEvents()
*/
@@ -7400,9 +7420,9 @@ void QQuickItem::setAcceptHoverEvents(bool enabled)
/*!
Returns whether touch events are accepted by this item.
- The default value is false.
+ The default value is \c false.
- If this is false, then the item will not receive any touch events through
+ If this is \c false, then the item will not receive any touch events through
the touchEvent() function.
\since 5.10
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 419ad5bb87..b283f56ab9 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -2038,17 +2038,30 @@ void QQuickWindowPrivate::deliverKeyEvent(QKeyEvent *e)
}
}
-// TODO can we return by value to avoid heap allocation?
-// QQuickFlickablePrivate::delayedPressEvent is a ptr so that it can be null;
-// but QMouseEvent::isValid() would be an alternative if we can write it
-QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos)
+/*! \internal
+ Make a copy of any type of QPointerEvent, and optionally localize it
+ by setting its first point's local position() if \a transformedLocalPos is given.
+
+ \note some subclasses of QSinglePointEvent, such as QWheelEvent, add extra storage.
+ This function doesn't yet support cloning all of those; it can be extended if needed.
+*/
+QPointerEvent *QQuickWindowPrivate::clonePointerEvent(QPointerEvent *event, std::optional<QPointF> transformedLocalPos)
{
- QMouseEvent *me = new QMouseEvent(*event);
- QMutableEventPoint &point = QMutableEventPoint::from(me->point(0));
+ QPointerEvent *ret = nullptr;
+ if (isMouseEvent(event)) {
+ ret = new QMouseEvent(*(static_cast<QMouseEvent *>(event)));
+ } else if (isTouchEvent(event)) {
+ ret = new QTouchEvent(*(static_cast<QTouchEvent *>(event)));
+ } else if (isTabletEvent(event)) {
+ ret = new QTabletEvent(*(static_cast<QTabletEvent *>(event)));
+ }
+ QMutableEventPoint &point = QMutableEventPoint::from(ret->point(0));
point.detach();
point.setTimestamp(event->timestamp());
- point.setPosition(transformedLocalPos ? *transformedLocalPos : event->position());
- return me;
+ if (transformedLocalPos)
+ point.setPosition(*transformedLocalPos);
+
+ return ret;
}
void QQuickWindowPrivate::deliverToPassiveGrabbers(const QVector<QPointer <QObject> > &passiveGrabbers,
@@ -2581,15 +2594,17 @@ void QQuickWindowPrivate::onGrabChanged(QObject *grabber, QPointingDevice::GrabT
if (!filtered)
item->mouseUngrabEvent();
}
- if (point.device()->type() == QInputDevice::DeviceType::TouchScreen && event) {
- bool allReleased = true;
- for (const auto &pt : event->points()) {
- if (pt.state() != QEventPoint::State::Released) {
- allReleased = false;
- break;
+ if (point.device()->type() == QInputDevice::DeviceType::TouchScreen) {
+ bool allReleasedOrCancelled = true;
+ if (transition == QPointingDevice::UngrabExclusive && event) {
+ for (const auto &pt : event->points()) {
+ if (pt.state() != QEventPoint::State::Released) {
+ allReleasedOrCancelled = false;
+ break;
+ }
}
}
- if (allReleased)
+ if (allReleasedOrCancelled)
item->touchUngrabEvent();
}
}
@@ -2834,6 +2849,12 @@ bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QPointerEvent *event, bool
// nor to any item which already had a chance to filter.
if (skipDelivery.contains(item))
continue;
+
+ // sendFilteredPointerEvent() changed the QEventPoint::accepted() state,
+ // but per-point acceptance is opt-in during normal delivery to items.
+ for (int i = 0; i < event->pointCount(); ++i)
+ event->point(i).setAccepted(false);
+
deliverMatchingPointsToItem(item, false, event, handlersOnly);
if (event->allPointsAccepted())
handlersOnly = true;
@@ -2935,6 +2956,8 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, bool isG
bool isPressOrRelease = pointerEvent->isBeginEvent() || pointerEvent->isEndEvent();
for (int i = 0; i < touchEvent.pointCount(); ++i) {
auto &point = QMutableEventPoint::from(touchEvent.point(i));
+ // legacy-style delivery: if the item doesn't reject the event, that means it handled ALL the points
+ point.setAccepted();
if (isPressOrRelease)
pointerEvent->setExclusiveGrabber(point, item);
}
@@ -3220,14 +3243,19 @@ bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QPointerEvent *event, QQu
QTouchEvent filteringParentTouchEvent =
QQuickItemPrivate::get(receiver)->localizedTouchEvent(static_cast<QTouchEvent *>(event), true);
if (filteringParentTouchEvent.type() != QEvent::None) {
+ qCDebug(DBG_TOUCH) << "letting parent" << filteringParent << "filter for" << receiver << &filteringParentTouchEvent;
if (filteringParent->childMouseEventFilter(receiver, &filteringParentTouchEvent)) {
qCDebug(DBG_TOUCH) << "touch event intercepted by childMouseEventFilter of " << filteringParent;
skipDelivery.append(filteringParent);
for (auto point : filteringParentTouchEvent.points())
event->setExclusiveGrabber(point, filteringParent);
return true;
- }
- else if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents))) {
+ } else if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents) &&
+ (!acceptsTouchEvents || !filteringParent->acceptTouchEvents()))) {
+ qCDebug(DBG_TOUCH) << "touch event NOT intercepted by childMouseEventFilter of " << filteringParent
+ << "; accepts touch?" << filteringParent->acceptTouchEvents()
+ << "receiver accepts touch?" << acceptsTouchEvents
+ << "so, letting parent filter a synth-mouse event";
// filteringParent didn't filter the touch event. Give it a chance to filter a synthetic mouse event.
for (auto &tp : filteringParentTouchEvent.points()) {
QEvent::Type t;
@@ -3323,13 +3351,13 @@ bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent
return overThreshold;
}
-bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, const QEventPoint *tp, int startDragThreshold)
+bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, const QEventPoint &tp, int startDragThreshold)
{
QStyleHints *styleHints = qApp->styleHints();
bool overThreshold = qAbs(d) > (startDragThreshold >= 0 ? startDragThreshold : styleHints->startDragDistance());
const bool dragVelocityLimitAvailable = (styleHints->startDragVelocity() > 0);
if (!overThreshold && dragVelocityLimitAvailable) {
- qreal velocity = axis == Qt::XAxis ? tp->velocity().x() : tp->velocity().y();
+ qreal velocity = axis == Qt::XAxis ? tp.velocity().x() : tp.velocity().y();
overThreshold |= qAbs(velocity) > styleHints->startDragVelocity();
}
return overThreshold;
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index 62b16513c9..2928c99023 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -172,7 +172,7 @@ public:
void translateTouchEvent(QTouchEvent *touchEvent);
void removeGrabber(QQuickItem *grabber, bool mouse = true, bool touch = true, bool cancel = false);
void onGrabChanged(QObject *grabber, QPointingDevice::GrabTransition transition, const QPointerEvent *event, const QEventPoint &point);
- static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = nullptr);
+ static QPointerEvent *clonePointerEvent(QPointerEvent *event, std::optional<QPointF> transformedLocalPos = std::nullopt);
void deliverToPassiveGrabbers(const QVector<QPointer<QObject> > &passiveGrabbers, QPointerEvent *pointerEvent);
bool sendFilteredMouseEvent(QEvent *event, QQuickItem *receiver, QQuickItem *filteringParent);
bool sendFilteredPointerEvent(QPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent = nullptr);
@@ -188,7 +188,7 @@ public:
// utility functions that used to be in QQuickPointerEvent et al.
bool allUpdatedPointsAccepted(const QPointerEvent *ev);
- void localizePointerEvent(QPointerEvent *ev, const QQuickItem *dest);
+ static void localizePointerEvent(QPointerEvent *ev, const QQuickItem *dest);
QList<QObject *> exclusiveGrabbers(QPointerEvent *ev);
static bool isMouseEvent(const QPointerEvent *ev);
static bool isTouchEvent(const QPointerEvent *ev);
@@ -324,7 +324,11 @@ public:
static bool dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int startDragThreshold = -1);
- static bool dragOverThreshold(qreal d, Qt::Axis axis, const QEventPoint *tp, int startDragThreshold = -1);
+ static bool dragOverThreshold(qreal d, Qt::Axis axis, const QEventPoint &tp, int startDragThreshold = -1);
+
+ // currently in use in Controls 2; TODO remove
+ static bool dragOverThreshold(qreal d, Qt::Axis axis, const QEventPoint *tp, int startDragThreshold = -1)
+ { return dragOverThreshold(d, axis, *tp, startDragThreshold); }
static bool dragOverThreshold(QVector2D delta);