@@ -1,5 +1,5 @@
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013-2014 Canonical, Ltd.
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -14,64 +14,32 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17+#define ACTIVETOUCHESINFO_DEBUG 0
18+#define DIRECTIONALDRAGAREA_DEBUG 0
17#include "DirectionalDragArea.h"
20#include "DirectionalDragArea.h"
22+#include <QQuickWindow>
19#include <QtCore/qmath.h>
23#include <QtCore/qmath.h>
20−#include <QtCore/QTimer>
21#include <QDebug>
24#include <QDebug>
23−using namespace UbuntuGestures;
26+#pragma GCC diagnostic push
27+#pragma GCC diagnostic ignored "-pedantic"
28+#include <private/qquickwindow_p.h>
29+#pragma GCC diagnostic pop
25−#define DIRECTIONALDRAGAREA_DEBUG 0
31+// local
32+#include "TouchOwnershipEvent.h"
33+#include "TouchRegistry.h"
34+#include "UnownedTouchEvent.h"
36+using namespace UbuntuGestures;
27#if DIRECTIONALDRAGAREA_DEBUG
38#if DIRECTIONALDRAGAREA_DEBUG
28#define DDA_DEBUG(msg) qDebug("[DDA] " msg)
39#define DDA_DEBUG(msg) qDebug("[DDA] " msg)
29−namespace {
40+#include "DebugHelpers.h"
30−QString touchPointStateToString(Qt::TouchPointState state) {
32− case Qt::TouchPointPressed:
33− return QString("pressed");
34− case Qt::TouchPointMoved:
35− return QString("moved");
36− case Qt::TouchPointStationary:
37− return QString("stationary");
38− default: // Qt::TouchPointReleased:
39− return QString("released");
42−QString touchEventToString(QTouchEvent *ev)
46− switch (ev->type()) {
47− case QEvent::TouchBegin:
48− message.append("TouchBegin ");
50− case QEvent::TouchUpdate:
51− message.append("TouchUpdate ");
53− case QEvent::TouchEnd:
54− message.append("TouchEnd ");
56− default: //QEvent::TouchCancel
57− message.append("TouchCancel ");
60− for (int i=0; i < ev->touchPoints().size(); ++i) {
62− const QTouchEvent::TouchPoint& touchPoint = ev->touchPoints().at(i);
64− QString("(id:%1, state:%2, scenePos:(%3,%4)) ")
65− .arg(touchPoint.id())
66− .arg(touchPointStateToString(touchPoint.state()))
67− .arg(touchPoint.scenePos().x())
68− .arg(touchPoint.scenePos().y())
75const char *statusToString(DirectionalDragArea::Status status)
43const char *statusToString(DirectionalDragArea::Status status)
77 if (status == DirectionalDragArea::WaitingForTouch) {
45 if (status == DirectionalDragArea::WaitingForTouch) {
@@ -88,23 +56,6 @@ const char *statusToString(DirectionalDragArea::Status status)
88#define DDA_DEBUG(msg) do{}while(0)
56#define DDA_DEBUG(msg) do{}while(0)
89#endif // DIRECTIONALDRAGAREA_DEBUG
57#endif // DIRECTIONALDRAGAREA_DEBUG
91−// Essentially a QTimer wrapper
92−class RecognitionTimer : public UbuntuGestures::AbstractTimer
96− RecognitionTimer(QObject *parent) : UbuntuGestures::AbstractTimer(parent) {
97− m_timer.setSingleShot(false);
98− connect(&m_timer, &QTimer::timeout,
99− this, &UbuntuGestures::AbstractTimer::timeout);
101− virtual int interval() const { return m_timer.interval(); }
102− virtual void setInterval(int msecs) { m_timer.setInterval(msecs); }
103− virtual void start() { m_timer.start(); UbuntuGestures::AbstractTimer::start(); }
104− virtual void stop() { m_timer.stop(); UbuntuGestures::AbstractTimer::stop(); }
109DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
60DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
110 : QQuickItem(parent)
61 : QQuickItem(parent)
@@ -126,12 +77,14 @@ DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
126 , m_timeSource(new RealTimeSource)
77 , m_timeSource(new RealTimeSource)
127 , m_activeTouches(m_timeSource)
78 , m_activeTouches(m_timeSource)
129− setRecognitionTimer(new RecognitionTimer(this));
80+ setRecognitionTimer(new Timer(this));
130 m_recognitionTimer->setInterval(60);
81 m_recognitionTimer->setInterval(60);
82+ m_recognitionTimer->setSingleShot(false);
132 m_velocityCalculator = new AxisVelocityCalculator(this);
84 m_velocityCalculator = new AxisVelocityCalculator(this);
134− connect(this, &QQuickItem::enabledChanged, this, &DirectionalDragArea::onEnabledChanged);
86+ connect(this, &QQuickItem::enabledChanged, this, &DirectionalDragArea::giveUpIfDisabledOrInvisible);
87+ connect(this, &QQuickItem::visibleChanged, this, &DirectionalDragArea::giveUpIfDisabledOrInvisible);
137Direction::Type DirectionalDragArea::direction() const
90Direction::Type DirectionalDragArea::direction() const
@@ -214,6 +167,7 @@ void DirectionalDragArea::setRecognitionTimer(UbuntuGestures::AbstractTimer *tim
215 int interval = 0;
168 int interval = 0;
216 bool timerWasRunning = false;
169 bool timerWasRunning = false;
170+ bool wasSingleShot = false;
218 // can be null when called from the constructor
172 // can be null when called from the constructor
219 if (m_recognitionTimer) {
173 if (m_recognitionTimer) {
@@ -226,6 +180,7 @@ void DirectionalDragArea::setRecognitionTimer(UbuntuGestures::AbstractTimer *tim
227 m_recognitionTimer = timer;
181 m_recognitionTimer = timer;
228 timer->setInterval(interval);
182 timer->setInterval(interval);
183+ timer->setSingleShot(wasSingleShot);
229 connect(timer, &UbuntuGestures::AbstractTimer::timeout,
184 connect(timer, &UbuntuGestures::AbstractTimer::timeout,
230 this, &DirectionalDragArea::checkSpeed);
185 this, &DirectionalDragArea::checkSpeed);
231 if (timerWasRunning) {
186 if (timerWasRunning) {
@@ -280,8 +235,146 @@ qreal DirectionalDragArea::touchSceneY() const
280 return m_previousScenePos.y();
235 return m_previousScenePos.y();
238+bool DirectionalDragArea::event(QEvent *event)
240+ if (event->type() == TouchOwnershipEvent::touchOwnershipEventType()) {
241+ touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(event));
243+ } else if (event->type() == UnownedTouchEvent::unownedTouchEventType()) {
244+ unownedTouchEvent(static_cast<UnownedTouchEvent *>(event));
247+ return QQuickItem::event(event);
251+void DirectionalDragArea::touchOwnershipEvent(TouchOwnershipEvent *event)
253+ if (event->gained()) {
255+ ids.append(event->touchId());
256+ DDA_DEBUG("grabbing touch");
257+ grabTouchPoints(ids);
259+ // Work around for Qt bug. If we grab a touch that is being used for mouse pointer
260+ // emulation it will cause the emulation logic to go nuts.
261+ // Thus we have to also grab the mouse in this case.
263+ // The fix for this bug has landed in Qt 5.4 (https://codereview.qt-project.org/96887)
264+ // TODO: Remove this workaround once we start using Qt 5.4
266+ QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(window());
267+ if (windowPrivate->touchMouseId == event->touchId() && window()->mouseGrabberItem()) {
268+ DDA_DEBUG("removing mouse grabber");
269+ window()->mouseGrabberItem()->ungrabMouse();
273+ // We still wanna know when it ends for keeping the composition time window up-to-date
274+ TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
276+ setStatus(WaitingForTouch);
280+void DirectionalDragArea::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent)
282+ QTouchEvent *event = unownedTouchEvent->touchEvent();
284+ Q_ASSERT(!event->touchPointStates().testFlag(Qt::TouchPointPressed));
286+ #if DIRECTIONALDRAGAREA_DEBUG
287+ // TODO Consider using qCDebug() when available (Qt 5.2)
288+ qDebug() << "[DDA] Unowned" << m_timeSource->msecsSinceReference()
289+ << qPrintable(touchEventToString(event));
293+ case WaitingForTouch:
297+ Q_ASSERT(isEnabled() && isVisible());
298+ unownedTouchEvent_undecided(unownedTouchEvent);
300+ default: // Recognized:
305+ m_activeTouches.update(event);
308+void DirectionalDragArea::unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent)
310+ const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(unownedTouchEvent->touchEvent());
312+ qCritical() << "DirectionalDragArea[status=Undecided]: touch " << m_touchId
313+ << "missing from UnownedTouchEvent without first reaching state Qt::TouchPointReleased. "
314+ "Considering it as released.";
316+ TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
317+ setStatus(WaitingForTouch);
321+ const QPointF &touchScenePos = touchPoint->scenePos();
323+ if (touchPoint->state() == Qt::TouchPointReleased) {
324+ // touch has ended before recognition concluded
325+ DDA_DEBUG("Touch has ended before recognition concluded");
326+ TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
327+ emitSignalIfTapped();
328+ setStatus(WaitingForTouch);
332+ m_previousDampedScenePos.setX(m_dampedScenePos.x());
333+ m_previousDampedScenePos.setY(m_dampedScenePos.y());
334+ m_dampedScenePos.update(touchScenePos);
335+ updateVelocityCalculator(touchScenePos);
337+ if (!pointInsideAllowedArea()) {
338+ DDA_DEBUG("Rejecting gesture because touch point is outside allowed area.");
339+ TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
340+ // We still wanna know when it ends for keeping the composition time window up-to-date
341+ TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
342+ setStatus(WaitingForTouch);
346+ if (!movingInRightDirection()) {
347+ DDA_DEBUG("Rejecting gesture because touch point is moving in the wrong direction.");
348+ TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
349+ // We still wanna know when it ends for keeping the composition time window up-to-date
350+ TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
351+ setStatus(WaitingForTouch);
355+ setPreviousPos(touchPoint->pos());
356+ setPreviousScenePos(touchScenePos);
358+ if (isWithinTouchCompositionWindow()) {
359+ // There's still time for some new touch to appear and ruin our party as it would be combined
360+ // with our m_touchId one and therefore deny the possibility of a single-finger gesture.
361+ DDA_DEBUG("Sill within composition window. Let's wait more.");
365+ if (movedFarEnough(touchScenePos)) {
366+ TouchRegistry::instance()->requestTouchOwnership(m_touchId, this);
367+ setStatus(Recognized);
369+ DDA_DEBUG("Didn't move far enough yet. Let's wait more.");
283void DirectionalDragArea::touchEvent(QTouchEvent *event)
373void DirectionalDragArea::touchEvent(QTouchEvent *event)
375+ // TODO: Consider when more than one touch starts in the same event (although it's not possible
376+ // with Mir's android-input). Have to track them all. Consider it a plus/bonus.
285 #if DIRECTIONALDRAGAREA_DEBUG
378 #if DIRECTIONALDRAGAREA_DEBUG
286 // TODO Consider using qCDebug() when available (Qt 5.2)
379 // TODO Consider using qCDebug() when available (Qt 5.2)
287 qDebug() << "[DDA]" << m_timeSource->msecsSinceReference()
380 qDebug() << "[DDA]" << m_timeSource->msecsSinceReference()
@@ -310,29 +403,34 @@ void DirectionalDragArea::touchEvent(QTouchEvent *event)
311void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
404void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
406+ // TODO: accept/reject is for the whole event, not per touch id. See how that affects us.
313 if (!event->touchPointStates().testFlag(Qt::TouchPointPressed)) {
408 if (!event->touchPointStates().testFlag(Qt::TouchPointPressed)) {
314 // Nothing to see here. No touch starting in this event.
409 // Nothing to see here. No touch starting in this event.
413+ // to be proven wrong, if that's the case
414+ bool allGood = true;
318 if (isWithinTouchCompositionWindow()) {
416 if (isWithinTouchCompositionWindow()) {
319 // too close to the last touch start. So we consider them as starting roughly at the same time.
417 // too close to the last touch start. So we consider them as starting roughly at the same time.
320 // Can't be a single-touch gesture.
418 // Can't be a single-touch gesture.
321 #if DIRECTIONALDRAGAREA_DEBUG
419 #if DIRECTIONALDRAGAREA_DEBUG
322 qDebug("[DDA] A new touch point came in but we're still within time composition window. Ignoring it.");
420 qDebug("[DDA] A new touch point came in but we're still within time composition window. Ignoring it.");
324− return;
422+ allGood = false;
327 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
425 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
329 const QTouchEvent::TouchPoint *newTouchPoint = nullptr;
427 const QTouchEvent::TouchPoint *newTouchPoint = nullptr;
330− for (int i = 0; i < touchPoints.count(); ++i) {
428+ for (int i = 0; i < touchPoints.count() && allGood; ++i) {
331 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
429 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
332 if (touchPoint.state() == Qt::TouchPointPressed) {
430 if (touchPoint.state() == Qt::TouchPointPressed) {
333 if (newTouchPoint) {
431 if (newTouchPoint) {
334 // more than one touch starting in this QTouchEvent. Can't be a single-touch gesture
432 // more than one touch starting in this QTouchEvent. Can't be a single-touch gesture
335− return;
433+ allGood = false;
337 // that's our candidate
435 // that's our candidate
338 m_touchId = touchPoint.id();
436 m_touchId = touchPoint.id();
@@ -341,85 +439,61 @@ void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
344− Q_ASSERT(newTouchPoint);
443+ Q_ASSERT(newTouchPoint);
346− // If we have made this far, we are good to go to the next status.
348− m_startPos = newTouchPoint->pos();
445+ m_startPos = newTouchPoint->pos();
349− m_startScenePos = newTouchPoint->scenePos();
446+ m_startScenePos = newTouchPoint->scenePos();
350− m_touchId = newTouchPoint->id();
447+ m_touchId = newTouchPoint->id();
351− m_dampedScenePos.reset(m_startScenePos);
448+ m_dampedScenePos.reset(m_startScenePos);
352− m_velocityCalculator->setTrackedPosition(0.);
449+ m_velocityCalculator->setTrackedPosition(0.);
353− m_velocityCalculator->reset();
450+ m_velocityCalculator->reset();
354− m_numSamplesOnLastSpeedCheck = 0;
451+ m_numSamplesOnLastSpeedCheck = 0;
355− m_silenceTime = 0;
452+ m_silenceTime = 0;
356− setPreviousPos(m_startPos);
453+ setPreviousPos(m_startPos);
357− setPreviousScenePos(m_startScenePos);
454+ setPreviousScenePos(m_startScenePos);
358− updateSceneDirectionVector();
455+ updateSceneDirectionVector();
457+ if (recognitionIsDisabled()) {
458+ // Behave like a dumb TouchArea
459+ TouchRegistry::instance()->requestTouchOwnership(m_touchId, this);
460+ setStatus(Recognized);
463+ // just monitor the touch points for now.
464+ TouchRegistry::instance()->addCandidateOwnerForTouch(m_touchId, this);
360− setStatus(Undecided);
466+ setStatus(Undecided);
467+ // Let the item below have it. We will monitor it and grab it later if a gesture
472+ watchPressedTouchPoints(touchPoints);
363void DirectionalDragArea::touchEvent_undecided(QTouchEvent *event)
477void DirectionalDragArea::touchEvent_undecided(QTouchEvent *event)
479+ Q_ASSERT(event->type() == QEvent::TouchBegin);
365− const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);
480+ Q_ASSERT(fetchTargetTouchPoint(event) == nullptr);
367− if (!touchPoint) {
482+ // We're not interested in new touch points. We already have our candidate (m_touchId).
368− qCritical() << "DirectionalDragArea[status=Undecided]: touch " << m_touchId
483+ // But we do want to know when those new touches end for keeping the composition time
369− << "missing from QTouchEvent without first reaching state Qt::TouchPointReleased. "
484+ // window up-to-date
370− "Considering it as released.";
485+ event->ignore();
371− setStatus(WaitingForTouch);
486+ watchPressedTouchPoints(event->touchPoints());
375− const QPointF &touchScenePos = touchPoint->scenePos();
377− if (touchPoint->state() == Qt::TouchPointReleased) {
378− // touch has ended before recognition concluded
379− DDA_DEBUG("Touch has ended before recognition concluded");
380− setStatus(WaitingForTouch);
385 if (event->touchPointStates().testFlag(Qt::TouchPointPressed) && isWithinTouchCompositionWindow()) {
488 if (event->touchPointStates().testFlag(Qt::TouchPointPressed) && isWithinTouchCompositionWindow()) {
386 // multi-finger drags are not accepted
489 // multi-finger drags are not accepted
387 DDA_DEBUG("Multi-finger drags are not accepted");
490 DDA_DEBUG("Multi-finger drags are not accepted");
388− setStatus(WaitingForTouch);
392− m_previousDampedScenePos.setX(m_dampedScenePos.x());
492+ TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
393− m_previousDampedScenePos.setY(m_dampedScenePos.y());
493+ // We still wanna know when it ends for keeping the composition time window up-to-date
394− m_dampedScenePos.update(touchScenePos);
494+ TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
395− updateVelocityCalculator(touchScenePos);
397− if (!pointInsideAllowedArea()) {
398− DDA_DEBUG("Rejecting gesture because touch point is outside allowed area.");
399− setStatus(WaitingForTouch);
403− if (!movingInRightDirection()) {
404− DDA_DEBUG("Rejecting gesture becauuse touch point is moving in the wrong direction.");
405 setStatus(WaitingForTouch);
496 setStatus(WaitingForTouch);
409− setPreviousPos(touchPoint->pos());
410− setPreviousScenePos(touchScenePos);
412− if (isWithinTouchCompositionWindow()) {
413− // There's still time for some new touch to appear and ruin our party as it would be combined
414− // with our m_touchId one and therefore deny the possibility of a single-finger gesture.
415− DDA_DEBUG("Sill within composition window. Let's wait more.");
419− if (movedFarEnough(touchScenePos)) {
420− setStatus(Recognized);
422− DDA_DEBUG("Didn't move far enough yet. Let's wait more.");
@@ -437,11 +511,35 @@ void DirectionalDragArea::touchEvent_recognized(QTouchEvent *event)
437 setPreviousScenePos(touchPoint->scenePos());
511 setPreviousScenePos(touchPoint->scenePos());
439 if (touchPoint->state() == Qt::TouchPointReleased) {
513 if (touchPoint->state() == Qt::TouchPointReleased) {
514+ emitSignalIfTapped();
440 setStatus(WaitingForTouch);
515 setStatus(WaitingForTouch);
520+void DirectionalDragArea::watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints)
522+ for (int i = 0; i < touchPoints.count(); ++i) {
523+ const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
524+ if (touchPoint.state() == Qt::TouchPointPressed) {
525+ TouchRegistry::instance()->addTouchWatcher(touchPoint.id(), this);
530+bool DirectionalDragArea::recognitionIsDisabled() const
532+ return distanceThreshold() <= 0 && compositionTime() <= 0;
535+void DirectionalDragArea::emitSignalIfTapped()
537+ qint64 touchDuration = m_timeSource->msecsSinceReference() - m_activeTouches.touchStartTime(m_touchId);
538+ if (touchDuration <= maxTapDuration()) {
445const QTouchEvent::TouchPoint *DirectionalDragArea::fetchTargetTouchPoint(QTouchEvent *event)
543const QTouchEvent::TouchPoint *DirectionalDragArea::fetchTargetTouchPoint(QTouchEvent *event)
447 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
545 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
@@ -509,12 +607,16 @@ bool DirectionalDragArea::movedFarEnough(const QPointF &point) const
510void DirectionalDragArea::checkSpeed()
608void DirectionalDragArea::checkSpeed()
610+ Q_ASSERT(m_status == Undecided);
512 if (m_velocityCalculator->numSamples() >= AxisVelocityCalculator::MIN_SAMPLES_NEEDED) {
612 if (m_velocityCalculator->numSamples() >= AxisVelocityCalculator::MIN_SAMPLES_NEEDED) {
513 qreal speed = qFabs(m_velocityCalculator->calculate());
613 qreal speed = qFabs(m_velocityCalculator->calculate());
514 qreal minSpeedMsecs = m_minSpeed / 1000.0;
614 qreal minSpeedMsecs = m_minSpeed / 1000.0;
516 if (speed < minSpeedMsecs) {
616 if (speed < minSpeedMsecs) {
517 DDA_DEBUG("Rejecting gesture because it's below minimum speed.");
617 DDA_DEBUG("Rejecting gesture because it's below minimum speed.");
618+ TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
619+ TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
518 setStatus(WaitingForTouch);
620 setStatus(WaitingForTouch);
@@ -523,7 +625,9 @@ void DirectionalDragArea::checkSpeed()
523 m_silenceTime += m_recognitionTimer->interval();
625 m_silenceTime += m_recognitionTimer->interval();
525 if (m_silenceTime > m_maxSilenceTime) {
627 if (m_silenceTime > m_maxSilenceTime) {
526− DDA_DEBUG("Rejecting gesture because it's silence time has been exceeded.");
628+ DDA_DEBUG("Rejecting gesture because its silence time has been exceeded.");
629+ TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
630+ TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
527 setStatus(WaitingForTouch);
631 setStatus(WaitingForTouch);
@@ -532,10 +636,19 @@ void DirectionalDragArea::checkSpeed()
532 m_numSamplesOnLastSpeedCheck = m_velocityCalculator->numSamples();
636 m_numSamplesOnLastSpeedCheck = m_velocityCalculator->numSamples();
535−void DirectionalDragArea::onEnabledChanged()
639+void DirectionalDragArea::giveUpIfDisabledOrInvisible()
641+ if (!isEnabled() || !isVisible()) {
642+ if (m_status == Undecided) {
643+ TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
644+ // We still wanna know when it ends for keeping the composition time window up-to-date
645+ TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
537− if (!isEnabled() && m_status != WaitingForTouch) {
648+ if (m_status != WaitingForTouch) {
649+ DDA_DEBUG("Resetting status because got disabled or made invisible");
538− setStatus(WaitingForTouch);
650+ setStatus(WaitingForTouch);
@@ -631,7 +744,9 @@ void DirectionalDragArea::updateVelocityCalculator(const QPointF &scenePos)
632bool DirectionalDragArea::isWithinTouchCompositionWindow()
745bool DirectionalDragArea::isWithinTouchCompositionWindow()
748+ compositionTime() > 0 &&
634− return !m_activeTouches.isEmpty() &&
749+ !m_activeTouches.isEmpty() &&
635 m_timeSource->msecsSinceReference() <=
750 m_timeSource->msecsSinceReference() <=
636 m_activeTouches.mostRecentStartTime() + (qint64)compositionTime();
751 m_activeTouches.mostRecentStartTime() + (qint64)compositionTime();
@@ -639,18 +754,17 @@ bool DirectionalDragArea::isWithinTouchCompositionWindow()
639//************************** ActiveTouchesInfo **************************
754//************************** ActiveTouchesInfo **************************
641DirectionalDragArea::ActiveTouchesInfo::ActiveTouchesInfo(const SharedTimeSource &timeSource)
756DirectionalDragArea::ActiveTouchesInfo::ActiveTouchesInfo(const SharedTimeSource &timeSource)
642− : m_timeSource(timeSource), m_lastUsedIndex(-1)
757+ : m_timeSource(timeSource)
644− // Estimate of the maximum number of active touches we might reach.
645− // Not a problem if it ends up being an underestimate as this is just
650void DirectionalDragArea::ActiveTouchesInfo::update(QTouchEvent *event)
761void DirectionalDragArea::ActiveTouchesInfo::update(QTouchEvent *event)
652 if (!(event->touchPointStates() & (Qt::TouchPointPressed | Qt::TouchPointReleased))) {
763 if (!(event->touchPointStates() & (Qt::TouchPointPressed | Qt::TouchPointReleased))) {
653 // nothing to update
764 // nothing to update
765+ #if ACTIVETOUCHESINFO_DEBUG
766+ qDebug("[DDA::ActiveTouchesInfo] Nothing to Update");
@@ -658,74 +772,88 @@ void DirectionalDragArea::ActiveTouchesInfo::update(QTouchEvent *event)
658 for (int i = 0; i < touchPoints.count(); ++i) {
772 for (int i = 0; i < touchPoints.count(); ++i) {
659 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
773 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
660 if (touchPoint.state() == Qt::TouchPointPressed) {
774 if (touchPoint.state() == Qt::TouchPointPressed) {
661− addTouchPoint(touchPoint);
775+ addTouchPoint(touchPoint.id());
662 } else if (touchPoint.state() == Qt::TouchPointReleased) {
776 } else if (touchPoint.state() == Qt::TouchPointReleased) {
663− removeTouchPoint(touchPoint);
777+ removeTouchPoint(touchPoint.id());
782+#if ACTIVETOUCHESINFO_DEBUG
668−void DirectionalDragArea::ActiveTouchesInfo::addTouchPoint(const QTouchEvent::TouchPoint &touchPoint)
783+QString DirectionalDragArea::ActiveTouchesInfo::toString()
670− ActiveTouchInfo &activeTouchInfo = getEmptySlot();
785+ QString string = "(";
671− activeTouchInfo.id = touchPoint.id();
786+
672− activeTouchInfo.startTime = m_timeSource->msecsSinceReference();
787+ {
788+ QTextStream stream(&string);
789+ m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {
790+ stream << "(id=" << touchInfo->id << ",startTime=" << touchInfo->startTime << ")";
799+#endif // ACTIVETOUCHESINFO_DEBUG
675−void DirectionalDragArea::ActiveTouchesInfo::removeTouchPoint(const QTouchEvent::TouchPoint &touchPoint)
801+void DirectionalDragArea::ActiveTouchesInfo::addTouchPoint(int touchId)
677− for (int i = 0; i <= m_lastUsedIndex; ++i) {
803+ ActiveTouchInfo &activeTouchInfo = m_touchInfoPool.getEmptySlot();
678− if (touchPoint.id() == m_vector.at(i).id) {
804+ activeTouchInfo.id = touchId;
679− freeSlot(i);
805+ activeTouchInfo.startTime = m_timeSource->msecsSinceReference();
681− }
807+ #if ACTIVETOUCHESINFO_DEBUG
682− }
808+ qDebug() << "[DDA::ActiveTouchesInfo]" << qPrintable(toString());
683− Q_ASSERT(false); // shouldn't reach this point
809+ #endif
686−DirectionalDragArea::ActiveTouchInfo &DirectionalDragArea::ActiveTouchesInfo::getEmptySlot()
812+qint64 DirectionalDragArea::ActiveTouchesInfo::touchStartTime(int touchId)
688− Q_ASSERT(m_lastUsedIndex < m_vector.size());
814+ qint64 result = -1;
690− // Look for an in-between vacancy first
816+ m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {
691− for (int i = 0; i < m_lastUsedIndex; ++i) {
817+ if (touchId == touchInfo->id) {
692− ActiveTouchInfo &activeTouchInfo = m_vector[i];
818+ result = touchInfo->startTime;
693− if (!activeTouchInfo.isValid()) {
819+ return false;
694− return activeTouchInfo;
820+ } else {
699− if (m_lastUsedIndex >= m_vector.size()) {
700− m_vector.resize(m_lastUsedIndex + 1);
703− return m_vector[m_lastUsedIndex];
825+ Q_ASSERT(result != -1);
706−void DirectionalDragArea::ActiveTouchesInfo::freeSlot(int index)
829+void DirectionalDragArea::ActiveTouchesInfo::removeTouchPoint(int touchId)
708− m_vector[index].reset();
831+ m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {
709− if (index == m_lastUsedIndex) {
832+ if (touchId == touchInfo->id) {
710− do {
833+ m_touchInfoPool.freeSlot(touchInfo);
711− --m_lastUsedIndex;
834+ return false;
712− } while (m_lastUsedIndex >= 0 && !m_vector.at(m_lastUsedIndex).isValid());
835+ } else {
840+ #if ACTIVETOUCHESINFO_DEBUG
841+ qDebug() << "[DDA::ActiveTouchesInfo]" << qPrintable(toString());
716qint64 DirectionalDragArea::ActiveTouchesInfo::mostRecentStartTime()
845qint64 DirectionalDragArea::ActiveTouchesInfo::mostRecentStartTime()
718− Q_ASSERT(m_lastUsedIndex >= 0);
847+ Q_ASSERT(!m_touchInfoPool.isEmpty());
849+ qint64 highestStartTime = -1;
720− qint64 highestStartTime = m_vector.at(0).startTime;
723− const ActiveTouchInfo &activeTouchInfo = m_vector.at(i);
851+ m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &activeTouchInfo) {
724− if (activeTouchInfo.isValid() && activeTouchInfo.startTime > highestStartTime) {
852+ if (activeTouchInfo->startTime > highestStartTime) {
725− highestStartTime = activeTouchInfo.startTime;
853+ highestStartTime = activeTouchInfo->startTime;
727− ++i;
855+ return true;
728− } while (i < m_lastUsedIndex);
856+ });
730 return highestStartTime;
858 return highestStartTime;