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