diff options
author | Shawn Rutledge <shawn.rutledge@qt.io> | 2016-10-18 18:45:49 +0200 |
---|---|---|
committer | Shawn Rutledge <shawn.rutledge@qt.io> | 2017-02-09 14:32:21 +0000 |
commit | cb78d5c91ed33543a8e7fe7717f74f95834e4cc3 (patch) | |
tree | 3de3fbf56055d3aa0dc8017bbd705f9e6325f49c /src/quick/handlers | |
parent | 5c639a07fd90916d39823e800d5d89f779d892e9 (diff) |
TapHandler: add gesturePolicy
Until now it behaved as if this was set to DragThreshold: give up on
the tap as soon as you are clearly dragging rather than tapping.
But that's not what is normally wanted when building a Button control,
for example. So provide 3 options: give up past the drag threshold,
when the pointer goes outside the bounds, or when it's released
outside the bounds. The longPressThreshold also constrains all
three cases: holding (or dragging) for too long will not result
in an immediate cancellation, but it also will not be a tap gesture.
Change-Id: I95aec978e783892b55371391a27642751d91d9ff
Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
Diffstat (limited to 'src/quick/handlers')
-rw-r--r-- | src/quick/handlers/qquicktaphandler.cpp | 116 | ||||
-rw-r--r-- | src/quick/handlers/qquicktaphandler_p.h | 14 |
2 files changed, 110 insertions, 20 deletions
diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp index f1bb2f96b6..11d4301eb6 100644 --- a/src/quick/handlers/qquicktaphandler.cpp +++ b/src/quick/handlers/qquicktaphandler.cpp @@ -58,23 +58,19 @@ int QQuickTapHandler::m_touchMultiTapDistanceSquared(-1); TapHandler is a handler for taps on a touchscreen or clicks on a mouse. - It requires that any movement between the press and release remains - less than the drag threshold for a single tap, and less than - QPlatformTheme::MouseDoubleClickDistance for multi-tap gestures - (double-tap, triple-tap, etc.) with the mouse, or 10 pixels with touch. - It also requires that the time between press and release remains - less than QStyleHints::mouseDoubleClickInterval() for a single tap, - and that the time from one press/release sequence to the next remains - less than QStyleHints::mouseDoubleClickInterval() for multi-tap gestures. - + Detection of a valid tap gesture depends on \l gesturePolicy. Note that buttons (such as QPushButton) are often implemented not to care whether the press and release occur close together: if you press the button and then change your mind, you need to drag all the way off the edge of the - button in order to cancel the click. If you want to achieve such behavior, - it's enough to use a PointerHandler and consider the button clicked on - every \l {QQuickPointerHandler:}{released} event. But TapHandler requires - that the events occur close together in both space and time, which is anyway - necessary to detect double clicks or multi-click gestures. + button in order to cancel the click. Therefore the default + \l gesturePolicy is \l ReleaseWithinBounds. If you want to require + that the press and release are close together in both space and time, + set it to \l DragThreshold. + + For multi-tap gestures (double-tap, triple-tap etc.), the distance moved + must not exceed QPlatformTheme::MouseDoubleClickDistance with mouse and + QPlatformTheme::TouchDoubleTapDistance with touch, and the time between + taps must not exceed QStyleHints::mouseDoubleClickInterval(). \sa MouseArea */ @@ -82,6 +78,7 @@ int QQuickTapHandler::m_touchMultiTapDistanceSquared(-1); QQuickTapHandler::QQuickTapHandler(QObject *parent) : QQuickPointerSingleHandler(parent) , m_pressed(false) + , m_gesturePolicy(ReleaseWithinBounds) , m_tapCount(0) , m_longPressThreshold(-1) , m_lastTapTimestamp(0.0) @@ -102,15 +99,46 @@ QQuickTapHandler::~QQuickTapHandler() { } +static bool dragOverThreshold(QQuickEventPoint *point) +{ + QPointF delta = point->scenePos() - point->scenePressPos(); + return (QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, point) || + QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, point)); +} + bool QQuickTapHandler::wantsEventPoint(QQuickEventPoint *point) { - if (point->state() == QQuickEventPoint::Pressed && parentContains(point)) - return true; - // If the user has not dragged too far, it could be a tap. + // If the user has not violated any constraint, it could be a tap. // Otherwise we want to give up the grab so that a competing handler // (e.g. DragHandler) gets a chance to take over. // Don't forget to emit released in case of a cancel. - return !point->isDraggedOverThreshold(); + bool ret = false; + switch (point->state()) { + case QQuickEventPoint::Pressed: + case QQuickEventPoint::Released: + ret = parentContains(point); + break; + default: // update or stationary + switch (m_gesturePolicy) { + case DragThreshold: + ret = !dragOverThreshold(point); + break; + case WithinBounds: + ret = parentContains(point); + break; + case ReleaseWithinBounds: + ret = true; + break; + } + break; + } + // If this is the grabber, returning false from this function will + // cancel the grab, so handleGrabCancel() and setPressed(false) will be called. + // But when m_gesturePolicy is DragThreshold, we don't grab, but + // we still don't want to be pressed anymore. + if (!ret) + setPressed(false, true, point); + return ret; } void QQuickTapHandler::handleEventPoint(QQuickEventPoint *point) @@ -134,7 +162,7 @@ void QQuickTapHandler::handleEventPoint(QQuickEventPoint *point) The time in seconds that an event point must be pressed in order to trigger a long press gesture and emit the \l longPressed() signal. If the point is released before this time limit, a tap can be detected - if the other constraints are satisfied. The default value is + if the \l gesturePolicy constraint is satisfied. The default value is QStyleHints::mousePressAndHoldInterval() converted to seconds. */ qreal QQuickTapHandler::longPressThreshold() const @@ -165,6 +193,54 @@ void QQuickTapHandler::timerEvent(QTimerEvent *event) } } +/*! + \qmlproperty gesturePolicy + + The spatial constraint for a tap or long press gesture to be recognized, + in addition to the constraint that the release must occur before + \l longPressThreshold has elapsed. If these constraints are not satisfied, + the \l tapped signal is not emitted, and \l tapCount is not incremented. + If the spatial constraint is violated, \l isPressed transitions immediately + from true to false, regardless of the time held. + + \c DragThreshold means that the event point must not move significantly. + If the mouse, finger or stylus moves past the system-wide drag threshold + (QStyleHints::startDragDistance), the tap gesture is canceled, even if + the button or finger is still pressed. This policy can be useful whenever + TapHandler needs to cooperate with other pointer handlers (for example + \l DragHandler), because in this case TapHandler will never grab. + + \c WithinBounds means that if the event point leaves the bounds of the + \l target item, the tap gesture is canceled. The TapHandler will grab on + press, but release the grab as soon as the boundary constraint is no + longer satisfied. + + \c ReleaseWithinBounds (the default value) means that at the time of release + (the mouse button is released or the finger is lifted), if the event point + is outside the bounds of the \l target item, a tap gesture is not + recognized. This is the default value, because it corresponds to typical + button behavior: you can cancel a click by dragging outside the button, and + you can also change your mind by dragging back inside the button before + release. Note that it's necessary for TapHandler to grab on press and + retain it until release (greedy grab) in order to detect this gesture. +*/ +void QQuickTapHandler::setGesturePolicy(QQuickTapHandler::GesturePolicy gesturePolicy) +{ + if (m_gesturePolicy == gesturePolicy) + return; + + m_gesturePolicy = gesturePolicy; + emit gesturePolicyChanged(); +} + +/*! + \qmlproperty pressed + + This property will be true whenever the mouse or touch point is pressed, + and any movement since the press is compliant with the current + \l gesturePolicy. When the event point is released or the policy is + violated, pressed will change to false. +*/ void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *point) { if (m_pressed != press) { @@ -173,6 +249,8 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *poi m_longPressTimer.start(longPressThresholdMilliseconds(), this); else m_longPressTimer.stop(); + if (m_gesturePolicy != DragThreshold) + setGrab(point, press); if (!cancel && !press && point->timeHeld() < longPressThreshold()) { // Assuming here that pointerEvent()->timestamp() is in ms. qreal ts = point->pointerEvent()->timestamp() / 1000.0; diff --git a/src/quick/handlers/qquicktaphandler_p.h b/src/quick/handlers/qquicktaphandler_p.h index 0559089fbf..93fe817844 100644 --- a/src/quick/handlers/qquicktaphandler_p.h +++ b/src/quick/handlers/qquicktaphandler_p.h @@ -64,8 +64,16 @@ class Q_AUTOTEST_EXPORT QQuickTapHandler : public QQuickPointerSingleHandler Q_PROPERTY(bool isPressed READ isPressed NOTIFY pressedChanged) Q_PROPERTY(int tapCount READ tapCount NOTIFY tapCountChanged) Q_PROPERTY(qreal longPressThreshold READ longPressThreshold WRITE setLongPressThreshold NOTIFY longPressThresholdChanged) + Q_PROPERTY(GesturePolicy gesturePolicy READ gesturePolicy WRITE setGesturePolicy NOTIFY gesturePolicyChanged) public: + enum GesturePolicy { + DragThreshold, + WithinBounds, + ReleaseWithinBounds + }; + Q_ENUM(GesturePolicy) + QQuickTapHandler(QObject *parent = 0); ~QQuickTapHandler(); @@ -79,10 +87,14 @@ public: qreal longPressThreshold() const; void setLongPressThreshold(qreal longPressThreshold); + GesturePolicy gesturePolicy() const { return m_gesturePolicy; } + void setGesturePolicy(GesturePolicy gesturePolicy); + Q_SIGNALS: void pressedChanged(); void tapCountChanged(); void longPressThresholdChanged(); + void gesturePolicyChanged(); void tapped(QQuickEventPoint *point); void longPressed(); @@ -96,13 +108,13 @@ private: private: bool m_pressed; + GesturePolicy m_gesturePolicy; int m_tapCount; int m_longPressThreshold; QBasicTimer m_longPressTimer; QPointF m_lastTapPos; qreal m_lastTapTimestamp; - static qreal m_tapInterval; static qreal m_multiTapInterval; static int m_mouseMultiClickDistanceSquared; static int m_touchMultiTapDistanceSquared; |