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