1// Copyright (C) 2016 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 "private/qgesturemanager_p.h"
5#include "private/qstandardgestures_p.h"
6#include "private/qwidget_p.h"
7#include "private/qgesture_p.h"
8#if QT_CONFIG(graphicsview)
9#include "private/qgraphicsitem_p.h"
10#include "qgraphicsitem.h"
11#endif
12#include "private/qevent_p.h"
13#include "private/qapplication_p.h"
14#include "private/qwidgetwindow_p.h"
15#include "qgesture.h"
16#include "qevent.h"
17
18#ifdef Q_OS_MACOS
19#include "qmacgesturerecognizer_p.h"
20#endif
21
22#include "qdebug.h"
23#include <QtCore/QLoggingCategory>
24#include <QtCore/QVarLengthArray>
25
26#ifndef QT_NO_GESTURES
27
28QT_BEGIN_NAMESPACE
29
30Q_STATIC_LOGGING_CATEGORY(lcGestureManager, "qt.widgets.gestures")
31
32#if !defined(Q_OS_MACOS)
33static inline int panTouchPoints()
34{
35 // Override by environment variable for testing.
36 static const char panTouchPointVariable[] = "QT_PAN_TOUCHPOINTS";
37 if (qEnvironmentVariableIsSet(varName: panTouchPointVariable)) {
38 bool ok;
39 const int result = qEnvironmentVariableIntValue(varName: panTouchPointVariable, ok: &ok);
40 if (ok && result >= 1)
41 return result;
42 qWarning(msg: "Ignoring invalid value of %s", panTouchPointVariable);
43 }
44 // Pan should use 1 finger on a touch screen and 2 fingers on touch pads etc.
45 // where 1 finger movements are used for mouse event synthetization. For now,
46 // default to 2 until all classes inheriting QScrollArea are fixed to handle it
47 // correctly.
48 return 2;
49}
50#endif
51
52QGestureManager::QGestureManager(QObject *parent)
53 : QObject(parent), m_lastCustomGestureId(Qt::CustomGesture)
54{
55 qRegisterMetaType<Qt::GestureState>();
56
57#if defined(Q_OS_MACOS)
58 registerGestureRecognizer(new QMacSwipeGestureRecognizer);
59 registerGestureRecognizer(new QMacPinchGestureRecognizer);
60 registerGestureRecognizer(new QMacPanGestureRecognizer);
61#else
62 registerGestureRecognizer(recognizer: new QPanGestureRecognizer(panTouchPoints()));
63 registerGestureRecognizer(recognizer: new QPinchGestureRecognizer);
64 registerGestureRecognizer(recognizer: new QSwipeGestureRecognizer);
65 registerGestureRecognizer(recognizer: new QTapGestureRecognizer);
66#endif
67 registerGestureRecognizer(recognizer: new QTapAndHoldGestureRecognizer);
68}
69
70QGestureManager::~QGestureManager()
71{
72 qDeleteAll(c: m_recognizers);
73 for (auto it = m_obsoleteGestures.cbegin(), end = m_obsoleteGestures.cend(); it != end; ++it) {
74 qDeleteAll(c: it.value());
75 delete it.key();
76 }
77}
78
79Qt::GestureType QGestureManager::registerGestureRecognizer(QGestureRecognizer *recognizer)
80{
81 const QScopedPointer<QGesture> dummy(recognizer->create(target: nullptr));
82 if (Q_UNLIKELY(!dummy)) {
83 qWarning(msg: "QGestureManager::registerGestureRecognizer: "
84 "the recognizer fails to create a gesture object, skipping registration.");
85 return Qt::GestureType(0);
86 }
87 Qt::GestureType type = dummy->gestureType();
88 if (type == Qt::CustomGesture) {
89 // generate a new custom gesture id
90 ++m_lastCustomGestureId;
91 type = Qt::GestureType(m_lastCustomGestureId);
92 }
93 m_recognizers.insert(key: type, value: recognizer);
94 return type;
95}
96
97void QGestureManager::unregisterGestureRecognizer(Qt::GestureType type)
98{
99 QList<QGestureRecognizer *> list = m_recognizers.values(key: type);
100 m_recognizers.remove(key: type);
101 for (const auto &[g, recognizer] : std::as_const(t&: m_gestureToRecognizer).asKeyValueRange()) {
102 if (list.contains(t: recognizer)) {
103 m_deletedRecognizers.insert(key: g, value: recognizer);
104 }
105 }
106
107 for (const auto &[objectGesture, gestures] : std::as_const(t&: m_objectGestures).asKeyValueRange()) {
108 if (objectGesture.gesture == type) {
109 for (QGesture *g : gestures) {
110 auto it = m_gestureToRecognizer.constFind(key: g);
111 if (it != m_gestureToRecognizer.cend() && it.value()) {
112 QGestureRecognizer *recognizer = it.value();
113 m_gestureToRecognizer.erase(it);
114 m_obsoleteGestures[recognizer].insert(value: g);
115 }
116 }
117 }
118 }
119
120 for (QGestureRecognizer *recognizer : std::as_const(t&: list)) {
121 const bool isObsolete = m_obsoleteGestures.contains(key: recognizer);
122 const bool isDeleted = m_deletedRecognizers.values().contains(t: recognizer);
123
124 if (!isObsolete && !isDeleted)
125 delete recognizer;
126 }
127}
128
129void QGestureManager::cleanupCachedGestures(QObject *target, Qt::GestureType type)
130{
131 const auto iter = m_objectGestures.find(key: {target, type});
132 if (iter == m_objectGestures.end())
133 return;
134
135 const QList<QGesture *> &gestures = iter.value();
136 for (auto &e : m_obsoleteGestures) {
137 for (QGesture *g : gestures)
138 e -= g;
139 }
140 for (QGesture *g : gestures) {
141 m_deletedRecognizers.remove(key: g);
142 m_gestureToRecognizer.remove(key: g);
143 m_maybeGestures.remove(value: g);
144 m_activeGestures.remove(value: g);
145 m_gestureOwners.remove(key: g);
146 m_gestureTargets.remove(key: g);
147 m_gesturesToDelete.insert(value: g);
148 }
149
150 m_objectGestures.erase(it: iter);
151}
152
153// get or create a QGesture object that will represent the state for a given object, used by the recognizer
154QGesture *QGestureManager::getState(QObject *object, QGestureRecognizer *recognizer, Qt::GestureType type)
155{
156 // if the widget is being deleted we should be careful not to
157 // create a new state, as it will create QWeakPointer which doesn't work
158 // from the destructor.
159 if (object->isWidgetType()) {
160 if (static_cast<QWidget *>(object)->d_func()->data.in_destructor)
161 return nullptr;
162 } else if (QGesture *g = qobject_cast<QGesture *>(object)) {
163 return g;
164#if QT_CONFIG(graphicsview)
165 } else {
166 Q_ASSERT(qobject_cast<QGraphicsObject *>(object));
167 QGraphicsObject *graphicsObject = static_cast<QGraphicsObject *>(object);
168 if (graphicsObject->QGraphicsItem::d_func()->inDestructor)
169 return nullptr;
170#endif
171 }
172
173 // check if the QGesture for this recognizer has already been created
174 const auto states = m_objectGestures.value(key: QGestureManager::ObjectGesture(object, type));
175 for (QGesture *state : states) {
176 if (m_gestureToRecognizer.value(key: state) == recognizer)
177 return state;
178 }
179
180 Q_ASSERT(recognizer);
181 QGesture *state = recognizer->create(target: object);
182 if (!state)
183 return nullptr;
184 state->setParent(this);
185 if (state->gestureType() == Qt::CustomGesture) {
186 // if the recognizer didn't fill in the gesture type, then this
187 // is a custom gesture with autogenerated id and we fill it.
188 state->d_func()->gestureType = type;
189 if (lcGestureManager().isDebugEnabled())
190 state->setObjectName(QString::number((int)type));
191 }
192 m_objectGestures[QGestureManager::ObjectGesture(object, type)].append(t: state);
193 m_gestureToRecognizer[state] = recognizer;
194 m_gestureOwners[state] = object;
195
196 return state;
197}
198
199static bool logIgnoredEvent(QEvent::Type t)
200{
201 bool result = false;
202 switch (t) {
203 case QEvent::MouseButtonPress:
204 case QEvent::MouseButtonRelease:
205 case QEvent::MouseButtonDblClick:
206 case QEvent::MouseMove:
207 case QEvent::TouchBegin:
208 case QEvent::TouchUpdate:
209 case QEvent::TouchCancel:
210 case QEvent::TouchEnd:
211 case QEvent::TabletEnterProximity:
212 case QEvent::TabletLeaveProximity:
213 case QEvent::TabletMove:
214 case QEvent::TabletPress:
215 case QEvent::TabletRelease:
216 case QEvent::GraphicsSceneMouseDoubleClick:
217 case QEvent::GraphicsSceneMousePress:
218 case QEvent::GraphicsSceneMouseRelease:
219 case QEvent::GraphicsSceneMouseMove:
220 result = true;
221 break;
222 default:
223 break;
224
225 }
226 return result;
227}
228
229bool QGestureManager::filterEventThroughContexts(const QMultiMap<QObject *,
230 Qt::GestureType> &contexts,
231 QEvent *event)
232{
233 QSet<QGesture *> triggeredGestures;
234 QSet<QGesture *> finishedGestures;
235 QSet<QGesture *> newMaybeGestures;
236 QSet<QGesture *> notGestures;
237
238 // TODO: sort contexts by the gesture type and check if one of the contexts
239 // is already active.
240
241 bool consumeEventHint = false;
242
243 // filter the event through recognizers
244 typedef QMultiMap<QObject *, Qt::GestureType>::const_iterator ContextIterator;
245 ContextIterator contextEnd = contexts.end();
246 for (ContextIterator context = contexts.begin(); context != contextEnd; ++context) {
247 Qt::GestureType gestureType = context.value();
248 const QMultiMap<Qt::GestureType, QGestureRecognizer *> &const_recognizers = m_recognizers;
249 QMultiMap<Qt::GestureType, QGestureRecognizer *>::const_iterator
250 typeToRecognizerIterator = const_recognizers.lowerBound(key: gestureType),
251 typeToRecognizerEnd = const_recognizers.upperBound(key: gestureType);
252 for (; typeToRecognizerIterator != typeToRecognizerEnd; ++typeToRecognizerIterator) {
253 QGestureRecognizer *recognizer = typeToRecognizerIterator.value();
254 QObject *target = context.key();
255 QGesture *state = getState(object: target, recognizer, type: gestureType);
256 if (!state)
257 continue;
258 QGestureRecognizer::Result recognizerResult = recognizer->recognize(state, watched: target, event);
259 QGestureRecognizer::Result recognizerState = recognizerResult & QGestureRecognizer::ResultState_Mask;
260 QGestureRecognizer::Result resultHint = recognizerResult & QGestureRecognizer::ResultHint_Mask;
261 if (recognizerState == QGestureRecognizer::TriggerGesture) {
262 qCDebug(lcGestureManager) << "QGestureManager:Recognizer: gesture triggered: " << state << event;
263 triggeredGestures << state;
264 } else if (recognizerState == QGestureRecognizer::FinishGesture) {
265 qCDebug(lcGestureManager) << "QGestureManager:Recognizer: gesture finished: " << state << event;
266 finishedGestures << state;
267 } else if (recognizerState == QGestureRecognizer::MayBeGesture) {
268 qCDebug(lcGestureManager) << "QGestureManager:Recognizer: maybe gesture: " << state << event;
269 newMaybeGestures << state;
270 } else if (recognizerState == QGestureRecognizer::CancelGesture) {
271 qCDebug(lcGestureManager) << "QGestureManager:Recognizer: not gesture: " << state << event;
272 notGestures << state;
273 } else if (recognizerState == QGestureRecognizer::Ignore) {
274 if (logIgnoredEvent(t: event->type()))
275 qCDebug(lcGestureManager) << "QGestureManager:Recognizer: ignored the event: " << state << event;
276 } else {
277 if (logIgnoredEvent(t: event->type())) {
278 qCDebug(lcGestureManager) << "QGestureManager:Recognizer: hm, lets assume the recognizer"
279 << "ignored the event: " << state << event;
280 }
281 }
282 if (resultHint & QGestureRecognizer::ConsumeEventHint) {
283 qCDebug(lcGestureManager) << "QGestureManager: we were asked to consume the event: "
284 << state << event;
285 consumeEventHint = true;
286 }
287 }
288 }
289 if (!triggeredGestures.isEmpty() || !finishedGestures.isEmpty()
290 || !newMaybeGestures.isEmpty() || !notGestures.isEmpty()) {
291 const QSet<QGesture *> startedGestures = triggeredGestures - m_activeGestures;
292 triggeredGestures &= m_activeGestures;
293
294 // check if a running gesture switched back to maybe state
295 const QSet<QGesture *> activeToMaybeGestures = m_activeGestures & newMaybeGestures;
296
297 // check if a maybe gesture switched to canceled - reset it but don't send an event
298 QSet<QGesture *> maybeToCanceledGestures = m_maybeGestures & notGestures;
299
300 // check if a running gesture switched back to not gesture state,
301 // i.e. were canceled
302 const QSet<QGesture *> canceledGestures = m_activeGestures & notGestures;
303
304 // new gestures in maybe state
305 m_maybeGestures += newMaybeGestures;
306
307 // gestures that were in maybe state
308 QSet<QGesture *> notMaybeGestures = (startedGestures | triggeredGestures
309 | finishedGestures | canceledGestures
310 | notGestures);
311 m_maybeGestures -= notMaybeGestures;
312
313 Q_ASSERT((startedGestures & finishedGestures).isEmpty());
314 Q_ASSERT((startedGestures & newMaybeGestures).isEmpty());
315 Q_ASSERT((startedGestures & canceledGestures).isEmpty());
316 Q_ASSERT((finishedGestures & newMaybeGestures).isEmpty());
317 Q_ASSERT((finishedGestures & canceledGestures).isEmpty());
318 Q_ASSERT((canceledGestures & newMaybeGestures).isEmpty());
319
320 const QSet<QGesture *> notStarted = finishedGestures - m_activeGestures;
321 if (!notStarted.isEmpty()) {
322 // there are some gestures that claim to be finished, but never started.
323 // probably those are "singleshot" gestures so we'll fake the started state.
324 for (QGesture *gesture : notStarted)
325 gesture->d_func()->state = Qt::GestureStarted;
326 QSet<QGesture *> undeliveredGestures;
327 deliverEvents(gestures: notStarted, undeliveredGestures: &undeliveredGestures);
328 finishedGestures -= undeliveredGestures;
329 }
330
331 m_activeGestures += startedGestures;
332 // sanity check: all triggered gestures should already be in active gestures list
333 Q_ASSERT((m_activeGestures & triggeredGestures).size() == triggeredGestures.size());
334 m_activeGestures -= finishedGestures;
335 m_activeGestures -= activeToMaybeGestures;
336 m_activeGestures -= canceledGestures;
337
338 // set the proper gesture state on each gesture
339 for (QGesture *gesture : startedGestures)
340 gesture->d_func()->state = Qt::GestureStarted;
341 for (QGesture *gesture : std::as_const(t&: triggeredGestures))
342 gesture->d_func()->state = Qt::GestureUpdated;
343 for (QGesture *gesture : std::as_const(t&: finishedGestures))
344 gesture->d_func()->state = Qt::GestureFinished;
345 for (QGesture *gesture : canceledGestures)
346 gesture->d_func()->state = Qt::GestureCanceled;
347 for (QGesture *gesture : activeToMaybeGestures)
348 gesture->d_func()->state = Qt::GestureFinished;
349
350 if (!m_activeGestures.isEmpty() || !m_maybeGestures.isEmpty() ||
351 !startedGestures.isEmpty() || !triggeredGestures.isEmpty() ||
352 !finishedGestures.isEmpty() || !canceledGestures.isEmpty()) {
353 qCDebug(lcGestureManager) << "QGestureManager::filterEventThroughContexts:"
354 << "\n\tactiveGestures:" << m_activeGestures
355 << "\n\tmaybeGestures:" << m_maybeGestures
356 << "\n\tstarted:" << startedGestures
357 << "\n\ttriggered:" << triggeredGestures
358 << "\n\tfinished:" << finishedGestures
359 << "\n\tcanceled:" << canceledGestures
360 << "\n\tmaybe-canceled:" << maybeToCanceledGestures;
361 }
362
363 QSet<QGesture *> undeliveredGestures;
364 deliverEvents(gestures: startedGestures+triggeredGestures+finishedGestures+canceledGestures,
365 undeliveredGestures: &undeliveredGestures);
366
367 for (QGesture *g : startedGestures) {
368 if (undeliveredGestures.contains(value: g))
369 continue;
370 if (g->gestureCancelPolicy() == QGesture::CancelAllInContext) {
371 qCDebug(lcGestureManager) << "lets try to cancel some";
372 // find gestures in context in Qt::GestureStarted or Qt::GestureUpdated state and cancel them
373 cancelGesturesForChildren(originatingGesture: g);
374 }
375 }
376
377 m_activeGestures -= undeliveredGestures;
378
379 // reset gestures that ended
380 const QSet<QGesture *> endedGestures =
381 finishedGestures + canceledGestures + undeliveredGestures + maybeToCanceledGestures;
382 for (QGesture *gesture : endedGestures) {
383 recycle(gesture);
384 m_gestureTargets.remove(key: gesture);
385 }
386 }
387 //Clean up the Gestures
388 qDeleteAll(c: m_gesturesToDelete);
389 m_gesturesToDelete.clear();
390
391 return consumeEventHint;
392}
393
394// Cancel all gestures of children of the widget that original is associated with
395void QGestureManager::cancelGesturesForChildren(QGesture *original)
396{
397 Q_ASSERT(original);
398 QWidget *originatingWidget = m_gestureTargets.value(key: original);
399 Q_ASSERT(originatingWidget);
400 if (!originatingWidget)
401 return;
402
403 // iterate over all active gestures and all maybe gestures
404 // for each find the owner
405 // if the owner is part of our sub-hierarchy, cancel it.
406
407 QSet<QGesture*> cancelledGestures;
408 QSet<QGesture*>::Iterator iter = m_activeGestures.begin();
409 while (iter != m_activeGestures.end()) {
410 QWidget *widget = m_gestureTargets.value(key: *iter);
411 // note that we don't touch the gestures for our originatingWidget
412 if (widget != originatingWidget && originatingWidget->isAncestorOf(child: widget)) {
413 qCDebug(lcGestureManager) << " found a gesture to cancel" << (*iter);
414 (*iter)->d_func()->state = Qt::GestureCanceled;
415 cancelledGestures << *iter;
416 iter = m_activeGestures.erase(i: iter);
417 } else {
418 ++iter;
419 }
420 }
421
422 // TODO handle 'maybe' gestures too
423
424 // sort them per target widget by cherry picking from almostCanceledGestures and delivering
425 QSet<QGesture *> almostCanceledGestures = cancelledGestures;
426 while (!almostCanceledGestures.isEmpty()) {
427 QWidget *target = nullptr;
428 QSet<QGesture*> gestures;
429 iter = almostCanceledGestures.begin();
430 // sort per target widget
431 while (iter != almostCanceledGestures.end()) {
432 QWidget *widget = m_gestureTargets.value(key: *iter);
433 if (target == nullptr)
434 target = widget;
435 if (target == widget) {
436 gestures << *iter;
437 iter = almostCanceledGestures.erase(i: iter);
438 } else {
439 ++iter;
440 }
441 }
442 Q_ASSERT(target);
443
444 QSet<QGesture*> undeliveredGestures;
445 deliverEvents(gestures, undeliveredGestures: &undeliveredGestures);
446 }
447
448 for (iter = cancelledGestures.begin(); iter != cancelledGestures.end(); ++iter)
449 recycle(gesture: *iter);
450}
451
452void QGestureManager::cleanupGesturesForRemovedRecognizer(QGesture *gesture)
453{
454 QGestureRecognizer *recognizer = m_deletedRecognizers.value(key: gesture);
455 if (!recognizer) //The Gesture is removed while in the even loop, so the recognizers for this gestures was removed
456 return;
457 m_deletedRecognizers.remove(key: gesture);
458 if (m_deletedRecognizers.keys(value: recognizer).isEmpty()) {
459 // no more active gestures, cleanup!
460 qDeleteAll(c: m_obsoleteGestures.value(key: recognizer));
461 m_obsoleteGestures.remove(key: recognizer);
462 delete recognizer;
463 }
464}
465
466// return true if accepted (consumed)
467bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event)
468{
469 QVarLengthArray<Qt::GestureType, 16> types;
470 QMultiMap<QObject *, Qt::GestureType> contexts;
471 QWidget *w = receiver;
472 typedef QMap<Qt::GestureType, Qt::GestureFlags>::const_iterator ContextIterator;
473 if (!w->d_func()->gestureContext.isEmpty()) {
474 for(ContextIterator it = w->d_func()->gestureContext.constBegin(),
475 e = w->d_func()->gestureContext.constEnd(); it != e; ++it) {
476 types.push_back(t: it.key());
477 contexts.insert(key: w, value: it.key());
478 }
479 }
480 // find all gesture contexts for the widget tree
481 w = w->isWindow() ? nullptr : w->parentWidget();
482 while (w)
483 {
484 for (ContextIterator it = w->d_func()->gestureContext.constBegin(),
485 e = w->d_func()->gestureContext.constEnd(); it != e; ++it) {
486 if (!(it.value() & Qt::DontStartGestureOnChildren)) {
487 if (!types.contains(t: it.key())) {
488 types.push_back(t: it.key());
489 contexts.insert(key: w, value: it.key());
490 }
491 }
492 }
493 if (w->isWindow())
494 break;
495 w = w->parentWidget();
496 }
497 return contexts.isEmpty() ? false : filterEventThroughContexts(contexts, event);
498}
499
500#if QT_CONFIG(graphicsview)
501bool QGestureManager::filterEvent(QGraphicsObject *receiver, QEvent *event)
502{
503 QVarLengthArray<Qt::GestureType, 16> types;
504 QMultiMap<QObject *, Qt::GestureType> contexts;
505 QGraphicsObject *item = receiver;
506 if (!item->QGraphicsItem::d_func()->gestureContext.isEmpty()) {
507 typedef QMap<Qt::GestureType, Qt::GestureFlags>::const_iterator ContextIterator;
508 for(ContextIterator it = item->QGraphicsItem::d_func()->gestureContext.constBegin(),
509 e = item->QGraphicsItem::d_func()->gestureContext.constEnd(); it != e; ++it) {
510 types.push_back(t: it.key());
511 contexts.insert(key: item, value: it.key());
512 }
513 }
514 // find all gesture contexts for the graphics object tree
515 item = item->parentObject();
516 while (item)
517 {
518 typedef QMap<Qt::GestureType, Qt::GestureFlags>::const_iterator ContextIterator;
519 for (ContextIterator it = item->QGraphicsItem::d_func()->gestureContext.constBegin(),
520 e = item->QGraphicsItem::d_func()->gestureContext.constEnd(); it != e; ++it) {
521 if (!(it.value() & Qt::DontStartGestureOnChildren)) {
522 if (!types.contains(t: it.key())) {
523 types.push_back(t: it.key());
524 contexts.insert(key: item, value: it.key());
525 }
526 }
527 }
528 item = item->parentObject();
529 }
530 return contexts.isEmpty() ? false : filterEventThroughContexts(contexts, event);
531}
532#endif
533
534bool QGestureManager::filterEvent(QObject *receiver, QEvent *event)
535{
536 // if the receiver is actually a widget, we need to call the correct event
537 // filter method.
538 QWidgetWindow *widgetWindow = qobject_cast<QWidgetWindow *>(object: receiver);
539
540 if (widgetWindow && widgetWindow->widget())
541 return filterEvent(receiver: widgetWindow->widget(), event);
542
543 QGesture *state = qobject_cast<QGesture *>(object: receiver);
544 if (!state || !m_gestureToRecognizer.contains(key: state))
545 return false;
546 QMultiMap<QObject *, Qt::GestureType> contexts;
547 contexts.insert(key: state, value: state->gestureType());
548 return filterEventThroughContexts(contexts, event);
549}
550
551void QGestureManager::getGestureTargets(const QSet<QGesture*> &gestures,
552 QHash<QWidget *, QList<QGesture *> > *conflicts,
553 QHash<QWidget *, QList<QGesture *> > *normal)
554{
555 typedef QHash<Qt::GestureType, QHash<QWidget *, QGesture *> > GestureByTypes;
556 GestureByTypes gestureByTypes;
557
558 // sort gestures by types
559 for (QGesture *gesture : gestures) {
560 QWidget *receiver = m_gestureTargets.value(key: gesture, defaultValue: nullptr);
561 Q_ASSERT(receiver);
562 if (receiver)
563 gestureByTypes[gesture->gestureType()].insert(key: receiver, value: gesture);
564 }
565
566 // for each gesture type
567 for (GestureByTypes::const_iterator git = gestureByTypes.cbegin(), gend = gestureByTypes.cend(); git != gend; ++git) {
568 const QHash<QWidget *, QGesture *> &gestures = git.value();
569 for (QHash<QWidget *, QGesture *>::const_iterator wit = gestures.cbegin(), wend = gestures.cend(); wit != wend; ++wit) {
570 QWidget *widget = wit.key();
571 QWidget *w = widget->parentWidget();
572 while (w) {
573 QMap<Qt::GestureType, Qt::GestureFlags>::const_iterator it
574 = w->d_func()->gestureContext.constFind(key: git.key());
575 if (it != w->d_func()->gestureContext.constEnd()) {
576 // i.e. 'w' listens to gesture 'type'
577 if (!(it.value() & Qt::DontStartGestureOnChildren) && w != widget) {
578 // conflicting gesture!
579 (*conflicts)[widget].append(t: wit.value());
580 break;
581 }
582 }
583 if (w->isWindow()) {
584 w = nullptr;
585 break;
586 }
587 w = w->parentWidget();
588 }
589 if (!w)
590 (*normal)[widget].append(t: wit.value());
591 }
592 }
593}
594
595void QGestureManager::deliverEvents(const QSet<QGesture *> &gestures,
596 QSet<QGesture *> *undeliveredGestures)
597{
598 if (gestures.isEmpty())
599 return;
600
601 typedef QHash<QWidget *, QList<QGesture *> > GesturesPerWidget;
602 GesturesPerWidget conflictedGestures;
603 GesturesPerWidget normalStartedGestures;
604
605 QSet<QGesture *> startedGestures;
606 // first figure out the initial receivers of gestures
607 for (QSet<QGesture *>::const_iterator it = gestures.begin(),
608 e = gestures.end(); it != e; ++it) {
609 QGesture *gesture = *it;
610 QWidget *target = m_gestureTargets.value(key: gesture, defaultValue: nullptr);
611 if (!target) {
612 // the gesture has just started and doesn't have a target yet.
613 Q_ASSERT(gesture->state() == Qt::GestureStarted);
614 if (gesture->hasHotSpot()) {
615 // guess the target widget using the hotspot of the gesture
616 QPoint pt = gesture->hotSpot().toPoint();
617 qCDebug(lcGestureManager) << __FUNCTION__ << gesture
618 << "doesn't have a target yet."
619 << "Trying hotspot at" << pt;
620 if (QWidget *topLevel = QApplication::topLevelAt(p: pt)) {
621 QWidget *child = topLevel->childAt(p: topLevel->mapFromGlobal(pt));
622 target = child ? child : topLevel;
623 }
624 } else {
625 // or use the context of the gesture
626 QObject *context = m_gestureOwners.value(key: gesture, defaultValue: 0);
627 if (context->isWidgetType())
628 target = static_cast<QWidget *>(context);
629 qCDebug(lcGestureManager) << __FUNCTION__ << gesture
630 << "doesn't have a target yet."
631 << "Trying context" << context;
632 }
633 if (target)
634 m_gestureTargets.insert(key: gesture, value: target);
635 }
636
637 Qt::GestureType gestureType = gesture->gestureType();
638 Q_ASSERT(gestureType != Qt::CustomGesture);
639 Q_UNUSED(gestureType);
640
641 if (Q_UNLIKELY(!target)) {
642 qCDebug(lcGestureManager) << __FUNCTION__ << "could not find the target for gesture"
643 << gesture->gestureType();
644 qWarning(msg: "QGestureManager::deliverEvents: could not find the target for gesture");
645 undeliveredGestures->insert(value: gesture);
646 } else {
647 if (gesture->state() == Qt::GestureStarted) {
648 startedGestures.insert(value: gesture);
649 } else {
650 normalStartedGestures[target].append(t: gesture);
651 }
652 }
653 }
654
655 getGestureTargets(gestures: startedGestures, conflicts: &conflictedGestures, normal: &normalStartedGestures);
656 qCDebug(lcGestureManager) << __FUNCTION__
657 << "\nstarted: " << startedGestures
658 << "\nconflicted: " << conflictedGestures
659 << "\nnormal: " << normalStartedGestures
660 << "\n";
661
662 // if there are conflicting gestures, send the GestureOverride event
663 for (GesturesPerWidget::const_iterator it = conflictedGestures.constBegin(),
664 e = conflictedGestures.constEnd(); it != e; ++it) {
665 QWidget *receiver = it.key();
666 const QList<QGesture *> &gestures = it.value();
667 qCDebug(lcGestureManager) << __FUNCTION__ << "sending GestureOverride to"
668 << receiver
669 << "gestures:" << gestures;
670 QGestureEvent event(gestures);
671 event.t = QEvent::GestureOverride;
672 // mark event and individual gestures as ignored
673 event.ignore();
674 for (QGesture *g : gestures)
675 event.setAccepted(g, false);
676
677 QCoreApplication::sendEvent(receiver, event: &event);
678 bool eventAccepted = event.isAccepted();
679 const auto eventGestures = event.gestures();
680 for (QGesture *gesture : eventGestures) {
681 if (eventAccepted || event.isAccepted(gesture)) {
682 QWidget *w = event.m_targetWidgets.value(key: gesture->gestureType(), defaultValue: 0);
683 Q_ASSERT(w);
684 qCDebug(lcGestureManager) << "override event: gesture was accepted:" << gesture << w;
685 QList<QGesture *> &gestures = normalStartedGestures[w];
686 gestures.append(t: gesture);
687 // override the target
688 m_gestureTargets[gesture] = w;
689 } else {
690 qCDebug(lcGestureManager) << "override event: gesture wasn't accepted. putting back:" << gesture;
691 QList<QGesture *> &gestures = normalStartedGestures[receiver];
692 gestures.append(t: gesture);
693 }
694 }
695 }
696
697 // delivering gestures that are not in conflicted state
698 for (GesturesPerWidget::const_iterator it = normalStartedGestures.constBegin(),
699 e = normalStartedGestures.constEnd(); it != e; ++it) {
700 if (!it.value().isEmpty()) {
701 qCDebug(lcGestureManager) << __FUNCTION__ << "sending to" << it.key()
702 << "gestures:" << it.value();
703 QGestureEvent event(it.value());
704 QCoreApplication::sendEvent(receiver: it.key(), event: &event);
705 bool eventAccepted = event.isAccepted();
706 const auto eventGestures = event.gestures();
707 for (QGesture *gesture : eventGestures) {
708 if (gesture->state() == Qt::GestureStarted &&
709 (eventAccepted || event.isAccepted(gesture))) {
710 QWidget *w = event.m_targetWidgets.value(key: gesture->gestureType(), defaultValue: 0);
711 Q_ASSERT(w);
712 qCDebug(lcGestureManager) << "started gesture was delivered and accepted by" << w;
713 m_gestureTargets[gesture] = w;
714 }
715 }
716 }
717 }
718}
719
720void QGestureManager::recycle(QGesture *gesture)
721{
722 QGestureRecognizer *recognizer = m_gestureToRecognizer.value(key: gesture, defaultValue: 0);
723 if (recognizer) {
724 gesture->setGestureCancelPolicy(QGesture::CancelNone);
725 recognizer->reset(state: gesture);
726 m_activeGestures.remove(value: gesture);
727 } else {
728 cleanupGesturesForRemovedRecognizer(gesture);
729 }
730}
731
732bool QGestureManager::gesturePending(QObject *o)
733{
734 const QGestureManager *gm = QGestureManager::instance(ic: DontForceCreation);
735 return gm && gm->m_gestureOwners.key(value: o);
736}
737
738QT_END_NAMESPACE
739
740#endif // QT_NO_GESTURES
741
742#include "moc_qgesturemanager_p.cpp"
743

source code of qtbase/src/widgets/kernel/qgesturemanager.cpp