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