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