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