aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
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);