Merge lp:~dandrader/unity8/ddaImprovements into lp:unity8

Proposed by Daniel d'Andrada
Status: Superseded
Proposed branch: lp:~dandrader/unity8/ddaImprovements
Merge into: lp:unity8
Diff against target: 5424 lines (+1721/-1505)
47 files modified
libs/UbuntuGestures/CMakeLists.txt (+1/-0)
libs/UbuntuGestures/CandidateInactivityTimer.h (+1/-1)
libs/UbuntuGestures/TimeSource.h (+14/-4)
libs/UbuntuGestures/Timer.cpp (+67/-14)
libs/UbuntuGestures/Timer.h (+15/-3)
libs/UbuntuGestures/TouchRegistry.cpp (+12/-14)
libs/UbuntuGestures/TouchRegistry.h (+18/-4)
plugins/Ubuntu/Gestures/CMakeLists.txt (+1/-1)
plugins/Ubuntu/Gestures/Damper.cpp (+24/-0)
plugins/Ubuntu/Gestures/Damper.h (+2/-0)
plugins/Ubuntu/Gestures/DirectionalDragArea.cpp (+349/-363)
plugins/Ubuntu/Gestures/DirectionalDragArea.h (+19/-208)
plugins/Ubuntu/Gestures/DirectionalDragArea_p.h (+167/-0)
plugins/Ubuntu/Gestures/TouchDispatcher.cpp (+88/-53)
plugins/Ubuntu/Gestures/TouchDispatcher.h (+16/-9)
plugins/Ubuntu/Gestures/TouchGate.cpp (+87/-63)
plugins/Ubuntu/Gestures/TouchGate.h (+18/-5)
qml/Components/DragHandle.qml (+20/-35)
qml/Components/EdgeDragArea.qml (+0/-46)
qml/Dash/Dash.qml (+5/-12)
qml/Greeter/CoverPage.qml (+1/-0)
qml/Launcher/Launcher.qml (+6/-14)
qml/Panel/IndicatorsMenu.qml (+12/-2)
qml/Stages/PhoneStage.qml (+33/-43)
qml/Stages/TabletStage.qml (+19/-21)
qml/Tutorial/TutorialBottom.qml (+1/-1)
tests/libs/UbuntuGestures/tst_TouchRegistry.cpp (+68/-2)
tests/plugins/Ubuntu/Gestures/CMakeLists.txt (+3/-1)
tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml (+5/-19)
tests/plugins/Ubuntu/Gestures/GestureTest.cpp (+13/-3)
tests/plugins/Ubuntu/Gestures/GestureTest.h (+4/-0)
tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml (+6/-19)
tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml (+5/-19)
tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml (+6/-19)
tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp (+496/-461)
tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.qml (+8/-1)
tests/plugins/Ubuntu/Gestures/tst_TouchDispatcher.cpp (+5/-6)
tests/plugins/Ubuntu/Gestures/tst_TouchGate.cpp (+56/-1)
tests/qmltests/Components/CMakeLists.txt (+3/-1)
tests/qmltests/Components/tst_DragHandle.cpp (+15/-13)
tests/qmltests/Components/tst_DragHandle.qml (+7/-0)
tests/qmltests/Components/tst_DragHandle/BidirectionalShowable.qml (+2/-2)
tests/qmltests/Components/tst_DragHandle/HorizontalShowable.qml (+4/-4)
tests/qmltests/Components/tst_DragHandle/VerticalShowable.qml (+4/-4)
tests/qmltests/Stages/tst_PhoneStage.qml (+8/-8)
tests/qmltests/Tutorial/tst_Tutorial.qml (+5/-1)
tests/utils/modules/Unity/Test/UnityTestCase.qml (+2/-5)
To merge this branch: bzr merge lp:~dandrader/unity8/ddaImprovements
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Albert Astals Cid (community) Approve
Michał Sawicz Abstain
Review via email: mp+254964@code.launchpad.net

This proposal has been superseded by a proposal from 2015-04-10.

Commit message

Several improvements to Ubuntu.Gestures

-Simplified DirectionalDragArea gesture recognition
 - The widening angle property was dropped. Didn't really work
   as in real life swipes might start up in very different directions
   before finally turning to the final overall direction. That's specially
   bad when you have to conclude the recognition in a short amount
   of time so that you can still play animations to follow the user's finger
 - minSpeed as well. Went for a simpler way of expressing and evaluating it
   which is having maxTime and maxDistance.

-EdgeDragArea
 - Fine-tuned parameters and made them based on physical size.
 - Added tests with input from real gestures to catch those annoying
   false-negatives and false-positives introduced by badly set recognition parameters

-Improved debug output

-UnownedTouchEvents are no longer sent to interim owners

-Fixed issue when TouchGate got disabled while holding an active touch

Description of the change

Need for fixing bug 1417920.
The way parameters are currently set in EdgeDragArea, a gesture gets recognized regardless of its direction, it only have to be dragged far enough.

* Are there any related MPs required for this MP to build/function as expected? Please list.
No.
* Did you perform an exploratory manual test run of your code change and any related functionality?
Yes.
* Did you make sure that your branch does not contain spurious tags?
Yes.
* If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
Not applicable.
* If you changed the UI, has there been a design review?
Not applicable.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~dandrader/unity8/ddaImprovements updated
1705. By Launchpad Translations on behalf of unity-team

Launchpad automatic translations update.

Revision history for this message
Michał Sawicz (saviq) wrote :

Failed QML tests:
    qmltestrunner.Launcher::test_clickFlick
    qmltestrunner.PhoneStage::test_shortFlick
    qmltestrunner.ShellWithPin::test_shortLeftEdgeSwipeMakesLauncherStayVisible

review: Needs Fixing
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> Failed QML tests:
> qmltestrunner.Launcher::test_clickFlick
> qmltestrunner.PhoneStage::test_shortFlick
> qmltestrunner.ShellWithPin::test_shortLeftEdgeSwipeMakesLauncherStayVisible

Fixed.

Revision history for this message
Michał Sawicz (saviq) :
review: Abstain
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~dandrader/unity8/ddaImprovements updated
1706. By Launchpad Translations on behalf of unity-team

Launchpad automatic translations update.

1707. By Launchpad Translations on behalf of unity-team

Launchpad automatic translations update.

1708. By Launchpad Translations on behalf of unity-team

Launchpad automatic translations update.

1709. By Launchpad Translations on behalf of unity-team

Launchpad automatic translations update.

Revision history for this message
Albert Astals Cid (aacid) wrote :

This does not actually fix the linked bug right?

Any way to test this change?

review: Needs Information
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> This does not actually fix the linked bug right?
>
> Any way to test this change?

It does, but you have to make Launcher's EdgeDragArea cover the whole launcher in order to notice it.

=== modified file 'qml/Launcher/Launcher.qml'
--- qml/Launcher/Launcher.qml 2015-03-12 13:04:36 +0000
+++ qml/Launcher/Launcher.qml 2015-04-07 12:19:02 +0000
@@ -280,7 +280,7 @@ Item {

         enabled: root.available
         x: -root.x // so if launcher is adjusted relative to screen, we stay put (like tutorial does when teasing)
- width: root.dragAreaWidth
+ width: root.panelWidth
         height: root.height

         onTouchXChanged: {

Apply diff the diff above to trunk and try to scroll the launcher icons. It won't work.

Then merge this branch and try again. It will work.

And check that all DDAs still work normally
  - show and hide indicators panel
  - right-edge drag to show the spread
  - left-edge drag to show launcher
  - horizontal drag to dismiss the greeter
  - bottom edge drag to show the "manage dash" UI.

Revision history for this message
Albert Astals Cid (aacid) wrote :

Can you confirm that doing http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-vivid-armhf/2103/artifact/work/output/*zip*/output.zip + width: root.panelWidth in /usr/share/unity8/Launcher/Launcher.qml in the phone it works for you?

I just tried and it still closes when i swipe vertically over an open Launcher.

review: Needs Information
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

On 07/04/15 11:08, Albert Astals Cid wrote:
> Review: Needs Information
>
> Can you confirm that doing http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-vivid-armhf/2103/artifact/work/output/*zip*/output.zip + width: root.panelWidth in /usr/share/unity8/Launcher/Launcher.qml in the phone it works for you?
>
> I just tried and it still closes when i swipe vertically over an open Launcher.

Sorry, this is the full patch:

"""
=== modified file 'qml/Launcher/Launcher.qml'
--- qml/Launcher/Launcher.qml 2015-03-12 13:04:36 +0000
+++ qml/Launcher/Launcher.qml 2015-04-07 15:13:15 +0000
@@ -280,7 +280,7 @@ Item {

         enabled: root.available
         x: -root.x // so if launcher is adjusted relative to screen, we
stay put (like tutorial does when teasing)
- width: root.dragAreaWidth
+ width: root.panelWidth
         height: root.height

         onTouchXChanged: {
@@ -306,8 +306,9 @@ Item {
                     if (distance > minimizeDistance) {
                         root.dash();
                     }
- } else {
- root.switchToNextState("")
+ } else if (root.state === "") {
+ // didn't drag far enough. rollback
+ root.switchToNextState("");
                 }
             }
         }
"""

And you don't have to do it on the phone. Doing it in "make tryShell" is
enough. Apply this patch in trunk and you won't be able to scroll the
launcher as the DDA in front of it is recognizing even those vertical
swiped. Apply it on top of ddaImprovements branch and you will see you
can scroll launcher icons just fine as the DDA rejects vertical drags.

Revision history for this message
Albert Astals Cid (aacid) wrote :

Hmmm, the code looks ok, can't see anything wrong, but i think some parameter changes need some tweaking, with the old code i can show the launcher with a small drag and it'll show the margin of the launcher and very few of the icons, with this i need to do a bigger drag and it'll in consequence show more part of the launcher.

On the other hand not sure this is problematic either. Can you try it yourself and see what you think?

review: Needs Information
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

On 08/04/15 08:18, Albert Astals Cid wrote:
> Review: Needs Information
>
> Hmmm, the code looks ok, can't see anything wrong, but i think some parameter changes need some tweaking, with the old code i can show the launcher with a small drag and it'll show the margin of the launcher and very few of the icons, with this i need to do a bigger drag and it'll in consequence show more part of the launcher.
>
> On the other hand not sure this is problematic either. Can you try it yourself and see what you think?
I get what you mean. But this is not relevant as you only do those very
short drags when you're testing the edge drag feature, ie when you're
checking how much drag it needs to kick in etc. In real usage no one
would do a very short edge drag expose the launcher just a little bit. :)

In any case I reduced the distance threshold a bit so that the gesture
kicks in a bit sooner, if that makes you feel better. :) But it does
help smoothing out the very beginning of the right edge drag (to show
the spread) as it is not animated like the left edge one. It just jumps
to the current drag position instead of animating towards it. Which
makes the larger drag threshold noticeable. But that's an issue with the
"show the spread with right-edge gesture" qml code, not with DDA per se.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote :

Approving since code looks good and the behaviour change is acceptable to me but i'd like someone else to give it a try on the phone before top approving.

 * Did you perform an exploratory manual test run of the code change and any related functionality?
Yes

 * Did CI run pass?
known autopilot fixed elsewhere

 * Did you make sure that the branch does not contain spurious tags?
Yes

review: Approve
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

right-edge animation start is now smooth as silk

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) wrote :

Broken tests again:
    qmltestrunner.PhoneStage::test_enterSpread
    qmltestrunner.Tutorial::test_bottomShortDrag
    qmltestrunner.Tutorial::test_walkthrough

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

On 09/04/15 15:35, Michał Sawicz wrote:
> Broken tests again:
> qmltestrunner.PhoneStage::test_enterSpread
> qmltestrunner.Tutorial::test_bottomShortDrag
> qmltestrunner.Tutorial::test_walkthrough
Fixed

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
lp:~dandrader/unity8/ddaImprovements updated
1710. By Launchpad Translations on behalf of unity-team

Launchpad automatic translations update.

Revision history for this message
Michael Zanetti (mzanetti) wrote :

> right-edge animation start is now smooth as silk

If this would just be unity8, I'd be totally happy with it (Except that you're missing TabletStage.qml). But given that the DDA should be an upstream component, I'm hesitant to make all the app developers do that magic as you added in the Phone stage. They won't, and as a result we'll have the jumpiness every time an app uses the DDA.

lp:~dandrader/unity8/ddaImprovements updated
1711. By Daniel d'Andrada

DirectionalDragArea & friends: various improvements & API grooming

-Simplified DirectionalDragArea gesture recognition
 * The widening angle property was dropped. Didn't really work
   as in real life swipes might start up in very different directions
   before finally turning to the final overall direction. That's specially
   bad when you have to conclude the recognition in a short amount
   of time so that you can still play animations to follow the user's finger
 * minSpeed as well. Went for a simpler way of expressing and evaluating it
   which is having maxTime and maxDistance.

-EdgeDragArea got absorbed by DirectionalDragArea

-Privatized all gesture recognition parameters.

-Privatized DirectionalDragArea.status property. All apps need is
 DirectionalDragArea.dragging. Modified qml code using DDA accordingly.

-Modified DirectionalDragArea.dragging semantics a bit. It goes to true only
 once a gesture is recognized

-Moved code that smooths out touch movement into DirectionalDragArea so all users
 get it for free.

-Cleaned up DirectionalDragArea.h of all implementation details, getting it ready
 to be moved out of unity8, into the SDK.

-Removed DirectionalDragArea.tapped() signal as it doesn't belong to it.

-Fine-tuned gesture recognition parameters and made them based on physical size.

-Added tests with input from real gestures to catch those annoying
  false-negatives and false-positives introduced by badly set recognition parameters

-Improved debug output

-UnownedTouchEvents are no longer sent to interim owners

-Fixed issue when TouchGate got disabled while holding an active touch

-Made "make tryDirectionalDragArea" and "make tryDragHandle" work with mouse again.

1712. By Daniel d'Andrada

Fix whitespace issues

1713. By Daniel d'Andrada

Fix UnityTestCase.removeTimeConstraintsFromDirectionalDragAreas()

1714. By Daniel d'Andrada

Fix tst_Shell::test_leftEdgeDragFullscreen

1715. By Daniel d'Andrada

fix shell tests

1716. By Daniel d'Andrada

Added DirectionalDragArea.pressed property

1717. By Daniel d'Andrada

Fix tst_ShellWithPin

1718. By Daniel d'Andrada

Tweak launcher animation

Unmerged revisions

1710. By Launchpad Translations on behalf of unity-team

Launchpad automatic translations update.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'libs/UbuntuGestures/CMakeLists.txt'
--- libs/UbuntuGestures/CMakeLists.txt 2014-10-01 13:20:32 +0000
+++ libs/UbuntuGestures/CMakeLists.txt 2015-04-10 21:17:56 +0000
@@ -5,6 +5,7 @@
5 CandidateInactivityTimer.cpp5 CandidateInactivityTimer.cpp
6 DebugHelpers.cpp6 DebugHelpers.cpp
7 Timer.cpp7 Timer.cpp
8 TimeSource.cpp
8 TouchOwnershipEvent.cpp9 TouchOwnershipEvent.cpp
9 TouchRegistry.cpp10 TouchRegistry.cpp
10 UnownedTouchEvent.cpp11 UnownedTouchEvent.cpp
1112
=== modified file 'libs/UbuntuGestures/CandidateInactivityTimer.h'
--- libs/UbuntuGestures/CandidateInactivityTimer.h 2014-10-01 13:20:32 +0000
+++ libs/UbuntuGestures/CandidateInactivityTimer.h 2015-04-10 21:17:56 +0000
@@ -32,7 +32,7 @@
32 AbstractTimerFactory &timerFactory,32 AbstractTimerFactory &timerFactory,
33 QObject *parent = nullptr);33 QObject *parent = nullptr);
3434
35 const int durationMs = 350;35 const int durationMs = 1000;
3636
37Q_SIGNALS:37Q_SIGNALS:
38 void candidateDefaulted(int touchId, QQuickItem *candidate);38 void candidateDefaulted(int touchId, QQuickItem *candidate);
3939
=== renamed file 'plugins/Ubuntu/Gestures/TimeSource.cpp' => 'libs/UbuntuGestures/TimeSource.cpp'
=== renamed file 'plugins/Ubuntu/Gestures/TimeSource.h' => 'libs/UbuntuGestures/TimeSource.h'
--- plugins/Ubuntu/Gestures/TimeSource.h 2014-10-01 13:20:32 +0000
+++ libs/UbuntuGestures/TimeSource.h 2015-04-10 21:17:56 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013 - Canonical Ltd.2 * Copyright (C) 2013,2015 Canonical Ltd.
3 *3 *
4 * This program is free software: you can redistribute it and/or modify it4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License, as5 * under the terms of the GNU Lesser General Public License, as
@@ -21,14 +21,14 @@
21#ifndef UBUNTUGESTURES_TIMESOURCE_H21#ifndef UBUNTUGESTURES_TIMESOURCE_H
22#define UBUNTUGESTURES_TIMESOURCE_H22#define UBUNTUGESTURES_TIMESOURCE_H
2323
24#include "UbuntuGesturesQmlGlobal.h"24#include "UbuntuGesturesGlobal.h"
25#include <QSharedPointer>25#include <QSharedPointer>
2626
27namespace UbuntuGestures {27namespace UbuntuGestures {
28/*28/*
29 Interface for a time source.29 Interface for a time source.
30 */30 */
31class UBUNTUGESTURESQML_EXPORT TimeSource {31class UBUNTUGESTURES_EXPORT TimeSource {
32public:32public:
33 virtual ~TimeSource() {}33 virtual ~TimeSource() {}
34 /* Returns the current time in milliseconds since some reference time in the past. */34 /* Returns the current time in milliseconds since some reference time in the past. */
@@ -40,7 +40,7 @@
40 Implementation of a time source40 Implementation of a time source
41 */41 */
42class RealTimeSourcePrivate;42class RealTimeSourcePrivate;
43class RealTimeSource : public TimeSource {43class UBUNTUGESTURES_EXPORT RealTimeSource : public TimeSource {
44public:44public:
45 RealTimeSource();45 RealTimeSource();
46 virtual ~RealTimeSource();46 virtual ~RealTimeSource();
@@ -49,6 +49,16 @@
49 RealTimeSourcePrivate *d;49 RealTimeSourcePrivate *d;
50};50};
5151
52/*
53 A fake time source, useful for tests
54 */
55class FakeTimeSource : public TimeSource {
56public:
57 FakeTimeSource() { m_msecsSinceReference = 0; }
58 qint64 msecsSinceReference() override { return m_msecsSinceReference; }
59 qint64 m_msecsSinceReference;
60};
61
52} // namespace UbuntuGestures62} // namespace UbuntuGestures
5363
54#endif // UBUNTUGESTURES_TIMESOURCE_H64#endif // UBUNTUGESTURES_TIMESOURCE_H
5565
=== modified file 'libs/UbuntuGestures/Timer.cpp'
--- libs/UbuntuGestures/Timer.cpp 2014-10-01 13:20:32 +0000
+++ libs/UbuntuGestures/Timer.cpp 2015-04-10 21:17:56 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2014 Canonical, Ltd.2 * Copyright (C) 2014-2015 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * 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 by5 * it under the terms of the GNU General Public License as published by
@@ -58,11 +58,34 @@
5858
59/////////////////////////////////// FakeTimer //////////////////////////////////59/////////////////////////////////// FakeTimer //////////////////////////////////
6060
61FakeTimer::FakeTimer(QObject *parent)61FakeTimer::FakeTimer(const SharedTimeSource &timeSource, QObject *parent)
62 : UbuntuGestures::AbstractTimer(parent)62 : UbuntuGestures::AbstractTimer(parent)
63 , m_interval(0)63 , m_interval(0)
64 , m_singleShot(false)64 , m_singleShot(false)
65{65 , m_timeSource(timeSource)
66{
67}
68
69void FakeTimer::update()
70{
71 if (!isRunning()) {
72 return;
73 }
74
75 if (m_nextTimeoutTime <= m_timeSource->msecsSinceReference()) {
76 if (isSingleShot()) {
77 stop();
78 } else {
79 m_nextTimeoutTime += interval();
80 }
81 Q_EMIT timeout();
82 }
83}
84
85void FakeTimer::start()
86{
87 AbstractTimer::start();
88 m_nextTimeoutTime = m_timeSource->msecsSinceReference() + (qint64)interval();
66}89}
6790
68int FakeTimer::interval() const91int FakeTimer::interval() const
@@ -87,23 +110,53 @@
87110
88/////////////////////////////////// FakeTimerFactory //////////////////////////////////111/////////////////////////////////// FakeTimerFactory //////////////////////////////////
89112
113FakeTimerFactory::FakeTimerFactory()
114{
115 m_timeSource.reset(new FakeTimeSource);
116}
117
118FakeTimerFactory::~FakeTimerFactory()
119{
120 for (int i = 0; i < timers.count(); ++i) {
121 FakeTimer *timer = timers[i].data();
122 if (timer) {
123 delete timer;
124 }
125 }
126}
127
128void FakeTimerFactory::updateTime(qint64 targetTime)
129{
130 qint64 minTimeoutTime = targetTime;
131
132 for (int i = 0; i < timers.count(); ++i) {
133 FakeTimer *timer = timers[i].data();
134 if (timer && timer->isRunning() && timer->nextTimeoutTime() < minTimeoutTime) {
135 minTimeoutTime = timer->nextTimeoutTime();
136 }
137 }
138
139 m_timeSource->m_msecsSinceReference = minTimeoutTime;
140
141 for (int i = 0; i < timers.count(); ++i) {
142 FakeTimer *timer = timers[i].data();
143 if (timer) {
144 timer->update();
145 }
146 }
147
148 if (m_timeSource->msecsSinceReference() < targetTime) {
149 updateTime(targetTime);
150 }
151}
152
90AbstractTimer *FakeTimerFactory::createTimer(QObject *parent)153AbstractTimer *FakeTimerFactory::createTimer(QObject *parent)
91{154{
92 FakeTimer *fakeTimer = new FakeTimer(parent);155 FakeTimer *fakeTimer = new FakeTimer(m_timeSource, parent);
93156
94 timers.append(fakeTimer);157 timers.append(fakeTimer);
95158
96 return fakeTimer;159 return fakeTimer;
97}160}
98161
99void FakeTimerFactory::makeRunningTimersTimeout()
100{
101 for (int i = 0; i < timers.count(); ++i) {
102 FakeTimer *timer = timers[i].data();
103 if (timer && timer->isRunning()) {
104 timer->emitTimeout();
105 }
106 }
107}
108
109} // namespace UbuntuGestures162} // namespace UbuntuGestures
110163
=== modified file 'libs/UbuntuGestures/Timer.h'
--- libs/UbuntuGestures/Timer.h 2014-10-01 13:20:32 +0000
+++ libs/UbuntuGestures/Timer.h 2015-04-10 21:17:56 +0000
@@ -18,6 +18,7 @@
18#define UBUNTUGESTURES_TIMER_H18#define UBUNTUGESTURES_TIMER_H
1919
20#include "UbuntuGesturesGlobal.h"20#include "UbuntuGesturesGlobal.h"
21#include "TimeSource.h"
2122
22#include <QObject>23#include <QObject>
23#include <QPointer>24#include <QPointer>
@@ -66,17 +67,21 @@
66{67{
67 Q_OBJECT68 Q_OBJECT
68public:69public:
69 FakeTimer(QObject *parent = nullptr);70 FakeTimer(const SharedTimeSource &timeSource, QObject *parent = nullptr);
7071
71 virtual void emitTimeout() { Q_EMIT timeout(); }72 void update();
73 qint64 nextTimeoutTime() const { return m_nextTimeoutTime; }
7274
73 int interval() const override;75 int interval() const override;
74 void setInterval(int msecs) override;76 void setInterval(int msecs) override;
77 void start() override;
75 bool isSingleShot() const override;78 bool isSingleShot() const override;
76 void setSingleShot(bool value) override;79 void setSingleShot(bool value) override;
77private:80private:
78 int m_interval;81 int m_interval;
79 bool m_singleShot;82 bool m_singleShot;
83 SharedTimeSource m_timeSource;
84 qint64 m_nextTimeoutTime;
80};85};
8186
82class UBUNTUGESTURES_EXPORT AbstractTimerFactory87class UBUNTUGESTURES_EXPORT AbstractTimerFactory
@@ -95,9 +100,16 @@
95class UBUNTUGESTURES_EXPORT FakeTimerFactory : public AbstractTimerFactory100class UBUNTUGESTURES_EXPORT FakeTimerFactory : public AbstractTimerFactory
96{101{
97public:102public:
103 FakeTimerFactory();
104 virtual ~FakeTimerFactory();
105
106 void updateTime(qint64 msecsSinceReference);
107 QSharedPointer<TimeSource> timeSource() { return m_timeSource; }
108
98 AbstractTimer *createTimer(QObject *parent = nullptr) override;109 AbstractTimer *createTimer(QObject *parent = nullptr) override;
99 void makeRunningTimersTimeout();
100 QList<QPointer<FakeTimer>> timers;110 QList<QPointer<FakeTimer>> timers;
111private:
112 QSharedPointer<FakeTimeSource> m_timeSource;
101};113};
102114
103} // namespace UbuntuGestures115} // namespace UbuntuGestures
104116
=== modified file 'libs/UbuntuGestures/TouchRegistry.cpp'
--- libs/UbuntuGestures/TouchRegistry.cpp 2014-10-01 13:20:32 +0000
+++ libs/UbuntuGestures/TouchRegistry.cpp 2015-04-10 21:17:56 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2014 Canonical, Ltd.2 * Copyright (C) 2014-2015 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * 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 by5 * it under the terms of the GNU General Public License as published by
@@ -95,10 +95,6 @@
95 // for each point and there should not be many active points at any given moment.95 // for each point and there should not be many active points at any given moment.
96 // But having three nested for-loops does scare.96 // But having three nested for-loops does scare.
9797
98 // TODO: Don't send it to the object that is already receiving the regular event
99 // because QQuickWindow is sending it to him (i.e., he's the touch owner from Qt's point of view)
100 // Problem is, we cannnot easily get this information.
101
102 const QList<QTouchEvent::TouchPoint> &updatedTouchPoints = event->touchPoints();98 const QList<QTouchEvent::TouchPoint> &updatedTouchPoints = event->touchPoints();
10399
104 // Maps an item to the touches in this event he should be informed about.100 // Maps an item to the touches in this event he should be informed about.
@@ -118,7 +114,9 @@
118 for (int i = 0; i < touchInfo->candidates.count(); ++i) {114 for (int i = 0; i < touchInfo->candidates.count(); ++i) {
119 CandidateInfo &candidate = touchInfo->candidates[i];115 CandidateInfo &candidate = touchInfo->candidates[i];
120 Q_ASSERT(!candidate.item.isNull());116 Q_ASSERT(!candidate.item.isNull());
121 touchIdsForItems[candidate.item.data()].append(touchInfo->id);117 if (candidate.state != CandidateInfo::InterimOwner) {
118 touchIdsForItems[candidate.item.data()].append(touchInfo->id);
119 }
122 }120 }
123 }121 }
124122
@@ -260,7 +258,7 @@
260 // TODO: Check if candidate already exists258 // TODO: Check if candidate already exists
261259
262 CandidateInfo candidateInfo;260 CandidateInfo candidateInfo;
263 candidateInfo.undecided = true;261 candidateInfo.state = CandidateInfo::Undecided;
264 candidateInfo.item = candidate;262 candidateInfo.item = candidate;
265 candidateInfo.inactivityTimer = new CandidateInactivityTimer(id, candidate,263 candidateInfo.inactivityTimer = new CandidateInactivityTimer(id, candidate,
266 *m_timerFactory,264 *m_timerFactory,
@@ -301,8 +299,8 @@
301 for (int i = 0; i < touchInfo->candidates.count() && indexRemoved == -1; ++i) {299 for (int i = 0; i < touchInfo->candidates.count() && indexRemoved == -1; ++i) {
302 CandidateInfo &candidateInfo = touchInfo->candidates[i];300 CandidateInfo &candidateInfo = touchInfo->candidates[i];
303 if (candidateInfo.item == candidate) {301 if (candidateInfo.item == candidate) {
304 Q_ASSERT(i > 0 || candidateInfo.undecided);302 Q_ASSERT(i > 0 || candidateInfo.state == CandidateInfo::Undecided);
305 if (i == 0 && !candidateInfo.undecided) {303 if (i == 0 && candidateInfo.state != CandidateInfo::Undecided) {
306 qCritical("TouchRegistry: touch owner is being removed.");304 qCritical("TouchRegistry: touch owner is being removed.");
307 }305 }
308 delete candidateInfo.inactivityTimer;306 delete candidateInfo.inactivityTimer;
@@ -340,7 +338,7 @@
340 for (int i = 0; i < touchInfo->candidates.count(); ++i) {338 for (int i = 0; i < touchInfo->candidates.count(); ++i) {
341 CandidateInfo &candidateInfo = touchInfo->candidates[i];339 CandidateInfo &candidateInfo = touchInfo->candidates[i];
342 if (candidateInfo.item == candidate) {340 if (candidateInfo.item == candidate) {
343 candidateInfo.undecided = false;341 candidateInfo.state = CandidateInfo::Requested;
344 delete candidateInfo.inactivityTimer;342 delete candidateInfo.inactivityTimer;
345 candidateInfo.inactivityTimer = nullptr;343 candidateInfo.inactivityTimer = nullptr;
346 candidateIndex = i;344 candidateIndex = i;
@@ -351,7 +349,7 @@
351 // add it as a candidate if not present yet349 // add it as a candidate if not present yet
352 if (candidateIndex < 0) {350 if (candidateIndex < 0) {
353 CandidateInfo candidateInfo;351 CandidateInfo candidateInfo;
354 candidateInfo.undecided = false;352 candidateInfo.state = CandidateInfo::InterimOwner;
355 candidateInfo.item = candidate;353 candidateInfo.item = candidate;
356 candidateInfo.inactivityTimer = nullptr;354 candidateInfo.inactivityTimer = nullptr;
357 touchInfo->candidates.append(candidateInfo);355 touchInfo->candidates.append(candidateInfo);
@@ -407,8 +405,8 @@
407 for (int i = 0; i < touchInfo->candidates.count() && rejectedCandidateIndex == -1; ++i) {405 for (int i = 0; i < touchInfo->candidates.count() && rejectedCandidateIndex == -1; ++i) {
408 CandidateInfo &candidateInfo = touchInfo->candidates[i];406 CandidateInfo &candidateInfo = touchInfo->candidates[i];
409 if (candidateInfo.item == candidate) {407 if (candidateInfo.item == candidate) {
410 Q_ASSERT(i > 0 || candidateInfo.undecided);408 Q_ASSERT(i > 0 || candidateInfo.state == CandidateInfo::Undecided);
411 if (i == 0 && !candidateInfo.undecided) {409 if (i == 0 && candidateInfo.state != CandidateInfo::Undecided) {
412 qCritical() << "TouchRegistry: Can't reject item (" << (void*)candidate410 qCritical() << "TouchRegistry: Can't reject item (" << (void*)candidate
413 << ") as it already owns touch" << id;411 << ") as it already owns touch" << id;
414 return;412 return;
@@ -467,7 +465,7 @@
467465
468bool TouchRegistry::TouchInfo::isOwned() const466bool TouchRegistry::TouchInfo::isOwned() const
469{467{
470 return !candidates.isEmpty() && !candidates.first().undecided;468 return !candidates.isEmpty() && candidates.first().state != CandidateInfo::Undecided;
471}469}
472470
473bool TouchRegistry::TouchInfo::ended() const471bool TouchRegistry::TouchInfo::ended() const
474472
=== modified file 'libs/UbuntuGestures/TouchRegistry.h'
--- libs/UbuntuGestures/TouchRegistry.h 2014-10-02 12:47:07 +0000
+++ libs/UbuntuGestures/TouchRegistry.h 2015-04-10 21:17:56 +0000
@@ -79,9 +79,11 @@
79 If an item wants ownership over touches as soon as he receives the TouchBegin for them, his step 179 If an item wants ownership over touches as soon as he receives the TouchBegin for them, his step 1
80 would be instead:80 would be instead:
81 TouchRegistry::instance()->requestTouchOwnership(touchId, this);81 TouchRegistry::instance()->requestTouchOwnership(touchId, this);
82 return true;82 touchEvent->accept();
83 He would then be notified once ownership has been granted to him, from which point onwards he could83 He won't get any UnownedTouchEvent for that touch as he is already the interim owner (ie, QQuickWindow
84 safely assume other TouchRegistry users wouldn't snatch this touch away from him.84 will keep sending touch updates to him already). Eventually he will be notified once ownership has
85 been granted to him (from TouchRegistry perspective), from which point onwards he could safely assume
86 other TouchRegistry users wouldn't snatch this touch away from him.
8587
86 Items oblivious to TouchRegistry will lose their touch points without warning, just like in plain Qt.88 Items oblivious to TouchRegistry will lose their touch points without warning, just like in plain Qt.
8789
@@ -132,7 +134,19 @@
132private:134private:
133 class CandidateInfo {135 class CandidateInfo {
134 public:136 public:
135 bool undecided;137 enum {
138 // A candidate owner that doesn't yet know for sure whether he wants the touch point
139 // (gesture recognition is stilll going on)
140 Undecided = 0,
141 // A candidate owner that wants the touch but hasn't been granted it yet,
142 // most likely because there's an undecided candidate with higher priority
143 Requested = 1,
144 // An item that is the interim owner of the touch, receiving QTouchEvents of it
145 // from QQuickWindow. Ie, it's the actual touch owner from Qt's point of view.
146 // It wants to keep its touch ownership but hasn't been granted it by TouchRegistry
147 // yet because of undecided candidates higher up.
148 InterimOwner = 2
149 } state;
136 // TODO: Prune candidates that become null and resolve ownership accordingly.150 // TODO: Prune candidates that become null and resolve ownership accordingly.
137 QPointer<QQuickItem> item;151 QPointer<QQuickItem> item;
138 QPointer<UbuntuGestures::CandidateInactivityTimer> inactivityTimer;152 QPointer<UbuntuGestures::CandidateInactivityTimer> inactivityTimer;
139153
=== modified file 'plugins/Ubuntu/Gestures/CMakeLists.txt'
--- plugins/Ubuntu/Gestures/CMakeLists.txt 2014-10-17 11:01:53 +0000
+++ plugins/Ubuntu/Gestures/CMakeLists.txt 2015-04-10 21:17:56 +0000
@@ -4,10 +4,10 @@
4set(UbuntuGesturesQml_SOURCES4set(UbuntuGesturesQml_SOURCES
5 plugin.cpp5 plugin.cpp
6 AxisVelocityCalculator.cpp6 AxisVelocityCalculator.cpp
7 Damper.cpp
7 Direction.cpp8 Direction.cpp
8 DirectionalDragArea.cpp9 DirectionalDragArea.cpp
9 PressedOutsideNotifier.cpp10 PressedOutsideNotifier.cpp
10 TimeSource.cpp
11 TouchDispatcher.cpp11 TouchDispatcher.cpp
12 TouchGate.cpp12 TouchGate.cpp
13)13)
1414
=== added file 'plugins/Ubuntu/Gestures/Damper.cpp'
--- plugins/Ubuntu/Gestures/Damper.cpp 1970-01-01 00:00:00 +0000
+++ plugins/Ubuntu/Gestures/Damper.cpp 2015-04-10 21:17:56 +0000
@@ -0,0 +1,24 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
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
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "Damper.h"
18#include <QDebug>
19
20QDebug operator<<(QDebug dbg, const DampedPointF &p)
21{
22 dbg.nospace() << "(" << p.x() << ", " << p.y() << ")";
23 return dbg.space();
24}
025
=== modified file 'plugins/Ubuntu/Gestures/Damper.h'
--- plugins/Ubuntu/Gestures/Damper.h 2013-08-06 13:18:34 +0000
+++ plugins/Ubuntu/Gestures/Damper.h 2015-04-10 21:17:56 +0000
@@ -84,4 +84,6 @@
84 Damper<qreal> m_y;84 Damper<qreal> m_y;
85};85};
8686
87QDebug operator<<(QDebug dbg, const DampedPointF &p);
88
87#endif // UBUNTU_GESTURES_DAMPER_H89#endif // UBUNTU_GESTURES_DAMPER_H
8890
=== modified file 'plugins/Ubuntu/Gestures/DirectionalDragArea.cpp'
--- plugins/Ubuntu/Gestures/DirectionalDragArea.cpp 2014-12-09 11:00:37 +0000
+++ plugins/Ubuntu/Gestures/DirectionalDragArea.cpp 2015-04-10 21:17:56 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013-2014 Canonical, Ltd.2 * Copyright (C) 2013-2015 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * 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 by5 * it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
2121
22#include <QQuickWindow>22#include <QQuickWindow>
23#include <QtCore/qmath.h>23#include <QtCore/qmath.h>
24#include <QScreen>
24#include <QDebug>25#include <QDebug>
2526
26#pragma GCC diagnostic push27#pragma GCC diagnostic push
@@ -33,6 +34,8 @@
33#include "TouchRegistry.h"34#include "TouchRegistry.h"
34#include "UnownedTouchEvent.h"35#include "UnownedTouchEvent.h"
3536
37#include "DirectionalDragArea_p.h"
38
36using namespace UbuntuGestures;39using namespace UbuntuGestures;
3740
38#if DIRECTIONALDRAGAREA_DEBUG41#if DIRECTIONALDRAGAREA_DEBUG
@@ -40,11 +43,11 @@
40#include "DebugHelpers.h"43#include "DebugHelpers.h"
4144
42namespace {45namespace {
43const char *statusToString(DirectionalDragArea::Status status)46const char *statusToString(DirectionalDragAreaPrivate::Status status)
44{47{
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";
49 } else {52 } else {
50 return "Recognized";53 return "Recognized";
@@ -56,205 +59,156 @@
56#define ddaDebug(params) ((void)0)59#define ddaDebug(params) ((void)0)
57#endif // DIRECTIONALDRAGAREA_DEBUG60#endif // DIRECTIONALDRAGAREA_DEBUG
5861
59
60DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)62DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
61 : QQuickItem(parent)63 : QQuickItem(parent)
62 , m_status(WaitingForTouch)64 , d(new DirectionalDragAreaPrivate(this))
63 , m_sceneDistance(0)
64 , m_touchId(-1)
65 , m_direction(Direction::Rightwards)
66 , m_wideningAngle(0)
67 , m_wideningFactor(0)
68 , m_distanceThreshold(0)
69 , m_distanceThresholdSquared(0.)
70 , m_minSpeed(0)
71 , m_maxSilenceTime(200)
72 , m_silenceTime(0)
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)
79{65{
80 setRecognitionTimer(new Timer(this));66 d->setRecognitionTimer(new Timer(this));
81 m_recognitionTimer->setInterval(60);67 d->recognitionTimer->setInterval(d->maxTime);
82 m_recognitionTimer->setSingleShot(false);68 d->recognitionTimer->setSingleShot(true);
8369
84 m_velocityCalculator = new AxisVelocityCalculator(this);70 connect(this, &QQuickItem::enabledChanged, d, &DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible);
8571 connect(this, &QQuickItem::visibleChanged, d, &DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible);
86 connect(this, &QQuickItem::enabledChanged, this, &DirectionalDragArea::giveUpIfDisabledOrInvisible);
87 connect(this, &QQuickItem::visibleChanged, this, &DirectionalDragArea::giveUpIfDisabledOrInvisible);
88}72}
8973
90Direction::Type DirectionalDragArea::direction() const74Direction::Type DirectionalDragArea::direction() const
91{75{
92 return m_direction;76 return d->direction;
93}77}
9478
95void DirectionalDragArea::setDirection(Direction::Type direction)79void DirectionalDragArea::setDirection(Direction::Type direction)
96{80{
97 if (direction != m_direction) {81 if (direction != d->direction) {
98 m_direction = direction;82 d->direction = direction;
99 Q_EMIT directionChanged(m_direction);83 Q_EMIT directionChanged(d->direction);
100 }84 }
101}85}
10286
103void DirectionalDragArea::setMaxDeviation(qreal value)87void DirectionalDragAreaPrivate::setDistanceThreshold(qreal value)
104{88{
105 if (m_dampedScenePos.maxDelta() != value) {89 if (distanceThreshold != value) {
106 m_dampedScenePos.setMaxDelta(value);90 distanceThreshold = value;
107 Q_EMIT maxDeviationChanged(value);91 distanceThresholdSquared = distanceThreshold * distanceThreshold;
108 }92 }
109}93}
11094
111qreal DirectionalDragArea::wideningAngle() const95void DirectionalDragAreaPrivate::setMaxTime(int value)
112{96{
113 return m_wideningAngle;97 if (maxTime != value) {
114}98 maxTime = value;
11599 recognitionTimer->setInterval(maxTime);
116void DirectionalDragArea::setWideningAngle(qreal angle)100 }
117{101}
118 if (angle == m_wideningAngle)102
119 return;103void DirectionalDragAreaPrivate::setRecognitionTimer(UbuntuGestures::AbstractTimer *timer)
120
121 m_wideningAngle = angle;
122
123 // wideningFactor = pow(cosine(angle), 2)
124 {
125 qreal angleRadians = angle * M_PI / 180.0;
126 m_wideningFactor = qCos(angleRadians);
127 m_wideningFactor = m_wideningFactor * m_wideningFactor;
128 }
129
130 Q_EMIT wideningAngleChanged(angle);
131}
132
133void DirectionalDragArea::setDistanceThreshold(qreal value)
134{
135 if (m_distanceThreshold != value) {
136 m_distanceThreshold = value;
137 m_distanceThresholdSquared = m_distanceThreshold * m_distanceThreshold;
138 Q_EMIT distanceThresholdChanged(value);
139 }
140}
141
142void DirectionalDragArea::setMinSpeed(qreal value)
143{
144 if (m_minSpeed != value) {
145 m_minSpeed = value;
146 Q_EMIT minSpeedChanged(value);
147 }
148}
149
150void DirectionalDragArea::setMaxSilenceTime(int value)
151{
152 if (m_maxSilenceTime != value) {
153 m_maxSilenceTime = value;
154 Q_EMIT maxSilenceTimeChanged(value);
155 }
156}
157
158void DirectionalDragArea::setCompositionTime(int value)
159{
160 if (m_compositionTime != value) {
161 m_compositionTime = value;
162 Q_EMIT compositionTimeChanged(value);
163 }
164}
165
166void DirectionalDragArea::setRecognitionTimer(UbuntuGestures::AbstractTimer *timer)
167{104{
168 int interval = 0;105 int interval = 0;
169 bool timerWasRunning = false;106 bool timerWasRunning = false;
170 bool wasSingleShot = false;107 bool wasSingleShot = false;
171108
172 // can be null when called from the constructor109 // 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;
178 }115 }
179 }116 }
180117
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();
188 }125 }
189}126}
190127
191void DirectionalDragArea::setTimeSource(const SharedTimeSource &timeSource)128void DirectionalDragAreaPrivate::setTimeSource(const SharedTimeSource &timeSource)
192{129{
193 m_timeSource = timeSource;130 this->timeSource = timeSource;
194 m_velocityCalculator->setTimeSource(timeSource);131 activeTouches.m_timeSource = timeSource;
195 m_activeTouches.m_timeSource = timeSource;
196}132}
197133
198qreal DirectionalDragArea::distance() const134qreal DirectionalDragArea::distance() const
199{135{
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();
202 } else {138 } else {
203 return m_previousPos.y() - m_startPos.y();139 return d->publicPos.y() - d->startPos.y();
204 }140 }
205}141}
206142
207void DirectionalDragArea::updateSceneDistance()143void DirectionalDragAreaPrivate::updateSceneDistance()
208{144{
209 QPointF totalMovement = m_previousScenePos - m_startScenePos;145 QPointF totalMovement = publicScenePos - startScenePos;
210 m_sceneDistance = projectOntoDirectionVector(totalMovement);146 sceneDistance = projectOntoDirectionVector(totalMovement);
211}147}
212148
213qreal DirectionalDragArea::sceneDistance() const149qreal DirectionalDragArea::sceneDistance() const
214{150{
215 return m_sceneDistance;151 return d->sceneDistance;
216}152}
217153
218qreal DirectionalDragArea::touchX() const154qreal DirectionalDragArea::touchX() const
219{155{
220 return m_previousPos.x();156 return d->publicPos.x();
221}157}
222158
223qreal DirectionalDragArea::touchY() const159qreal DirectionalDragArea::touchY() const
224{160{
225 return m_previousPos.y();161 return d->publicPos.y();
226}162}
227163
228qreal DirectionalDragArea::touchSceneX() const164qreal DirectionalDragArea::touchSceneX() const
229{165{
230 return m_previousScenePos.x();166 return d->publicScenePos.x();
231}167}
232168
233qreal DirectionalDragArea::touchSceneY() const169qreal DirectionalDragArea::touchSceneY() const
234{170{
235 return m_previousScenePos.y();171 return d->publicScenePos.y();
172}
173
174bool DirectionalDragArea::dragging() const
175{
176 return d->status == DirectionalDragAreaPrivate::Recognized;
177}
178
179bool DirectionalDragArea::immediateRecognition() const
180{
181 return d->immediateRecognition;
182}
183
184void DirectionalDragArea::setImmediateRecognition(bool enabled)
185{
186 if (d->immediateRecognition != enabled) {
187 d->immediateRecognition = enabled;
188 Q_EMIT immediateRecognitionChanged(enabled);
189 }
236}190}
237191
238bool DirectionalDragArea::event(QEvent *event)192bool DirectionalDragArea::event(QEvent *event)
239{193{
240 if (event->type() == TouchOwnershipEvent::touchOwnershipEventType()) {194 if (event->type() == TouchOwnershipEvent::touchOwnershipEventType()) {
241 touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(event));195 d->touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(event));
242 return true;196 return true;
243 } else if (event->type() == UnownedTouchEvent::unownedTouchEventType()) {197 } else if (event->type() == UnownedTouchEvent::unownedTouchEventType()) {
244 unownedTouchEvent(static_cast<UnownedTouchEvent *>(event));198 d->unownedTouchEvent(static_cast<UnownedTouchEvent *>(event));
245 return true;199 return true;
246 } else {200 } else {
247 return QQuickItem::event(event);201 return QQuickItem::event(event);
248 }202 }
249}203}
250204
251void DirectionalDragArea::touchOwnershipEvent(TouchOwnershipEvent *event)205void DirectionalDragAreaPrivate::touchOwnershipEvent(TouchOwnershipEvent *event)
252{206{
253 if (event->gained()) {207 if (event->gained()) {
254 QVector<int> ids;208 QVector<int> ids;
255 ids.append(event->touchId());209 ids.append(event->touchId());
256 ddaDebug("grabbing touch");210 ddaDebug("grabbing touch");
257 grabTouchPoints(ids);211 q->grabTouchPoints(ids);
258212
259 // Work around for Qt bug. If we grab a touch that is being used for mouse pointer213 // 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.214 // emulation it will cause the emulation logic to go nuts.
@@ -262,35 +216,35 @@
262 //216 //
263 // The fix for this bug has landed in Qt 5.4 (https://codereview.qt-project.org/96887)217 // 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.4218 // TODO: Remove this workaround once we start using Qt 5.4
265 if (window()) {219 if (q->window()) {
266 QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(window());220 QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(q->window());
267 if (windowPrivate->touchMouseId == event->touchId() && window()->mouseGrabberItem()) {221 if (windowPrivate->touchMouseId == event->touchId() && q->window()->mouseGrabberItem()) {
268 ddaDebug("removing mouse grabber");222 ddaDebug("removing mouse grabber");
269 window()->mouseGrabberItem()->ungrabMouse();223 q->window()->mouseGrabberItem()->ungrabMouse();
270 }224 }
271 }225 }
272 } else {226 } else {
273 // We still wanna know when it ends for keeping the composition time window up-to-date227 // We still wanna know when it ends for keeping the composition time window up-to-date
274 TouchRegistry::instance()->addTouchWatcher(m_touchId, this);228 TouchRegistry::instance()->addTouchWatcher(touchId, q);
275229
276 setStatus(WaitingForTouch);230 setStatus(WaitingForTouch);
277 }231 }
278}232}
279233
280void DirectionalDragArea::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent)234void DirectionalDragAreaPrivate::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent)
281{235{
282 QTouchEvent *event = unownedTouchEvent->touchEvent();236 QTouchEvent *event = unownedTouchEvent->touchEvent();
283237
284 Q_ASSERT(!event->touchPointStates().testFlag(Qt::TouchPointPressed));238 Q_ASSERT(!event->touchPointStates().testFlag(Qt::TouchPointPressed));
285239
286 ddaDebug("Unowned " << m_timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));240 ddaDebug("Unowned " << timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
287241
288 switch (m_status) {242 switch (status) {
289 case WaitingForTouch:243 case WaitingForTouch:
290 // do nothing244 // do nothing
291 break;245 break;
292 case Undecided:246 case Undecided:
293 Q_ASSERT(isEnabled() && isVisible());247 Q_ASSERT(q->isEnabled() && q->isVisible());
294 unownedTouchEvent_undecided(unownedTouchEvent);248 unownedTouchEvent_undecided(unownedTouchEvent);
295 break;249 break;
296 default: // Recognized:250 default: // Recognized:
@@ -298,18 +252,18 @@
298 break;252 break;
299 }253 }
300254
301 m_activeTouches.update(event);255 activeTouches.update(event);
302}256}
303257
304void DirectionalDragArea::unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent)258void DirectionalDragAreaPrivate::unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent)
305{259{
306 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(unownedTouchEvent->touchEvent());260 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(unownedTouchEvent->touchEvent());
307 if (!touchPoint) {261 if (!touchPoint) {
308 qCritical() << "DirectionalDragArea[status=Undecided]: touch " << m_touchId262 qCritical() << "DirectionalDragArea[status=Undecided]: touch " << touchId
309 << "missing from UnownedTouchEvent without first reaching state Qt::TouchPointReleased. "263 << "missing from UnownedTouchEvent without first reaching state Qt::TouchPointReleased. "
310 "Considering it as released.";264 "Considering it as released.";
311265
312 TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);266 TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
313 setStatus(WaitingForTouch);267 setStatus(WaitingForTouch);
314 return;268 return;
315 }269 }
@@ -319,48 +273,42 @@
319 if (touchPoint->state() == Qt::TouchPointReleased) {273 if (touchPoint->state() == Qt::TouchPointReleased) {
320 // touch has ended before recognition concluded274 // touch has ended before recognition concluded
321 ddaDebug("Touch has ended before recognition concluded");275 ddaDebug("Touch has ended before recognition concluded");
322 TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);276 TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
323 emitSignalIfTapped();277 setStatus(WaitingForTouch);
324 setStatus(WaitingForTouch);278 return;
325 return;279 }
326 }280
327281 previousDampedScenePos.setX(dampedScenePos.x());
328 m_previousDampedScenePos.setX(m_dampedScenePos.x());282 previousDampedScenePos.setY(dampedScenePos.y());
329 m_previousDampedScenePos.setY(m_dampedScenePos.y());283 dampedScenePos.update(touchScenePos);
330 m_dampedScenePos.update(touchScenePos);
331 updateVelocityCalculator(touchScenePos);
332
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);
339 return;
340 }
341284
342 if (!movingInRightDirection()) {285 if (!movingInRightDirection()) {
343 ddaDebug("Rejecting gesture because touch point is moving in the wrong direction.");286 ddaDebug("Rejecting gesture because touch point is moving in the wrong direction.");
344 TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);287 TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
345 // We still wanna know when it ends for keeping the composition time window up-to-date288 // We still wanna know when it ends for keeping the composition time window up-to-date
346 TouchRegistry::instance()->addTouchWatcher(m_touchId, this);289 TouchRegistry::instance()->addTouchWatcher(touchId, q);
347 setStatus(WaitingForTouch);290 setStatus(WaitingForTouch);
348 return;291 return;
349 }292 }
350293
351 setPreviousPos(touchPoint->pos());
352 setPreviousScenePos(touchScenePos);
353
354 if (isWithinTouchCompositionWindow()) {294 if (isWithinTouchCompositionWindow()) {
355 // There's still time for some new touch to appear and ruin our party as it would be combined295 // 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.296 // with our touchId one and therefore deny the possibility of a single-finger gesture.
357 ddaDebug("Sill within composition window. Let's wait more.");297 ddaDebug("Sill within composition window. Let's wait more.");
358 return;298 return;
359 }299 }
360300
361 if (movedFarEnough(touchScenePos)) {301 if (movedFarEnoughAlongGestureAxis()) {
362 TouchRegistry::instance()->requestTouchOwnership(m_touchId, this);302 TouchRegistry::instance()->requestTouchOwnership(touchId, q);
363 setStatus(Recognized);303 setStatus(Recognized);
304 setPublicPos(touchPoint->pos());
305 setPublicScenePos(touchScenePos);
306 } else if (isPastMaxDistance()) {
307 ddaDebug("Rejecting gesture because it went farther than maxDistance without getting recognized.");
308 TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
309 // We still wanna know when it ends for keeping the composition time window up-to-date
310 TouchRegistry::instance()->addTouchWatcher(touchId, q);
311 setStatus(WaitingForTouch);
364 } else {312 } else {
365 ddaDebug("Didn't move far enough yet. Let's wait more.");313 ddaDebug("Didn't move far enough yet. Let's wait more.");
366 }314 }
@@ -371,29 +319,29 @@
371 // TODO: Consider when more than one touch starts in the same event (although it's not possible319 // 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.320 // with Mir's android-input). Have to track them all. Consider it a plus/bonus.
373321
374 ddaDebug(m_timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));322 ddaDebug(d->timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
375323
376 if (!isEnabled() || !isVisible()) {324 if (!isEnabled() || !isVisible()) {
377 QQuickItem::touchEvent(event);325 QQuickItem::touchEvent(event);
378 return;326 return;
379 }327 }
380328
381 switch (m_status) {329 switch (d->status) {
382 case WaitingForTouch:330 case DirectionalDragAreaPrivate::WaitingForTouch:
383 touchEvent_absent(event);331 d->touchEvent_absent(event);
384 break;332 break;
385 case Undecided:333 case DirectionalDragAreaPrivate::Undecided:
386 touchEvent_undecided(event);334 d->touchEvent_undecided(event);
387 break;335 break;
388 default: // Recognized:336 default: // Recognized:
389 touchEvent_recognized(event);337 d->touchEvent_recognized(event);
390 break;338 break;
391 }339 }
392340
393 m_activeTouches.update(event);341 d->activeTouches.update(event);
394}342}
395343
396void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)344void DirectionalDragAreaPrivate::touchEvent_absent(QTouchEvent *event)
397{345{
398 // TODO: accept/reject is for the whole event, not per touch id. See how that affects us.346 // TODO: accept/reject is for the whole event, not per touch id. See how that affects us.
399347
@@ -423,36 +371,41 @@
423 allGood = false;371 allGood = false;
424 } else {372 } else {
425 // that's our candidate373 // that's our candidate
426 m_touchId = touchPoint.id();374 touchId = touchPoint.id();
427 newTouchPoint = &touchPoint;375 newTouchPoint = &touchPoint;
428 }376 }
429 }377 }
430 }378 }
431379
432 if (allGood) {380 if (allGood) {
381 allGood = sanityCheckRecognitionProperties();
382 if (!allGood) {
383 qWarning("DirectionalDragArea: recognition properties are wrongly set. Gesture recognition"
384 " is impossible");
385 }
386 }
387
388 if (allGood) {
433 Q_ASSERT(newTouchPoint);389 Q_ASSERT(newTouchPoint);
434390
435 m_startPos = newTouchPoint->pos();391 startPos = newTouchPoint->pos();
436 m_startScenePos = newTouchPoint->scenePos();392 startScenePos = newTouchPoint->scenePos();
437 m_touchId = newTouchPoint->id();393 touchId = newTouchPoint->id();
438 m_dampedScenePos.reset(m_startScenePos);394 dampedScenePos.reset(startScenePos);
439 m_velocityCalculator->setTrackedPosition(0.);395 setPublicPos(startPos);
440 m_velocityCalculator->reset();396
441 m_numSamplesOnLastSpeedCheck = 0;397 setPublicScenePos(startScenePos);
442 m_silenceTime = 0;
443 setPreviousPos(m_startPos);
444 setPreviousScenePos(m_startScenePos);
445 updateSceneDirectionVector();398 updateSceneDirectionVector();
446399
447 if (recognitionIsDisabled()) {400 if (recognitionIsDisabled()) {
448 // Behave like a dumb TouchArea401 // Behave like a dumb TouchArea
449 ddaDebug("Gesture recognition is disabled. Requesting touch ownership immediately.");402 ddaDebug("Gesture recognition is disabled. Requesting touch ownership immediately.");
450 TouchRegistry::instance()->requestTouchOwnership(m_touchId, this);403 TouchRegistry::instance()->requestTouchOwnership(touchId, q);
451 setStatus(Recognized);404 setStatus(Recognized);
452 event->accept();405 event->accept();
453 } else {406 } else {
454 // just monitor the touch points for now.407 // just monitor the touch points for now.
455 TouchRegistry::instance()->addCandidateOwnerForTouch(m_touchId, this);408 TouchRegistry::instance()->addCandidateOwnerForTouch(touchId, q);
456409
457 setStatus(Undecided);410 setStatus(Undecided);
458 // Let the item below have it. We will monitor it and grab it later if a gesture411 // Let the item below have it. We will monitor it and grab it later if a gesture
@@ -465,12 +418,11 @@
465 }418 }
466}419}
467420
468void DirectionalDragArea::touchEvent_undecided(QTouchEvent *event)421void DirectionalDragAreaPrivate::touchEvent_undecided(QTouchEvent *event)
469{422{
470 Q_ASSERT(event->type() == QEvent::TouchBegin);
471 Q_ASSERT(fetchTargetTouchPoint(event) == nullptr);423 Q_ASSERT(fetchTargetTouchPoint(event) == nullptr);
472424
473 // We're not interested in new touch points. We already have our candidate (m_touchId).425 // 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 time426 // But we do want to know when those new touches end for keeping the composition time
475 // window up-to-date427 // window up-to-date
476 event->ignore();428 event->ignore();
@@ -480,63 +432,60 @@
480 // multi-finger drags are not accepted432 // multi-finger drags are not accepted
481 ddaDebug("Multi-finger drags are not accepted");433 ddaDebug("Multi-finger drags are not accepted");
482434
483 TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);435 TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
484 // We still wanna know when it ends for keeping the composition time window up-to-date436 // We still wanna know when it ends for keeping the composition time window up-to-date
485 TouchRegistry::instance()->addTouchWatcher(m_touchId, this);437 TouchRegistry::instance()->addTouchWatcher(touchId, q);
486438
487 setStatus(WaitingForTouch);439 setStatus(WaitingForTouch);
488 }440 }
489}441}
490442
491void DirectionalDragArea::touchEvent_recognized(QTouchEvent *event)443void DirectionalDragAreaPrivate::touchEvent_recognized(QTouchEvent *event)
492{444{
493 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);445 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);
494446
495 if (!touchPoint) {447 if (!touchPoint) {
496 qCritical() << "DirectionalDragArea[status=Recognized]: touch " << m_touchId448 qCritical() << "DirectionalDragArea[status=Recognized]: touch " << touchId
497 << "missing from QTouchEvent without first reaching state Qt::TouchPointReleased. "449 << "missing from QTouchEvent without first reaching state Qt::TouchPointReleased. "
498 "Considering it as released.";450 "Considering it as released.";
499 setStatus(WaitingForTouch);451 setStatus(WaitingForTouch);
500 } else {452 } else {
501 setPreviousPos(touchPoint->pos());453 setPublicPos(touchPoint->pos());
502 setPreviousScenePos(touchPoint->scenePos());454 setPublicScenePos(touchPoint->scenePos());
503455
504 if (touchPoint->state() == Qt::TouchPointReleased) {456 if (touchPoint->state() == Qt::TouchPointReleased) {
505 emitSignalIfTapped();
506 setStatus(WaitingForTouch);457 setStatus(WaitingForTouch);
507 }458 }
508 }459 }
509}460}
510461
511void DirectionalDragArea::watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints)462void DirectionalDragAreaPrivate::watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints)
512{463{
513 for (int i = 0; i < touchPoints.count(); ++i) {464 for (int i = 0; i < touchPoints.count(); ++i) {
514 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);465 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
515 if (touchPoint.state() == Qt::TouchPointPressed) {466 if (touchPoint.state() == Qt::TouchPointPressed) {
516 TouchRegistry::instance()->addTouchWatcher(touchPoint.id(), this);467 TouchRegistry::instance()->addTouchWatcher(touchPoint.id(), q);
517 }468 }
518 }469 }
519}470}
520471
521bool DirectionalDragArea::recognitionIsDisabled() const472bool DirectionalDragAreaPrivate::recognitionIsDisabled() const
522{473{
523 return distanceThreshold() <= 0 && compositionTime() <= 0;474 return immediateRecognition || (distanceThreshold <= 0 && compositionTime <= 0);
524}475}
525476
526void DirectionalDragArea::emitSignalIfTapped()477bool DirectionalDragAreaPrivate::sanityCheckRecognitionProperties()
527{478{
528 qint64 touchDuration = m_timeSource->msecsSinceReference() - m_activeTouches.touchStartTime(m_touchId);479 return recognitionIsDisabled()
529 if (touchDuration <= maxTapDuration()) {480 || (distanceThreshold < maxDistance && compositionTime < maxTime);
530 Q_EMIT tapped();481}
531 }482
532}483const QTouchEvent::TouchPoint *DirectionalDragAreaPrivate::fetchTargetTouchPoint(QTouchEvent *event)
533
534const QTouchEvent::TouchPoint *DirectionalDragArea::fetchTargetTouchPoint(QTouchEvent *event)
535{484{
536 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();485 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
537 const QTouchEvent::TouchPoint *touchPoint = 0;486 const QTouchEvent::TouchPoint *touchPoint = 0;
538 for (int i = 0; i < touchPoints.size(); ++i) {487 for (int i = 0; i < touchPoints.size(); ++i) {
539 if (touchPoints.at(i).id() == m_touchId) {488 if (touchPoints.at(i).id() == touchId) {
540 touchPoint = &touchPoints.at(i);489 touchPoint = &touchPoints.at(i);
541 break;490 break;
542 }491 }
@@ -544,39 +493,13 @@
544 return touchPoint;493 return touchPoint;
545}494}
546495
547bool DirectionalDragArea::pointInsideAllowedArea() const496bool DirectionalDragAreaPrivate::movingInRightDirection() const
548{497{
549 // NB: Using squared values to avoid computing the square root to find498 if (direction == Direction::Horizontal) {
550 // the length totalMovement
551
552 QPointF totalMovement(m_dampedScenePos.x() - m_startScenePos.x(),
553 m_dampedScenePos.y() - m_startScenePos.y());
554
555 qreal squaredTotalMovSize = totalMovement.x() * totalMovement.x() +
556 totalMovement.y() * totalMovement.y();
557
558 if (squaredTotalMovSize == 0.) {
559 // didn't move
560 return true;
561 }
562
563 qreal projectedMovement = projectOntoDirectionVector(totalMovement);
564
565
566 qreal cosineAngleSquared = (projectedMovement * projectedMovement) / squaredTotalMovSize;
567
568 // Same as:
569 // angle_between_movement_vector_and_gesture_direction_vector <= widening_angle
570 return cosineAngleSquared >= m_wideningFactor;
571}
572
573bool DirectionalDragArea::movingInRightDirection() const
574{
575 if (m_direction == Direction::Horizontal) {
576 return true;499 return true;
577 } else {500 } else {
578 QPointF movementVector(m_dampedScenePos.x() - m_previousDampedScenePos.x(),501 QPointF movementVector(dampedScenePos.x() - previousDampedScenePos.x(),
579 m_dampedScenePos.y() - m_previousDampedScenePos.y());502 dampedScenePos.y() - previousDampedScenePos.y());
580503
581 qreal scalarProjection = projectOntoDirectionVector(movementVector);504 qreal scalarProjection = projectOntoDirectionVector(movementVector);
582505
@@ -584,96 +507,93 @@
584 }507 }
585}508}
586509
587bool DirectionalDragArea::movedFarEnough(const QPointF &point) const510bool DirectionalDragAreaPrivate::movedFarEnoughAlongGestureAxis() const
588{511{
589 if (m_distanceThreshold <= 0.) {512 if (distanceThreshold <= 0.) {
590 // distance threshold check is disabled513 // distance threshold check is disabled
591 return true;514 return true;
592 } else {515 } else {
593 QPointF totalMovement(point.x() - m_startScenePos.x(),516 QPointF totalMovement(dampedScenePos.x() - startScenePos.x(),
594 point.y() - m_startScenePos.y());517 dampedScenePos.y() - startScenePos.y());
595518
596 qreal squaredTotalMovSize = totalMovement.x() * totalMovement.x() +519 qreal scalarProjection = projectOntoDirectionVector(totalMovement);
597 totalMovement.y() * totalMovement.y();520
598521 ddaDebug(" movedFarEnoughAlongGestureAxis: scalarProjection=" << scalarProjection
599 return squaredTotalMovSize > m_distanceThresholdSquared;522 << ", distanceThreshold=" << distanceThreshold);
600 }523
601}524 if (direction == Direction::Horizontal) {
602525 return qAbs(scalarProjection) > distanceThreshold;
603void DirectionalDragArea::checkSpeed()526 } else {
604{527 return scalarProjection > distanceThreshold;
605 Q_ASSERT(m_status == Undecided);528 }
606529 }
607 if (m_velocityCalculator->numSamples() >= AxisVelocityCalculator::MIN_SAMPLES_NEEDED) {530}
608 qreal speed = qFabs(m_velocityCalculator->calculate());531
609 qreal minSpeedMsecs = m_minSpeed / 1000.0;532bool DirectionalDragAreaPrivate::isPastMaxDistance() const
610533{
611 if (speed < minSpeedMsecs) {534 QPointF totalMovement(dampedScenePos.x() - startScenePos.x(),
612 ddaDebug("Rejecting gesture because it's below minimum speed.");535 dampedScenePos.y() - startScenePos.y());
613 TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);536
614 TouchRegistry::instance()->addTouchWatcher(m_touchId, this);537 qreal squaredDistance = totalMovement.x()*totalMovement.x() + totalMovement.y()*totalMovement.y();
615 setStatus(WaitingForTouch);538 return squaredDistance > maxDistance*maxDistance;
616 }539}
617 }540
618541void DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible()
619 if (m_velocityCalculator->numSamples() == m_numSamplesOnLastSpeedCheck) {542{
620 m_silenceTime += m_recognitionTimer->interval();543 if (!q->isEnabled() || !q->isVisible()) {
621544 if (status == Undecided) {
622 if (m_silenceTime > m_maxSilenceTime) {545 TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
623 ddaDebug("Rejecting gesture because its silence time has been exceeded.");
624 TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
625 TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
626 setStatus(WaitingForTouch);
627 }
628 } else {
629 m_silenceTime = 0;
630 }
631 m_numSamplesOnLastSpeedCheck = m_velocityCalculator->numSamples();
632}
633
634void DirectionalDragArea::giveUpIfDisabledOrInvisible()
635{
636 if (!isEnabled() || !isVisible()) {
637 if (m_status == Undecided) {
638 TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
639 // We still wanna know when it ends for keeping the composition time window up-to-date546 // We still wanna know when it ends for keeping the composition time window up-to-date
640 TouchRegistry::instance()->addTouchWatcher(m_touchId, this);547 TouchRegistry::instance()->addTouchWatcher(touchId, q);
641 }548 }
642549
643 if (m_status != WaitingForTouch) {550 if (status != WaitingForTouch) {
644 ddaDebug("Resetting status because got disabled or made invisible");551 ddaDebug("Resetting status because got disabled or made invisible");
645 setStatus(WaitingForTouch);552 setStatus(WaitingForTouch);
646 }553 }
647 }554 }
648}555}
649556
650void DirectionalDragArea::setStatus(DirectionalDragArea::Status newStatus)557void DirectionalDragAreaPrivate::rejectGesture()
651{558{
652 if (newStatus == m_status)559 if (status == Undecided) {
560 ddaDebug("Rejecting gesture because it's taking too long to drag beyond the threshold.");
561
562 TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
563 // We still wanna know when it ends for keeping the composition time window up-to-date
564 TouchRegistry::instance()->addTouchWatcher(touchId, q);
565
566 setStatus(WaitingForTouch);
567 }
568}
569
570void DirectionalDragAreaPrivate::setStatus(Status newStatus)
571{
572 if (newStatus == status)
653 return;573 return;
654574
655 DirectionalDragArea::Status oldStatus = m_status;575 Status oldStatus = status;
656576
657 if (oldStatus == Undecided) {577 if (oldStatus == Undecided) {
658 m_recognitionTimer->stop();578 recognitionTimer->stop();
659 }579 }
660580
661 m_status = newStatus;581 status = newStatus;
662 Q_EMIT statusChanged(m_status);582 Q_EMIT statusChanged(status);
663583
664 ddaDebug(statusToString(oldStatus) << " -> " << statusToString(newStatus));584 ddaDebug(statusToString(oldStatus) << " -> " << statusToString(newStatus));
665585
666 switch (newStatus) {586 switch (newStatus) {
667 case WaitingForTouch:587 case WaitingForTouch:
668 Q_EMIT draggingChanged(false);588 if (oldStatus == Recognized) {
589 Q_EMIT q->draggingChanged(false);
590 }
669 break;591 break;
670 case Undecided:592 case Undecided:
671 m_recognitionTimer->start();593 recognitionTimer->start();
672 Q_EMIT draggingChanged(true);
673 break;594 break;
674 case Recognized:595 case Recognized:
675 if (oldStatus == WaitingForTouch)596 Q_EMIT q->draggingChanged(true);
676 Q_EMIT draggingChanged(true);
677 break;597 break;
678 default:598 default:
679 // no-op599 // no-op
@@ -681,77 +601,126 @@
681 }601 }
682}602}
683603
684void DirectionalDragArea::setPreviousPos(const QPointF &point)604void DirectionalDragAreaPrivate::setPublicPos(const QPointF &point)
685{605{
686 bool xChanged = m_previousPos.x() != point.x();606 bool xChanged = publicPos.x() != point.x();
687 bool yChanged = m_previousPos.y() != point.y();607 bool yChanged = publicPos.y() != point.y();
688608
689 m_previousPos = point;609 // Public position should not get updated while the gesture is still being recognized
610 // (ie, Undecided status).
611 Q_ASSERT(status == WaitingForTouch || status == Recognized);
612
613 if (status == Recognized && !recognitionIsDisabled()) {
614 // When the gesture finally gets recognized, the finger will likely be
615 // reasonably far from the edge. If we made the contentX immediately
616 // follow the finger position it would be visually unpleasant as it
617 // would appear right next to the user's finger out of nowhere (ie,
618 // it would jump). Instead, we make contentX go towards the user's
619 // finger in several steps. ie., in an animated way.
620 QPointF delta = point - publicPos;
621 // the trick is not to go all the way (1.0) as it would cause a sudden jump
622 publicPos.rx() += 0.4 * delta.x();
623 publicPos.ry() += 0.4 * delta.y();
624 } else {
625 // no smoothing when initializing or if gesture recognition was immediate as there will
626 // be no jump.
627 publicPos = point;
628 }
690629
691 if (xChanged) {630 if (xChanged) {
692 Q_EMIT touchXChanged(point.x());631 Q_EMIT q->touchXChanged(publicPos.x());
693 if (Direction::isHorizontal(m_direction))632 if (Direction::isHorizontal(direction))
694 Q_EMIT distanceChanged(distance());633 Q_EMIT q->distanceChanged(q->distance());
695 }634 }
696635
697 if (yChanged) {636 if (yChanged) {
698 Q_EMIT touchYChanged(point.y());637 Q_EMIT q->touchYChanged(publicPos.y());
699 if (Direction::isVertical(m_direction))638 if (Direction::isVertical(direction))
700 Q_EMIT distanceChanged(distance());639 Q_EMIT q->distanceChanged(q->distance());
701 }640 }
702}641}
703642
704void DirectionalDragArea::setPreviousScenePos(const QPointF &point)643void DirectionalDragAreaPrivate::setPublicScenePos(const QPointF &point)
705{644{
706 bool xChanged = m_previousScenePos.x() != point.x();645 bool xChanged = publicScenePos.x() != point.x();
707 bool yChanged = m_previousScenePos.y() != point.y();646 bool yChanged = publicScenePos.y() != point.y();
708647
709 if (!xChanged && !yChanged)648 if (!xChanged && !yChanged)
710 return;649 return;
711650
712 qreal oldSceneDistance = sceneDistance();651 // Public position should not get updated while the gesture is still being recognized
713 m_previousScenePos = point;652 // (ie, Undecided status).
653 Q_ASSERT(status == WaitingForTouch || status == Recognized);
654
655 qreal oldSceneDistance = sceneDistance;
656
657 if (status == Recognized && !recognitionIsDisabled()) {
658 // When the gesture finally gets recognized, the finger will likely be
659 // reasonably far from the edge. If we made the contentX immediately
660 // follow the finger position it would be visually unpleasant as it
661 // would appear right next to the user's finger out of nowhere (ie,
662 // it would jump). Instead, we make contentX go towards the user's
663 // finger in several steps. ie., in an animated way.
664 QPointF delta = point - publicScenePos;
665 // the trick is not to go all the way (1.0) as it would cause a sudden jump
666 publicScenePos.rx() += 0.4 * delta.x();
667 publicScenePos.ry() += 0.4 * delta.y();
668 } else {
669 // no smoothing when initializing or if gesture recognition was immediate as there will
670 // be no jump.
671 publicScenePos = point;
672 }
673
714 updateSceneDistance();674 updateSceneDistance();
715675
716 if (oldSceneDistance != sceneDistance()) {676 if (oldSceneDistance != sceneDistance) {
717 Q_EMIT sceneDistanceChanged(sceneDistance());677 Q_EMIT q->sceneDistanceChanged(sceneDistance);
718 }678 }
719679
720 if (xChanged) {680 if (xChanged) {
721 Q_EMIT touchSceneXChanged(point.x());681 Q_EMIT q->touchSceneXChanged(publicScenePos.x());
722 }682 }
723683
724 if (yChanged) {684 if (yChanged) {
725 Q_EMIT touchSceneYChanged(point.y());685 Q_EMIT q->touchSceneYChanged(publicScenePos.y());
726 }686 }
727}687}
728688
729void DirectionalDragArea::updateVelocityCalculator(const QPointF &scenePos)689bool DirectionalDragAreaPrivate::isWithinTouchCompositionWindow()
730{
731 QPointF totalSceneMovement = scenePos - m_startScenePos;
732
733 qreal scalarProjection = projectOntoDirectionVector(totalSceneMovement);
734
735 m_velocityCalculator->setTrackedPosition(scalarProjection);
736}
737
738bool DirectionalDragArea::isWithinTouchCompositionWindow()
739{690{
740 return691 return
741 compositionTime() > 0 &&692 compositionTime > 0 &&
742 !m_activeTouches.isEmpty() &&693 !activeTouches.isEmpty() &&
743 m_timeSource->msecsSinceReference() <=694 timeSource->msecsSinceReference() <=
744 m_activeTouches.mostRecentStartTime() + (qint64)compositionTime();695 activeTouches.mostRecentStartTime() + (qint64)compositionTime;
696}
697
698void DirectionalDragArea::itemChange(ItemChange change, const ItemChangeData &value)
699{
700 if (change == QQuickItem::ItemSceneChange) {
701 if (value.window != nullptr) {
702 // TODO: Handle window->screen() changes (ie window changing screens)
703 qreal pixelsPerMm = value.window->screen()->physicalDotsPerInch() / 25.4;
704 d->setPixelsPerMm(pixelsPerMm);
705 }
706 }
707}
708
709void DirectionalDragAreaPrivate::setPixelsPerMm(qreal pixelsPerMm)
710{
711 dampedScenePos.setMaxDelta(1. * pixelsPerMm);
712 setDistanceThreshold(4. * pixelsPerMm);
713 maxDistance = 10. * pixelsPerMm;
745}714}
746715
747//************************** ActiveTouchesInfo **************************716//************************** ActiveTouchesInfo **************************
748717
749DirectionalDragArea::ActiveTouchesInfo::ActiveTouchesInfo(const SharedTimeSource &timeSource)718ActiveTouchesInfo::ActiveTouchesInfo(const SharedTimeSource &timeSource)
750 : m_timeSource(timeSource)719 : m_timeSource(timeSource)
751{720{
752}721}
753722
754void DirectionalDragArea::ActiveTouchesInfo::update(QTouchEvent *event)723void ActiveTouchesInfo::update(QTouchEvent *event)
755{724{
756 if (!(event->touchPointStates() & (Qt::TouchPointPressed | Qt::TouchPointReleased))) {725 if (!(event->touchPointStates() & (Qt::TouchPointPressed | Qt::TouchPointReleased))) {
757 // nothing to update726 // nothing to update
@@ -773,7 +742,7 @@
773}742}
774743
775#if ACTIVETOUCHESINFO_DEBUG744#if ACTIVETOUCHESINFO_DEBUG
776QString DirectionalDragArea::ActiveTouchesInfo::toString()745QString ActiveTouchesInfo::toString()
777{746{
778 QString string = "(";747 QString string = "(";
779748
@@ -791,7 +760,7 @@
791}760}
792#endif // ACTIVETOUCHESINFO_DEBUG761#endif // ACTIVETOUCHESINFO_DEBUG
793762
794void DirectionalDragArea::ActiveTouchesInfo::addTouchPoint(int touchId)763void ActiveTouchesInfo::addTouchPoint(int touchId)
795{764{
796 ActiveTouchInfo &activeTouchInfo = m_touchInfoPool.getEmptySlot();765 ActiveTouchInfo &activeTouchInfo = m_touchInfoPool.getEmptySlot();
797 activeTouchInfo.id = touchId;766 activeTouchInfo.id = touchId;
@@ -802,7 +771,7 @@
802 #endif771 #endif
803}772}
804773
805qint64 DirectionalDragArea::ActiveTouchesInfo::touchStartTime(int touchId)774qint64 ActiveTouchesInfo::touchStartTime(int touchId)
806{775{
807 qint64 result = -1;776 qint64 result = -1;
808777
@@ -819,7 +788,7 @@
819 return result;788 return result;
820}789}
821790
822void DirectionalDragArea::ActiveTouchesInfo::removeTouchPoint(int touchId)791void ActiveTouchesInfo::removeTouchPoint(int touchId)
823{792{
824 m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {793 m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {
825 if (touchId == touchInfo->id) {794 if (touchId == touchInfo->id) {
@@ -835,7 +804,7 @@
835 #endif804 #endif
836}805}
837806
838qint64 DirectionalDragArea::ActiveTouchesInfo::mostRecentStartTime()807qint64 ActiveTouchesInfo::mostRecentStartTime()
839{808{
840 Q_ASSERT(!m_touchInfoPool.isEmpty());809 Q_ASSERT(!m_touchInfoPool.isEmpty());
841810
@@ -851,11 +820,11 @@
851 return highestStartTime;820 return highestStartTime;
852}821}
853822
854void DirectionalDragArea::updateSceneDirectionVector()823void DirectionalDragAreaPrivate::updateSceneDirectionVector()
855{824{
856 QPointF localOrigin(0., 0.);825 QPointF localOrigin(0., 0.);
857 QPointF localDirection;826 QPointF localDirection;
858 switch (m_direction) {827 switch (direction) {
859 case Direction::Upwards:828 case Direction::Upwards:
860 localDirection.rx() = 0.;829 localDirection.rx() = 0.;
861 localDirection.ry() = -1.;830 localDirection.ry() = -1.;
@@ -873,14 +842,31 @@
873 localDirection.ry() = 0.;842 localDirection.ry() = 0.;
874 break;843 break;
875 }844 }
876 QPointF sceneOrigin = mapToScene(localOrigin);845 QPointF sceneOrigin = q->mapToScene(localOrigin);
877 QPointF sceneDirection = mapToScene(localDirection);846 QPointF sceneDirection = q->mapToScene(localDirection);
878 m_sceneDirectionVector = sceneDirection - sceneOrigin;847 sceneDirectionVector = sceneDirection - sceneOrigin;
879}848}
880849
881qreal DirectionalDragArea::projectOntoDirectionVector(const QPointF &sceneVector) const850qreal DirectionalDragAreaPrivate::projectOntoDirectionVector(const QPointF &sceneVector) const
882{851{
883 // same as dot product as m_sceneDirectionVector is a unit vector852 // same as dot product as sceneDirectionVector is a unit vector
884 return sceneVector.x() * m_sceneDirectionVector.x() +853 return sceneVector.x() * sceneDirectionVector.x() +
885 sceneVector.y() * m_sceneDirectionVector.y();854 sceneVector.y() * sceneDirectionVector.y();
855}
856
857DirectionalDragAreaPrivate::DirectionalDragAreaPrivate(DirectionalDragArea *q)
858 : q(q)
859 , status(WaitingForTouch)
860 , sceneDistance(0)
861 , touchId(-1)
862 , direction(Direction::Rightwards)
863 , distanceThreshold(0)
864 , distanceThresholdSquared(0.)
865 , maxTime(400)
866 , compositionTime(60)
867 , immediateRecognition(false)
868 , recognitionTimer(nullptr)
869 , timeSource(new RealTimeSource)
870 , activeTouches(timeSource)
871{
886}872}
887873
=== modified file 'plugins/Ubuntu/Gestures/DirectionalDragArea.h'
--- plugins/Ubuntu/Gestures/DirectionalDragArea.h 2014-10-01 13:20:32 +0000
+++ plugins/Ubuntu/Gestures/DirectionalDragArea.h 2015-04-10 21:17:56 +0000
@@ -18,7 +18,6 @@
18#define DIRECTIONAL_DRAG_AREA_H18#define DIRECTIONAL_DRAG_AREA_H
1919
20#include <QtQuick/QQuickItem>20#include <QtQuick/QQuickItem>
21#include "AxisVelocityCalculator.h"
22#include "UbuntuGesturesQmlGlobal.h"21#include "UbuntuGesturesQmlGlobal.h"
23#include "Damper.h"22#include "Damper.h"
24#include "Direction.h"23#include "Direction.h"
@@ -29,6 +28,7 @@
2928
30class TouchOwnershipEvent;29class TouchOwnershipEvent;
31class UnownedTouchEvent;30class UnownedTouchEvent;
31class DirectionalDragAreaPrivate;
3232
33/*33/*
34 An area that detects axis-aligned single-finger drag gestures34 An area that detects axis-aligned single-finger drag gestures
@@ -61,95 +61,28 @@
61 Q_PROPERTY(qreal touchSceneX READ touchSceneX NOTIFY touchSceneXChanged)61 Q_PROPERTY(qreal touchSceneX READ touchSceneX NOTIFY touchSceneXChanged)
62 Q_PROPERTY(qreal touchSceneY READ touchSceneY NOTIFY touchSceneYChanged)62 Q_PROPERTY(qreal touchSceneY READ touchSceneY NOTIFY touchSceneYChanged)
6363
64 // The current status of the directional drag gesture area.
65 Q_PROPERTY(Status status READ status NOTIFY statusChanged)
66
67 // Whether a drag gesture is taking place64 // Whether a drag gesture is taking place
68 // This will be true as long as status is Undecided or Recognized
69 // When a gesture gets rejected, dragging turns to false.
70 Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged)65 Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged)
7166
72 /////67 // Whether a gesture should be Recognized as soon a touch lands on the area.
73 // stuff that will be set in stone at some point68 // With this property enabled it will work pretty much like a MultiPointTouchArea,
7469 // just with a different API.
75 // How far the touch point can move away from its expected position before70 //
76 // it causes a rejection in the gesture recognition. This is to compensate71 // It's false by default. In most cases you will not want that enabled.
77 // for both noise in the touch input signal and for the natural irregularities72 Q_PROPERTY(bool immediateRecognition
78 // in the finger movement.73 READ immediateRecognition
79 // Proper value is likely device-specific.74 WRITE setImmediateRecognition
80 Q_PROPERTY(qreal maxDeviation READ maxDeviation WRITE setMaxDeviation NOTIFY maxDeviationChanged)75 NOTIFY immediateRecognitionChanged)
81
82 // Widening angle, in degrees
83 // It's roughly the maximum angle a touch point can make relative to the
84 // axis defined by the compoment's direction for it to be recognized as a
85 // directional drag.
86 Q_PROPERTY(qreal wideningAngle READ wideningAngle WRITE setWideningAngle
87 NOTIFY wideningAngleChanged)
88
89 // How far a touch point has to move from its initial position in order for
90 // it to be recognized as a directional drag.
91 Q_PROPERTY(qreal distanceThreshold READ distanceThreshold WRITE setDistanceThreshold
92 NOTIFY distanceThresholdChanged)
93
94 // Minimum speed a gesture needs to have in order to be recognized as a
95 // directional drag.
96 // In pixels per second
97 Q_PROPERTY(qreal minSpeed READ minSpeed WRITE setMinSpeed NOTIFY minSpeedChanged)
98
99 // A gesture will be rejected if more than maxSilenceTime milliseconds has
100 // passed since we last got an input event from it (during Undecided state).
101 //
102 // Silence (i.e., lack of new input events) doesn't necessarily mean that the user's
103 // finger is still (zero drag speed). In some cases the finger might be moving but
104 // the driver's high noise filtering might cause those silence periods, specially
105 // in the moments succeeding a press (talking about Galaxy Nexus here).
106 Q_PROPERTY(int maxSilenceTime READ maxSilenceTime
107 WRITE setMaxSilenceTime
108 NOTIFY maxSilenceTimeChanged)
109
110 //
111 /////
112
113 // Maximum time (in milliseconds) after the start of a given touch point where
114 // subsequent touch starts are grouped with the first one into an N-touches gesture
115 // (e.g. a two-fingers tap or drag).
116 Q_PROPERTY(int compositionTime READ compositionTime
117 WRITE setCompositionTime
118 NOTIFY compositionTimeChanged)
11976
120 Q_ENUMS(Direction)77 Q_ENUMS(Direction)
121 Q_ENUMS(Status)
122public:78public:
123 DirectionalDragArea(QQuickItem *parent = 0);79 DirectionalDragArea(QQuickItem *parent = 0);
12480
125 Direction::Type direction() const;81 Direction::Type direction() const;
126 void setDirection(Direction::Type);82 void setDirection(Direction::Type);
12783
128 // Describes the state of the directional drag gesture.
129 enum Status {
130 // Waiting for a new touch point to land on this area. No gesture is being processed
131 // or tracked.
132 WaitingForTouch,
133
134 // A touch point has landed on this area but it's not know yet whether it is
135 // performing a drag in the correct direction.
136 // If it's decided that the touch point is not performing a directional drag gesture,
137 // it will be rejected/ignored and status will return to WaitingForTouch.
138 Undecided, //Recognizing,
139
140 // There's a touch point in this area and it performed a drag in the correct
141 // direction.
142 //
143 // Once recognized, the gesture state will move back to WaitingForTouch only once
144 // that touch point ends. The gesture will remain in the Recognized state even if
145 // the touch point starts moving in other directions or halts.
146 Recognized,
147 };
148 Status status() const { return m_status; }
149
150 qreal distance() const;84 qreal distance() const;
151 qreal sceneDistance() const;85 qreal sceneDistance() const;
152 void updateSceneDistance();
15386
154 qreal touchX() const;87 qreal touchX() const;
155 qreal touchY() const;88 qreal touchY() const;
@@ -157,152 +90,30 @@
157 qreal touchSceneX() const;90 qreal touchSceneX() const;
158 qreal touchSceneY() const;91 qreal touchSceneY() const;
15992
160 bool dragging() const { return (m_status == Undecided) || (m_status == Recognized); }93 bool dragging() const;
16194
162 qreal maxDeviation() const { return m_dampedScenePos.maxDelta(); }95 bool immediateRecognition() const;
163 void setMaxDeviation(qreal value);96 void setImmediateRecognition(bool enabled);
164
165 qreal wideningAngle() const;
166 void setWideningAngle(qreal value);
167
168 qreal distanceThreshold() const { return m_distanceThreshold; }
169 void setDistanceThreshold(qreal value);
170
171 qreal minSpeed() const { return m_minSpeed; }
172 void setMinSpeed(qreal value);
173
174 int maxSilenceTime() const { return m_maxSilenceTime; }
175 void setMaxSilenceTime(int value);
176
177 int compositionTime() const { return m_compositionTime; }
178 void setCompositionTime(int value);
179
180 // Replaces the existing Timer with the given one.
181 //
182 // Useful for providing a fake timer when testing.
183 void setRecognitionTimer(UbuntuGestures::AbstractTimer *timer);
184
185 // Useful for testing, where a fake time source can be supplied
186 void setTimeSource(const UbuntuGestures::SharedTimeSource &timeSource);
18797
188 bool event(QEvent *e) override;98 bool event(QEvent *e) override;
18999
190 // Maximum time, in milliseconds, between a press and a release, for a touch
191 // sequence to be considered a tap.
192 int maxTapDuration() const { return 300; }
193
194Q_SIGNALS:100Q_SIGNALS:
195 void directionChanged(Direction::Type direction);101 void directionChanged(Direction::Type direction);
196 void statusChanged(Status value);
197 void draggingChanged(bool value);102 void draggingChanged(bool value);
198 void distanceChanged(qreal value);103 void distanceChanged(qreal value);
199 void sceneDistanceChanged(qreal value);104 void sceneDistanceChanged(qreal value);
200 void maxDeviationChanged(qreal value);
201 void wideningAngleChanged(qreal value);
202 void distanceThresholdChanged(qreal value);
203 void minSpeedChanged(qreal value);
204 void maxSilenceTimeChanged(int value);
205 void compositionTimeChanged(int value);
206 void touchXChanged(qreal value);105 void touchXChanged(qreal value);
207 void touchYChanged(qreal value);106 void touchYChanged(qreal value);
208 void touchSceneXChanged(qreal value);107 void touchSceneXChanged(qreal value);
209 void touchSceneYChanged(qreal value);108 void touchSceneYChanged(qreal value);
210109 void immediateRecognitionChanged(bool value);
211 // TODO: I would rather not have such signal as it has nothing to do with drag gestures.
212 // Remove when no longer used or move its implementation to the QML code that uses it
213 // See maxTapDuration()
214 void tapped();
215110
216protected:111protected:
217 virtual void touchEvent(QTouchEvent *event);112 virtual void touchEvent(QTouchEvent *event);
218113 virtual void itemChange(ItemChange change, const ItemChangeData &value);
219private Q_SLOTS:114
220 void checkSpeed();115public: // so tests can access it
221 void giveUpIfDisabledOrInvisible();116 DirectionalDragAreaPrivate *d;
222
223private:
224 void touchEvent_absent(QTouchEvent *event);
225 void touchEvent_undecided(QTouchEvent *event);
226 void touchEvent_recognized(QTouchEvent *event);
227 bool pointInsideAllowedArea() const;
228 bool movingInRightDirection() const;
229 bool movedFarEnough(const QPointF &point) const;
230 const QTouchEvent::TouchPoint *fetchTargetTouchPoint(QTouchEvent *event);
231 void setStatus(Status newStatus);
232 void setPreviousPos(const QPointF &point);
233 void setPreviousScenePos(const QPointF &point);
234 void updateVelocityCalculator(const QPointF &point);
235 bool isWithinTouchCompositionWindow();
236 void updateSceneDirectionVector();
237 // returns the scalar projection between the given vector (in scene coordinates)
238 // and m_sceneDirectionVector
239 qreal projectOntoDirectionVector(const QPointF &sceneVector) const;
240 void touchOwnershipEvent(TouchOwnershipEvent *event);
241 void unownedTouchEvent(UnownedTouchEvent *event);
242 void unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent);
243 void watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints);
244 bool recognitionIsDisabled() const;
245 void emitSignalIfTapped();
246
247 Status m_status;
248
249 QPointF m_startPos;
250 QPointF m_startScenePos;
251 QPointF m_previousPos;
252 QPointF m_previousScenePos;
253 qreal m_sceneDistance;
254 int m_touchId;
255
256 // A movement damper is used in some of the gesture recognition calculations
257 // to get rid of noise or small oscillations in the touch position.
258 DampedPointF m_dampedScenePos;
259 QPointF m_previousDampedScenePos;
260
261 // Unit vector in scene coordinates describing the direction of the gesture recognition
262 QPointF m_sceneDirectionVector;
263
264 Direction::Type m_direction;
265 qreal m_wideningAngle; // in degrees
266 qreal m_wideningFactor; // it's pow(cosine(m_wideningAngle), 2)
267 qreal m_distanceThreshold;
268 qreal m_distanceThresholdSquared; // it's pow(m_distanceThreshold, 2)
269 qreal m_minSpeed;
270 int m_maxSilenceTime; // in milliseconds
271 int m_silenceTime; // in milliseconds
272 int m_compositionTime; // in milliseconds
273 int m_numSamplesOnLastSpeedCheck;
274 UbuntuGestures::AbstractTimer *m_recognitionTimer;
275 AxisVelocityCalculator *m_velocityCalculator;
276
277 UbuntuGestures::SharedTimeSource m_timeSource;
278
279 // Information about an active touch point
280 struct ActiveTouchInfo {
281 ActiveTouchInfo() : id(-1), startTime(-1) {}
282 bool isValid() const { return id != -1; }
283 void reset() { id = -1; }
284 int id;
285 qint64 startTime;
286 };
287 class ActiveTouchesInfo {
288 public:
289 ActiveTouchesInfo(const UbuntuGestures::SharedTimeSource &timeSource);
290 void update(QTouchEvent *event);
291 qint64 touchStartTime(int id);
292 bool isEmpty() const { return m_touchInfoPool.isEmpty(); }
293 qint64 mostRecentStartTime();
294 UbuntuGestures::SharedTimeSource m_timeSource;
295 private:
296 void addTouchPoint(int touchId);
297 void removeTouchPoint(int touchId);
298 #if ACTIVETOUCHESINFO_DEBUG
299 QString toString();
300 #endif
301
302 Pool<ActiveTouchInfo> m_touchInfoPool;
303 } m_activeTouches;
304
305 friend class tst_DirectionalDragArea;
306};117};
307118
308#endif // DIRECTIONAL_DRAG_AREA_H119#endif // DIRECTIONAL_DRAG_AREA_H
309120
=== added file 'plugins/Ubuntu/Gestures/DirectionalDragArea_p.h'
--- plugins/Ubuntu/Gestures/DirectionalDragArea_p.h 1970-01-01 00:00:00 +0000
+++ plugins/Ubuntu/Gestures/DirectionalDragArea_p.h 2015-04-10 21:17:56 +0000
@@ -0,0 +1,167 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
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
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef DIRECTIONAL_DRAG_AREA_PRIV_H
18#define DIRECTIONAL_DRAG_AREA_PRIV_H
19
20// Information about an active touch point
21struct UBUNTUGESTURESQML_EXPORT ActiveTouchInfo {
22 ActiveTouchInfo() : id(-1), startTime(-1) {}
23 bool isValid() const { return id != -1; }
24 void reset() { id = -1; }
25 int id;
26 qint64 startTime;
27};
28class UBUNTUGESTURESQML_EXPORT ActiveTouchesInfo {
29public:
30 ActiveTouchesInfo(const UbuntuGestures::SharedTimeSource &timeSource);
31 void update(QTouchEvent *event);
32 qint64 touchStartTime(int id);
33 bool isEmpty() const { return m_touchInfoPool.isEmpty(); }
34 qint64 mostRecentStartTime();
35 UbuntuGestures::SharedTimeSource m_timeSource;
36private:
37 void addTouchPoint(int touchId);
38 void removeTouchPoint(int touchId);
39 #if ACTIVETOUCHESINFO_DEBUG
40 QString toString();
41 #endif
42
43 Pool<ActiveTouchInfo> m_touchInfoPool;
44};
45
46class UBUNTUGESTURESQML_EXPORT DirectionalDragAreaPrivate : public QObject {
47 Q_OBJECT
48
49 Q_ENUMS(Status)
50public:
51 DirectionalDragAreaPrivate(DirectionalDragArea *q);
52
53public Q_SLOTS:
54 void giveUpIfDisabledOrInvisible();
55 void rejectGesture();
56
57public:
58 // Describes the state of the directional drag gesture.
59 enum Status {
60 // Waiting for a new touch point to land on this area. No gesture is being processed
61 // or tracked.
62 WaitingForTouch,
63
64 // A touch point has landed on this area but it's not know yet whether it is
65 // performing a drag in the correct direction.
66 // If it's decided that the touch point is not performing a directional drag gesture,
67 // it will be rejected/ignored and status will return to WaitingForTouch.
68 Undecided, //Recognizing,
69
70 // There's a touch point in this area and it performed a drag in the correct
71 // direction.
72 //
73 // Once recognized, the gesture state will move back to WaitingForTouch only once
74 // that touch point ends. The gesture will remain in the Recognized state even if
75 // the touch point starts moving in other directions or halts.
76 Recognized,
77 };
78
79 void touchEvent_absent(QTouchEvent *event);
80 void touchEvent_undecided(QTouchEvent *event);
81 void touchEvent_recognized(QTouchEvent *event);
82 bool movingInRightDirection() const;
83 bool movedFarEnoughAlongGestureAxis() const;
84 bool isPastMaxDistance() const;
85 const QTouchEvent::TouchPoint *fetchTargetTouchPoint(QTouchEvent *event);
86 void setStatus(Status newStatus);
87 void setPublicPos(const QPointF &point);
88 void setPublicScenePos(const QPointF &point);
89 bool isWithinTouchCompositionWindow();
90 void updateSceneDirectionVector();
91 // returns the scalar projection between the given vector (in scene coordinates)
92 // and m_sceneDirectionVector
93 qreal projectOntoDirectionVector(const QPointF &sceneVector) const;
94 void touchOwnershipEvent(TouchOwnershipEvent *event);
95 void unownedTouchEvent(UnownedTouchEvent *event);
96 void unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent);
97 void watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints);
98 bool recognitionIsDisabled() const;
99 bool sanityCheckRecognitionProperties();
100 void updateSceneDistance();
101 void setMaxTime(int value);
102 void setDistanceThreshold(qreal value);
103 void setPixelsPerMm(qreal pixelsPerMm);
104 QString objectName() const { return q->objectName(); }
105
106 // Replaces the existing Timer with the given one.
107 //
108 // Useful for providing a fake timer when testing.
109 void setRecognitionTimer(UbuntuGestures::AbstractTimer *timer);
110
111 // Useful for testing, where a fake time source can be supplied
112 void setTimeSource(const UbuntuGestures::SharedTimeSource &timeSource);
113
114 DirectionalDragArea *q;
115
116 // The current status of the directional drag gesture area.
117 Status status;
118
119 QPointF startPos;
120 QPointF startScenePos;
121 qreal sceneDistance;
122 int touchId;
123
124 // The touch position exposed in the public API.
125 // It only starts to move once the gesture gets recognized.
126 QPointF publicPos;
127 QPointF publicScenePos;
128
129 // A movement damper is used in some of the gesture recognition calculations
130 // to get rid of noise or small oscillations in the touch position.
131 DampedPointF dampedScenePos;
132 QPointF previousDampedScenePos;
133
134 // Unit vector in scene coordinates describing the direction of the gesture recognition
135 QPointF sceneDirectionVector;
136
137 Direction::Type direction;
138
139 // How far a touch point has to move from its initial position along the gesture axis in order
140 // for it to be recognized as a directional drag.
141 qreal distanceThreshold;
142 qreal distanceThresholdSquared; // it's pow(distanceThreshold, 2)
143
144 // Maximum time (in milliseconds) the gesture can take to go beyond the distance threshold
145 int maxTime;
146
147 // Maximum distance the gesture can go without crossing the axis-aligned distance threshold
148 qreal maxDistance;
149
150 // Maximum time (in milliseconds) after the start of a given touch point where
151 // subsequent touch starts are grouped with the first one into an N-touches gesture
152 // (e.g. a two-fingers tap or drag).
153 int compositionTime;
154
155 bool immediateRecognition;
156
157 UbuntuGestures::AbstractTimer *recognitionTimer;
158
159 UbuntuGestures::SharedTimeSource timeSource;
160
161 ActiveTouchesInfo activeTouches;
162
163Q_SIGNALS:
164 void statusChanged(Status value);
165};
166
167#endif // DIRECTIONAL_DRAG_AREA_PRIV_H
0168
=== modified file 'plugins/Ubuntu/Gestures/TouchDispatcher.cpp'
--- plugins/Ubuntu/Gestures/TouchDispatcher.cpp 2014-11-03 15:21:46 +0000
+++ plugins/Ubuntu/Gestures/TouchDispatcher.cpp 2015-04-10 21:17:56 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2014 Canonical, Ltd.2 * Copyright (C) 2014-2015 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * 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 by5 * it under the terms of the GNU General Public License as published by
@@ -28,8 +28,11 @@
28#define TOUCHDISPATCHER_DEBUG 028#define TOUCHDISPATCHER_DEBUG 0
2929
30#if TOUCHDISPATCHER_DEBUG30#if TOUCHDISPATCHER_DEBUG
31#define ugDebug(params) qDebug().nospace() << "[TouchDispatcher(" << this << ")] " << params
31#include <DebugHelpers.h>32#include <DebugHelpers.h>
32#endif33#else // TOUCHDISPATCHER_DEBUG
34#define ugDebug(params) ((void)0)
35#endif // TOUCHDISPATCHER_DEBUG
3336
34TouchDispatcher::TouchDispatcher()37TouchDispatcher::TouchDispatcher()
35 : m_status(NoActiveTouch)38 : m_status(NoActiveTouch)
@@ -44,13 +47,12 @@
44 m_targetItem = target;47 m_targetItem = target;
45 if (m_status != NoActiveTouch) {48 if (m_status != NoActiveTouch) {
46 qWarning("[TouchDispatcher] Changing target item in the middle of a touch stream");49 qWarning("[TouchDispatcher] Changing target item in the middle of a touch stream");
47 m_status = TargetRejectedTouches;50 setStatus(TargetRejectedTouches);
48 }51 }
49 }52 }
50}53}
5154
52void TouchDispatcher::dispatch(QEvent::Type eventType,55void TouchDispatcher::dispatch(QTouchDevice *device,
53 QTouchDevice *device,
54 Qt::KeyboardModifiers modifiers,56 Qt::KeyboardModifiers modifiers,
55 const QList<QTouchEvent::TouchPoint> &touchPoints,57 const QList<QTouchEvent::TouchPoint> &touchPoints,
56 QWindow *window,58 QWindow *window,
@@ -61,6 +63,8 @@
61 return;63 return;
62 }64 }
6365
66 QEvent::Type eventType = resolveEventType(touchPoints);
67
64 if (eventType == QEvent::TouchBegin) {68 if (eventType == QEvent::TouchBegin) {
65 dispatchTouchBegin(device, modifiers, touchPoints, window, timestamp);69 dispatchTouchBegin(device, modifiers, touchPoints, window, timestamp);
6670
@@ -72,15 +76,13 @@
72 dispatchAsMouse(device, modifiers, touchPoints, timestamp);76 dispatchAsMouse(device, modifiers, touchPoints, timestamp);
73 } else {77 } else {
74 Q_ASSERT(m_status == TargetRejectedTouches);78 Q_ASSERT(m_status == TargetRejectedTouches);
75 #if TOUCHDISPATCHER_DEBUG79 ugDebug("Not dispatching touch event to " << m_targetItem.data()
76 qDebug() << "[TouchDispatcher] Not dispatching touch event to" << m_targetItem.data()80 << "because it already rejected the touch stream.");
77 << "because it already rejected the touch stream.";
78 #endif
79 // Do nothing81 // Do nothing
80 }82 }
8183
82 if (eventType == QEvent::TouchEnd) {84 if (eventType == QEvent::TouchEnd) {
83 m_status = NoActiveTouch;85 setStatus(NoActiveTouch);
84 m_touchMouseId = -1;86 m_touchMouseId = -1;
85 }87 }
8688
@@ -103,10 +105,7 @@
103 QQuickItem *targetItem = m_targetItem.data();105 QQuickItem *targetItem = m_targetItem.data();
104106
105 if (!targetItem->isEnabled() || !targetItem->isVisible()) {107 if (!targetItem->isEnabled() || !targetItem->isVisible()) {
106 #if TOUCHDISPATCHER_DEBUG108 ugDebug("Cannot dispatch touch event to " << targetItem << " because it's disabled or invisible.");
107 qDebug() << "[TouchDispatcher] Cannot dispatch touch event to" << targetItem
108 << "because it's disabled or invisible.";
109 #endif
110 return;109 return;
111 }110 }
112111
@@ -118,62 +117,46 @@
118 createQTouchEvent(QEvent::TouchBegin, device, modifiers, targetTouchPoints, window, timestamp));117 createQTouchEvent(QEvent::TouchBegin, device, modifiers, targetTouchPoints, window, timestamp));
119118
120119
121 #if TOUCHDISPATCHER_DEBUG120 ugDebug("dispatching " << qPrintable(touchEventToString(touchEvent.data()))
122 qDebug() << "[TouchDispatcher] dispatching" << qPrintable(touchEventToString(touchEvent.data()))121 << " to " << targetItem);
123 << "to" << targetItem;
124 #endif
125 QCoreApplication::sendEvent(targetItem, touchEvent.data());122 QCoreApplication::sendEvent(targetItem, touchEvent.data());
126123
127124
128 if (touchEvent->isAccepted()) {125 if (touchEvent->isAccepted()) {
129 #if TOUCHDISPATCHER_DEBUG126 ugDebug("Item accepted the touch event.");
130 qDebug() << "[TouchDispatcher] Item accepted the touch event.";127 setStatus(DeliveringTouchEvents);
131 #endif
132 m_status = DeliveringTouchEvents;
133 } else if (targetItem->acceptedMouseButtons() & Qt::LeftButton) {128 } else if (targetItem->acceptedMouseButtons() & Qt::LeftButton) {
134 #if TOUCHDISPATCHER_DEBUG129 ugDebug("Item rejected the touch event. Trying a QMouseEvent");
135 qDebug() << "[TouchDispatcher] Item rejected the touch event. Trying a QMouseEvent";
136 #endif
137 // NB: Arbitrarily chose the first touch point to emulate the mouse pointer130 // NB: Arbitrarily chose the first touch point to emulate the mouse pointer
138 QScopedPointer<QMouseEvent> mouseEvent(131 QScopedPointer<QMouseEvent> mouseEvent(
139 touchToMouseEvent(QEvent::MouseButtonPress, targetTouchPoints.at(0), timestamp,132 touchToMouseEvent(QEvent::MouseButtonPress, targetTouchPoints.at(0), timestamp,
140 modifiers, false /* transformNeeded */));133 modifiers, false /* transformNeeded */));
141 Q_ASSERT(targetTouchPoints.at(0).state() == Qt::TouchPointPressed);134 Q_ASSERT(targetTouchPoints.at(0).state() == Qt::TouchPointPressed);
142135
143 #if TOUCHDISPATCHER_DEBUG136 ugDebug("dispatching " << qPrintable(mouseEventToString(mouseEvent.data()))
144 qDebug() << "[TouchDispatcher] dispatching" << qPrintable(mouseEventToString(mouseEvent.data()))137 << " to " << m_targetItem.data());
145 << "to" << m_targetItem.data();
146 #endif
147 QCoreApplication::sendEvent(targetItem, mouseEvent.data());138 QCoreApplication::sendEvent(targetItem, mouseEvent.data());
148 if (mouseEvent->isAccepted()) {139 if (mouseEvent->isAccepted()) {
149 #if TOUCHDISPATCHER_DEBUG140 ugDebug("Item accepted the QMouseEvent.");
150 qDebug() << "[TouchDispatcher] Item accepted the QMouseEvent.";141 setStatus(DeliveringMouseEvents);
151 #endif
152 m_status = DeliveringMouseEvents;
153 m_touchMouseId = targetTouchPoints.at(0).id();142 m_touchMouseId = targetTouchPoints.at(0).id();
154143
155 if (checkIfDoubleClicked(timestamp)) {144 if (checkIfDoubleClicked(timestamp)) {
156 QScopedPointer<QMouseEvent> doubleClickEvent(145 QScopedPointer<QMouseEvent> doubleClickEvent(
157 touchToMouseEvent(QEvent::MouseButtonDblClick, targetTouchPoints.at(0), timestamp,146 touchToMouseEvent(QEvent::MouseButtonDblClick, targetTouchPoints.at(0), timestamp,
158 modifiers, false /* transformNeeded */));147 modifiers, false /* transformNeeded */));
159 #if TOUCHDISPATCHER_DEBUG148 ugDebug("dispatching " << qPrintable(mouseEventToString(doubleClickEvent.data()))
160 qDebug() << "[TouchDispatcher] dispatching" << qPrintable(mouseEventToString(doubleClickEvent.data()))149 << " to " << m_targetItem.data());
161 << "to" << m_targetItem.data();
162 #endif
163 QCoreApplication::sendEvent(targetItem, doubleClickEvent.data());150 QCoreApplication::sendEvent(targetItem, doubleClickEvent.data());
164 }151 }
165152
166 } else {153 } else {
167 #if TOUCHDISPATCHER_DEBUG154 ugDebug("Item rejected the QMouseEvent.");
168 qDebug() << "[TouchDispatcher] Item rejected the QMouseEvent.";155 setStatus(TargetRejectedTouches);
169 #endif
170 m_status = TargetRejectedTouches;
171 }156 }
172 } else {157 } else {
173 #if TOUCHDISPATCHER_DEBUG158 ugDebug("Item rejected the touch event and does not accept mouse buttons.");
174 qDebug() << "[TouchDispatcher] Item rejected the touch event and does not accept mouse buttons.";159 setStatus(TargetRejectedTouches);
175 #endif
176 m_status = TargetRejectedTouches;
177 }160 }
178}161}
179162
@@ -194,10 +177,8 @@
194 createQTouchEvent(eventType, device, modifiers, targetTouchPoints, window, timestamp));177 createQTouchEvent(eventType, device, modifiers, targetTouchPoints, window, timestamp));
195178
196179
197 #if TOUCHDISPATCHER_DEBUG180 ugDebug("dispatching " << qPrintable(touchEventToString(eventForTargetItem.data()))
198 qDebug() << "[TouchDispatcher] dispatching" << qPrintable(touchEventToString(eventForTargetItem.data()))181 << " to " << targetItem);
199 << "to" << targetItem;
200 #endif
201 QCoreApplication::sendEvent(targetItem, eventForTargetItem.data());182 QCoreApplication::sendEvent(targetItem, eventForTargetItem.data());
202}183}
203184
@@ -254,10 +235,8 @@
254 QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(eventType, *touchMouse, timestamp, modifiers,235 QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(eventType, *touchMouse, timestamp, modifiers,
255 true /* transformNeeded */));236 true /* transformNeeded */));
256237
257 #if TOUCHDISPATCHER_DEBUG238 ugDebug("dispatching " << qPrintable(mouseEventToString(mouseEvent.data()))
258 qDebug() << "[TouchDispatcher] dispatching" << qPrintable(mouseEventToString(mouseEvent.data()))239 << " to " << m_targetItem.data());
259 << "to" << m_targetItem.data();
260 #endif
261 QCoreApplication::sendEvent(m_targetItem.data(), mouseEvent.data());240 QCoreApplication::sendEvent(m_targetItem.data(), mouseEvent.data());
262 }241 }
263}242}
@@ -365,3 +344,59 @@
365344
366 return doubleClicked;345 return doubleClicked;
367}346}
347
348void TouchDispatcher::setStatus(Status status)
349{
350 if (status != m_status) {
351 #if TOUCHDISPATCHER_DEBUG
352 switch (status) {
353 case NoActiveTouch:
354 ugDebug("status = NoActiveTouch");
355 break;
356 case DeliveringTouchEvents:
357 ugDebug("status = DeliveringTouchEvents");
358 break;
359 case DeliveringMouseEvents:
360 ugDebug("status = DeliveringMouseEvents");
361 break;
362 case TargetRejectedTouches:
363 ugDebug("status = TargetRejectedTouches");
364 break;
365 default:
366 ugDebug("status = " << status);
367 break;
368 }
369 #endif
370 m_status = status;
371 }
372}
373
374void TouchDispatcher::reset()
375{
376 setStatus(NoActiveTouch);
377 m_touchMouseId = -1;
378 m_touchMousePressTimestamp =0;
379}
380
381QEvent::Type TouchDispatcher::resolveEventType(const QList<QTouchEvent::TouchPoint> &touchPoints)
382{
383 QEvent::Type eventType;
384
385 Qt::TouchPointStates eventStates = 0;
386 for (int i = 0; i < touchPoints.count(); i++)
387 eventStates |= touchPoints[i].state();
388
389 switch (eventStates) {
390 case Qt::TouchPointPressed:
391 eventType = QEvent::TouchBegin;
392 break;
393 case Qt::TouchPointReleased:
394 eventType = QEvent::TouchEnd;
395 break;
396 default:
397 eventType = QEvent::TouchUpdate;
398 break;
399 }
400
401 return eventType;
402}
368403
=== modified file 'plugins/Ubuntu/Gestures/TouchDispatcher.h'
--- plugins/Ubuntu/Gestures/TouchDispatcher.h 2014-11-03 15:21:46 +0000
+++ plugins/Ubuntu/Gestures/TouchDispatcher.h 2015-04-10 21:17:56 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2014 Canonical, Ltd.2 * Copyright (C) 2014-2015 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * 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 by5 * it under the terms of the GNU General Public License as published by
@@ -36,12 +36,20 @@
36 void setTargetItem(QQuickItem *target);36 void setTargetItem(QQuickItem *target);
37 QQuickItem *targetItem() { return m_targetItem; }37 QQuickItem *targetItem() { return m_targetItem; }
3838
39 void dispatch(QEvent::Type eventType,39 void dispatch(QTouchDevice *device,
40 QTouchDevice *device,
41 Qt::KeyboardModifiers modifiers,40 Qt::KeyboardModifiers modifiers,
42 const QList<QTouchEvent::TouchPoint> &touchPoints,41 const QList<QTouchEvent::TouchPoint> &touchPoints,
43 QWindow *window,42 QWindow *window,
44 ulong timestamp);43 ulong timestamp);
44
45 void reset();
46
47 enum Status {
48 NoActiveTouch,
49 DeliveringTouchEvents,
50 DeliveringMouseEvents,
51 TargetRejectedTouches
52 };
45private:53private:
46 void dispatchTouchBegin(54 void dispatchTouchBegin(
47 QTouchDevice *device,55 QTouchDevice *device,
@@ -73,14 +81,13 @@
7381
74 bool checkIfDoubleClicked(ulong newPressEventTimestamp);82 bool checkIfDoubleClicked(ulong newPressEventTimestamp);
7583
84 void setStatus(Status status);
85
86 static QEvent::Type resolveEventType(const QList<QTouchEvent::TouchPoint> &touchPoints);
87
76 QPointer<QQuickItem> m_targetItem;88 QPointer<QQuickItem> m_targetItem;
7789
78 enum {90 Status m_status;
79 NoActiveTouch,
80 DeliveringTouchEvents,
81 DeliveringMouseEvents,
82 TargetRejectedTouches
83 } m_status;
8491
85 int m_touchMouseId;92 int m_touchMouseId;
86 ulong m_touchMousePressTimestamp;93 ulong m_touchMousePressTimestamp;
8794
=== modified file 'plugins/Ubuntu/Gestures/TouchGate.cpp'
--- plugins/Ubuntu/Gestures/TouchGate.cpp 2015-01-28 12:59:21 +0000
+++ plugins/Ubuntu/Gestures/TouchGate.cpp 2015-04-10 21:17:56 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2014 Canonical, Ltd.2 * Copyright (C) 2014-2015 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * 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 by5 * it under the terms of the GNU General Public License as published by
@@ -23,8 +23,18 @@
23#include <TouchRegistry.h>23#include <TouchRegistry.h>
2424
25#if TOUCHGATE_DEBUG25#if TOUCHGATE_DEBUG
26#define ugDebug(params) qDebug().nospace() << "[TouchGate(" << (void*)this << ")] " << params
26#include <DebugHelpers.h>27#include <DebugHelpers.h>
27#endif28#else // TOUCHGATE_DEBUG
29#define ugDebug(params) ((void)0)
30#endif // TOUCHGATE_DEBUG
31
32TouchGate::TouchGate(QQuickItem *parent)
33 : QQuickItem(parent)
34{
35 connect(this, &QQuickItem::enabledChanged,
36 this, &TouchGate::onEnabledChanged);
37}
2838
29bool TouchGate::event(QEvent *e)39bool TouchGate::event(QEvent *e)
30{40{
@@ -38,13 +48,12 @@
3848
39void TouchGate::touchEvent(QTouchEvent *event)49void TouchGate::touchEvent(QTouchEvent *event)
40{50{
41 #if TOUCHGATE_DEBUG51 ugDebug("got touch event" << qPrintable(touchEventToString(event)));
42 qDebug() << "[TouchGate] got touch event" << qPrintable(touchEventToString(event));
43 #endif
44 event->accept();52 event->accept();
4553
46 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();54 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
47 bool goodToGo = true;55 QList<QTouchEvent::TouchPoint> validTouchPoints;
56 bool ownsAllTouches = true;
48 for (int i = 0; i < touchPoints.count(); ++i) {57 for (int i = 0; i < touchPoints.count(); ++i) {
49 const QTouchEvent::TouchPoint &touchPoint = touchPoints[i];58 const QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
5059
@@ -57,31 +66,41 @@
57 Q_EMIT pressed();66 Q_EMIT pressed();
58 }67 }
5968
60 goodToGo &= m_touchInfoMap.contains(touchPoint.id())69 if (m_touchInfoMap.contains(touchPoint.id())) {
61 && m_touchInfoMap[touchPoint.id()].ownership == OwnershipGranted;70 validTouchPoints.append(touchPoint);
6271
63 if (touchPoint.state() == Qt::TouchPointReleased && m_touchInfoMap.contains(touchPoint.id())) {72 ownsAllTouches &= m_touchInfoMap[touchPoint.id()].ownership == OwnershipGranted;
64 m_touchInfoMap[touchPoint.id()].ended = true;73
74 if (touchPoint.state() == Qt::TouchPointReleased) {
75 m_touchInfoMap[touchPoint.id()].ended = true;
76 }
65 }77 }
6678
67 }79 }
6880
69 if (goodToGo) {81 if (validTouchPoints.isEmpty()) {
82 // nothing to do.
83 return;
84 }
85
86 if (ownsAllTouches) {
70 if (m_storedEvents.isEmpty()) {87 if (m_storedEvents.isEmpty()) {
71 // let it pass through88 // let it pass through
72 dispatchTouchEventToTarget(event);89 removeTouchInfoForEndedTouches(validTouchPoints);
90 m_dispatcher.dispatch(event->device(), event->modifiers(), validTouchPoints,
91 event->window(), event->timestamp());
73 } else {92 } else {
74 // Retain the event to ensure TouchGate dispatches them in order.93 // Retain the event to ensure TouchGate dispatches them in order.
75 // Otherwise the current event would come before the stored ones, which are older.94 // Otherwise the current event would come before the stored ones, which are older.
76 #if TOUCHGATE_DEBUG95 ugDebug("Storing event because thouches " << qPrintable(oldestPendingTouchIdsString())
77 qDebug("[TouchGate] Storing event because thouches %s are still pending ownership.",96 << " are still pending ownership.");
78 qPrintable(oldestPendingTouchIdsString()));97 storeTouchEvent(event->device(), event->modifiers(), validTouchPoints,
79 #endif98 event->window(), event->timestamp());
80 storeTouchEvent(event);
81 }99 }
82 } else {100 } else {
83 // Retain events that have unowned touches101 // Retain events that have unowned touches
84 storeTouchEvent(event);102 storeTouchEvent(event->device(), event->modifiers(), validTouchPoints,
103 event->window(), event->timestamp());
85 }104 }
86}105}
87106
@@ -90,24 +109,23 @@
90 // TODO: Optimization: batch those actions as TouchOwnershipEvents109 // TODO: Optimization: batch those actions as TouchOwnershipEvents
91 // might come one right after the other.110 // might come one right after the other.
92111
93 Q_ASSERT(m_touchInfoMap.contains(event->touchId()));112 if (m_touchInfoMap.contains(event->touchId())) {
94113 TouchInfo &touchInfo = m_touchInfoMap[event->touchId()];
95 TouchInfo &touchInfo = m_touchInfoMap[event->touchId()];114
96115 if (event->gained()) {
97 if (event->gained()) {116 ugDebug("Got ownership of touch " << event->touchId());
98 #if TOUCHGATE_DEBUG117 touchInfo.ownership = OwnershipGranted;
99 qDebug() << "[TouchGate] Got ownership of touch " << event->touchId();118 } else {
100 #endif119 ugDebug("Lost ownership of touch " << event->touchId());
101 touchInfo.ownership = OwnershipGranted;120 m_touchInfoMap.remove(event->touchId());
121 removeTouchFromStoredEvents(event->touchId());
122 }
123
124 dispatchFullyOwnedEvents();
102 } else {125 } else {
103 #if TOUCHGATE_DEBUG126 // Ignore it. It probably happened because the TouchGate got disabled
104 qDebug() << "[TouchGate] Lost ownership of touch " << event->touchId();127 // between the time it requested ownership and the time it got it.
105 #endif
106 m_touchInfoMap.remove(event->touchId());
107 removeTouchFromStoredEvents(event->touchId());
108 }128 }
109
110 dispatchFullyOwnedEvents();
111}129}
112130
113bool TouchGate::isTouchPointOwned(int touchId) const131bool TouchGate::isTouchPointOwned(int touchId) const
@@ -115,14 +133,15 @@
115 return m_touchInfoMap[touchId].ownership == OwnershipGranted;133 return m_touchInfoMap[touchId].ownership == OwnershipGranted;
116}134}
117135
118void TouchGate::storeTouchEvent(const QTouchEvent *event)136void TouchGate::storeTouchEvent(QTouchDevice *device,
137 Qt::KeyboardModifiers modifiers,
138 const QList<QTouchEvent::TouchPoint> &touchPoints,
139 QWindow *window,
140 ulong timestamp)
119{141{
120 #if TOUCHGATE_DEBUG142 ugDebug("Storing" << touchPoints);
121 qDebug() << "[TouchGate] Storing" << qPrintable(touchEventToString(event));143 TouchEvent event(device, modifiers, touchPoints, window, timestamp);
122 #endif144 m_storedEvents.append(std::move(event));
123
124 TouchEvent clonedEvent(event);
125 m_storedEvents.append(std::move(clonedEvent));
126}145}
127146
128void TouchGate::removeTouchFromStoredEvents(int touchId)147void TouchGate::removeTouchFromStoredEvents(int touchId)
@@ -195,25 +214,13 @@
195void TouchGate::dispatchTouchEventToTarget(const TouchEvent &event)214void TouchGate::dispatchTouchEventToTarget(const TouchEvent &event)
196{215{
197 removeTouchInfoForEndedTouches(event.touchPoints);216 removeTouchInfoForEndedTouches(event.touchPoints);
198 m_dispatcher.dispatch(event.eventType,217 m_dispatcher.dispatch(event.device,
199 event.device,
200 event.modifiers,218 event.modifiers,
201 event.touchPoints,219 event.touchPoints,
202 event.window,220 event.window,
203 event.timestamp);221 event.timestamp);
204}222}
205223
206void TouchGate::dispatchTouchEventToTarget(QTouchEvent* event)
207{
208 removeTouchInfoForEndedTouches(event->touchPoints());
209 m_dispatcher.dispatch(event->type(),
210 event->device(),
211 event->modifiers(),
212 event->touchPoints(),
213 event->window(),
214 event->timestamp());
215}
216
217void TouchGate::removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints)224void TouchGate::removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints)
218{225{
219 for (int i = 0; i < touchPoints.size(); ++i) {\226 for (int i = 0; i < touchPoints.size(); ++i) {\
@@ -228,14 +235,31 @@
228 }235 }
229}236}
230237
231TouchGate::TouchEvent::TouchEvent(const QTouchEvent *event)238void TouchGate::onEnabledChanged()
232 : eventType(event->type())239{
233 , device(event->device())240 ugDebug(" enabled = " << isEnabled());
234 , modifiers(event->modifiers())241 if (!isEnabled()) {
235 , touchPoints(event->touchPoints())242 reset();
236 , target(qobject_cast<QQuickItem*>(event->target()))243 }
237 , window(event->window())244}
238 , timestamp(event->timestamp())245
246void TouchGate::reset()
247{
248 m_storedEvents.clear();
249 m_touchInfoMap.clear();
250 m_dispatcher.reset();
251}
252
253TouchGate::TouchEvent::TouchEvent(QTouchDevice *device,
254 Qt::KeyboardModifiers modifiers,
255 const QList<QTouchEvent::TouchPoint> &touchPoints,
256 QWindow *window,
257 ulong timestamp)
258 : device(device)
259 , modifiers(modifiers)
260 , touchPoints(touchPoints)
261 , window(window)
262 , timestamp(timestamp)
239{263{
240}264}
241265
242266
=== modified file 'plugins/Ubuntu/Gestures/TouchGate.h'
--- plugins/Ubuntu/Gestures/TouchGate.h 2015-01-28 12:59:21 +0000
+++ plugins/Ubuntu/Gestures/TouchGate.h 2015-04-10 21:17:56 +0000
@@ -45,6 +45,8 @@
45 Q_PROPERTY(QQuickItem* targetItem READ targetItem WRITE setTargetItem NOTIFY targetItemChanged)45 Q_PROPERTY(QQuickItem* targetItem READ targetItem WRITE setTargetItem NOTIFY targetItemChanged)
4646
47public:47public:
48 TouchGate(QQuickItem *parent = nullptr);
49
48 bool event(QEvent *e) override;50 bool event(QEvent *e) override;
4951
50 QQuickItem *targetItem() { return m_dispatcher.targetItem(); }52 QQuickItem *targetItem() { return m_dispatcher.targetItem(); }
@@ -57,31 +59,42 @@
5759
58protected:60protected:
59 void touchEvent(QTouchEvent *event) override;61 void touchEvent(QTouchEvent *event) override;
62
63private Q_SLOTS:
64 void onEnabledChanged();
65
60private:66private:
67 void reset();
68
61 class TouchEvent {69 class TouchEvent {
62 public:70 public:
63 TouchEvent(const QTouchEvent *event);71 TouchEvent(QTouchDevice *device,
72 Qt::KeyboardModifiers modifiers,
73 const QList<QTouchEvent::TouchPoint> &touchPoints,
74 QWindow *window,
75 ulong timestamp);
6476
65 bool removeTouch(int touchId);77 bool removeTouch(int touchId);
6678
67 QEvent::Type eventType;
68 QTouchDevice *device;79 QTouchDevice *device;
69 Qt::KeyboardModifiers modifiers;80 Qt::KeyboardModifiers modifiers;
70 QList<QTouchEvent::TouchPoint> touchPoints;81 QList<QTouchEvent::TouchPoint> touchPoints;
71 QQuickItem *target;
72 QWindow *window;82 QWindow *window;
73 ulong timestamp;83 ulong timestamp;
74 };84 };
7585
76 void touchOwnershipEvent(TouchOwnershipEvent *event);86 void touchOwnershipEvent(TouchOwnershipEvent *event);
77 bool isTouchPointOwned(int touchId) const;87 bool isTouchPointOwned(int touchId) const;
78 void storeTouchEvent(const QTouchEvent *event);88 void storeTouchEvent(QTouchDevice *device,
89 Qt::KeyboardModifiers modifiers,
90 const QList<QTouchEvent::TouchPoint> &touchPoints,
91 QWindow *window,
92 ulong timestamp);
79 void removeTouchFromStoredEvents(int touchId);93 void removeTouchFromStoredEvents(int touchId);
80 void dispatchFullyOwnedEvents();94 void dispatchFullyOwnedEvents();
81 bool eventIsFullyOwned(const TouchEvent &event) const;95 bool eventIsFullyOwned(const TouchEvent &event) const;
8296
83 void dispatchTouchEventToTarget(const TouchEvent &event);97 void dispatchTouchEventToTarget(const TouchEvent &event);
84 void dispatchTouchEventToTarget(QTouchEvent* event);
8598
86 void removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints);99 void removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints);
87100
88101
=== modified file 'qml/Components/DragHandle.qml'
--- qml/Components/DragHandle.qml 2014-12-05 17:06:36 +0000
+++ qml/Components/DragHandle.qml 2015-04-10 21:17:56 +0000
@@ -43,16 +43,8 @@
43 }43 }
4444
45 */45 */
46EdgeDragArea {46DirectionalDragArea {
47 id: dragArea47 id: dragArea
48 objectName: "dragHandle"
49
50 // Disable gesture recognition by default when hinting is used as
51 // it conflicts with the hinting idea.
52 distanceThreshold: hintDisplacement > 0 ? 0 : defaultDistanceThreshold
53 maxSilenceTime: hintDisplacement > 0 ? 60*60*1000 : defaultMaxSilenceTime
54 maxDeviation: hintDisplacement > 0 ? 999999 : defaultMaxDeviation
55 compositionTime: hintDisplacement > 0 ? 0 : defaultCompositionTime
5648
57 property bool stretch: false49 property bool stretch: false
5850
@@ -68,6 +60,9 @@
68 }60 }
6961
70 property real hintDisplacement: 062 property real hintDisplacement: 0
63
64 immediateRecognition: hintDisplacement > 0
65
71 property var overrideStartValue: undefined66 property var overrideStartValue: undefined
72 SmoothedAnimation {67 SmoothedAnimation {
73 id: hintingAnimation68 id: hintingAnimation
@@ -98,7 +93,6 @@
98 // Private stuff93 // Private stuff
99 QtObject {94 QtObject {
100 id: d95 id: d
101 property var previousStatus: DirectionalDragArea.WaitingForTouch
102 property real startValue96 property real startValue
103 property real minValue: {97 property real minValue: {
104 if (direction == Direction.Horizontal) {98 if (direction == Direction.Horizontal) {
@@ -183,7 +177,7 @@
183 }177 }
184178
185 onDistanceChanged: {179 onDistanceChanged: {
186 if (status === DirectionalDragArea.Recognized) {180 if (dragging) {
187 // don't go the whole distance in order to smooth out the movement181 // don't go the whole distance in order to smooth out the movement
188 var step = distance * 0.3;182 var step = distance * 0.3;
189183
@@ -193,31 +187,22 @@
193 }187 }
194 }188 }
195189
196 onStatusChanged: {190 onDraggingChanged: {
197 if (status === DirectionalDragArea.WaitingForTouch) {191 if (dragging) {
192 dragEvaluator.reset();
193 if (overrideStartValue !== undefined) {
194 d.startValue = overrideStartValue;
195 } else {
196 d.startValue = parent[d.targetProp];
197 }
198
199 if (hintDisplacement > 0) {
200 hintingAnimation.targetValue = d.startValue;
201 hintingAnimation.start();
202 }
203 } else {
198 hintingAnimation.stop();204 hintingAnimation.stop();
199 if (d.previousStatus === DirectionalDragArea.Recognized) {205 d.onFinishedRecognizedGesture();
200 d.onFinishedRecognizedGesture();
201 } else /* d.previousStatus === DirectionalDragArea.Undecided */ {
202 // Gesture was rejected.
203 d.rollbackDrag();
204 }
205 } else /* Undecided || Recognized */ {
206 if (d.previousStatus === DirectionalDragArea.WaitingForTouch) {
207 dragEvaluator.reset();
208 if (overrideStartValue !== undefined) {
209 d.startValue = overrideStartValue;
210 } else {
211 d.startValue = parent[d.targetProp];
212 }
213
214 if (hintDisplacement > 0) {
215 hintingAnimation.targetValue = d.startValue;
216 hintingAnimation.start();
217 }
218 }
219 }206 }
220
221 d.previousStatus = status;
222 }207 }
223}208}
224209
=== removed file 'qml/Components/EdgeDragArea.qml'
--- qml/Components/EdgeDragArea.qml 2014-10-01 13:20:32 +0000
+++ qml/Components/EdgeDragArea.qml 1970-01-01 00:00:00 +0000
@@ -1,46 +0,0 @@
1/*
2 * Copyright (C) 2013 Canonical, Ltd.
3 *
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
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18import Ubuntu.Components 0.1
19import Ubuntu.Gestures 0.1
20
21/*
22 A DirectionalDragArea wrapper that provides some well-chosen defaults
23 for its gesture recognition parameters.
24*/
25DirectionalDragArea {
26
27 // TODO: Re-evaluate those or even the recognition heuristics itself once
28 // we have gesture cancelling/forwarding in place.
29 //
30 // The idea here is that it's better having lax rules than false negatives.
31 // False negatives are very frustrating to the user.
32 maxDeviation: defaultMaxDeviation
33 wideningAngle: defaultWideningAngle
34 distanceThreshold: defaultDistanceThreshold
35 minSpeed: defaultMinSpeed
36 maxSilenceTime: defaultMaxSilenceTime
37 compositionTime: defaultCompositionTime
38
39 readonly property real defaultMaxDeviation: units.gu(3)
40 readonly property real defaultWideningAngle: 50
41 readonly property real defaultDistanceThreshold: units.gu(1.5)
42 // some people were getting false negatives with it enabled.
43 readonly property real defaultMinSpeed: units.gu(0)
44 readonly property int defaultMaxSilenceTime: 200
45 readonly property int defaultCompositionTime: 60
46}
470
=== modified file 'qml/Dash/Dash.qml'
--- qml/Dash/Dash.qml 2015-02-17 08:26:20 +0000
+++ qml/Dash/Dash.qml 2015-04-10 21:17:56 +0000
@@ -140,7 +140,7 @@
140 // (as expected) but would also cause the dash content flickable to move a bit, because140 // (as expected) but would also cause the dash content flickable to move a bit, because
141 // that flickable was getting the touch events while overviewDragHandle was still undecided141 // that flickable was getting the touch events while overviewDragHandle was still undecided
142 // about whether that touch was indeed performing a directional drag gesture.142 // about whether that touch was indeed performing a directional drag gesture.
143 forceNonInteractive: overviewDragHandle.status != DirectionalDragArea.WaitingForTouch143 forceNonInteractive: overviewDragHandle.dragging
144144
145 enabled: bottomEdgeController.progress == 0145 enabled: bottomEdgeController.progress == 0
146 }146 }
@@ -317,7 +317,7 @@
317 }317 }
318 }318 }
319319
320 EdgeDragArea {320 DirectionalDragArea {
321 id: overviewDragHandle321 id: overviewDragHandle
322 objectName: "overviewDragHandle"322 objectName: "overviewDragHandle"
323 z: 1323 z: 1
@@ -333,21 +333,14 @@
333 height: units.gu(2)333 height: units.gu(2)
334334
335 onSceneDistanceChanged: {335 onSceneDistanceChanged: {
336 if (status == DirectionalDragArea.Recognized) {336 if (dragging) {
337 bottomEdgeController.enableAnimation = false;337 bottomEdgeController.enableAnimation = false;
338 bottomEdgeController.progress = Math.max(0, Math.min(1, sceneDistance / fullMovement));338 bottomEdgeController.progress = Math.max(0, Math.min(1, sceneDistance / fullMovement));
339 }339 }
340 }340 }
341341
342 property int previousStatus: -1342 onDraggingChanged: {
343 property int currentStatus: DirectionalDragArea.WaitingForTouch343 if (!dragging) {
344
345 onStatusChanged: {
346 previousStatus = currentStatus;
347 currentStatus = status;
348
349 if (status == DirectionalDragArea.WaitingForTouch &&
350 previousStatus == DirectionalDragArea.Recognized) {
351 bottomEdgeController.enableAnimation = true;344 bottomEdgeController.enableAnimation = true;
352 bottomEdgeController.progress = (bottomEdgeController.progress > 0.2) ? 1 : 0;345 bottomEdgeController.progress = (bottomEdgeController.progress > 0.2) ? 1 : 0;
353 }346 }
354347
=== modified file 'qml/Greeter/CoverPage.qml'
--- qml/Greeter/CoverPage.qml 2015-02-17 15:54:17 +0000
+++ qml/Greeter/CoverPage.qml 2015-04-10 21:17:56 +0000
@@ -129,6 +129,7 @@
129129
130 DragHandle {130 DragHandle {
131 id: dragHandle131 id: dragHandle
132 objectName: "coverPageDragHandle"
132 anchors.fill: parent133 anchors.fill: parent
133 anchors.leftMargin: root.dragHandleLeftMargin134 anchors.leftMargin: root.dragHandleLeftMargin
134 enabled: root.draggable135 enabled: root.draggable
135136
=== modified file 'qml/Launcher/Launcher.qml'
--- qml/Launcher/Launcher.qml 2015-03-12 13:04:36 +0000
+++ qml/Launcher/Launcher.qml 2015-04-10 21:17:56 +0000
@@ -210,7 +210,7 @@
210 bottom: parent.bottom210 bottom: parent.bottom
211 }211 }
212 x: -width212 x: -width
213 visible: root.x > 0 || x > -width || dragArea.status === DirectionalDragArea.Undecided213 visible: root.x > 0 || x > -width
214 model: LauncherModel214 model: LauncherModel
215215
216 property bool animate: true216 property bool animate: true
@@ -272,7 +272,7 @@
272 }272 }
273 }273 }
274274
275 EdgeDragArea {275 DirectionalDragArea {
276 id: dragArea276 id: dragArea
277 objectName: "launcherDragArea"277 objectName: "launcherDragArea"
278278
@@ -284,19 +284,10 @@
284 height: root.height284 height: root.height
285285
286 onTouchXChanged: {286 onTouchXChanged: {
287 if (status !== DirectionalDragArea.Recognized || launcher.state == "visible")287 if (!dragging || launcher.state == "visible")
288 return;288 return;
289289
290 // When the gesture finally gets recognized, the finger will likely be290 panel.x = Math.min(0, touchX - panel.width) - root.x
291 // reasonably far from the edge. If we made the panel immediately
292 // follow the finger position it would be visually unpleasant as it
293 // would appear right next to the user's finger out of nowhere.
294 // Instead, we make the panel go towards the user's finger in several
295 // steps. ie., in an animated way.
296 var targetPanelX = Math.min(0, touchX - panel.width) - root.x
297 var delta = targetPanelX - panel.x
298 // the trick is not to go all the way (1.0) as it would cause a sudden jump
299 panel.x += 0.4 * delta
300 }291 }
301292
302 onDraggingChanged: {293 onDraggingChanged: {
@@ -306,7 +297,8 @@
306 if (distance > minimizeDistance) {297 if (distance > minimizeDistance) {
307 root.dash();298 root.dash();
308 }299 }
309 } else {300 } else if (root.state === "") {
301 // didn't drag far enough. rollback
310 root.switchToNextState("")302 root.switchToNextState("")
311 }303 }
312 }304 }
313305
=== modified file 'qml/Panel/IndicatorsMenu.qml'
--- qml/Panel/IndicatorsMenu.qml 2015-03-18 10:17:21 +0000
+++ qml/Panel/IndicatorsMenu.qml 2015-04-10 21:17:56 +0000
@@ -182,9 +182,18 @@
182 enabled: !root.shown && root.available182 enabled: !root.shown && root.available
183 autoCompleteDragThreshold: maxTotalDragDistance / 2183 autoCompleteDragThreshold: maxTotalDragDistance / 2
184 stretch: true184 stretch: true
185 distanceThreshold: enableHint ? 0 : minimizedPanelHeight
186185
187 onTapped: showTapped(Qt.point(touchSceneX, touchSceneY));186 onDraggingChanged: {
187 if (dragging) {
188 touchPressTime = new Date().getTime();
189 } else {
190 var touchReleaseTime = new Date().getTime();
191 if (touchReleaseTime - touchPressTime <= 300) {
192 root.showTapped(Qt.point(touchSceneX, touchSceneY));
193 }
194 }
195 }
196 property var touchPressTime
188197
189 // using hint regulates minimum to hint displacement, but in fullscreen mode, we need to do it manually.198 // using hint regulates minimum to hint displacement, but in fullscreen mode, we need to do it manually.
190 overrideStartValue: enableHint ? minimizedPanelHeight : expandedPanelHeight + handle.height199 overrideStartValue: enableHint ? minimizedPanelHeight : expandedPanelHeight + handle.height
@@ -200,6 +209,7 @@
200209
201 DragHandle {210 DragHandle {
202 id: __hideDragHandle211 id: __hideDragHandle
212 objectName: "hideDragHandle"
203 anchors.fill: handle213 anchors.fill: handle
204 direction: Direction.Upwards214 direction: Direction.Upwards
205 enabled: root.shown && root.available215 enabled: root.shown && root.available
206216
=== modified file 'qml/Stages/PhoneStage.qml'
--- qml/Stages/PhoneStage.qml 2015-02-18 18:29:03 +0000
+++ qml/Stages/PhoneStage.qml 2015-04-10 21:17:56 +0000
@@ -146,14 +146,13 @@
146 id: spreadView146 id: spreadView
147 objectName: "spreadView"147 objectName: "spreadView"
148 anchors.fill: parent148 anchors.fill: parent
149 interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1)149 interactive: (spreadDragArea.dragging || phase > 1) && draggedDelegateCount === 0
150 && draggedDelegateCount === 0
151 contentWidth: spreadRow.width - shift150 contentWidth: spreadRow.width - shift
152 contentX: -shift151 contentX: -shift
153152
154 // This indicates when the spreadView is active. That means, all the animations153 // This indicates when the spreadView is active. That means, all the animations
155 // are activated and tiles need to line up for the spread.154 // are activated and tiles need to line up for the spread.
156 readonly property bool active: shiftedContentX > 0 || spreadDragArea.status === DirectionalDragArea.Recognized || !root.focusFirstApp155 readonly property bool active: shiftedContentX > 0 || spreadDragArea.dragging || !root.focusFirstApp
157156
158 // The flickable needs to fill the screen in order to get touch events all over.157 // The flickable needs to fill the screen in order to get touch events all over.
159 // However, we don't want to the user to be able to scroll back all the way. For158 // However, we don't want to the user to be able to scroll back all the way. For
@@ -450,7 +449,7 @@
450 }449 }
451 }450 }
452451
453 EdgeDragArea {452 DirectionalDragArea {
454 id: spreadDragArea453 id: spreadDragArea
455 objectName: "spreadDragArea"454 objectName: "spreadDragArea"
456 direction: Direction.Leftwards455 direction: Direction.Leftwards
@@ -462,59 +461,50 @@
462 property var gesturePoints: new Array()461 property var gesturePoints: new Array()
463462
464 onTouchXChanged: {463 onTouchXChanged: {
465 if (!dragging) {464 if (dragging) {
466 // Initial touch. Let's reset the spreadView to the starting position.
467 spreadView.phase = 0;
468 spreadView.contentX = -spreadView.shift;
469 }
470 if (dragging && status == DirectionalDragArea.Recognized) {
471 // Gesture recognized. Let's move the spreadView with the finger465 // Gesture recognized. Let's move the spreadView with the finger
472 var dragX = Math.min(touchX + width, width); // Prevent dragging rightwards466 var dragX = Math.min(touchX + width, width); // Prevent dragging rightwards
473 dragX = -dragX + spreadDragArea.width - spreadView.shift;467 dragX = -dragX + spreadDragArea.width - spreadView.shift;
474 // Don't allow dragging further than the animation crossing with phase2's animation468 // Don't allow dragging further than the animation crossing with phase2's animation
475 var maxMovement = spreadView.width * spreadView.positionMarker4 - spreadView.shift;469 var maxMovement = spreadView.width * spreadView.positionMarker4 - spreadView.shift;
470
476 spreadView.contentX = Math.min(dragX, maxMovement);471 spreadView.contentX = Math.min(dragX, maxMovement);
472 } else {
473 // Initial touch. Let's reset the spreadView to the starting position.
474 spreadView.phase = 0;
475 spreadView.contentX = -spreadView.shift;
477 }476 }
477
478 gesturePoints.push(touchX);478 gesturePoints.push(touchX);
479 }479 }
480480
481 property int previousStatus: -1
482 property int currentStatus: DirectionalDragArea.WaitingForTouch
483
484 onStatusChanged: {
485 previousStatus = currentStatus;
486 currentStatus = status;
487 }
488
489 onDraggingChanged: {481 onDraggingChanged: {
490 if (dragging) {482 if (dragging) {
491 // A potential edge-drag gesture has started. Start recording it483 // A potential edge-drag gesture has started. Start recording it
492 gesturePoints = [];484 gesturePoints = [];
493 return;485 } else {
494 }486 // Ok. The user released. Find out if it was a one-way movement.
495487 var oneWayFlick = true;
496 // Ok. The user released. Find out if it was a one-way movement.488 var smallestX = spreadDragArea.width;
497 var oneWayFlick = true;489 for (var i = 0; i < gesturePoints.length; i++) {
498 var smallestX = spreadDragArea.width;490 if (gesturePoints[i] >= smallestX) {
499 for (var i = 0; i < gesturePoints.length; i++) {491 oneWayFlick = false;
500 if (gesturePoints[i] >= smallestX) {492 break;
501 oneWayFlick = false;493 }
502 break;494 smallestX = gesturePoints[i];
503 }495 }
504 smallestX = gesturePoints[i];496 gesturePoints = [];
505 }497
506 gesturePoints = [];498 if (oneWayFlick && spreadView.shiftedContentX > units.gu(2) &&
507499 spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
508 if (previousStatus == DirectionalDragArea.Recognized &&500 // If it was a short one-way movement, do the Alt+Tab switch
509 oneWayFlick && spreadView.shiftedContentX > units.gu(2) &&501 // no matter if we didn't cross positionMarker1 yet.
510 spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {502 spreadView.snapTo(1);
511 // If it was a short one-way movement, do the Alt+Tab switch503 } else if (!dragging) {
512 // no matter if we didn't cross positionMarker1 yet.504 // otherwise snap to the closest snap position we can find
513 spreadView.snapTo(1);505 // (might be back to start, to app 1 or to spread)
514 } else if (!dragging) {506 spreadView.snap();
515 // otherwise snap to the closest snap position we can find507 }
516 // (might be back to start, to app 1 or to spread)
517 spreadView.snap();
518 }508 }
519 }509 }
520 }510 }
521511
=== modified file 'qml/Stages/TabletStage.qml'
--- qml/Stages/TabletStage.qml 2015-02-02 16:28:03 +0000
+++ qml/Stages/TabletStage.qml 2015-04-10 21:17:56 +0000
@@ -170,8 +170,7 @@
170 Flickable {170 Flickable {
171 id: spreadView171 id: spreadView
172 anchors.fill: parent172 anchors.fill: parent
173 interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1)173 interactive: (spreadDragArea.dragging || phase > 1) && draggedDelegateCount === 0
174 && draggedDelegateCount === 0
175 contentWidth: spreadRow.width - shift174 contentWidth: spreadRow.width - shift
176 contentX: -shift175 contentX: -shift
177176
@@ -598,7 +597,7 @@
598 }597 }
599 }598 }
600599
601 EdgeDragArea {600 DirectionalDragArea {
602 id: spreadDragArea601 id: spreadDragArea
603 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }602 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
604 width: root.dragAreaWidth603 width: root.dragAreaWidth
@@ -624,26 +623,25 @@
624 if (dragging) {623 if (dragging) {
625 // Gesture recognized. Start recording this gesture624 // Gesture recognized. Start recording this gesture
626 gesturePoints = [];625 gesturePoints = [];
627 return;626 } else {
628 }627 // Ok. The user released. Find out if it was a one-way movement.
629628 var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints);
630 // Ok. The user released. Find out if it was a one-way movement.629 gesturePoints = [];
631 var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints);630
632 gesturePoints = [];631 if (oneWayFlick && spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
633632 // If it was a short one-way movement, do the Alt+Tab switch
634 if (oneWayFlick && spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {633 // no matter if we didn't cross positionMarker1 yet.
635 // If it was a short one-way movement, do the Alt+Tab switch
636 // no matter if we didn't cross positionMarker1 yet.
637 spreadView.snapTo(spreadView.nextInStack);
638 } else if (!dragging) {
639 if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker1) {
640 spreadView.snap();
641 } else if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2) {
642 spreadView.snapTo(spreadView.nextInStack);634 spreadView.snapTo(spreadView.nextInStack);
643 } else {635 } else {
644 // otherwise snap to the closest snap position we can find636 if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker1) {
645 // (might be back to start, to app 1 or to spread)637 spreadView.snap();
646 spreadView.snap();638 } else if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2) {
639 spreadView.snapTo(spreadView.nextInStack);
640 } else {
641 // otherwise snap to the closest snap position we can find
642 // (might be back to start, to app 1 or to spread)
643 spreadView.snap();
644 }
647 }645 }
648 }646 }
649 }647 }
650648
=== modified file 'qml/Tutorial/TutorialBottom.qml'
--- qml/Tutorial/TutorialBottom.qml 2015-02-01 22:39:25 +0000
+++ qml/Tutorial/TutorialBottom.qml 2015-04-10 21:17:56 +0000
@@ -73,7 +73,7 @@
73 ]73 ]
74 }74 }
7575
76 EdgeDragArea {76 DirectionalDragArea {
77 id: dragArea77 id: dragArea
78 direction: Direction.Upwards78 direction: Direction.Upwards
79 anchors {79 anchors {
8080
=== modified file 'tests/libs/UbuntuGestures/tst_TouchRegistry.cpp'
--- tests/libs/UbuntuGestures/tst_TouchRegistry.cpp 2014-10-01 13:20:32 +0000
+++ tests/libs/UbuntuGestures/tst_TouchRegistry.cpp 2015-04-10 21:17:56 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2014 Canonical, Ltd.2 * Copyright (C) 2014-2015 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * 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 by5 * it under the terms of the GNU General Public License as published by
@@ -66,6 +66,7 @@
66 void candidatesAndWatchers_2();66 void candidatesAndWatchers_2();
67 void rejectingTouchfterItsEnd();67 void rejectingTouchfterItsEnd();
68 void removeOldUndecidedCandidates();68 void removeOldUndecidedCandidates();
69 void interimOwnerWontGetUnownedTouchEvents();
69};70};
7071
71void tst_TouchRegistry::requestWithNoCandidates()72void tst_TouchRegistry::requestWithNoCandidates()
@@ -742,7 +743,7 @@
742743
743 // Simulate that enough time has passed to cause the CandidateInactivityTimer to timeout,744 // Simulate that enough time has passed to cause the CandidateInactivityTimer to timeout,
744 // making TouchRegistry consider that undecidedCantidate defaulted.745 // making TouchRegistry consider that undecidedCantidate defaulted.
745 fakeTimerFactory->makeRunningTimersTimeout();746 fakeTimerFactory->updateTime(10000);
746747
747 QVERIFY(undecidedCandidate.ownedTouches.isEmpty());748 QVERIFY(undecidedCandidate.ownedTouches.isEmpty());
748 QVERIFY(undecidedCandidate.lostTouches.contains(0));749 QVERIFY(undecidedCandidate.lostTouches.contains(0));
@@ -752,6 +753,71 @@
752 delete touchRegistry;753 delete touchRegistry;
753}754}
754755
756/*
757 An item that calls requestTouchOwnership() without first having called addCandidateOwnerForTouch()
758 is assumed to be the interim owner of the touch point, thus there's no point in sending
759 UnownedTouchEvents to him as he already gets proper QTouchEvents from QQuickWindow because
760 he didn't ignore the first QTouchEvent with that touch point.
761 */
762void tst_TouchRegistry::interimOwnerWontGetUnownedTouchEvents()
763{
764 FakeTimerFactory *fakeTimerFactory = new FakeTimerFactory;
765 TouchRegistry *touchRegistry = new TouchRegistry(nullptr, fakeTimerFactory);
766
767 DummyCandidate undecidedCandidate;
768 undecidedCandidate.setObjectName("undecided");
769
770 DummyCandidate interimOwner;
771 interimOwner.setObjectName("interimOwner");
772
773 {
774 QList<QTouchEvent::TouchPoint> touchPoints;
775 touchPoints.append(QTouchEvent::TouchPoint(0));
776 touchPoints[0].setState(Qt::TouchPointPressed);
777 QTouchEvent touchEvent(QEvent::TouchBegin,
778 0 /* device */,
779 Qt::NoModifier,
780 Qt::TouchPointPressed,
781 touchPoints);
782 touchRegistry->update(&touchEvent);
783 }
784
785 touchRegistry->addCandidateOwnerForTouch(0, &undecidedCandidate);
786 touchRegistry->requestTouchOwnership(0, &interimOwner);
787
788 {
789 QList<QTouchEvent::TouchPoint> touchPoints;
790 touchPoints.append(QTouchEvent::TouchPoint(0));
791 touchPoints[0].setState(Qt::TouchPointMoved);
792 QTouchEvent touchEvent(QEvent::TouchUpdate,
793 0 /* device */,
794 Qt::NoModifier,
795 Qt::TouchPointMoved,
796 touchPoints);
797 touchRegistry->update(&touchEvent);
798 }
799
800 QCOMPARE(undecidedCandidate.unownedTouchEvents.count(), 1);
801 QCOMPARE(interimOwner.unownedTouchEvents.count(), 0);
802
803 {
804 QList<QTouchEvent::TouchPoint> touchPoints;
805 touchPoints.append(QTouchEvent::TouchPoint(0));
806 touchPoints[0].setState(Qt::TouchPointMoved);
807 QTouchEvent touchEvent(QEvent::TouchUpdate,
808 0 /* device */,
809 Qt::NoModifier,
810 Qt::TouchPointMoved,
811 touchPoints);
812 touchRegistry->update(&touchEvent);
813 }
814
815 QCOMPARE(undecidedCandidate.unownedTouchEvents.count(), 2);
816 QCOMPARE(interimOwner.unownedTouchEvents.count(), 0);
817
818 delete touchRegistry;
819}
820
755////////////// TouchMemento //////////821////////////// TouchMemento //////////
756822
757TouchMemento::TouchMemento(const QTouchEvent *touchEvent)823TouchMemento::TouchMemento(const QTouchEvent *touchEvent)
758824
=== modified file 'tests/plugins/Ubuntu/Gestures/CMakeLists.txt'
--- tests/plugins/Ubuntu/Gestures/CMakeLists.txt 2014-10-17 11:01:53 +0000
+++ tests/plugins/Ubuntu/Gestures/CMakeLists.txt 2015-04-10 21:17:56 +0000
@@ -19,6 +19,7 @@
19)19)
2020
21add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")21add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
22add_definitions(-DTESTS_UTILS_MODULES_DIR="${CMAKE_BINARY_DIR}/tests/utils/modules")
2223
23macro(add_gesture_ui_test CLASSNAME)24macro(add_gesture_ui_test CLASSNAME)
24 add_executable(${CLASSNAME}TestExec tst_${CLASSNAME}.cpp GestureTest.cpp)25 add_executable(${CLASSNAME}TestExec tst_${CLASSNAME}.cpp GestureTest.cpp)
@@ -26,7 +27,8 @@
26 target_link_libraries(${CLASSNAME}TestExec UbuntuGesturesQml UbuntuGestures)27 target_link_libraries(${CLASSNAME}TestExec UbuntuGesturesQml UbuntuGestures)
2728
28 add_binary_qml_test(${CLASSNAME} ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures UbuntuGesturesTestQmlFiles "")29 add_binary_qml_test(${CLASSNAME} ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures UbuntuGesturesTestQmlFiles "")
29 add_manual_qml_test(. ${CLASSNAME} IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)30 add_manual_qml_test(. ${CLASSNAME} IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins
31 ${CMAKE_BINARY_DIR}/tests/utils/modules)
30endmacro(add_gesture_ui_test)32endmacro(add_gesture_ui_test)
3133
32macro(add_gesture_test CLASSNAME)34macro(add_gesture_test CLASSNAME)
3335
=== modified file 'tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml'
--- tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml 2014-02-11 20:21:24 +0000
+++ tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml 2015-04-10 21:17:56 +0000
@@ -37,8 +37,8 @@
3737
38 Rectangle {38 Rectangle {
39 id: dragAreaRect39 id: dragAreaRect
40 color: "yellow"40 opacity: dragArea.dragging ? 0.5 : 0.0
41 opacity: 0.041 color: "green"
42 anchors.fill: dragArea42 anchors.fill: dragArea
43 }43 }
4444
@@ -49,24 +49,10 @@
49 height: units.gu(5)49 height: units.gu(5)
5050
51 direction: Direction.Downwards51 direction: Direction.Downwards
52 maxDeviation: units.gu(2)
53 wideningAngle: 10
54 distanceThreshold: units.gu(4)
5552
56 onStatusChanged: {53 onDraggingChanged: {
57 switch (status) {54 if (dragging) {
58 case DirectionalDragArea.WaitingForTouch:55 launcher.y = Qt.binding(launcher.followDragArea)
59 dragAreaRect.opacity = 0.0
60 break;
61 case DirectionalDragArea.Undecided:
62 dragAreaRect.color = "yellow"
63 dragAreaRect.opacity = 0.3
64 launcher.y = Qt.binding(launcher.followDragArea)
65 break;
66 default: // DirectionalDragArea.Recognized:
67 dragAreaRect.color = "green"
68 dragAreaRect.opacity = 0.5
69 break;
70 }56 }
71 }57 }
7258
7359
=== modified file 'tests/plugins/Ubuntu/Gestures/GestureTest.cpp'
--- tests/plugins/Ubuntu/Gestures/GestureTest.cpp 2014-11-03 15:21:46 +0000
+++ tests/plugins/Ubuntu/Gestures/GestureTest.cpp 2015-04-10 21:17:56 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013 Canonical, Ltd.2 * Copyright (C) 2013,2015 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * 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 by5 * it under the terms of the GNU General Public License as published by
@@ -21,8 +21,11 @@
21#include <QQuickView>21#include <QQuickView>
22#include <QtTest>22#include <QtTest>
2323
24#include <Timer.h>
24#include <TouchRegistry.h>25#include <TouchRegistry.h>
2526
27using namespace UbuntuGestures;
28
26GestureTest::GestureTest(const QString &qmlFilename)29GestureTest::GestureTest(const QString &qmlFilename)
27 : QObject(), m_device(nullptr), m_view(nullptr), m_qmlFilename(qmlFilename)30 : QObject(), m_device(nullptr), m_view(nullptr), m_qmlFilename(qmlFilename)
28{31{
@@ -40,14 +43,17 @@
40void GestureTest::init()43void GestureTest::init()
41{44{
42 m_view = new QQuickView;45 m_view = new QQuickView;
43 m_view->setResizeMode(QQuickView::SizeViewToRootObject);46 m_view->setResizeMode(QQuickView::SizeRootObjectToView);
44 m_view->engine()->addImportPath(QStringLiteral(UBUNTU_GESTURES_PLUGIN_DIR));47 m_view->engine()->addImportPath(QStringLiteral(UBUNTU_GESTURES_PLUGIN_DIR));
48 m_view->engine()->addImportPath(QStringLiteral(TESTS_UTILS_MODULES_DIR));
45 m_view->setSource(QUrl::fromLocalFile(m_qmlFilename));49 m_view->setSource(QUrl::fromLocalFile(m_qmlFilename));
46 m_view->show();50 m_view->show();
47 QVERIFY(QTest::qWaitForWindowExposed(m_view));51 QVERIFY(QTest::qWaitForWindowExposed(m_view));
48 QVERIFY(m_view->rootObject() != 0);52 QVERIFY(m_view->rootObject() != 0);
4953
50 m_touchRegistry = new TouchRegistry;54 m_fakeTimerFactory = new FakeTimerFactory;
55
56 m_touchRegistry = new TouchRegistry(nullptr, m_fakeTimerFactory);
51 m_view->installEventFilter(m_touchRegistry);57 m_view->installEventFilter(m_touchRegistry);
5258
53 qApp->processEvents();59 qApp->processEvents();
@@ -59,6 +65,10 @@
59 delete m_touchRegistry;65 delete m_touchRegistry;
60 m_touchRegistry = nullptr;66 m_touchRegistry = nullptr;
6167
68 // TouchRegistry will take down the timer factory along with him
69 // delete m_fakeTimerFactory;
70 m_fakeTimerFactory = nullptr;
71
62 delete m_view;72 delete m_view;
63 m_view = nullptr;73 m_view = nullptr;
64}74}
6575
=== modified file 'tests/plugins/Ubuntu/Gestures/GestureTest.h'
--- tests/plugins/Ubuntu/Gestures/GestureTest.h 2014-11-03 15:21:46 +0000
+++ tests/plugins/Ubuntu/Gestures/GestureTest.h 2015-04-10 21:17:56 +0000
@@ -23,6 +23,9 @@
23class QQuickView;23class QQuickView;
24class QTouchDevice;24class QTouchDevice;
2525
26namespace UbuntuGestures {
27 class FakeTimerFactory;
28}
26class TouchRegistry;29class TouchRegistry;
2730
28// C++ std lib31// C++ std lib
@@ -81,6 +84,7 @@
81 QTouchDevice *m_device;84 QTouchDevice *m_device;
82 QQuickView *m_view;85 QQuickView *m_view;
83 TouchRegistry *m_touchRegistry;86 TouchRegistry *m_touchRegistry;
87 UbuntuGestures::FakeTimerFactory *m_fakeTimerFactory;
84 QString m_qmlFilename;88 QString m_qmlFilename;
85};89};
8690
8791
=== modified file 'tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml'
--- tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml 2014-02-11 20:21:24 +0000
+++ tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml 2015-04-10 21:17:56 +0000
@@ -21,7 +21,7 @@
21Item {21Item {
22 id: root22 id: root
2323
24 function reset() { launcher.x = root.width }24 function reset() { launcher.x = Qt.binding(function(){return root.width;}); }
2525
26 Rectangle {26 Rectangle {
27 id: launcher27 id: launcher
@@ -41,7 +41,8 @@
4141
42 Rectangle {42 Rectangle {
43 id: dragAreaRect43 id: dragAreaRect
44 opacity: 0.044 opacity: dragArea.dragging ? 0.5 : 0.0
45 color: "green"
45 anchors.fill: dragArea46 anchors.fill: dragArea
46 }47 }
4748
@@ -52,24 +53,10 @@
52 width: units.gu(5)53 width: units.gu(5)
5354
54 direction: Direction.Leftwards55 direction: Direction.Leftwards
55 maxDeviation: units.gu(2)
56 wideningAngle: 10
57 distanceThreshold: units.gu(4)
5856
59 onStatusChanged: {57 onDraggingChanged: {
60 switch (status) {58 if (dragging) {
61 case DirectionalDragArea.WaitingForTouch:59 launcher.x = Qt.binding(launcher.followDragArea)
62 dragAreaRect.opacity = 0.0
63 break;
64 case DirectionalDragArea.Undecided:
65 dragAreaRect.color = "yellow"
66 dragAreaRect.opacity = 0.3
67 launcher.x = Qt.binding(launcher.followDragArea)
68 break;
69 default: //case DirectionalDragArea.Recognized:
70 dragAreaRect.color = "green"
71 dragAreaRect.opacity = 0.5
72 break;
73 }60 }
74 }61 }
7562
7663
=== modified file 'tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml'
--- tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml 2014-10-01 13:20:32 +0000
+++ tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml 2015-04-10 21:17:56 +0000
@@ -38,7 +38,8 @@
3838
39 Rectangle {39 Rectangle {
40 id: dragAreaRect40 id: dragAreaRect
41 opacity: 0.041 opacity: dragArea.dragging ? 0.5 : 0.0
42 color: "green"
42 anchors.fill: dragArea43 anchors.fill: dragArea
43 }44 }
4445
@@ -52,25 +53,10 @@
52 width: units.gu(5)53 width: units.gu(5)
5354
54 direction: Direction.Rightwards55 direction: Direction.Rightwards
55 maxDeviation: units.gu(2)
56 wideningAngle: 10
57 distanceThreshold: units.gu(4)
58 minSpeed: 50
5956
60 onStatusChanged: {57 onDraggingChanged: {
61 switch (status) {58 if (dragging) {
62 case DirectionalDragArea.WaitingForTouch:59 launcher.x = Qt.binding(launcher.followDragArea)
63 dragAreaRect.opacity = 0.0
64 break;
65 case DirectionalDragArea.Undecided:
66 dragAreaRect.color = "yellow"
67 dragAreaRect.opacity = 0.3
68 launcher.x = Qt.binding(launcher.followDragArea)
69 break;
70 default: //case DirectionalDragArea.Recognized:
71 dragAreaRect.color = "green"
72 dragAreaRect.opacity = 0.5
73 break;
74 }60 }
75 }61 }
7662
7763
=== modified file 'tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml'
--- tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml 2014-02-11 20:21:24 +0000
+++ tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml 2015-04-10 21:17:56 +0000
@@ -21,7 +21,7 @@
21Item {21Item {
22 id: root22 id: root
2323
24 function reset() { launcher.y = root.height }24 function reset() { launcher.y = Qt.binding(function(){return root.height;}); }
2525
26 Rectangle {26 Rectangle {
27 id: launcher27 id: launcher
@@ -41,7 +41,8 @@
4141
42 Rectangle {42 Rectangle {
43 id: dragAreaRect43 id: dragAreaRect
44 opacity: 0.044 opacity: dragArea.dragging ? 0.5 : 0.0
45 color: "green"
45 anchors.fill: dragArea46 anchors.fill: dragArea
46 }47 }
4748
@@ -52,24 +53,10 @@
52 height: units.gu(5)53 height: units.gu(5)
5354
54 direction: Direction.Upwards55 direction: Direction.Upwards
55 maxDeviation: units.gu(2)
56 wideningAngle: 10
57 distanceThreshold: units.gu(4)
5856
59 onStatusChanged: {57 onDraggingChanged: {
60 switch (status) {58 if (dragging) {
61 case DirectionalDragArea.WaitingForTouch:59 launcher.y = Qt.binding(launcher.followDragArea)
62 dragAreaRect.opacity = 0.0
63 break;
64 case DirectionalDragArea.Undecided:
65 dragAreaRect.color = "yellow"
66 dragAreaRect.opacity = 0.3
67 launcher.y = Qt.binding(launcher.followDragArea)
68 break;
69 default: //case DirectionalDragArea.Recognized:
70 dragAreaRect.color = "green"
71 dragAreaRect.opacity = 0.5
72 break;
73 }60 }
74 }61 }
7562
7663
=== modified file 'tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp'
--- tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp 2015-02-18 13:51:39 +0000
+++ tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp 2015-04-10 21:17:56 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013-2014 Canonical, Ltd.2 * Copyright (C) 2013-2015 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * 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 by5 * it under the terms of the GNU General Public License as published by
@@ -20,51 +20,38 @@
20#include <QtQml/QQmlEngine>20#include <QtQml/QQmlEngine>
21#include <QPointer>21#include <QPointer>
22#include <private/qquickmousearea_p.h>22#include <private/qquickmousearea_p.h>
23#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))23#include <private/qquickwindow_p.h>
24 #include <private/qquickwindow_p.h>
25#endif
2624
2725
28#include <DirectionalDragArea.h>26#include <DirectionalDragArea.h>
27#include <DirectionalDragArea_p.h>
29#include <TouchRegistry.h>28#include <TouchRegistry.h>
3029
31#include "GestureTest.h"30#include "GestureTest.h"
3231
33using namespace UbuntuGestures;32using namespace UbuntuGestures;
3433
3534// Because QSignalSpy(directionalDragArea, SIGNAL(DirectionalDragArea::Status)) simply
36class ComplexFakeTimer : public FakeTimer35// doesn't work
37{36class StatusSpy : public QObject {
38 Q_OBJECT37 Q_OBJECT
39public:38public:
40 ComplexFakeTimer(const SharedTimeSource &timeSource, QObject *parent = 0)39 StatusSpy(DirectionalDragArea *edgeDragArea) {
41 : FakeTimer(parent),40 m_recognized = false;
42 m_timeSource(timeSource)41 connect(edgeDragArea->d, &DirectionalDragAreaPrivate::statusChanged,
43 {}42 this, &StatusSpy::onStatusChanged);
4443 }
45 void start() override {44 bool recognized() {
46 AbstractTimer::start();45 return m_recognized;
47 m_nextTimeoutTime = m_timeSource->msecsSinceReference() + (qint64)interval();46 }
48 }47
4948private Q_SLOTS:
50 void emitTimeout() {49 void onStatusChanged(DirectionalDragAreaPrivate::Status status) {
51 m_nextTimeoutTime += interval();50 m_recognized |= status == DirectionalDragAreaPrivate::Recognized;
52 Q_EMIT timeout();51 }
53 }
54
55 qint64 nextTimeoutTime() const { return m_nextTimeoutTime; }
5652
57private:53private:
58 SharedTimeSource m_timeSource;54 bool m_recognized;
59 qint64 m_nextTimeoutTime;
60};
61
62class FakeTimeSource : public UbuntuGestures::TimeSource
63{
64public:
65 FakeTimeSource() { m_msecsSinceReference = 0; }
66 virtual qint64 msecsSinceReference() {return m_msecsSinceReference;}
67 qint64 m_msecsSinceReference;
68};55};
6956
70/*57/*
@@ -98,15 +85,9 @@
98 tst_DirectionalDragArea();85 tst_DirectionalDragArea();
99private Q_SLOTS:86private Q_SLOTS:
100 void init(); // called right before each and every test function is executed87 void init(); // called right before each and every test function is executed
101 void cleanup(); // called right after each and every test function is executed
10288
103 void edgeDrag();
104 void edgeDrag_data();
105 void dragWithShortDirectionChange();89 void dragWithShortDirectionChange();
106 void minSpeed();
107 void minSpeed_data();
108 void recognitionTimerUsage();90 void recognitionTimerUsage();
109 void maxSilenceTime();
110 void sceneXAndX();91 void sceneXAndX();
111 void sceneYAndY();92 void sceneYAndY();
112 void twoFingerTap();93 void twoFingerTap();
@@ -122,14 +103,24 @@
122 void immediateRecognitionWhenConstraintsDisabled();103 void immediateRecognitionWhenConstraintsDisabled();
123 void withdrawTouchOwnershipCandidacyIfDisabledDuringRecognition();104 void withdrawTouchOwnershipCandidacyIfDisabledDuringRecognition();
124 void withdrawTouchOwnershipCandidacyIfDisabledDuringRecognition_data();105 void withdrawTouchOwnershipCandidacyIfDisabledDuringRecognition_data();
125 void tappedSignal();
126 void tappedSignal_data();
127 void gettingTouchOwnershipMakesMouseAreaBehindGetCanceled();106 void gettingTouchOwnershipMakesMouseAreaBehindGetCanceled();
107 void interleavedTouches();
108 void makoRightEdgeDrag();
109 void makoRightEdgeDrag_verticalDownwards();
110 void makoLeftEdgeDrag_slowStart();
111 void makoLeftEdgeDrag_movesSlightlyBackwardsOnStart();
128112
129private:113private:
114 // QTest::touchEvent takes QPoint instead of QPointF and I don't want to
115 // lose precision due to rounding.
116 // Besides, those helper functions lead to more compact code.
117 void sendTouchPress(qint64 timestamp, int id, QPointF pos);
118 void sendTouchUpdate(qint64 timestamp, int id, QPointF pos);
119 void sendTouchRelease(qint64 timestamp, int id, QPointF pos);
120 void sendTouch(qint64 timestamp, int id, QPointF pos,
121 Qt::TouchPointState pointState, QEvent::Type eventType);
122
130 void passTime(qint64 timeSpanMs);123 void passTime(qint64 timeSpanMs);
131 ComplexFakeTimer *fakeTimer;
132 QSharedPointer<FakeTimeSource> fakeTimeSource;
133};124};
134125
135tst_DirectionalDragArea::tst_DirectionalDragArea()126tst_DirectionalDragArea::tst_DirectionalDragArea()
@@ -148,36 +139,49 @@
148 m_view->resize(m_view->rootObject()->width(), m_view->rootObject()->height());139 m_view->resize(m_view->rootObject()->width(), m_view->rootObject()->height());
149 QTRY_COMPARE(m_view->width(), (int)m_view->rootObject()->width());140 QTRY_COMPARE(m_view->width(), (int)m_view->rootObject()->width());
150 QTRY_COMPARE(m_view->height(), (int)m_view->rootObject()->height());141 QTRY_COMPARE(m_view->height(), (int)m_view->rootObject()->height());
151142}
152 fakeTimeSource.reset(new FakeTimeSource);143
153 fakeTimer = new ComplexFakeTimer(fakeTimeSource);144void tst_DirectionalDragArea::sendTouchPress(qint64 timestamp, int id, QPointF pos)
154}145{
155146 sendTouch(timestamp, id, pos, Qt::TouchPointPressed, QEvent::TouchBegin);
156void tst_DirectionalDragArea::cleanup()147}
157{148
158 delete fakeTimer;149void tst_DirectionalDragArea::sendTouchUpdate(qint64 timestamp, int id, QPointF pos)
159 fakeTimer = 0;150{
160151 sendTouch(timestamp, id, pos, Qt::TouchPointMoved, QEvent::TouchUpdate);
161 fakeTimeSource.reset();152}
162153
163 GestureTest::cleanup();154void tst_DirectionalDragArea::sendTouchRelease(qint64 timestamp, int id, QPointF pos)
155{
156 sendTouch(timestamp, id, pos, Qt::TouchPointReleased, QEvent::TouchEnd);
157}
158
159void tst_DirectionalDragArea::sendTouch(qint64 timestamp, int id, QPointF pos,
160 Qt::TouchPointState pointState, QEvent::Type eventType)
161{
162 m_fakeTimerFactory->updateTime(timestamp);
163
164 QTouchEvent::TouchPoint point;
165
166 point.setState(pointState);
167 point.setId(id);
168 point.setScenePos(pos);
169 point.setPos(pos);
170
171 QList<QTouchEvent::TouchPoint> points;
172 points << point;
173
174 QTouchEvent touchEvent(eventType, m_device, Qt::NoModifier, Qt::TouchPointPressed, points);
175 QCoreApplication::sendEvent(m_view, &touchEvent);
176
177 QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(m_view);
178 windowPrivate->flushDelayedTouchEvent();
164}179}
165180
166void tst_DirectionalDragArea::passTime(qint64 timeSpanMs)181void tst_DirectionalDragArea::passTime(qint64 timeSpanMs)
167{182{
168 qint64 finalTime = fakeTimeSource->m_msecsSinceReference + timeSpanMs;183 qint64 finalTime = m_fakeTimerFactory->timeSource()->msecsSinceReference() + timeSpanMs;
169184 m_fakeTimerFactory->updateTime(finalTime);
170 if (fakeTimer->isRunning() && finalTime >= fakeTimer->nextTimeoutTime()) {
171 fakeTimeSource->m_msecsSinceReference = fakeTimer->nextTimeoutTime();
172 fakeTimer->emitTimeout();
173
174 qint64 timeSpanRemainder = finalTime - fakeTimeSource->m_msecsSinceReference;
175 if (timeSpanRemainder > 0) {
176 passTime(timeSpanRemainder);
177 }
178 } else {
179 fakeTimeSource->m_msecsSinceReference = finalTime;
180 }
181}185}
182186
183namespace {187namespace {
@@ -186,147 +190,6 @@
186 QPointF localCenter(edgeDragArea->width() / 2., edgeDragArea->height() / 2.);190 QPointF localCenter(edgeDragArea->width() / 2., edgeDragArea->height() / 2.);
187 return edgeDragArea->mapToScene(localCenter);191 return edgeDragArea->mapToScene(localCenter);
188}192}
189
190QPointF calculateDirectionVector(DirectionalDragArea *edgeDragArea,
191 qreal wideningAngleMultiplier)
192{
193 qreal angleRadians = edgeDragArea->wideningAngle() * wideningAngleMultiplier
194 * M_PI / 180.0;
195
196 qreal angleCos = qCos(angleRadians);
197 qreal angleSin = qSin(angleRadians);
198
199 switch (edgeDragArea->direction()) {
200 case Direction::Upwards:
201 return QPointF(angleSin, -angleCos);
202 case Direction::Downwards:
203 return QPointF(angleSin, angleCos);
204 case Direction::Leftwards:
205 return QPointF(-angleCos, angleSin);
206 default: // Direction::Rightwards:
207 return QPointF(angleCos, angleSin);
208 }
209}
210
211QPointF createTouchDeviation(DirectionalDragArea *edgeDragArea)
212{
213 qreal deviation = edgeDragArea->maxDeviation() * 0.8;
214
215 if (Direction::isHorizontal(edgeDragArea->direction())) {
216 return QPointF(0, deviation);
217 } else {
218 return QPointF(deviation, 0);
219 }
220}
221}
222
223void tst_DirectionalDragArea::edgeDrag()
224{
225 QFETCH(QString, dragAreaObjectName);
226 QFETCH(qreal, wideningAngleMultiplier);
227 QFETCH(qreal, dragDistanceFactor);
228 QFETCH(bool, expectGestureRecognition);
229
230 DirectionalDragArea *edgeDragArea =
231 m_view->rootObject()->findChild<DirectionalDragArea*>(dragAreaObjectName);
232 QVERIFY(edgeDragArea != 0);
233 edgeDragArea->setRecognitionTimer(fakeTimer);
234 edgeDragArea->setTimeSource(fakeTimeSource);
235
236 QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool)));
237
238 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
239 QPointF touchPoint = initialTouchPos;
240
241 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*dragDistanceFactor;
242 QPointF dragDirectionVector = calculateDirectionVector(edgeDragArea,
243 wideningAngleMultiplier);
244 qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
245 QPointF touchMovement = dragDirectionVector * movementStepDistance;
246 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
247 int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
248
249 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
250
251 QCOMPARE(draggingSpy.count(), 1);
252 QCOMPARE(edgeDragArea->dragging(), true);
253
254 if (wideningAngleMultiplier > 0) {
255 // go close to the border of the valid area for this touch point
256 // in order to make it easier to leave it by dragging at an angle
257 // slightly bigger than the widening angle
258 touchPoint += createTouchDeviation(edgeDragArea);
259 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
260 }
261
262 for (int i = 0; i < totalMovementSteps; ++i) {
263 touchPoint += touchMovement;
264 passTime(movementTimeStepMs);
265 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
266 }
267
268 if (expectGestureRecognition)
269 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
270
271 if (edgeDragArea->status() == DirectionalDragArea::WaitingForTouch) {
272 QCOMPARE(edgeDragArea->dragging(), false);
273 QCOMPARE(draggingSpy.count(), 2);
274 }
275
276 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
277
278 QCOMPARE(draggingSpy.count(), 2);
279 QCOMPARE(edgeDragArea->dragging(), false);
280}
281
282void tst_DirectionalDragArea::edgeDrag_data()
283{
284 QTest::addColumn<QString>("dragAreaObjectName");
285 QTest::addColumn<qreal>("wideningAngleMultiplier");
286 QTest::addColumn<qreal>("dragDistanceFactor");
287 QTest::addColumn<bool>("expectGestureRecognition");
288
289 QTest::newRow("rightwards, tiny drag")
290 << "hpDragArea" << 0.0 << 0.2 << false;
291
292 QTest::newRow("rightwards, straight drag")
293 << "hpDragArea" << 0.0 << 3.0 << true;
294
295 QTest::newRow("rightwards, diagonal drag")
296 << "hpDragArea" << 0.9 << 3.0 << true;
297
298 QTest::newRow("rightwards, overly diagonal drag")
299 << "hpDragArea" << 2.0 << 3.0 << false;
300
301 QTest::newRow("leftwards, tiny drag")
302 << "hnDragArea" << 0.0 << 0.2 << false;
303
304 QTest::newRow("leftwards, straight drag")
305 << "hnDragArea" << 0.0 << 3.0 << true;
306
307 QTest::newRow("leftwards, diagonal drag")
308 << "hnDragArea" << 0.9 << 3.0 << true;
309
310 QTest::newRow("downwards, tiny drag")
311 << "vpDragArea" << 0.0 << 0.2 << false;
312
313 QTest::newRow("downwards, straight drag")
314 << "vpDragArea" << 0.0 << 3.0 << true;
315
316 QTest::newRow("downwards, diagonal drag")
317 << "vpDragArea" << 0.9 << 3.0 << true;
318
319 QTest::newRow("upwards, tiny drag")
320 << "vnDragArea" << 0.0 << 0.2 << false;
321
322 QTest::newRow("upwards, straight drag")
323 << "vnDragArea" << 0.0 << 3.0 << true;
324
325 QTest::newRow("upwards, diagonal drag")
326 << "vnDragArea" << 0.9 << 3.0 << true;
327
328 QTest::newRow("upwards, overly diagonal drag")
329 << "vnDragArea" << 2.0 << 3.0 << false;
330}193}
331194
332/*195/*
@@ -339,17 +202,17 @@
339 DirectionalDragArea *edgeDragArea =202 DirectionalDragArea *edgeDragArea =
340 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");203 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
341 QVERIFY(edgeDragArea != 0);204 QVERIFY(edgeDragArea != 0);
342 edgeDragArea->setRecognitionTimer(fakeTimer);205 edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
343 edgeDragArea->setTimeSource(fakeTimeSource);206 edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
344207
345 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);208 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
346 QPointF touchPoint = initialTouchPos;209 QPointF touchPoint = initialTouchPos;
347210
348 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0;211 qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0;
349 QPointF dragDirectionVector(1.0, 0.0);212 QPointF dragDirectionVector(1.0, 0.0);
350 qreal touchStepDistance = edgeDragArea->distanceThreshold() * 0.1f;213 qreal touchStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
351 // make sure we are above minimum speed214 // make sure we are above maximum time
352 int touchStepTimeMs = (touchStepDistance / (edgeDragArea->minSpeed() * 5.0f)) * 1000.0f;215 int touchStepTimeMs = edgeDragArea->d->maxTime / 20. ;
353 QPointF touchMovement = dragDirectionVector * touchStepDistance;216 QPointF touchMovement = dragDirectionVector * touchStepDistance;
354217
355 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());218 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
@@ -373,66 +236,14 @@
373 passTime(touchStepTimeMs);236 passTime(touchStepTimeMs);
374 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());237 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
375 } while ((touchPoint - initialTouchPos).manhattanLength() < desiredDragDistance238 } while ((touchPoint - initialTouchPos).manhattanLength() < desiredDragDistance
376 || fakeTimeSource->m_msecsSinceReference < (edgeDragArea->compositionTime() * 1.5f));239 || m_fakeTimerFactory->timeSource()->msecsSinceReference() < (edgeDragArea->d->compositionTime * 1.5f));
377240
378 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);241 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
379242
380 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());243 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
381}244}
382245
383/*246/*
384 Checks that a gesture will be rejected if it's slower than minSpeed while
385 status is Undecided.
386 */
387void tst_DirectionalDragArea::minSpeed()
388{
389 QFETCH(qreal, minSpeed);
390 QFETCH(qreal, speed);
391 QFETCH(int, expectedStatusAfterSpeedCheck);
392
393 DirectionalDragArea *edgeDragArea =
394 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
395 QVERIFY(edgeDragArea != 0);
396 edgeDragArea->setRecognitionTimer(fakeTimer);
397 edgeDragArea->setTimeSource(fakeTimeSource);
398
399 // A really long, unattainable, number. We don't want it getting recognized before
400 // the speed checks we want have been performed
401 edgeDragArea->setDistanceThreshold(500000);
402
403 edgeDragArea->setMinSpeed(minSpeed);
404
405 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
406 QPointF touchPoint = initialTouchPos;
407
408 QPointF dragDirectionVector(1.0, 0.0);
409 qint64 timeStepMsecs = 10;
410 qreal distanceStep = (speed / 1000.0f) * timeStepMsecs;
411 QPointF touchMovement = dragDirectionVector * distanceStep;
412
413 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
414
415 // Move for a while to ensure our speed check is performed a couple of times
416 for (int i=0; i < 20; ++i) {
417 touchPoint += touchMovement;
418 passTime(timeStepMsecs);
419 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
420 }
421
422 QCOMPARE((int)edgeDragArea->status(), expectedStatusAfterSpeedCheck);
423}
424
425void tst_DirectionalDragArea::minSpeed_data()
426{
427 QTest::addColumn<qreal>("minSpeed");
428 QTest::addColumn<qreal>("speed");
429 QTest::addColumn<int>("expectedStatusAfterSpeedCheck");
430
431 QTest::newRow("slower than minSpeed") << 100.0 << 50.0 << (int)DirectionalDragArea::WaitingForTouch;
432 QTest::newRow("faster than minSpeed") << 100.0 << 150.0 << (int)DirectionalDragArea::Undecided;
433}
434
435/*
436 Checks that the recognition timer is started and stopped appropriately.247 Checks that the recognition timer is started and stopped appropriately.
437 I.e., check that it's running only while gesture recognition is taking place248 I.e., check that it's running only while gesture recognition is taking place
438 (status == Undecided)249 (status == Undecided)
@@ -442,11 +253,9 @@
442 DirectionalDragArea *edgeDragArea =253 DirectionalDragArea *edgeDragArea =
443 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");254 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
444 QVERIFY(edgeDragArea != 0);255 QVERIFY(edgeDragArea != 0);
445 edgeDragArea->setRecognitionTimer(fakeTimer);256 AbstractTimer *fakeTimer = m_fakeTimerFactory->createTimer();
446 edgeDragArea->setTimeSource(fakeTimeSource);257 edgeDragArea->d->setRecognitionTimer(fakeTimer);
447258 edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
448 // don't let it interfere with our test
449 edgeDragArea->setMinSpeed(0.0);
450259
451 int timeStepMs = 5; // some arbitrary small value.260 int timeStepMs = 5; // some arbitrary small value.
452261
@@ -454,63 +263,32 @@
454 QPointF touchPoint = initialTouchPos;263 QPointF touchPoint = initialTouchPos;
455264
456 QPointF dragDirectionVector(1.0, 0.0);265 QPointF dragDirectionVector(1.0, 0.0);
457 QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.2f);266 QPointF touchMovement = dragDirectionVector * (edgeDragArea->d->distanceThreshold * 0.2f);
458267
459 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);268 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
460 QVERIFY(!fakeTimer->isRunning());269 QVERIFY(!fakeTimer->isRunning());
461270
462 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());271 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
463272
464 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);273 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
465 QVERIFY(fakeTimer->isRunning());274 QVERIFY(fakeTimer->isRunning());
466275
467 // Move beyond distance threshold and composition time to ensure recognition276 // Move beyond distance threshold and composition time to ensure recognition
468 while (fakeTimeSource->m_msecsSinceReference <= edgeDragArea->compositionTime() ||277 while (m_fakeTimerFactory->timeSource()->msecsSinceReference() <= edgeDragArea->d->compositionTime ||
469 (touchPoint - initialTouchPos).manhattanLength() <= edgeDragArea->distanceThreshold()) {278 (touchPoint - initialTouchPos).manhattanLength() <= edgeDragArea->d->distanceThreshold) {
470279
471 QCOMPARE(edgeDragArea->status() == DirectionalDragArea::Undecided, fakeTimer->isRunning());280 QCOMPARE(edgeDragArea->d->status == DirectionalDragAreaPrivate::Undecided, fakeTimer->isRunning());
472281
473 touchPoint += touchMovement;282 touchPoint += touchMovement;
474 passTime(timeStepMs);283 passTime(timeStepMs);
475 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());284 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
476 }285 }
477286
478 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);287 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
479 QVERIFY(!fakeTimer->isRunning());288 QVERIFY(!fakeTimer->isRunning());
480}289}
481290
482/*291/*
483 A gesture should be rejected if too much time has passed without any new input
484 events from it.
485 */
486void tst_DirectionalDragArea::maxSilenceTime()
487{
488 DirectionalDragArea *edgeDragArea =
489 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
490 QVERIFY(edgeDragArea != 0);
491 edgeDragArea->setRecognitionTimer(fakeTimer);
492 edgeDragArea->setTimeSource(fakeTimeSource);
493
494 // Make sure this property is not disabled
495 edgeDragArea->setMaxSilenceTime(100);
496
497 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
498 QPointF touchPoint = initialTouchPos;
499
500 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
501
502 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
503 QVERIFY(fakeTimer->isRunning());
504
505 // Force timer to timeout until after maxSilenceTime has been reached
506 while (fakeTimeSource->m_msecsSinceReference < edgeDragArea->maxSilenceTime()) {
507 passTime(fakeTimer->interval());
508 }
509
510 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
511}
512
513/*
514 Checks that it informs the X coordinate of the touch point in local and scene coordinates292 Checks that it informs the X coordinate of the touch point in local and scene coordinates
515 correctly.293 correctly.
516 */294 */
@@ -519,18 +297,19 @@
519 DirectionalDragArea *edgeDragArea =297 DirectionalDragArea *edgeDragArea =
520 m_view->rootObject()->findChild<DirectionalDragArea*>("hnDragArea");298 m_view->rootObject()->findChild<DirectionalDragArea*>("hnDragArea");
521 QVERIFY(edgeDragArea != 0);299 QVERIFY(edgeDragArea != 0);
522 edgeDragArea->setRecognitionTimer(fakeTimer);300 edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
523 edgeDragArea->setTimeSource(fakeTimeSource);301 edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
302 edgeDragArea->setImmediateRecognition(true);
524303
525 QPointF touchScenePos(m_view->width() - (edgeDragArea->width()/2.0f), m_view->height()/2.0f);304 QPointF touchScenePos(m_view->width() - (edgeDragArea->width()/2.0f), m_view->height()/2.0f);
526305
527 QTest::touchEvent(m_view, m_device).press(0, touchScenePos.toPoint());306 sendTouchPress(0 /* timestamp */, 0 /* id */, touchScenePos);
528307
529 QSignalSpy touchXSpy(edgeDragArea, SIGNAL(touchXChanged(qreal)));308 QSignalSpy touchXSpy(edgeDragArea, SIGNAL(touchXChanged(qreal)));
530 QSignalSpy touchSceneXSpy(edgeDragArea, SIGNAL(touchSceneXChanged(qreal)));309 QSignalSpy touchSceneXSpy(edgeDragArea, SIGNAL(touchSceneXChanged(qreal)));
531310
532 touchScenePos.rx() = m_view->width() / 2;311 touchScenePos.rx() = m_view->width() / 2;
533 QTest::touchEvent(m_view, m_device).move(0, touchScenePos.toPoint());312 sendTouchUpdate(50 /* timestamp */, 0 /* id */, touchScenePos);
534313
535 QCOMPARE(touchXSpy.count(), 1);314 QCOMPARE(touchXSpy.count(), 1);
536 QCOMPARE(touchSceneXSpy.count(), 1);315 QCOMPARE(touchSceneXSpy.count(), 1);
@@ -547,18 +326,19 @@
547 DirectionalDragArea *edgeDragArea =326 DirectionalDragArea *edgeDragArea =
548 m_view->rootObject()->findChild<DirectionalDragArea*>("vnDragArea");327 m_view->rootObject()->findChild<DirectionalDragArea*>("vnDragArea");
549 QVERIFY(edgeDragArea != 0);328 QVERIFY(edgeDragArea != 0);
550 edgeDragArea->setRecognitionTimer(fakeTimer);329 edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
551 edgeDragArea->setTimeSource(fakeTimeSource);330 edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
331 edgeDragArea->setImmediateRecognition(true);
552332
553 QPointF touchScenePos(m_view->width()/2.0f, m_view->height() - (edgeDragArea->height()/2.0f));333 QPointF touchScenePos(m_view->width()/2.0f, m_view->height() - (edgeDragArea->height()/2.0f));
554334
555 QTest::touchEvent(m_view, m_device).press(0, touchScenePos.toPoint());335 sendTouchPress(0 /* timestamp */, 0 /* id */, touchScenePos);
556336
557 QSignalSpy touchYSpy(edgeDragArea, SIGNAL(touchYChanged(qreal)));337 QSignalSpy touchYSpy(edgeDragArea, SIGNAL(touchYChanged(qreal)));
558 QSignalSpy touchSceneYSpy(edgeDragArea, SIGNAL(touchSceneYChanged(qreal)));338 QSignalSpy touchSceneYSpy(edgeDragArea, SIGNAL(touchSceneYChanged(qreal)));
559339
560 touchScenePos.ry() = m_view->height() / 2;340 touchScenePos.ry() = m_view->height() / 2;
561 QTest::touchEvent(m_view, m_device).move(0, touchScenePos.toPoint());341 sendTouchUpdate(50 /* timestamp */, 0 /* id */, touchScenePos);
562342
563 QCOMPARE(touchYSpy.count(), 1);343 QCOMPARE(touchYSpy.count(), 1);
564 QCOMPARE(touchSceneYSpy.count(), 1);344 QCOMPARE(touchSceneYSpy.count(), 1);
@@ -577,8 +357,8 @@
577 DirectionalDragArea *edgeDragArea =357 DirectionalDragArea *edgeDragArea =
578 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");358 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
579 QVERIFY(edgeDragArea != 0);359 QVERIFY(edgeDragArea != 0);
580 edgeDragArea->setRecognitionTimer(fakeTimer);360 edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
581 edgeDragArea->setTimeSource(fakeTimeSource);361 edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
582362
583 // Make touches evenly spaced along the edgeDragArea363 // Make touches evenly spaced along the edgeDragArea
584 QPoint touchAPos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);364 QPoint touchAPos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);
@@ -592,7 +372,7 @@
592 QTest::touchEvent(m_view, m_device)372 QTest::touchEvent(m_view, m_device)
593 .press(0, touchAPos);373 .press(0, touchAPos);
594374
595 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);375 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
596376
597 passTime(timeStepMsecs);377 passTime(timeStepMsecs);
598 QTest::touchEvent(m_view, m_device)378 QTest::touchEvent(m_view, m_device)
@@ -601,20 +381,20 @@
601381
602 // A second touch point appeared during recognition, reject immediately as this382 // A second touch point appeared during recognition, reject immediately as this
603 // can't be a single-touch gesture anymore.383 // can't be a single-touch gesture anymore.
604 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);384 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
605385
606 passTime(timeStepMsecs);386 passTime(timeStepMsecs);
607 QTest::touchEvent(m_view, m_device)387 QTest::touchEvent(m_view, m_device)
608 .release(0, touchAPos)388 .release(0, touchAPos)
609 .move(1, touchBPos);389 .move(1, touchBPos);
610390
611 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);391 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
612392
613 passTime(timeStepMsecs);393 passTime(timeStepMsecs);
614 QTest::touchEvent(m_view, m_device)394 QTest::touchEvent(m_view, m_device)
615 .release(1, touchBPos);395 .release(1, touchBPos);
616396
617 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);397 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
618398
619 // Perform the second two-finger tap399 // Perform the second two-finger tap
620400
@@ -622,27 +402,27 @@
622 QTest::touchEvent(m_view, m_device)402 QTest::touchEvent(m_view, m_device)
623 .press(0, touchAPos);403 .press(0, touchAPos);
624404
625 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);405 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
626406
627 passTime(timeStepMsecs);407 passTime(timeStepMsecs);
628 QTest::touchEvent(m_view, m_device)408 QTest::touchEvent(m_view, m_device)
629 .move(0, touchAPos)409 .move(0, touchAPos)
630 .press(1, touchBPos);410 .press(1, touchBPos);
631411
632 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);412 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
633413
634 passTime(timeStepMsecs);414 passTime(timeStepMsecs);
635 QTest::touchEvent(m_view, m_device)415 QTest::touchEvent(m_view, m_device)
636 .release(0, touchAPos)416 .release(0, touchAPos)
637 .move(1, touchBPos);417 .move(1, touchBPos);
638418
639 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);419 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
640420
641 passTime(timeStepMsecs);421 passTime(timeStepMsecs);
642 QTest::touchEvent(m_view, m_device)422 QTest::touchEvent(m_view, m_device)
643 .release(1, touchBPos);423 .release(1, touchBPos);
644424
645 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);425 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
646}426}
647427
648/*428/*
@@ -659,25 +439,25 @@
659 DirectionalDragArea *edgeDragArea =439 DirectionalDragArea *edgeDragArea =
660 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");440 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
661 Q_ASSERT(edgeDragArea != 0);441 Q_ASSERT(edgeDragArea != 0);
662 edgeDragArea->setRecognitionTimer(fakeTimer);442 edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
663 edgeDragArea->setTimeSource(fakeTimeSource);443 edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
664444
665 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);445 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
666 QPointF touchPoint = initialTouchPos;446 QPointF touchPoint = initialTouchPos;
667447
668 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;448 qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
669 QPointF dragDirectionVector(1.0f, 0.0f);449 QPointF dragDirectionVector(1.0f, 0.0f);
670450
671 qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;451 qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
672 QPointF touchMovement = dragDirectionVector * movementStepDistance;452 QPointF touchMovement = dragDirectionVector * movementStepDistance;
673 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);453 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
674 int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;454 int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
675455
676 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());456 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
677457
678 // Move it far ahead along the direction of the gesture458 // Move it far ahead along the direction of the gesture
679 // rightwardsLauncher is a parent of our DirectionalDragArea. So moving it will move our DDA459 // rightwardsLauncher is a parent of our DirectionalDragArea. So moving it will move our DDA
680 rightwardsLauncher->setX(rightwardsLauncher->x() + edgeDragArea->distanceThreshold() * 5.0f);460 rightwardsLauncher->setX(rightwardsLauncher->x() + edgeDragArea->d->distanceThreshold * 5.0f);
681461
682 for (int i = 0; i < totalMovementSteps; ++i) {462 for (int i = 0; i < totalMovementSteps; ++i) {
683 touchPoint += touchMovement;463 touchPoint += touchMovement;
@@ -685,7 +465,7 @@
685 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());465 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
686 }466 }
687467
688 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);468 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
689469
690 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());470 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
691}471}
@@ -699,8 +479,8 @@
699 DirectionalDragArea *edgeDragArea =479 DirectionalDragArea *edgeDragArea =
700 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");480 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
701 Q_ASSERT(edgeDragArea != 0);481 Q_ASSERT(edgeDragArea != 0);
702 edgeDragArea->setRecognitionTimer(fakeTimer);482 edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
703 edgeDragArea->setTimeSource(fakeTimeSource);483 edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
704484
705 // Make touches evenly spaced along the edgeDragArea485 // Make touches evenly spaced along the edgeDragArea
706 QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);486 QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);
@@ -708,26 +488,26 @@
708488
709 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);489 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
710490
711 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);491 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
712492
713 // leave it lying around for some time493 // leave it lying around for some time
714 passTime(qMax(edgeDragArea->maxSilenceTime(), edgeDragArea->compositionTime()) * 10);494 passTime(edgeDragArea->d->maxTime * 10);
715495
716 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);496 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
717497
718 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;498 qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
719 QPointF dragDirectionVector(1.0f, 0.0f);499 QPointF dragDirectionVector(1.0f, 0.0f);
720500
721 qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;501 qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
722 QPointF touchMovement = dragDirectionVector * movementStepDistance;502 QPointF touchMovement = dragDirectionVector * movementStepDistance;
723 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);503 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
724 int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;504 int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
725505
726 QTest::touchEvent(m_view, m_device)506 QTest::touchEvent(m_view, m_device)
727 .move(0, touch0Pos)507 .move(0, touch0Pos)
728 .press(1, touch1Pos);508 .press(1, touch1Pos);
729509
730 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);510 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
731511
732 for (int i = 0; i < totalMovementSteps; ++i) {512 for (int i = 0; i < totalMovementSteps; ++i) {
733 touch1Pos += touchMovement.toPoint();513 touch1Pos += touchMovement.toPoint();
@@ -737,13 +517,13 @@
737 .move(1, touch1Pos);517 .move(1, touch1Pos);
738 }518 }
739519
740 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);520 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
741521
742 QTest::touchEvent(m_view, m_device)522 QTest::touchEvent(m_view, m_device)
743 .move(0, touch0Pos)523 .move(0, touch0Pos)
744 .release(1, touch1Pos);524 .release(1, touch1Pos);
745525
746 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);526 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
747}527}
748528
749/*529/*
@@ -762,19 +542,19 @@
762 DirectionalDragArea *edgeDragArea =542 DirectionalDragArea *edgeDragArea =
763 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");543 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
764 Q_ASSERT(edgeDragArea != 0);544 Q_ASSERT(edgeDragArea != 0);
765 edgeDragArea->setRecognitionTimer(fakeTimer);545 edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
766 edgeDragArea->setTimeSource(fakeTimeSource);546 edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
767547
768 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);548 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
769 QPointF touchPoint = initialTouchPos;549 QPointF touchPoint = initialTouchPos;
770550
771 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;551 qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
772 QPointF dragDirectionVector(0.0f, 1.0f);552 QPointF dragDirectionVector(0.0f, 1.0f);
773553
774 qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;554 qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
775 QPointF touchMovement = dragDirectionVector * movementStepDistance;555 QPointF touchMovement = dragDirectionVector * movementStepDistance;
776 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);556 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
777 int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;557 int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
778558
779 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());559 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
780560
@@ -784,7 +564,7 @@
784 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());564 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
785 }565 }
786566
787 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);567 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
788568
789 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());569 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
790}570}
@@ -802,31 +582,30 @@
802 DirectionalDragArea *edgeDragArea =582 DirectionalDragArea *edgeDragArea =
803 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");583 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
804 Q_ASSERT(edgeDragArea != 0);584 Q_ASSERT(edgeDragArea != 0);
805 edgeDragArea->setRecognitionTimer(fakeTimer);585 edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
806 edgeDragArea->setTimeSource(fakeTimeSource);586 edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
587
588 // to disable the position smoothing so that we can more easily check sceneDistance values
589 edgeDragArea->setImmediateRecognition(true);
807590
808 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);591 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
809 QPointF touchPoint = initialTouchPos;592 QPointF touchPoint = initialTouchPos;
810593
811 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;594 qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
812595
813 qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;596 qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
814 QPointF touchMovement = dragDirectionVector * movementStepDistance;597 QPointF touchMovement = dragDirectionVector * movementStepDistance;
815 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);598 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
816 int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;599 int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
817600
818 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());601 qint64 timestamp = 0;
819602
820#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))603 sendTouchPress(timestamp, 0, touchPoint);
821 QQuickWindowPrivate *wp = QQuickWindowPrivate::get(m_view);604
822#endif
823 for (int i = 0; i < totalMovementSteps; ++i) {605 for (int i = 0; i < totalMovementSteps; ++i) {
824 touchPoint += touchMovement;606 touchPoint += touchMovement;
825 passTime(movementTimeStepMs);607 timestamp += movementTimeStepMs;
826 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());608 sendTouchUpdate(timestamp, 0, touchPoint);
827#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
828 wp->flushDelayedTouchEvent();
829#endif
830 }609 }
831610
832 qreal actualDragDistance = ((qreal)totalMovementSteps) * movementStepDistance;611 qreal actualDragDistance = ((qreal)totalMovementSteps) * movementStepDistance;
@@ -836,7 +615,8 @@
836 // NB: qFuzzyCompare(), used internally by QCOMPARE(), is broken.615 // NB: qFuzzyCompare(), used internally by QCOMPARE(), is broken.
837 QVERIFY(qAbs(edgeDragArea->sceneDistance() - actualDragDistance) < 0.001);616 QVERIFY(qAbs(edgeDragArea->sceneDistance() - actualDragDistance) < 0.001);
838617
839 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());618 timestamp += movementTimeStepMs;
619 sendTouchRelease(timestamp, 0, touchPoint);
840}620}
841621
842void tst_DirectionalDragArea::sceneDistance_data()622void tst_DirectionalDragArea::sceneDistance_data()
@@ -860,18 +640,18 @@
860 DirectionalDragArea *edgeDragArea =640 DirectionalDragArea *edgeDragArea =
861 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");641 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
862 Q_ASSERT(edgeDragArea != 0);642 Q_ASSERT(edgeDragArea != 0);
863 edgeDragArea->setRecognitionTimer(fakeTimer);643 edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
864 edgeDragArea->setTimeSource(fakeTimeSource);644 edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
865645
866 QPointF touchPoint = calculateInitialTouchPos(edgeDragArea);646 QPointF touchPoint = calculateInitialTouchPos(edgeDragArea);
867647
868 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;648 qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
869 QPointF dragDirectionVector(1., 0.); // horizontal positive649 QPointF dragDirectionVector(1., 0.); // horizontal positive
870650
871 qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;651 qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
872 QPointF touchMovement = dragDirectionVector * movementStepDistance;652 QPointF touchMovement = dragDirectionVector * movementStepDistance;
873 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);653 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
874 int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;654 int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
875655
876 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());656 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
877657
@@ -881,13 +661,13 @@
881 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());661 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
882 }662 }
883663
884 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);664 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
885 QCOMPARE(edgeDragArea->dragging(), true);665 QCOMPARE(edgeDragArea->dragging(), true);
886666
887 // disable the dragArea while it's being dragged.667 // disable the dragArea while it's being dragged.
888 edgeDragArea->setEnabled(false);668 edgeDragArea->setEnabled(false);
889669
890 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);670 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
891 QCOMPARE(edgeDragArea->dragging(), false);671 QCOMPARE(edgeDragArea->dragging(), false);
892672
893 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());673 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
@@ -898,15 +678,14 @@
898 DirectionalDragArea *edgeDragArea =678 DirectionalDragArea *edgeDragArea =
899 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");679 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
900 Q_ASSERT(edgeDragArea != 0);680 Q_ASSERT(edgeDragArea != 0);
901 edgeDragArea->setRecognitionTimer(fakeTimer);681 edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
902 edgeDragArea->setTimeSource(fakeTimeSource);682 edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
903683
904 // Disable some constraints we're not interested in684 // Disable some constraints we're not interested in
905 edgeDragArea->setMaxSilenceTime(60 * 1000);685 edgeDragArea->d->setMaxTime(60 * 1000);
906 edgeDragArea->setMinSpeed(0);
907686
908 // And ensure others have the values we want687 // And ensure others have the values we want
909 edgeDragArea->setCompositionTime(60);688 edgeDragArea->d->compositionTime = 60;
910689
911 // Put an item right behind edgeDragArea to receive the touches ignored by it690 // Put an item right behind edgeDragArea to receive the touches ignored by it
912 DummyItem *dummyItem = new DummyItem;691 DummyItem *dummyItem = new DummyItem;
@@ -923,10 +702,10 @@
923702
924 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);703 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
925704
926 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);705 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
927706
928 // We are now going to be way beyond compositionTime707 // We are now going to be way beyond compositionTime
929 passTime(edgeDragArea->compositionTime()*3);708 passTime(edgeDragArea->d->compositionTime*3);
930709
931 QTest::touchEvent(m_view, m_device)710 QTest::touchEvent(m_view, m_device)
932 .move(0, touch0Pos)711 .move(0, touch0Pos)
@@ -948,7 +727,7 @@
948 .move(0, touch0Pos)727 .move(0, touch0Pos)
949 .move(1, touch1Pos);728 .move(1, touch1Pos);
950729
951 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);730 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
952731
953 passTime(5);732 passTime(5);
954733
@@ -956,7 +735,7 @@
956 .release(0, touch0Pos)735 .release(0, touch0Pos)
957 .move(1, touch1Pos);736 .move(1, touch1Pos);
958737
959 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);738 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
960739
961 passTime(5);740 passTime(5);
962741
@@ -964,7 +743,7 @@
964 .release(1, touch1Pos);743 .release(1, touch1Pos);
965744
966 // Shouldn't be keepping info about touches that no longer exist or interest us745 // Shouldn't be keepping info about touches that no longer exist or interest us
967 QVERIFY(edgeDragArea->m_activeTouches.isEmpty());746 QVERIFY(edgeDragArea->d->activeTouches.isEmpty());
968747
969 delete dummyItem;748 delete dummyItem;
970}749}
@@ -974,12 +753,11 @@
974 DirectionalDragArea *edgeDragArea =753 DirectionalDragArea *edgeDragArea =
975 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");754 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
976 Q_ASSERT(edgeDragArea != 0);755 Q_ASSERT(edgeDragArea != 0);
977 edgeDragArea->setRecognitionTimer(fakeTimer);756 edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
978 edgeDragArea->setTimeSource(fakeTimeSource);757 edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
979758
980 // Disable some constraints we're not interested in759 // Disable some constraints we're not interested in
981 edgeDragArea->setMaxSilenceTime(60 * 1000);760 edgeDragArea->d->setMaxTime(60 * 1000);
982 edgeDragArea->setMinSpeed(0);
983761
984 // Put an item right in front of edgeDragArea762 // Put an item right in front of edgeDragArea
985 DummyItem *dummyItem = new DummyItem(edgeDragArea->parentItem());763 DummyItem *dummyItem = new DummyItem(edgeDragArea->parentItem());
@@ -998,11 +776,11 @@
998776
999 QTest::touchEvent(m_view, m_device).press(0, touchPos);777 QTest::touchEvent(m_view, m_device).press(0, touchPos);
1000778
1001 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);779 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
1002780
1003 m_touchRegistry->requestTouchOwnership(0, dummyItem);781 m_touchRegistry->requestTouchOwnership(0, dummyItem);
1004782
1005 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);783 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1006784
1007 dummyItem->grabTouchPoints({0});785 dummyItem->grabTouchPoints({0});
1008 dummyItem->touchEventHandler = [&](QTouchEvent *event) { event->accept(); };786 dummyItem->touchEventHandler = [&](QTouchEvent *event) { event->accept(); };
@@ -1010,9 +788,9 @@
1010 passTime(5);788 passTime(5);
1011 QTest::touchEvent(m_view, m_device).release(0, touchPos);789 QTest::touchEvent(m_view, m_device).release(0, touchPos);
1012790
1013 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);791 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1014792
1015 QVERIFY(edgeDragArea->m_activeTouches.isEmpty());793 QVERIFY(edgeDragArea->d->activeTouches.isEmpty());
1016}794}
1017795
1018void tst_DirectionalDragArea::threeFingerDrag()796void tst_DirectionalDragArea::threeFingerDrag()
@@ -1020,15 +798,14 @@
1020 DirectionalDragArea *edgeDragArea =798 DirectionalDragArea *edgeDragArea =
1021 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");799 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1022 Q_ASSERT(edgeDragArea != 0);800 Q_ASSERT(edgeDragArea != 0);
1023 edgeDragArea->setRecognitionTimer(fakeTimer);801 edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
1024 edgeDragArea->setTimeSource(fakeTimeSource);802 edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1025803
1026 // Disable some constraints we're not interested in804 // Disable some constraints we're not interested in
1027 edgeDragArea->setMaxSilenceTime(60 * 1000);805 edgeDragArea->d->setMaxTime(60 * 1000);
1028 edgeDragArea->setMinSpeed(0);
1029806
1030 // And ensure others have the values we want807 // And ensure others have the values we want
1031 edgeDragArea->setCompositionTime(60);808 edgeDragArea->d->compositionTime = 60;
1032809
1033 // Make touches evenly spaced along the edgeDragArea810 // Make touches evenly spaced along the edgeDragArea
1034 QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()*0.25f);811 QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()*0.25f);
@@ -1038,14 +815,14 @@
1038 QTest::touchEvent(m_view, m_device)815 QTest::touchEvent(m_view, m_device)
1039 .press(0, touch0Pos);816 .press(0, touch0Pos);
1040817
1041 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);818 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
1042819
1043 passTime(5);820 passTime(5);
1044 QTest::touchEvent(m_view, m_device)821 QTest::touchEvent(m_view, m_device)
1045 .move(0, touch0Pos)822 .move(0, touch0Pos)
1046 .press(1, touch1Pos);823 .press(1, touch1Pos);
1047824
1048 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);825 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1049826
1050 passTime(5);827 passTime(5);
1051 QTest::touchEvent(m_view, m_device)828 QTest::touchEvent(m_view, m_device)
@@ -1053,7 +830,7 @@
1053 .move(1, touch1Pos)830 .move(1, touch1Pos)
1054 .press(2, touch2Pos);831 .press(2, touch2Pos);
1055832
1056 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);833 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1057834
1058 passTime(10);835 passTime(10);
1059 QTest::touchEvent(m_view, m_device)836 QTest::touchEvent(m_view, m_device)
@@ -1077,7 +854,7 @@
1077 .release(0, touch0Pos);854 .release(0, touch0Pos);
1078855
1079 // Shouldn't be keepping info about touches that no longer exist or interest us856 // Shouldn't be keepping info about touches that no longer exist or interest us
1080 QVERIFY(edgeDragArea->m_activeTouches.isEmpty());857 QVERIFY(edgeDragArea->d->activeTouches.isEmpty());
1081}858}
1082859
1083/*860/*
@@ -1090,12 +867,12 @@
1090 DirectionalDragArea *edgeDragArea =867 DirectionalDragArea *edgeDragArea =
1091 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");868 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1092 Q_ASSERT(edgeDragArea != 0);869 Q_ASSERT(edgeDragArea != 0);
1093 edgeDragArea->setRecognitionTimer(fakeTimer);870 edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
1094 edgeDragArea->setTimeSource(fakeTimeSource);871 edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1095872
1096 // Disable the minimum amount of constraints to ensure immediate recognition873 // Disable the minimum amount of constraints to ensure immediate recognition
1097 edgeDragArea->setDistanceThreshold(0);874 edgeDragArea->d->setDistanceThreshold(0);
1098 edgeDragArea->setCompositionTime(0);875 edgeDragArea->d->compositionTime = 0;
1099876
1100 // Put an item right behind edgeDragArea to receive the touches ignored by it877 // Put an item right behind edgeDragArea to receive the touches ignored by it
1101 DummyItem *dummyItem = new DummyItem;878 DummyItem *dummyItem = new DummyItem;
@@ -1111,7 +888,7 @@
1111 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);888 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
1112889
1113 // check for immediate recognition890 // check for immediate recognition
1114 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);891 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
1115892
1116 // and therefore it should have immediately grabbed the touch point,893 // and therefore it should have immediately grabbed the touch point,
1117 // not letting it leak to items behind him.894 // not letting it leak to items behind him.
@@ -1123,19 +900,19 @@
1123 DirectionalDragArea *edgeDragArea =900 DirectionalDragArea *edgeDragArea =
1124 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");901 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1125 Q_ASSERT(edgeDragArea != 0);902 Q_ASSERT(edgeDragArea != 0);
1126 edgeDragArea->setRecognitionTimer(fakeTimer);903 edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
1127 edgeDragArea->setTimeSource(fakeTimeSource);904 edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1128905
1129 QPointF touchPoint = calculateInitialTouchPos(edgeDragArea);906 QPointF touchPoint = calculateInitialTouchPos(edgeDragArea);
1130907
1131 // Move less than the minimum needed for the drag gesture recognition908 // Move less than the minimum needed for the drag gesture recognition
1132 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*0.5f;909 qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 0.5f;
1133 QPointF dragDirectionVector(1., 0.); // horizontal positive910 QPointF dragDirectionVector(1., 0.); // horizontal positive
1134911
1135 qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;912 qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
1136 QPointF touchMovement = dragDirectionVector * movementStepDistance;913 QPointF touchMovement = dragDirectionVector * movementStepDistance;
1137 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);914 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
1138 int movementTimeStepMs = (edgeDragArea->compositionTime() * 0.8f) / totalMovementSteps;915 int movementTimeStepMs = (edgeDragArea->d->compositionTime * 0.8f) / totalMovementSteps;
1139916
1140 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());917 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
1141918
@@ -1145,14 +922,14 @@
1145 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());922 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
1146 }923 }
1147924
1148 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);925 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
1149926
1150 // edgeDragArea should be an undecided candidate927 // edgeDragArea should be an undecided candidate
1151 {928 {
1152 auto touchInfo = m_touchRegistry->findTouchInfo(0);929 auto touchInfo = m_touchRegistry->findTouchInfo(0);
1153 QCOMPARE(touchInfo->candidates.size(), 1);930 QCOMPARE(touchInfo->candidates.size(), 1);
1154 QCOMPARE(touchInfo->candidates.at(0).item.data(), edgeDragArea);931 QCOMPARE(touchInfo->candidates.at(0).item.data(), edgeDragArea);
1155 QCOMPARE(touchInfo->candidates.at(0).undecided, true);932 QCOMPARE(touchInfo->candidates.at(0).state, TouchRegistry::CandidateInfo::Undecided);
1156 }933 }
1157934
1158 // disable the dragArea while it's still recognizing a possible drag gesture.935 // disable the dragArea while it's still recognizing a possible drag gesture.
@@ -1169,7 +946,7 @@
1169 QCOMPARE(touchInfo->candidates.size(), 0);946 QCOMPARE(touchInfo->candidates.size(), 0);
1170 }947 }
1171948
1172 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);949 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1173950
1174 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());951 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
1175}952}
@@ -1182,47 +959,13 @@
1182 QTest::newRow("invisible") << false;959 QTest::newRow("invisible") << false;
1183}960}
1184961
1185void tst_DirectionalDragArea::tappedSignal()
1186{
1187 DirectionalDragArea *edgeDragArea =
1188 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1189 QVERIFY(edgeDragArea != 0);
1190 edgeDragArea->setRecognitionTimer(fakeTimer);
1191 edgeDragArea->setTimeSource(fakeTimeSource);
1192
1193 QFETCH(bool, immediateGestureRecognition);
1194 if (immediateGestureRecognition) {
1195 // Disable the minimum amount of constraints to ensure immediate recognition
1196 edgeDragArea->setDistanceThreshold(0);
1197 edgeDragArea->setCompositionTime(0);
1198 }
1199
1200 QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()/2.0f);
1201
1202 QSignalSpy tappedSpy(edgeDragArea, SIGNAL(tapped()));
1203
1204 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
1205 passTime(edgeDragArea->maxTapDuration() / 2);
1206 QTest::touchEvent(m_view, m_device).release(0, touch0Pos);
1207
1208 QCOMPARE(tappedSpy.count(), 1);
1209}
1210
1211void tst_DirectionalDragArea::tappedSignal_data()
1212{
1213 QTest::addColumn<bool>("immediateGestureRecognition");
1214
1215 QTest::newRow("immediate gesture recognition") << true;
1216 QTest::newRow("default gesture recognition") << false;
1217}
1218
1219void tst_DirectionalDragArea::gettingTouchOwnershipMakesMouseAreaBehindGetCanceled()962void tst_DirectionalDragArea::gettingTouchOwnershipMakesMouseAreaBehindGetCanceled()
1220{963{
1221 DirectionalDragArea *edgeDragArea =964 DirectionalDragArea *edgeDragArea =
1222 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");965 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1223 QVERIFY(edgeDragArea != nullptr);966 QVERIFY(edgeDragArea != nullptr);
1224 edgeDragArea->setRecognitionTimer(fakeTimer);967 edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
1225 edgeDragArea->setTimeSource(fakeTimeSource);968 edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1226969
1227 QQuickMouseArea *mouseArea =970 QQuickMouseArea *mouseArea =
1228 m_view->rootObject()->findChild<QQuickMouseArea*>("mouseArea");971 m_view->rootObject()->findChild<QQuickMouseArea*>("mouseArea");
@@ -1233,12 +976,12 @@
1233 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);976 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
1234 QPointF touchPoint = initialTouchPos;977 QPointF touchPoint = initialTouchPos;
1235978
1236 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2;979 qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2;
1237 QPointF dragDirectionVector(1.0f, 0.0f); // rightwards980 QPointF dragDirectionVector(1.0f, 0.0f); // rightwards
1238 qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;981 qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
1239 QPointF touchMovement = dragDirectionVector * movementStepDistance;982 QPointF touchMovement = dragDirectionVector * movementStepDistance;
1240 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);983 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
1241 int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;984 int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
1242985
1243 QCOMPARE(mouseArea->pressed(), false);986 QCOMPARE(mouseArea->pressed(), false);
1244987
@@ -1259,13 +1002,305 @@
1259 // As the DirectionalDragArea recognizes the gesture, it grabs the touch from the MouseArea,1002 // As the DirectionalDragArea recognizes the gesture, it grabs the touch from the MouseArea,
1260 // which should make the MouseArea get a cancelation event, which will then cause it to1003 // which should make the MouseArea get a cancelation event, which will then cause it to
1261 // reset its state (going back to "unpressed"/"released").1004 // reset its state (going back to "unpressed"/"released").
1262 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);1005 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
1263 QCOMPARE(mouseArea->pressed(), false);1006 QCOMPARE(mouseArea->pressed(), false);
1264 QCOMPARE(mouseAreaSpy.canceledCount, 1);1007 QCOMPARE(mouseAreaSpy.canceledCount, 1);
12651008
1266 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());1009 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
1267}1010}
12681011
1012void tst_DirectionalDragArea::interleavedTouches()
1013{
1014 DirectionalDragArea *edgeDragArea =
1015 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1016 QVERIFY(edgeDragArea != 0);
1017 edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
1018 edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1019
1020 QPointF touch0 = edgeDragArea->mapToScene(
1021 QPointF(edgeDragArea->width()*0.5, edgeDragArea->height()*0.3));
1022
1023 qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2;
1024 QPointF dragDirectionVector(1.0f, 0.0f); // rightwards
1025 qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
1026 QPointF touchMovement = dragDirectionVector * movementStepDistance;
1027 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
1028 int movementTimeStepMs = (edgeDragArea->d->maxTime * 0.4f) / totalMovementSteps;
1029
1030 QTest::touchEvent(m_view, m_device).press(0, touch0.toPoint());
1031 for (int i = 0; i < totalMovementSteps; ++i) {
1032 touch0 += touchMovement;
1033 passTime(movementTimeStepMs);
1034 QTest::touchEvent(m_view, m_device).move(0, touch0.toPoint());
1035 }
1036 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
1037
1038 QPointF touch1 = edgeDragArea->mapToScene(
1039 QPointF(edgeDragArea->width()*0.5, edgeDragArea->height()*0.6));
1040
1041 QTest::touchEvent(m_view, m_device)
1042 .move(0, touch0.toPoint())
1043 .press(1, touch1.toPoint());
1044
1045 touch1 += touchMovement;
1046 passTime(movementTimeStepMs);
1047 QTest::touchEvent(m_view, m_device)
1048 .move(0, touch0.toPoint())
1049 .move(1, touch1.toPoint());
1050
1051 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
1052
1053 QTest::touchEvent(m_view, m_device)
1054 .release(0, touch0.toPoint())
1055 .move(1, touch1.toPoint());
1056
1057 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1058
1059 touch1 += touchMovement;
1060 passTime(movementTimeStepMs);
1061 QTest::touchEvent(m_view, m_device)
1062 .move(1, touch1.toPoint());
1063
1064 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1065
1066 QPointF touch2 = edgeDragArea->mapToScene(
1067 QPointF(edgeDragArea->width()*0.5, edgeDragArea->height()*0.9));
1068
1069 passTime(edgeDragArea->d->compositionTime + movementTimeStepMs);
1070 QTest::touchEvent(m_view, m_device)
1071 .move(1, touch1.toPoint())
1072 .press(2, touch2.toPoint());
1073
1074 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
1075 QCOMPARE(edgeDragArea->d->touchId, 2);
1076
1077 touch2 += touchMovement;
1078 passTime(movementTimeStepMs);
1079 QTest::touchEvent(m_view, m_device)
1080 .move(1, touch1.toPoint())
1081 .move(2, touch2.toPoint());
1082
1083 touch1 += touchMovement;
1084 passTime(movementTimeStepMs);
1085 QTest::touchEvent(m_view, m_device)
1086 .move(1, touch1.toPoint())
1087 .move(2, touch2.toPoint());
1088
1089 passTime(movementTimeStepMs);
1090 QTest::touchEvent(m_view, m_device)
1091 .release(1, touch1.toPoint())
1092 .move(2, touch2.toPoint());
1093
1094 passTime(movementTimeStepMs);
1095 QTest::touchEvent(m_view, m_device)
1096 .release(2, touch2.toPoint());
1097
1098 QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1099}
1100
1101/*
1102 A valid right-edge drag performed on mako
1103 */
1104void tst_DirectionalDragArea::makoRightEdgeDrag()
1105{
1106 m_view->resize(768, 1280);
1107 QTest::qWait(300);
1108
1109 DirectionalDragArea *edgeDragArea =
1110 m_view->rootObject()->findChild<DirectionalDragArea*>("hnDragArea");
1111 QVERIFY(edgeDragArea != nullptr);
1112 edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
1113 edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1114
1115 StatusSpy *statusSpy = new StatusSpy(edgeDragArea);
1116
1117 edgeDragArea->d->setPixelsPerMm(320.0 /*mako ppi*/ * 0.03937 /* inches per mm*/);
1118
1119 sendTouchPress(319744, 0, QPointF(767.001, 719.719));
1120 sendTouchUpdate(319765, 0, QPointF(765.744,729.973));
1121 sendTouchUpdate(319784, 0, QPointF(740.879,752.182));
1122 sendTouchUpdate(319803, 0, QPointF(689.698,795.795));
1123 sendTouchUpdate(319826, 0, QPointF(616.978,856.212));
1124 sendTouchUpdate(319845, 0, QPointF(558.769,906.157));
1125 sendTouchUpdate(319859, 0, QPointF(513.219,945.266));
1126 sendTouchUpdate(319878, 0, QPointF(481.31,975.496));
1127 sendTouchUpdate(319902, 0, QPointF(460.016,997.439));
1128 sendTouchUpdate(319920, 0, QPointF(449.761,1008.6));
1129 sendTouchUpdate(319929, 0, QPointF(445.891,1012.42));
1130 sendTouchUpdate(319947, 0, QPointF(444.884,1013.93));
1131 sendTouchUpdate(319965, 0, QPointF(444.461,1014.35));
1132 sendTouchUpdate(320057, 0, QPointF(444.71,1013.56));
1133 sendTouchUpdate(320138, 0, QPointF(445.434,1013.6));
1134 sendTouchUpdate(320154, 0, QPointF(446.338,1012.98));
1135 sendTouchUpdate(320171, 0, QPointF(447.232,1012.08));
1136 sendTouchRelease(320171, 0, QPointF(447.232,1012.08));
1137
1138 QCOMPARE(statusSpy->recognized(), true);
1139
1140 delete statusSpy;
1141}
1142
1143/*
1144 A vertical, downwards swipe performed on mako near its right edge.
1145
1146 The DirectionalDragArea on the right edge must not recognize this
1147 gesture.
1148 */
1149void tst_DirectionalDragArea::makoRightEdgeDrag_verticalDownwards()
1150{
1151 m_view->resize(768, 1280);
1152 QTest::qWait(300);
1153
1154 DirectionalDragArea *edgeDragArea =
1155 m_view->rootObject()->findChild<DirectionalDragArea*>("hnDragArea");
1156 QVERIFY(edgeDragArea != nullptr);
1157 edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
1158 edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1159
1160 edgeDragArea->d->setPixelsPerMm(320.0 /*mako ppi*/ * 0.03937 /* inches per mm*/);
1161
1162 StatusSpy *statusSpy = new StatusSpy(edgeDragArea);
1163
1164 sendTouchPress(12012445, 26, QPointF(767.001,461.82));
1165 sendTouchUpdate(12012472, 26, QPointF(767.001,462.569));
1166 sendTouchUpdate(12012528, 26, QPointF(767.001,463.334));
1167 sendTouchUpdate(12012546, 26, QPointF(767.001,466.856));
1168 sendTouchUpdate(12012571, 26, QPointF(767.001,473.291));
1169 sendTouchUpdate(12012587, 26, QPointF(767.001,487.31));
1170 sendTouchUpdate(12012604, 26, QPointF(765.364,507.521));
1171 sendTouchUpdate(12012618, 26, QPointF(765.364,507.521));
1172 sendTouchUpdate(12012627, 26, QPointF(762.642,534.317));
1173 sendTouchUpdate(12012655, 26, QPointF(760.846,573.406));
1174 sendTouchUpdate(12012667, 26, QPointF(759.838,625.295));
1175 sendTouchUpdate(12012675, 26, QPointF(758.875,703.207));
1176 sendTouchUpdate(12012696, 26, QPointF(761.52,777.015));
1177 sendTouchUpdate(12012713, 26, QPointF(765.659,835.591));
1178 sendTouchUpdate(12012731, 26, QPointF(766.778,883.206));
1179 sendTouchUpdate(12012748, 26, QPointF(767.001,922.937));
1180 sendTouchUpdate(12012779, 26, QPointF(767.001,967.558));
1181 sendTouchUpdate(12012798, 26, QPointF(767.001,1006.12));
1182 sendTouchUpdate(12012809, 26, QPointF(767.001,1033.1));
1183 sendTouchRelease(12012810, 26, QPointF(767.001,1033.1));
1184
1185 QCOMPARE(statusSpy->recognized(), false);
1186
1187 delete statusSpy;
1188}
1189
1190/*
1191 A valid left-edge drag performed on mako. This one starts a bit slow than speeds up
1192 */
1193void tst_DirectionalDragArea::makoLeftEdgeDrag_slowStart()
1194{
1195 m_view->resize(768, 1280);
1196 QTest::qWait(300);
1197
1198 DirectionalDragArea *edgeDragArea =
1199 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1200 QVERIFY(edgeDragArea != nullptr);
1201 edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
1202 edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1203
1204 edgeDragArea->d->setPixelsPerMm(320.0 /*mako ppi*/ * 0.03937 /* inches per mm*/);
1205
1206 StatusSpy *statusSpy = new StatusSpy(edgeDragArea);
1207
1208 sendTouchPress(4002267, 77, QPointF(0,885.154));
1209 sendTouchUpdate(4002275, 77, QPointF(0,886.214));
1210 sendTouchUpdate(4002311, 77, QPointF(1.09568,887.75));
1211 sendTouchUpdate(4002329, 77, QPointF(3.53647,890.191));
1212 sendTouchUpdate(4002347, 77, QPointF(7.87434,892.879));
1213 sendTouchUpdate(4002366, 77, QPointF(12.3036,895.075));
1214 sendTouchUpdate(4002384, 77, QPointF(15.8885,896.849));
1215 sendTouchUpdate(4002406, 77, QPointF(18.4504,897.88));
1216 sendTouchUpdate(4002420, 77, QPointF(20.2429,898.149));
1217 sendTouchUpdate(4002439, 77, QPointF(20.9945,898.149));
1218 sendTouchUpdate(4002457, 77, QPointF(21.8819,898.149));
1219 sendTouchUpdate(4002480, 77, QPointF(22.7454,897.389));
1220 sendTouchUpdate(4002493, 77, QPointF(23.5456,896.589));
1221 sendTouchUpdate(4002511, 77, QPointF(24.5435,895.031));
1222 sendTouchUpdate(4002529, 77, QPointF(25.4271,892.32));
1223 sendTouchUpdate(4002548, 77, QPointF(26.3145,889.658));
1224 sendTouchUpdate(4002566, 77, QPointF(27.2004,886.999));
1225 sendTouchUpdate(4002584, 77, QPointF(28.035,885.048));
1226 sendTouchUpdate(4002603, 77, QPointF(29.9684,883.167));
1227 sendTouchUpdate(4002620, 77, QPointF(33.3591,881.403));
1228 sendTouchUpdate(4002639, 77, QPointF(44.1017,879.642));
1229 sendTouchUpdate(4002657, 77, QPointF(64.828,878.502));
1230 sendTouchUpdate(4002675, 77, QPointF(87.9486,878.157));
1231 sendTouchUpdate(4002693, 77, QPointF(112.96,877.742));
1232 sendTouchUpdate(4002711, 77, QPointF(138.903,877.157));
1233 sendTouchUpdate(4002729, 77, QPointF(163.204,877.157));
1234 sendTouchUpdate(4002747, 77, QPointF(182.127,877.157));
1235 sendTouchUpdate(4002765, 77, QPointF(194.478,877.657));
1236 sendTouchUpdate(4002785, 77, QPointF(201.474,878.508));
1237 sendTouchUpdate(4002803, 77, QPointF(204.855,879.401));
1238 sendTouchUpdate(4002822, 77, QPointF(206.616,880.281));
1239 sendTouchUpdate(4002839, 77, QPointF(207.115,880.906));
1240 sendTouchUpdate(4002894, 77, QPointF(206.865,881.184));
1241 sendTouchUpdate(4002912, 77, QPointF(206.865,882.143));
1242 sendTouchUpdate(4002930, 77, QPointF(206.865,883.106));
1243 sendTouchUpdate(4002949, 77, QPointF(206.526,883.994));
1244 sendTouchUpdate(4002967, 77, QPointF(205.866,884.88));
1245 sendTouchUpdate(4002985, 77, QPointF(205.866,885.766));
1246 sendTouchUpdate(4003005, 77, QPointF(205.866,886.654));
1247 sendTouchUpdate(4003021, 77, QPointF(205.366,887.537));
1248 sendTouchUpdate(4003039, 77, QPointF(204.592,888.428));
1249 sendTouchUpdate(4003050, 77, QPointF(204.367,888.653));
1250 sendTouchRelease(4003050, 77, QPointF(204.367,888.653));
1251
1252 QCOMPARE(statusSpy->recognized(), true);
1253
1254 delete statusSpy;
1255}
1256
1257void tst_DirectionalDragArea::makoLeftEdgeDrag_movesSlightlyBackwardsOnStart()
1258{
1259 m_view->resize(768, 1280);
1260 QTest::qWait(300);
1261
1262 DirectionalDragArea *edgeDragArea =
1263 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1264 QVERIFY(edgeDragArea != nullptr);
1265 edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer());
1266 edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1267
1268 edgeDragArea->d->setPixelsPerMm(320.0 /*mako ppi*/ * 0.03937 /* inches per mm*/);
1269
1270 StatusSpy *statusSpy = new StatusSpy(edgeDragArea);
1271
1272 sendTouchPress(41097, 24, QPointF(13.9909,827.177));
1273 sendTouchUpdate(41120, 24, QPointF(19.2375,825.677));
1274 sendTouchUpdate(41138, 24, QPointF(18.4057,826.177));
1275 sendTouchUpdate(41161, 24, QPointF(20.1067,825.867));
1276 sendTouchUpdate(41177, 24, QPointF(21.8869,824.977));
1277 sendTouchUpdate(41193, 24, QPointF(24.7603,823.494));
1278 sendTouchUpdate(41211, 24, QPointF(28.3889,821.725));
1279 sendTouchUpdate(41229, 24, QPointF(32.2909,819.955));
1280 sendTouchUpdate(41247, 24, QPointF(38.2251,817.431));
1281 sendTouchUpdate(41266, 24, QPointF(52.4182,814.223));
1282 sendTouchUpdate(41284, 24, QPointF(85.8465,809.483));
1283 sendTouchUpdate(41302, 24, QPointF(126.091,802.741));
1284 sendTouchUpdate(41320, 24, QPointF(153.171,797.977));
1285 sendTouchUpdate(41338, 24, QPointF(170.565,795.077));
1286 sendTouchUpdate(41356, 24, QPointF(178.685,794.101));
1287 sendTouchUpdate(41375, 24, QPointF(183.706,793.225));
1288 sendTouchUpdate(41393, 24, QPointF(186.112,793.19));
1289 sendTouchUpdate(41411, 24, QPointF(187.634,793.19));
1290 sendTouchUpdate(41429, 24, QPointF(188.505,793.19));
1291 sendTouchUpdate(41532, 24, QPointF(187.816,793.19));
1292 sendTouchUpdate(41538, 24, QPointF(186.902,793.19));
1293 sendTouchUpdate(41557, 24, QPointF(186.01,793.19));
1294 sendTouchUpdate(41575, 24, QPointF(185.125,793.444));
1295 sendTouchUpdate(41593, 24, QPointF(184.229,793.69));
1296 sendTouchUpdate(41605, 24, QPointF(183.88,793.69));
1297 sendTouchRelease(41607, 24, QPointF(183.88,793.69));
1298
1299 QCOMPARE(statusSpy->recognized(), true);
1300
1301 delete statusSpy;
1302}
1303
1269QTEST_MAIN(tst_DirectionalDragArea)1304QTEST_MAIN(tst_DirectionalDragArea)
12701305
1271#include "tst_DirectionalDragArea.moc"1306#include "tst_DirectionalDragArea.moc"
12721307
=== modified file 'tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.qml'
--- tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.qml 2014-10-07 13:02:29 +0000
+++ tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.qml 2015-04-10 21:17:56 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013 Canonical, Ltd.2 * Copyright (C) 2013,2015 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * 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 by5 * it under the terms of the GNU General Public License as published by
@@ -16,12 +16,19 @@
1616
17import QtQuick 2.017import QtQuick 2.0
18import Ubuntu.Components 0.118import Ubuntu.Components 0.1
19import Unity.Test 0.1
1920
20Rectangle {21Rectangle {
21 width: units.gu(60)22 width: units.gu(60)
22 height: units.gu(60)23 height: units.gu(60)
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches