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, keeping accepted=true causes deliverPressOrReleaseEvent() to set handlersOnly=true,
146 // which prevents sibling items with MouseArea from also receiving the press (QTBUG-145896).
147 if (isTouch && m_gesturePolicy == DragThreshold)
148 point.setAccepted(false);
149}
150
151/*!
152 \qmlproperty real QtQuick::TapHandler::longPressThreshold
153
154 The time in seconds that an \l eventPoint must be pressed in order to
155 trigger a long press gesture and emit the \l longPressed() signal, if the
156 value is greater than \c 0. If the point is released before this time
157 limit, a tap can be detected if the \l gesturePolicy constraint is
158 satisfied. If \c longPressThreshold is \c 0, the timer is disabled and the
159 signal will not be emitted. If \c longPressThreshold is set to \c undefined,
160 the default value is used instead, and can be read back from this property.
161
162 The default value is QStyleHints::mousePressAndHoldInterval() converted to
163 seconds.
164*/
165qreal QQuickTapHandler::longPressThreshold() const
166{
167 return m_longPressThreshold / qreal(1000);
168}
169
170void QQuickTapHandler::setLongPressThreshold(qreal longPressThreshold)
171{
172 if (longPressThreshold < 0) {
173 resetLongPressThreshold();
174 return;
175 }
176 int ms = qRound(longPressThreshold * 1000);
177 if (m_longPressThreshold == ms)
178 return;
179
180 m_longPressThreshold = ms;
181 emit longPressThresholdChanged();
182}
183
184void QQuickTapHandler::resetLongPressThreshold()
185{
186 int ms = QGuiApplication::styleHints()->mousePressAndHoldInterval();
187 if (m_longPressThreshold == ms)
188 return;
189
190 m_longPressThreshold = ms;
191 emit longPressThresholdChanged();
192}
193
194void QQuickTapHandler::timerEvent(QTimerEvent *event)
195{
196 if (event->timerId() == m_longPressTimer.timerId()) {
197 m_longPressTimer.stop();
198 qCDebug(lcTapHandler) << objectName() << "longPressed";
199 m_longPressed = true;
200 emit longPressed();
201 } else if (event->timerId() == m_doubleTapTimer.timerId()) {
202 m_doubleTapTimer.stop();
203 qCDebug(lcTapHandler) << objectName() << "double-tap timer expired; taps:" << m_tapCount;
204 Q_ASSERT(m_exclusiveSignals == (SingleTap | DoubleTap));
205 if (m_tapCount == 1)
206 emit singleTapped(m_singleTapReleasedPoint, m_singleTapReleasedButton);
207 else if (m_tapCount == 2)
208 emit doubleTapped(m_singleTapReleasedPoint, m_singleTapReleasedButton);
209 }
210}
211
212/*!
213 \qmlproperty enumeration QtQuick::TapHandler::gesturePolicy
214
215 The spatial constraint for a tap or long press gesture to be recognized,
216 in addition to the constraint that the release must occur before
217 \l longPressThreshold has elapsed. If these constraints are not satisfied,
218 the \l tapped signal is not emitted, and \l tapCount is not incremented.
219 If the spatial constraint is violated, \l pressed transitions immediately
220 from true to false, regardless of the time held.
221
222 The \c gesturePolicy also affects grab behavior as described below.
223
224 \table
225 \header
226 \li Constant
227 \li Description
228 \row
229 \li \c TapHandler.DragThreshold
230 \image pointerHandlers/tapHandlerOverlappingButtons.webp
231 {Two overlapping Click Me buttons both responding to a single tap}
232 Grab on press: \e passive
233 \li (the default value) The \l eventPoint must not move significantly.
234 If the mouse, finger or stylus moves past the system-wide drag
235 threshold (QStyleHints::startDragDistance), the tap gesture is
236 canceled, even if the device or finger is still pressed. This policy
237 can be useful whenever TapHandler needs to cooperate with other
238 input handlers (for example \l DragHandler) or event-handling Items
239 (for example \l {Qt Quick Controls}), because in this case TapHandler
240 will not take the exclusive grab, but merely a
241 \l {QPointerEvent::addPassiveGrabber()}{passive grab}.
242 That is, \c DragThreshold is especially useful to \e augment
243 existing behavior: it reacts to tap/click/long-press even when
244 another item or handler is already reacting, perhaps even in a
245 different layer of the UI. The following snippet shows one
246 TapHandler as used in one component; but if we stack up two
247 instances of the component, you will see the handlers in both of them
248 react simultaneously when a press occurs over both of them, because
249 the passive grab does not stop event propagation:
250 \quotefromfile pointerHandlers/tapHandlerOverlappingButtons.qml
251 \skipto Item
252 \printuntil component Button
253 \skipto TapHandler
254 \printuntil }
255 \skipuntil Text {
256 \skipuntil }
257 \printuntil Button
258 \printuntil Button
259 \printuntil }
260
261 \row
262 \li \c TapHandler.WithinBounds
263 \image pointerHandlers/tapHandlerButtonWithinBounds.webp
264 {Click Me button canceling tap when mouse moves outside}
265 Grab on press: \e exclusive
266 \li If the \l eventPoint leaves the bounds of the \c parent Item, the tap
267 gesture is canceled. The TapHandler will take the
268 \l {QPointerEvent::setExclusiveGrabber}{exclusive grab} on
269 press, but will release the grab as soon as the boundary constraint
270 is no longer satisfied.
271 \snippet pointerHandlers/tapHandlerButtonWithinBounds.qml 1
272
273 \row
274 \li \c TapHandler.ReleaseWithinBounds
275 \image pointerHandlers/tapHandlerButtonReleaseWithinBounds.webp
276 {dragging outside a Click Me button and back inside before release}
277 Grab on press: \e exclusive
278 \li At the time of release (the mouse button is released or the finger
279 is lifted), if the \l eventPoint is outside the bounds of the
280 \c parent Item, a tap gesture is not recognized. This corresponds to
281 typical behavior for button widgets: you can cancel a click by
282 dragging outside the button, and you can also change your mind by
283 dragging back inside the button before release. Note that it's
284 necessary for TapHandler to take the
285 \l {QPointerEvent::setExclusiveGrabber}{exclusive grab} on press
286 and retain it until release in order to detect this gesture.
287 \snippet pointerHandlers/tapHandlerButtonReleaseWithinBounds.qml 1
288
289 \row
290 \li \c TapHandler.DragWithinBounds
291 \image pointerHandlers/dragReleaseMenu.webp
292 {Long press showing context menu with top, middle, bottom options}
293 Grab on press: \e exclusive
294 \li On press, TapHandler takes the
295 \l {QPointerEvent::setExclusiveGrabber}{exclusive grab}; after that,
296 the \l eventPoint can be dragged within the bounds of the \c parent
297 item, while the \l timeHeld property keeps counting, and the
298 \l longPressed() signal will be emitted regardless of drag distance.
299 However, like \c WithinBounds, if the point leaves the bounds,
300 the tap gesture is \l {PointerHandler::}{canceled()}, \l active()
301 becomes \c false, and \l timeHeld stops counting. This is suitable
302 for implementing press-drag-release components, such as menus, in
303 which a single TapHandler detects press, \c timeHeld drives an
304 "opening" animation, and then the user can drag to a menu item and
305 release, while never leaving the bounds of the parent scene containing
306 the menu. This value was added in Qt 6.3.
307 \snippet pointerHandlers/dragReleaseMenu.qml 1
308 \endtable
309
310 The \l {Qt Quick Examples - Pointer Handlers} demonstrates some use cases for these.
311
312 \note If you find that TapHandler is reacting in cases that conflict with
313 some other behavior, the first thing you should try is to think about which
314 \c gesturePolicy is appropriate. If you cannot fix it by changing \c gesturePolicy,
315 some cases are better served by adjusting \l {PointerHandler::}{grabPermissions},
316 either in this handler, or in another handler that should \e prevent TapHandler
317 from reacting.
318*/
319void QQuickTapHandler::setGesturePolicy(QQuickTapHandler::GesturePolicy gesturePolicy)
320{
321 if (m_gesturePolicy == gesturePolicy)
322 return;
323
324 m_gesturePolicy = gesturePolicy;
325 emit gesturePolicyChanged();
326}
327
328/*!
329 \qmlproperty enumeration QtQuick::TapHandler::exclusiveSignals
330 \since 6.5
331
332 Determines the exclusivity of the singleTapped() and doubleTapped() signals.
333
334 \value NotExclusive (the default) singleTapped() and doubleTapped() are
335 emitted immediately when the user taps once or twice, respectively.
336
337 \value SingleTap singleTapped() is emitted immediately when the user taps
338 once, and doubleTapped() is never emitted.
339
340 \value DoubleTap doubleTapped() is emitted immediately when the user taps
341 twice, and singleTapped() is never emitted.
342
343 \value (SingleTap | DoubleTap) Both signals are delayed until
344 QStyleHints::mouseDoubleClickInterval(), such that either singleTapped()
345 or doubleTapped() can be emitted, but not both. But if 3 or more taps
346 occur within \c mouseDoubleClickInterval, neither signal is emitted.
347
348 \note The remaining signals such as tapped() and tapCountChanged() are
349 always emitted immediately, regardless of this property.
350*/
351void QQuickTapHandler::setExclusiveSignals(QQuickTapHandler::ExclusiveSignals exc)
352{
353 if (m_exclusiveSignals == exc)
354 return;
355
356 m_exclusiveSignals = exc;
357 emit exclusiveSignalsChanged();
358}
359
360/*!
361 \qmlproperty bool QtQuick::TapHandler::pressed
362 \readonly
363
364 Holds true whenever the mouse or touch point is pressed,
365 and any movement since the press is compliant with the current
366 \l gesturePolicy. When the \l eventPoint is released or the policy is
367 violated, \e pressed will change to false.
368*/
369void QQuickTapHandler::setPressed(bool press, bool cancel, QPointerEvent *event, QEventPoint &point)
370{
371 if (m_pressed != press) {
372 qCDebug(lcTapHandler) << objectName() << "pressed" << m_pressed << "->" << press
373 << (cancel ? "CANCEL" : "") << point << "gp" << m_gesturePolicy;
374 m_pressed = press;
375 connectPreRenderSignal(press);
376 updateTimeHeld();
377 if (press) {
378 if (m_longPressThreshold > 0)
379 m_longPressTimer.start(m_longPressThreshold, this);
380 m_holdTimer.start();
381 } else {
382 m_longPressTimer.stop();
383 m_holdTimer.invalidate();
384 }
385 if (press) {
386 // on press, grab before emitting changed signals
387 if (m_gesturePolicy == DragThreshold)
388 setPassiveGrab(event, point, press);
389 else
390 setExclusiveGrab(event, point, press);
391 }
392 if (!cancel && !press && parentContains(point)) {
393 if (m_longPressed) {
394 qCDebug(lcTapHandler) << objectName() << "long press threshold" << longPressThreshold() << "exceeded:" << point.timeHeld();
395 } else if (event) {
396 // Assuming here that pointerEvent()->timestamp() is in ms.
397 const quint64 ts = event->timestamp();
398 const quint64 interval = ts - m_lastTapTimestamp;
399 const auto distanceSquared = QVector2D(point.scenePosition() - m_lastTapPos).lengthSquared();
400 const auto singleTapReleasedButton = event->isSinglePointEvent() ? static_cast<QSinglePointEvent *>(event)->button() : Qt::NoButton;
401 if ((interval < m_multiTapInterval && distanceSquared <
402 (event->device()->type() == QInputDevice::DeviceType::Mouse ?
403 m_mouseMultiClickDistanceSquared : m_touchMultiTapDistanceSquared))
404 && m_singleTapReleasedButton == singleTapReleasedButton) {
405 ++m_tapCount;
406 } else {
407 m_singleTapReleasedButton = singleTapReleasedButton;
408 m_singleTapReleasedPoint = point;
409 m_tapCount = 1;
410 }
411 qCDebug(lcTapHandler) << objectName() << "tapped" << m_tapCount << "times; interval since last:" << interval
412 << "sec; distance since last:" << qSqrt(distanceSquared);
413 auto button = event->isSinglePointEvent() ? static_cast<QSinglePointEvent *>(event)->button() : Qt::NoButton;
414 emit tapped(point, button);
415 emit tapCountChanged();
416 switch (m_exclusiveSignals) {
417 case NotExclusive:
418 if (m_tapCount == 1)
419 emit singleTapped(point, button);
420 else if (m_tapCount == 2)
421 emit doubleTapped(point, button);
422 break;
423 case SingleTap:
424 if (m_tapCount == 1)
425 emit singleTapped(point, button);
426 break;
427 case DoubleTap:
428 if (m_tapCount == 2)
429 emit doubleTapped(point, button);
430 break;
431 case (SingleTap | DoubleTap):
432 if (m_tapCount == 1) {
433 qCDebug(lcTapHandler) << objectName() << "waiting to emit singleTapped:" << m_multiTapInterval << "ms";
434 m_doubleTapTimer.start(m_multiTapInterval, this);
435 }
436 }
437 qCDebug(lcTapHandler) << objectName() << "tap" << m_tapCount << "after" << event->timestamp() - m_lastTapTimestamp << "ms";
438
439 m_lastTapTimestamp = ts;
440 m_lastTapPos = point.scenePosition();
441 }
442 }
443 m_longPressed = false;
444 emit pressedChanged();
445 if (!press && m_gesturePolicy != DragThreshold) {
446 // on release, ungrab after emitting changed signals
447 setExclusiveGrab(event, point, press);
448 }
449 if (cancel) {
450 emit canceled(point);
451 if (event)
452 setExclusiveGrab(event, point, false);
453 // In case there is a filtering parent (Flickable), we should not give up the passive grab,
454 // so that it can continue to filter future events.
455 d_func()->reset();
456 emit pointChanged();
457 }
458 }
459}
460
461void QQuickTapHandler::onGrabChanged(QQuickPointerHandler *grabber, QPointingDevice::GrabTransition transition,
462 QPointerEvent *ev, QEventPoint &point)
463{
464 // QQuickPointerHandler::onGrabChanged() calls setActive(false) in many cases.
465 QQuickSinglePointHandler::onGrabChanged(grabber, transition, ev, point);
466 // We don't override onActiveChanged(): we could not call setPressed(false) from there anyway.
467 // But ensure that if the TapHandler just got deactivated, it's no longer pressed either.
468 const bool isCanceled = transition == QPointingDevice::CancelGrabExclusive || transition == QPointingDevice::CancelGrabPassive;
469 // But passive grab/ungrab does not change the active state, so that's not a reason to change pressed state either
470 // (i.e. when gesturePolicy == DragThreshold, TapHandler does not become active).
471 const bool passiveGrab = transition == QPointingDevice::GrabPassive || transition == QPointingDevice::UngrabPassive;
472 if (grabber == this && (isCanceled || point.state() == QEventPoint::Released || (!active() && !passiveGrab)))
473 setPressed(false, isCanceled, ev, point);
474}
475
476void QQuickTapHandler::connectPreRenderSignal(bool conn)
477{
478 // disconnect pre-existing connection, if any
479 disconnect(m_preRenderSignalConnection);
480
481 auto par = parentItem();
482 if (!par || !par->window())
483 return;
484
485 /*
486 Note: beforeSynchronizing is emitted from the SG thread, and the
487 timeHeldChanged signal can be used to do arbitrary things in user QML.
488
489 But the docs say the GUI thread is blockd, and "Therefore, it is safe
490 to access GUI thread thread data in a slot or lambda that is connected
491 with Qt::DirectConnection." We use the default AutoConnection just in case.
492 */
493 if (conn) {
494 m_preRenderSignalConnection = connect(par->window(), &QQuickWindow::beforeSynchronizing,
495 this, &QQuickTapHandler::updateTimeHeld);
496 }
497}
498
499void QQuickTapHandler::updateTimeHeld()
500{
501 emit timeHeldChanged();
502}
503
504/*!
505 \qmlproperty int QtQuick::TapHandler::tapCount
506 \readonly
507
508 The number of taps which have occurred within the time and space
509 constraints to be considered a single gesture. The counter is reset to 1
510 if the button changed. For example, to detect a triple-tap, you can write:
511
512 \qml
513 Rectangle {
514 width: 100; height: 30
515 signal tripleTap
516 TapHandler {
517 acceptedButtons: Qt.AllButtons
518 onTapped: if (tapCount == 3) tripleTap()
519 }
520 }
521 \endqml
522*/
523
524/*!
525 \qmlproperty real QtQuick::TapHandler::timeHeld
526 \readonly
527
528 The amount of time in seconds that a pressed point has been held, without
529 moving beyond the drag threshold. It will be updated at least once per
530 frame rendered, which enables rendering an animation showing the progress
531 towards an action which will be triggered by a long-press. It is also
532 possible to trigger one of a series of actions depending on how long the
533 press is held.
534
535 A value of less than zero means no point is being held within this
536 handler's \l [QML] Item.
537
538 \note If \l gesturePolicy is set to \c TapHandler.DragWithinBounds,
539 \c timeHeld does not stop counting even when the pressed point is moved
540 beyond the drag threshold, but only when the point leaves the \l {Item::}
541 {parent} item's \l {QtQuick::Item::contains()}{bounds}.
542*/
543
544/*!
545 \qmlsignal QtQuick::TapHandler::tapped(eventPoint eventPoint, Qt::MouseButton button)
546
547 This signal is emitted each time the \c parent Item is tapped.
548
549 That is, if you press and release a touchpoint or button within a time
550 period less than \l longPressThreshold, while any movement does not exceed
551 the drag threshold, then the \c tapped signal will be emitted at the time
552 of release. The \a eventPoint signal parameter contains information
553 from the release event about the point that was tapped, and \a button
554 is the \l {Qt::MouseButton}{mouse button} that was clicked, or \c NoButton
555 on a touchscreen.
556
557 \snippet pointerHandlers/tapHandlerOnTapped.qml 0
558*/
559
560/*!
561 \qmlsignal QtQuick::TapHandler::singleTapped(eventPoint eventPoint, Qt::MouseButton button)
562 \since 5.11
563
564 This signal is emitted when the \c parent Item is tapped once.
565 After an amount of time greater than QStyleHints::mouseDoubleClickInterval,
566 it can be tapped again; but if the time until the next tap is less,
567 \l tapCount will increase. The \a eventPoint signal parameter contains
568 information from the release event about the point that was tapped, and
569 \a button is the \l {Qt::MouseButton}{mouse button} that was clicked, or
570 \c NoButton on a touchscreen.
571*/
572
573/*!
574 \qmlsignal QtQuick::TapHandler::doubleTapped(eventPoint eventPoint, Qt::MouseButton button)
575 \since 5.11
576
577 This signal is emitted when the \c parent Item is tapped twice within a
578 short span of time (QStyleHints::mouseDoubleClickInterval()) and distance
579 (QStyleHints::mouseDoubleClickDistance() or
580 QStyleHints::touchDoubleTapDistance()). This signal always occurs after
581 \l singleTapped, \l tapped, and \l tapCountChanged. The \a eventPoint
582 signal parameter contains information from the release event about the
583 point that was tapped, and \a button is the
584 \l {Qt::MouseButton}{mouse button} that was clicked, or \c NoButton
585 on a touchscreen.
586*/
587
588/*!
589 \qmlsignal QtQuick::TapHandler::longPressed()
590
591 This signal is emitted when the \c parent Item is pressed and held for a
592 time period greater than \l longPressThreshold. That is, if you press and
593 hold a touchpoint or button, while any movement does not exceed the drag
594 threshold, then the \c longPressed signal will be emitted at the time that
595 \l timeHeld exceeds \l longPressThreshold.
596*/
597
598/*!
599 \qmlsignal QtQuick::TapHandler::tapCountChanged()
600
601 This signal is emitted when the \c parent Item is tapped once or more (within
602 a specified time and distance span) and when the present \c tapCount differs
603 from the previous \c tapCount.
604*/
605QT_END_NAMESPACE
606
607#include "moc_qquicktaphandler_p.cpp"
Combined button and popup list for selecting options.