aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quick/handlers/qquickmultipointhandler.cpp28
-rw-r--r--src/quick/handlers/qquickmultipointhandler_p.h5
-rw-r--r--src/quick/handlers/qquickpointerhandler.cpp39
-rw-r--r--src/quick/handlers/qquickpointerhandler_p.h6
-rw-r--r--src/quick/handlers/qquickpointerhandler_p_p.h1
-rw-r--r--src/quick/items/qquickitem.cpp11
-rw-r--r--src/quick/items/qquickitem_p.h1
-rw-r--r--src/quick/items/qquickwindow.cpp23
-rw-r--r--src/quick/items/qquickwindow_p.h2
-rw-r--r--tests/manual/pointer/content/MultiButton.qml1
-rw-r--r--tests/manual/pointer/multibuttons.qml3
11 files changed, 76 insertions, 44 deletions
diff --git a/src/quick/handlers/qquickmultipointhandler.cpp b/src/quick/handlers/qquickmultipointhandler.cpp
index a074e966aa..2236a7d18e 100644
--- a/src/quick/handlers/qquickmultipointhandler.cpp
+++ b/src/quick/handlers/qquickmultipointhandler.cpp
@@ -63,7 +63,6 @@ QQuickMultiPointHandler::QQuickMultiPointHandler(QObject *parent, int minimumPoi
: QQuickPointerDeviceHandler(parent)
, m_minimumPointCount(minimumPointCount)
, m_maximumPointCount(-1)
- , m_pointDistanceThreshold(0)
{
}
@@ -109,8 +108,6 @@ QVector<QQuickEventPoint *> QQuickMultiPointHandler::eligiblePoints(QQuickPointe
{
QVector<QQuickEventPoint *> ret;
int c = event->pointCount();
- QRectF parentBounds = parentItem()->mapRectToScene(parentItem()->boundingRect())
- .marginsAdded(QMarginsF(m_pointDistanceThreshold, m_pointDistanceThreshold, m_pointDistanceThreshold, m_pointDistanceThreshold));
// If one or more points are newly pressed or released, all non-released points are candidates for this handler.
// In other cases however, do not steal the grab: that is, if a point has a grabber,
// it's not a candidate for this handler.
@@ -122,7 +119,7 @@ QVector<QQuickEventPoint *> QQuickMultiPointHandler::eligiblePoints(QQuickPointe
if (exclusiveGrabber && exclusiveGrabber != this)
continue;
}
- if (p->state() != QQuickEventPoint::Released && parentBounds.contains(p->scenePosition()))
+ if (p->state() != QQuickEventPoint::Released && wantsEventPoint(p))
ret << p;
}
return ret;
@@ -176,29 +173,6 @@ void QQuickMultiPointHandler::setMaximumPointCount(int maximumPointCount)
emit maximumPointCountChanged();
}
-/*!
- \qmlproperty real MultiPointHandler::pointDistanceThreshold
-
- The margin beyond the bounds of the \l {PointerHandler::parent}{parent}
- item within which a touch point can activate this handler. For example, on
- a PinchHandler where the \l {PointerHandler::target}{target} is also the
- \c parent, it's useful to set this to a distance at least half the width
- of a typical user's finger, so that if the \c parent has been scaled down
- to a very small size, the pinch gesture is still possible.
-
- The default value is 0.
-
- \image pointDistanceThreshold.png
-*/
-void QQuickMultiPointHandler::setPointDistanceThreshold(qreal pointDistanceThreshold)
-{
- if (m_pointDistanceThreshold == pointDistanceThreshold)
- return;
-
- m_pointDistanceThreshold = pointDistanceThreshold;
- emit pointDistanceThresholdChanged();
-}
-
bool QQuickMultiPointHandler::hasCurrentPoints(QQuickPointerEvent *event)
{
bool ret = true;
diff --git a/src/quick/handlers/qquickmultipointhandler_p.h b/src/quick/handlers/qquickmultipointhandler_p.h
index ddcf731227..06045771fb 100644
--- a/src/quick/handlers/qquickmultipointhandler_p.h
+++ b/src/quick/handlers/qquickmultipointhandler_p.h
@@ -63,7 +63,6 @@ class Q_AUTOTEST_EXPORT QQuickMultiPointHandler : public QQuickPointerDeviceHand
Q_OBJECT
Q_PROPERTY(int minimumPointCount READ minimumPointCount WRITE setMinimumPointCount NOTIFY minimumPointCountChanged)
Q_PROPERTY(int maximumPointCount READ maximumPointCount WRITE setMaximumPointCount NOTIFY maximumPointCountChanged)
- Q_PROPERTY(qreal pointDistanceThreshold READ pointDistanceThreshold WRITE setPointDistanceThreshold NOTIFY pointDistanceThresholdChanged)
Q_PROPERTY(QQuickHandlerPoint centroid READ centroid NOTIFY centroidChanged)
public:
@@ -76,9 +75,6 @@ public:
int maximumPointCount() const { return m_maximumPointCount >= 0 ? m_maximumPointCount : m_minimumPointCount; }
void setMaximumPointCount(int maximumPointCount);
- qreal pointDistanceThreshold() const { return m_pointDistanceThreshold; }
- void setPointDistanceThreshold(qreal pointDistanceThreshold);
-
QQuickHandlerPoint centroid() const { return m_centroid; }
signals:
@@ -114,7 +110,6 @@ protected:
QQuickHandlerPoint m_centroid;
int m_minimumPointCount;
int m_maximumPointCount;
- qreal m_pointDistanceThreshold;
};
QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp
index 33c2acebe2..f222ac68f2 100644
--- a/src/quick/handlers/qquickpointerhandler.cpp
+++ b/src/quick/handlers/qquickpointerhandler.cpp
@@ -81,6 +81,39 @@ QQuickPointerHandler::~QQuickPointerHandler()
}
/*!
+ \qmlproperty real PointerHandler::margin
+
+ The margin beyond the bounds of the \l {PointerHandler::parent}{parent}
+ item within which an event point can activate this handler. For example, on
+ a PinchHandler where the \l {PointerHandler::target}{target} is also the
+ \c parent, it's useful to set this to a distance at least half the width
+ of a typical user's finger, so that if the \c parent has been scaled down
+ to a very small size, the pinch gesture is still possible. Or, if a
+ TapHandler-based button is placed near the screen edge, it can be used
+ to comply with Fitts's Law: react to mouse clicks at the screen edge
+ even though the button is visually spaced away from the edge by a few pixels.
+
+ The default value is 0.
+
+ \image pointDistanceThreshold.png
+*/
+qreal QQuickPointerHandler::margin() const
+{
+ Q_D(const QQuickPointerHandler);
+ return d->m_margin;
+}
+
+void QQuickPointerHandler::setMargin(qreal pointDistanceThreshold)
+{
+ Q_D(QQuickPointerHandler);
+ if (d->m_margin == pointDistanceThreshold)
+ return;
+
+ d->m_margin = pointDistanceThreshold;
+ emit marginChanged();
+}
+
+/*!
Notification that the grab has changed in some way which is relevant to this handler.
The \a grabber (subject) will be the PointerHandler whose state is changing,
or null if the state change regards an Item.
@@ -318,7 +351,11 @@ bool QQuickPointerHandler::parentContains(const QQuickEventPoint *point) const
if (!par->window()->geometry().contains(screenPosition))
return false;
}
- return par->contains(par->mapFromScene(point->scenePosition()));
+ QPointF p = par->mapFromScene(point->scenePosition());
+ qreal m = margin();
+ if (m > 0)
+ return p.x() >= -m && p.y() >= -m && p.x() <= par->width() + m && p.y() <= par->height() + m;
+ return par->contains(p);
}
return false;
}
diff --git a/src/quick/handlers/qquickpointerhandler_p.h b/src/quick/handlers/qquickpointerhandler_p.h
index c7420bcec2..9644bb959f 100644
--- a/src/quick/handlers/qquickpointerhandler_p.h
+++ b/src/quick/handlers/qquickpointerhandler_p.h
@@ -70,6 +70,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPointerHandler : public QObject, public QQmlP
Q_PROPERTY(QQuickItem * target READ target WRITE setTarget NOTIFY targetChanged)
Q_PROPERTY(QQuickItem * parent READ parentItem CONSTANT)
Q_PROPERTY(GrabPermissions grabPermissions READ grabPermissions WRITE setGrabPermissions NOTIFY grabPermissionChanged)
+ Q_PROPERTY(qreal margin READ margin WRITE setMargin NOTIFY marginChanged)
public:
explicit QQuickPointerHandler(QObject *parent = nullptr);
@@ -106,10 +107,14 @@ public:
GrabPermissions grabPermissions() const;
void setGrabPermissions(GrabPermissions grabPermissions);
+ qreal margin() const;
+ void setMargin(qreal pointDistanceThreshold);
+
Q_SIGNALS:
void enabledChanged();
void activeChanged();
void targetChanged();
+ void marginChanged();
void grabChanged(QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point);
void grabPermissionChanged();
void canceled(QQuickEventPoint *point);
@@ -137,6 +142,7 @@ protected:
bool parentContains(const QQuickEventPoint *point) const;
friend class QQuickEventPoint;
+ friend class QQuickItemPrivate;
friend class QQuickWindowPrivate;
Q_DECLARE_PRIVATE(QQuickPointerHandler)
diff --git a/src/quick/handlers/qquickpointerhandler_p_p.h b/src/quick/handlers/qquickpointerhandler_p_p.h
index ce50b98c57..c0b2b84a66 100644
--- a/src/quick/handlers/qquickpointerhandler_p_p.h
+++ b/src/quick/handlers/qquickpointerhandler_p_p.h
@@ -71,6 +71,7 @@ public:
QQuickPointerEvent *currentEvent = nullptr;
QQuickItem *target = nullptr;
+ qreal m_margin = 0;
uint8_t grabPermissions : 8;
bool enabled : 1;
bool active : 1;
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index 3b1402fe16..b09dbdad84 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -5140,6 +5140,17 @@ void QQuickItemPrivate::deliverShortcutOverrideEvent(QKeyEvent *event)
}
}
+bool QQuickItemPrivate::anyPointerHandlerWants(QQuickEventPoint *point) const
+{
+ if (!hasPointerHandlers())
+ return false;
+ for (QQuickPointerHandler *handler : extra->pointerHandlers) {
+ if (handler->wantsEventPoint(point))
+ return true;
+ }
+ return false;
+}
+
/*!
\internal
Deliver the \a event to all PointerHandlers which are in the pre-determined
diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h
index f43f04ed39..bb63bc1ee4 100644
--- a/src/quick/items/qquickitem_p.h
+++ b/src/quick/items/qquickitem_p.h
@@ -582,6 +582,7 @@ public:
#endif
void deliverShortcutOverrideEvent(QKeyEvent *);
+ bool anyPointerHandlerWants(QQuickEventPoint *point) const;
virtual bool handlePointerEvent(QQuickPointerEvent *, bool avoidExclusiveGrabber = false);
virtual void setVisible(bool visible);
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index e53ca7f8de..759bb573b1 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -1789,7 +1789,7 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven
// If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order
if (!pointerEvent->allPointsGrabbed() && pointerEvent->buttons()) {
- QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point->scenePosition(), false, false);
+ QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point, false, false);
for (QQuickItem *item : targetItems) {
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
if (!itemPrivate->extra.isAllocated() || itemPrivate->extra->pointerHandlers.isEmpty())
@@ -1924,7 +1924,7 @@ bool QQuickWindowPrivate::deliverSinglePointEventUntilAccepted(QQuickPointerEven
{
Q_ASSERT(event->pointCount() == 1);
QQuickEventPoint *point = event->point(0);
- QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point->scenePosition(), false, false);
+ QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point, false, false);
for (QQuickItem *item : targetItems) {
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
@@ -2340,16 +2340,16 @@ void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event)
--pointerEventRecursionGuard;
}
-// check if item or any of its child items contain the point
+// check if item or any of its child items contain the point, or if any pointer handler "wants" the point
// FIXME: should this be iterative instead of recursive?
// If checkMouseButtons is true, it means we are finding targets for a mouse event, so no item for which acceptedMouseButtons() is NoButton will be added.
// If checkAcceptsTouch is true, it means we are finding targets for a touch event, so either acceptTouchEvents() must return true OR
// it must accept a synth. mouse event, thus if acceptTouchEvents() returns false but acceptedMouseButtons() is true, gets added; if not, it doesn't.
-QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, const QPointF &scenePos, bool checkMouseButtons, bool checkAcceptsTouch) const
+QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, QQuickEventPoint *point, bool checkMouseButtons, bool checkAcceptsTouch) const
{
QVector<QQuickItem *> targets;
auto itemPrivate = QQuickItemPrivate::get(item);
- QPointF itemPos = item->mapFromScene(scenePos);
+ QPointF itemPos = item->mapFromScene(point->scenePosition());
// if the item clips, we can potentially return early
if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
if (!item->contains(itemPos))
@@ -2363,11 +2363,15 @@ QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, cons
auto childPrivate = QQuickItemPrivate::get(child);
if (!child->isVisible() || !child->isEnabled() || childPrivate->culled)
continue;
- targets << pointerTargets(child, scenePos, checkMouseButtons, checkAcceptsTouch);
+ targets << pointerTargets(child, point, checkMouseButtons, checkAcceptsTouch);
}
bool relevant = item->contains(itemPos);
- if (!(itemPrivate->hasPointerHandlers())) {
+ if (itemPrivate->hasPointerHandlers()) {
+ if (!relevant)
+ if (itemPrivate->anyPointerHandlerWants(point))
+ relevant = true;
+ } else {
if (relevant && checkMouseButtons && item->acceptedMouseButtons() == Qt::NoButton)
relevant = false;
if (relevant && checkAcceptsTouch && !(item->acceptTouchEvents() || item->acceptedMouseButtons()))
@@ -2477,7 +2481,7 @@ void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve
QQuickEventPoint *point = event->point(i);
if (point->state() == QQuickEventPoint::Pressed)
continue; // presses were delivered earlier; not the responsibility of deliverUpdatedTouchPoints
- QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point->scenePosition(), false, false);
+ QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point, false, false);
if (targetItems.count()) {
targetItems = mergePointerTargets(targetItems, targetItemsForPoint);
} else {
@@ -2507,7 +2511,7 @@ bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event,
point->setAccepted(false); // because otherwise touchEventForItem will ignore it
if (point->grabberPointerHandler() && point->state() == QQuickEventPoint::Released)
point->setGrabberPointerHandler(nullptr, true);
- QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point->scenePosition(), !isTouchEvent, isTouchEvent);
+ QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point, !isTouchEvent, isTouchEvent);
if (targetItems.count()) {
targetItems = mergePointerTargets(targetItems, targetItemsForPoint);
} else {
@@ -2567,7 +2571,6 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo
// synthetic events - flickable sends one when setPressDelay is used.
auto oldMouseGrabber = q->mouseGrabberItem();
QPointF localPos = item->mapFromScene(point->scenePosition());
- Q_ASSERT(item->contains(localPos)); // transform is checked already
QMouseEvent *me = event->asMouseEvent(localPos);
me->accept();
QCoreApplication::sendEvent(item, me);
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index eb2f4d20fa..9a83e8523b 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -175,7 +175,7 @@ public:
void deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event);
void deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, bool handlersOnly = false);
- QVector<QQuickItem *> pointerTargets(QQuickItem *, const QPointF &, bool checkMouseButtons, bool checkAcceptsTouch) const;
+ QVector<QQuickItem *> pointerTargets(QQuickItem *, QQuickEventPoint *point, bool checkMouseButtons, bool checkAcceptsTouch) const;
QVector<QQuickItem *> mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const;
// hover delivery
diff --git a/tests/manual/pointer/content/MultiButton.qml b/tests/manual/pointer/content/MultiButton.qml
index 2a41967a36..ca9b16a957 100644
--- a/tests/manual/pointer/content/MultiButton.qml
+++ b/tests/manual/pointer/content/MultiButton.qml
@@ -35,6 +35,7 @@ Rectangle {
property alias pressed: tap.pressed
property bool checked: false
property alias gesturePolicy: tap.gesturePolicy
+ property alias margin: tap.margin
signal tapped
width: label.implicitWidth * 1.5; height: label.implicitHeight * 2.0
diff --git a/tests/manual/pointer/multibuttons.qml b/tests/manual/pointer/multibuttons.qml
index cd7c5d0b19..28c3cc0f50 100644
--- a/tests/manual/pointer/multibuttons.qml
+++ b/tests/manual/pointer/multibuttons.qml
@@ -44,6 +44,7 @@ Item {
label: "Launch Balloons"
Layout.fillWidth: true
gesturePolicy: TapHandler.WithinBounds
+ margin: 10
}
Text { text: "the goons"; font.pointSize: 12 }
MultiButton {
@@ -51,6 +52,7 @@ Item {
label: "Launch Missile"
Layout.fillWidth: true
gesturePolicy: TapHandler.ReleaseWithinBounds
+ margin: 10
onTapped: missileEmitter.burst(1)
}
MultiButton {
@@ -58,6 +60,7 @@ Item {
label: "Launch Fighters"
Layout.fillWidth: true
gesturePolicy: TapHandler.DragThreshold
+ margin: 10
}
}
ParticleSystem {