1 | // Copyright (C) 2019 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qquicktaphandler_p.h" |
5 | #include "qquicksinglepointhandler_p_p.h" |
6 | #include <QtQuick/private/qquickdeliveryagent_p_p.h> |
7 | #include <QtQuick/qquickwindow.h> |
8 | #include <qpa/qplatformtheme.h> |
9 | #include <private/qguiapplication_p.h> |
10 | #include <QtGui/qstylehints.h> |
11 | |
12 | QT_BEGIN_NAMESPACE |
13 | |
14 | Q_LOGGING_CATEGORY(lcTapHandler, "qt.quick.handler.tap" ) |
15 | |
16 | quint64 QQuickTapHandler::m_multiTapInterval(0); |
17 | // single tap distance is the same as the drag threshold |
18 | int QQuickTapHandler::m_mouseMultiClickDistanceSquared(-1); |
19 | int QQuickTapHandler::m_touchMultiTapDistanceSquared(-1); |
20 | |
21 | /*! |
22 | \qmltype TapHandler |
23 | \instantiates QQuickTapHandler |
24 | \inherits SinglePointHandler |
25 | \inqmlmodule QtQuick |
26 | \ingroup qtquick-input-handlers |
27 | \brief Handler for taps and clicks. |
28 | |
29 | TapHandler is a handler for taps on a touchscreen or clicks on a mouse. |
30 | |
31 | Detection of a valid tap gesture depends on \l gesturePolicy. The default |
32 | value is DragThreshold, which requires the press and release to be close |
33 | together in both space and time. In this case, DragHandler is able to |
34 | function using only a passive grab, and therefore does not interfere with |
35 | event delivery to any other Items or Input Handlers. So the default |
36 | gesturePolicy is useful when you want to modify behavior of an existing |
37 | control or Item by adding a TapHandler with bindings and/or JavaScript |
38 | callbacks. |
39 | |
40 | Note that buttons (such as QPushButton) are often implemented not to care |
41 | whether the press and release occur close together: if you press the button |
42 | and then change your mind, you need to drag all the way off the edge of the |
43 | button in order to cancel the click. For this use case, set the |
44 | \l gesturePolicy to \c TapHandler.ReleaseWithinBounds. |
45 | |
46 | \snippet pointerHandlers/tapHandlerButton.qml 0 |
47 | |
48 | For multi-tap gestures (double-tap, triple-tap etc.), the distance moved |
49 | must not exceed QStyleHints::mouseDoubleClickDistance() with mouse and |
50 | QStyleHints::touchDoubleTapDistance() with touch, and the time between |
51 | taps must not exceed QStyleHints::mouseDoubleClickInterval(). |
52 | |
53 | \sa MouseArea, {Qt Quick Examples - Pointer Handlers} |
54 | */ |
55 | |
56 | QQuickTapHandler::QQuickTapHandler(QQuickItem *parent) |
57 | : QQuickSinglePointHandler(parent) |
58 | { |
59 | if (m_mouseMultiClickDistanceSquared < 0) { |
60 | m_multiTapInterval = qApp->styleHints()->mouseDoubleClickInterval(); |
61 | m_mouseMultiClickDistanceSquared = qApp->styleHints()->mouseDoubleClickDistance(); |
62 | m_mouseMultiClickDistanceSquared *= m_mouseMultiClickDistanceSquared; |
63 | m_touchMultiTapDistanceSquared = qApp->styleHints()->touchDoubleTapDistance(); |
64 | m_touchMultiTapDistanceSquared *= m_touchMultiTapDistanceSquared; |
65 | } |
66 | } |
67 | |
68 | bool QQuickTapHandler::wantsEventPoint(const QPointerEvent *event, const QEventPoint &point) |
69 | { |
70 | if (!QQuickDeliveryAgentPrivate::isMouseEvent(ev: event) && |
71 | !QQuickDeliveryAgentPrivate::isTouchEvent(ev: event) && |
72 | !QQuickDeliveryAgentPrivate::isTabletEvent(ev: event)) |
73 | return false; |
74 | // If the user has not violated any constraint, it could be a tap. |
75 | // Otherwise we want to give up the grab so that a competing handler |
76 | // (e.g. DragHandler) gets a chance to take over. |
77 | // Don't forget to emit released in case of a cancel. |
78 | bool ret = false; |
79 | bool overThreshold = d_func()->dragOverThreshold(point); |
80 | if (overThreshold && m_gesturePolicy != DragWithinBounds) { |
81 | m_longPressTimer.stop(); |
82 | m_holdTimer.invalidate(); |
83 | } |
84 | switch (point.state()) { |
85 | case QEventPoint::Pressed: |
86 | case QEventPoint::Released: |
87 | ret = parentContains(point); |
88 | break; |
89 | case QEventPoint::Updated: |
90 | ret = point.id() == this->point().id(); |
91 | switch (m_gesturePolicy) { |
92 | case DragThreshold: |
93 | ret = ret && !overThreshold && parentContains(point); |
94 | break; |
95 | case WithinBounds: |
96 | case DragWithinBounds: |
97 | ret = ret && parentContains(point); |
98 | break; |
99 | case ReleaseWithinBounds: |
100 | // no change to ret: depends only whether it's the already-tracking point ID |
101 | break; |
102 | } |
103 | break; |
104 | case QEventPoint::Stationary: |
105 | // If the point hasn't moved since last time, the return value should be the same as last time. |
106 | // If we return false here, QQuickPointerHandler::handlePointerEvent() will call setActive(false). |
107 | ret = point.id() == this->point().id(); |
108 | break; |
109 | case QEventPoint::Unknown: |
110 | break; |
111 | } |
112 | // If this is the grabber, returning false from this function will cancel the grab, |
113 | // so onGrabChanged(this, CancelGrabExclusive, point) and setPressed(false) will be called. |
114 | // But when m_gesturePolicy is DragThreshold, we don't get an exclusive grab, but |
115 | // we still don't want to be pressed anymore. |
116 | if (!ret && point.id() == this->point().id()) |
117 | setPressed(press: false, cancel: true, event: const_cast<QPointerEvent *>(event), point&: const_cast<QEventPoint &>(point)); |
118 | return ret; |
119 | } |
120 | |
121 | void QQuickTapHandler::handleEventPoint(QPointerEvent *event, QEventPoint &point) |
122 | { |
123 | const bool isTouch = QQuickDeliveryAgentPrivate::isTouchEvent(ev: event); |
124 | switch (point.state()) { |
125 | case QEventPoint::Pressed: |
126 | setPressed(press: true, cancel: false, event, point); |
127 | break; |
128 | case QEventPoint::Released: { |
129 | if (isTouch || (static_cast<const QSinglePointEvent *>(event)->buttons() & acceptedButtons()) == Qt::NoButton) |
130 | setPressed(press: false, cancel: false, event, point); |
131 | break; |
132 | } |
133 | default: |
134 | break; |
135 | } |
136 | |
137 | QQuickSinglePointHandler::handleEventPoint(event, point); |
138 | |
139 | // If TapHandler only needs a passive grab, it should not block other items and handlers from reacting. |
140 | // If the point is accepted, QQuickItemPrivate::localizedTouchEvent() would skip it. |
141 | if (isTouch && m_gesturePolicy == DragThreshold) |
142 | point.setAccepted(false); |
143 | } |
144 | |
145 | /*! |
146 | \qmlproperty real QtQuick::TapHandler::longPressThreshold |
147 | |
148 | The time in seconds that an \l eventPoint must be pressed in order to |
149 | trigger a long press gesture and emit the \l longPressed() signal. |
150 | If the point is released before this time limit, a tap can be detected |
151 | if the \l gesturePolicy constraint is satisfied. The default value is |
152 | QStyleHints::mousePressAndHoldInterval() converted to seconds. |
153 | */ |
154 | qreal QQuickTapHandler::longPressThreshold() const |
155 | { |
156 | return longPressThresholdMilliseconds() / 1000.0; |
157 | } |
158 | |
159 | void QQuickTapHandler::setLongPressThreshold(qreal longPressThreshold) |
160 | { |
161 | int ms = qRound(d: longPressThreshold * 1000); |
162 | if (m_longPressThreshold == ms) |
163 | return; |
164 | |
165 | m_longPressThreshold = ms; |
166 | emit longPressThresholdChanged(); |
167 | } |
168 | |
169 | int QQuickTapHandler::longPressThresholdMilliseconds() const |
170 | { |
171 | return (m_longPressThreshold < 0 ? QGuiApplication::styleHints()->mousePressAndHoldInterval() : m_longPressThreshold); |
172 | } |
173 | |
174 | void QQuickTapHandler::timerEvent(QTimerEvent *event) |
175 | { |
176 | if (event->timerId() == m_longPressTimer.timerId()) { |
177 | m_longPressTimer.stop(); |
178 | qCDebug(lcTapHandler) << objectName() << "longPressed" ; |
179 | emit longPressed(); |
180 | } else if (event->timerId() == m_doubleTapTimer.timerId()) { |
181 | m_doubleTapTimer.stop(); |
182 | qCDebug(lcTapHandler) << objectName() << "double-tap timer expired; taps:" << m_tapCount; |
183 | Q_ASSERT(m_exclusiveSignals == (SingleTap | DoubleTap)); |
184 | if (m_tapCount == 1) |
185 | emit singleTapped(eventPoint: m_singleTapReleasedPoint, m_singleTapReleasedButton); |
186 | else if (m_tapCount == 2) |
187 | emit doubleTapped(eventPoint: m_singleTapReleasedPoint, m_singleTapReleasedButton); |
188 | } |
189 | } |
190 | |
191 | /*! |
192 | \qmlproperty enumeration QtQuick::TapHandler::gesturePolicy |
193 | |
194 | The spatial constraint for a tap or long press gesture to be recognized, |
195 | in addition to the constraint that the release must occur before |
196 | \l longPressThreshold has elapsed. If these constraints are not satisfied, |
197 | the \l tapped signal is not emitted, and \l tapCount is not incremented. |
198 | If the spatial constraint is violated, \l pressed transitions immediately |
199 | from true to false, regardless of the time held. |
200 | |
201 | The \c gesturePolicy also affects grab behavior as described below. |
202 | |
203 | \table |
204 | \header |
205 | \li Constant |
206 | \li Description |
207 | \row |
208 | \li \c TapHandler.DragThreshold |
209 | \image pointerHandlers/tapHandlerOverlappingButtons.webp |
210 | Grab on press: \e passive |
211 | \li (the default value) The \l eventPoint must not move significantly. |
212 | If the mouse, finger or stylus moves past the system-wide drag |
213 | threshold (QStyleHints::startDragDistance), the tap gesture is |
214 | canceled, even if the device or finger is still pressed. This policy |
215 | can be useful whenever TapHandler needs to cooperate with other |
216 | input handlers (for example \l DragHandler) or event-handling Items |
217 | (for example \l {Qt Quick Controls}), because in this case TapHandler |
218 | will not take the exclusive grab, but merely a |
219 | \l {QPointerEvent::addPassiveGrabber()}{passive grab}. |
220 | That is, \c DragThreshold is especially useful to \e augment |
221 | existing behavior: it reacts to tap/click/long-press even when |
222 | another item or handler is already reacting, perhaps even in a |
223 | different layer of the UI. The following snippet shows one |
224 | TapHandler as used in one component; but if we stack up two |
225 | instances of the component, you will see the handlers in both of them |
226 | react simultaneously when a press occurs over both of them, because |
227 | the passive grab does not stop event propagation: |
228 | \quotefromfile pointerHandlers/tapHandlerOverlappingButtons.qml |
229 | \skipto Item |
230 | \printuntil component Button |
231 | \skipto TapHandler |
232 | \printuntil } |
233 | \skipuntil Text { |
234 | \skipuntil } |
235 | \printuntil Button |
236 | \printuntil Button |
237 | \printuntil } |
238 | |
239 | \row |
240 | \li \c TapHandler.WithinBounds |
241 | \image pointerHandlers/tapHandlerButtonWithinBounds.webp |
242 | Grab on press: \e exclusive |
243 | \li If the \l eventPoint leaves the bounds of the \c parent Item, the tap |
244 | gesture is canceled. The TapHandler will take the |
245 | \l {QPointerEvent::setExclusiveGrabber}{exclusive grab} on |
246 | press, but will release the grab as soon as the boundary constraint |
247 | is no longer satisfied. |
248 | \snippet pointerHandlers/tapHandlerButtonWithinBounds.qml 1 |
249 | |
250 | \row |
251 | \li \c TapHandler.ReleaseWithinBounds |
252 | \image pointerHandlers/tapHandlerButtonReleaseWithinBounds.webp |
253 | Grab on press: \e exclusive |
254 | \li At the time of release (the mouse button is released or the finger |
255 | is lifted), if the \l eventPoint is outside the bounds of the |
256 | \c parent Item, a tap gesture is not recognized. This corresponds to |
257 | typical behavior for button widgets: you can cancel a click by |
258 | dragging outside the button, and you can also change your mind by |
259 | dragging back inside the button before release. Note that it's |
260 | necessary for TapHandler to take the |
261 | \l {QPointerEvent::setExclusiveGrabber}{exclusive grab} on press |
262 | and retain it until release in order to detect this gesture. |
263 | \snippet pointerHandlers/tapHandlerButtonReleaseWithinBounds.qml 1 |
264 | |
265 | \row |
266 | \li \c TapHandler.DragWithinBounds |
267 | \image pointerHandlers/dragReleaseMenu.webp |
268 | Grab on press: \e exclusive |
269 | \li On press, TapHandler takes the |
270 | \l {QPointerEvent::setExclusiveGrabber}{exclusive grab}; after that, |
271 | the \l eventPoint can be dragged within the bounds of the \c parent |
272 | item, while the \l timeHeld property keeps counting, and the |
273 | \l longPressed() signal will be emitted regardless of drag distance. |
274 | However, like \c WithinBounds, if the point leaves the bounds, |
275 | the tap gesture is \l {PointerHandler::}{canceled()}, \l active() |
276 | becomes \c false, and \l timeHeld stops counting. This is suitable |
277 | for implementing press-drag-release components, such as menus, in |
278 | which a single TapHandler detects press, \c timeHeld drives an |
279 | "opening" animation, and then the user can drag to a menu item and |
280 | release, while never leaving the bounds of the parent scene containing |
281 | the menu. This value was added in Qt 6.3. |
282 | \snippet pointerHandlers/dragReleaseMenu.qml 1 |
283 | \endtable |
284 | |
285 | The \l {Qt Quick Examples - Pointer Handlers} demonstrates some use cases for these. |
286 | |
287 | \note If you find that TapHandler is reacting in cases that conflict with |
288 | some other behavior, the first thing you should try is to think about which |
289 | \c gesturePolicy is appropriate. If you cannot fix it by changing \c gesturePolicy, |
290 | some cases are better served by adjusting \l {PointerHandler::}{grabPermissions}, |
291 | either in this handler, or in another handler that should \e prevent TapHandler |
292 | from reacting. |
293 | */ |
294 | void QQuickTapHandler::setGesturePolicy(QQuickTapHandler::GesturePolicy gesturePolicy) |
295 | { |
296 | if (m_gesturePolicy == gesturePolicy) |
297 | return; |
298 | |
299 | m_gesturePolicy = gesturePolicy; |
300 | emit gesturePolicyChanged(); |
301 | } |
302 | |
303 | /*! |
304 | \qmlproperty enumeration QtQuick::TapHandler::exclusiveSignals |
305 | \since 6.5 |
306 | |
307 | Determines the exclusivity of the singleTapped() and doubleTapped() signals. |
308 | |
309 | \value NotExclusive (the default) singleTapped() and doubleTapped() are |
310 | emitted immediately when the user taps once or twice, respectively. |
311 | |
312 | \value SingleTap singleTapped() is emitted immediately when the user taps |
313 | once, and doubleTapped() is never emitted. |
314 | |
315 | \value DoubleTap doubleTapped() is emitted immediately when the user taps |
316 | twice, and singleTapped() is never emitted. |
317 | |
318 | \value (SingleTap | DoubleTap) Both signals are delayed until |
319 | QStyleHints::mouseDoubleClickInterval(), such that either singleTapped() |
320 | or doubleTapped() can be emitted, but not both. But if 3 or more taps |
321 | occur within \c mouseDoubleClickInterval, neither signal is emitted. |
322 | |
323 | \note The remaining signals such as tapped() and tapCountChanged() are |
324 | always emitted immediately, regardless of this property. |
325 | */ |
326 | void QQuickTapHandler::setExclusiveSignals(QQuickTapHandler::ExclusiveSignals exc) |
327 | { |
328 | if (m_exclusiveSignals == exc) |
329 | return; |
330 | |
331 | m_exclusiveSignals = exc; |
332 | emit exclusiveSignalsChanged(); |
333 | } |
334 | |
335 | /*! |
336 | \qmlproperty bool QtQuick::TapHandler::pressed |
337 | \readonly |
338 | |
339 | Holds true whenever the mouse or touch point is pressed, |
340 | and any movement since the press is compliant with the current |
341 | \l gesturePolicy. When the \l eventPoint is released or the policy is |
342 | violated, \e pressed will change to false. |
343 | */ |
344 | void QQuickTapHandler::setPressed(bool press, bool cancel, QPointerEvent *event, QEventPoint &point) |
345 | { |
346 | if (m_pressed != press) { |
347 | qCDebug(lcTapHandler) << objectName() << "pressed" << m_pressed << "->" << press |
348 | << (cancel ? "CANCEL" : "" ) << point << "gp" << m_gesturePolicy; |
349 | m_pressed = press; |
350 | connectPreRenderSignal(conn: press); |
351 | updateTimeHeld(); |
352 | if (press) { |
353 | m_longPressTimer.start(msec: longPressThresholdMilliseconds(), obj: this); |
354 | m_holdTimer.start(); |
355 | } else { |
356 | m_longPressTimer.stop(); |
357 | m_holdTimer.invalidate(); |
358 | } |
359 | if (press) { |
360 | // on press, grab before emitting changed signals |
361 | if (m_gesturePolicy == DragThreshold) |
362 | setPassiveGrab(event, point, grab: press); |
363 | else |
364 | setExclusiveGrab(ev: event, point, grab: press); |
365 | } |
366 | if (!cancel && !press && parentContains(point)) { |
367 | if (point.timeHeld() < longPressThreshold()) { |
368 | // Assuming here that pointerEvent()->timestamp() is in ms. |
369 | const quint64 ts = event->timestamp(); |
370 | const quint64 interval = ts - m_lastTapTimestamp; |
371 | const auto distanceSquared = QVector2D(point.scenePosition() - m_lastTapPos).lengthSquared(); |
372 | const auto singleTapReleasedButton = event->isSinglePointEvent() ? static_cast<QSinglePointEvent *>(event)->button() : Qt::NoButton; |
373 | if ((interval < m_multiTapInterval && distanceSquared < |
374 | (event->device()->type() == QInputDevice::DeviceType::Mouse ? |
375 | m_mouseMultiClickDistanceSquared : m_touchMultiTapDistanceSquared)) |
376 | && m_singleTapReleasedButton == singleTapReleasedButton) { |
377 | ++m_tapCount; |
378 | } else { |
379 | m_singleTapReleasedButton = singleTapReleasedButton; |
380 | m_singleTapReleasedPoint = point; |
381 | m_tapCount = 1; |
382 | } |
383 | qCDebug(lcTapHandler) << objectName() << "tapped" << m_tapCount << "times; interval since last:" << interval |
384 | << "sec; distance since last:" << qSqrt(v: distanceSquared); |
385 | auto button = event->isSinglePointEvent() ? static_cast<QSinglePointEvent *>(event)->button() : Qt::NoButton; |
386 | emit tapped(eventPoint: point, button); |
387 | emit tapCountChanged(); |
388 | switch (m_exclusiveSignals) { |
389 | case NotExclusive: |
390 | if (m_tapCount == 1) |
391 | emit singleTapped(eventPoint: point, button); |
392 | else if (m_tapCount == 2) |
393 | emit doubleTapped(eventPoint: point, button); |
394 | break; |
395 | case SingleTap: |
396 | if (m_tapCount == 1) |
397 | emit singleTapped(eventPoint: point, button); |
398 | break; |
399 | case DoubleTap: |
400 | if (m_tapCount == 2) |
401 | emit doubleTapped(eventPoint: point, button); |
402 | break; |
403 | case (SingleTap | DoubleTap): |
404 | if (m_tapCount == 1) { |
405 | qCDebug(lcTapHandler) << objectName() << "waiting to emit singleTapped:" << m_multiTapInterval << "ms" ; |
406 | m_doubleTapTimer.start(msec: m_multiTapInterval, obj: this); |
407 | } |
408 | } |
409 | qCDebug(lcTapHandler) << objectName() << "tap" << m_tapCount << "after" << event->timestamp() - m_lastTapTimestamp << "ms" ; |
410 | |
411 | m_lastTapTimestamp = ts; |
412 | m_lastTapPos = point.scenePosition(); |
413 | } else { |
414 | qCDebug(lcTapHandler) << objectName() << "tap threshold" << longPressThreshold() << "exceeded:" << point.timeHeld(); |
415 | } |
416 | } |
417 | emit pressedChanged(); |
418 | if (!press && m_gesturePolicy != DragThreshold) { |
419 | // on release, ungrab after emitting changed signals |
420 | setExclusiveGrab(ev: event, point, grab: press); |
421 | } |
422 | if (cancel) { |
423 | emit canceled(point); |
424 | setExclusiveGrab(ev: event, point, grab: false); |
425 | // In case there is a filtering parent (Flickable), we should not give up the passive grab, |
426 | // so that it can continue to filter future events. |
427 | d_func()->reset(); |
428 | emit pointChanged(); |
429 | } |
430 | } |
431 | } |
432 | |
433 | void QQuickTapHandler::onGrabChanged(QQuickPointerHandler *grabber, QPointingDevice::GrabTransition transition, |
434 | QPointerEvent *ev, QEventPoint &point) |
435 | { |
436 | QQuickSinglePointHandler::onGrabChanged(grabber, transition, event: ev, point); |
437 | bool isCanceled = transition == QPointingDevice::CancelGrabExclusive || transition == QPointingDevice::CancelGrabPassive; |
438 | if (grabber == this && (isCanceled || point.state() == QEventPoint::Released)) |
439 | setPressed(press: false, cancel: isCanceled, event: ev, point); |
440 | } |
441 | |
442 | void QQuickTapHandler::connectPreRenderSignal(bool conn) |
443 | { |
444 | auto par = parentItem(); |
445 | if (!par) |
446 | return; |
447 | if (conn) |
448 | connect(sender: par->window(), signal: &QQuickWindow::beforeSynchronizing, context: this, slot: &QQuickTapHandler::updateTimeHeld); |
449 | else |
450 | disconnect(sender: par->window(), signal: &QQuickWindow::beforeSynchronizing, receiver: this, slot: &QQuickTapHandler::updateTimeHeld); |
451 | } |
452 | |
453 | void QQuickTapHandler::updateTimeHeld() |
454 | { |
455 | emit timeHeldChanged(); |
456 | } |
457 | |
458 | /*! |
459 | \qmlproperty int QtQuick::TapHandler::tapCount |
460 | \readonly |
461 | |
462 | The number of taps which have occurred within the time and space |
463 | constraints to be considered a single gesture. The counter is reset to 1 |
464 | if the button changed. For example, to detect a triple-tap, you can write: |
465 | |
466 | \qml |
467 | Rectangle { |
468 | width: 100; height: 30 |
469 | signal tripleTap |
470 | TapHandler { |
471 | acceptedButtons: Qt.AllButtons |
472 | onTapped: if (tapCount == 3) tripleTap() |
473 | } |
474 | } |
475 | \endqml |
476 | */ |
477 | |
478 | /*! |
479 | \qmlproperty real QtQuick::TapHandler::timeHeld |
480 | \readonly |
481 | |
482 | The amount of time in seconds that a pressed point has been held, without |
483 | moving beyond the drag threshold. It will be updated at least once per |
484 | frame rendered, which enables rendering an animation showing the progress |
485 | towards an action which will be triggered by a long-press. It is also |
486 | possible to trigger one of a series of actions depending on how long the |
487 | press is held. |
488 | |
489 | A value of less than zero means no point is being held within this |
490 | handler's \l [QML] Item. |
491 | |
492 | \note If \l gesturePolicy is set to \c TapHandler.DragWithinBounds, |
493 | \c timeHeld does not stop counting even when the pressed point is moved |
494 | beyond the drag threshold, but only when the point leaves the \l {Item::} |
495 | {parent} item's \l {QtQuick::Item::contains()}{bounds}. |
496 | */ |
497 | |
498 | /*! |
499 | \qmlsignal QtQuick::TapHandler::tapped(eventPoint eventPoint, Qt::MouseButton button) |
500 | |
501 | This signal is emitted each time the \c parent Item is tapped. |
502 | |
503 | That is, if you press and release a touchpoint or button within a time |
504 | period less than \l longPressThreshold, while any movement does not exceed |
505 | the drag threshold, then the \c tapped signal will be emitted at the time |
506 | of release. The \a eventPoint signal parameter contains information |
507 | from the release event about the point that was tapped, and \a button |
508 | is the \l {Qt::MouseButton}{mouse button} that was clicked, or \c NoButton |
509 | on a touchscreen. |
510 | |
511 | \snippet pointerHandlers/tapHandlerOnTapped.qml 0 |
512 | */ |
513 | |
514 | /*! |
515 | \qmlsignal QtQuick::TapHandler::singleTapped(eventPoint eventPoint, Qt::MouseButton button) |
516 | \since 5.11 |
517 | |
518 | This signal is emitted when the \c parent Item is tapped once. |
519 | After an amount of time greater than QStyleHints::mouseDoubleClickInterval, |
520 | it can be tapped again; but if the time until the next tap is less, |
521 | \l tapCount will increase. The \a eventPoint signal parameter contains |
522 | information from the release event about the point that was tapped, and |
523 | \a button is the \l {Qt::MouseButton}{mouse button} that was clicked, or |
524 | \c NoButton on a touchscreen. |
525 | */ |
526 | |
527 | /*! |
528 | \qmlsignal QtQuick::TapHandler::doubleTapped(eventPoint eventPoint, Qt::MouseButton button) |
529 | \since 5.11 |
530 | |
531 | This signal is emitted when the \c parent Item is tapped twice within a |
532 | short span of time (QStyleHints::mouseDoubleClickInterval()) and distance |
533 | (QStyleHints::mouseDoubleClickDistance() or |
534 | QStyleHints::touchDoubleTapDistance()). This signal always occurs after |
535 | \l singleTapped, \l tapped, and \l tapCountChanged. The \a eventPoint |
536 | signal parameter contains information from the release event about the |
537 | point that was tapped, and \a button is the |
538 | \l {Qt::MouseButton}{mouse button} that was clicked, or \c NoButton |
539 | on a touchscreen. |
540 | */ |
541 | |
542 | /*! |
543 | \qmlsignal QtQuick::TapHandler::longPressed() |
544 | |
545 | This signal is emitted when the \c parent Item is pressed and held for a |
546 | time period greater than \l longPressThreshold. That is, if you press and |
547 | hold a touchpoint or button, while any movement does not exceed the drag |
548 | threshold, then the \c longPressed signal will be emitted at the time that |
549 | \l timeHeld exceeds \l longPressThreshold. |
550 | */ |
551 | |
552 | /*! |
553 | \qmlsignal QtQuick::TapHandler::tapCountChanged() |
554 | |
555 | This signal is emitted when the \c parent Item is tapped once or more (within |
556 | a specified time and distance span) and when the present \c tapCount differs |
557 | from the previous \c tapCount. |
558 | */ |
559 | QT_END_NAMESPACE |
560 | |
561 | #include "moc_qquicktaphandler_p.cpp" |
562 | |