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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtdeclarative/src/quick/handlers/qquicktaphandler.cpp