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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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