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