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