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

Proposed by Daniel d'Andrada
Status: Merged
Approved by: Albert Astals Cid
Approved revision: 1718
Merged at revision: 1758
Proposed branch: lp:~dandrader/unity8/ddaImprovements
Merge into: lp:unity8
Diff against target: 5591 lines (+1769/-1521)
49 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 (+362/-363)
plugins/Ubuntu/Gestures/DirectionalDragArea.h (+34/-207)
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 (+3/-2)
qml/Launcher/Launcher.qml (+7/-15)
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/qmltests/tst_Shell.qml (+14/-10)
tests/qmltests/tst_ShellWithPin.qml (+2/-2)
tests/utils/modules/Unity/Test/UnityTestCase.qml (+3/-7)
To merge this branch: bzr merge lp:~dandrader/unity8/ddaImprovements
Reviewer Review Type Date Requested Status
Michael Zanetti (community) Approve
Albert Astals Cid (community) Approve
PS Jenkins bot (community) continuous-integration Needs Fixing
Michał Sawicz Pending
Review via email: mp+255896@code.launchpad.net

This proposal supersedes a proposal from 2015-04-01.

Commit message

DirectionalDragArea: 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.

Description of the change

Fixes 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 : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) wrote : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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

Fixed.

Revision history for this message
Michał Sawicz (saviq) : Posted in a previous version of this proposal
review: Abstain
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

> 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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

right-edge animation start is now smooth as silk

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) wrote : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote : Posted in a previous version of this proposal

> 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.

Revision history for this message
Daniel d'Andrada (dandrader) 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.

Ok, you asked for it: Put all the DDA improvements into this branch.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~dandrader/unity8/ddaImprovements updated
1712. By Daniel d'Andrada

Fix whitespace issues

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

2838- if (status !== DirectionalDragArea.Recognized || launcher.state == "visible")
2839+ if (!dragging || launcher.state == "visible")

This seems to make the launcher jump a little... also design wants to see the launcher's shadow immediately when you touch the edge.

My suggestion would be a "pressed" property in the DirectionalDragArea that could be used for that.

lp:~dandrader/unity8/ddaImprovements updated
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

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)
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> 2838- if (status !== DirectionalDragArea.Recognized ||
> launcher.state == "visible")
> 2839+ if (!dragging || launcher.state == "visible")
>
> This seems to make the launcher jump a little... also design wants to see the
> launcher's shadow immediately when you touch the edge.
>
> My suggestion would be a "pressed" property in the DirectionalDragArea that
> could be used for that.

Added the pressed property. It's needed in the Greeter.
Made Launcher use it although I don't think it makes a difference. What did solve Launcher's jumpiness was making it use DDA.distance instead of DDA.touchX, since the former starts from 0 whereas the latter starts from the X position from the the drag started (eg: touchStartPos.x())

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

qmltests are also passing now.

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

Had another look at this and for what my manual testing and looking and the code says it's fine, next time i would really appreciate if you do not rebase everything into a single commit since it makes reviewing the changes from the last time i did a review much harder since basically i have to look at it all.

Leaving top un-approved to let others have a look.

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

 * Did CI run pass?
Yes, to the extend it passes everywhere else

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

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

Ack. My concerns have been addressed. If Albert is fine with it now, so am I.

review: Approve

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-15 20:21:35 +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-15 20:21:35 +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-15 20:21:35 +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-15 20:21:35 +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-15 20:21:35 +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-15 20:21:35 +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-15 20:21:35 +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-15 20:21:35 +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-15 20:21:35 +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-15 20:21:35 +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-15 20:21:35 +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,168 @@
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::pressed() const
180{
181 return d->status != DirectionalDragAreaPrivate::WaitingForTouch;
182}
183
184bool DirectionalDragArea::immediateRecognition() const
185{
186 return d->immediateRecognition;
187}
188
189void DirectionalDragArea::setImmediateRecognition(bool enabled)
190{
191 if (d->immediateRecognition != enabled) {
192 d->immediateRecognition = enabled;
193 Q_EMIT immediateRecognitionChanged(enabled);
194 }
195}
196
197void DirectionalDragArea::removeTimeConstraints()
198{
199 d->setMaxTime(60 * 60 * 1000);
200 d->compositionTime = 0;
201 ddaDebug("removed time constraints");
236}202}
237203
238bool DirectionalDragArea::event(QEvent *event)204bool DirectionalDragArea::event(QEvent *event)
239{205{
240 if (event->type() == TouchOwnershipEvent::touchOwnershipEventType()) {206 if (event->type() == TouchOwnershipEvent::touchOwnershipEventType()) {
241 touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(event));207 d->touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(event));
242 return true;208 return true;
243 } else if (event->type() == UnownedTouchEvent::unownedTouchEventType()) {209 } else if (event->type() == UnownedTouchEvent::unownedTouchEventType()) {
244 unownedTouchEvent(static_cast<UnownedTouchEvent *>(event));210 d->unownedTouchEvent(static_cast<UnownedTouchEvent *>(event));
245 return true;211 return true;
246 } else {212 } else {
247 return QQuickItem::event(event);213 return QQuickItem::event(event);
248 }214 }
249}215}
250216
251void DirectionalDragArea::touchOwnershipEvent(TouchOwnershipEvent *event)217void DirectionalDragAreaPrivate::touchOwnershipEvent(TouchOwnershipEvent *event)
252{218{
253 if (event->gained()) {219 if (event->gained()) {
254 QVector<int> ids;220 QVector<int> ids;
255 ids.append(event->touchId());221 ids.append(event->touchId());
256 ddaDebug("grabbing touch");222 ddaDebug("grabbing touch");
257 grabTouchPoints(ids);223 q->grabTouchPoints(ids);
258224
259 // Work around for Qt bug. If we grab a touch that is being used for mouse pointer225 // Work around for Qt bug. If we grab a touch that is being used for mouse pointer
260 // emulation it will cause the emulation logic to go nuts.226 // emulation it will cause the emulation logic to go nuts.
@@ -262,35 +228,35 @@
262 //228 //
263 // The fix for this bug has landed in Qt 5.4 (https://codereview.qt-project.org/96887)229 // The fix for this bug has landed in Qt 5.4 (https://codereview.qt-project.org/96887)
264 // TODO: Remove this workaround once we start using Qt 5.4230 // TODO: Remove this workaround once we start using Qt 5.4
265 if (window()) {231 if (q->window()) {
266 QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(window());232 QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(q->window());
267 if (windowPrivate->touchMouseId == event->touchId() && window()->mouseGrabberItem()) {233 if (windowPrivate->touchMouseId == event->touchId() && q->window()->mouseGrabberItem()) {
268 ddaDebug("removing mouse grabber");234 ddaDebug("removing mouse grabber");
269 window()->mouseGrabberItem()->ungrabMouse();235 q->window()->mouseGrabberItem()->ungrabMouse();
270 }236 }
271 }237 }
272 } else {238 } else {
273 // We still wanna know when it ends for keeping the composition time window up-to-date239 // We still wanna know when it ends for keeping the composition time window up-to-date
274 TouchRegistry::instance()->addTouchWatcher(m_touchId, this);240 TouchRegistry::instance()->addTouchWatcher(touchId, q);
275241
276 setStatus(WaitingForTouch);242 setStatus(WaitingForTouch);
277 }243 }
278}244}
279245
280void DirectionalDragArea::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent)246void DirectionalDragAreaPrivate::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent)
281{247{
282 QTouchEvent *event = unownedTouchEvent->touchEvent();248 QTouchEvent *event = unownedTouchEvent->touchEvent();
283249
284 Q_ASSERT(!event->touchPointStates().testFlag(Qt::TouchPointPressed));250 Q_ASSERT(!event->touchPointStates().testFlag(Qt::TouchPointPressed));
285251
286 ddaDebug("Unowned " << m_timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));252 ddaDebug("Unowned " << timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
287253
288 switch (m_status) {254 switch (status) {
289 case WaitingForTouch:255 case WaitingForTouch:
290 // do nothing256 // do nothing
291 break;257 break;
292 case Undecided:258 case Undecided:
293 Q_ASSERT(isEnabled() && isVisible());259 Q_ASSERT(q->isEnabled() && q->isVisible());
294 unownedTouchEvent_undecided(unownedTouchEvent);260 unownedTouchEvent_undecided(unownedTouchEvent);
295 break;261 break;
296 default: // Recognized:262 default: // Recognized:
@@ -298,18 +264,18 @@
298 break;264 break;
299 }265 }
300266
301 m_activeTouches.update(event);267 activeTouches.update(event);
302}268}
303269
304void DirectionalDragArea::unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent)270void DirectionalDragAreaPrivate::unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent)
305{271{
306 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(unownedTouchEvent->touchEvent());272 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(unownedTouchEvent->touchEvent());
307 if (!touchPoint) {273 if (!touchPoint) {
308 qCritical() << "DirectionalDragArea[status=Undecided]: touch " << m_touchId274 qCritical() << "DirectionalDragArea[status=Undecided]: touch " << touchId
309 << "missing from UnownedTouchEvent without first reaching state Qt::TouchPointReleased. "275 << "missing from UnownedTouchEvent without first reaching state Qt::TouchPointReleased. "
310 "Considering it as released.";276 "Considering it as released.";
311277
312 TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);278 TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
313 setStatus(WaitingForTouch);279 setStatus(WaitingForTouch);
314 return;280 return;
315 }281 }
@@ -319,48 +285,42 @@
319 if (touchPoint->state() == Qt::TouchPointReleased) {285 if (touchPoint->state() == Qt::TouchPointReleased) {
320 // touch has ended before recognition concluded286 // touch has ended before recognition concluded
321 ddaDebug("Touch has ended before recognition concluded");287 ddaDebug("Touch has ended before recognition concluded");
322 TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);288 TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
323 emitSignalIfTapped();289 setStatus(WaitingForTouch);
324 setStatus(WaitingForTouch);290 return;
325 return;291 }
326 }292
327293 previousDampedScenePos.setX(dampedScenePos.x());
328 m_previousDampedScenePos.setX(m_dampedScenePos.x());294 previousDampedScenePos.setY(dampedScenePos.y());
329 m_previousDampedScenePos.setY(m_dampedScenePos.y());295 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 }
341296
342 if (!movingInRightDirection()) {297 if (!movingInRightDirection()) {
343 ddaDebug("Rejecting gesture because touch point is moving in the wrong direction.");298 ddaDebug("Rejecting gesture because touch point is moving in the wrong direction.");
344 TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);299 TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
345 // We still wanna know when it ends for keeping the composition time window up-to-date300 // We still wanna know when it ends for keeping the composition time window up-to-date
346 TouchRegistry::instance()->addTouchWatcher(m_touchId, this);301 TouchRegistry::instance()->addTouchWatcher(touchId, q);
347 setStatus(WaitingForTouch);302 setStatus(WaitingForTouch);
348 return;303 return;
349 }304 }
350305
351 setPreviousPos(touchPoint->pos());
352 setPreviousScenePos(touchScenePos);
353
354 if (isWithinTouchCompositionWindow()) {306 if (isWithinTouchCompositionWindow()) {
355 // There's still time for some new touch to appear and ruin our party as it would be combined307 // There's still time for some new touch to appear and ruin our party as it would be combined
356 // with our m_touchId one and therefore deny the possibility of a single-finger gesture.308 // with our touchId one and therefore deny the possibility of a single-finger gesture.
357 ddaDebug("Sill within composition window. Let's wait more.");309 ddaDebug("Sill within composition window. Let's wait more.");
358 return;310 return;
359 }311 }
360312
361 if (movedFarEnough(touchScenePos)) {313 if (movedFarEnoughAlongGestureAxis()) {
362 TouchRegistry::instance()->requestTouchOwnership(m_touchId, this);314 TouchRegistry::instance()->requestTouchOwnership(touchId, q);
363 setStatus(Recognized);315 setStatus(Recognized);
316 setPublicPos(touchPoint->pos());
317 setPublicScenePos(touchScenePos);
318 } else if (isPastMaxDistance()) {
319 ddaDebug("Rejecting gesture because it went farther than maxDistance without getting recognized.");
320 TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
321 // We still wanna know when it ends for keeping the composition time window up-to-date
322 TouchRegistry::instance()->addTouchWatcher(touchId, q);
323 setStatus(WaitingForTouch);
364 } else {324 } else {
365 ddaDebug("Didn't move far enough yet. Let's wait more.");325 ddaDebug("Didn't move far enough yet. Let's wait more.");
366 }326 }
@@ -371,29 +331,29 @@
371 // TODO: Consider when more than one touch starts in the same event (although it's not possible331 // TODO: Consider when more than one touch starts in the same event (although it's not possible
372 // with Mir's android-input). Have to track them all. Consider it a plus/bonus.332 // with Mir's android-input). Have to track them all. Consider it a plus/bonus.
373333
374 ddaDebug(m_timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));334 ddaDebug(d->timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
375335
376 if (!isEnabled() || !isVisible()) {336 if (!isEnabled() || !isVisible()) {
377 QQuickItem::touchEvent(event);337 QQuickItem::touchEvent(event);
378 return;338 return;
379 }339 }
380340
381 switch (m_status) {341 switch (d->status) {
382 case WaitingForTouch:342 case DirectionalDragAreaPrivate::WaitingForTouch:
383 touchEvent_absent(event);343 d->touchEvent_absent(event);
384 break;344 break;
385 case Undecided:345 case DirectionalDragAreaPrivate::Undecided:
386 touchEvent_undecided(event);346 d->touchEvent_undecided(event);
387 break;347 break;
388 default: // Recognized:348 default: // Recognized:
389 touchEvent_recognized(event);349 d->touchEvent_recognized(event);
390 break;350 break;
391 }351 }
392352
393 m_activeTouches.update(event);353 d->activeTouches.update(event);
394}354}
395355
396void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)356void DirectionalDragAreaPrivate::touchEvent_absent(QTouchEvent *event)
397{357{
398 // TODO: accept/reject is for the whole event, not per touch id. See how that affects us.358 // TODO: accept/reject is for the whole event, not per touch id. See how that affects us.
399359
@@ -423,36 +383,40 @@
423 allGood = false;383 allGood = false;
424 } else {384 } else {
425 // that's our candidate385 // that's our candidate
426 m_touchId = touchPoint.id();
427 newTouchPoint = &touchPoint;386 newTouchPoint = &touchPoint;
428 }387 }
429 }388 }
430 }389 }
431390
432 if (allGood) {391 if (allGood) {
392 allGood = sanityCheckRecognitionProperties();
393 if (!allGood) {
394 qWarning("DirectionalDragArea: recognition properties are wrongly set. Gesture recognition"
395 " is impossible");
396 }
397 }
398
399 if (allGood) {
433 Q_ASSERT(newTouchPoint);400 Q_ASSERT(newTouchPoint);
434401
435 m_startPos = newTouchPoint->pos();402 startPos = newTouchPoint->pos();
436 m_startScenePos = newTouchPoint->scenePos();403 startScenePos = newTouchPoint->scenePos();
437 m_touchId = newTouchPoint->id();404 touchId = newTouchPoint->id();
438 m_dampedScenePos.reset(m_startScenePos);405 dampedScenePos.reset(startScenePos);
439 m_velocityCalculator->setTrackedPosition(0.);406 setPublicPos(startPos);
440 m_velocityCalculator->reset();407
441 m_numSamplesOnLastSpeedCheck = 0;408 setPublicScenePos(startScenePos);
442 m_silenceTime = 0;
443 setPreviousPos(m_startPos);
444 setPreviousScenePos(m_startScenePos);
445 updateSceneDirectionVector();409 updateSceneDirectionVector();
446410
447 if (recognitionIsDisabled()) {411 if (recognitionIsDisabled()) {
448 // Behave like a dumb TouchArea412 // Behave like a dumb TouchArea
449 ddaDebug("Gesture recognition is disabled. Requesting touch ownership immediately.");413 ddaDebug("Gesture recognition is disabled. Requesting touch ownership immediately.");
450 TouchRegistry::instance()->requestTouchOwnership(m_touchId, this);414 TouchRegistry::instance()->requestTouchOwnership(touchId, q);
451 setStatus(Recognized);415 setStatus(Recognized);
452 event->accept();416 event->accept();
453 } else {417 } else {
454 // just monitor the touch points for now.418 // just monitor the touch points for now.
455 TouchRegistry::instance()->addCandidateOwnerForTouch(m_touchId, this);419 TouchRegistry::instance()->addCandidateOwnerForTouch(touchId, q);
456420
457 setStatus(Undecided);421 setStatus(Undecided);
458 // Let the item below have it. We will monitor it and grab it later if a gesture422 // Let the item below have it. We will monitor it and grab it later if a gesture
@@ -465,12 +429,11 @@
465 }429 }
466}430}
467431
468void DirectionalDragArea::touchEvent_undecided(QTouchEvent *event)432void DirectionalDragAreaPrivate::touchEvent_undecided(QTouchEvent *event)
469{433{
470 Q_ASSERT(event->type() == QEvent::TouchBegin);
471 Q_ASSERT(fetchTargetTouchPoint(event) == nullptr);434 Q_ASSERT(fetchTargetTouchPoint(event) == nullptr);
472435
473 // We're not interested in new touch points. We already have our candidate (m_touchId).436 // We're not interested in new touch points. We already have our candidate (touchId).
474 // But we do want to know when those new touches end for keeping the composition time437 // But we do want to know when those new touches end for keeping the composition time
475 // window up-to-date438 // window up-to-date
476 event->ignore();439 event->ignore();
@@ -480,63 +443,60 @@
480 // multi-finger drags are not accepted443 // multi-finger drags are not accepted
481 ddaDebug("Multi-finger drags are not accepted");444 ddaDebug("Multi-finger drags are not accepted");
482445
483 TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);446 TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
484 // We still wanna know when it ends for keeping the composition time window up-to-date447 // We still wanna know when it ends for keeping the composition time window up-to-date
485 TouchRegistry::instance()->addTouchWatcher(m_touchId, this);448 TouchRegistry::instance()->addTouchWatcher(touchId, q);
486449
487 setStatus(WaitingForTouch);450 setStatus(WaitingForTouch);
488 }451 }
489}452}
490453
491void DirectionalDragArea::touchEvent_recognized(QTouchEvent *event)454void DirectionalDragAreaPrivate::touchEvent_recognized(QTouchEvent *event)
492{455{
493 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);456 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);
494457
495 if (!touchPoint) {458 if (!touchPoint) {
496 qCritical() << "DirectionalDragArea[status=Recognized]: touch " << m_touchId459 qCritical() << "DirectionalDragArea[status=Recognized]: touch " << touchId
497 << "missing from QTouchEvent without first reaching state Qt::TouchPointReleased. "460 << "missing from QTouchEvent without first reaching state Qt::TouchPointReleased. "
498 "Considering it as released.";461 "Considering it as released.";
499 setStatus(WaitingForTouch);462 setStatus(WaitingForTouch);
500 } else {463 } else {
501 setPreviousPos(touchPoint->pos());464 setPublicPos(touchPoint->pos());
502 setPreviousScenePos(touchPoint->scenePos());465 setPublicScenePos(touchPoint->scenePos());
503466
504 if (touchPoint->state() == Qt::TouchPointReleased) {467 if (touchPoint->state() == Qt::TouchPointReleased) {
505 emitSignalIfTapped();
506 setStatus(WaitingForTouch);468 setStatus(WaitingForTouch);
507 }469 }
508 }470 }
509}471}
510472
511void DirectionalDragArea::watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints)473void DirectionalDragAreaPrivate::watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints)
512{474{
513 for (int i = 0; i < touchPoints.count(); ++i) {475 for (int i = 0; i < touchPoints.count(); ++i) {
514 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);476 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
515 if (touchPoint.state() == Qt::TouchPointPressed) {477 if (touchPoint.state() == Qt::TouchPointPressed) {
516 TouchRegistry::instance()->addTouchWatcher(touchPoint.id(), this);478 TouchRegistry::instance()->addTouchWatcher(touchPoint.id(), q);
517 }479 }
518 }480 }
519}481}
520482
521bool DirectionalDragArea::recognitionIsDisabled() const483bool DirectionalDragAreaPrivate::recognitionIsDisabled() const
522{484{
523 return distanceThreshold() <= 0 && compositionTime() <= 0;485 return immediateRecognition || (distanceThreshold <= 0 && compositionTime <= 0);
524}486}
525487
526void DirectionalDragArea::emitSignalIfTapped()488bool DirectionalDragAreaPrivate::sanityCheckRecognitionProperties()
527{489{
528 qint64 touchDuration = m_timeSource->msecsSinceReference() - m_activeTouches.touchStartTime(m_touchId);490 return recognitionIsDisabled()
529 if (touchDuration <= maxTapDuration()) {491 || (distanceThreshold < maxDistance && compositionTime < maxTime);
530 Q_EMIT tapped();492}
531 }493
532}494const QTouchEvent::TouchPoint *DirectionalDragAreaPrivate::fetchTargetTouchPoint(QTouchEvent *event)
533
534const QTouchEvent::TouchPoint *DirectionalDragArea::fetchTargetTouchPoint(QTouchEvent *event)
535{495{
536 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();496 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
537 const QTouchEvent::TouchPoint *touchPoint = 0;497 const QTouchEvent::TouchPoint *touchPoint = 0;
538 for (int i = 0; i < touchPoints.size(); ++i) {498 for (int i = 0; i < touchPoints.size(); ++i) {
539 if (touchPoints.at(i).id() == m_touchId) {499 if (touchPoints.at(i).id() == touchId) {
540 touchPoint = &touchPoints.at(i);500 touchPoint = &touchPoints.at(i);
541 break;501 break;
542 }502 }
@@ -544,39 +504,13 @@
544 return touchPoint;504 return touchPoint;
545}505}
546506
547bool DirectionalDragArea::pointInsideAllowedArea() const507bool DirectionalDragAreaPrivate::movingInRightDirection() const
548{508{
549 // NB: Using squared values to avoid computing the square root to find509 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;510 return true;
577 } else {511 } else {
578 QPointF movementVector(m_dampedScenePos.x() - m_previousDampedScenePos.x(),512 QPointF movementVector(dampedScenePos.x() - previousDampedScenePos.x(),
579 m_dampedScenePos.y() - m_previousDampedScenePos.y());513 dampedScenePos.y() - previousDampedScenePos.y());
580514
581 qreal scalarProjection = projectOntoDirectionVector(movementVector);515 qreal scalarProjection = projectOntoDirectionVector(movementVector);
582516
@@ -584,96 +518,95 @@
584 }518 }
585}519}
586520
587bool DirectionalDragArea::movedFarEnough(const QPointF &point) const521bool DirectionalDragAreaPrivate::movedFarEnoughAlongGestureAxis() const
588{522{
589 if (m_distanceThreshold <= 0.) {523 if (distanceThreshold <= 0.) {
590 // distance threshold check is disabled524 // distance threshold check is disabled
591 return true;525 return true;
592 } else {526 } else {
593 QPointF totalMovement(point.x() - m_startScenePos.x(),527 QPointF totalMovement(dampedScenePos.x() - startScenePos.x(),
594 point.y() - m_startScenePos.y());528 dampedScenePos.y() - startScenePos.y());
595529
596 qreal squaredTotalMovSize = totalMovement.x() * totalMovement.x() +530 qreal scalarProjection = projectOntoDirectionVector(totalMovement);
597 totalMovement.y() * totalMovement.y();531
598532 ddaDebug(" movedFarEnoughAlongGestureAxis: scalarProjection=" << scalarProjection
599 return squaredTotalMovSize > m_distanceThresholdSquared;533 << ", distanceThreshold=" << distanceThreshold);
600 }534
601}535 if (direction == Direction::Horizontal) {
602536 return qAbs(scalarProjection) > distanceThreshold;
603void DirectionalDragArea::checkSpeed()537 } else {
604{538 return scalarProjection > distanceThreshold;
605 Q_ASSERT(m_status == Undecided);539 }
606540 }
607 if (m_velocityCalculator->numSamples() >= AxisVelocityCalculator::MIN_SAMPLES_NEEDED) {541}
608 qreal speed = qFabs(m_velocityCalculator->calculate());542
609 qreal minSpeedMsecs = m_minSpeed / 1000.0;543bool DirectionalDragAreaPrivate::isPastMaxDistance() const
610544{
611 if (speed < minSpeedMsecs) {545 QPointF totalMovement(dampedScenePos.x() - startScenePos.x(),
612 ddaDebug("Rejecting gesture because it's below minimum speed.");546 dampedScenePos.y() - startScenePos.y());
613 TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);547
614 TouchRegistry::instance()->addTouchWatcher(m_touchId, this);548 qreal squaredDistance = totalMovement.x()*totalMovement.x() + totalMovement.y()*totalMovement.y();
615 setStatus(WaitingForTouch);549 return squaredDistance > maxDistance*maxDistance;
616 }550}
617 }551
618552void DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible()
619 if (m_velocityCalculator->numSamples() == m_numSamplesOnLastSpeedCheck) {553{
620 m_silenceTime += m_recognitionTimer->interval();554 if (!q->isEnabled() || !q->isVisible()) {
621555 if (status == Undecided) {
622 if (m_silenceTime > m_maxSilenceTime) {556 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-date557 // We still wanna know when it ends for keeping the composition time window up-to-date
640 TouchRegistry::instance()->addTouchWatcher(m_touchId, this);558 TouchRegistry::instance()->addTouchWatcher(touchId, q);
641 }559 }
642560
643 if (m_status != WaitingForTouch) {561 if (status != WaitingForTouch) {
644 ddaDebug("Resetting status because got disabled or made invisible");562 ddaDebug("Resetting status because got disabled or made invisible");
645 setStatus(WaitingForTouch);563 setStatus(WaitingForTouch);
646 }564 }
647 }565 }
648}566}
649567
650void DirectionalDragArea::setStatus(DirectionalDragArea::Status newStatus)568void DirectionalDragAreaPrivate::rejectGesture()
651{569{
652 if (newStatus == m_status)570 if (status == Undecided) {
571 ddaDebug("Rejecting gesture because it's taking too long to drag beyond the threshold.");
572
573 TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
574 // We still wanna know when it ends for keeping the composition time window up-to-date
575 TouchRegistry::instance()->addTouchWatcher(touchId, q);
576
577 setStatus(WaitingForTouch);
578 }
579}
580
581void DirectionalDragAreaPrivate::setStatus(Status newStatus)
582{
583 if (newStatus == status)
653 return;584 return;
654585
655 DirectionalDragArea::Status oldStatus = m_status;586 Status oldStatus = status;
656587
657 if (oldStatus == Undecided) {588 if (oldStatus == Undecided) {
658 m_recognitionTimer->stop();589 recognitionTimer->stop();
659 }590 }
660591
661 m_status = newStatus;592 status = newStatus;
662 Q_EMIT statusChanged(m_status);593 Q_EMIT statusChanged(status);
663594
664 ddaDebug(statusToString(oldStatus) << " -> " << statusToString(newStatus));595 ddaDebug(statusToString(oldStatus) << " -> " << statusToString(newStatus));
665596
666 switch (newStatus) {597 switch (newStatus) {
667 case WaitingForTouch:598 case WaitingForTouch:
668 Q_EMIT draggingChanged(false);599 if (oldStatus == Recognized) {
600 Q_EMIT q->draggingChanged(false);
601 }
602 Q_EMIT q->pressedChanged(false);
669 break;603 break;
670 case Undecided:604 case Undecided:
671 m_recognitionTimer->start();605 recognitionTimer->start();
672 Q_EMIT draggingChanged(true);606 Q_EMIT q->pressedChanged(true);
673 break;607 break;
674 case Recognized:608 case Recognized:
675 if (oldStatus == WaitingForTouch)609 Q_EMIT q->draggingChanged(true);
676 Q_EMIT draggingChanged(true);
677 break;610 break;
678 default:611 default:
679 // no-op612 // no-op
@@ -681,77 +614,126 @@
681 }614 }
682}615}
683616
684void DirectionalDragArea::setPreviousPos(const QPointF &point)617void DirectionalDragAreaPrivate::setPublicPos(const QPointF &point)
685{618{
686 bool xChanged = m_previousPos.x() != point.x();619 bool xChanged = publicPos.x() != point.x();
687 bool yChanged = m_previousPos.y() != point.y();620 bool yChanged = publicPos.y() != point.y();
688621
689 m_previousPos = point;622 // Public position should not get updated while the gesture is still being recognized
623 // (ie, Undecided status).
624 Q_ASSERT(status == WaitingForTouch || status == Recognized);
625
626 if (status == Recognized && !recognitionIsDisabled()) {
627 // When the gesture finally gets recognized, the finger will likely be
628 // reasonably far from the edge. If we made the contentX immediately
629 // follow the finger position it would be visually unpleasant as it
630 // would appear right next to the user's finger out of nowhere (ie,
631 // it would jump). Instead, we make contentX go towards the user's
632 // finger in several steps. ie., in an animated way.
633 QPointF delta = point - publicPos;
634 // the trick is not to go all the way (1.0) as it would cause a sudden jump
635 publicPos.rx() += 0.4 * delta.x();
636 publicPos.ry() += 0.4 * delta.y();
637 } else {
638 // no smoothing when initializing or if gesture recognition was immediate as there will
639 // be no jump.
640 publicPos = point;
641 }
690642
691 if (xChanged) {643 if (xChanged) {
692 Q_EMIT touchXChanged(point.x());644 Q_EMIT q->touchXChanged(publicPos.x());
693 if (Direction::isHorizontal(m_direction))645 if (Direction::isHorizontal(direction))
694 Q_EMIT distanceChanged(distance());646 Q_EMIT q->distanceChanged(q->distance());
695 }647 }
696648
697 if (yChanged) {649 if (yChanged) {
698 Q_EMIT touchYChanged(point.y());650 Q_EMIT q->touchYChanged(publicPos.y());
699 if (Direction::isVertical(m_direction))651 if (Direction::isVertical(direction))
700 Q_EMIT distanceChanged(distance());652 Q_EMIT q->distanceChanged(q->distance());
701 }653 }
702}654}
703655
704void DirectionalDragArea::setPreviousScenePos(const QPointF &point)656void DirectionalDragAreaPrivate::setPublicScenePos(const QPointF &point)
705{657{
706 bool xChanged = m_previousScenePos.x() != point.x();658 bool xChanged = publicScenePos.x() != point.x();
707 bool yChanged = m_previousScenePos.y() != point.y();659 bool yChanged = publicScenePos.y() != point.y();
708660
709 if (!xChanged && !yChanged)661 if (!xChanged && !yChanged)
710 return;662 return;
711663
712 qreal oldSceneDistance = sceneDistance();664 // Public position should not get updated while the gesture is still being recognized
713 m_previousScenePos = point;665 // (ie, Undecided status).
666 Q_ASSERT(status == WaitingForTouch || status == Recognized);
667
668 qreal oldSceneDistance = sceneDistance;
669
670 if (status == Recognized && !recognitionIsDisabled()) {
671 // When the gesture finally gets recognized, the finger will likely be
672 // reasonably far from the edge. If we made the contentX immediately
673 // follow the finger position it would be visually unpleasant as it
674 // would appear right next to the user's finger out of nowhere (ie,
675 // it would jump). Instead, we make contentX go towards the user's
676 // finger in several steps. ie., in an animated way.
677 QPointF delta = point - publicScenePos;
678 // the trick is not to go all the way (1.0) as it would cause a sudden jump
679 publicScenePos.rx() += 0.4 * delta.x();
680 publicScenePos.ry() += 0.4 * delta.y();
681 } else {
682 // no smoothing when initializing or if gesture recognition was immediate as there will
683 // be no jump.
684 publicScenePos = point;
685 }
686
714 updateSceneDistance();687 updateSceneDistance();
715688
716 if (oldSceneDistance != sceneDistance()) {689 if (oldSceneDistance != sceneDistance) {
717 Q_EMIT sceneDistanceChanged(sceneDistance());690 Q_EMIT q->sceneDistanceChanged(sceneDistance);
718 }691 }
719692
720 if (xChanged) {693 if (xChanged) {
721 Q_EMIT touchSceneXChanged(point.x());694 Q_EMIT q->touchSceneXChanged(publicScenePos.x());
722 }695 }
723696
724 if (yChanged) {697 if (yChanged) {
725 Q_EMIT touchSceneYChanged(point.y());698 Q_EMIT q->touchSceneYChanged(publicScenePos.y());
726 }699 }
727}700}
728701
729void DirectionalDragArea::updateVelocityCalculator(const QPointF &scenePos)702bool 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{703{
740 return704 return
741 compositionTime() > 0 &&705 compositionTime > 0 &&
742 !m_activeTouches.isEmpty() &&706 !activeTouches.isEmpty() &&
743 m_timeSource->msecsSinceReference() <=707 timeSource->msecsSinceReference() <=
744 m_activeTouches.mostRecentStartTime() + (qint64)compositionTime();708 activeTouches.mostRecentStartTime() + (qint64)compositionTime;
709}
710
711void DirectionalDragArea::itemChange(ItemChange change, const ItemChangeData &value)
712{
713 if (change == QQuickItem::ItemSceneChange) {
714 if (value.window != nullptr) {
715 // TODO: Handle window->screen() changes (ie window changing screens)
716 qreal pixelsPerMm = value.window->screen()->physicalDotsPerInch() / 25.4;
717 d->setPixelsPerMm(pixelsPerMm);
718 }
719 }
720}
721
722void DirectionalDragAreaPrivate::setPixelsPerMm(qreal pixelsPerMm)
723{
724 dampedScenePos.setMaxDelta(1. * pixelsPerMm);
725 setDistanceThreshold(4. * pixelsPerMm);
726 maxDistance = 10. * pixelsPerMm;
745}727}
746728
747//************************** ActiveTouchesInfo **************************729//************************** ActiveTouchesInfo **************************
748730
749DirectionalDragArea::ActiveTouchesInfo::ActiveTouchesInfo(const SharedTimeSource &timeSource)731ActiveTouchesInfo::ActiveTouchesInfo(const SharedTimeSource &timeSource)
750 : m_timeSource(timeSource)732 : m_timeSource(timeSource)
751{733{
752}734}
753735
754void DirectionalDragArea::ActiveTouchesInfo::update(QTouchEvent *event)736void ActiveTouchesInfo::update(QTouchEvent *event)
755{737{
756 if (!(event->touchPointStates() & (Qt::TouchPointPressed | Qt::TouchPointReleased))) {738 if (!(event->touchPointStates() & (Qt::TouchPointPressed | Qt::TouchPointReleased))) {
757 // nothing to update739 // nothing to update
@@ -773,7 +755,7 @@
773}755}
774756
775#if ACTIVETOUCHESINFO_DEBUG757#if ACTIVETOUCHESINFO_DEBUG
776QString DirectionalDragArea::ActiveTouchesInfo::toString()758QString ActiveTouchesInfo::toString()
777{759{
778 QString string = "(";760 QString string = "(";
779761
@@ -791,7 +773,7 @@
791}773}
792#endif // ACTIVETOUCHESINFO_DEBUG774#endif // ACTIVETOUCHESINFO_DEBUG
793775
794void DirectionalDragArea::ActiveTouchesInfo::addTouchPoint(int touchId)776void ActiveTouchesInfo::addTouchPoint(int touchId)
795{777{
796 ActiveTouchInfo &activeTouchInfo = m_touchInfoPool.getEmptySlot();778 ActiveTouchInfo &activeTouchInfo = m_touchInfoPool.getEmptySlot();
797 activeTouchInfo.id = touchId;779 activeTouchInfo.id = touchId;
@@ -802,7 +784,7 @@
802 #endif784 #endif
803}785}
804786
805qint64 DirectionalDragArea::ActiveTouchesInfo::touchStartTime(int touchId)787qint64 ActiveTouchesInfo::touchStartTime(int touchId)
806{788{
807 qint64 result = -1;789 qint64 result = -1;
808790
@@ -819,7 +801,7 @@
819 return result;801 return result;
820}802}
821803
822void DirectionalDragArea::ActiveTouchesInfo::removeTouchPoint(int touchId)804void ActiveTouchesInfo::removeTouchPoint(int touchId)
823{805{
824 m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {806 m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {
825 if (touchId == touchInfo->id) {807 if (touchId == touchInfo->id) {
@@ -835,7 +817,7 @@
835 #endif817 #endif
836}818}
837819
838qint64 DirectionalDragArea::ActiveTouchesInfo::mostRecentStartTime()820qint64 ActiveTouchesInfo::mostRecentStartTime()
839{821{
840 Q_ASSERT(!m_touchInfoPool.isEmpty());822 Q_ASSERT(!m_touchInfoPool.isEmpty());
841823
@@ -851,11 +833,11 @@
851 return highestStartTime;833 return highestStartTime;
852}834}
853835
854void DirectionalDragArea::updateSceneDirectionVector()836void DirectionalDragAreaPrivate::updateSceneDirectionVector()
855{837{
856 QPointF localOrigin(0., 0.);838 QPointF localOrigin(0., 0.);
857 QPointF localDirection;839 QPointF localDirection;
858 switch (m_direction) {840 switch (direction) {
859 case Direction::Upwards:841 case Direction::Upwards:
860 localDirection.rx() = 0.;842 localDirection.rx() = 0.;
861 localDirection.ry() = -1.;843 localDirection.ry() = -1.;
@@ -873,14 +855,31 @@
873 localDirection.ry() = 0.;855 localDirection.ry() = 0.;
874 break;856 break;
875 }857 }
876 QPointF sceneOrigin = mapToScene(localOrigin);858 QPointF sceneOrigin = q->mapToScene(localOrigin);
877 QPointF sceneDirection = mapToScene(localDirection);859 QPointF sceneDirection = q->mapToScene(localDirection);
878 m_sceneDirectionVector = sceneDirection - sceneOrigin;860 sceneDirectionVector = sceneDirection - sceneOrigin;
879}861}
880862
881qreal DirectionalDragArea::projectOntoDirectionVector(const QPointF &sceneVector) const863qreal DirectionalDragAreaPrivate::projectOntoDirectionVector(const QPointF &sceneVector) const
882{864{
883 // same as dot product as m_sceneDirectionVector is a unit vector865 // same as dot product as sceneDirectionVector is a unit vector
884 return sceneVector.x() * m_sceneDirectionVector.x() +866 return sceneVector.x() * sceneDirectionVector.x() +
885 sceneVector.y() * m_sceneDirectionVector.y();867 sceneVector.y() * sceneDirectionVector.y();
868}
869
870DirectionalDragAreaPrivate::DirectionalDragAreaPrivate(DirectionalDragArea *q)
871 : q(q)
872 , status(WaitingForTouch)
873 , sceneDistance(0)
874 , touchId(-1)
875 , direction(Direction::Rightwards)
876 , distanceThreshold(0)
877 , distanceThresholdSquared(0.)
878 , maxTime(400)
879 , compositionTime(60)
880 , immediateRecognition(false)
881 , recognitionTimer(nullptr)
882 , timeSource(new RealTimeSource)
883 , activeTouches(timeSource)
884{
886}885}
887886
=== 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-15 20:21:35 +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,31 @@
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 the drag area is pressed.
73 // stuff that will be set in stone at some point68 Q_PROPERTY(bool pressed READ pressed NOTIFY pressedChanged)
7469
75 // How far the touch point can move away from its expected position before70 // Whether a gesture should be Recognized as soon a touch lands on the area.
76 // it causes a rejection in the gesture recognition. This is to compensate71 // With this property enabled it will work pretty much like a MultiPointTouchArea,
77 // for both noise in the touch input signal and for the natural irregularities72 // just with a different API.
78 // in the finger movement.73 //
79 // Proper value is likely device-specific.74 // It's false by default. In most cases you will not want that enabled.
80 Q_PROPERTY(qreal maxDeviation READ maxDeviation WRITE setMaxDeviation NOTIFY maxDeviationChanged)75 Q_PROPERTY(bool immediateRecognition
8176 READ immediateRecognition
82 // Widening angle, in degrees77 WRITE setImmediateRecognition
83 // It's roughly the maximum angle a touch point can make relative to the78 NOTIFY immediateRecognitionChanged)
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)
11979
120 Q_ENUMS(Direction)80 Q_ENUMS(Direction)
121 Q_ENUMS(Status)
122public:81public:
123 DirectionalDragArea(QQuickItem *parent = 0);82 DirectionalDragArea(QQuickItem *parent = 0);
12483
125 Direction::Type direction() const;84 Direction::Type direction() const;
126 void setDirection(Direction::Type);85 void setDirection(Direction::Type);
12786
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;87 qreal distance() const;
151 qreal sceneDistance() const;88 qreal sceneDistance() const;
152 void updateSceneDistance();
15389
154 qreal touchX() const;90 qreal touchX() const;
155 qreal touchY() const;91 qreal touchY() const;
@@ -157,152 +93,43 @@
157 qreal touchSceneX() const;93 qreal touchSceneX() const;
158 qreal touchSceneY() const;94 qreal touchSceneY() const;
15995
160 bool dragging() const { return (m_status == Undecided) || (m_status == Recognized); }96 bool dragging() const;
16197
162 qreal maxDeviation() const { return m_dampedScenePos.maxDelta(); }98 bool pressed() const;
163 void setMaxDeviation(qreal value);99
164100 bool immediateRecognition() const;
165 qreal wideningAngle() const;101 void setImmediateRecognition(bool enabled);
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);
187102
188 bool event(QEvent *e) override;103 bool event(QEvent *e) override;
189104
190 // Maximum time, in milliseconds, between a press and a release, for a touch105 /*
191 // sequence to be considered a tap.106 In qmltests, sequences of touch events are sent all at once, unlike in "real life".
192 int maxTapDuration() const { return 300; }107 Also qmltests might run really slowly, e.g. when run from inside virtual machines.
108 Thus to remove a variable that qmltests cannot really control, namely time, this
109 function removes all constraints that are sensible to elapsed time.
110
111 This effectively makes the DirectionalDragArea easier to fool.
112 */
113 Q_INVOKABLE void removeTimeConstraints();
193114
194Q_SIGNALS:115Q_SIGNALS:
195 void directionChanged(Direction::Type direction);116 void directionChanged(Direction::Type direction);
196 void statusChanged(Status value);
197 void draggingChanged(bool value);117 void draggingChanged(bool value);
118 void pressedChanged(bool value);
198 void distanceChanged(qreal value);119 void distanceChanged(qreal value);
199 void sceneDistanceChanged(qreal value);120 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);121 void touchXChanged(qreal value);
207 void touchYChanged(qreal value);122 void touchYChanged(qreal value);
208 void touchSceneXChanged(qreal value);123 void touchSceneXChanged(qreal value);
209 void touchSceneYChanged(qreal value);124 void touchSceneYChanged(qreal value);
210125 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();
215126
216protected:127protected:
217 virtual void touchEvent(QTouchEvent *event);128 virtual void touchEvent(QTouchEvent *event);
218129 virtual void itemChange(ItemChange change, const ItemChangeData &value);
219private Q_SLOTS:130
220 void checkSpeed();131public: // so tests can access it
221 void giveUpIfDisabledOrInvisible();132 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};133};
307134
308#endif // DIRECTIONAL_DRAG_AREA_H135#endif // DIRECTIONAL_DRAG_AREA_H
309136
=== 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-15 20:21:35 +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-15 20:21:35 +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-15 20:21:35 +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-03-12 13:52:35 +0000
+++ plugins/Ubuntu/Gestures/TouchGate.cpp 2015-04-15 20:21:35 +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
@@ -55,31 +64,41 @@
55 TouchRegistry::instance()->requestTouchOwnership(touchPoint.id(), this);64 TouchRegistry::instance()->requestTouchOwnership(touchPoint.id(), this);
56 }65 }
5766
58 goodToGo &= m_touchInfoMap.contains(touchPoint.id())67 if (m_touchInfoMap.contains(touchPoint.id())) {
59 && m_touchInfoMap[touchPoint.id()].ownership == OwnershipGranted;68 validTouchPoints.append(touchPoint);
6069
61 if (touchPoint.state() == Qt::TouchPointReleased && m_touchInfoMap.contains(touchPoint.id())) {70 ownsAllTouches &= m_touchInfoMap[touchPoint.id()].ownership == OwnershipGranted;
62 m_touchInfoMap[touchPoint.id()].ended = true;71
72 if (touchPoint.state() == Qt::TouchPointReleased) {
73 m_touchInfoMap[touchPoint.id()].ended = true;
74 }
63 }75 }
6476
65 }77 }
6678
67 if (goodToGo) {79 if (validTouchPoints.isEmpty()) {
80 // nothing to do.
81 return;
82 }
83
84 if (ownsAllTouches) {
68 if (m_storedEvents.isEmpty()) {85 if (m_storedEvents.isEmpty()) {
69 // let it pass through86 // let it pass through
70 dispatchTouchEventToTarget(event);87 removeTouchInfoForEndedTouches(validTouchPoints);
88 m_dispatcher.dispatch(event->device(), event->modifiers(), validTouchPoints,
89 event->window(), event->timestamp());
71 } else {90 } else {
72 // Retain the event to ensure TouchGate dispatches them in order.91 // Retain the event to ensure TouchGate dispatches them in order.
73 // Otherwise the current event would come before the stored ones, which are older.92 // Otherwise the current event would come before the stored ones, which are older.
74 #if TOUCHGATE_DEBUG93 ugDebug("Storing event because thouches " << qPrintable(oldestPendingTouchIdsString())
75 qDebug("[TouchGate] Storing event because thouches %s are still pending ownership.",94 << " are still pending ownership.");
76 qPrintable(oldestPendingTouchIdsString()));95 storeTouchEvent(event->device(), event->modifiers(), validTouchPoints,
77 #endif96 event->window(), event->timestamp());
78 storeTouchEvent(event);
79 }97 }
80 } else {98 } else {
81 // Retain events that have unowned touches99 // Retain events that have unowned touches
82 storeTouchEvent(event);100 storeTouchEvent(event->device(), event->modifiers(), validTouchPoints,
101 event->window(), event->timestamp());
83 }102 }
84}103}
85104
@@ -88,24 +107,23 @@
88 // TODO: Optimization: batch those actions as TouchOwnershipEvents107 // TODO: Optimization: batch those actions as TouchOwnershipEvents
89 // might come one right after the other.108 // might come one right after the other.
90109
91 Q_ASSERT(m_touchInfoMap.contains(event->touchId()));110 if (m_touchInfoMap.contains(event->touchId())) {
92111 TouchInfo &touchInfo = m_touchInfoMap[event->touchId()];
93 TouchInfo &touchInfo = m_touchInfoMap[event->touchId()];112
94113 if (event->gained()) {
95 if (event->gained()) {114 ugDebug("Got ownership of touch " << event->touchId());
96 #if TOUCHGATE_DEBUG115 touchInfo.ownership = OwnershipGranted;
97 qDebug() << "[TouchGate] Got ownership of touch " << event->touchId();116 } else {
98 #endif117 ugDebug("Lost ownership of touch " << event->touchId());
99 touchInfo.ownership = OwnershipGranted;118 m_touchInfoMap.remove(event->touchId());
119 removeTouchFromStoredEvents(event->touchId());
120 }
121
122 dispatchFullyOwnedEvents();
100 } else {123 } else {
101 #if TOUCHGATE_DEBUG124 // Ignore it. It probably happened because the TouchGate got disabled
102 qDebug() << "[TouchGate] Lost ownership of touch " << event->touchId();125 // between the time it requested ownership and the time it got it.
103 #endif
104 m_touchInfoMap.remove(event->touchId());
105 removeTouchFromStoredEvents(event->touchId());
106 }126 }
107
108 dispatchFullyOwnedEvents();
109}127}
110128
111bool TouchGate::isTouchPointOwned(int touchId) const129bool TouchGate::isTouchPointOwned(int touchId) const
@@ -113,14 +131,15 @@
113 return m_touchInfoMap[touchId].ownership == OwnershipGranted;131 return m_touchInfoMap[touchId].ownership == OwnershipGranted;
114}132}
115133
116void TouchGate::storeTouchEvent(const QTouchEvent *event)134void TouchGate::storeTouchEvent(QTouchDevice *device,
135 Qt::KeyboardModifiers modifiers,
136 const QList<QTouchEvent::TouchPoint> &touchPoints,
137 QWindow *window,
138 ulong timestamp)
117{139{
118 #if TOUCHGATE_DEBUG140 ugDebug("Storing" << touchPoints);
119 qDebug() << "[TouchGate] Storing" << qPrintable(touchEventToString(event));141 TouchEvent event(device, modifiers, touchPoints, window, timestamp);
120 #endif142 m_storedEvents.append(std::move(event));
121
122 TouchEvent clonedEvent(event);
123 m_storedEvents.append(std::move(clonedEvent));
124}143}
125144
126void TouchGate::removeTouchFromStoredEvents(int touchId)145void TouchGate::removeTouchFromStoredEvents(int touchId)
@@ -193,25 +212,13 @@
193void TouchGate::dispatchTouchEventToTarget(const TouchEvent &event)212void TouchGate::dispatchTouchEventToTarget(const TouchEvent &event)
194{213{
195 removeTouchInfoForEndedTouches(event.touchPoints);214 removeTouchInfoForEndedTouches(event.touchPoints);
196 m_dispatcher.dispatch(event.eventType,215 m_dispatcher.dispatch(event.device,
197 event.device,
198 event.modifiers,216 event.modifiers,
199 event.touchPoints,217 event.touchPoints,
200 event.window,218 event.window,
201 event.timestamp);219 event.timestamp);
202}220}
203221
204void TouchGate::dispatchTouchEventToTarget(QTouchEvent* event)
205{
206 removeTouchInfoForEndedTouches(event->touchPoints());
207 m_dispatcher.dispatch(event->type(),
208 event->device(),
209 event->modifiers(),
210 event->touchPoints(),
211 event->window(),
212 event->timestamp());
213}
214
215void TouchGate::removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints)222void TouchGate::removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints)
216{223{
217 for (int i = 0; i < touchPoints.size(); ++i) {\224 for (int i = 0; i < touchPoints.size(); ++i) {\
@@ -226,14 +233,31 @@
226 }233 }
227}234}
228235
229TouchGate::TouchEvent::TouchEvent(const QTouchEvent *event)236void TouchGate::onEnabledChanged()
230 : eventType(event->type())237{
231 , device(event->device())238 ugDebug(" enabled = " << isEnabled());
232 , modifiers(event->modifiers())239 if (!isEnabled()) {
233 , touchPoints(event->touchPoints())240 reset();
234 , target(qobject_cast<QQuickItem*>(event->target()))241 }
235 , window(event->window())242}
236 , timestamp(event->timestamp())243
244void TouchGate::reset()
245{
246 m_storedEvents.clear();
247 m_touchInfoMap.clear();
248 m_dispatcher.reset();
249}
250
251TouchGate::TouchEvent::TouchEvent(QTouchDevice *device,
252 Qt::KeyboardModifiers modifiers,
253 const QList<QTouchEvent::TouchPoint> &touchPoints,
254 QWindow *window,
255 ulong timestamp)
256 : device(device)
257 , modifiers(modifiers)
258 , touchPoints(touchPoints)
259 , window(window)
260 , timestamp(timestamp)
237{261{
238}262}
239263
240264
=== modified file 'plugins/Ubuntu/Gestures/TouchGate.h'
--- plugins/Ubuntu/Gestures/TouchGate.h 2015-03-12 13:52:35 +0000
+++ plugins/Ubuntu/Gestures/TouchGate.h 2015-04-15 20:21:35 +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(); }
@@ -55,31 +57,42 @@
5557
56protected:58protected:
57 void touchEvent(QTouchEvent *event) override;59 void touchEvent(QTouchEvent *event) override;
60
61private Q_SLOTS:
62 void onEnabledChanged();
63
58private:64private:
65 void reset();
66
59 class TouchEvent {67 class TouchEvent {
60 public:68 public:
61 TouchEvent(const QTouchEvent *event);69 TouchEvent(QTouchDevice *device,
70 Qt::KeyboardModifiers modifiers,
71 const QList<QTouchEvent::TouchPoint> &touchPoints,
72 QWindow *window,
73 ulong timestamp);
6274
63 bool removeTouch(int touchId);75 bool removeTouch(int touchId);
6476
65 QEvent::Type eventType;
66 QTouchDevice *device;77 QTouchDevice *device;
67 Qt::KeyboardModifiers modifiers;78 Qt::KeyboardModifiers modifiers;
68 QList<QTouchEvent::TouchPoint> touchPoints;79 QList<QTouchEvent::TouchPoint> touchPoints;
69 QQuickItem *target;
70 QWindow *window;80 QWindow *window;
71 ulong timestamp;81 ulong timestamp;
72 };82 };
7383
74 void touchOwnershipEvent(TouchOwnershipEvent *event);84 void touchOwnershipEvent(TouchOwnershipEvent *event);
75 bool isTouchPointOwned(int touchId) const;85 bool isTouchPointOwned(int touchId) const;
76 void storeTouchEvent(const QTouchEvent *event);86 void storeTouchEvent(QTouchDevice *device,
87 Qt::KeyboardModifiers modifiers,
88 const QList<QTouchEvent::TouchPoint> &touchPoints,
89 QWindow *window,
90 ulong timestamp);
77 void removeTouchFromStoredEvents(int touchId);91 void removeTouchFromStoredEvents(int touchId);
78 void dispatchFullyOwnedEvents();92 void dispatchFullyOwnedEvents();
79 bool eventIsFullyOwned(const TouchEvent &event) const;93 bool eventIsFullyOwned(const TouchEvent &event) const;
8094
81 void dispatchTouchEventToTarget(const TouchEvent &event);95 void dispatchTouchEventToTarget(const TouchEvent &event);
82 void dispatchTouchEventToTarget(QTouchEvent* event);
8396
84 void removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints);97 void removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints);
8598
8699
=== modified file 'qml/Components/DragHandle.qml'
--- qml/Components/DragHandle.qml 2014-12-05 17:06:36 +0000
+++ qml/Components/DragHandle.qml 2015-04-15 20:21:35 +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-04-09 14:00:37 +0000
+++ qml/Dash/Dash.qml 2015-04-15 20:21:35 +0000
@@ -153,7 +153,7 @@
153 // (as expected) but would also cause the dash content flickable to move a bit, because153 // (as expected) but would also cause the dash content flickable to move a bit, because
154 // that flickable was getting the touch events while overviewDragHandle was still undecided154 // that flickable was getting the touch events while overviewDragHandle was still undecided
155 // about whether that touch was indeed performing a directional drag gesture.155 // about whether that touch was indeed performing a directional drag gesture.
156 forceNonInteractive: overviewDragHandle.status != DirectionalDragArea.WaitingForTouch156 forceNonInteractive: overviewDragHandle.dragging
157157
158 enabled: bottomEdgeController.progress == 0158 enabled: bottomEdgeController.progress == 0
159 }159 }
@@ -330,7 +330,7 @@
330 }330 }
331 }331 }
332332
333 EdgeDragArea {333 DirectionalDragArea {
334 id: overviewDragHandle334 id: overviewDragHandle
335 objectName: "overviewDragHandle"335 objectName: "overviewDragHandle"
336 z: 1336 z: 1
@@ -346,21 +346,14 @@
346 height: units.gu(2)346 height: units.gu(2)
347347
348 onSceneDistanceChanged: {348 onSceneDistanceChanged: {
349 if (status == DirectionalDragArea.Recognized) {349 if (dragging) {
350 bottomEdgeController.enableAnimation = false;350 bottomEdgeController.enableAnimation = false;
351 bottomEdgeController.progress = Math.max(0, Math.min(1, sceneDistance / fullMovement));351 bottomEdgeController.progress = Math.max(0, Math.min(1, sceneDistance / fullMovement));
352 }352 }
353 }353 }
354354
355 property int previousStatus: -1355 onDraggingChanged: {
356 property int currentStatus: DirectionalDragArea.WaitingForTouch356 if (!dragging) {
357
358 onStatusChanged: {
359 previousStatus = currentStatus;
360 currentStatus = status;
361
362 if (status == DirectionalDragArea.WaitingForTouch &&
363 previousStatus == DirectionalDragArea.Recognized) {
364 bottomEdgeController.enableAnimation = true;357 bottomEdgeController.enableAnimation = true;
365 bottomEdgeController.progress = (bottomEdgeController.progress > 0.2) ? 1 : 0;358 bottomEdgeController.progress = (bottomEdgeController.progress > 0.2) ? 1 : 0;
366 }359 }
367360
=== modified file 'qml/Greeter/CoverPage.qml'
--- qml/Greeter/CoverPage.qml 2015-02-17 15:54:17 +0000
+++ qml/Greeter/CoverPage.qml 2015-04-15 20:21:35 +0000
@@ -129,13 +129,14 @@
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
135 direction: Direction.Horizontal136 direction: Direction.Horizontal
136137
137 onDraggingChanged: {138 onPressedChanged: {
138 if (dragging) {139 if (pressed) {
139 root.tease();140 root.tease();
140 showLabelAnimation.start();141 showLabelAnimation.start();
141 }142 }
142143
=== modified file 'qml/Launcher/Launcher.qml'
--- qml/Launcher/Launcher.qml 2015-03-12 13:04:36 +0000
+++ qml/Launcher/Launcher.qml 2015-04-15 20:21:35 +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 || dragArea.pressed
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
@@ -283,20 +283,11 @@
283 width: root.dragAreaWidth283 width: root.dragAreaWidth
284 height: root.height284 height: root.height
285285
286 onTouchXChanged: {286 onDistanceChanged: {
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 = -panel.width + Math.min(Math.max(0, distance), panel.width);
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-04-02 15:29:10 +0000
+++ qml/Panel/IndicatorsMenu.qml 2015-04-15 20:21:35 +0000
@@ -186,9 +186,18 @@
186 enabled: !root.shown && root.available186 enabled: !root.shown && root.available
187 autoCompleteDragThreshold: maxTotalDragDistance / 2187 autoCompleteDragThreshold: maxTotalDragDistance / 2
188 stretch: true188 stretch: true
189 distanceThreshold: enableHint ? 0 : minimizedPanelHeight
190189
191 onTapped: showTapped(Qt.point(touchSceneX, touchSceneY));190 onPressedChanged: {
191 if (pressed) {
192 touchPressTime = new Date().getTime();
193 } else {
194 var touchReleaseTime = new Date().getTime();
195 if (touchReleaseTime - touchPressTime <= 300) {
196 root.showTapped(Qt.point(touchSceneX, touchSceneY));
197 }
198 }
199 }
200 property var touchPressTime
192201
193 // using hint regulates minimum to hint displacement, but in fullscreen mode, we need to do it manually.202 // using hint regulates minimum to hint displacement, but in fullscreen mode, we need to do it manually.
194 overrideStartValue: enableHint ? minimizedPanelHeight : expandedPanelHeight + handle.height203 overrideStartValue: enableHint ? minimizedPanelHeight : expandedPanelHeight + handle.height
@@ -204,6 +213,7 @@
204213
205 DragHandle {214 DragHandle {
206 id: __hideDragHandle215 id: __hideDragHandle
216 objectName: "hideDragHandle"
207 anchors.fill: handle217 anchors.fill: handle
208 direction: Direction.Upwards218 direction: Direction.Upwards
209 enabled: root.shown && root.available219 enabled: root.shown && root.available
210220
=== modified file 'qml/Stages/PhoneStage.qml'
--- qml/Stages/PhoneStage.qml 2015-02-18 18:29:03 +0000
+++ qml/Stages/PhoneStage.qml 2015-04-15 20:21:35 +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-15 20:21:35 +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-15 20:21:35 +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-15 20:21:35 +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-15 20:21:35 +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-15 20:21:35 +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-15 20:21:35 +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-15 20:21:35 +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-15 20:21:35 +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-15 20:21:35 +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-15 20:21:35 +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-15 20:21:35 +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));
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches