aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/handlers
diff options
context:
space:
mode:
authorShawn Rutledge <shawn.rutledge@qt.io>2016-10-18 18:45:49 +0200
committerShawn Rutledge <shawn.rutledge@qt.io>2017-02-09 14:32:21 +0000
commitcb78d5c91ed33543a8e7fe7717f74f95834e4cc3 (patch)
tree3de3fbf56055d3aa0dc8017bbd705f9e6325f49c /src/quick/handlers
parent5c639a07fd90916d39823e800d5d89f779d892e9 (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.cpp116
-rw-r--r--src/quick/handlers/qquicktaphandler_p.h14
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;