@@ -1,5 +1,5 @@
2− * Copyright (C) 2013-2014 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 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
@@ -21,6 +21,7 @@
22#include <QQuickWindow>
22#include <QQuickWindow>
23#include <QtCore/qmath.h>
23#include <QtCore/qmath.h>
24#include <QDebug>
25#include <QDebug>
26#pragma GCC diagnostic push
27#pragma GCC diagnostic push
@@ -33,6 +34,8 @@
33#include "TouchRegistry.h"
34#include "TouchRegistry.h"
34#include "UnownedTouchEvent.h"
35#include "UnownedTouchEvent.h"
37+#include "DirectionalDragArea_p.h"
36using namespace UbuntuGestures;
39using namespace UbuntuGestures;
38#if DIRECTIONALDRAGAREA_DEBUG
41#if DIRECTIONALDRAGAREA_DEBUG
@@ -40,11 +43,11 @@ using namespace UbuntuGestures;
40#include "DebugHelpers.h"
43#include "DebugHelpers.h"
42namespace {
45namespace {
43−const char *statusToString(DirectionalDragArea::Status status)
46+const char *statusToString(DirectionalDragAreaPrivate::Status status)
45− if (status == DirectionalDragArea::WaitingForTouch) {
48+ if (status == DirectionalDragAreaPrivate::WaitingForTouch) {
46 return "WaitingForTouch";
49 return "WaitingForTouch";
47− } else if (status == DirectionalDragArea::Undecided) {
50+ } else if (status == DirectionalDragAreaPrivate::Undecided) {
48 return "Undecided";
51 return "Undecided";
50 return "Recognized";
53 return "Recognized";
@@ -56,205 +59,168 @@ const char *statusToString(DirectionalDragArea::Status status)
56#define ddaDebug(params) ((void)0)
59#define ddaDebug(params) ((void)0)
57#endif // DIRECTIONALDRAGAREA_DEBUG
60#endif // DIRECTIONALDRAGAREA_DEBUG
60DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
62DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
61 : QQuickItem(parent)
63 : QQuickItem(parent)
62− , m_status(WaitingForTouch)
64+ , d(new DirectionalDragAreaPrivate(this))
65− , m_direction(Direction::Rightwards)
67− , m_wideningFactor(0)
68− , m_distanceThreshold(0)
69− , m_distanceThresholdSquared(0.)
71− , m_maxSilenceTime(200)
73− , m_compositionTime(60)
74− , m_numSamplesOnLastSpeedCheck(0)
75− , m_recognitionTimer(0)
76− , m_velocityCalculator(0)
77− , m_timeSource(new RealTimeSource)
78− , m_activeTouches(m_timeSource)
80− setRecognitionTimer(new Timer(this));
81− m_recognitionTimer->setInterval(60);
82− m_recognitionTimer->setSingleShot(false);
84− m_velocityCalculator = new AxisVelocityCalculator(this);
86− connect(this, &QQuickItem::enabledChanged, this, &DirectionalDragArea::giveUpIfDisabledOrInvisible);
87− connect(this, &QQuickItem::visibleChanged, this, &DirectionalDragArea::giveUpIfDisabledOrInvisible);
90−Direction::Type DirectionalDragArea::direction() const
95−void DirectionalDragArea::setDirection(Direction::Type direction)
97− if (direction != m_direction) {
66+ d->setRecognitionTimer(new Timer(this));
98− m_direction = direction;
67+ d->recognitionTimer->setInterval(d->maxTime);
99− Q_EMIT directionChanged(m_direction);
68+ d->recognitionTimer->setSingleShot(true);
103−void DirectionalDragArea::setMaxDeviation(qreal value)
70+ connect(this, &QQuickItem::enabledChanged, d, &DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible);
104−{
71+ connect(this, &QQuickItem::visibleChanged, d, &DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible);
105− if (m_dampedScenePos.maxDelta() != value) {
106− m_dampedScenePos.setMaxDelta(value);
107− Q_EMIT maxDeviationChanged(value);
111−qreal DirectionalDragArea::wideningAngle() const
74+Direction::Type DirectionalDragArea::direction() const
113− return m_wideningAngle;
116−void DirectionalDragArea::setWideningAngle(qreal angle)
118− if (angle == m_wideningAngle)
121− m_wideningAngle = angle;
123− // wideningFactor = pow(cosine(angle), 2)
125− qreal angleRadians = angle * M_PI / 180.0;
126− m_wideningFactor = qCos(angleRadians);
127− m_wideningFactor = m_wideningFactor * m_wideningFactor;
130− Q_EMIT wideningAngleChanged(angle);
133−void DirectionalDragArea::setDistanceThreshold(qreal value)
135− if (m_distanceThreshold != value) {
76+ return d->direction;
136− m_distanceThreshold = value;
137− m_distanceThresholdSquared = m_distanceThreshold * m_distanceThreshold;
138− Q_EMIT distanceThresholdChanged(value);
142−void DirectionalDragArea::setMinSpeed(qreal value)
79+void DirectionalDragArea::setDirection(Direction::Type direction)
144− if (m_minSpeed != value) {
81+ if (direction != d->direction) {
145− m_minSpeed = value;
82+ d->direction = direction;
146− Q_EMIT minSpeedChanged(value);
83+ Q_EMIT directionChanged(d->direction);
150−void DirectionalDragArea::setMaxSilenceTime(int value)
87+void DirectionalDragAreaPrivate::setDistanceThreshold(qreal value)
152− if (m_maxSilenceTime != value) {
89+ if (distanceThreshold != value) {
153− m_maxSilenceTime = value;
90+ distanceThreshold = value;
154− Q_EMIT maxSilenceTimeChanged(value);
91+ distanceThresholdSquared = distanceThreshold * distanceThreshold;
158−void DirectionalDragArea::setCompositionTime(int value)
95+void DirectionalDragAreaPrivate::setMaxTime(int value)
160− if (m_compositionTime != value) {
97+ if (maxTime != value) {
161− m_compositionTime = value;
98+ maxTime = value;
162− Q_EMIT compositionTimeChanged(value);
99+ recognitionTimer->setInterval(maxTime);
166−void DirectionalDragArea::setRecognitionTimer(UbuntuGestures::AbstractTimer *timer)
103+void DirectionalDragAreaPrivate::setRecognitionTimer(UbuntuGestures::AbstractTimer *timer)
168 int interval = 0;
105 int interval = 0;
169 bool timerWasRunning = false;
106 bool timerWasRunning = false;
170 bool wasSingleShot = false;
107 bool wasSingleShot = false;
172 // can be null when called from the constructor
109 // can be null when called from the constructor
173− if (m_recognitionTimer) {
110+ if (recognitionTimer) {
174− interval = m_recognitionTimer->interval();
111+ interval = recognitionTimer->interval();
175− timerWasRunning = m_recognitionTimer->isRunning();
112+ timerWasRunning = recognitionTimer->isRunning();
176− if (m_recognitionTimer->parent() == this) {
113+ if (recognitionTimer->parent() == this) {
177− delete m_recognitionTimer;
114+ delete recognitionTimer;
181− m_recognitionTimer = timer;
118+ recognitionTimer = timer;
182 timer->setInterval(interval);
119 timer->setInterval(interval);
183 timer->setSingleShot(wasSingleShot);
120 timer->setSingleShot(wasSingleShot);
184 connect(timer, &UbuntuGestures::AbstractTimer::timeout,
121 connect(timer, &UbuntuGestures::AbstractTimer::timeout,
185− this, &DirectionalDragArea::checkSpeed);
122+ this, &DirectionalDragAreaPrivate::rejectGesture);
186 if (timerWasRunning) {
123 if (timerWasRunning) {
187− m_recognitionTimer->start();
124+ recognitionTimer->start();
191−void DirectionalDragArea::setTimeSource(const SharedTimeSource &timeSource)
128+void DirectionalDragAreaPrivate::setTimeSource(const SharedTimeSource &timeSource)
193− m_timeSource = timeSource;
130+ this->timeSource = timeSource;
194− m_velocityCalculator->setTimeSource(timeSource);
195− m_activeTouches.m_timeSource = timeSource;
131+ activeTouches.m_timeSource = timeSource;
198qreal DirectionalDragArea::distance() const
134qreal DirectionalDragArea::distance() const
200− if (Direction::isHorizontal(m_direction)) {
136+ if (Direction::isHorizontal(d->direction)) {
201− return m_previousPos.x() - m_startPos.x();
137+ return d->publicPos.x() - d->startPos.x();
203− return m_previousPos.y() - m_startPos.y();
139+ return d->publicPos.y() - d->startPos.y();
207−void DirectionalDragArea::updateSceneDistance()
143+void DirectionalDragAreaPrivate::updateSceneDistance()
209− QPointF totalMovement = m_previousScenePos - m_startScenePos;
145+ QPointF totalMovement = publicScenePos - startScenePos;
210− m_sceneDistance = projectOntoDirectionVector(totalMovement);
146+ sceneDistance = projectOntoDirectionVector(totalMovement);
213qreal DirectionalDragArea::sceneDistance() const
149qreal DirectionalDragArea::sceneDistance() const
215− return m_sceneDistance;
151+ return d->sceneDistance;
218qreal DirectionalDragArea::touchX() const
154qreal DirectionalDragArea::touchX() const
220− return m_previousPos.x();
156+ return d->publicPos.x();
223qreal DirectionalDragArea::touchY() const
159qreal DirectionalDragArea::touchY() const
225− return m_previousPos.y();
161+ return d->publicPos.y();
228qreal DirectionalDragArea::touchSceneX() const
164qreal DirectionalDragArea::touchSceneX() const
230− return m_previousScenePos.x();
166+ return d->publicScenePos.x();
233qreal DirectionalDragArea::touchSceneY() const
169qreal DirectionalDragArea::touchSceneY() const
235− return m_previousScenePos.y();
171+ return d->publicScenePos.y();
174+bool DirectionalDragArea::dragging() const
176+ return d->status == DirectionalDragAreaPrivate::Recognized;
179+bool DirectionalDragArea::pressed() const
181+ return d->status != DirectionalDragAreaPrivate::WaitingForTouch;
184+bool DirectionalDragArea::immediateRecognition() const
186+ return d->immediateRecognition;
189+void DirectionalDragArea::setImmediateRecognition(bool enabled)
191+ if (d->immediateRecognition != enabled) {
192+ d->immediateRecognition = enabled;
193+ Q_EMIT immediateRecognitionChanged(enabled);
197+void DirectionalDragArea::removeTimeConstraints()
199+ d->setMaxTime(60 * 60 * 1000);
200+ d->compositionTime = 0;
201+ ddaDebug("removed time constraints");
238bool DirectionalDragArea::event(QEvent *event)
204bool DirectionalDragArea::event(QEvent *event)
240 if (event->type() == TouchOwnershipEvent::touchOwnershipEventType()) {
206 if (event->type() == TouchOwnershipEvent::touchOwnershipEventType()) {
241− touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(event));
207+ d->touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(event));
242 return true;
208 return true;
243 } else if (event->type() == UnownedTouchEvent::unownedTouchEventType()) {
209 } else if (event->type() == UnownedTouchEvent::unownedTouchEventType()) {
244− unownedTouchEvent(static_cast<UnownedTouchEvent *>(event));
210+ d->unownedTouchEvent(static_cast<UnownedTouchEvent *>(event));
245 return true;
211 return true;
247 return QQuickItem::event(event);
213 return QQuickItem::event(event);
251−void DirectionalDragArea::touchOwnershipEvent(TouchOwnershipEvent *event)
217+void DirectionalDragAreaPrivate::touchOwnershipEvent(TouchOwnershipEvent *event)
253 if (event->gained()) {
219 if (event->gained()) {
254 QVector<int> ids;
220 QVector<int> ids;
255 ids.append(event->touchId());
221 ids.append(event->touchId());
256 ddaDebug("grabbing touch");
222 ddaDebug("grabbing touch");
257− grabTouchPoints(ids);
223+ q->grabTouchPoints(ids);
259 // Work around for Qt bug. If we grab a touch that is being used for mouse pointer
225 // 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.
226 // emulation it will cause the emulation logic to go nuts.
@@ -262,35 +228,35 @@ void DirectionalDragArea::touchOwnershipEvent(TouchOwnershipEvent *event)
263 // The fix for this bug has landed in Qt 5.4 (https://codereview.qt-project.org/96887)
229 // 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
230 // TODO: Remove this workaround once we start using Qt 5.4
265− if (window()) {
231+ if (q->window()) {
266− QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(window());
232+ QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(q->window());
267− if (windowPrivate->touchMouseId == event->touchId() && window()->mouseGrabberItem()) {
233+ if (windowPrivate->touchMouseId == event->touchId() && q->window()->mouseGrabberItem()) {
268 ddaDebug("removing mouse grabber");
234 ddaDebug("removing mouse grabber");
269− window()->mouseGrabberItem()->ungrabMouse();
235+ q->window()->mouseGrabberItem()->ungrabMouse();
273 // We still wanna know when it ends for keeping the composition time window up-to-date
239 // We still wanna know when it ends for keeping the composition time window up-to-date
274− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
240+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
276 setStatus(WaitingForTouch);
242 setStatus(WaitingForTouch);
280−void DirectionalDragArea::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent)
246+void DirectionalDragAreaPrivate::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent)
282 QTouchEvent *event = unownedTouchEvent->touchEvent();
248 QTouchEvent *event = unownedTouchEvent->touchEvent();
284 Q_ASSERT(!event->touchPointStates().testFlag(Qt::TouchPointPressed));
250 Q_ASSERT(!event->touchPointStates().testFlag(Qt::TouchPointPressed));
286− ddaDebug("Unowned " << m_timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
252+ ddaDebug("Unowned " << timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
288− switch (m_status) {
254+ switch (status) {
289 case WaitingForTouch:
255 case WaitingForTouch:
290 // do nothing
256 // do nothing
292 case Undecided:
258 case Undecided:
293− Q_ASSERT(isEnabled() && isVisible());
259+ Q_ASSERT(q->isEnabled() && q->isVisible());
294 unownedTouchEvent_undecided(unownedTouchEvent);
260 unownedTouchEvent_undecided(unownedTouchEvent);
296 default: // Recognized:
262 default: // Recognized:
@@ -298,18 +264,18 @@ void DirectionalDragArea::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent
301− m_activeTouches.update(event);
267+ activeTouches.update(event);
304−void DirectionalDragArea::unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent)
270+void DirectionalDragAreaPrivate::unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent)
306 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(unownedTouchEvent->touchEvent());
272 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(unownedTouchEvent->touchEvent());
307 if (!touchPoint) {
273 if (!touchPoint) {
308− qCritical() << "DirectionalDragArea[status=Undecided]: touch " << m_touchId
274+ qCritical() << "DirectionalDragArea[status=Undecided]: touch " << touchId
309 << "missing from UnownedTouchEvent without first reaching state Qt::TouchPointReleased. "
275 << "missing from UnownedTouchEvent without first reaching state Qt::TouchPointReleased. "
310 "Considering it as released.";
276 "Considering it as released.";
312− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
278+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
313 setStatus(WaitingForTouch);
279 setStatus(WaitingForTouch);
@@ -319,48 +285,42 @@ void DirectionalDragArea::unownedTouchEvent_undecided(UnownedTouchEvent *unowned
319 if (touchPoint->state() == Qt::TouchPointReleased) {
285 if (touchPoint->state() == Qt::TouchPointReleased) {
320 // touch has ended before recognition concluded
286 // touch has ended before recognition concluded
321 ddaDebug("Touch has ended before recognition concluded");
287 ddaDebug("Touch has ended before recognition concluded");
322− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
288+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
323− emitSignalIfTapped();
324 setStatus(WaitingForTouch);
289 setStatus(WaitingForTouch);
328− m_previousDampedScenePos.setX(m_dampedScenePos.x());
293+ previousDampedScenePos.setX(dampedScenePos.x());
329− m_previousDampedScenePos.setY(m_dampedScenePos.y());
294+ previousDampedScenePos.setY(dampedScenePos.y());
330− m_dampedScenePos.update(touchScenePos);
295+ dampedScenePos.update(touchScenePos);
331− updateVelocityCalculator(touchScenePos);
333− if (!pointInsideAllowedArea()) {
334− ddaDebug("Rejecting gesture because touch point is outside allowed area.");
335− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
336− // We still wanna know when it ends for keeping the composition time window up-to-date
337− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
338− setStatus(WaitingForTouch);
342 if (!movingInRightDirection()) {
297 if (!movingInRightDirection()) {
343 ddaDebug("Rejecting gesture because touch point is moving in the wrong direction.");
298 ddaDebug("Rejecting gesture because touch point is moving in the wrong direction.");
344− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
299+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
345 // We still wanna know when it ends for keeping the composition time window up-to-date
300 // We still wanna know when it ends for keeping the composition time window up-to-date
346− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
301+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
347 setStatus(WaitingForTouch);
302 setStatus(WaitingForTouch);
351− setPreviousPos(touchPoint->pos());
352− setPreviousScenePos(touchScenePos);
354 if (isWithinTouchCompositionWindow()) {
306 if (isWithinTouchCompositionWindow()) {
355 // There's still time for some new touch to appear and ruin our party as it would be combined
307 // There's still time for some new touch to appear and ruin our party as it would be combined
356− // with our m_touchId one and therefore deny the possibility of a single-finger gesture.
308+ // with our touchId one and therefore deny the possibility of a single-finger gesture.
357 ddaDebug("Sill within composition window. Let's wait more.");
309 ddaDebug("Sill within composition window. Let's wait more.");
361− if (movedFarEnough(touchScenePos)) {
313+ if (movedFarEnoughAlongGestureAxis()) {
362− TouchRegistry::instance()->requestTouchOwnership(m_touchId, this);
314+ TouchRegistry::instance()->requestTouchOwnership(touchId, q);
363 setStatus(Recognized);
315 setStatus(Recognized);
316+ setPublicPos(touchPoint->pos());
317+ setPublicScenePos(touchScenePos);
318+ } else if (isPastMaxDistance()) {
319+ ddaDebug("Rejecting gesture because it went farther than maxDistance without getting recognized.");
320+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
321+ // We still wanna know when it ends for keeping the composition time window up-to-date
322+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
323+ setStatus(WaitingForTouch);
365 ddaDebug("Didn't move far enough yet. Let's wait more.");
325 ddaDebug("Didn't move far enough yet. Let's wait more.");
@@ -371,29 +331,29 @@ void DirectionalDragArea::touchEvent(QTouchEvent *event)
371 // TODO: Consider when more than one touch starts in the same event (although it's not possible
331 // TODO: Consider when more than one touch starts in the same event (although it's not possible
372 // with Mir's android-input). Have to track them all. Consider it a plus/bonus.
332 // with Mir's android-input). Have to track them all. Consider it a plus/bonus.
374− ddaDebug(m_timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
334+ ddaDebug(d->timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
376 if (!isEnabled() || !isVisible()) {
336 if (!isEnabled() || !isVisible()) {
377 QQuickItem::touchEvent(event);
337 QQuickItem::touchEvent(event);
381− switch (m_status) {
341+ switch (d->status) {
382− case WaitingForTouch:
342+ case DirectionalDragAreaPrivate::WaitingForTouch:
383− touchEvent_absent(event);
343+ d->touchEvent_absent(event);
385− case Undecided:
345+ case DirectionalDragAreaPrivate::Undecided:
386− touchEvent_undecided(event);
346+ d->touchEvent_undecided(event);
388 default: // Recognized:
348 default: // Recognized:
389− touchEvent_recognized(event);
349+ d->touchEvent_recognized(event);
393− m_activeTouches.update(event);
353+ d->activeTouches.update(event);
396−void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
356+void DirectionalDragAreaPrivate::touchEvent_absent(QTouchEvent *event)
398 // TODO: accept/reject is for the whole event, not per touch id. See how that affects us.
358 // TODO: accept/reject is for the whole event, not per touch id. See how that affects us.
@@ -423,36 +383,40 @@ void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
423 allGood = false;
383 allGood = false;
425 // that's our candidate
385 // that's our candidate
426− m_touchId = touchPoint.id();
427 newTouchPoint = &touchPoint;
386 newTouchPoint = &touchPoint;
392+ allGood = sanityCheckRecognitionProperties();
394+ qWarning("DirectionalDragArea: recognition properties are wrongly set. Gesture recognition"
432 if (allGood) {
399 if (allGood) {
433 Q_ASSERT(newTouchPoint);
400 Q_ASSERT(newTouchPoint);
435− m_startPos = newTouchPoint->pos();
402+ startPos = newTouchPoint->pos();
436− m_startScenePos = newTouchPoint->scenePos();
403+ startScenePos = newTouchPoint->scenePos();
437− m_touchId = newTouchPoint->id();
404+ touchId = newTouchPoint->id();
438− m_dampedScenePos.reset(m_startScenePos);
405+ dampedScenePos.reset(startScenePos);
439− m_velocityCalculator->setTrackedPosition(0.);
406+ setPublicPos(startPos);
440− m_velocityCalculator->reset();
407+
441− m_numSamplesOnLastSpeedCheck = 0;
408+ setPublicScenePos(startScenePos);
443− setPreviousPos(m_startPos);
444− setPreviousScenePos(m_startScenePos);
445 updateSceneDirectionVector();
409 updateSceneDirectionVector();
447 if (recognitionIsDisabled()) {
411 if (recognitionIsDisabled()) {
448 // Behave like a dumb TouchArea
412 // Behave like a dumb TouchArea
449 ddaDebug("Gesture recognition is disabled. Requesting touch ownership immediately.");
413 ddaDebug("Gesture recognition is disabled. Requesting touch ownership immediately.");
450− TouchRegistry::instance()->requestTouchOwnership(m_touchId, this);
414+ TouchRegistry::instance()->requestTouchOwnership(touchId, q);
451 setStatus(Recognized);
415 setStatus(Recognized);
452 event->accept();
416 event->accept();
454 // just monitor the touch points for now.
418 // just monitor the touch points for now.
455− TouchRegistry::instance()->addCandidateOwnerForTouch(m_touchId, this);
419+ TouchRegistry::instance()->addCandidateOwnerForTouch(touchId, q);
457 setStatus(Undecided);
421 setStatus(Undecided);
458 // Let the item below have it. We will monitor it and grab it later if a gesture
422 // Let the item below have it. We will monitor it and grab it later if a gesture
@@ -465,12 +429,11 @@ void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
468−void DirectionalDragArea::touchEvent_undecided(QTouchEvent *event)
432+void DirectionalDragAreaPrivate::touchEvent_undecided(QTouchEvent *event)
470− Q_ASSERT(event->type() == QEvent::TouchBegin);
471 Q_ASSERT(fetchTargetTouchPoint(event) == nullptr);
434 Q_ASSERT(fetchTargetTouchPoint(event) == nullptr);
473− // We're not interested in new touch points. We already have our candidate (m_touchId).
436+ // We're not interested in new touch points. We already have our candidate (touchId).
474 // But we do want to know when those new touches end for keeping the composition time
437 // But we do want to know when those new touches end for keeping the composition time
475 // window up-to-date
438 // window up-to-date
476 event->ignore();
439 event->ignore();
@@ -480,63 +443,60 @@ void DirectionalDragArea::touchEvent_undecided(QTouchEvent *event)
480 // multi-finger drags are not accepted
443 // multi-finger drags are not accepted
481 ddaDebug("Multi-finger drags are not accepted");
444 ddaDebug("Multi-finger drags are not accepted");
483− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
446+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
484 // We still wanna know when it ends for keeping the composition time window up-to-date
447 // We still wanna know when it ends for keeping the composition time window up-to-date
485− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
448+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
487 setStatus(WaitingForTouch);
450 setStatus(WaitingForTouch);
491−void DirectionalDragArea::touchEvent_recognized(QTouchEvent *event)
454+void DirectionalDragAreaPrivate::touchEvent_recognized(QTouchEvent *event)
493 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);
456 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);
495 if (!touchPoint) {
458 if (!touchPoint) {
496− qCritical() << "DirectionalDragArea[status=Recognized]: touch " << m_touchId
459+ qCritical() << "DirectionalDragArea[status=Recognized]: touch " << touchId
497 << "missing from QTouchEvent without first reaching state Qt::TouchPointReleased. "
460 << "missing from QTouchEvent without first reaching state Qt::TouchPointReleased. "
498 "Considering it as released.";
461 "Considering it as released.";
499 setStatus(WaitingForTouch);
462 setStatus(WaitingForTouch);
501− setPreviousPos(touchPoint->pos());
464+ setPublicPos(touchPoint->pos());
502− setPreviousScenePos(touchPoint->scenePos());
465+ setPublicScenePos(touchPoint->scenePos());
504 if (touchPoint->state() == Qt::TouchPointReleased) {
467 if (touchPoint->state() == Qt::TouchPointReleased) {
505− emitSignalIfTapped();
506 setStatus(WaitingForTouch);
468 setStatus(WaitingForTouch);
511−void DirectionalDragArea::watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints)
473+void DirectionalDragAreaPrivate::watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints)
513 for (int i = 0; i < touchPoints.count(); ++i) {
475 for (int i = 0; i < touchPoints.count(); ++i) {
514 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
476 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
515 if (touchPoint.state() == Qt::TouchPointPressed) {
477 if (touchPoint.state() == Qt::TouchPointPressed) {
516− TouchRegistry::instance()->addTouchWatcher(touchPoint.id(), this);
478+ TouchRegistry::instance()->addTouchWatcher(touchPoint.id(), q);
521−bool DirectionalDragArea::recognitionIsDisabled() const
483+bool DirectionalDragAreaPrivate::recognitionIsDisabled() const
523− return distanceThreshold() <= 0 && compositionTime() <= 0;
485+ return immediateRecognition || (distanceThreshold <= 0 && compositionTime <= 0);
526−void DirectionalDragArea::emitSignalIfTapped()
488+bool DirectionalDragAreaPrivate::sanityCheckRecognitionProperties()
528− qint64 touchDuration = m_timeSource->msecsSinceReference() - m_activeTouches.touchStartTime(m_touchId);
490+ return recognitionIsDisabled()
529− if (touchDuration <= maxTapDuration()) {
491+ || (distanceThreshold < maxDistance && compositionTime < maxTime);
534−const QTouchEvent::TouchPoint *DirectionalDragArea::fetchTargetTouchPoint(QTouchEvent *event)
494+const QTouchEvent::TouchPoint *DirectionalDragAreaPrivate::fetchTargetTouchPoint(QTouchEvent *event)
536 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
496 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
537 const QTouchEvent::TouchPoint *touchPoint = 0;
497 const QTouchEvent::TouchPoint *touchPoint = 0;
538 for (int i = 0; i < touchPoints.size(); ++i) {
498 for (int i = 0; i < touchPoints.size(); ++i) {
539− if (touchPoints.at(i).id() == m_touchId) {
499+ if (touchPoints.at(i).id() == touchId) {
540 touchPoint = &touchPoints.at(i);
500 touchPoint = &touchPoints.at(i);
@@ -544,39 +504,13 @@ const QTouchEvent::TouchPoint *DirectionalDragArea::fetchTargetTouchPoint(QTouch
544 return touchPoint;
504 return touchPoint;
547−bool DirectionalDragArea::pointInsideAllowedArea() const
507+bool DirectionalDragAreaPrivate::movingInRightDirection() const
549− // NB: Using squared values to avoid computing the square root to find
550− // the length totalMovement
552− QPointF totalMovement(m_dampedScenePos.x() - m_startScenePos.x(),
553− m_dampedScenePos.y() - m_startScenePos.y());
555− qreal squaredTotalMovSize = totalMovement.x() * totalMovement.x() +
556− totalMovement.y() * totalMovement.y();
558− if (squaredTotalMovSize == 0.) {
563− qreal projectedMovement = projectOntoDirectionVector(totalMovement);
566− qreal cosineAngleSquared = (projectedMovement * projectedMovement) / squaredTotalMovSize;
569− // angle_between_movement_vector_and_gesture_direction_vector <= widening_angle
570− return cosineAngleSquared >= m_wideningFactor;
573−bool DirectionalDragArea::movingInRightDirection() const
575− if (m_direction == Direction::Horizontal) {
509+ if (direction == Direction::Horizontal || direction == Direction::Vertical) {
576 return true;
510 return true;
578− QPointF movementVector(m_dampedScenePos.x() - m_previousDampedScenePos.x(),
512+ QPointF movementVector(dampedScenePos.x() - previousDampedScenePos.x(),
579− m_dampedScenePos.y() - m_previousDampedScenePos.y());
513+ dampedScenePos.y() - previousDampedScenePos.y());
581 qreal scalarProjection = projectOntoDirectionVector(movementVector);
515 qreal scalarProjection = projectOntoDirectionVector(movementVector);
@@ -584,96 +518,95 @@ bool DirectionalDragArea::movingInRightDirection() const
587−bool DirectionalDragArea::movedFarEnough(const QPointF &point) const
521+bool DirectionalDragAreaPrivate::movedFarEnoughAlongGestureAxis() const
589− if (m_distanceThreshold <= 0.) {
523+ if (distanceThreshold <= 0.) {
590 // distance threshold check is disabled
524 // distance threshold check is disabled
591 return true;
525 return true;
593− QPointF totalMovement(point.x() - m_startScenePos.x(),
527+ QPointF totalMovement(dampedScenePos.x() - startScenePos.x(),
594− point.y() - m_startScenePos.y());
528+ dampedScenePos.y() - startScenePos.y());
596− qreal squaredTotalMovSize = totalMovement.x() * totalMovement.x() +
530+ qreal scalarProjection = projectOntoDirectionVector(totalMovement);
597− totalMovement.y() * totalMovement.y();
599− return squaredTotalMovSize > m_distanceThresholdSquared;
532+ ddaDebug(" movedFarEnoughAlongGestureAxis: scalarProjection=" << scalarProjection
533+ << ", distanceThreshold=" << distanceThreshold);
535+ if (direction == Direction::Horizontal || direction == Direction::Vertical) {
536+ return qAbs(scalarProjection) > distanceThreshold;
538+ return scalarProjection > distanceThreshold;
603−void DirectionalDragArea::checkSpeed()
543+bool DirectionalDragAreaPrivate::isPastMaxDistance() const
605− Q_ASSERT(m_status == Undecided);
545+ QPointF totalMovement(dampedScenePos.x() - startScenePos.x(),
546+ dampedScenePos.y() - startScenePos.y());
607− if (m_velocityCalculator->numSamples() >= AxisVelocityCalculator::MIN_SAMPLES_NEEDED) {
548+ qreal squaredDistance = totalMovement.x()*totalMovement.x() + totalMovement.y()*totalMovement.y();
608− qreal speed = qFabs(m_velocityCalculator->calculate());
549+ return squaredDistance > maxDistance*maxDistance;
609− qreal minSpeedMsecs = m_minSpeed / 1000.0;
550+}
611− if (speed < minSpeedMsecs) {
552+void DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible()
612− ddaDebug("Rejecting gesture because it's below minimum speed.");
553+{
554+ if (!q->isEnabled() || !q->isVisible()) {
555+ if (status == Undecided) {
613− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
556+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
557+ // We still wanna know when it ends for keeping the composition time window up-to-date
614− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
558+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
615− setStatus(WaitingForTouch);
619− if (m_velocityCalculator->numSamples() == m_numSamplesOnLastSpeedCheck) {
620− m_silenceTime += m_recognitionTimer->interval();
622− if (m_silenceTime > m_maxSilenceTime) {
561+ if (status != WaitingForTouch) {
623− ddaDebug("Rejecting gesture because its silence time has been exceeded.");
562+ ddaDebug("Resetting status because got disabled or made invisible");
624− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
625− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
626 setStatus(WaitingForTouch);
563 setStatus(WaitingForTouch);
631− m_numSamplesOnLastSpeedCheck = m_velocityCalculator->numSamples();
634−void DirectionalDragArea::giveUpIfDisabledOrInvisible()
568+void DirectionalDragAreaPrivate::rejectGesture()
636− if (!isEnabled() || !isVisible()) {
637− if (m_status == Undecided) {
570+ if (status == Undecided) {
638− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
571+ ddaDebug("Rejecting gesture because it's taking too long to drag beyond the threshold.");
639− // We still wanna know when it ends for keeping the composition time window up-to-date
640− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
643− if (m_status != WaitingForTouch) {
573+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
644− ddaDebug("Resetting status because got disabled or made invisible");
574+ // We still wanna know when it ends for keeping the composition time window up-to-date
575+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
645− setStatus(WaitingForTouch);
577+ setStatus(WaitingForTouch);
650−void DirectionalDragArea::setStatus(DirectionalDragArea::Status newStatus)
581+void DirectionalDragAreaPrivate::setStatus(Status newStatus)
652− if (newStatus == m_status)
583+ if (newStatus == status)
655− DirectionalDragArea::Status oldStatus = m_status;
586+ Status oldStatus = status;
657 if (oldStatus == Undecided) {
588 if (oldStatus == Undecided) {
658− m_recognitionTimer->stop();
589+ recognitionTimer->stop();
661− m_status = newStatus;
592+ status = newStatus;
662− Q_EMIT statusChanged(m_status);
593+ Q_EMIT statusChanged(status);
664 ddaDebug(statusToString(oldStatus) << " -> " << statusToString(newStatus));
595 ddaDebug(statusToString(oldStatus) << " -> " << statusToString(newStatus));
666 switch (newStatus) {
597 switch (newStatus) {
667 case WaitingForTouch:
598 case WaitingForTouch:
599+ if (oldStatus == Recognized) {
668− Q_EMIT draggingChanged(false);
600+ Q_EMIT q->draggingChanged(false);
602+ Q_EMIT q->pressedChanged(false);
670 case Undecided:
604 case Undecided:
671− m_recognitionTimer->start();
605+ recognitionTimer->start();
672− Q_EMIT draggingChanged(true);
606+ Q_EMIT q->pressedChanged(true);
674 case Recognized:
608 case Recognized:
675− if (oldStatus == WaitingForTouch)
676− Q_EMIT draggingChanged(true);
609+ Q_EMIT q->draggingChanged(true);
@@ -681,77 +614,128 @@ void DirectionalDragArea::setStatus(DirectionalDragArea::Status newStatus)
684−void DirectionalDragArea::setPreviousPos(const QPointF &point)
617+void DirectionalDragAreaPrivate::setPublicPos(const QPointF &point)
686− bool xChanged = m_previousPos.x() != point.x();
619+ bool xChanged = publicPos.x() != point.x();
687− bool yChanged = m_previousPos.y() != point.y();
620+ bool yChanged = publicPos.y() != point.y();
689− m_previousPos = point;
622+ // Public position should not get updated while the gesture is still being recognized
623+ // (ie, Undecided status).
624+ Q_ASSERT(status == WaitingForTouch || status == Recognized);
626+ if (status == Recognized && !recognitionIsDisabled()) {
627+ // When the gesture finally gets recognized, the finger will likely be
628+ // reasonably far from the edge. If we made the contentX immediately
629+ // follow the finger position it would be visually unpleasant as it
630+ // would appear right next to the user's finger out of nowhere (ie,
631+ // it would jump). Instead, we make contentX go towards the user's
632+ // finger in several steps. ie., in an animated way.
633+ QPointF delta = point - publicPos;
634+ // the trick is not to go all the way (1.0) as it would cause a sudden jump
635+ publicPos.rx() += 0.4 * delta.x();
636+ publicPos.ry() += 0.4 * delta.y();
638+ // no smoothing when initializing or if gesture recognition was immediate as there will
691 if (xChanged) {
643 if (xChanged) {
692− Q_EMIT touchXChanged(point.x());
644+ Q_EMIT q->touchXChanged(publicPos.x());
693− if (Direction::isHorizontal(m_direction))
645+ if (Direction::isHorizontal(direction))
694− Q_EMIT distanceChanged(distance());
646+ Q_EMIT q->distanceChanged(q->distance());
697 if (yChanged) {
649 if (yChanged) {
698− Q_EMIT touchYChanged(point.y());
650+ Q_EMIT q->touchYChanged(publicPos.y());
699− if (Direction::isVertical(m_direction))
651+ if (Direction::isVertical(direction))
700− Q_EMIT distanceChanged(distance());
652+ Q_EMIT q->distanceChanged(q->distance());
704−void DirectionalDragArea::setPreviousScenePos(const QPointF &point)
656+void DirectionalDragAreaPrivate::setPublicScenePos(const QPointF &point)
706− bool xChanged = m_previousScenePos.x() != point.x();
658+ bool xChanged = publicScenePos.x() != point.x();
707− bool yChanged = m_previousScenePos.y() != point.y();
659+ bool yChanged = publicScenePos.y() != point.y();
709 if (!xChanged && !yChanged)
661 if (!xChanged && !yChanged)
664+ // Public position should not get updated while the gesture is still being recognized
665+ // (ie, Undecided status).
666+ Q_ASSERT(status == WaitingForTouch || status == Recognized);
712− qreal oldSceneDistance = sceneDistance();
668+ qreal oldSceneDistance = sceneDistance;
713− m_previousScenePos = point;
669+
670+ if (status == Recognized && !recognitionIsDisabled()) {
671+ // When the gesture finally gets recognized, the finger will likely be
672+ // reasonably far from the edge. If we made the contentX immediately
673+ // follow the finger position it would be visually unpleasant as it
674+ // would appear right next to the user's finger out of nowhere (ie,
675+ // it would jump). Instead, we make contentX go towards the user's
676+ // finger in several steps. ie., in an animated way.
677+ QPointF delta = point - publicScenePos;
678+ // the trick is not to go all the way (1.0) as it would cause a sudden jump
679+ publicScenePos.rx() += 0.4 * delta.x();
680+ publicScenePos.ry() += 0.4 * delta.y();
682+ // no smoothing when initializing or if gesture recognition was immediate as there will
684+ publicScenePos = point;
714 updateSceneDistance();
687 updateSceneDistance();
716− if (oldSceneDistance != sceneDistance()) {
689+ if (oldSceneDistance != sceneDistance) {
717− Q_EMIT sceneDistanceChanged(sceneDistance());
690+ Q_EMIT q->sceneDistanceChanged(sceneDistance);
720 if (xChanged) {
693 if (xChanged) {
721− Q_EMIT touchSceneXChanged(point.x());
694+ Q_EMIT q->touchSceneXChanged(publicScenePos.x());
724 if (yChanged) {
697 if (yChanged) {
725− Q_EMIT touchSceneYChanged(point.y());
698+ Q_EMIT q->touchSceneYChanged(publicScenePos.y());
729−void DirectionalDragArea::updateVelocityCalculator(const QPointF &scenePos)
702+bool DirectionalDragAreaPrivate::isWithinTouchCompositionWindow()
731− QPointF totalSceneMovement = scenePos - m_startScenePos;
704+ return
705+ compositionTime > 0 &&
706+ !activeTouches.isEmpty() &&
707+ timeSource->msecsSinceReference() <=
708+ activeTouches.mostRecentStartTime() + (qint64)compositionTime;
733− qreal scalarProjection = projectOntoDirectionVector(totalSceneMovement);
711+void DirectionalDragArea::itemChange(ItemChange change, const ItemChangeData &value)
713+ if (change == QQuickItem::ItemSceneChange) {
714+ if (value.window != nullptr) {
715+ value.window->installEventFilter(TouchRegistry::instance());
735− m_velocityCalculator->setTrackedPosition(scalarProjection);
717+ // TODO: Handle window->screen() changes (ie window changing screens)
718+ qreal pixelsPerMm = value.window->screen()->physicalDotsPerInch() / 25.4;
719+ d->setPixelsPerMm(pixelsPerMm);
738−bool DirectionalDragArea::isWithinTouchCompositionWindow()
724+void DirectionalDragAreaPrivate::setPixelsPerMm(qreal pixelsPerMm)
740− return
726+ dampedScenePos.setMaxDelta(1. * pixelsPerMm);
741− compositionTime() > 0 &&
727+ setDistanceThreshold(4. * pixelsPerMm);
742− !m_activeTouches.isEmpty() &&
728+ maxDistance = 10. * pixelsPerMm;
743− m_timeSource->msecsSinceReference() <=
744− m_activeTouches.mostRecentStartTime() + (qint64)compositionTime();
747//************************** ActiveTouchesInfo **************************
731//************************** ActiveTouchesInfo **************************
749−DirectionalDragArea::ActiveTouchesInfo::ActiveTouchesInfo(const SharedTimeSource &timeSource)
733+ActiveTouchesInfo::ActiveTouchesInfo(const SharedTimeSource &timeSource)
750 : m_timeSource(timeSource)
734 : m_timeSource(timeSource)
754−void DirectionalDragArea::ActiveTouchesInfo::update(QTouchEvent *event)
738+void ActiveTouchesInfo::update(QTouchEvent *event)
756 if (!(event->touchPointStates() & (Qt::TouchPointPressed | Qt::TouchPointReleased))) {
740 if (!(event->touchPointStates() & (Qt::TouchPointPressed | Qt::TouchPointReleased))) {
757 // nothing to update
741 // nothing to update
@@ -773,7 +757,7 @@ void DirectionalDragArea::ActiveTouchesInfo::update(QTouchEvent *event)
775#if ACTIVETOUCHESINFO_DEBUG
759#if ACTIVETOUCHESINFO_DEBUG
776−QString DirectionalDragArea::ActiveTouchesInfo::toString()
760+QString ActiveTouchesInfo::toString()
778 QString string = "(";
762 QString string = "(";
@@ -791,7 +775,7 @@ QString DirectionalDragArea::ActiveTouchesInfo::toString()
792#endif // ACTIVETOUCHESINFO_DEBUG
776#endif // ACTIVETOUCHESINFO_DEBUG
794−void DirectionalDragArea::ActiveTouchesInfo::addTouchPoint(int touchId)
778+void ActiveTouchesInfo::addTouchPoint(int touchId)
796 ActiveTouchInfo &activeTouchInfo = m_touchInfoPool.getEmptySlot();
780 ActiveTouchInfo &activeTouchInfo = m_touchInfoPool.getEmptySlot();
797 activeTouchInfo.id = touchId;
781 activeTouchInfo.id = touchId;
@@ -802,7 +786,7 @@ void DirectionalDragArea::ActiveTouchesInfo::addTouchPoint(int touchId)
805−qint64 DirectionalDragArea::ActiveTouchesInfo::touchStartTime(int touchId)
789+qint64 ActiveTouchesInfo::touchStartTime(int touchId)
807 qint64 result = -1;
791 qint64 result = -1;
@@ -819,7 +803,7 @@ qint64 DirectionalDragArea::ActiveTouchesInfo::touchStartTime(int touchId)
819 return result;
803 return result;
822−void DirectionalDragArea::ActiveTouchesInfo::removeTouchPoint(int touchId)
806+void ActiveTouchesInfo::removeTouchPoint(int touchId)
824 m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {
808 m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {
825 if (touchId == touchInfo->id) {
809 if (touchId == touchInfo->id) {
@@ -835,7 +819,7 @@ void DirectionalDragArea::ActiveTouchesInfo::removeTouchPoint(int touchId)
838−qint64 DirectionalDragArea::ActiveTouchesInfo::mostRecentStartTime()
822+qint64 ActiveTouchesInfo::mostRecentStartTime()
840 Q_ASSERT(!m_touchInfoPool.isEmpty());
824 Q_ASSERT(!m_touchInfoPool.isEmpty());
@@ -851,16 +835,17 @@ qint64 DirectionalDragArea::ActiveTouchesInfo::mostRecentStartTime()
851 return highestStartTime;
835 return highestStartTime;
854−void DirectionalDragArea::updateSceneDirectionVector()
838+void DirectionalDragAreaPrivate::updateSceneDirectionVector()
856 QPointF localOrigin(0., 0.);
840 QPointF localOrigin(0., 0.);
857 QPointF localDirection;
841 QPointF localDirection;
858− switch (m_direction) {
842+ switch (direction) {
859 case Direction::Upwards:
843 case Direction::Upwards:
860 localDirection.rx() = 0.;
844 localDirection.rx() = 0.;
861 localDirection.ry() = -1.;
845 localDirection.ry() = -1.;
863 case Direction::Downwards:
847 case Direction::Downwards:
848+ case Direction::Vertical:
864 localDirection.rx() = 0.;
849 localDirection.rx() = 0.;
865 localDirection.ry() = 1;
850 localDirection.ry() = 1;
@@ -873,14 +858,31 @@ void DirectionalDragArea::updateSceneDirectionVector()
873 localDirection.ry() = 0.;
858 localDirection.ry() = 0.;
876− QPointF sceneOrigin = mapToScene(localOrigin);
861+ QPointF sceneOrigin = q->mapToScene(localOrigin);
877− QPointF sceneDirection = mapToScene(localDirection);
862+ QPointF sceneDirection = q->mapToScene(localDirection);
878− m_sceneDirectionVector = sceneDirection - sceneOrigin;
863+ sceneDirectionVector = sceneDirection - sceneOrigin;
866+qreal DirectionalDragAreaPrivate::projectOntoDirectionVector(const QPointF &sceneVector) const
868+ // same as dot product as sceneDirectionVector is a unit vector
869+ return sceneVector.x() * sceneDirectionVector.x() +
870+ sceneVector.y() * sceneDirectionVector.y();
881−qreal DirectionalDragArea::projectOntoDirectionVector(const QPointF &sceneVector) const
873+DirectionalDragAreaPrivate::DirectionalDragAreaPrivate(DirectionalDragArea *q)
875+ , status(WaitingForTouch)
878+ , direction(Direction::Rightwards)
879+ , distanceThreshold(0)
880+ , distanceThresholdSquared(0.)
882+ , compositionTime(60)
883+ , immediateRecognition(false)
884+ , recognitionTimer(nullptr)
885+ , timeSource(new RealTimeSource)
886+ , activeTouches(timeSource)
883− // same as dot product as m_sceneDirectionVector is a unit vector
884− return sceneVector.x() * m_sceneDirectionVector.x() +
885− sceneVector.y() * m_sceneDirectionVector.y();