@@ -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,26 @@
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"
19#include <QtCore/qmath.h>
22#include <QtCore/qmath.h>
20−#include <QtCore/QTimer>
21#include <QDebug>
23#include <QDebug>
23−using namespace UbuntuGestures;
25+// local
26+#include "TouchOwnershipEvent.h"
27+#include "TouchRegistry.h"
28+#include "UnownedTouchEvent.h"
25−#define DIRECTIONALDRAGAREA_DEBUG 0
30+using namespace UbuntuGestures;
27#if DIRECTIONALDRAGAREA_DEBUG
32#if DIRECTIONALDRAGAREA_DEBUG
28#define DDA_DEBUG(msg) qDebug("[DDA] " msg)
33#define DDA_DEBUG(msg) qDebug("[DDA] " msg)
29−namespace {
34+#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)
37const char *statusToString(DirectionalDragArea::Status status)
77 if (status == DirectionalDragArea::WaitingForTouch) {
39 if (status == DirectionalDragArea::WaitingForTouch) {
@@ -88,23 +50,6 @@ const char *statusToString(DirectionalDragArea::Status status)
88#define DDA_DEBUG(msg) do{}while(0)
50#define DDA_DEBUG(msg) do{}while(0)
89#endif // DIRECTIONALDRAGAREA_DEBUG
51#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)
54DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
110 : QQuickItem(parent)
55 : QQuickItem(parent)
@@ -126,12 +71,14 @@ DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
126 , m_timeSource(new RealTimeSource)
71 , m_timeSource(new RealTimeSource)
127 , m_activeTouches(m_timeSource)
72 , m_activeTouches(m_timeSource)
129− setRecognitionTimer(new RecognitionTimer(this));
74+ setRecognitionTimer(new Timer(this));
130 m_recognitionTimer->setInterval(60);
75 m_recognitionTimer->setInterval(60);
76+ m_recognitionTimer->setSingleShot(false);
132 m_velocityCalculator = new AxisVelocityCalculator(this);
78 m_velocityCalculator = new AxisVelocityCalculator(this);
134− connect(this, &QQuickItem::enabledChanged, this, &DirectionalDragArea::onEnabledChanged);
80+ connect(this, &QQuickItem::enabledChanged, this, &DirectionalDragArea::giveUpIfDisabledOrInvisible);
81+ connect(this, &QQuickItem::visibleChanged, this, &DirectionalDragArea::giveUpIfDisabledOrInvisible);
137Direction::Type DirectionalDragArea::direction() const
84Direction::Type DirectionalDragArea::direction() const
@@ -214,6 +161,7 @@ void DirectionalDragArea::setRecognitionTimer(UbuntuGestures::AbstractTimer *tim
215 int interval = 0;
162 int interval = 0;
216 bool timerWasRunning = false;
163 bool timerWasRunning = false;
164+ bool wasSingleShot = false;
218 // can be null when called from the constructor
166 // can be null when called from the constructor
219 if (m_recognitionTimer) {
167 if (m_recognitionTimer) {
@@ -226,6 +174,7 @@ void DirectionalDragArea::setRecognitionTimer(UbuntuGestures::AbstractTimer *tim
227 m_recognitionTimer = timer;
175 m_recognitionTimer = timer;
228 timer->setInterval(interval);
176 timer->setInterval(interval);
177+ timer->setSingleShot(wasSingleShot);
229 connect(timer, &UbuntuGestures::AbstractTimer::timeout,
178 connect(timer, &UbuntuGestures::AbstractTimer::timeout,
230 this, &DirectionalDragArea::checkSpeed);
179 this, &DirectionalDragArea::checkSpeed);
231 if (timerWasRunning) {
180 if (timerWasRunning) {
@@ -280,8 +229,131 @@ qreal DirectionalDragArea::touchSceneY() const
280 return m_previousScenePos.y();
229 return m_previousScenePos.y();
232+bool DirectionalDragArea::event(QEvent *event)
234+ if (event->type() == TouchOwnershipEvent::touchOwnershipEventType()) {
235+ touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(event));
237+ } else if (event->type() == UnownedTouchEvent::unownedTouchEventType()) {
238+ unownedTouchEvent(static_cast<UnownedTouchEvent *>(event));
241+ return QQuickItem::event(event);
245+void DirectionalDragArea::touchOwnershipEvent(TouchOwnershipEvent *event)
247+ if (event->gained()) {
249+ ids.append(event->touchId());
250+ grabTouchPoints(ids);
252+ // We still wanna know when it ends for keeping the composition time window up-to-date
253+ TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
255+ setStatus(WaitingForTouch);
259+void DirectionalDragArea::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent)
261+ QTouchEvent *event = unownedTouchEvent->touchEvent();
263+ Q_ASSERT(!event->touchPointStates().testFlag(Qt::TouchPointPressed));
265+ #if DIRECTIONALDRAGAREA_DEBUG
266+ // TODO Consider using qCDebug() when available (Qt 5.2)
267+ qDebug() << "[DDA] Unowned" << m_timeSource->msecsSinceReference()
268+ << qPrintable(touchEventToString(event));
272+ case WaitingForTouch:
276+ Q_ASSERT(isEnabled() && isVisible());
277+ unownedTouchEvent_undecided(unownedTouchEvent);
279+ default: // Recognized:
284+ m_activeTouches.update(event);
287+void DirectionalDragArea::unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent)
289+ const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(unownedTouchEvent->touchEvent());
291+ qCritical() << "DirectionalDragArea[status=Undecided]: touch " << m_touchId
292+ << "missing from UnownedTouchEvent without first reaching state Qt::TouchPointReleased. "
293+ "Considering it as released.";
295+ TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
296+ setStatus(WaitingForTouch);
300+ const QPointF &touchScenePos = touchPoint->scenePos();
302+ if (touchPoint->state() == Qt::TouchPointReleased) {
303+ // touch has ended before recognition concluded
304+ DDA_DEBUG("Touch has ended before recognition concluded");
305+ TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
306+ emitSignalIfTapped();
307+ setStatus(WaitingForTouch);
311+ m_previousDampedScenePos.setX(m_dampedScenePos.x());
312+ m_previousDampedScenePos.setY(m_dampedScenePos.y());
313+ m_dampedScenePos.update(touchScenePos);
314+ updateVelocityCalculator(touchScenePos);
316+ if (!pointInsideAllowedArea()) {
317+ DDA_DEBUG("Rejecting gesture because touch point is outside allowed area.");
318+ TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
319+ // We still wanna know when it ends for keeping the composition time window up-to-date
320+ TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
321+ setStatus(WaitingForTouch);
325+ if (!movingInRightDirection()) {
326+ DDA_DEBUG("Rejecting gesture because touch point is moving in the wrong direction.");
327+ TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
328+ // We still wanna know when it ends for keeping the composition time window up-to-date
329+ TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
330+ setStatus(WaitingForTouch);
334+ setPreviousPos(touchPoint->pos());
335+ setPreviousScenePos(touchScenePos);
337+ if (isWithinTouchCompositionWindow()) {
338+ // There's still time for some new touch to appear and ruin our party as it would be combined
339+ // with our m_touchId one and therefore deny the possibility of a single-finger gesture.
340+ DDA_DEBUG("Sill within composition window. Let's wait more.");
344+ if (movedFarEnough(touchScenePos)) {
345+ TouchRegistry::instance()->requestTouchOwnership(m_touchId, this);
346+ setStatus(Recognized);
348+ DDA_DEBUG("Didn't move far enough yet. Let's wait more.");
283void DirectionalDragArea::touchEvent(QTouchEvent *event)
352void DirectionalDragArea::touchEvent(QTouchEvent *event)
354+ // TODO: Consider when more than one touch starts in the same event (although it's not possible
355+ // with Mir's android-input). Have to track them all. Consider it a plus/bonus.
285 #if DIRECTIONALDRAGAREA_DEBUG
357 #if DIRECTIONALDRAGAREA_DEBUG
286 // TODO Consider using qCDebug() when available (Qt 5.2)
358 // TODO Consider using qCDebug() when available (Qt 5.2)
287 qDebug() << "[DDA]" << m_timeSource->msecsSinceReference()
359 qDebug() << "[DDA]" << m_timeSource->msecsSinceReference()
@@ -310,18 +382,23 @@ void DirectionalDragArea::touchEvent(QTouchEvent *event)
311void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
383void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
385+ // TODO: accept/reject is for the whole event, not per touch id. See how that affects us.
313 if (!event->touchPointStates().testFlag(Qt::TouchPointPressed)) {
387 if (!event->touchPointStates().testFlag(Qt::TouchPointPressed)) {
314 // Nothing to see here. No touch starting in this event.
388 // Nothing to see here. No touch starting in this event.
392+ // to be proven wrong, if that's the case
393+ bool allGood = true;
318 if (isWithinTouchCompositionWindow()) {
395 if (isWithinTouchCompositionWindow()) {
319 // too close to the last touch start. So we consider them as starting roughly at the same time.
396 // 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.
397 // Can't be a single-touch gesture.
321 #if DIRECTIONALDRAGAREA_DEBUG
398 #if DIRECTIONALDRAGAREA_DEBUG
322 qDebug("[DDA] A new touch point came in but we're still within time composition window. Ignoring it.");
399 qDebug("[DDA] A new touch point came in but we're still within time composition window. Ignoring it.");
324− return;
401+ allGood = false;
327 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
404 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
@@ -332,7 +409,7 @@ void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
332 if (touchPoint.state() == Qt::TouchPointPressed) {
409 if (touchPoint.state() == Qt::TouchPointPressed) {
333 if (newTouchPoint) {
410 if (newTouchPoint) {
334 // more than one touch starting in this QTouchEvent. Can't be a single-touch gesture
411 // more than one touch starting in this QTouchEvent. Can't be a single-touch gesture
335− return;
412+ allGood = false;
337 // that's our candidate
414 // that's our candidate
338 m_touchId = touchPoint.id();
415 m_touchId = touchPoint.id();
@@ -341,86 +418,63 @@ void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
344− Q_ASSERT(newTouchPoint);
422+ Q_ASSERT(newTouchPoint);
346− // If we have made this far, we are good to go to the next status.
348− m_startPos = newTouchPoint->pos();
424+ m_startPos = newTouchPoint->pos();
349− m_startScenePos = newTouchPoint->scenePos();
425+ m_startScenePos = newTouchPoint->scenePos();
350− m_touchId = newTouchPoint->id();
426+ m_touchId = newTouchPoint->id();
351− m_dampedScenePos.reset(m_startScenePos);
427+ m_dampedScenePos.reset(m_startScenePos);
352− m_velocityCalculator->setTrackedPosition(0.);
428+ m_velocityCalculator->setTrackedPosition(0.);
353− m_velocityCalculator->reset();
429+ m_velocityCalculator->reset();
354− m_numSamplesOnLastSpeedCheck = 0;
430+ m_numSamplesOnLastSpeedCheck = 0;
355− m_silenceTime = 0;
431+ m_silenceTime = 0;
356− setPreviousPos(m_startPos);
432+ setPreviousPos(m_startPos);
357− setPreviousScenePos(m_startScenePos);
433+ setPreviousScenePos(m_startScenePos);
358− updateSceneDirectionVector();
434+ updateSceneDirectionVector();
436+ if (recognitionIsDisabled()) {
437+ // Behave like a dumb TouchArea
438+ TouchRegistry::instance()->requestTouchOwnership(m_touchId, this);
439+ setStatus(Recognized);
442+ // just monitor the touch points for now.
443+ TouchRegistry::instance()->addCandidateOwnerForTouch(m_touchId, this);
360− setStatus(Undecided);
445+ setStatus(Undecided);
446+ // Let the item below have it. We will monitor it and grab it later if a gesture
451+ watchPressedTouchPoints(touchPoints);
363void DirectionalDragArea::touchEvent_undecided(QTouchEvent *event)
456void DirectionalDragArea::touchEvent_undecided(QTouchEvent *event)
458+ Q_ASSERT(event->type() == QEvent::TouchBegin);
365− const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);
459+ Q_ASSERT(fetchTargetTouchPoint(event) == nullptr);
368− qCritical() << "DirectionalDragArea[status=Undecided]: touch " << m_touchId
369− << "missing from QTouchEvent without first reaching state Qt::TouchPointReleased. "
370− "Considering it as released.";
371− setStatus(WaitingForTouch);
375− const QPointF &touchScenePos = touchPoint->scenePos();
461+ // We're not interested in new touch points. We already have our candidate (m_touchId).
376−
462+ // But we do want to know when those new touches end for keeping the composition time
377− if (touchPoint->state() == Qt::TouchPointReleased) {
463+ // window up-to-date
378− // touch has ended before recognition concluded
464+ event->ignore();
379− DDA_DEBUG("Touch has ended before recognition concluded");
465+ watchPressedTouchPoints(event->touchPoints());
380− setStatus(WaitingForTouch);
385 if (event->touchPointStates().testFlag(Qt::TouchPointPressed) && isWithinTouchCompositionWindow()) {
467 if (event->touchPointStates().testFlag(Qt::TouchPointPressed) && isWithinTouchCompositionWindow()) {
386 // multi-finger drags are not accepted
468 // multi-finger drags are not accepted
387 DDA_DEBUG("Multi-finger drags are not accepted");
469 DDA_DEBUG("Multi-finger drags are not accepted");
388− setStatus(WaitingForTouch);
392− m_previousDampedScenePos.setX(m_dampedScenePos.x());
471+ TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
393− m_previousDampedScenePos.setY(m_dampedScenePos.y());
472+ // We still wanna know when it ends for keeping the composition time window up-to-date
394− m_dampedScenePos.update(touchScenePos);
473+ 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);
475 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.");
426void DirectionalDragArea::touchEvent_recognized(QTouchEvent *event)
480void DirectionalDragArea::touchEvent_recognized(QTouchEvent *event)
@@ -437,11 +491,35 @@ void DirectionalDragArea::touchEvent_recognized(QTouchEvent *event)
437 setPreviousScenePos(touchPoint->scenePos());
491 setPreviousScenePos(touchPoint->scenePos());
439 if (touchPoint->state() == Qt::TouchPointReleased) {
493 if (touchPoint->state() == Qt::TouchPointReleased) {
494+ emitSignalIfTapped();
440 setStatus(WaitingForTouch);
495 setStatus(WaitingForTouch);
500+void DirectionalDragArea::watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints)
502+ for (int i = 0; i < touchPoints.count(); ++i) {
503+ const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
504+ if (touchPoint.state() == Qt::TouchPointPressed) {
505+ TouchRegistry::instance()->addTouchWatcher(touchPoint.id(), this);
510+bool DirectionalDragArea::recognitionIsDisabled() const
512+ return distanceThreshold() <= 0 && compositionTime() <= 0;
515+void DirectionalDragArea::emitSignalIfTapped()
517+ qint64 touchDuration = m_timeSource->msecsSinceReference() - m_activeTouches.touchStartTime(m_touchId);
518+ if (touchDuration <= maxTapDuration()) {
445const QTouchEvent::TouchPoint *DirectionalDragArea::fetchTargetTouchPoint(QTouchEvent *event)
523const QTouchEvent::TouchPoint *DirectionalDragArea::fetchTargetTouchPoint(QTouchEvent *event)
447 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
525 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
@@ -509,12 +587,16 @@ bool DirectionalDragArea::movedFarEnough(const QPointF &point) const
510void DirectionalDragArea::checkSpeed()
588void DirectionalDragArea::checkSpeed()
590+ Q_ASSERT(m_status == Undecided);
512 if (m_velocityCalculator->numSamples() >= AxisVelocityCalculator::MIN_SAMPLES_NEEDED) {
592 if (m_velocityCalculator->numSamples() >= AxisVelocityCalculator::MIN_SAMPLES_NEEDED) {
513 qreal speed = qFabs(m_velocityCalculator->calculate());
593 qreal speed = qFabs(m_velocityCalculator->calculate());
514 qreal minSpeedMsecs = m_minSpeed / 1000.0;
594 qreal minSpeedMsecs = m_minSpeed / 1000.0;
516 if (speed < minSpeedMsecs) {
596 if (speed < minSpeedMsecs) {
517 DDA_DEBUG("Rejecting gesture because it's below minimum speed.");
597 DDA_DEBUG("Rejecting gesture because it's below minimum speed.");
598+ TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
599+ TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
518 setStatus(WaitingForTouch);
600 setStatus(WaitingForTouch);
@@ -523,7 +605,9 @@ void DirectionalDragArea::checkSpeed()
523 m_silenceTime += m_recognitionTimer->interval();
605 m_silenceTime += m_recognitionTimer->interval();
525 if (m_silenceTime > m_maxSilenceTime) {
607 if (m_silenceTime > m_maxSilenceTime) {
526− DDA_DEBUG("Rejecting gesture because it's silence time has been exceeded.");
608+ DDA_DEBUG("Rejecting gesture because its silence time has been exceeded.");
609+ TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
610+ TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
527 setStatus(WaitingForTouch);
611 setStatus(WaitingForTouch);
@@ -532,10 +616,19 @@ void DirectionalDragArea::checkSpeed()
532 m_numSamplesOnLastSpeedCheck = m_velocityCalculator->numSamples();
616 m_numSamplesOnLastSpeedCheck = m_velocityCalculator->numSamples();
535−void DirectionalDragArea::onEnabledChanged()
619+void DirectionalDragArea::giveUpIfDisabledOrInvisible()
621+ if (!isEnabled() || !isVisible()) {
622+ if (m_status == Undecided) {
623+ TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
624+ // We still wanna know when it ends for keeping the composition time window up-to-date
625+ TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
537− if (!isEnabled() && m_status != WaitingForTouch) {
628+ if (m_status != WaitingForTouch) {
629+ DDA_DEBUG("Resetting status because got disabled or made invisible");
538− setStatus(WaitingForTouch);
630+ setStatus(WaitingForTouch);
@@ -560,6 +653,8 @@ void DirectionalDragArea::setStatus(DirectionalDragArea::Status newStatus)
560 switch (newStatus) {
653 switch (newStatus) {
561 case WaitingForTouch:
654 case WaitingForTouch:
562 Q_EMIT draggingChanged(false);
655 Q_EMIT draggingChanged(false);
656+ if (oldStatus == Undecided) {
564 case Undecided:
659 case Undecided:
565 m_recognitionTimer->start();
660 m_recognitionTimer->start();
@@ -631,7 +726,9 @@ void DirectionalDragArea::updateVelocityCalculator(const QPointF &scenePos)
632bool DirectionalDragArea::isWithinTouchCompositionWindow()
727bool DirectionalDragArea::isWithinTouchCompositionWindow()
730+ compositionTime() > 0 &&
634− return !m_activeTouches.isEmpty() &&
731+ !m_activeTouches.isEmpty() &&
635 m_timeSource->msecsSinceReference() <=
732 m_timeSource->msecsSinceReference() <=
636 m_activeTouches.mostRecentStartTime() + (qint64)compositionTime();
733 m_activeTouches.mostRecentStartTime() + (qint64)compositionTime();
@@ -639,18 +736,17 @@ bool DirectionalDragArea::isWithinTouchCompositionWindow()
639//************************** ActiveTouchesInfo **************************
736//************************** ActiveTouchesInfo **************************
641DirectionalDragArea::ActiveTouchesInfo::ActiveTouchesInfo(const SharedTimeSource &timeSource)
738DirectionalDragArea::ActiveTouchesInfo::ActiveTouchesInfo(const SharedTimeSource &timeSource)
642− : m_timeSource(timeSource), m_lastUsedIndex(-1)
739+ : 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)
743void DirectionalDragArea::ActiveTouchesInfo::update(QTouchEvent *event)
652 if (!(event->touchPointStates() & (Qt::TouchPointPressed | Qt::TouchPointReleased))) {
745 if (!(event->touchPointStates() & (Qt::TouchPointPressed | Qt::TouchPointReleased))) {
653 // nothing to update
746 // nothing to update
747+ #if ACTIVETOUCHESINFO_DEBUG
748+ qDebug("[DDA::ActiveTouchesInfo] Nothing to Update");
@@ -658,74 +754,88 @@ void DirectionalDragArea::ActiveTouchesInfo::update(QTouchEvent *event)
658 for (int i = 0; i < touchPoints.count(); ++i) {
754 for (int i = 0; i < touchPoints.count(); ++i) {
659 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
755 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
660 if (touchPoint.state() == Qt::TouchPointPressed) {
756 if (touchPoint.state() == Qt::TouchPointPressed) {
661− addTouchPoint(touchPoint);
757+ addTouchPoint(touchPoint.id());
662 } else if (touchPoint.state() == Qt::TouchPointReleased) {
758 } else if (touchPoint.state() == Qt::TouchPointReleased) {
663− removeTouchPoint(touchPoint);
759+ removeTouchPoint(touchPoint.id());
764+#if ACTIVETOUCHESINFO_DEBUG
668−void DirectionalDragArea::ActiveTouchesInfo::addTouchPoint(const QTouchEvent::TouchPoint &touchPoint)
765+QString DirectionalDragArea::ActiveTouchesInfo::toString()
670− ActiveTouchInfo &activeTouchInfo = getEmptySlot();
767+ QString string = "(";
671− activeTouchInfo.id = touchPoint.id();
768+
672− activeTouchInfo.startTime = m_timeSource->msecsSinceReference();
769+ {
770+ QTextStream stream(&string);
771+ m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {
772+ stream << "(id=" << touchInfo->id << ",startTime=" << touchInfo->startTime << ")";
781+#endif // ACTIVETOUCHESINFO_DEBUG
675−void DirectionalDragArea::ActiveTouchesInfo::removeTouchPoint(const QTouchEvent::TouchPoint &touchPoint)
783+void DirectionalDragArea::ActiveTouchesInfo::addTouchPoint(int touchId)
677− for (int i = 0; i <= m_lastUsedIndex; ++i) {
785+ ActiveTouchInfo &activeTouchInfo = m_touchInfoPool.getEmptySlot();
678− if (touchPoint.id() == m_vector.at(i).id) {
786+ activeTouchInfo.id = touchId;
679− freeSlot(i);
787+ activeTouchInfo.startTime = m_timeSource->msecsSinceReference();
681− }
789+ #if ACTIVETOUCHESINFO_DEBUG
682− }
790+ qDebug() << "[DDA::ActiveTouchesInfo]" << qPrintable(toString());
683− Q_ASSERT(false); // shouldn't reach this point
791+ #endif
686−DirectionalDragArea::ActiveTouchInfo &DirectionalDragArea::ActiveTouchesInfo::getEmptySlot()
794+qint64 DirectionalDragArea::ActiveTouchesInfo::touchStartTime(int touchId)
688− Q_ASSERT(m_lastUsedIndex < m_vector.size());
796+ qint64 result = -1;
690− // Look for an in-between vacancy first
798+ m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {
691− for (int i = 0; i < m_lastUsedIndex; ++i) {
799+ if (touchId == touchInfo->id) {
692− ActiveTouchInfo &activeTouchInfo = m_vector[i];
800+ result = touchInfo->startTime;
693− if (!activeTouchInfo.isValid()) {
801+ return false;
694− return activeTouchInfo;
802+ } else {
699− if (m_lastUsedIndex >= m_vector.size()) {
700− m_vector.resize(m_lastUsedIndex + 1);
703− return m_vector[m_lastUsedIndex];
807+ Q_ASSERT(result != -1);
706−void DirectionalDragArea::ActiveTouchesInfo::freeSlot(int index)
811+void DirectionalDragArea::ActiveTouchesInfo::removeTouchPoint(int touchId)
708− m_vector[index].reset();
813+ m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {
709− if (index == m_lastUsedIndex) {
814+ if (touchId == touchInfo->id) {
710− do {
815+ m_touchInfoPool.freeSlot(touchInfo);
711− --m_lastUsedIndex;
816+ return false;
712− } while (m_lastUsedIndex >= 0 && !m_vector.at(m_lastUsedIndex).isValid());
817+ } else {
822+ #if ACTIVETOUCHESINFO_DEBUG
823+ qDebug() << "[DDA::ActiveTouchesInfo]" << qPrintable(toString());
716qint64 DirectionalDragArea::ActiveTouchesInfo::mostRecentStartTime()
827qint64 DirectionalDragArea::ActiveTouchesInfo::mostRecentStartTime()
718− Q_ASSERT(m_lastUsedIndex >= 0);
829+ Q_ASSERT(!m_touchInfoPool.isEmpty());
831+ qint64 highestStartTime = -1;
720− qint64 highestStartTime = m_vector.at(0).startTime;
723− const ActiveTouchInfo &activeTouchInfo = m_vector.at(i);
833+ m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &activeTouchInfo) {
724− if (activeTouchInfo.isValid() && activeTouchInfo.startTime > highestStartTime) {
834+ if (activeTouchInfo->startTime > highestStartTime) {
725− highestStartTime = activeTouchInfo.startTime;
835+ highestStartTime = activeTouchInfo->startTime;
727− ++i;
837+ return true;
728− } while (i < m_lastUsedIndex);
838+ });
730 return highestStartTime;
840 return highestStartTime;