Merge lp:~dandrader/unity8/touchOwnership into lp:unity8
- touchOwnership
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Albert Astals Cid |
Approved revision: | 1341 |
Merged at revision: | 1364 |
Proposed branch: | lp:~dandrader/unity8/touchOwnership |
Merge into: | lp:unity8 |
Diff against target: |
6741 lines (+4656/-503) 79 files modified
CMakeLists.txt (+2/-0) debian/control (+1/-1) debian/unity8-private.install (+1/-0) libs/CMakeLists.txt (+1/-0) libs/UbuntuGestures/CMakeLists.txt (+40/-0) libs/UbuntuGestures/CandidateInactivityTimer.cpp (+41/-0) libs/UbuntuGestures/CandidateInactivityTimer.h (+49/-0) libs/UbuntuGestures/DebugHelpers.cpp (+68/-0) libs/UbuntuGestures/DebugHelpers.h (+29/-0) libs/UbuntuGestures/Pool.h (+134/-0) libs/UbuntuGestures/Timer.cpp (+109/-0) libs/UbuntuGestures/Timer.h (+105/-0) libs/UbuntuGestures/TouchOwnershipEvent.cpp (+35/-0) libs/UbuntuGestures/TouchOwnershipEvent.h (+50/-0) libs/UbuntuGestures/TouchRegistry.cpp (+500/-0) libs/UbuntuGestures/TouchRegistry.h (+182/-0) libs/UbuntuGestures/UbuntuGesturesGlobal.h (+23/-0) libs/UbuntuGestures/UnownedTouchEvent.cpp (+39/-0) libs/UbuntuGestures/UnownedTouchEvent.h (+45/-0) plugins/Ubuntu/Gestures/AxisVelocityCalculator.h (+2/-2) plugins/Ubuntu/Gestures/CMakeLists.txt (+26/-7) plugins/Ubuntu/Gestures/Direction.h (+2/-2) plugins/Ubuntu/Gestures/DirectionalDragArea.cpp (+335/-209) plugins/Ubuntu/Gestures/DirectionalDragArea.h (+37/-29) plugins/Ubuntu/Gestures/TimeSource.h (+2/-2) plugins/Ubuntu/Gestures/TouchGate.cpp (+347/-0) plugins/Ubuntu/Gestures/TouchGate.h (+126/-0) plugins/Ubuntu/Gestures/UbuntuGesturesQmlGlobal.h (+3/-3) plugins/Ubuntu/Gestures/plugin.cpp (+3/-1) plugins/Ubuntu/Gestures/plugin.h (+1/-1) plugins/Ubuntu/Gestures/qmldir (+1/-1) qml/Components/DragHandle.qml (+2/-2) qml/Components/EdgeDragArea.qml (+2/-0) qml/Components/InputMethod.qml (+13/-0) qml/Dash/Dash.qml (+29/-7) qml/Dash/DashContent.qml (+4/-1) qml/Dash/GenericScopeView.qml (+2/-0) qml/Launcher/Launcher.qml (+3/-1) qml/Launcher/LauncherPanel.qml (+1/-0) qml/Stages/ApplicationWindow.qml (+0/-18) qml/Stages/PhoneStage.qml (+14/-5) qml/Stages/SessionContainer.qml (+1/-13) qml/Stages/SurfaceContainer.qml (+38/-7) src/CMakeLists.txt (+6/-0) src/Dash/CMakeLists.txt (+5/-0) src/Dash/main.cpp (+6/-0) src/main.cpp (+5/-0) tests/CMakeLists.txt (+1/-0) tests/libs/CMakeLists.txt (+1/-0) tests/libs/UbuntuGestures/CMakeLists.txt (+20/-0) tests/libs/UbuntuGestures/tst_TouchRegistry.cpp (+803/-0) tests/mocks/Unity/Application/CMakeLists.txt (+1/-0) tests/mocks/Unity/Application/MirSurfaceItem.cpp (+18/-22) tests/mocks/Unity/Application/MirSurfaceItem.h (+18/-1) tests/mocks/Unity/Application/MirSurfaceItem.qml (+0/-12) tests/mocks/Unity/Application/UbuntuKeyboardInfo.cpp (+28/-0) tests/mocks/Unity/Application/UbuntuKeyboardInfo.h (+60/-0) tests/mocks/Unity/Application/plugin.cpp (+7/-0) tests/plugins/Ubuntu/Gestures/CMakeLists.txt (+9/-2) tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml (+3/-0) tests/plugins/Ubuntu/Gestures/touchGateExample.qml (+27/-0) tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp (+494/-17) tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.qml (+1/-0) tests/plugins/Ubuntu/Gestures/tst_TouchGate.cpp (+345/-0) tests/qmltests/Components/CMakeLists.txt (+2/-1) tests/qmltests/Components/tst_DragHandle.cpp (+14/-17) tests/qmltests/Greeter/tst_SingleGreeter.qml (+1/-1) tests/qmltests/Launcher/tst_Launcher.qml (+123/-107) tests/qmltests/Stages/tst_ApplicationWindow.qml (+2/-2) tests/qmltests/Stages/tst_PhoneStage.qml (+2/-1) tests/qmltests/Stages/tst_SurfaceContainer.qml (+26/-1) tests/qmltests/tst_Shell.qml (+118/-3) tests/uqmlscene/CMakeLists.txt (+2/-0) tests/uqmlscene/README (+3/-1) tests/uqmlscene/main.cpp (+9/-0) tests/utils/modules/Unity/Test/CMakeLists.txt (+2/-0) tests/utils/modules/Unity/Test/UnityTestCase.qml (+2/-0) tests/utils/modules/Unity/Test/testutil.cpp (+41/-2) tests/utils/modules/Unity/Test/testutil.h (+3/-1) |
To merge this branch: | bzr merge lp:~dandrader/unity8/touchOwnership |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Albert Astals Cid (community) | Approve | ||
PS Jenkins bot (community) | continuous-integration | Needs Fixing | |
kevin gunn (community) | Needs Fixing | ||
Review via email: mp+236152@code.launchpad.net |
Commit message
Add touch ownership logic on top of qt input handling
It does mainly 3 things:
- Adds TouchRegistry.
- Adds TouchGate
- Modifies DirectionalDragArea so that it uses TouchRegistry and only grabs a touch once it has recognized a gesture done by it.
TouchRegistry was put into a library as both unity8 and Ubuntu.Gestures plugin need to use it.
The next step is to have surfaces participating in the touch ownership negotiation. But for now they're put behind TouchGates so that they only receive thouches whose ownership has already been settled.
The most noticeable improvement of this patch is that taps near screen edges will now reach applications.
It also makes right-edge and bottom-edge drags only animate once a directional drag gesture has been fully recognized.
Description of the change
* Are there any related MPs required for this MP to build/function as expected? Please list.
https:/
* 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.
PS Jenkins bot (ps-jenkins) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1297
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1299
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 1296. By Nick Dedekind
-
Visual changes for indicator RTM polishing Fixes: 1329289, 1349921, 1350308, 1350952, 1354506, 1373319
Approved by: Andrea Cimitan - 1297. By PS Jenkins bot
-
Releasing 8.00+14.
10.20140926- 0ubuntu1 - 1298. By PS Jenkins bot
-
Resync trunk
- 1299. By Launchpad Translations on behalf of unity-team
-
Launchpad automatic translations update.
- 1300. By Launchpad Translations on behalf of unity-team
-
Launchpad automatic translations update.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1300
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1301
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 1301. By Launchpad Translations on behalf of unity-team
-
Launchpad automatic translations update.
Albert Astals Cid (aacid) wrote : | # |
Found 2 license problems:
libs/UbuntuGest
tests/plugins/
kevin gunn (kgunn72) wrote : | # |
currently failling to build in rtm-silo
looks like some code unhappiness & unit test failure of touchRegistryInput
Daniel d'Andrada (dandrader) wrote : | # |
> Found 2 license problems:
> libs/UbuntuGest
> tests/plugins/
Fixed.
Daniel d'Andrada (dandrader) wrote : | # |
> currently failling to build in rtm-silo
>
> https:/
> rtm-14.
>
> looks like some code unhappiness & unit test failure of touchRegistryInput
Fixed.
Daniel d'Andrada (dandrader) wrote : | # |
> currently failling to build in rtm-silo
>
> https:/
> rtm-14.
>
> looks like some code unhappiness & unit test failure of touchRegistryInput
Fixed.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1302
http://
Executed test runs:
FAILURE: http://
UNSTABLE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1304
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1302. By Josh Arenson
-
Fix lp:1367894 by correcting how the minute value is calculated in the panel. Fixes: 1367894
Approved by: Nick Dedekind - 1303. By Ying-Chun Liu
-
Remove maxLineCount in preview. (LP: 1328513) Fixes: 1328513
Approved by: Michael Terry - 1304. By Gerry Boland
-
Cleanup: Remove unused member and fix small syntax error in OrientationLock
Approved by: Daniel d'Andrada - 1305. By Andrea Cimitan
-
Move activity indicator on top of keyboard Fixes: 1354519
Approved by: Michael Terry - 1306. By Alexandros Frantzis
-
Remove stale trusted socket before starting unity8 from upstart (LP: #1371597) Fixes: 1371597
Approved by: Ying-Chun Liu - 1307. By Michael Terry
-
Fix some code that accidentally landed in trunk before it got cleaned up. The current code just has some duplication to it that should be unified.
Approved by: Michael Zanetti - 1308. By Michael Terry
-
Implement latest visual designs for passphrase lockscreen.
The visual specs are here:
https://drive. google. com/a/canonical .com/folderview ?id=0B8I8ZVKH- 8Ssb2Zhcm9kUE9T LVE This branch does a few things:
- Total redesign for passphrase lockscreen. It now has no visible text box nor says "Hello".
- Total redesign of how we inform the user that they have attempted to log in too many times. Now instead of saying something like "please wait" in the same lockscreen, we show a custom lockscreen with a longer message and a lock icon.
- Tweak the spacing on the passcode lockscreen.
- Adds a phone icon to the "Emergency Call" label on the bottom -- and makes that label move up if the OSK covers it.
- Changes the "Swipe to Unlock" text to just "Unlock" with arrow brackets on either side.
Approved by: Michael Zanetti - 1309. By Michael Terry
-
Make it easier to use the Lockscreen component from the welcome wizard.
Approved by: Albert Astals Cid - 1310. By Daniel d'Andrada
-
Add gdbTestComponen
tName build targets
Approved by: Michael Zanetti - 1311. By Michael Terry
-
Limit how much memory we reserve for the greeter background image, allowing giant images to appear correctly.
The downside is that even if the image is smaller, we'll still reserve the whole screen size in memory. But we avoid the actual bug anyway. Fixes: 1373462
Approved by: Michael Zanetti - 1312. By Nick Dedekind
-
Fixed DefaultIndicato
rPage test. Fixed warnings from test.
Approved by: Albert Astals Cid - 1313. By PS Jenkins bot
-
Releasing 8.00+14.
10.20140930. 2-0ubuntu1
Albert Astals Cid (aacid) wrote : | # |
Do you think you can add to the commit a two or three lines description of the big picture of this change so that if someone in the future comes back looking at it has a starting point?
Albert Astals Cid (aacid) wrote : | # |
Copyright (C) 2013 Canonical -> 2014
Albert Astals Cid (aacid) wrote : | # |
m_touchId = touchId;
m_candidate = candidate;
Move those up to the constructor initializer list?
Albert Astals Cid (aacid) wrote : | # |
default: // Qt::TouchPointR
default: //QEvent:
I think it's better if you actually add a case so if/when they add new values to the enum we get a warning.
Albert Astals Cid (aacid) wrote : | # |
for (int i=0; i < ev->touchPoints
const QTouchEvent:
foreach?
Albert Astals Cid (aacid) wrote : | # |
template <class ItemType> class Pool
Can you please document the functions that ItemType has to have to be able to use Pool?
Albert Astals Cid (aacid) wrote : | # |
virtual void start() { m_isRunning = true; };
Remove the extra ; at the end
Albert Astals Cid (aacid) wrote : | # |
FakeTimer(QObject *parent = nullptr) needs to initialize m_interval to something
Albert Astals Cid (aacid) wrote : | # |
auto &watchers = touchInfo-
is this smart enough to make it const? if not make it const :)
- 1314. By PS Jenkins bot
-
Resync trunk
Daniel d'Andrada (dandrader) wrote : | # |
> Do you think you can add to the commit a two or three lines description of the
> big picture of this change so that if someone in the future comes back looking
> at it has a starting point?
Done.
Daniel d'Andrada (dandrader) wrote : | # |
> Copyright (C) 2013 Canonical -> 2014
Done.
Albert Astals Cid (aacid) wrote : | # |
what about this?
=== modified file 'libs/UbuntuGes
--- libs/UbuntuGest
+++ libs/UbuntuGest
@@ -139,10 +139,10 @@
// TODO: Consider what happens if an item calls any of TouchRegistry's public methods
// from the event handler callback.
m_
- auto it = touchIdsForItem
- while (it != touchIdsForItem
+ auto it = touchIdsForItem
+ while (it != touchIdsForItem
QQuickItem *item = it.key();
- QList<int> &touchIds = it.value();
+ const QList<int> &touchIds = it.value();
++it;
};
@@ -163,7 +163,7 @@
Extracts the touches with the given touchIds from event and send them in a
UnownedTouc
*/
-void TouchRegistry:
+void TouchRegistry:
QQuickItem *item)
{
Qt:
=== modified file 'libs/UbuntuGes
--- libs/UbuntuGest
+++ libs/UbuntuGest
@@ -156,7 +156,7 @@
static void translateTouchP
- static void dispatchPointsT
+ static void dispatchPointsT
void freeEndedTouchI
Daniel d'Andrada (dandrader) wrote : | # |
> m_touchId = touchId;
> m_candidate = candidate;
>
> Move those up to the constructor initializer list?
Done.
Daniel d'Andrada (dandrader) wrote : | # |
> default: // Qt::TouchPointR
> default: //QEvent:
>
> I think it's better if you actually add a case so if/when they add new values
> to the enum we get a warning.
Done.
Daniel d'Andrada (dandrader) wrote : | # |
> for (int i=0; i < ev->touchPoints
> const QTouchEvent:
>
> foreach?
Done.
Albert Astals Cid (aacid) wrote : | # |
In TouchRegistry:
QTouchEvent *eventForItem = new QTouchEvent
?
Daniel d'Andrada (dandrader) wrote : | # |
> template <class ItemType> class Pool
>
> Can you please document the functions that ItemType has to have to be able to
> use Pool?
Done.
Albert Astals Cid (aacid) wrote : | # |
"2- That item will receive UnownedTouchEvents about for items"
I could not understand, Daniel agrees needs to be fixed
Daniel d'Andrada (dandrader) wrote : | # |
> virtual void start() { m_isRunning = true; };
>
> Remove the extra ; at the end
Done.
Daniel d'Andrada (dandrader) wrote : | # |
> FakeTimer(QObject *parent = nullptr) needs to initialize m_interval to
> something
Done.
Daniel d'Andrada (dandrader) wrote : | # |
> auto &watchers = touchInfo-
>
> is this smart enough to make it const? if not make it const :)
Done.
Daniel d'Andrada (dandrader) wrote : | # |
> what about this?
>
> === modified file 'libs/UbuntuGes
> [...]
Done.
Daniel d'Andrada (dandrader) wrote : | # |
> In TouchRegistry:
>
> QTouchEvent *eventForItem = new QTouchEvent
>
> ?
Oh, you scared me for a minute.
UnownedTouchEve
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1305
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Daniel d'Andrada (dandrader) wrote : | # |
> "2- That item will receive UnownedTouchEvents about for items"
>
> I could not understand, Daniel agrees needs to be fixed
Done.
- 1315. By Daniel d'Andrada
-
Add touch ownership logic on top of qt input handling
It does mainly 3 things:
- Adds TouchRegistry.
- Adds TouchGate
- Modifies DirectionalDragArea so that it uses TouchRegistry and only grabs a touch once it has recognized a gesture done by it.TouchRegistry was put into a library as both unity8 and Ubuntu.Gestures plugin need to use it.
The next step is to have surfaces participating in the touch ownership negotiation. But for now they're put behind TouchGates so that they only receive thouches whose ownership has already been settled.
The most noticeable improvement of this patch is that taps near screen edges will now reach applications.
Albert Astals Cid (aacid) wrote : | # |
DirectionalDrag
Albert Astals Cid (aacid) wrote : | # |
if (oldStatus == Undecided) {
}
in WaitingForTouch case of DirectionalDrag
- 1316. By Daniel d'Andrada
-
exit loop once there's no point in continuing
- 1317. By Daniel d'Andrada
-
Remove superfluous return statement.
Daniel d'Andrada (dandrader) wrote : | # |
On 01/10/14 10:38, Albert Astals Cid wrote:
> Review: Needs Fixing
>
> DirectionalDrag
Fixed.
- 1318. By Daniel d'Andrada
-
Remove leftover code
Daniel d'Andrada (dandrader) wrote : | # |
On 01/10/14 10:40, Albert Astals Cid wrote:
> Review: Needs Fixing
>
> if (oldStatus == Undecided) {
> }
>
> in WaitingForTouch case of DirectionalDrag
Curious leftover. :)
Removed.
Albert Astals Cid (aacid) wrote : | # |
What about ?
void TouchGate:
{
while (!m_storedEvent
- QTouchEvent *event = m_storedEvents.
- m_storedEvents.
+ QTouchEvent *event = m_storedEvents.
Albert Astals Cid (aacid) wrote : | # |
I think we're leaking event in TouchGate:
Daniel d'Andrada (dandrader) wrote : | # |
On 01/10/14 11:44, Albert Astals Cid wrote:
> What about ?
>
> void TouchGate:
> {
> while (!m_storedEvent
> - QTouchEvent *event = m_storedEvents.
> - m_storedEvents.
> + QTouchEvent *event = m_storedEvents.
>
Done.
- 1319. By Daniel d'Andrada
-
Small refactoring
Albert Astals Cid (aacid) wrote : | # |
Should we have a qDeleteAll(
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1309
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1320. By Daniel d'Andrada
-
TouchGate can no longer leak stored QTouchEvents
Daniel d'Andrada (dandrader) wrote : | # |
On 01/10/14 11:46, Albert Astals Cid wrote:
> Review: Needs Fixing
>
> I think we're leaking event in TouchGate:
Yes. Fixed. Thanks for spotting this.
Daniel d'Andrada (dandrader) wrote : | # |
On 01/10/14 11:52, Albert Astals Cid wrote:
> Review: Needs Fixing
>
> Should we have a qDeleteAll(
No longer applicable after I refactored m_storedEvents.
- 1321. By Daniel d'Andrada
-
Missing bit
- 1322. By Daniel d'Andrada
-
Clean up things
- 1323. By Daniel d'Andrada
-
Fix debug code
- 1324. By Daniel d'Andrada
-
fix tst_Application
Window. qml
Albert Astals Cid (aacid) wrote : | # |
Failing tests:
FAIL! : qmltestrunner::Drag and Drop::test_
Actual (): -64
Expected (): 0
Loc: [/home/
FAIL! : qmltestrunner::Drag and Drop::test_
Actual (): -64
Expected (): 0
Loc: [/home/
FAIL! : qmltestrunner::Drag and Drop::test_
Actual (): false
Expected (): true
Loc: [/home/
FAIL! : qmltestrunner::Drag and Drop::test_
Actual (): 0
Expected (): 0.8
Loc: [/home/
FAIL! : qmltestrunner:
Actual ():
Expected (): dialer-app
Loc: [/home/
FAIL! : qmltestrunner:
Loc: [/home/
FAIL! : qmltestrunner:
Loc: [/home/
FAIL! : qmltestrunner:
Actual (): false
Expected (): true
Loc: [/home/
QFATAL : qmltestrunner:
FAIL! : qmltestrunner:
Loc: [Unknown file(0)]
FAIL! : qmltestrunner:
Actual (): webbrowser-app
Expected (): facebook-webapp
- 1325. By Daniel d'Andrada
-
Add a mock UbuntuKeyboardInfo
- 1326. By Daniel d'Andrada
-
Made tst_Launcher tests pass again took the opportunity to recfactor it a bit
Daniel d'Andrada (dandrader) wrote : | # |
> Failing tests:
>
> [...]
Fixed.
Albert Astals Cid (aacid) wrote : | # |
New one
FAIL! : qmltestrunner:
Loc: [/home/
Albert Astals Cid (aacid) wrote : | # |
Interestingly now test_clickFlick failed but got
FAIL! : qmltestrunner:
Actual (): 1
Expected (): 0
Loc: [/home/
Try looping it with
while [ true ]; do make xvfbtestLauncher; if [ $? -ne 0 ]; then break; fi; done
Albert Astals Cid (aacid) wrote : | # |
Crashes when trying to show the dash overview.
$ GRID_UNIT_PX=12 gdb -arg ./builddir/
#0 0x00007fffcf9c0f11 in Pool<TouchRegis
at /home/tsdgeos_
#1 0x00007fffcf9c0675 in TouchRegistry:
#2 0x00007fffcf9bfdd7 in TouchRegistry:
#3 0x00007fffcfbe4ed2 in DirectionalDrag
#4 0x00007fffcfbe4bf8 in DirectionalDrag
#5 0x00007ffff6c6677d in QQuickItem:
#6 0x00007fffcfbe4606 in DirectionalDrag
#7 0x00007ffff610ad05 in QCoreApplicatio
#8 0x00007ffff610ae3b in QCoreApplicatio
#9 0x00007ffff6c77f57 in QQuickWindowPri
#10 0x00007ffff6c788cb in QQuickWindowPri
#11 0x00007ffff6c7873e in QQuickWindowPri
#12 0x00007ffff6c7873e in QQuickWindowPri
#13 0x00007ffff6c7873e in QQuickWindowPri
#14 0x00007ffff6c7873e in QQuickWindowPri
- 1327. By Daniel d'Andrada
-
unity8-dash also needs a TouchRegistry
as it uses DirectionDragArea as well
Daniel d'Andrada (dandrader) wrote : | # |
On 02/10/14 12:10, Albert Astals Cid wrote:
> Review: Needs Fixing
>
> Crashes when trying to show the dash overview.
Fixed.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1327
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Albert Astals Cid (aacid) wrote : | # |
I've been playing with it and the right hand site is much better but still is a bit flacky, i'm using this ppa https:/
Can you have a look? I did put a
Rectangle { anchors.fill: parent; color: "red"; opacity: 0.5 }
inside starArea of /usr/share/
Daniel d'Andrada (dandrader) wrote : | # |
> I've been playing with it and the right hand site is much better but still is
> a bit flacky, i'm using this ppa https:/
> team/+archive/
> lp:~aacid/unity8/list_on_bottom_swipe and merge edgeowner
> lp:~dandrader/unity8/touchOwnership and half of the times i do a click tap
> very close to the right edge while being the the "scopes list" (the thing that
> shows when you pull up from the bottom in the scopes) i get the
> favorite/unfavorite toggled and the other half i get the right edge animation.
>
> Can you have a look? I did put a
> Rectangle { anchors.fill: parent; color: "red"; opacity: 0.5 }
> inside starArea of /usr/share/
> sure it's really attach to the border.
The remaining problem with the right edge is that its edge-drag recognizer parameters makes it recognize pretty much anything as a edge drag. So a tap there with a fat finger will cause a small movement enough for it to consider as an edge drag. So it's parameters must be tweaked. Strictly speaking it's a separate issue and thus I was planning to tackle it on a separate MP as this one is already huge. But maybe I should do it already...
- 1328. By Daniel d'Andrada
-
Make tst_Launcher stable
Daniel d'Andrada (dandrader) wrote : | # |
> New one
>
> FAIL! : qmltestrunner:
> returned FALSE. ()
> Loc: [/home/
> her/tst_
Fixed. tst_Launcher works reliably now.
- 1329. By Daniel d'Andrada
-
Only move the stage once an edge-drag gesture has been recognized
Daniel d'Andrada (dandrader) wrote : | # |
> > I've been playing with it and the right hand site is much better but still
> is
> > a bit flacky, i'm using this ppa https:/
> > team/+archive/
> > lp:~aacid/unity8/list_on_bottom_swipe and merge edgeowner
> > lp:~dandrader/unity8/touchOwnership and half of the times i do a click tap
> > very close to the right edge while being the the "scopes list" (the thing
> that
> > shows when you pull up from the bottom in the scopes) i get the
> > favorite/unfavorite toggled and the other half i get the right edge
> animation.
> >
> > Can you have a look? I did put a
> > Rectangle { anchors.fill: parent; color: "red"; opacity: 0.5 }
> > inside starArea of /usr/share/
> > sure it's really attach to the border.
>
> The remaining problem with the right edge is that its edge-drag recognizer
> parameters makes it recognize pretty much anything as a edge drag. So a tap
> there with a fat finger will cause a small movement enough for it to consider
> as an edge drag. So it's parameters must be tweaked. Strictly speaking it's a
> separate issue and thus I was planning to tackle it on a separate MP as this
> one is already huge. But maybe I should do it already...
Fixed.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1328
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1329
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1330. By Daniel d'Andrada
-
Only show app shadows when we are indeed perfoming and edge drag
- 1331. By Daniel d'Andrada
-
Only animate the dash overview drag after the gesture has been recognized
Daniel d'Andrada (dandrader) wrote : | # |
Found an issue that seems to have been caused by this branch:
1 - Launch a couple of apps
2 - go to the app spread
3 - Drag in the launcher
4 - tap on the ubuntu icon
expected outcome:
dash is brought to foreground
actual outcome:
the app spread behind the launcher just shudders a bit and that's it. Only on the third tap or so does the dash finally go to foreground.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1330
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1331
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1332. By Daniel d'Andrada
-
Add regression test Shell.test_
tapUbuntuIconIn LauncherOverApp Spread - 1333. By Daniel d'Andrada
-
Merge lp:unity8
[ Andrea Cimitan ]
* Tweak card header to match the spec
* Add preview image slideshow (LP: #1351537)
[ Michał Sawicz ]
* Cache more things in memory, so flicking scopes should be faster
(LP: #1336724)
* Tweak card header to match the spec
* Save texture memory by limiting sourceSize (LP: #1338430)
[ Ying-Chun Liu ]
* Add attributes to Preview. (LP: #1282460)
[ Albert Astals ]
* Update pot
* Cache more things in memory, so flicking scopes should be faster
(LP: #1336724)
* Save texture memory by limiting sourceSize (LP: #1338430)
* Clip the settings list
* Fix unlocking from the left again
* Add wait_ makes tests more reliable
[ Michael Zanetti ]
* fix fading out the launcher instead of sliding it out on left-edge
minimizing an app.
* Make the DashCommunicator async and more flexible to handle a
lifecycle-suspended dash (LP: #1339883)
[ Michael Terry ]
* Retry unlock-device script if it fails, as there is always a risk of
a small race with boot-up. (LP: #1370644)
* Add pull-to-refresh functionality to scopes. (LP: #1368336)
[ CI bot ]
* Resync trunk
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1333
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 1334. By Daniel d'Andrada
-
Work around bug in Qt
Daniel d'Andrada (dandrader) wrote : | # |
> Found an issue that seems to have been caused by this branch:
> 1 - Launch a couple of apps
> 2 - go to the app spread
> 3 - Drag in the launcher
> 4 - tap on the ubuntu icon
>
> expected outcome:
> dash is brought to foreground
>
> actual outcome:
> the app spread behind the launcher just shudders a bit and that's it. Only on
> the third tap or so does the dash finally go to foreground.
Fixed it. It's actually caused by a problem in Qt code.
Albert Astals Cid (aacid) wrote : | # |
> > Found an issue that seems to have been caused by this branch:
> > 1 - Launch a couple of apps
> > 2 - go to the app spread
> > 3 - Drag in the launcher
> > 4 - tap on the ubuntu icon
> >
> > expected outcome:
> > dash is brought to foreground
> >
> > actual outcome:
> > the app spread behind the launcher just shudders a bit and that's it. Only
> on
> > the third tap or so does the dash finally go to foreground.
>
> Fixed it. It's actually caused by a problem in Qt code.
Should we file a bug for it with testcase?
- 1335. By Daniel d'Andrada
-
Remove traling whitespace
- 1336. By Daniel d'Andrada
-
Add TODO comment on bug report
Daniel d'Andrada (dandrader) wrote : | # |
> > > Found an issue that seems to have been caused by this branch:
> > > 1 - Launch a couple of apps
> > > 2 - go to the app spread
> > > 3 - Drag in the launcher
> > > 4 - tap on the ubuntu icon
> > >
> > > expected outcome:
> > > dash is brought to foreground
> > >
> > > actual outcome:
> > > the app spread behind the launcher just shudders a bit and that's it. Only
> > on
> > > the third tap or so does the dash finally go to foreground.
> >
> > Fixed it. It's actually caused by a problem in Qt code.
>
> Should we file a bug for it with testcase?
Yes, but this kind of thing takes time and a critical bug just landed on my lap.
Added it to my TODO list and add a TODO comment in the code.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1334
http://
Executed test runs:
FAILURE: http://
UNSTABLE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1336
http://
Executed test runs:
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1337. By Daniel d'Andrada
-
Update tst_PhoneStage.qml
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1337
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1337
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Albert Astals Cid (aacid) wrote : | # |
So i've been playing with the bottom edge and still didn't like that sometimes you can get it to scroll the list while doing the overview and i came up with http://
What do you think? Maybe the name should forceNoFlickInt
- 1338. By Daniel d'Andrada
-
Avoid conflict between dash vertical flickable and its bottom-edge DDA
Daniel d'Andrada (dandrader) wrote : | # |
> So i've been playing with the bottom edge and still didn't like that sometimes
> you can get it to scroll the list while doing the overview and i came up with
> http://
> can still click on the bottom edges.
>
> What do you think? Maybe the name should forceNoFlickInt
> ClickInteractions are still enabled?
I think it's a sound approach. Pushed with minor modifications (added a big fat comment and s/forceNoIntera
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1338
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Albert Astals Cid (aacid) wrote : | # |
$ make xvfbtestPhoneStage
...
QFATAL : qmltestrunner:
FAIL! : qmltestrunner:
Albert Astals Cid (aacid) wrote : | # |
$ make xvfbtestShell
....
FAIL! : qmltestrunner:
Actual (): dialer-app
Expected (): unity8-dash
Loc: [/home/
Daniel d'Andrada (dandrader) wrote : | # |
On 08/10/14 10:40, Albert Astals Cid wrote:
> Review: Needs Fixing
>
> $ make xvfbtestPhoneStage
> ...
> QFATAL : qmltestrunner:
> FAIL! : qmltestrunner:
>
I don't get this error. Are you sure you have the latest version of this
branch?
Daniel d'Andrada (dandrader) wrote : | # |
On 08/10/14 10:42, Albert Astals Cid wrote:
> Review: Needs Fixing
>
> $ make xvfbtestShell
> ....
> FAIL! : qmltestrunner:
> Actual (): dialer-app
> Expected (): unity8-dash
> Loc: [/home/
>
That's failing in lp:unity8 as well. I'm not fixing it in this branch.
Albert Astals Cid (aacid) wrote : | # |
> On 08/10/14 10:40, Albert Astals Cid wrote:
> > Review: Needs Fixing
> >
> > $ make xvfbtestPhoneStage
> > ...
> > QFATAL : qmltestrunner:
> "event->type() == QEvent::TouchBegin" in file /home/tsdgeos_
> 8/touchOwnershi
> > FAIL! : qmltestrunner:
> fatal error.
> >
> I don't get this error. Are you sure you have the latest version of this
> branch?
Error went away when i dist-upgraded and this came in via the ppa so i guess there is a need to prepend the local libUbuntuGestures to the library path so the one compiled in the code is the one that takes precedence and not this one.
Daniel d'Andrada (dandrader) wrote : | # |
On 08/10/14 11:01, Albert Astals Cid wrote:
>> On 08/10/14 10:40, Albert Astals Cid wrote:
>>> Review: Needs Fixing
>>>
>>> $ make xvfbtestPhoneStage
>>> ...
>>> QFATAL : qmltestrunner:
>> "event->type() == QEvent::TouchBegin" in file /home/tsdgeos_
>> 8/touchOwnershi
>>> FAIL! : qmltestrunner:
>> fatal error.
>> I don't get this error. Are you sure you have the latest version of this
>> branch?
> Error went away when i dist-upgraded and this came in via the ppa so i guess there is a need to prepend the local libUbuntuGestures to the library path so the one compiled in the code is the one that takes precedence and not this one.
Code in libUbuntuGestures didn't change in a while.
Albert Astals Cid (aacid) wrote : | # |
> On 08/10/14 11:01, Albert Astals Cid wrote:
> >> On 08/10/14 10:40, Albert Astals Cid wrote:
> >>> Review: Needs Fixing
> >>>
> >>> $ make xvfbtestPhoneStage
> >>> ...
> >>> QFATAL : qmltestrunner:
> >> "event->type() == QEvent::TouchBegin" in file
> /home/tsdgeos_
> >> 8/touchOwnershi
> >>> FAIL! : qmltestrunner:
> a
> >> fatal error.
> >> I don't get this error. Are you sure you have the latest version of this
> >> branch?
> > Error went away when i dist-upgraded and this came in via the ppa so i guess
> there is a need to prepend the local libUbuntuGestures to the library path so
> the one compiled in the code is the one that takes precedence and not this
> one.
> Code in libUbuntuGestures didn't change in a while.
Ok, the plugin then r1334?
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1338
http://
Executed test runs:
FAILURE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1338
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1339. By Daniel d'Andrada
-
left-edge swipe should always work, even when the launcher is being show
- 1340. By Daniel d'Andrada
-
Work around qt bug to make tests pass
Daniel d'Andrada (dandrader) wrote : | # |
> On 08/10/14 10:42, Albert Astals Cid wrote:
> > Review: Needs Fixing
> >
> > $ make xvfbtestShell
> > ....
> > FAIL! : qmltestrunner:
> focusedApplicat
> > Actual (): dialer-app
> > Expected (): unity8-dash
> > Loc: [/home/
> _Shell.qml(440)]
> >
> That's failing in lp:unity8 as well. I'm not fixing it in this branch.
I messed up. Indeed passes in lp:unity8.
Fixed it, finally.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1339
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1341. By Daniel d'Andrada
-
DDA - ungrab current mouse grabber instead of going all the way grabbing for himself
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1340
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1341
http://
Executed test runs:
FAILURE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Albert Astals Cid (aacid) wrote : | # |
* Did you perform an exploratory manual test run of the code change and any related functionality?
Yes
* Did CI run pass? If not, please explain why.
qmluitests: ShellWithPin:
autopilot: could not reproduce locally
* Did you make sure that the branch does not contain spurious tags?
Yes
- 1342. By Daniel d'Andrada
-
Bump qtmir version dependency
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2014-10-06 15:56:08 +0000 |
3 | +++ CMakeLists.txt 2014-10-10 15:05:15 +0000 |
4 | @@ -58,6 +58,7 @@ |
5 | include(GNUInstallDirs) |
6 | |
7 | set(SHELL_APP_DIR ${CMAKE_INSTALL_DATADIR}/unity8) |
8 | +set(SHELL_PRIVATE_LIBDIR ${CMAKE_INSTALL_LIBDIR}/unity8) |
9 | |
10 | execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=plugindir_suffix unity-shell-api OUTPUT_VARIABLE SHELL_INSTALL_QML OUTPUT_STRIP_TRAILING_WHITESPACE) |
11 | if(SHELL_INSTALL_QML STREQUAL "") |
12 | @@ -109,6 +110,7 @@ |
13 | |
14 | # add subdirectories to build |
15 | add_subdirectory(include) |
16 | +add_subdirectory(libs) |
17 | add_subdirectory(src) |
18 | add_subdirectory(tools) |
19 | add_subdirectory(qml) |
20 | |
21 | === modified file 'debian/control' |
22 | --- debian/control 2014-10-06 08:00:45 +0000 |
23 | +++ debian/control 2014-10-10 15:05:15 +0000 |
24 | @@ -86,7 +86,7 @@ |
25 | qmenumodel-qml (>= 0.2.8), |
26 | qml-module-qtquick-xmllistmodel, |
27 | qtdeclarative5-gsettings1.0, |
28 | - qtdeclarative5-qtmir-plugin (>= 0.4.3), |
29 | + qtdeclarative5-qtmir-plugin (>= 0.4.4), |
30 | qtdeclarative5-ubuntu-telephony0.1, |
31 | unity-launcher-impl-4, |
32 | unity8-common (= ${source:Version}), |
33 | |
34 | === modified file 'debian/unity8-private.install' |
35 | --- debian/unity8-private.install 2014-09-05 12:25:51 +0000 |
36 | +++ debian/unity8-private.install 2014-10-10 15:05:15 +0000 |
37 | @@ -9,5 +9,6 @@ |
38 | usr/lib/*/unity8/qml/Ubuntu |
39 | usr/lib/*/unity8/qml/Unity |
40 | usr/lib/*/unity8/qml/Utils |
41 | +usr/lib/*/unity8/libUbuntuGestures* |
42 | usr/share/accountsservice/interfaces |
43 | usr/share/dbus-1/interfaces |
44 | |
45 | === added directory 'libs' |
46 | === added file 'libs/CMakeLists.txt' |
47 | --- libs/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
48 | +++ libs/CMakeLists.txt 2014-10-10 15:05:15 +0000 |
49 | @@ -0,0 +1,1 @@ |
50 | +add_subdirectory(UbuntuGestures) |
51 | |
52 | === added directory 'libs/UbuntuGestures' |
53 | === added file 'libs/UbuntuGestures/CMakeLists.txt' |
54 | --- libs/UbuntuGestures/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
55 | +++ libs/UbuntuGestures/CMakeLists.txt 2014-10-10 15:05:15 +0000 |
56 | @@ -0,0 +1,40 @@ |
57 | +# in order to include Qt's private headers |
58 | +remove_definitions(-DQT_NO_KEYWORDS) |
59 | + |
60 | +set(UbuntuGestures_SOURCES |
61 | + CandidateInactivityTimer.cpp |
62 | + DebugHelpers.cpp |
63 | + Timer.cpp |
64 | + TouchOwnershipEvent.cpp |
65 | + TouchRegistry.cpp |
66 | + UnownedTouchEvent.cpp |
67 | +) |
68 | + |
69 | +add_definitions(-DUBUNTUGESTURES_LIBRARY) |
70 | + |
71 | +add_library(UbuntuGestures SHARED ${UbuntuGestures_SOURCES}) |
72 | + |
73 | +qt5_use_modules(UbuntuGestures Core Quick) |
74 | + |
75 | +# So that Foo.cpp can #include "Foo.moc" |
76 | +include_directories(${CMAKE_CURRENT_BINARY_DIR}) |
77 | + |
78 | +install(TARGETS UbuntuGestures |
79 | + DESTINATION ${SHELL_PRIVATE_LIBDIR}) |
80 | + |
81 | + |
82 | +# There's no cmake var for v8 include path :-/ so create one |
83 | +LIST(GET Qt5Core_INCLUDE_DIRS 0 QtCoreDir0) |
84 | +if(${Qt5Core_VERSION_STRING} VERSION_LESS "5.1.0") |
85 | + SET(Qt5V8_PRIVATE_INCLUDE_DIR ${QtCoreDir0}/../QtV8/${Qt5Core_VERSION_STRING}/QtV8) |
86 | +else() |
87 | + SET(Qt5V8_PRIVATE_INCLUDE_DIR ${QtCoreDir0}/QtV8/${Qt5Core_VERSION_STRING}/QtV8) |
88 | +endif() |
89 | + |
90 | +# DANGER! DANGER! Using Qt's private API! |
91 | +include_directories( |
92 | + ${Qt5Qml_PRIVATE_INCLUDE_DIRS} |
93 | + ${Qt5Quick_INCLUDE_DIRS} |
94 | + ${Qt5Quick_PRIVATE_INCLUDE_DIRS} |
95 | + ${Qt5V8_PRIVATE_INCLUDE_DIR} |
96 | +) |
97 | |
98 | === added file 'libs/UbuntuGestures/CandidateInactivityTimer.cpp' |
99 | --- libs/UbuntuGestures/CandidateInactivityTimer.cpp 1970-01-01 00:00:00 +0000 |
100 | +++ libs/UbuntuGestures/CandidateInactivityTimer.cpp 2014-10-10 15:05:15 +0000 |
101 | @@ -0,0 +1,41 @@ |
102 | +/* |
103 | + * Copyright (C) 2014 Canonical, Ltd. |
104 | + * |
105 | + * This program is free software; you can redistribute it and/or modify |
106 | + * it under the terms of the GNU General Public License as published by |
107 | + * the Free Software Foundation; version 3. |
108 | + * |
109 | + * This program is distributed in the hope that it will be useful, |
110 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
111 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
112 | + * GNU General Public License for more details. |
113 | + * |
114 | + * You should have received a copy of the GNU General Public License |
115 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
116 | + */ |
117 | + |
118 | +#include "CandidateInactivityTimer.h" |
119 | + |
120 | +namespace UbuntuGestures { |
121 | + |
122 | +CandidateInactivityTimer::CandidateInactivityTimer(int touchId, QQuickItem *candidate, |
123 | + AbstractTimerFactory &timerFactory, QObject *parent) |
124 | + : QObject(parent) |
125 | + , m_touchId(touchId) |
126 | + , m_candidate(candidate) |
127 | +{ |
128 | + m_timer = timerFactory.createTimer(this); |
129 | + connect(m_timer, &AbstractTimer::timeout, |
130 | + this, &CandidateInactivityTimer::onTimeout); |
131 | + m_timer->setInterval(durationMs); |
132 | + m_timer->setSingleShot(true); |
133 | + m_timer->start(); |
134 | +} |
135 | + |
136 | +void CandidateInactivityTimer::onTimeout() |
137 | +{ |
138 | + qWarning("[TouchRegistry] Candidate for touch %d defaulted!", m_touchId); |
139 | + Q_EMIT candidateDefaulted(m_touchId, m_candidate); |
140 | +} |
141 | + |
142 | +} // namespace UbuntuGestures |
143 | |
144 | === added file 'libs/UbuntuGestures/CandidateInactivityTimer.h' |
145 | --- libs/UbuntuGestures/CandidateInactivityTimer.h 1970-01-01 00:00:00 +0000 |
146 | +++ libs/UbuntuGestures/CandidateInactivityTimer.h 2014-10-10 15:05:15 +0000 |
147 | @@ -0,0 +1,49 @@ |
148 | +/* |
149 | + * Copyright (C) 2014 Canonical, Ltd. |
150 | + * |
151 | + * This program is free software; you can redistribute it and/or modify |
152 | + * it under the terms of the GNU General Public License as published by |
153 | + * the Free Software Foundation; version 3. |
154 | + * |
155 | + * This program is distributed in the hope that it will be useful, |
156 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
157 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
158 | + * GNU General Public License for more details. |
159 | + * |
160 | + * You should have received a copy of the GNU General Public License |
161 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
162 | + */ |
163 | + |
164 | +#ifndef UBUNTUGESTURES_CANDIDATE_INACTIVITY_TIMER_H |
165 | +#define UBUNTUGESTURES_CANDIDATE_INACTIVITY_TIMER_H |
166 | + |
167 | +#include <QObject> |
168 | + |
169 | +class QQuickItem; |
170 | + |
171 | +#include "Timer.h" |
172 | + |
173 | +namespace UbuntuGestures { |
174 | + |
175 | +class UBUNTUGESTURES_EXPORT CandidateInactivityTimer : public QObject { |
176 | + Q_OBJECT |
177 | +public: |
178 | + CandidateInactivityTimer(int touchId, QQuickItem *candidate, |
179 | + AbstractTimerFactory &timerFactory, |
180 | + QObject *parent = nullptr); |
181 | + |
182 | + const int durationMs = 350; |
183 | + |
184 | +Q_SIGNALS: |
185 | + void candidateDefaulted(int touchId, QQuickItem *candidate); |
186 | +private Q_SLOTS: |
187 | + void onTimeout(); |
188 | +private: |
189 | + AbstractTimer *m_timer; |
190 | + int m_touchId; |
191 | + QQuickItem *m_candidate; |
192 | +}; |
193 | + |
194 | +} // namespace UbuntuGestures |
195 | + |
196 | +#endif // UBUNTUGESTURES_CANDIDATE_INACTIVITY_TIMER_H |
197 | |
198 | === added file 'libs/UbuntuGestures/DebugHelpers.cpp' |
199 | --- libs/UbuntuGestures/DebugHelpers.cpp 1970-01-01 00:00:00 +0000 |
200 | +++ libs/UbuntuGestures/DebugHelpers.cpp 2014-10-10 15:05:15 +0000 |
201 | @@ -0,0 +1,68 @@ |
202 | +/* |
203 | + * Copyright (C) 2014 Canonical, Ltd. |
204 | + * |
205 | + * This program is free software; you can redistribute it and/or modify |
206 | + * it under the terms of the GNU General Public License as published by |
207 | + * the Free Software Foundation; version 3. |
208 | + * |
209 | + * This program is distributed in the hope that it will be useful, |
210 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
211 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
212 | + * GNU General Public License for more details. |
213 | + * |
214 | + * You should have received a copy of the GNU General Public License |
215 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
216 | + */ |
217 | + |
218 | +#include "DebugHelpers.h" |
219 | +#include <QTouchEvent> |
220 | + |
221 | +QString touchPointStateToString(Qt::TouchPointState state) |
222 | +{ |
223 | + switch (state) { |
224 | + case Qt::TouchPointPressed: |
225 | + return QString("pressed"); |
226 | + case Qt::TouchPointMoved: |
227 | + return QString("moved"); |
228 | + case Qt::TouchPointStationary: |
229 | + return QString("stationary"); |
230 | + case Qt::TouchPointReleased: |
231 | + return QString("released"); |
232 | + default: |
233 | + return QString("INVALID_STATE"); |
234 | + } |
235 | +} |
236 | + |
237 | +QString touchEventToString(const QTouchEvent *ev) |
238 | +{ |
239 | + QString message; |
240 | + |
241 | + switch (ev->type()) { |
242 | + case QEvent::TouchBegin: |
243 | + message.append("TouchBegin "); |
244 | + break; |
245 | + case QEvent::TouchUpdate: |
246 | + message.append("TouchUpdate "); |
247 | + break; |
248 | + case QEvent::TouchEnd: |
249 | + message.append("TouchEnd "); |
250 | + break; |
251 | + case QEvent::TouchCancel: |
252 | + message.append("TouchCancel "); |
253 | + break; |
254 | + default: |
255 | + message.append("INVALID_TOUCH_EVENT_TYPE "); |
256 | + } |
257 | + |
258 | + foreach(const QTouchEvent::TouchPoint& touchPoint, ev->touchPoints()) { |
259 | + message.append( |
260 | + QString("(id:%1, state:%2, scenePos:(%3,%4)) ") |
261 | + .arg(touchPoint.id()) |
262 | + .arg(touchPointStateToString(touchPoint.state())) |
263 | + .arg(touchPoint.scenePos().x()) |
264 | + .arg(touchPoint.scenePos().y()) |
265 | + ); |
266 | + } |
267 | + |
268 | + return message; |
269 | +} |
270 | |
271 | === added file 'libs/UbuntuGestures/DebugHelpers.h' |
272 | --- libs/UbuntuGestures/DebugHelpers.h 1970-01-01 00:00:00 +0000 |
273 | +++ libs/UbuntuGestures/DebugHelpers.h 2014-10-10 15:05:15 +0000 |
274 | @@ -0,0 +1,29 @@ |
275 | +/* |
276 | + * Copyright (C) 2014 Canonical, Ltd. |
277 | + * |
278 | + * This program is free software; you can redistribute it and/or modify |
279 | + * it under the terms of the GNU General Public License as published by |
280 | + * the Free Software Foundation; version 3. |
281 | + * |
282 | + * This program is distributed in the hope that it will be useful, |
283 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
284 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
285 | + * GNU General Public License for more details. |
286 | + * |
287 | + * You should have received a copy of the GNU General Public License |
288 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
289 | + */ |
290 | + |
291 | +#ifndef UBUNTUGESTURES_DEBUG_HELPER_H |
292 | +#define UBUNTUGESTURES_DEBUG_HELPER_H |
293 | + |
294 | +#include <QString> |
295 | + |
296 | +#include "UbuntuGesturesGlobal.h" |
297 | + |
298 | +class QTouchEvent; |
299 | + |
300 | +UBUNTUGESTURES_EXPORT QString touchPointStateToString(Qt::TouchPointState state); |
301 | +UBUNTUGESTURES_EXPORT QString touchEventToString(const QTouchEvent *ev); |
302 | + |
303 | +#endif // UBUNTUGESTURES_DEBUG_HELPER_H |
304 | |
305 | === added file 'libs/UbuntuGestures/Pool.h' |
306 | --- libs/UbuntuGestures/Pool.h 1970-01-01 00:00:00 +0000 |
307 | +++ libs/UbuntuGestures/Pool.h 2014-10-10 15:05:15 +0000 |
308 | @@ -0,0 +1,134 @@ |
309 | +/* |
310 | + * Copyright (C) 2014 Canonical, Ltd. |
311 | + * |
312 | + * This program is free software; you can redistribute it and/or modify |
313 | + * it under the terms of the GNU General Public License as published by |
314 | + * the Free Software Foundation; version 3. |
315 | + * |
316 | + * This program is distributed in the hope that it will be useful, |
317 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
318 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
319 | + * GNU General Public License for more details. |
320 | + * |
321 | + * You should have received a copy of the GNU General Public License |
322 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
323 | + */ |
324 | + |
325 | +#ifndef UBUNTUGESTURES_POOL_H |
326 | +#define UBUNTUGESTURES_POOL_H |
327 | + |
328 | +#include <QVector> |
329 | + |
330 | +#include "UbuntuGesturesGlobal.h" |
331 | + |
332 | +/* |
333 | + An object pool. |
334 | + Avoids unnecessary creations/initializations and deletions/destructions of items. Useful |
335 | + in a scenario where items are created and destroyed very frequently but the total number |
336 | + of items at any given time remains small. They're stored in a unordered fashion. |
337 | + |
338 | + To be used in Pool, ItemType needs to have the following methods: |
339 | + |
340 | + - ItemType(); |
341 | + |
342 | + A constructor that takes no parameters. An object contructed with it must return false if |
343 | + isValid() is called. |
344 | + |
345 | + - bool isValid() const; |
346 | + |
347 | + Returns wheter the object holds a valid , "filled" state or is empty. |
348 | + Used by Pool to check if the slot occupied by this object is actually available. |
349 | + |
350 | + - void reset(); |
351 | + |
352 | + Resets the object to its initial, empty, state. After calling this method, isValid() must |
353 | + return false. |
354 | + */ |
355 | +template <class ItemType> class Pool |
356 | +{ |
357 | +public: |
358 | + Pool() : m_lastUsedIndex(-1) { |
359 | + } |
360 | + |
361 | + class Iterator { |
362 | + public: |
363 | + Iterator() : index(-1), item(nullptr) {} |
364 | + Iterator(int index, ItemType *item) |
365 | + : index(index), item(item) {} |
366 | + |
367 | + ItemType *operator->() const { return item; } |
368 | + ItemType &operator*() const { return *item; } |
369 | + ItemType &value() const { return *item; } |
370 | + |
371 | + Iterator &operator= (const Iterator& other) { |
372 | + index = other.index; |
373 | + item = other.item; |
374 | + |
375 | + // by convention, always return *this |
376 | + return *this; |
377 | + } |
378 | + |
379 | + operator bool() const { return item != nullptr; } |
380 | + |
381 | + int index; |
382 | + ItemType *item; |
383 | + }; |
384 | + |
385 | + Iterator &end() const { return Iterator(); } |
386 | + |
387 | + ItemType &getEmptySlot() { |
388 | + Q_ASSERT(m_lastUsedIndex < m_slots.size()); |
389 | + |
390 | + // Look for an in-between vacancy first |
391 | + for (int i = 0; i < m_lastUsedIndex; ++i) { |
392 | + ItemType &item = m_slots[i]; |
393 | + if (!item.isValid()) { |
394 | + return item; |
395 | + } |
396 | + } |
397 | + |
398 | + ++m_lastUsedIndex; |
399 | + if (m_lastUsedIndex >= m_slots.size()) { |
400 | + m_slots.resize(m_lastUsedIndex + 1); |
401 | + } |
402 | + |
403 | + return m_slots[m_lastUsedIndex]; |
404 | + } |
405 | + |
406 | + void freeSlot(Iterator &iterator) { |
407 | + m_slots[iterator.index].reset(); |
408 | + if (iterator.index == m_lastUsedIndex) { |
409 | + do { |
410 | + --m_lastUsedIndex; |
411 | + } while (m_lastUsedIndex >= 0 && !m_slots.at(m_lastUsedIndex).isValid()); |
412 | + } |
413 | + } |
414 | + |
415 | + // Iterates through all valid items (i.e. the occupied slots) |
416 | + // calling the given function, with the option of ending the loop early. |
417 | + // |
418 | + // bool Func(Iterator& item) |
419 | + // |
420 | + // Returning true means it wants to continue the "for" loop, false |
421 | + // terminates the loop. |
422 | + template<typename Func> void forEach(Func func) { |
423 | + Iterator it; |
424 | + for (it.index = 0; it.index <= m_lastUsedIndex; ++it.index) { |
425 | + it.item = &m_slots[it.index]; |
426 | + if (!it.item->isValid()) |
427 | + continue; |
428 | + |
429 | + if (!func(it)) |
430 | + break; |
431 | + } |
432 | + } |
433 | + |
434 | + bool isEmpty() const { return m_lastUsedIndex == -1; } |
435 | + |
436 | + |
437 | +private: |
438 | + QVector<ItemType> m_slots; |
439 | + int m_lastUsedIndex; |
440 | +}; |
441 | + |
442 | +#endif // UBUNTUGESTURES_POOL_H |
443 | |
444 | === added file 'libs/UbuntuGestures/Timer.cpp' |
445 | --- libs/UbuntuGestures/Timer.cpp 1970-01-01 00:00:00 +0000 |
446 | +++ libs/UbuntuGestures/Timer.cpp 2014-10-10 15:05:15 +0000 |
447 | @@ -0,0 +1,109 @@ |
448 | +/* |
449 | + * Copyright (C) 2014 Canonical, Ltd. |
450 | + * |
451 | + * This program is free software; you can redistribute it and/or modify |
452 | + * it under the terms of the GNU General Public License as published by |
453 | + * the Free Software Foundation; version 3. |
454 | + * |
455 | + * This program is distributed in the hope that it will be useful, |
456 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
457 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
458 | + * GNU General Public License for more details. |
459 | + * |
460 | + * You should have received a copy of the GNU General Public License |
461 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
462 | + */ |
463 | + |
464 | +#include "Timer.h" |
465 | + |
466 | +namespace UbuntuGestures { |
467 | + |
468 | +Timer::Timer(QObject *parent) : AbstractTimer(parent) |
469 | +{ |
470 | + m_timer.setSingleShot(false); |
471 | + connect(&m_timer, &QTimer::timeout, this, &AbstractTimer::timeout); |
472 | +} |
473 | + |
474 | +int Timer::interval() const |
475 | +{ |
476 | + return m_timer.interval(); |
477 | +} |
478 | + |
479 | +void Timer::setInterval(int msecs) |
480 | +{ |
481 | + m_timer.setInterval(msecs); |
482 | +} |
483 | + |
484 | +void Timer::start() |
485 | +{ |
486 | + m_timer.start(); |
487 | + AbstractTimer::start(); |
488 | +} |
489 | + |
490 | +void Timer::stop() |
491 | +{ |
492 | + m_timer.stop(); |
493 | + AbstractTimer::stop(); |
494 | +} |
495 | + |
496 | +bool Timer::isSingleShot() const |
497 | +{ |
498 | + return m_timer.isSingleShot(); |
499 | +} |
500 | + |
501 | +void Timer::setSingleShot(bool value) |
502 | +{ |
503 | + m_timer.setSingleShot(value); |
504 | +} |
505 | + |
506 | +/////////////////////////////////// FakeTimer ////////////////////////////////// |
507 | + |
508 | +FakeTimer::FakeTimer(QObject *parent) |
509 | + : UbuntuGestures::AbstractTimer(parent) |
510 | + , m_interval(0) |
511 | + , m_singleShot(false) |
512 | +{ |
513 | +} |
514 | + |
515 | +int FakeTimer::interval() const |
516 | +{ |
517 | + return m_interval; |
518 | +} |
519 | + |
520 | +void FakeTimer::setInterval(int msecs) |
521 | +{ |
522 | + m_interval = msecs; |
523 | +} |
524 | + |
525 | +bool FakeTimer::isSingleShot() const |
526 | +{ |
527 | + return m_singleShot; |
528 | +} |
529 | + |
530 | +void FakeTimer::setSingleShot(bool value) |
531 | +{ |
532 | + m_singleShot = value; |
533 | +} |
534 | + |
535 | +/////////////////////////////////// FakeTimerFactory ////////////////////////////////// |
536 | + |
537 | +AbstractTimer *FakeTimerFactory::createTimer(QObject *parent) |
538 | +{ |
539 | + FakeTimer *fakeTimer = new FakeTimer(parent); |
540 | + |
541 | + timers.append(fakeTimer); |
542 | + |
543 | + return fakeTimer; |
544 | +} |
545 | + |
546 | +void FakeTimerFactory::makeRunningTimersTimeout() |
547 | +{ |
548 | + for (int i = 0; i < timers.count(); ++i) { |
549 | + FakeTimer *timer = timers[i].data(); |
550 | + if (timer && timer->isRunning()) { |
551 | + timer->emitTimeout(); |
552 | + } |
553 | + } |
554 | +} |
555 | + |
556 | +} // namespace UbuntuGestures |
557 | |
558 | === added file 'libs/UbuntuGestures/Timer.h' |
559 | --- libs/UbuntuGestures/Timer.h 1970-01-01 00:00:00 +0000 |
560 | +++ libs/UbuntuGestures/Timer.h 2014-10-10 15:05:15 +0000 |
561 | @@ -0,0 +1,105 @@ |
562 | +/* |
563 | + * Copyright (C) 2014 Canonical, Ltd. |
564 | + * |
565 | + * This program is free software; you can redistribute it and/or modify |
566 | + * it under the terms of the GNU General Public License as published by |
567 | + * the Free Software Foundation; version 3. |
568 | + * |
569 | + * This program is distributed in the hope that it will be useful, |
570 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
571 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
572 | + * GNU General Public License for more details. |
573 | + * |
574 | + * You should have received a copy of the GNU General Public License |
575 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
576 | + */ |
577 | + |
578 | +#ifndef UBUNTUGESTURES_TIMER_H |
579 | +#define UBUNTUGESTURES_TIMER_H |
580 | + |
581 | +#include "UbuntuGesturesGlobal.h" |
582 | + |
583 | +#include <QObject> |
584 | +#include <QPointer> |
585 | +#include <QTimer> |
586 | + |
587 | +namespace UbuntuGestures { |
588 | + |
589 | +/* Defines an interface for a Timer. Useful for tests. */ |
590 | +class UBUNTUGESTURES_EXPORT AbstractTimer : public QObject |
591 | +{ |
592 | + Q_OBJECT |
593 | +public: |
594 | + AbstractTimer(QObject *parent) : QObject(parent), m_isRunning(false) {} |
595 | + virtual int interval() const = 0; |
596 | + virtual void setInterval(int msecs) = 0; |
597 | + virtual void start() { m_isRunning = true; } |
598 | + virtual void stop() { m_isRunning = false; } |
599 | + bool isRunning() const { return m_isRunning; } |
600 | + virtual bool isSingleShot() const = 0; |
601 | + virtual void setSingleShot(bool value) = 0; |
602 | +Q_SIGNALS: |
603 | + void timeout(); |
604 | +private: |
605 | + bool m_isRunning; |
606 | +}; |
607 | + |
608 | +/* Essentially a QTimer wrapper */ |
609 | +class UBUNTUGESTURES_EXPORT Timer : public AbstractTimer |
610 | +{ |
611 | + Q_OBJECT |
612 | +public: |
613 | + Timer(QObject *parent = nullptr); |
614 | + |
615 | + int interval() const override; |
616 | + void setInterval(int msecs) override; |
617 | + void start() override; |
618 | + void stop() override; |
619 | + bool isSingleShot() const override; |
620 | + void setSingleShot(bool value) override; |
621 | +private: |
622 | + QTimer m_timer; |
623 | +}; |
624 | + |
625 | +/* For tests */ |
626 | +class UBUNTUGESTURES_EXPORT FakeTimer : public AbstractTimer |
627 | +{ |
628 | + Q_OBJECT |
629 | +public: |
630 | + FakeTimer(QObject *parent = nullptr); |
631 | + |
632 | + virtual void emitTimeout() { Q_EMIT timeout(); } |
633 | + |
634 | + int interval() const override; |
635 | + void setInterval(int msecs) override; |
636 | + bool isSingleShot() const override; |
637 | + void setSingleShot(bool value) override; |
638 | +private: |
639 | + int m_interval; |
640 | + bool m_singleShot; |
641 | +}; |
642 | + |
643 | +class UBUNTUGESTURES_EXPORT AbstractTimerFactory |
644 | +{ |
645 | +public: |
646 | + virtual ~AbstractTimerFactory() {} |
647 | + virtual AbstractTimer *createTimer(QObject *parent = nullptr) = 0; |
648 | +}; |
649 | + |
650 | +class UBUNTUGESTURES_EXPORT TimerFactory : public AbstractTimerFactory |
651 | +{ |
652 | +public: |
653 | + AbstractTimer *createTimer(QObject *parent = nullptr) override { return new Timer(parent); } |
654 | +}; |
655 | + |
656 | +class UBUNTUGESTURES_EXPORT FakeTimerFactory : public AbstractTimerFactory |
657 | +{ |
658 | +public: |
659 | + AbstractTimer *createTimer(QObject *parent = nullptr) override; |
660 | + void makeRunningTimersTimeout(); |
661 | + QList<QPointer<FakeTimer>> timers; |
662 | +}; |
663 | + |
664 | +} // namespace UbuntuGestures |
665 | + |
666 | +#endif // UBUNTUGESTURES_TIMER_H |
667 | |
668 | === added file 'libs/UbuntuGestures/TouchOwnershipEvent.cpp' |
669 | --- libs/UbuntuGestures/TouchOwnershipEvent.cpp 1970-01-01 00:00:00 +0000 |
670 | +++ libs/UbuntuGestures/TouchOwnershipEvent.cpp 2014-10-10 15:05:15 +0000 |
671 | @@ -0,0 +1,35 @@ |
672 | +/* |
673 | + * Copyright (C) 2014 Canonical, Ltd. |
674 | + * |
675 | + * This program is free software; you can redistribute it and/or modify |
676 | + * it under the terms of the GNU General Public License as published by |
677 | + * the Free Software Foundation; version 3. |
678 | + * |
679 | + * This program is distributed in the hope that it will be useful, |
680 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
681 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
682 | + * GNU General Public License for more details. |
683 | + * |
684 | + * You should have received a copy of the GNU General Public License |
685 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
686 | + */ |
687 | + |
688 | +#include "TouchOwnershipEvent.h" |
689 | + |
690 | +QEvent::Type TouchOwnershipEvent::m_touchOwnershipType = (QEvent::Type)-1; |
691 | + |
692 | +TouchOwnershipEvent::TouchOwnershipEvent(int touchId, bool gained) |
693 | + : QEvent(touchOwnershipEventType()) |
694 | + , m_touchId(touchId) |
695 | + , m_gained(gained) |
696 | +{ |
697 | +} |
698 | + |
699 | +QEvent::Type TouchOwnershipEvent::touchOwnershipEventType() |
700 | +{ |
701 | + if (m_touchOwnershipType == (QEvent::Type)-1) { |
702 | + m_touchOwnershipType = (QEvent::Type)registerEventType(); |
703 | + } |
704 | + |
705 | + return m_touchOwnershipType; |
706 | +} |
707 | |
708 | === added file 'libs/UbuntuGestures/TouchOwnershipEvent.h' |
709 | --- libs/UbuntuGestures/TouchOwnershipEvent.h 1970-01-01 00:00:00 +0000 |
710 | +++ libs/UbuntuGestures/TouchOwnershipEvent.h 2014-10-10 15:05:15 +0000 |
711 | @@ -0,0 +1,50 @@ |
712 | +/* |
713 | + * Copyright (C) 2014 Canonical, Ltd. |
714 | + * |
715 | + * This program is free software; you can redistribute it and/or modify |
716 | + * it under the terms of the GNU General Public License as published by |
717 | + * the Free Software Foundation; version 3. |
718 | + * |
719 | + * This program is distributed in the hope that it will be useful, |
720 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
721 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
722 | + * GNU General Public License for more details. |
723 | + * |
724 | + * You should have received a copy of the GNU General Public License |
725 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
726 | + */ |
727 | + |
728 | +#ifndef UBUNTU_TOUCHOWNERSHIPEVENT_H |
729 | +#define UBUNTU_TOUCHOWNERSHIPEVENT_H |
730 | + |
731 | +#include <QEvent> |
732 | +#include "UbuntuGesturesGlobal.h" |
733 | + |
734 | +/* |
735 | + When an item get an ownership event for a touch it can grab/steal that touch |
736 | + with a clean conscience. |
737 | + */ |
738 | +class UBUNTUGESTURES_EXPORT TouchOwnershipEvent : public QEvent |
739 | +{ |
740 | +public: |
741 | + TouchOwnershipEvent(int touchId, bool gained); |
742 | + |
743 | + static Type touchOwnershipEventType(); |
744 | + |
745 | + /* |
746 | + Whether ownership was gained (true) or lost (false) |
747 | + */ |
748 | + bool gained() const { return m_gained; } |
749 | + |
750 | + /* |
751 | + Id of the touch whose ownership was granted. |
752 | + */ |
753 | + int touchId() const { return m_touchId; } |
754 | + |
755 | +private: |
756 | + static Type m_touchOwnershipType; |
757 | + int m_touchId; |
758 | + bool m_gained; |
759 | +}; |
760 | + |
761 | +#endif // UBUNTU_TOUCHOWNERSHIPEVENT_H |
762 | |
763 | === added file 'libs/UbuntuGestures/TouchRegistry.cpp' |
764 | --- libs/UbuntuGestures/TouchRegistry.cpp 1970-01-01 00:00:00 +0000 |
765 | +++ libs/UbuntuGestures/TouchRegistry.cpp 2014-10-10 15:05:15 +0000 |
766 | @@ -0,0 +1,500 @@ |
767 | +/* |
768 | + * Copyright (C) 2014 Canonical, Ltd. |
769 | + * |
770 | + * This program is free software; you can redistribute it and/or modify |
771 | + * it under the terms of the GNU General Public License as published by |
772 | + * the Free Software Foundation; version 3. |
773 | + * |
774 | + * This program is distributed in the hope that it will be useful, |
775 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
776 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
777 | + * GNU General Public License for more details. |
778 | + * |
779 | + * You should have received a copy of the GNU General Public License |
780 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
781 | + */ |
782 | + |
783 | +#include "TouchRegistry.h" |
784 | + |
785 | +#include <QCoreApplication> |
786 | +#include <QDebug> |
787 | + |
788 | +#pragma GCC diagnostic push |
789 | +#pragma GCC diagnostic ignored "-pedantic" |
790 | +#include <private/qquickitem_p.h> |
791 | +#pragma GCC diagnostic pop |
792 | + |
793 | +#include "CandidateInactivityTimer.h" |
794 | +#include "Timer.h" |
795 | +#include "TouchOwnershipEvent.h" |
796 | +#include "UnownedTouchEvent.h" |
797 | + |
798 | +#define TOUCHREGISTRY_DEBUG 0 |
799 | + |
800 | +#if TOUCHREGISTRY_DEBUG |
801 | + #include "DebugHelpers.h" |
802 | + #define UG_DEBUG qDebug() << "[TouchRegistry]" |
803 | +#endif // TOUCHREGISTRY_DEBUG |
804 | + |
805 | +using namespace UbuntuGestures; |
806 | + |
807 | +TouchRegistry *TouchRegistry::m_instance = nullptr; |
808 | + |
809 | +TouchRegistry::TouchRegistry(QObject *parent) |
810 | + : TouchRegistry(parent, new TimerFactory) |
811 | +{ |
812 | +} |
813 | + |
814 | +TouchRegistry::TouchRegistry(QObject *parent, AbstractTimerFactory *timerFactory) |
815 | + : QObject(parent) |
816 | + , m_inDispatchLoop(false) |
817 | + , m_timerFactory(timerFactory) |
818 | +{ |
819 | + if (m_instance == nullptr) { |
820 | + m_instance = this; |
821 | + } else { |
822 | + qFatal("Cannot have more than one instance of TouchRegistry. It must be a singleton."); |
823 | + } |
824 | +} |
825 | + |
826 | +TouchRegistry::~TouchRegistry() |
827 | +{ |
828 | + Q_ASSERT(m_instance != nullptr); |
829 | + m_instance = nullptr; |
830 | + delete m_timerFactory; |
831 | +} |
832 | + |
833 | +void TouchRegistry::update(const QTouchEvent *event) |
834 | +{ |
835 | + #if TOUCHREGISTRY_DEBUG |
836 | + UG_DEBUG << "got" << qPrintable(touchEventToString(event)); |
837 | + #endif |
838 | + |
839 | + const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints(); |
840 | + for (int i = 0; i < touchPoints.count(); ++i) { |
841 | + const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i); |
842 | + if (touchPoint.state() == Qt::TouchPointPressed) { |
843 | + TouchInfo &touchInfo = m_touchInfoPool.getEmptySlot(); |
844 | + touchInfo.init(touchPoint.id()); |
845 | + } else if (touchPoint.state() == Qt::TouchPointReleased) { |
846 | + Pool<TouchInfo>::Iterator touchInfo = findTouchInfo(touchPoint.id()); |
847 | + |
848 | + touchInfo->physicallyEnded = true; |
849 | + } |
850 | + } |
851 | + |
852 | + deliverTouchUpdatesToUndecidedCandidatesAndWatchers(event); |
853 | + |
854 | + freeEndedTouchInfos(); |
855 | +} |
856 | + |
857 | +void TouchRegistry::deliverTouchUpdatesToUndecidedCandidatesAndWatchers(const QTouchEvent *event) |
858 | +{ |
859 | + // TODO: Look into how we could optimize this whole thing. |
860 | + // Although it's not really a problem as we should have at most two candidates |
861 | + // for each point and there should not be many active points at any given moment. |
862 | + // But having three nested for-loops does scare. |
863 | + |
864 | + // TODO: Don't send it to the object that is already receiving the regular event |
865 | + // because QQuickWindow is sending it to him (i.e., he's the touch owner from Qt's point of view) |
866 | + // Problem is, we cannnot easily get this information. |
867 | + |
868 | + const QList<QTouchEvent::TouchPoint> &updatedTouchPoints = event->touchPoints(); |
869 | + |
870 | + // Maps an item to the touches in this event he should be informed about. |
871 | + // E.g.: a QTouchEvent might have three touches but a given item might be interested in only |
872 | + // one of them. So he will get a UnownedTouchEvent from this QTouchEvent containing only that |
873 | + // touch point. |
874 | + QMap<QQuickItem*, QList<int>> touchIdsForItems; |
875 | + |
876 | + // Build touchIdsForItems |
877 | + m_touchInfoPool.forEach([&](Pool<TouchInfo>::Iterator &touchInfo) { |
878 | + if (touchInfo->isOwned() && touchInfo->watchers.isEmpty()) |
879 | + return true; |
880 | + |
881 | + for (int j = 0; j < updatedTouchPoints.count(); ++j) { |
882 | + if (updatedTouchPoints[j].id() == touchInfo->id) { |
883 | + if (!touchInfo->isOwned()) { |
884 | + for (int i = 0; i < touchInfo->candidates.count(); ++i) { |
885 | + CandidateInfo &candidate = touchInfo->candidates[i]; |
886 | + Q_ASSERT(!candidate.item.isNull()); |
887 | + touchIdsForItems[candidate.item.data()].append(touchInfo->id); |
888 | + } |
889 | + } |
890 | + |
891 | + const QList<QPointer<QQuickItem>> &watchers = touchInfo->watchers; |
892 | + for (int i = 0; i < watchers.count(); ++i) { |
893 | + if (!watchers[i].isNull()) { |
894 | + touchIdsForItems[watchers[i].data()].append(touchInfo->id); |
895 | + } |
896 | + } |
897 | + |
898 | + return true; |
899 | + } |
900 | + } |
901 | + |
902 | + return true; |
903 | + }); |
904 | + |
905 | + // TODO: Consider what happens if an item calls any of TouchRegistry's public methods |
906 | + // from the event handler callback. |
907 | + m_inDispatchLoop = true; |
908 | + auto it = touchIdsForItems.constBegin(); |
909 | + while (it != touchIdsForItems.constEnd()) { |
910 | + QQuickItem *item = it.key(); |
911 | + const QList<int> &touchIds = it.value(); |
912 | + dispatchPointsToItem(event, touchIds, item); |
913 | + ++it; |
914 | + }; |
915 | + m_inDispatchLoop = false; |
916 | +} |
917 | + |
918 | +void TouchRegistry::freeEndedTouchInfos() |
919 | +{ |
920 | + m_touchInfoPool.forEach([&](Pool<TouchInfo>::Iterator &touchInfo) { |
921 | + if (touchInfo->ended()) { |
922 | + m_touchInfoPool.freeSlot(touchInfo); |
923 | + } |
924 | + return true; |
925 | + }); |
926 | +} |
927 | + |
928 | +/* |
929 | + Extracts the touches with the given touchIds from event and send them in a |
930 | + UnownedTouchEvent to the given item |
931 | + */ |
932 | +void TouchRegistry::dispatchPointsToItem(const QTouchEvent *event, const QList<int> &touchIds, |
933 | + QQuickItem *item) |
934 | +{ |
935 | + Qt::TouchPointStates touchPointStates = 0; |
936 | + QList<QTouchEvent::TouchPoint> touchPoints; |
937 | + |
938 | + const QList<QTouchEvent::TouchPoint> &allTouchPoints = event->touchPoints(); |
939 | + |
940 | + QTransform windowToCandidateTransform = QQuickItemPrivate::get(item)->windowToItemTransform(); |
941 | + QMatrix4x4 windowToCandidateMatrix(windowToCandidateTransform); |
942 | + |
943 | + for (int i = 0; i < allTouchPoints.count(); ++i) { |
944 | + const QTouchEvent::TouchPoint &originalTouchPoint = allTouchPoints[i]; |
945 | + if (touchIds.contains(originalTouchPoint.id())) { |
946 | + QTouchEvent::TouchPoint touchPoint = originalTouchPoint; |
947 | + |
948 | + translateTouchPointFromScreenToWindowCoords(touchPoint); |
949 | + |
950 | + // Set the point's local coordinates to that of the item |
951 | + touchPoint.setRect(windowToCandidateTransform.mapRect(touchPoint.sceneRect())); |
952 | + touchPoint.setStartPos(windowToCandidateTransform.map(touchPoint.startScenePos())); |
953 | + touchPoint.setLastPos(windowToCandidateTransform.map(touchPoint.lastScenePos())); |
954 | + touchPoint.setVelocity(windowToCandidateMatrix.mapVector(touchPoint.velocity()).toVector2D()); |
955 | + |
956 | + touchPoints.append(touchPoint); |
957 | + touchPointStates |= touchPoint.state(); |
958 | + } |
959 | + } |
960 | + |
961 | + QTouchEvent *eventForItem = new QTouchEvent(event->type(), |
962 | + event->device(), |
963 | + event->modifiers(), |
964 | + touchPointStates, |
965 | + touchPoints); |
966 | + eventForItem->setWindow(event->window()); |
967 | + eventForItem->setTimestamp(event->timestamp()); |
968 | + eventForItem->setTarget(event->target()); |
969 | + |
970 | + UnownedTouchEvent unownedTouchEvent(eventForItem); |
971 | + |
972 | + #if TOUCHREGISTRY_DEBUG |
973 | + UG_DEBUG << "Sending unowned" << qPrintable(touchEventToString(eventForItem)) |
974 | + << "to" << item; |
975 | + #endif |
976 | + |
977 | + QCoreApplication::sendEvent(item, &unownedTouchEvent); |
978 | +} |
979 | + |
980 | +void TouchRegistry::translateTouchPointFromScreenToWindowCoords(QTouchEvent::TouchPoint &touchPoint) |
981 | +{ |
982 | + touchPoint.setScreenRect(touchPoint.sceneRect()); |
983 | + touchPoint.setStartScreenPos(touchPoint.startScenePos()); |
984 | + touchPoint.setLastScreenPos(touchPoint.lastScenePos()); |
985 | + |
986 | + touchPoint.setSceneRect(touchPoint.rect()); |
987 | + touchPoint.setStartScenePos(touchPoint.startPos()); |
988 | + touchPoint.setLastScenePos(touchPoint.lastPos()); |
989 | +} |
990 | + |
991 | +bool TouchRegistry::eventFilter(QObject *watched, QEvent *event) |
992 | +{ |
993 | + Q_UNUSED(watched); |
994 | + |
995 | + switch (event->type()) { |
996 | + case QEvent::TouchBegin: |
997 | + case QEvent::TouchUpdate: |
998 | + case QEvent::TouchEnd: |
999 | + case QEvent::TouchCancel: |
1000 | + update(static_cast<QTouchEvent*>(event)); |
1001 | + break; |
1002 | + default: |
1003 | + // do nothing |
1004 | + break; |
1005 | + } |
1006 | + |
1007 | + // Do not filter out the event. i.e., let it be handled further as |
1008 | + // we're just monitoring events |
1009 | + return false; |
1010 | +} |
1011 | + |
1012 | +void TouchRegistry::addCandidateOwnerForTouch(int id, QQuickItem *candidate) |
1013 | +{ |
1014 | + #if TOUCHREGISTRY_DEBUG |
1015 | + UG_DEBUG << "addCandidateOwnerForTouch id" << id << "candidate" << candidate; |
1016 | + #endif |
1017 | + |
1018 | + Pool<TouchInfo>::Iterator touchInfo = findTouchInfo(id); |
1019 | + if (!touchInfo) { qFatal("TouchRegistry: Failed to find TouchInfo"); } |
1020 | + |
1021 | + if (touchInfo->isOwned()) { |
1022 | + qWarning("TouchRegistry: trying to add candidate owner for a touch that's already owned"); |
1023 | + return; |
1024 | + } |
1025 | + |
1026 | + // TODO: Check if candidate already exists |
1027 | + |
1028 | + CandidateInfo candidateInfo; |
1029 | + candidateInfo.undecided = true; |
1030 | + candidateInfo.item = candidate; |
1031 | + candidateInfo.inactivityTimer = new CandidateInactivityTimer(id, candidate, |
1032 | + *m_timerFactory, |
1033 | + this); |
1034 | + connect(candidateInfo.inactivityTimer, &CandidateInactivityTimer::candidateDefaulted, |
1035 | + this, &TouchRegistry::rejectCandidateOwnerForTouch); |
1036 | + |
1037 | + touchInfo->candidates.append(candidateInfo); |
1038 | +} |
1039 | + |
1040 | +void TouchRegistry::addTouchWatcher(int touchId, QQuickItem *watcher) |
1041 | +{ |
1042 | + #if TOUCHREGISTRY_DEBUG |
1043 | + UG_DEBUG << "addTouchWatcher id" << touchId << "watcher" << watcher; |
1044 | + #endif |
1045 | + |
1046 | + Pool<TouchInfo>::Iterator touchInfo = findTouchInfo(touchId); |
1047 | + if (!touchInfo) { qFatal("TouchRegistry: Failed to find TouchInfo"); } |
1048 | + |
1049 | + // TODO: Check if watcher already exists |
1050 | + |
1051 | + touchInfo->watchers.append(watcher); |
1052 | +} |
1053 | + |
1054 | +void TouchRegistry::removeCandidateOwnerForTouch(int id, QQuickItem *candidate) |
1055 | +{ |
1056 | + #if TOUCHREGISTRY_DEBUG |
1057 | + UG_DEBUG << "removeCandidateOwnerForTouch id" << id << "candidate" << candidate; |
1058 | + #endif |
1059 | + |
1060 | + Pool<TouchInfo>::Iterator touchInfo = findTouchInfo(id); |
1061 | + if (!touchInfo) { qFatal("TouchRegistry: Failed to find TouchInfo"); } |
1062 | + |
1063 | + int indexRemoved = -1; |
1064 | + |
1065 | + // TODO: check if the candidate is in fact the owner of the touch |
1066 | + |
1067 | + for (int i = 0; i < touchInfo->candidates.count() && indexRemoved == -1; ++i) { |
1068 | + CandidateInfo &candidateInfo = touchInfo->candidates[i]; |
1069 | + if (candidateInfo.item == candidate) { |
1070 | + Q_ASSERT(i > 0 || candidateInfo.undecided); |
1071 | + if (i == 0 && !candidateInfo.undecided) { |
1072 | + qCritical("TouchRegistry: touch owner is being removed."); |
1073 | + } |
1074 | + delete candidateInfo.inactivityTimer; |
1075 | + candidateInfo.inactivityTimer = nullptr; |
1076 | + touchInfo->candidates.removeAt(i); |
1077 | + indexRemoved = i; |
1078 | + } |
1079 | + } |
1080 | + |
1081 | + if (indexRemoved == 0) { |
1082 | + // the top candidate has been removed. if the new top candidate |
1083 | + // wants the touch let him know he's now the owner. |
1084 | + if (touchInfo->isOwned()) { |
1085 | + touchInfo->notifyCandidatesOfOwnershipResolution(); |
1086 | + } |
1087 | + } |
1088 | + |
1089 | + if (!m_inDispatchLoop && touchInfo->ended()) { |
1090 | + m_touchInfoPool.freeSlot(touchInfo); |
1091 | + } |
1092 | +} |
1093 | + |
1094 | +void TouchRegistry::requestTouchOwnership(int id, QQuickItem *candidate) |
1095 | +{ |
1096 | + #if TOUCHREGISTRY_DEBUG |
1097 | + UG_DEBUG << "requestTouchOwnership id " << id << "candidate" << candidate; |
1098 | + #endif |
1099 | + |
1100 | + Pool<TouchInfo>::Iterator touchInfo = findTouchInfo(id); |
1101 | + if (!touchInfo) { qFatal("TouchRegistry: Failed to find TouchInfo"); } |
1102 | + |
1103 | + Q_ASSERT(!touchInfo->isOwned()); |
1104 | + |
1105 | + int candidateIndex = -1; |
1106 | + for (int i = 0; i < touchInfo->candidates.count(); ++i) { |
1107 | + CandidateInfo &candidateInfo = touchInfo->candidates[i]; |
1108 | + if (candidateInfo.item == candidate) { |
1109 | + candidateInfo.undecided = false; |
1110 | + delete candidateInfo.inactivityTimer; |
1111 | + candidateInfo.inactivityTimer = nullptr; |
1112 | + candidateIndex = i; |
1113 | + break; |
1114 | + } |
1115 | + } |
1116 | + |
1117 | + // add it as a candidate if not present yet |
1118 | + if (candidateIndex < 0) { |
1119 | + CandidateInfo candidateInfo; |
1120 | + candidateInfo.undecided = false; |
1121 | + candidateInfo.item = candidate; |
1122 | + candidateInfo.inactivityTimer = nullptr; |
1123 | + touchInfo->candidates.append(candidateInfo); |
1124 | + // it's the last one |
1125 | + candidateIndex = touchInfo->candidates.count() - 1; |
1126 | + } |
1127 | + |
1128 | + // If it's the top candidate it means it's now the owner. Let |
1129 | + // it know about it. |
1130 | + if (candidateIndex == 0) { |
1131 | + touchInfo->notifyCandidatesOfOwnershipResolution(); |
1132 | + } |
1133 | +} |
1134 | + |
1135 | +Pool<TouchRegistry::TouchInfo>::Iterator TouchRegistry::findTouchInfo(int id) |
1136 | +{ |
1137 | + Pool<TouchInfo>::Iterator touchInfo; |
1138 | + |
1139 | + m_touchInfoPool.forEach([&](Pool<TouchInfo>::Iterator &someTouchInfo) -> bool { |
1140 | + if (someTouchInfo->id == id) { |
1141 | + touchInfo = someTouchInfo; |
1142 | + return false; |
1143 | + } else { |
1144 | + return true; |
1145 | + } |
1146 | + }); |
1147 | + |
1148 | + return touchInfo; |
1149 | +} |
1150 | + |
1151 | + |
1152 | +void TouchRegistry::rejectCandidateOwnerForTouch(int id, QQuickItem *candidate) |
1153 | +{ |
1154 | + // NB: It's technically possible that candidate is a dangling pointer at this point. |
1155 | + // Although that would most likely be due to a bug in our code. |
1156 | + // In any case, only dereference it after it's confirmed that it indeed exists. |
1157 | + |
1158 | + #if TOUCHREGISTRY_DEBUG |
1159 | + UG_DEBUG << "rejectCandidateOwnerForTouch id" << id << "candidate" << (void*)candidate; |
1160 | + #endif |
1161 | + |
1162 | + Pool<TouchInfo>::Iterator touchInfo = findTouchInfo(id); |
1163 | + if (!touchInfo) { |
1164 | + #if TOUCHREGISTRY_DEBUG |
1165 | + UG_DEBUG << "Failed to find TouchInfo for id" << id; |
1166 | + #endif |
1167 | + return; |
1168 | + } |
1169 | + |
1170 | + int rejectedCandidateIndex = -1; |
1171 | + |
1172 | + // Check if the given candidate is valid and still undecided |
1173 | + for (int i = 0; i < touchInfo->candidates.count() && rejectedCandidateIndex == -1; ++i) { |
1174 | + CandidateInfo &candidateInfo = touchInfo->candidates[i]; |
1175 | + if (candidateInfo.item == candidate) { |
1176 | + Q_ASSERT(i > 0 || candidateInfo.undecided); |
1177 | + if (i == 0 && !candidateInfo.undecided) { |
1178 | + qCritical() << "TouchRegistry: Can't reject item (" << (void*)candidate |
1179 | + << ") as it already owns touch" << id; |
1180 | + return; |
1181 | + } else { |
1182 | + // we found the guy and it's all fine. |
1183 | + rejectedCandidateIndex = i; |
1184 | + } |
1185 | + } |
1186 | + } |
1187 | + |
1188 | + // If we reached this point it's because the given candidate exists and is indeed undecided. |
1189 | + |
1190 | + Q_ASSERT(rejectedCandidateIndex >= 0 && rejectedCandidateIndex < touchInfo->candidates.size()); |
1191 | + |
1192 | + { |
1193 | + TouchOwnershipEvent lostOwnershipEvent(id, false /*gained*/); |
1194 | + QCoreApplication::sendEvent(candidate, &lostOwnershipEvent); |
1195 | + } |
1196 | + |
1197 | + touchInfo->candidates.removeAt(rejectedCandidateIndex); |
1198 | + |
1199 | + if (rejectedCandidateIndex == 0) { |
1200 | + // the top candidate has been removed. if the new top candidate |
1201 | + // wants the touch let him know he's now the owner. |
1202 | + if (touchInfo->isOwned()) { |
1203 | + touchInfo->notifyCandidatesOfOwnershipResolution(); |
1204 | + } |
1205 | + } |
1206 | +} |
1207 | + |
1208 | +////////////////////////////////////// TouchRegistry::TouchInfo //////////////////////////////////// |
1209 | + |
1210 | +TouchRegistry::TouchInfo::TouchInfo(int id) |
1211 | +{ |
1212 | + init(id); |
1213 | +} |
1214 | + |
1215 | +void TouchRegistry::TouchInfo::reset() |
1216 | +{ |
1217 | + id = -1; |
1218 | + |
1219 | + for (int i = 0; i < candidates.count(); ++i) { |
1220 | + CandidateInfo &candidate = candidates[i]; |
1221 | + delete candidate.inactivityTimer; |
1222 | + candidate.inactivityTimer.clear(); // shoundn't be needed but anyway... |
1223 | + } |
1224 | +} |
1225 | + |
1226 | +void TouchRegistry::TouchInfo::init(int id) |
1227 | +{ |
1228 | + this->id = id; |
1229 | + physicallyEnded = false; |
1230 | + candidates.clear(); |
1231 | + watchers.clear(); |
1232 | +} |
1233 | + |
1234 | +bool TouchRegistry::TouchInfo::isOwned() const |
1235 | +{ |
1236 | + return !candidates.isEmpty() && !candidates.first().undecided; |
1237 | +} |
1238 | + |
1239 | +bool TouchRegistry::TouchInfo::ended() const |
1240 | +{ |
1241 | + Q_ASSERT(isValid()); |
1242 | + return physicallyEnded && (isOwned() || candidates.isEmpty()); |
1243 | +} |
1244 | + |
1245 | +void TouchRegistry::TouchInfo::notifyCandidatesOfOwnershipResolution() |
1246 | +{ |
1247 | + Q_ASSERT(isOwned()); |
1248 | + |
1249 | + #if TOUCHREGISTRY_DEBUG |
1250 | + UG_DEBUG << "sending TouchOwnershipEvent(id =" << id |
1251 | + << " gained) to candidate" << candidates[0].item; |
1252 | + #endif |
1253 | + |
1254 | + TouchOwnershipEvent gainedOwnershipEvent(id, true /*gained*/); |
1255 | + QCoreApplication::sendEvent(candidates[0].item, &gainedOwnershipEvent); |
1256 | + |
1257 | + |
1258 | + TouchOwnershipEvent lostOwnershipEvent(id, false /*gained*/); |
1259 | + for (int i = 1; i < candidates.count(); ++i) { |
1260 | + #if TOUCHREGISTRY_DEBUG |
1261 | + UG_DEBUG << "sending TouchWonershipEvent(id =" << id << " lost) to candidate" |
1262 | + << candidates[i].item; |
1263 | + #endif |
1264 | + QCoreApplication::sendEvent(candidates[i].item, &lostOwnershipEvent); |
1265 | + } |
1266 | +} |
1267 | |
1268 | === added file 'libs/UbuntuGestures/TouchRegistry.h' |
1269 | --- libs/UbuntuGestures/TouchRegistry.h 1970-01-01 00:00:00 +0000 |
1270 | +++ libs/UbuntuGestures/TouchRegistry.h 2014-10-10 15:05:15 +0000 |
1271 | @@ -0,0 +1,182 @@ |
1272 | +/* |
1273 | + * Copyright (C) 2014 Canonical, Ltd. |
1274 | + * |
1275 | + * This program is free software; you can redistribute it and/or modify |
1276 | + * it under the terms of the GNU General Public License as published by |
1277 | + * the Free Software Foundation; version 3. |
1278 | + * |
1279 | + * This program is distributed in the hope that it will be useful, |
1280 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1281 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1282 | + * GNU General Public License for more details. |
1283 | + * |
1284 | + * You should have received a copy of the GNU General Public License |
1285 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1286 | + */ |
1287 | + |
1288 | +#ifndef UNITY_TOUCHREGISTRY_H |
1289 | +#define UNITY_TOUCHREGISTRY_H |
1290 | + |
1291 | +#include <QQuickItem> |
1292 | +#include <QObject> |
1293 | +#include <QPointer> |
1294 | +#include <QTouchEvent> |
1295 | +#include <QVector> |
1296 | + |
1297 | +#include "UbuntuGesturesGlobal.h" |
1298 | +#include "CandidateInactivityTimer.h" |
1299 | +#include "Pool.h" |
1300 | + |
1301 | +namespace UbuntuGestures { |
1302 | + class AbstractTimerFactory; |
1303 | +} |
1304 | + |
1305 | +/* |
1306 | + Where the ownership of touches is registered. |
1307 | + |
1308 | + Singleton used for adding a touch point ownership model analogous to the one |
1309 | + described in the XInput 2.2 protocol[1] on top of the existing input dispatch logic in QQuickWindow. |
1310 | + |
1311 | + It provides a much more flexible and powerful way of dealing with pointer ownership than the existing |
1312 | + mechanisms in Qt. Namely QQuickItem::grabTouchPoints, QuickItem::keepTouchGrab, |
1313 | + QQuickItem::setFiltersChildMouseEvents, QQuickItem::ungrabTouchPoints and QQuickItem::touchUngrabEvent. |
1314 | + |
1315 | + Usage: |
1316 | + |
1317 | + 1- An item receives a a new touch point. If he's not sure whether he wants it yet, he calls: |
1318 | + TouchRegistry::instance()->addCandidateOwnerForTouch(touchId, this); |
1319 | + touchEvent->ignore(); |
1320 | + Ignoring the event is crucial so that it can be seen by other interested parties, which will |
1321 | + behave similarly. |
1322 | + |
1323 | + 2- That item will then start receiving UnownedTouchEvents for that touch from step 1. Once he's |
1324 | + made a decision he calls either: |
1325 | + TouchRegistry::instance()->requestTouchOwnership(touchId, this); |
1326 | + If he wants the touch point or: |
1327 | + TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, this); |
1328 | +Â if he does not want it. |
1329 | + |
1330 | + Candidates are put in a priority queue. The first one to call addCandidateOwnerForTouch() will |
1331 | + take precedence over the others for receiving ownership over the touch point (from now on called |
1332 | + simply top-candidate). |
1333 | + |
1334 | + If the top-candidate calls requestTouchOwnership() he will immediately receive a |
1335 | + TouchOwnershipEvent(gained=true) for that touch point. He can then safely call |
1336 | + QQuickItem::grabTouchPoints to actually get the owned touch points. The other candidates |
1337 | + will receive TouchOwnershipEvent(gained=false) and will no longer receive UnownedTouchEvents |
1338 | + for that touch point. They will have to undo whatever action they were performing with that |
1339 | + touch point. |
1340 | + |
1341 | + But if the top-candidate calls removeCandidateOwnerForTouch() instead, he's popped from the |
1342 | + candidacy queue and ownership is given to the new top-most candidate if he has already |
1343 | + made his decision, that is. |
1344 | + |
1345 | + The TouchRegistry cannot enforce the results of this pointer ownership negotiation (i.e., |
1346 | + who gets to grab the touch points) as that would clash with QQuickWindow's input event |
1347 | + dispatching logic. The candidates have to respect the decision and grab the touch points |
1348 | + themselves. |
1349 | + |
1350 | + If an item wants ownership over touches as soon as he receives the TouchBegin for them, his step 1 |
1351 | + would be instead: |
1352 | + TouchRegistry::instance()->requestTouchOwnership(touchId, this); |
1353 | + return true; |
1354 | + He would then be notified once ownership has been granted to him, from which point onwards he could |
1355 | + safely assume other TouchRegistry users wouldn't snatch this touch away from him. |
1356 | + |
1357 | + Items oblivious to TouchRegistry will lose their touch points without warning, just like in plain Qt. |
1358 | + |
1359 | + [1] - http://www.x.org/releases/X11R7.7/doc/inputproto/XI2proto.txt (see multitouch-ownership) |
1360 | + */ |
1361 | +class UBUNTUGESTURES_EXPORT TouchRegistry : public QObject |
1362 | +{ |
1363 | + Q_OBJECT |
1364 | +public: |
1365 | + TouchRegistry(QObject *parent = nullptr); |
1366 | + // Useful for tests, where you should feed a fake timer |
1367 | + TouchRegistry(QObject *parent, UbuntuGestures::AbstractTimerFactory *timerFactory); |
1368 | + |
1369 | + virtual ~TouchRegistry(); |
1370 | + |
1371 | + // Returns a pointer to the application's TouchRegistry instance. |
1372 | + // If no instance has been allocated, null is returned. |
1373 | + static TouchRegistry *instance() { return m_instance; } |
1374 | + |
1375 | + void update(const QTouchEvent *event); |
1376 | + |
1377 | + // Calls update() if the given event is a QTouchEvent |
1378 | + bool eventFilter(QObject *watched, QEvent *event) override; |
1379 | + |
1380 | + // An item that might later request ownership over the given touch point. |
1381 | + // He will be kept informed about that touch point through UnownedTouchEvents |
1382 | + // All candidates must eventually decide whether they want to own the touch point |
1383 | + // or not. That decision is informed through requestTouchOwnership() or |
1384 | + // removeCandidateOwnerForTouch() |
1385 | + void addCandidateOwnerForTouch(int id, QQuickItem *candidate); |
1386 | + |
1387 | + // The same as rejecting ownership of a touch |
1388 | + void removeCandidateOwnerForTouch(int id, QQuickItem *candidate); |
1389 | + |
1390 | + // The candidate object wants to be the owner of the touch with the given id. |
1391 | + // If he's currently the oldest/top-most candidate, he will get an ownership |
1392 | + // event immediately. If not, he will get ownership if (or once) he becomes the |
1393 | + // top-most candidate. |
1394 | + void requestTouchOwnership(int id, QQuickItem *candidate); |
1395 | + |
1396 | + // An item that has no interest (effective or potential) in owning a touch point |
1397 | + // but would nonetheless like to be kept up-to-date on its state. |
1398 | + void addTouchWatcher(int touchId, QQuickItem *watcherItem); |
1399 | + |
1400 | +private Q_SLOTS: |
1401 | + void rejectCandidateOwnerForTouch(int id, QQuickItem *candidate); |
1402 | + |
1403 | +private: |
1404 | + class CandidateInfo { |
1405 | + public: |
1406 | + bool undecided; |
1407 | + // TODO: Prune candidates that become null and resolve ownership accordingly. |
1408 | + QPointer<QQuickItem> item; |
1409 | + QPointer<UbuntuGestures::CandidateInactivityTimer> inactivityTimer; |
1410 | + }; |
1411 | + |
1412 | + class TouchInfo { |
1413 | + public: |
1414 | + TouchInfo() : id(-1) {} |
1415 | + TouchInfo(int id); |
1416 | + bool isValid() const { return id >= 0; } |
1417 | + void reset(); |
1418 | + void init(int id); |
1419 | + int id; |
1420 | + bool physicallyEnded; |
1421 | + bool isOwned() const; |
1422 | + bool ended() const; |
1423 | + void notifyCandidatesOfOwnershipResolution(); |
1424 | + |
1425 | + // TODO optimize storage (s/QList/Pool) |
1426 | + QList<CandidateInfo> candidates; |
1427 | + QList<QPointer<QQuickItem>> watchers; |
1428 | + }; |
1429 | + |
1430 | + Pool<TouchInfo>::Iterator findTouchInfo(int id); |
1431 | + |
1432 | + void deliverTouchUpdatesToUndecidedCandidatesAndWatchers(const QTouchEvent *event); |
1433 | + |
1434 | + static void translateTouchPointFromScreenToWindowCoords(QTouchEvent::TouchPoint &touchPoint); |
1435 | + |
1436 | + static void dispatchPointsToItem(const QTouchEvent *event, const QList<int> &touchIds, |
1437 | + QQuickItem *item); |
1438 | + void freeEndedTouchInfos(); |
1439 | + |
1440 | + Pool<TouchInfo> m_touchInfoPool; |
1441 | + |
1442 | + // the singleton instance |
1443 | + static TouchRegistry *m_instance; |
1444 | + |
1445 | + bool m_inDispatchLoop; |
1446 | + |
1447 | + UbuntuGestures::AbstractTimerFactory *m_timerFactory; |
1448 | + |
1449 | + friend class tst_TouchRegistry; |
1450 | + friend class tst_DirectionalDragArea; |
1451 | +}; |
1452 | + |
1453 | +#endif // UNITY_TOUCHREGISTRY_H |
1454 | |
1455 | === added file 'libs/UbuntuGestures/UbuntuGesturesGlobal.h' |
1456 | --- libs/UbuntuGestures/UbuntuGesturesGlobal.h 1970-01-01 00:00:00 +0000 |
1457 | +++ libs/UbuntuGestures/UbuntuGesturesGlobal.h 2014-10-10 15:05:15 +0000 |
1458 | @@ -0,0 +1,23 @@ |
1459 | +/* |
1460 | + * Copyright (C) 2014 Canonical, Ltd. |
1461 | + * |
1462 | + * This program is free software; you can redistribute it and/or modify |
1463 | + * it under the terms of the GNU General Public License as published by |
1464 | + * the Free Software Foundation; version 3. |
1465 | + * |
1466 | + * This program is distributed in the hope that it will be useful, |
1467 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1468 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1469 | + * GNU General Public License for more details. |
1470 | + * |
1471 | + * You should have received a copy of the GNU General Public License |
1472 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1473 | + */ |
1474 | + |
1475 | +#include <QtCore/QtGlobal> |
1476 | + |
1477 | +#if defined(UBUNTUGESTURES_LIBRARY) |
1478 | +# define UBUNTUGESTURES_EXPORT Q_DECL_EXPORT |
1479 | +#else |
1480 | +# define UBUNTUGESTURES_EXPORT Q_DECL_IMPORT |
1481 | +#endif |
1482 | |
1483 | === added file 'libs/UbuntuGestures/UnownedTouchEvent.cpp' |
1484 | --- libs/UbuntuGestures/UnownedTouchEvent.cpp 1970-01-01 00:00:00 +0000 |
1485 | +++ libs/UbuntuGestures/UnownedTouchEvent.cpp 2014-10-10 15:05:15 +0000 |
1486 | @@ -0,0 +1,39 @@ |
1487 | +/* |
1488 | + * Copyright (C) 2014 Canonical, Ltd. |
1489 | + * |
1490 | + * This program is free software; you can redistribute it and/or modify |
1491 | + * it under the terms of the GNU General Public License as published by |
1492 | + * the Free Software Foundation; version 3. |
1493 | + * |
1494 | + * This program is distributed in the hope that it will be useful, |
1495 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1496 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1497 | + * GNU General Public License for more details. |
1498 | + * |
1499 | + * You should have received a copy of the GNU General Public License |
1500 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1501 | + */ |
1502 | + |
1503 | +#include "UnownedTouchEvent.h" |
1504 | + |
1505 | +QEvent::Type UnownedTouchEvent::m_unownedTouchEventType = (QEvent::Type)-1; |
1506 | + |
1507 | +UnownedTouchEvent::UnownedTouchEvent(QTouchEvent *touchEvent) |
1508 | + : QEvent(unownedTouchEventType()) |
1509 | + , m_touchEvent(touchEvent) |
1510 | +{ |
1511 | +} |
1512 | + |
1513 | +QEvent::Type UnownedTouchEvent::unownedTouchEventType() |
1514 | +{ |
1515 | + if (m_unownedTouchEventType == (QEvent::Type)-1) { |
1516 | + m_unownedTouchEventType = (QEvent::Type)registerEventType(); |
1517 | + } |
1518 | + |
1519 | + return m_unownedTouchEventType; |
1520 | +} |
1521 | + |
1522 | +QTouchEvent *UnownedTouchEvent::touchEvent() |
1523 | +{ |
1524 | + return m_touchEvent.data(); |
1525 | +} |
1526 | |
1527 | === added file 'libs/UbuntuGestures/UnownedTouchEvent.h' |
1528 | --- libs/UbuntuGestures/UnownedTouchEvent.h 1970-01-01 00:00:00 +0000 |
1529 | +++ libs/UbuntuGestures/UnownedTouchEvent.h 2014-10-10 15:05:15 +0000 |
1530 | @@ -0,0 +1,45 @@ |
1531 | +/* |
1532 | + * Copyright (C) 2014 Canonical, Ltd. |
1533 | + * |
1534 | + * This program is free software; you can redistribute it and/or modify |
1535 | + * it under the terms of the GNU General Public License as published by |
1536 | + * the Free Software Foundation; version 3. |
1537 | + * |
1538 | + * This program is distributed in the hope that it will be useful, |
1539 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1540 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1541 | + * GNU General Public License for more details. |
1542 | + * |
1543 | + * You should have received a copy of the GNU General Public License |
1544 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1545 | + */ |
1546 | + |
1547 | +#ifndef UBUNTU_UNOWNEDTOUCHEVENT_H |
1548 | +#define UBUNTU_UNOWNEDTOUCHEVENT_H |
1549 | + |
1550 | +#include <QTouchEvent> |
1551 | +#include <QScopedPointer> |
1552 | +#include "UbuntuGesturesGlobal.h" |
1553 | + |
1554 | +/* |
1555 | + A touch event with touch points that do not belong the item receiving it. |
1556 | + |
1557 | + See TouchRegistry::addCandidateOwnerForTouch and TouchRegistry::addTouchWatcher |
1558 | + */ |
1559 | +class UBUNTUGESTURES_EXPORT UnownedTouchEvent : public QEvent |
1560 | +{ |
1561 | +public: |
1562 | + UnownedTouchEvent(QTouchEvent *touchEvent); |
1563 | + static Type unownedTouchEventType(); |
1564 | + |
1565 | + // TODO: It might be cleaner to store the information directly in UnownedTouchEvent |
1566 | + // instead of carrying around a synthesized QTouchEvent. But the latter option |
1567 | + // is very convenient. |
1568 | + QTouchEvent *touchEvent(); |
1569 | + |
1570 | +private: |
1571 | + static Type m_unownedTouchEventType; |
1572 | + QScopedPointer<QTouchEvent> m_touchEvent; |
1573 | +}; |
1574 | + |
1575 | +#endif // UBUNTU_UNOWNEDTOUCHEVENT_H |
1576 | |
1577 | === modified file 'plugins/Ubuntu/Gestures/AxisVelocityCalculator.h' |
1578 | --- plugins/Ubuntu/Gestures/AxisVelocityCalculator.h 2013-10-23 12:25:40 +0000 |
1579 | +++ plugins/Ubuntu/Gestures/AxisVelocityCalculator.h 2014-10-10 15:05:15 +0000 |
1580 | @@ -21,7 +21,7 @@ |
1581 | #ifndef VELOCITY_CALCULATOR_H |
1582 | #define VELOCITY_CALCULATOR_H |
1583 | |
1584 | -#include "UbuntuGesturesGlobal.h" |
1585 | +#include "UbuntuGesturesQmlGlobal.h" |
1586 | #include <stdint.h> |
1587 | #include <QtCore/QObject> |
1588 | #include "TimeSource.h" |
1589 | @@ -49,7 +49,7 @@ |
1590 | } |
1591 | } |
1592 | */ |
1593 | -class UBUNTUGESTURES_EXPORT AxisVelocityCalculator : public QObject |
1594 | +class UBUNTUGESTURESQML_EXPORT AxisVelocityCalculator : public QObject |
1595 | { |
1596 | Q_OBJECT |
1597 | |
1598 | |
1599 | === modified file 'plugins/Ubuntu/Gestures/CMakeLists.txt' |
1600 | --- plugins/Ubuntu/Gestures/CMakeLists.txt 2014-05-02 22:57:00 +0000 |
1601 | +++ plugins/Ubuntu/Gestures/CMakeLists.txt 2014-10-10 15:05:15 +0000 |
1602 | @@ -1,19 +1,38 @@ |
1603 | -set(UbuntuGestureQml_SOURCES |
1604 | +# in order to include Qt's private headers |
1605 | +remove_definitions(-DQT_NO_KEYWORDS) |
1606 | + |
1607 | +set(UbuntuGesturesQml_SOURCES |
1608 | plugin.cpp |
1609 | AxisVelocityCalculator.cpp |
1610 | Direction.cpp |
1611 | DirectionalDragArea.cpp |
1612 | PressedOutsideNotifier.cpp |
1613 | TimeSource.cpp |
1614 | + TouchGate.cpp |
1615 | ) |
1616 | |
1617 | -add_definitions(-DUBUNTUGESTURES_LIBRARY) |
1618 | - |
1619 | -add_library(UbuntuGestureQml MODULE ${UbuntuGestureQml_SOURCES}) |
1620 | - |
1621 | -qt5_use_modules(UbuntuGestureQml Core Quick) |
1622 | +add_definitions(-DUBUNTUGESTURESQML_LIBRARY) |
1623 | + |
1624 | +add_library(UbuntuGesturesQml MODULE ${UbuntuGesturesQml_SOURCES}) |
1625 | +target_link_libraries(UbuntuGesturesQml UbuntuGestures) |
1626 | + |
1627 | +qt5_use_modules(UbuntuGesturesQml Core Quick) |
1628 | |
1629 | # So that Foo.cpp can #include "Foo.moc" |
1630 | include_directories(${CMAKE_CURRENT_BINARY_DIR}) |
1631 | |
1632 | -add_unity8_plugin(Ubuntu.Gestures 0.1 Ubuntu/Gestures TARGETS UbuntuGestureQml) |
1633 | +include_directories(${CMAKE_SOURCE_DIR}/libs/UbuntuGestures) |
1634 | + |
1635 | +# There's no cmake var for v8 include path :-/ so create one |
1636 | +LIST(GET Qt5Core_INCLUDE_DIRS 0 QtCoreDir0) |
1637 | +SET(Qt5V8_PRIVATE_INCLUDE_DIR ${QtCoreDir0}/QtV8/${Qt5Core_VERSION_STRING}/QtV8) |
1638 | + |
1639 | +# DANGER! DANGER! Using Qt's private API! |
1640 | +include_directories( |
1641 | + ${Qt5Qml_PRIVATE_INCLUDE_DIRS} |
1642 | + ${Qt5Quick_INCLUDE_DIRS} |
1643 | + ${Qt5Quick_PRIVATE_INCLUDE_DIRS} |
1644 | + ${Qt5V8_PRIVATE_INCLUDE_DIR} |
1645 | +) |
1646 | + |
1647 | +add_unity8_plugin(Ubuntu.Gestures 0.1 Ubuntu/Gestures TARGETS UbuntuGesturesQml) |
1648 | |
1649 | === modified file 'plugins/Ubuntu/Gestures/Direction.h' |
1650 | --- plugins/Ubuntu/Gestures/Direction.h 2013-06-19 08:29:34 +0000 |
1651 | +++ plugins/Ubuntu/Gestures/Direction.h 2014-10-10 15:05:15 +0000 |
1652 | @@ -17,14 +17,14 @@ |
1653 | #ifndef DIRECTION_H |
1654 | #define DIRECTION_H |
1655 | |
1656 | -#include "UbuntuGesturesGlobal.h" |
1657 | +#include "UbuntuGesturesQmlGlobal.h" |
1658 | #include <QObject> |
1659 | |
1660 | /* |
1661 | A Direction enum wrapper so that we can do things like "direction: Direction.Righwards" |
1662 | from QML. |
1663 | */ |
1664 | -class UBUNTUGESTURES_EXPORT Direction : public QObject { |
1665 | +class UBUNTUGESTURESQML_EXPORT Direction : public QObject { |
1666 | Q_OBJECT |
1667 | Q_ENUMS(Type) |
1668 | |
1669 | |
1670 | === modified file 'plugins/Ubuntu/Gestures/DirectionalDragArea.cpp' |
1671 | --- plugins/Ubuntu/Gestures/DirectionalDragArea.cpp 2014-05-02 08:29:26 +0000 |
1672 | +++ plugins/Ubuntu/Gestures/DirectionalDragArea.cpp 2014-10-10 15:05:15 +0000 |
1673 | @@ -1,5 +1,5 @@ |
1674 | /* |
1675 | - * Copyright (C) 2013 Canonical, Ltd. |
1676 | + * Copyright (C) 2013-2014 Canonical, Ltd. |
1677 | * |
1678 | * This program is free software; you can redistribute it and/or modify |
1679 | * it under the terms of the GNU General Public License as published by |
1680 | @@ -14,64 +14,32 @@ |
1681 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1682 | */ |
1683 | |
1684 | +#define ACTIVETOUCHESINFO_DEBUG 0 |
1685 | +#define DIRECTIONALDRAGAREA_DEBUG 0 |
1686 | + |
1687 | #include "DirectionalDragArea.h" |
1688 | |
1689 | +#include <QQuickWindow> |
1690 | #include <QtCore/qmath.h> |
1691 | -#include <QtCore/QTimer> |
1692 | #include <QDebug> |
1693 | |
1694 | +#pragma GCC diagnostic push |
1695 | +#pragma GCC diagnostic ignored "-pedantic" |
1696 | +#include <private/qquickwindow_p.h> |
1697 | +#pragma GCC diagnostic pop |
1698 | + |
1699 | +// local |
1700 | +#include "TouchOwnershipEvent.h" |
1701 | +#include "TouchRegistry.h" |
1702 | +#include "UnownedTouchEvent.h" |
1703 | + |
1704 | using namespace UbuntuGestures; |
1705 | |
1706 | -#define DIRECTIONALDRAGAREA_DEBUG 0 |
1707 | - |
1708 | #if DIRECTIONALDRAGAREA_DEBUG |
1709 | #define DDA_DEBUG(msg) qDebug("[DDA] " msg) |
1710 | +#include "DebugHelpers.h" |
1711 | + |
1712 | namespace { |
1713 | -QString touchPointStateToString(Qt::TouchPointState state) { |
1714 | - switch (state) { |
1715 | - case Qt::TouchPointPressed: |
1716 | - return QString("pressed"); |
1717 | - case Qt::TouchPointMoved: |
1718 | - return QString("moved"); |
1719 | - case Qt::TouchPointStationary: |
1720 | - return QString("stationary"); |
1721 | - default: // Qt::TouchPointReleased: |
1722 | - return QString("released"); |
1723 | - } |
1724 | -} |
1725 | -QString touchEventToString(QTouchEvent *ev) |
1726 | -{ |
1727 | - QString message; |
1728 | - |
1729 | - switch (ev->type()) { |
1730 | - case QEvent::TouchBegin: |
1731 | - message.append("TouchBegin "); |
1732 | - break; |
1733 | - case QEvent::TouchUpdate: |
1734 | - message.append("TouchUpdate "); |
1735 | - break; |
1736 | - case QEvent::TouchEnd: |
1737 | - message.append("TouchEnd "); |
1738 | - break; |
1739 | - default: //QEvent::TouchCancel |
1740 | - message.append("TouchCancel "); |
1741 | - } |
1742 | - |
1743 | - for (int i=0; i < ev->touchPoints().size(); ++i) { |
1744 | - |
1745 | - const QTouchEvent::TouchPoint& touchPoint = ev->touchPoints().at(i); |
1746 | - message.append( |
1747 | - QString("(id:%1, state:%2, scenePos:(%3,%4)) ") |
1748 | - .arg(touchPoint.id()) |
1749 | - .arg(touchPointStateToString(touchPoint.state())) |
1750 | - .arg(touchPoint.scenePos().x()) |
1751 | - .arg(touchPoint.scenePos().y()) |
1752 | - ); |
1753 | - } |
1754 | - |
1755 | - return message; |
1756 | -} |
1757 | - |
1758 | const char *statusToString(DirectionalDragArea::Status status) |
1759 | { |
1760 | if (status == DirectionalDragArea::WaitingForTouch) { |
1761 | @@ -88,23 +56,6 @@ |
1762 | #define DDA_DEBUG(msg) do{}while(0) |
1763 | #endif // DIRECTIONALDRAGAREA_DEBUG |
1764 | |
1765 | -// Essentially a QTimer wrapper |
1766 | -class RecognitionTimer : public UbuntuGestures::AbstractTimer |
1767 | -{ |
1768 | - Q_OBJECT |
1769 | -public: |
1770 | - RecognitionTimer(QObject *parent) : UbuntuGestures::AbstractTimer(parent) { |
1771 | - m_timer.setSingleShot(false); |
1772 | - connect(&m_timer, &QTimer::timeout, |
1773 | - this, &UbuntuGestures::AbstractTimer::timeout); |
1774 | - } |
1775 | - virtual int interval() const { return m_timer.interval(); } |
1776 | - virtual void setInterval(int msecs) { m_timer.setInterval(msecs); } |
1777 | - virtual void start() { m_timer.start(); UbuntuGestures::AbstractTimer::start(); } |
1778 | - virtual void stop() { m_timer.stop(); UbuntuGestures::AbstractTimer::stop(); } |
1779 | -private: |
1780 | - QTimer m_timer; |
1781 | -}; |
1782 | |
1783 | DirectionalDragArea::DirectionalDragArea(QQuickItem *parent) |
1784 | : QQuickItem(parent) |
1785 | @@ -126,12 +77,14 @@ |
1786 | , m_timeSource(new RealTimeSource) |
1787 | , m_activeTouches(m_timeSource) |
1788 | { |
1789 | - setRecognitionTimer(new RecognitionTimer(this)); |
1790 | + setRecognitionTimer(new Timer(this)); |
1791 | m_recognitionTimer->setInterval(60); |
1792 | + m_recognitionTimer->setSingleShot(false); |
1793 | |
1794 | m_velocityCalculator = new AxisVelocityCalculator(this); |
1795 | |
1796 | - connect(this, &QQuickItem::enabledChanged, this, &DirectionalDragArea::onEnabledChanged); |
1797 | + connect(this, &QQuickItem::enabledChanged, this, &DirectionalDragArea::giveUpIfDisabledOrInvisible); |
1798 | + connect(this, &QQuickItem::visibleChanged, this, &DirectionalDragArea::giveUpIfDisabledOrInvisible); |
1799 | } |
1800 | |
1801 | Direction::Type DirectionalDragArea::direction() const |
1802 | @@ -214,6 +167,7 @@ |
1803 | { |
1804 | int interval = 0; |
1805 | bool timerWasRunning = false; |
1806 | + bool wasSingleShot = false; |
1807 | |
1808 | // can be null when called from the constructor |
1809 | if (m_recognitionTimer) { |
1810 | @@ -226,6 +180,7 @@ |
1811 | |
1812 | m_recognitionTimer = timer; |
1813 | timer->setInterval(interval); |
1814 | + timer->setSingleShot(wasSingleShot); |
1815 | connect(timer, &UbuntuGestures::AbstractTimer::timeout, |
1816 | this, &DirectionalDragArea::checkSpeed); |
1817 | if (timerWasRunning) { |
1818 | @@ -280,8 +235,144 @@ |
1819 | return m_previousScenePos.y(); |
1820 | } |
1821 | |
1822 | +bool DirectionalDragArea::event(QEvent *event) |
1823 | +{ |
1824 | + if (event->type() == TouchOwnershipEvent::touchOwnershipEventType()) { |
1825 | + touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(event)); |
1826 | + return true; |
1827 | + } else if (event->type() == UnownedTouchEvent::unownedTouchEventType()) { |
1828 | + unownedTouchEvent(static_cast<UnownedTouchEvent *>(event)); |
1829 | + return true; |
1830 | + } else { |
1831 | + return QQuickItem::event(event); |
1832 | + } |
1833 | +} |
1834 | + |
1835 | +void DirectionalDragArea::touchOwnershipEvent(TouchOwnershipEvent *event) |
1836 | +{ |
1837 | + if (event->gained()) { |
1838 | + QVector<int> ids; |
1839 | + ids.append(event->touchId()); |
1840 | + DDA_DEBUG("grabbing touch"); |
1841 | + grabTouchPoints(ids); |
1842 | + |
1843 | + // Work around for Qt bug. If we grab a touch that is being used for mouse pointer |
1844 | + // emulation it will cause the emulation logic to go nuts. |
1845 | + // Thus we have to also grab the mouse in this case. |
1846 | + // TODO: Report bug to Qt |
1847 | + if (window()) { |
1848 | + QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(window()); |
1849 | + if (windowPrivate->touchMouseId == event->touchId() && window()->mouseGrabberItem()) { |
1850 | + DDA_DEBUG("removing mouse grabber"); |
1851 | + window()->mouseGrabberItem()->ungrabMouse(); |
1852 | + } |
1853 | + } |
1854 | + } else { |
1855 | + // We still wanna know when it ends for keeping the composition time window up-to-date |
1856 | + TouchRegistry::instance()->addTouchWatcher(m_touchId, this); |
1857 | + |
1858 | + setStatus(WaitingForTouch); |
1859 | + } |
1860 | +} |
1861 | + |
1862 | +void DirectionalDragArea::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent) |
1863 | +{ |
1864 | + QTouchEvent *event = unownedTouchEvent->touchEvent(); |
1865 | + |
1866 | + Q_ASSERT(!event->touchPointStates().testFlag(Qt::TouchPointPressed)); |
1867 | + |
1868 | + #if DIRECTIONALDRAGAREA_DEBUG |
1869 | + // TODO Consider using qCDebug() when available (Qt 5.2) |
1870 | + qDebug() << "[DDA] Unowned" << m_timeSource->msecsSinceReference() |
1871 | + << qPrintable(touchEventToString(event)); |
1872 | + #endif |
1873 | + |
1874 | + switch (m_status) { |
1875 | + case WaitingForTouch: |
1876 | + // do nothing |
1877 | + break; |
1878 | + case Undecided: |
1879 | + Q_ASSERT(isEnabled() && isVisible()); |
1880 | + unownedTouchEvent_undecided(unownedTouchEvent); |
1881 | + break; |
1882 | + default: // Recognized: |
1883 | + // do nothing |
1884 | + break; |
1885 | + } |
1886 | + |
1887 | + m_activeTouches.update(event); |
1888 | +} |
1889 | + |
1890 | +void DirectionalDragArea::unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent) |
1891 | +{ |
1892 | + const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(unownedTouchEvent->touchEvent()); |
1893 | + if (!touchPoint) { |
1894 | + qCritical() << "DirectionalDragArea[status=Undecided]: touch " << m_touchId |
1895 | + << "missing from UnownedTouchEvent without first reaching state Qt::TouchPointReleased. " |
1896 | + "Considering it as released."; |
1897 | + |
1898 | + TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this); |
1899 | + setStatus(WaitingForTouch); |
1900 | + return; |
1901 | + } |
1902 | + |
1903 | + const QPointF &touchScenePos = touchPoint->scenePos(); |
1904 | + |
1905 | + if (touchPoint->state() == Qt::TouchPointReleased) { |
1906 | + // touch has ended before recognition concluded |
1907 | + DDA_DEBUG("Touch has ended before recognition concluded"); |
1908 | + TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this); |
1909 | + emitSignalIfTapped(); |
1910 | + setStatus(WaitingForTouch); |
1911 | + return; |
1912 | + } |
1913 | + |
1914 | + m_previousDampedScenePos.setX(m_dampedScenePos.x()); |
1915 | + m_previousDampedScenePos.setY(m_dampedScenePos.y()); |
1916 | + m_dampedScenePos.update(touchScenePos); |
1917 | + updateVelocityCalculator(touchScenePos); |
1918 | + |
1919 | + if (!pointInsideAllowedArea()) { |
1920 | + DDA_DEBUG("Rejecting gesture because touch point is outside allowed area."); |
1921 | + TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this); |
1922 | + // We still wanna know when it ends for keeping the composition time window up-to-date |
1923 | + TouchRegistry::instance()->addTouchWatcher(m_touchId, this); |
1924 | + setStatus(WaitingForTouch); |
1925 | + return; |
1926 | + } |
1927 | + |
1928 | + if (!movingInRightDirection()) { |
1929 | + DDA_DEBUG("Rejecting gesture because touch point is moving in the wrong direction."); |
1930 | + TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this); |
1931 | + // We still wanna know when it ends for keeping the composition time window up-to-date |
1932 | + TouchRegistry::instance()->addTouchWatcher(m_touchId, this); |
1933 | + setStatus(WaitingForTouch); |
1934 | + return; |
1935 | + } |
1936 | + |
1937 | + setPreviousPos(touchPoint->pos()); |
1938 | + setPreviousScenePos(touchScenePos); |
1939 | + |
1940 | + if (isWithinTouchCompositionWindow()) { |
1941 | + // There's still time for some new touch to appear and ruin our party as it would be combined |
1942 | + // with our m_touchId one and therefore deny the possibility of a single-finger gesture. |
1943 | + DDA_DEBUG("Sill within composition window. Let's wait more."); |
1944 | + return; |
1945 | + } |
1946 | + |
1947 | + if (movedFarEnough(touchScenePos)) { |
1948 | + TouchRegistry::instance()->requestTouchOwnership(m_touchId, this); |
1949 | + setStatus(Recognized); |
1950 | + } else { |
1951 | + DDA_DEBUG("Didn't move far enough yet. Let's wait more."); |
1952 | + } |
1953 | +} |
1954 | + |
1955 | void DirectionalDragArea::touchEvent(QTouchEvent *event) |
1956 | { |
1957 | + // TODO: Consider when more than one touch starts in the same event (although it's not possible |
1958 | + // with Mir's android-input). Have to track them all. Consider it a plus/bonus. |
1959 | + |
1960 | #if DIRECTIONALDRAGAREA_DEBUG |
1961 | // TODO Consider using qCDebug() when available (Qt 5.2) |
1962 | qDebug() << "[DDA]" << m_timeSource->msecsSinceReference() |
1963 | @@ -310,29 +401,34 @@ |
1964 | |
1965 | void DirectionalDragArea::touchEvent_absent(QTouchEvent *event) |
1966 | { |
1967 | + // TODO: accept/reject is for the whole event, not per touch id. See how that affects us. |
1968 | + |
1969 | if (!event->touchPointStates().testFlag(Qt::TouchPointPressed)) { |
1970 | // Nothing to see here. No touch starting in this event. |
1971 | return; |
1972 | } |
1973 | |
1974 | + // to be proven wrong, if that's the case |
1975 | + bool allGood = true; |
1976 | + |
1977 | if (isWithinTouchCompositionWindow()) { |
1978 | // too close to the last touch start. So we consider them as starting roughly at the same time. |
1979 | // Can't be a single-touch gesture. |
1980 | #if DIRECTIONALDRAGAREA_DEBUG |
1981 | qDebug("[DDA] A new touch point came in but we're still within time composition window. Ignoring it."); |
1982 | #endif |
1983 | - return; |
1984 | + allGood = false; |
1985 | } |
1986 | |
1987 | const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints(); |
1988 | |
1989 | const QTouchEvent::TouchPoint *newTouchPoint = nullptr; |
1990 | - for (int i = 0; i < touchPoints.count(); ++i) { |
1991 | + for (int i = 0; i < touchPoints.count() && allGood; ++i) { |
1992 | const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i); |
1993 | if (touchPoint.state() == Qt::TouchPointPressed) { |
1994 | if (newTouchPoint) { |
1995 | // more than one touch starting in this QTouchEvent. Can't be a single-touch gesture |
1996 | - return; |
1997 | + allGood = false; |
1998 | } else { |
1999 | // that's our candidate |
2000 | m_touchId = touchPoint.id(); |
2001 | @@ -341,85 +437,61 @@ |
2002 | } |
2003 | } |
2004 | |
2005 | - Q_ASSERT(newTouchPoint); |
2006 | - |
2007 | - // If we have made this far, we are good to go to the next status. |
2008 | - |
2009 | - m_startPos = newTouchPoint->pos(); |
2010 | - m_startScenePos = newTouchPoint->scenePos(); |
2011 | - m_touchId = newTouchPoint->id(); |
2012 | - m_dampedScenePos.reset(m_startScenePos); |
2013 | - m_velocityCalculator->setTrackedPosition(0.); |
2014 | - m_velocityCalculator->reset(); |
2015 | - m_numSamplesOnLastSpeedCheck = 0; |
2016 | - m_silenceTime = 0; |
2017 | - setPreviousPos(m_startPos); |
2018 | - setPreviousScenePos(m_startScenePos); |
2019 | - updateSceneDirectionVector(); |
2020 | - |
2021 | - setStatus(Undecided); |
2022 | + if (allGood) { |
2023 | + Q_ASSERT(newTouchPoint); |
2024 | + |
2025 | + m_startPos = newTouchPoint->pos(); |
2026 | + m_startScenePos = newTouchPoint->scenePos(); |
2027 | + m_touchId = newTouchPoint->id(); |
2028 | + m_dampedScenePos.reset(m_startScenePos); |
2029 | + m_velocityCalculator->setTrackedPosition(0.); |
2030 | + m_velocityCalculator->reset(); |
2031 | + m_numSamplesOnLastSpeedCheck = 0; |
2032 | + m_silenceTime = 0; |
2033 | + setPreviousPos(m_startPos); |
2034 | + setPreviousScenePos(m_startScenePos); |
2035 | + updateSceneDirectionVector(); |
2036 | + |
2037 | + if (recognitionIsDisabled()) { |
2038 | + // Behave like a dumb TouchArea |
2039 | + TouchRegistry::instance()->requestTouchOwnership(m_touchId, this); |
2040 | + setStatus(Recognized); |
2041 | + event->accept(); |
2042 | + } else { |
2043 | + // just monitor the touch points for now. |
2044 | + TouchRegistry::instance()->addCandidateOwnerForTouch(m_touchId, this); |
2045 | + |
2046 | + setStatus(Undecided); |
2047 | + // Let the item below have it. We will monitor it and grab it later if a gesture |
2048 | + // gets recognized. |
2049 | + event->ignore(); |
2050 | + } |
2051 | + } else { |
2052 | + watchPressedTouchPoints(touchPoints); |
2053 | + event->ignore(); |
2054 | + } |
2055 | } |
2056 | |
2057 | void DirectionalDragArea::touchEvent_undecided(QTouchEvent *event) |
2058 | { |
2059 | - const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event); |
2060 | - |
2061 | - if (!touchPoint) { |
2062 | - qCritical() << "DirectionalDragArea[status=Undecided]: touch " << m_touchId |
2063 | - << "missing from QTouchEvent without first reaching state Qt::TouchPointReleased. " |
2064 | - "Considering it as released."; |
2065 | - setStatus(WaitingForTouch); |
2066 | - return; |
2067 | - } |
2068 | - |
2069 | - const QPointF &touchScenePos = touchPoint->scenePos(); |
2070 | - |
2071 | - if (touchPoint->state() == Qt::TouchPointReleased) { |
2072 | - // touch has ended before recognition concluded |
2073 | - DDA_DEBUG("Touch has ended before recognition concluded"); |
2074 | - setStatus(WaitingForTouch); |
2075 | - Q_EMIT tapped(); |
2076 | - return; |
2077 | - } |
2078 | + Q_ASSERT(event->type() == QEvent::TouchBegin); |
2079 | + Q_ASSERT(fetchTargetTouchPoint(event) == nullptr); |
2080 | + |
2081 | + // We're not interested in new touch points. We already have our candidate (m_touchId). |
2082 | + // But we do want to know when those new touches end for keeping the composition time |
2083 | + // window up-to-date |
2084 | + event->ignore(); |
2085 | + watchPressedTouchPoints(event->touchPoints()); |
2086 | |
2087 | if (event->touchPointStates().testFlag(Qt::TouchPointPressed) && isWithinTouchCompositionWindow()) { |
2088 | // multi-finger drags are not accepted |
2089 | DDA_DEBUG("Multi-finger drags are not accepted"); |
2090 | - setStatus(WaitingForTouch); |
2091 | - return; |
2092 | - } |
2093 | - |
2094 | - m_previousDampedScenePos.setX(m_dampedScenePos.x()); |
2095 | - m_previousDampedScenePos.setY(m_dampedScenePos.y()); |
2096 | - m_dampedScenePos.update(touchScenePos); |
2097 | - updateVelocityCalculator(touchScenePos); |
2098 | - |
2099 | - if (!pointInsideAllowedArea()) { |
2100 | - DDA_DEBUG("Rejecting gesture because touch point is outside allowed area."); |
2101 | - setStatus(WaitingForTouch); |
2102 | - return; |
2103 | - } |
2104 | - |
2105 | - if (!movingInRightDirection()) { |
2106 | - DDA_DEBUG("Rejecting gesture becauuse touch point is moving in the wrong direction."); |
2107 | - setStatus(WaitingForTouch); |
2108 | - return; |
2109 | - } |
2110 | - |
2111 | - setPreviousPos(touchPoint->pos()); |
2112 | - setPreviousScenePos(touchScenePos); |
2113 | - |
2114 | - if (isWithinTouchCompositionWindow()) { |
2115 | - // There's still time for some new touch to appear and ruin our party as it would be combined |
2116 | - // with our m_touchId one and therefore deny the possibility of a single-finger gesture. |
2117 | - DDA_DEBUG("Sill within composition window. Let's wait more."); |
2118 | - return; |
2119 | - } |
2120 | - |
2121 | - if (movedFarEnough(touchScenePos)) { |
2122 | - setStatus(Recognized); |
2123 | - } else { |
2124 | - DDA_DEBUG("Didn't move far enough yet. Let's wait more."); |
2125 | + |
2126 | + TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this); |
2127 | + // We still wanna know when it ends for keeping the composition time window up-to-date |
2128 | + TouchRegistry::instance()->addTouchWatcher(m_touchId, this); |
2129 | + |
2130 | + setStatus(WaitingForTouch); |
2131 | } |
2132 | } |
2133 | |
2134 | @@ -437,11 +509,35 @@ |
2135 | setPreviousScenePos(touchPoint->scenePos()); |
2136 | |
2137 | if (touchPoint->state() == Qt::TouchPointReleased) { |
2138 | + emitSignalIfTapped(); |
2139 | setStatus(WaitingForTouch); |
2140 | } |
2141 | } |
2142 | } |
2143 | |
2144 | +void DirectionalDragArea::watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints) |
2145 | +{ |
2146 | + for (int i = 0; i < touchPoints.count(); ++i) { |
2147 | + const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i); |
2148 | + if (touchPoint.state() == Qt::TouchPointPressed) { |
2149 | + TouchRegistry::instance()->addTouchWatcher(touchPoint.id(), this); |
2150 | + } |
2151 | + } |
2152 | +} |
2153 | + |
2154 | +bool DirectionalDragArea::recognitionIsDisabled() const |
2155 | +{ |
2156 | + return distanceThreshold() <= 0 && compositionTime() <= 0; |
2157 | +} |
2158 | + |
2159 | +void DirectionalDragArea::emitSignalIfTapped() |
2160 | +{ |
2161 | + qint64 touchDuration = m_timeSource->msecsSinceReference() - m_activeTouches.touchStartTime(m_touchId); |
2162 | + if (touchDuration <= maxTapDuration()) { |
2163 | + Q_EMIT tapped(); |
2164 | + } |
2165 | +} |
2166 | + |
2167 | const QTouchEvent::TouchPoint *DirectionalDragArea::fetchTargetTouchPoint(QTouchEvent *event) |
2168 | { |
2169 | const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints(); |
2170 | @@ -509,12 +605,16 @@ |
2171 | |
2172 | void DirectionalDragArea::checkSpeed() |
2173 | { |
2174 | + Q_ASSERT(m_status == Undecided); |
2175 | + |
2176 | if (m_velocityCalculator->numSamples() >= AxisVelocityCalculator::MIN_SAMPLES_NEEDED) { |
2177 | qreal speed = qFabs(m_velocityCalculator->calculate()); |
2178 | qreal minSpeedMsecs = m_minSpeed / 1000.0; |
2179 | |
2180 | if (speed < minSpeedMsecs) { |
2181 | DDA_DEBUG("Rejecting gesture because it's below minimum speed."); |
2182 | + TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this); |
2183 | + TouchRegistry::instance()->addTouchWatcher(m_touchId, this); |
2184 | setStatus(WaitingForTouch); |
2185 | } |
2186 | } |
2187 | @@ -523,7 +623,9 @@ |
2188 | m_silenceTime += m_recognitionTimer->interval(); |
2189 | |
2190 | if (m_silenceTime > m_maxSilenceTime) { |
2191 | - DDA_DEBUG("Rejecting gesture because it's silence time has been exceeded."); |
2192 | + DDA_DEBUG("Rejecting gesture because its silence time has been exceeded."); |
2193 | + TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this); |
2194 | + TouchRegistry::instance()->addTouchWatcher(m_touchId, this); |
2195 | setStatus(WaitingForTouch); |
2196 | } |
2197 | } else { |
2198 | @@ -532,10 +634,19 @@ |
2199 | m_numSamplesOnLastSpeedCheck = m_velocityCalculator->numSamples(); |
2200 | } |
2201 | |
2202 | -void DirectionalDragArea::onEnabledChanged() |
2203 | +void DirectionalDragArea::giveUpIfDisabledOrInvisible() |
2204 | { |
2205 | - if (!isEnabled() && m_status != WaitingForTouch) { |
2206 | - setStatus(WaitingForTouch); |
2207 | + if (!isEnabled() || !isVisible()) { |
2208 | + if (m_status == Undecided) { |
2209 | + TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this); |
2210 | + // We still wanna know when it ends for keeping the composition time window up-to-date |
2211 | + TouchRegistry::instance()->addTouchWatcher(m_touchId, this); |
2212 | + } |
2213 | + |
2214 | + if (m_status != WaitingForTouch) { |
2215 | + DDA_DEBUG("Resetting status because got disabled or made invisible"); |
2216 | + setStatus(WaitingForTouch); |
2217 | + } |
2218 | } |
2219 | } |
2220 | |
2221 | @@ -631,7 +742,9 @@ |
2222 | |
2223 | bool DirectionalDragArea::isWithinTouchCompositionWindow() |
2224 | { |
2225 | - return !m_activeTouches.isEmpty() && |
2226 | + return |
2227 | + compositionTime() > 0 && |
2228 | + !m_activeTouches.isEmpty() && |
2229 | m_timeSource->msecsSinceReference() <= |
2230 | m_activeTouches.mostRecentStartTime() + (qint64)compositionTime(); |
2231 | } |
2232 | @@ -639,18 +752,17 @@ |
2233 | //************************** ActiveTouchesInfo ************************** |
2234 | |
2235 | DirectionalDragArea::ActiveTouchesInfo::ActiveTouchesInfo(const SharedTimeSource &timeSource) |
2236 | - : m_timeSource(timeSource), m_lastUsedIndex(-1) |
2237 | + : m_timeSource(timeSource) |
2238 | { |
2239 | - // Estimate of the maximum number of active touches we might reach. |
2240 | - // Not a problem if it ends up being an underestimate as this is just |
2241 | - // an optimization. |
2242 | - m_vector.resize(3); |
2243 | } |
2244 | |
2245 | void DirectionalDragArea::ActiveTouchesInfo::update(QTouchEvent *event) |
2246 | { |
2247 | if (!(event->touchPointStates() & (Qt::TouchPointPressed | Qt::TouchPointReleased))) { |
2248 | // nothing to update |
2249 | + #if ACTIVETOUCHESINFO_DEBUG |
2250 | + qDebug("[DDA::ActiveTouchesInfo] Nothing to Update"); |
2251 | + #endif |
2252 | return; |
2253 | } |
2254 | |
2255 | @@ -658,74 +770,88 @@ |
2256 | for (int i = 0; i < touchPoints.count(); ++i) { |
2257 | const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i); |
2258 | if (touchPoint.state() == Qt::TouchPointPressed) { |
2259 | - addTouchPoint(touchPoint); |
2260 | + addTouchPoint(touchPoint.id()); |
2261 | } else if (touchPoint.state() == Qt::TouchPointReleased) { |
2262 | - removeTouchPoint(touchPoint); |
2263 | + removeTouchPoint(touchPoint.id()); |
2264 | } |
2265 | } |
2266 | } |
2267 | |
2268 | -void DirectionalDragArea::ActiveTouchesInfo::addTouchPoint(const QTouchEvent::TouchPoint &touchPoint) |
2269 | -{ |
2270 | - ActiveTouchInfo &activeTouchInfo = getEmptySlot(); |
2271 | - activeTouchInfo.id = touchPoint.id(); |
2272 | +#if ACTIVETOUCHESINFO_DEBUG |
2273 | +QString DirectionalDragArea::ActiveTouchesInfo::toString() |
2274 | +{ |
2275 | + QString string = "("; |
2276 | + |
2277 | + { |
2278 | + QTextStream stream(&string); |
2279 | + m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) { |
2280 | + stream << "(id=" << touchInfo->id << ",startTime=" << touchInfo->startTime << ")"; |
2281 | + return true; |
2282 | + }); |
2283 | + } |
2284 | + |
2285 | + string.append(")"); |
2286 | + |
2287 | + return string; |
2288 | +} |
2289 | +#endif // ACTIVETOUCHESINFO_DEBUG |
2290 | + |
2291 | +void DirectionalDragArea::ActiveTouchesInfo::addTouchPoint(int touchId) |
2292 | +{ |
2293 | + ActiveTouchInfo &activeTouchInfo = m_touchInfoPool.getEmptySlot(); |
2294 | + activeTouchInfo.id = touchId; |
2295 | activeTouchInfo.startTime = m_timeSource->msecsSinceReference(); |
2296 | -} |
2297 | - |
2298 | -void DirectionalDragArea::ActiveTouchesInfo::removeTouchPoint(const QTouchEvent::TouchPoint &touchPoint) |
2299 | -{ |
2300 | - for (int i = 0; i <= m_lastUsedIndex; ++i) { |
2301 | - if (touchPoint.id() == m_vector.at(i).id) { |
2302 | - freeSlot(i); |
2303 | - return; |
2304 | - } |
2305 | - } |
2306 | - Q_ASSERT(false); // shouldn't reach this point |
2307 | -} |
2308 | - |
2309 | -DirectionalDragArea::ActiveTouchInfo &DirectionalDragArea::ActiveTouchesInfo::getEmptySlot() |
2310 | -{ |
2311 | - Q_ASSERT(m_lastUsedIndex < m_vector.size()); |
2312 | - |
2313 | - // Look for an in-between vacancy first |
2314 | - for (int i = 0; i < m_lastUsedIndex; ++i) { |
2315 | - ActiveTouchInfo &activeTouchInfo = m_vector[i]; |
2316 | - if (!activeTouchInfo.isValid()) { |
2317 | - return activeTouchInfo; |
2318 | - } |
2319 | - } |
2320 | - |
2321 | - ++m_lastUsedIndex; |
2322 | - if (m_lastUsedIndex >= m_vector.size()) { |
2323 | - m_vector.resize(m_lastUsedIndex + 1); |
2324 | - } |
2325 | - |
2326 | - return m_vector[m_lastUsedIndex]; |
2327 | -} |
2328 | - |
2329 | -void DirectionalDragArea::ActiveTouchesInfo::freeSlot(int index) |
2330 | -{ |
2331 | - m_vector[index].reset(); |
2332 | - if (index == m_lastUsedIndex) { |
2333 | - do { |
2334 | - --m_lastUsedIndex; |
2335 | - } while (m_lastUsedIndex >= 0 && !m_vector.at(m_lastUsedIndex).isValid()); |
2336 | - } |
2337 | + |
2338 | + #if ACTIVETOUCHESINFO_DEBUG |
2339 | + qDebug() << "[DDA::ActiveTouchesInfo]" << qPrintable(toString()); |
2340 | + #endif |
2341 | +} |
2342 | + |
2343 | +qint64 DirectionalDragArea::ActiveTouchesInfo::touchStartTime(int touchId) |
2344 | +{ |
2345 | + qint64 result = -1; |
2346 | + |
2347 | + m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) { |
2348 | + if (touchId == touchInfo->id) { |
2349 | + result = touchInfo->startTime; |
2350 | + return false; |
2351 | + } else { |
2352 | + return true; |
2353 | + } |
2354 | + }); |
2355 | + |
2356 | + Q_ASSERT(result != -1); |
2357 | + return result; |
2358 | +} |
2359 | + |
2360 | +void DirectionalDragArea::ActiveTouchesInfo::removeTouchPoint(int touchId) |
2361 | +{ |
2362 | + m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) { |
2363 | + if (touchId == touchInfo->id) { |
2364 | + m_touchInfoPool.freeSlot(touchInfo); |
2365 | + return false; |
2366 | + } else { |
2367 | + return true; |
2368 | + } |
2369 | + }); |
2370 | + |
2371 | + #if ACTIVETOUCHESINFO_DEBUG |
2372 | + qDebug() << "[DDA::ActiveTouchesInfo]" << qPrintable(toString()); |
2373 | + #endif |
2374 | } |
2375 | |
2376 | qint64 DirectionalDragArea::ActiveTouchesInfo::mostRecentStartTime() |
2377 | { |
2378 | - Q_ASSERT(m_lastUsedIndex >= 0); |
2379 | - |
2380 | - qint64 highestStartTime = m_vector.at(0).startTime; |
2381 | - int i = 1; |
2382 | - do { |
2383 | - const ActiveTouchInfo &activeTouchInfo = m_vector.at(i); |
2384 | - if (activeTouchInfo.isValid() && activeTouchInfo.startTime > highestStartTime) { |
2385 | - highestStartTime = activeTouchInfo.startTime; |
2386 | + Q_ASSERT(!m_touchInfoPool.isEmpty()); |
2387 | + |
2388 | + qint64 highestStartTime = -1; |
2389 | + |
2390 | + m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &activeTouchInfo) { |
2391 | + if (activeTouchInfo->startTime > highestStartTime) { |
2392 | + highestStartTime = activeTouchInfo->startTime; |
2393 | } |
2394 | - ++i; |
2395 | - } while (i < m_lastUsedIndex); |
2396 | + return true; |
2397 | + }); |
2398 | |
2399 | return highestStartTime; |
2400 | } |
2401 | |
2402 | === modified file 'plugins/Ubuntu/Gestures/DirectionalDragArea.h' |
2403 | --- plugins/Ubuntu/Gestures/DirectionalDragArea.h 2014-05-02 08:29:26 +0000 |
2404 | +++ plugins/Ubuntu/Gestures/DirectionalDragArea.h 2014-10-10 15:05:15 +0000 |
2405 | @@ -1,5 +1,5 @@ |
2406 | /* |
2407 | - * Copyright (C) 2013 Canonical, Ltd. |
2408 | + * Copyright (C) 2013,2014 Canonical, Ltd. |
2409 | * |
2410 | * This program is free software; you can redistribute it and/or modify |
2411 | * it under the terms of the GNU General Public License as published by |
2412 | @@ -19,27 +19,16 @@ |
2413 | |
2414 | #include <QtQuick/QQuickItem> |
2415 | #include "AxisVelocityCalculator.h" |
2416 | -#include "UbuntuGesturesGlobal.h" |
2417 | +#include "UbuntuGesturesQmlGlobal.h" |
2418 | #include "Damper.h" |
2419 | #include "Direction.h" |
2420 | |
2421 | -namespace UbuntuGestures { |
2422 | -/* Defines an interface for a Timer. */ |
2423 | -class UBUNTUGESTURES_EXPORT AbstractTimer : public QObject { |
2424 | - Q_OBJECT |
2425 | -public: |
2426 | - AbstractTimer(QObject *parent) : QObject(parent), m_isRunning(false) {} |
2427 | - virtual int interval() const = 0; |
2428 | - virtual void setInterval(int msecs) = 0; |
2429 | - virtual void start() { m_isRunning = true; }; |
2430 | - virtual void stop() { m_isRunning = false; } |
2431 | - bool isRunning() const { return m_isRunning; } |
2432 | -Q_SIGNALS: |
2433 | - void timeout(); |
2434 | -private: |
2435 | - bool m_isRunning; |
2436 | -}; |
2437 | -} |
2438 | +// lib UbuntuGestures |
2439 | +#include <Pool.h> |
2440 | +#include <Timer.h> |
2441 | + |
2442 | +class TouchOwnershipEvent; |
2443 | +class UnownedTouchEvent; |
2444 | |
2445 | /* |
2446 | An area that detects axis-aligned single-finger drag gestures |
2447 | @@ -50,7 +39,7 @@ |
2448 | |
2449 | See doc/DirectionalDragArea.svg |
2450 | */ |
2451 | -class UBUNTUGESTURES_EXPORT DirectionalDragArea : public QQuickItem { |
2452 | +class UBUNTUGESTURESQML_EXPORT DirectionalDragArea : public QQuickItem { |
2453 | Q_OBJECT |
2454 | |
2455 | // The direction in which the gesture should move in order to be recognized. |
2456 | @@ -196,6 +185,12 @@ |
2457 | // Useful for testing, where a fake time source can be supplied |
2458 | void setTimeSource(const UbuntuGestures::SharedTimeSource &timeSource); |
2459 | |
2460 | + bool event(QEvent *e) override; |
2461 | + |
2462 | + // Maximum time, in milliseconds, between a press and a release, for a touch |
2463 | + // sequence to be considered a tap. |
2464 | + int maxTapDuration() const { return 300; } |
2465 | + |
2466 | Q_SIGNALS: |
2467 | void directionChanged(Direction::Type direction); |
2468 | void statusChanged(Status value); |
2469 | @@ -212,6 +207,10 @@ |
2470 | void touchYChanged(qreal value); |
2471 | void touchSceneXChanged(qreal value); |
2472 | void touchSceneYChanged(qreal value); |
2473 | + |
2474 | + // TODO: I would rather not have such signal as it has nothing to do with drag gestures. |
2475 | + // Remove when no longer used or move its implementation to the QML code that uses it |
2476 | + // See maxTapDuration() |
2477 | void tapped(); |
2478 | |
2479 | protected: |
2480 | @@ -219,7 +218,7 @@ |
2481 | |
2482 | private Q_SLOTS: |
2483 | void checkSpeed(); |
2484 | - void onEnabledChanged(); |
2485 | + void giveUpIfDisabledOrInvisible(); |
2486 | |
2487 | private: |
2488 | void touchEvent_absent(QTouchEvent *event); |
2489 | @@ -238,6 +237,12 @@ |
2490 | // returns the scalar projection between the given vector (in scene coordinates) |
2491 | // and m_sceneDirectionVector |
2492 | qreal projectOntoDirectionVector(const QPointF &sceneVector) const; |
2493 | + void touchOwnershipEvent(TouchOwnershipEvent *event); |
2494 | + void unownedTouchEvent(UnownedTouchEvent *event); |
2495 | + void unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent); |
2496 | + void watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints); |
2497 | + bool recognitionIsDisabled() const; |
2498 | + void emitSignalIfTapped(); |
2499 | |
2500 | Status m_status; |
2501 | |
2502 | @@ -283,18 +288,21 @@ |
2503 | public: |
2504 | ActiveTouchesInfo(const UbuntuGestures::SharedTimeSource &timeSource); |
2505 | void update(QTouchEvent *event); |
2506 | - ActiveTouchInfo &touchInfo(int id); |
2507 | + qint64 touchStartTime(int id); |
2508 | + bool isEmpty() const { return m_touchInfoPool.isEmpty(); } |
2509 | qint64 mostRecentStartTime(); |
2510 | UbuntuGestures::SharedTimeSource m_timeSource; |
2511 | - bool isEmpty() const { return m_lastUsedIndex == -1; } |
2512 | private: |
2513 | - void addTouchPoint(const QTouchEvent::TouchPoint &touchPoint); |
2514 | - ActiveTouchInfo &getEmptySlot(); |
2515 | - void freeSlot(int index); |
2516 | - void removeTouchPoint(const QTouchEvent::TouchPoint &touchPoint); |
2517 | - QVector<struct ActiveTouchInfo> m_vector; |
2518 | - int m_lastUsedIndex; |
2519 | + void addTouchPoint(int touchId); |
2520 | + void removeTouchPoint(int touchId); |
2521 | + #if ACTIVETOUCHESINFO_DEBUG |
2522 | + QString toString(); |
2523 | + #endif |
2524 | + |
2525 | + Pool<ActiveTouchInfo> m_touchInfoPool; |
2526 | } m_activeTouches; |
2527 | + |
2528 | + friend class tst_DirectionalDragArea; |
2529 | }; |
2530 | |
2531 | #endif // DIRECTIONAL_DRAG_AREA_H |
2532 | |
2533 | === modified file 'plugins/Ubuntu/Gestures/TimeSource.h' |
2534 | --- plugins/Ubuntu/Gestures/TimeSource.h 2013-10-22 15:56:37 +0000 |
2535 | +++ plugins/Ubuntu/Gestures/TimeSource.h 2014-10-10 15:05:15 +0000 |
2536 | @@ -21,14 +21,14 @@ |
2537 | #ifndef UBUNTUGESTURES_TIMESOURCE_H |
2538 | #define UBUNTUGESTURES_TIMESOURCE_H |
2539 | |
2540 | -#include "UbuntuGesturesGlobal.h" |
2541 | +#include "UbuntuGesturesQmlGlobal.h" |
2542 | #include <QSharedPointer> |
2543 | |
2544 | namespace UbuntuGestures { |
2545 | /* |
2546 | Interface for a time source. |
2547 | */ |
2548 | -class UBUNTUGESTURES_EXPORT TimeSource { |
2549 | +class UBUNTUGESTURESQML_EXPORT TimeSource { |
2550 | public: |
2551 | virtual ~TimeSource() {} |
2552 | /* Returns the current time in milliseconds since some reference time in the past. */ |
2553 | |
2554 | === added file 'plugins/Ubuntu/Gestures/TouchGate.cpp' |
2555 | --- plugins/Ubuntu/Gestures/TouchGate.cpp 1970-01-01 00:00:00 +0000 |
2556 | +++ plugins/Ubuntu/Gestures/TouchGate.cpp 2014-10-10 15:05:15 +0000 |
2557 | @@ -0,0 +1,347 @@ |
2558 | +/* |
2559 | + * Copyright (C) 2014 Canonical, Ltd. |
2560 | + * |
2561 | + * This program is free software; you can redistribute it and/or modify |
2562 | + * it under the terms of the GNU General Public License as published by |
2563 | + * the Free Software Foundation; version 3. |
2564 | + * |
2565 | + * This program is distributed in the hope that it will be useful, |
2566 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2567 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2568 | + * GNU General Public License for more details. |
2569 | + * |
2570 | + * You should have received a copy of the GNU General Public License |
2571 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2572 | + */ |
2573 | + |
2574 | +#include "TouchGate.h" |
2575 | + |
2576 | +#include <QCoreApplication> |
2577 | +#include <QDebug> |
2578 | + |
2579 | +#include <TouchOwnershipEvent.h> |
2580 | +#include <TouchRegistry.h> |
2581 | + |
2582 | +#pragma GCC diagnostic push |
2583 | +#pragma GCC diagnostic ignored "-pedantic" |
2584 | +#include <private/qquickitem_p.h> |
2585 | +#pragma GCC diagnostic pop |
2586 | + |
2587 | +#if TOUCHGATE_DEBUG |
2588 | +#include <DebugHelpers.h> |
2589 | +#endif |
2590 | + |
2591 | + |
2592 | +bool TouchGate::event(QEvent *e) |
2593 | +{ |
2594 | + if (e->type() == TouchOwnershipEvent::touchOwnershipEventType()) { |
2595 | + touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(e)); |
2596 | + return true; |
2597 | + } else { |
2598 | + return QQuickItem::event(e); |
2599 | + } |
2600 | +} |
2601 | + |
2602 | +void TouchGate::touchEvent(QTouchEvent *event) |
2603 | +{ |
2604 | + #if TOUCHGATE_DEBUG |
2605 | + qDebug() << "[TouchGate] got touch event" << qPrintable(touchEventToString(event)); |
2606 | + #endif |
2607 | + event->accept(); |
2608 | + |
2609 | + const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints(); |
2610 | + bool goodToGo = true; |
2611 | + for (int i = 0; i < touchPoints.count(); ++i) { |
2612 | + const QTouchEvent::TouchPoint &touchPoint = touchPoints[i]; |
2613 | + |
2614 | + if (touchPoint.state() == Qt::TouchPointPressed) { |
2615 | + Q_ASSERT(!m_touchInfoMap.contains(touchPoint.id())); |
2616 | + m_touchInfoMap[touchPoint.id()].ownership = OwnershipRequested; |
2617 | + m_touchInfoMap[touchPoint.id()].ended = false; |
2618 | + TouchRegistry::instance()->requestTouchOwnership(touchPoint.id(), this); |
2619 | + } |
2620 | + |
2621 | + goodToGo &= m_touchInfoMap.contains(touchPoint.id()) |
2622 | + && m_touchInfoMap[touchPoint.id()].ownership == OwnershipGranted; |
2623 | + |
2624 | + if (touchPoint.state() == Qt::TouchPointReleased && m_touchInfoMap.contains(touchPoint.id())) { |
2625 | + m_touchInfoMap[touchPoint.id()].ended = true; |
2626 | + } |
2627 | + |
2628 | + } |
2629 | + |
2630 | + if (goodToGo) { |
2631 | + if (m_storedEvents.isEmpty()) { |
2632 | + // let it pass through |
2633 | + dispatchTouchEventToTarget(event); |
2634 | + } else { |
2635 | + // Retain the event to ensure TouchGate dispatches them in order. |
2636 | + // Otherwise the current event would come before the stored ones, which are older. |
2637 | + #if TOUCHGATE_DEBUG |
2638 | + qDebug("[TouchGate] Storing event because thouches %s are still pending ownership.", |
2639 | + qPrintable(oldestPendingTouchIdsString())); |
2640 | + #endif |
2641 | + storeTouchEvent(event); |
2642 | + } |
2643 | + } else { |
2644 | + // Retain events that have unowned touches |
2645 | + storeTouchEvent(event); |
2646 | + } |
2647 | +} |
2648 | + |
2649 | +void TouchGate::touchOwnershipEvent(TouchOwnershipEvent *event) |
2650 | +{ |
2651 | + // TODO: Optimization: batch those actions as TouchOwnershipEvents |
2652 | + // might come one right after the other. |
2653 | + |
2654 | + Q_ASSERT(m_touchInfoMap.contains(event->touchId())); |
2655 | + |
2656 | + TouchInfo &touchInfo = m_touchInfoMap[event->touchId()]; |
2657 | + |
2658 | + if (event->gained()) { |
2659 | + #if TOUCHGATE_DEBUG |
2660 | + qDebug() << "[TouchGate] Got ownership of touch " << event->touchId(); |
2661 | + #endif |
2662 | + touchInfo.ownership = OwnershipGranted; |
2663 | + } else { |
2664 | + #if TOUCHGATE_DEBUG |
2665 | + qDebug() << "[TouchGate] Lost ownership of touch " << event->touchId(); |
2666 | + #endif |
2667 | + m_touchInfoMap.remove(event->touchId()); |
2668 | + removeTouchFromStoredEvents(event->touchId()); |
2669 | + } |
2670 | + |
2671 | + dispatchFullyOwnedEvents(); |
2672 | +} |
2673 | + |
2674 | +bool TouchGate::isTouchPointOwned(int touchId) const |
2675 | +{ |
2676 | + return m_touchInfoMap[touchId].ownership == OwnershipGranted; |
2677 | +} |
2678 | + |
2679 | +void TouchGate::storeTouchEvent(const QTouchEvent *event) |
2680 | +{ |
2681 | + #if TOUCHGATE_DEBUG |
2682 | + qDebug() << "[TouchGate] Storing" << qPrintable(touchEventToString(event)); |
2683 | + #endif |
2684 | + |
2685 | + TouchEvent clonedEvent(event); |
2686 | + m_storedEvents.append(std::move(clonedEvent)); |
2687 | +} |
2688 | + |
2689 | +void TouchGate::removeTouchFromStoredEvents(int touchId) |
2690 | +{ |
2691 | + int i = 0; |
2692 | + while (i < m_storedEvents.count()) { |
2693 | + TouchEvent &event = m_storedEvents[i]; |
2694 | + bool removed = event.removeTouch(touchId); |
2695 | + |
2696 | + if (removed && event.touchPoints.isEmpty()) { |
2697 | + m_storedEvents.removeAt(i); |
2698 | + } else { |
2699 | + ++i; |
2700 | + } |
2701 | + } |
2702 | +} |
2703 | + |
2704 | +void TouchGate::dispatchFullyOwnedEvents() |
2705 | +{ |
2706 | + while (!m_storedEvents.isEmpty() && eventIsFullyOwned(m_storedEvents.first())) { |
2707 | + TouchEvent event = m_storedEvents.takeFirst(); |
2708 | + dispatchTouchEventToTarget(event); |
2709 | + } |
2710 | +} |
2711 | + |
2712 | +#if TOUCHGATE_DEBUG |
2713 | +QString TouchGate::oldestPendingTouchIdsString() |
2714 | +{ |
2715 | + Q_ASSERT(!m_storedEvents.isEmpty()); |
2716 | + |
2717 | + QString str; |
2718 | + |
2719 | + const auto &touchPoints = m_storedEvents.first().touchPoints; |
2720 | + for (int i = 0; i < touchPoints.count(); ++i) { |
2721 | + if (!isTouchPointOwned(touchPoints[i].id())) { |
2722 | + if (!str.isEmpty()) { |
2723 | + str.append(", "); |
2724 | + } |
2725 | + str.append(QString::number(touchPoints[i].id())); |
2726 | + } |
2727 | + } |
2728 | + |
2729 | + return str; |
2730 | +} |
2731 | +#endif |
2732 | + |
2733 | +bool TouchGate::eventIsFullyOwned(const TouchGate::TouchEvent &event) const |
2734 | +{ |
2735 | + for (int i = 0; i < event.touchPoints.count(); ++i) { |
2736 | + if (!isTouchPointOwned(event.touchPoints[i].id())) { |
2737 | + return false; |
2738 | + } |
2739 | + } |
2740 | + |
2741 | + return true; |
2742 | +} |
2743 | + |
2744 | +void TouchGate::setTargetItem(QQuickItem *item) |
2745 | +{ |
2746 | + // TODO: changing the target item while dispatch of touch events is taking place will |
2747 | + // create a mess |
2748 | + |
2749 | + if (item == m_targetItem.data()) |
2750 | + return; |
2751 | + |
2752 | + m_targetItem = item; |
2753 | + Q_EMIT targetItemChanged(item); |
2754 | +} |
2755 | + |
2756 | +void TouchGate::dispatchTouchEventToTarget(const TouchEvent &event) |
2757 | +{ |
2758 | + dispatchTouchEventToTarget(event.eventType, |
2759 | + event.device, |
2760 | + event.modifiers, |
2761 | + event.touchPoints, |
2762 | + event.target, |
2763 | + event.window, |
2764 | + event.timestamp); |
2765 | +} |
2766 | + |
2767 | +void TouchGate::dispatchTouchEventToTarget(QTouchEvent* event) |
2768 | +{ |
2769 | + dispatchTouchEventToTarget(event->type(), |
2770 | + event->device(), |
2771 | + event->modifiers(), |
2772 | + event->touchPoints(), |
2773 | + event->target(), |
2774 | + event->window(), |
2775 | + event->timestamp()); |
2776 | +} |
2777 | + |
2778 | +void TouchGate::dispatchTouchEventToTarget(QEvent::Type eventType, |
2779 | + QTouchDevice *device, |
2780 | + Qt::KeyboardModifiers modifiers, |
2781 | + const QList<QTouchEvent::TouchPoint> &touchPoints, |
2782 | + QObject *target, |
2783 | + QWindow *window, |
2784 | + ulong timestamp) |
2785 | +{ |
2786 | + removeTouchInfoForEndedTouches(touchPoints); |
2787 | + |
2788 | + if (m_targetItem.isNull()) { |
2789 | + qWarning("[TouchGate] Cannot dispatch touch event because target item is null"); |
2790 | + return; |
2791 | + } |
2792 | + |
2793 | + QQuickItem *targetItem = m_targetItem.data(); |
2794 | + |
2795 | + if (!targetItem->isEnabled() || !targetItem->isVisible()) { |
2796 | + #if TOUCHGATE_DEBUG |
2797 | + qDebug() << "[TouchGate] Cannot dispatch touch event to" << targetItem |
2798 | + << "because it's disabled or invisible."; |
2799 | + #endif |
2800 | + return; |
2801 | + } |
2802 | + |
2803 | + // Map touch points to targetItem coordinates |
2804 | + QList<QTouchEvent::TouchPoint> targetTouchPoints = touchPoints; |
2805 | + transformTouchPoints(targetTouchPoints, QQuickItemPrivate::get(targetItem)->windowToItemTransform()); |
2806 | + QTouchEvent *eventForTargetItem = createQTouchEvent(eventType, device, modifiers, targetTouchPoints, |
2807 | + target, window, timestamp); |
2808 | + |
2809 | + #if TOUCHGATE_DEBUG |
2810 | + qDebug() << "[TouchGate] dispatching" << qPrintable(touchEventToString(eventForTargetItem)) |
2811 | + << "to" << targetItem; |
2812 | + #endif |
2813 | + |
2814 | + QCoreApplication::sendEvent(targetItem, eventForTargetItem); |
2815 | + |
2816 | + delete eventForTargetItem; |
2817 | +} |
2818 | + |
2819 | +// NB: From QQuickWindow |
2820 | +void TouchGate::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform) |
2821 | +{ |
2822 | + QMatrix4x4 transformMatrix(transform); |
2823 | + for (int i=0; i<touchPoints.count(); i++) { |
2824 | + QTouchEvent::TouchPoint &touchPoint = touchPoints[i]; |
2825 | + touchPoint.setRect(transform.mapRect(touchPoint.sceneRect())); |
2826 | + touchPoint.setStartPos(transform.map(touchPoint.startScenePos())); |
2827 | + touchPoint.setLastPos(transform.map(touchPoint.lastScenePos())); |
2828 | + touchPoint.setVelocity(transformMatrix.mapVector(touchPoint.velocity()).toVector2D()); |
2829 | + } |
2830 | +} |
2831 | + |
2832 | +QTouchEvent *TouchGate::createQTouchEvent(QEvent::Type eventType, |
2833 | + QTouchDevice *device, |
2834 | + Qt::KeyboardModifiers modifiers, |
2835 | + const QList<QTouchEvent::TouchPoint> &touchPoints, |
2836 | + QObject *target, |
2837 | + QWindow *window, |
2838 | + ulong timestamp) |
2839 | +{ |
2840 | + Qt::TouchPointStates eventStates = 0; |
2841 | + for (int i = 0; i < touchPoints.count(); i++) |
2842 | + eventStates |= touchPoints[i].state(); |
2843 | + // if all points have the same state, set the event type accordingly |
2844 | + switch (eventStates) { |
2845 | + case Qt::TouchPointPressed: |
2846 | + eventType = QEvent::TouchBegin; |
2847 | + break; |
2848 | + case Qt::TouchPointReleased: |
2849 | + eventType = QEvent::TouchEnd; |
2850 | + break; |
2851 | + default: |
2852 | + eventType = QEvent::TouchUpdate; |
2853 | + break; |
2854 | + } |
2855 | + |
2856 | + QTouchEvent *touchEvent = new QTouchEvent(eventType); |
2857 | + touchEvent->setWindow(window); |
2858 | + touchEvent->setTarget(target); |
2859 | + touchEvent->setDevice(device); |
2860 | + touchEvent->setModifiers(modifiers); |
2861 | + touchEvent->setTouchPoints(touchPoints); |
2862 | + touchEvent->setTouchPointStates(eventStates); |
2863 | + touchEvent->setTimestamp(timestamp); |
2864 | + touchEvent->accept(); |
2865 | + return touchEvent; |
2866 | +} |
2867 | + |
2868 | +void TouchGate::removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints) |
2869 | +{ |
2870 | + for (int i = 0; i < touchPoints.size(); ++i) {\ |
2871 | + const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i); |
2872 | + |
2873 | + if (touchPoint.state() == Qt::TouchPointReleased) { |
2874 | + Q_ASSERT(m_touchInfoMap.contains(touchPoint.id())); |
2875 | + Q_ASSERT(m_touchInfoMap[touchPoint.id()].ended); |
2876 | + Q_ASSERT(m_touchInfoMap[touchPoint.id()].ownership == OwnershipGranted); |
2877 | + m_touchInfoMap.remove(touchPoint.id()); |
2878 | + } |
2879 | + } |
2880 | +} |
2881 | + |
2882 | +TouchGate::TouchEvent::TouchEvent(const QTouchEvent *event) |
2883 | + : eventType(event->type()) |
2884 | + , device(event->device()) |
2885 | + , modifiers(event->modifiers()) |
2886 | + , touchPoints(event->touchPoints()) |
2887 | + , target(event->target()) |
2888 | + , window(event->window()) |
2889 | + , timestamp(event->timestamp()) |
2890 | +{ |
2891 | +} |
2892 | + |
2893 | +bool TouchGate::TouchEvent::removeTouch(int touchId) |
2894 | +{ |
2895 | + bool removed = false; |
2896 | + for (int i = 0; i < touchPoints.count() && !removed; ++i) { |
2897 | + if (touchPoints[i].id() == touchId) { |
2898 | + touchPoints.removeAt(i); |
2899 | + removed = true; |
2900 | + } |
2901 | + } |
2902 | + |
2903 | + return removed; |
2904 | +} |
2905 | |
2906 | === added file 'plugins/Ubuntu/Gestures/TouchGate.h' |
2907 | --- plugins/Ubuntu/Gestures/TouchGate.h 1970-01-01 00:00:00 +0000 |
2908 | +++ plugins/Ubuntu/Gestures/TouchGate.h 2014-10-10 15:05:15 +0000 |
2909 | @@ -0,0 +1,126 @@ |
2910 | +/* |
2911 | + * Copyright (C) 2014 Canonical, Ltd. |
2912 | + * |
2913 | + * This program is free software; you can redistribute it and/or modify |
2914 | + * it under the terms of the GNU General Public License as published by |
2915 | + * the Free Software Foundation; version 3. |
2916 | + * |
2917 | + * This program is distributed in the hope that it will be useful, |
2918 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2919 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2920 | + * GNU General Public License for more details. |
2921 | + * |
2922 | + * You should have received a copy of the GNU General Public License |
2923 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2924 | + */ |
2925 | + |
2926 | +#ifndef UBUNTU_TOUCH_GATE_H |
2927 | +#define UBUNTU_TOUCH_GATE_H |
2928 | + |
2929 | +#include "UbuntuGesturesQmlGlobal.h" |
2930 | + |
2931 | +#include <QQuickItem> |
2932 | +#include <QList> |
2933 | +#include <QPointer> |
2934 | +#include <QMap> |
2935 | + |
2936 | +#define TOUCHGATE_DEBUG 0 |
2937 | + |
2938 | +class TouchOwnershipEvent; |
2939 | + |
2940 | +/* |
2941 | + Blocks the passage of events until ownership over the related touch points is granted. |
2942 | + |
2943 | + Blocked touch events won't be discarded. Instead they will be buffered until ownership |
2944 | + is granted. If ownership is given to another item, the event buffer is cleared. |
2945 | + |
2946 | + A TouchGate is useful as a mediator for items that do not understand, or gracefully handle, |
2947 | + touch canceling. By having a TouchGate in front of them you guarantee that only owned touches (i.e., |
2948 | + touches that won't be canceled later) reaches them. |
2949 | + */ |
2950 | +class UBUNTUGESTURESQML_EXPORT TouchGate : public QQuickItem { |
2951 | + Q_OBJECT |
2952 | + |
2953 | + // Item that's going to receive the touch events that make it through the gate. |
2954 | + Q_PROPERTY(QQuickItem* targetItem READ targetItem WRITE setTargetItem NOTIFY targetItemChanged) |
2955 | + |
2956 | +public: |
2957 | + bool event(QEvent *e) override; |
2958 | + |
2959 | + QQuickItem *targetItem() { return m_targetItem; } |
2960 | + void setTargetItem(QQuickItem *item); |
2961 | + |
2962 | +Q_SIGNALS: |
2963 | + void targetItemChanged(QQuickItem *item); |
2964 | + |
2965 | +protected: |
2966 | + void touchEvent(QTouchEvent *event) override; |
2967 | +private: |
2968 | + class TouchEvent { |
2969 | + public: |
2970 | + TouchEvent(const QTouchEvent *event); |
2971 | + |
2972 | + bool removeTouch(int touchId); |
2973 | + |
2974 | + QEvent::Type eventType; |
2975 | + QTouchDevice *device; |
2976 | + Qt::KeyboardModifiers modifiers; |
2977 | + QList<QTouchEvent::TouchPoint> touchPoints; |
2978 | + QObject *target; |
2979 | + QWindow *window; |
2980 | + ulong timestamp; |
2981 | + }; |
2982 | + |
2983 | + void touchOwnershipEvent(TouchOwnershipEvent *event); |
2984 | + bool isTouchPointOwned(int touchId) const; |
2985 | + void storeTouchEvent(const QTouchEvent *event); |
2986 | + void removeTouchFromStoredEvents(int touchId); |
2987 | + void dispatchFullyOwnedEvents(); |
2988 | + bool eventIsFullyOwned(const TouchEvent &event) const; |
2989 | + |
2990 | + void dispatchTouchEventToTarget(const TouchEvent &event); |
2991 | + void dispatchTouchEventToTarget(QTouchEvent* event); |
2992 | + void dispatchTouchEventToTarget( |
2993 | + QEvent::Type eventType, |
2994 | + QTouchDevice *device, |
2995 | + Qt::KeyboardModifiers modifiers, |
2996 | + const QList<QTouchEvent::TouchPoint> &touchPoints, |
2997 | + QObject *target, |
2998 | + QWindow *window, |
2999 | + ulong timestamp); |
3000 | + |
3001 | + void transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform); |
3002 | + static QTouchEvent *createQTouchEvent(QEvent::Type eventType, |
3003 | + QTouchDevice *device, |
3004 | + Qt::KeyboardModifiers modifiers, |
3005 | + const QList<QTouchEvent::TouchPoint> &touchPoints, |
3006 | + QObject *target, |
3007 | + QWindow *window, |
3008 | + ulong timestamp); |
3009 | + void removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints); |
3010 | + |
3011 | + #if TOUCHGATE_DEBUG |
3012 | + QString oldestPendingTouchIdsString(); |
3013 | + #endif |
3014 | + |
3015 | + QList<TouchEvent> m_storedEvents; |
3016 | + |
3017 | + enum { |
3018 | + OwnershipUndefined, |
3019 | + OwnershipRequested, |
3020 | + OwnershipGranted, |
3021 | + }; |
3022 | + class TouchInfo { |
3023 | + public: |
3024 | + TouchInfo() {ownership = OwnershipUndefined; ended = false;} |
3025 | + int ownership; |
3026 | + bool ended; |
3027 | + }; |
3028 | + QMap<int, TouchInfo> m_touchInfoMap; |
3029 | + |
3030 | + QPointer<QQuickItem> m_targetItem; |
3031 | + |
3032 | + friend class tst_TouchGate; |
3033 | +}; |
3034 | + |
3035 | +#endif // UBUNTU_TOUCH_GATE_H |
3036 | |
3037 | === renamed file 'plugins/Ubuntu/Gestures/UbuntuGesturesGlobal.h' => 'plugins/Ubuntu/Gestures/UbuntuGesturesQmlGlobal.h' |
3038 | --- plugins/Ubuntu/Gestures/UbuntuGesturesGlobal.h 2013-06-05 22:03:08 +0000 |
3039 | +++ plugins/Ubuntu/Gestures/UbuntuGesturesQmlGlobal.h 2014-10-10 15:05:15 +0000 |
3040 | @@ -16,8 +16,8 @@ |
3041 | |
3042 | #include <QtCore/QtGlobal> |
3043 | |
3044 | -#if defined(UBUNTUGESTURES_LIBRARY) |
3045 | -# define UBUNTUGESTURES_EXPORT Q_DECL_EXPORT |
3046 | +#if defined(UBUNTUGESTURESQML_LIBRARY) |
3047 | +# define UBUNTUGESTURESQML_EXPORT Q_DECL_EXPORT |
3048 | #else |
3049 | -# define UBUNTUGESTURES_EXPORT Q_DECL_IMPORT |
3050 | +# define UBUNTUGESTURESQML_EXPORT Q_DECL_IMPORT |
3051 | #endif |
3052 | |
3053 | === modified file 'plugins/Ubuntu/Gestures/plugin.cpp' |
3054 | --- plugins/Ubuntu/Gestures/plugin.cpp 2013-11-22 13:43:40 +0000 |
3055 | +++ plugins/Ubuntu/Gestures/plugin.cpp 2014-10-10 15:05:15 +0000 |
3056 | @@ -19,6 +19,7 @@ |
3057 | #include "Direction.h" |
3058 | #include "DirectionalDragArea.h" |
3059 | #include "PressedOutsideNotifier.h" |
3060 | +#include "TouchGate.h" |
3061 | |
3062 | #include <qqml.h> |
3063 | |
3064 | @@ -28,10 +29,11 @@ |
3065 | return new Direction; |
3066 | } |
3067 | |
3068 | -void UbuntuGestureQmlPlugin::registerTypes(const char *uri) |
3069 | +void UbuntuGesturesQmlPlugin::registerTypes(const char *uri) |
3070 | { |
3071 | qmlRegisterSingletonType<Direction>(uri, 0, 1, "Direction", directionSingleton); |
3072 | qmlRegisterType<DirectionalDragArea>(uri, 0, 1, "DirectionalDragArea"); |
3073 | qmlRegisterType<AxisVelocityCalculator>(uri, 0, 1, "AxisVelocityCalculator"); |
3074 | qmlRegisterType<PressedOutsideNotifier>(uri, 0, 1, "PressedOutsideNotifier"); |
3075 | + qmlRegisterType<TouchGate>(uri, 0, 1, "TouchGate"); |
3076 | } |
3077 | |
3078 | === modified file 'plugins/Ubuntu/Gestures/plugin.h' |
3079 | --- plugins/Ubuntu/Gestures/plugin.h 2013-06-05 22:03:08 +0000 |
3080 | +++ plugins/Ubuntu/Gestures/plugin.h 2014-10-10 15:05:15 +0000 |
3081 | @@ -19,7 +19,7 @@ |
3082 | |
3083 | #include <QtQml/QQmlExtensionPlugin> |
3084 | |
3085 | -class UbuntuGestureQmlPlugin : public QQmlExtensionPlugin |
3086 | +class UbuntuGesturesQmlPlugin : public QQmlExtensionPlugin |
3087 | { |
3088 | Q_OBJECT |
3089 | Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") |
3090 | |
3091 | === modified file 'plugins/Ubuntu/Gestures/qmldir' |
3092 | --- plugins/Ubuntu/Gestures/qmldir 2014-05-02 22:57:00 +0000 |
3093 | +++ plugins/Ubuntu/Gestures/qmldir 2014-10-10 15:05:15 +0000 |
3094 | @@ -1,3 +1,3 @@ |
3095 | module Ubuntu.Gestures |
3096 | -plugin UbuntuGestureQml |
3097 | +plugin UbuntuGesturesQml |
3098 | typeinfo Gestures.qmltypes |
3099 | |
3100 | === modified file 'qml/Components/DragHandle.qml' |
3101 | --- qml/Components/DragHandle.qml 2014-02-12 18:51:36 +0000 |
3102 | +++ qml/Components/DragHandle.qml 2014-10-10 15:05:15 +0000 |
3103 | @@ -47,12 +47,12 @@ |
3104 | id: dragArea |
3105 | objectName: "dragHandle" |
3106 | |
3107 | - // Disable most of the gesture recognition parameters by default when hinting is used as |
3108 | + // Disable gesture recognition by default when hinting is used as |
3109 | // it conflicts with the hinting idea. |
3110 | - // The only part we keep in this situation is that it must be a single-finger gesture. |
3111 | distanceThreshold: hintDisplacement > 0 ? 0 : defaultDistanceThreshold |
3112 | maxSilenceTime: hintDisplacement > 0 ? 60*60*1000 : defaultMaxSilenceTime |
3113 | maxDeviation: hintDisplacement > 0 ? 999999 : defaultMaxDeviation |
3114 | + compositionTime: hintDisplacement > 0 ? 0 : defaultCompositionTime |
3115 | |
3116 | property bool stretch: false |
3117 | |
3118 | |
3119 | === modified file 'qml/Components/EdgeDragArea.qml' |
3120 | --- qml/Components/EdgeDragArea.qml 2013-10-22 15:56:37 +0000 |
3121 | +++ qml/Components/EdgeDragArea.qml 2014-10-10 15:05:15 +0000 |
3122 | @@ -34,6 +34,7 @@ |
3123 | distanceThreshold: defaultDistanceThreshold |
3124 | minSpeed: defaultMinSpeed |
3125 | maxSilenceTime: defaultMaxSilenceTime |
3126 | + compositionTime: defaultCompositionTime |
3127 | |
3128 | readonly property real defaultMaxDeviation: units.gu(3) |
3129 | readonly property real defaultWideningAngle: 50 |
3130 | @@ -41,4 +42,5 @@ |
3131 | // some people were getting false negatives with it enabled. |
3132 | readonly property real defaultMinSpeed: units.gu(0) |
3133 | readonly property int defaultMaxSilenceTime: 200 |
3134 | + readonly property int defaultCompositionTime: 60 |
3135 | } |
3136 | |
3137 | === modified file 'qml/Components/InputMethod.qml' |
3138 | --- qml/Components/InputMethod.qml 2014-07-23 18:25:53 +0000 |
3139 | +++ qml/Components/InputMethod.qml 2014-10-10 15:05:15 +0000 |
3140 | @@ -17,6 +17,7 @@ |
3141 | import QtQuick 2.0 |
3142 | import Unity.Application 0.1 |
3143 | import Ubuntu.Components 0.1 |
3144 | +import Ubuntu.Gestures 0.1 |
3145 | |
3146 | Item { |
3147 | id: root |
3148 | @@ -25,6 +26,18 @@ |
3149 | |
3150 | property int transitionDuration: UbuntuAnimation.FastDuration |
3151 | |
3152 | + Binding { target: surface; property: "z"; value: 0 } |
3153 | + |
3154 | + TouchGate { |
3155 | + x: UbuntuKeyboardInfo.x |
3156 | + y: UbuntuKeyboardInfo.y |
3157 | + z: 1 |
3158 | + width: UbuntuKeyboardInfo.width |
3159 | + height: UbuntuKeyboardInfo.height |
3160 | + |
3161 | + targetItem: root.surface |
3162 | + } |
3163 | + |
3164 | state: { |
3165 | if (surface && surface.state != MirSurfaceItem.Minimized) { |
3166 | return "shown"; |
3167 | |
3168 | === modified file 'qml/Dash/Dash.qml' |
3169 | --- qml/Dash/Dash.qml 2014-10-09 14:04:53 +0000 |
3170 | +++ qml/Dash/Dash.qml 2014-10-10 15:05:15 +0000 |
3171 | @@ -207,6 +207,12 @@ |
3172 | } |
3173 | } |
3174 | |
3175 | + // This is to avoid the situation where a bottom-edge swipe would bring up the dash overview |
3176 | + // (as expected) but would also cause the dash content flickable to move a bit, because |
3177 | + // that flickable was getting the touch events while overviewDragHandle was still undecided |
3178 | + // about whether that touch was indeed performing a directional drag gesture. |
3179 | + forceNonInteractive: overviewDragHandle.status != DirectionalDragArea.WaitingForTouch |
3180 | + |
3181 | enabled: overviewController.progress == 0 |
3182 | opacity: enabled ? 1 : 0 |
3183 | } |
3184 | @@ -363,16 +369,32 @@ |
3185 | height: units.gu(2) |
3186 | |
3187 | onSceneDistanceChanged: { |
3188 | - if (overviewController.enableAnimation) { |
3189 | - dashContentCache.scheduleUpdate(); |
3190 | + if (status == DirectionalDragArea.Recognized && initialSceneDistance != -1) { |
3191 | + if (overviewController.enableAnimation) { |
3192 | + dashContentCache.scheduleUpdate(); |
3193 | + } |
3194 | + overviewController.enableAnimation = false; |
3195 | + var deltaDistance = sceneDistance - initialSceneDistance; |
3196 | + overviewController.progress = Math.max(0, Math.min(1, deltaDistance / fullMovement)); |
3197 | } |
3198 | - overviewController.enableAnimation = false; |
3199 | - overviewController.progress = Math.max(0, Math.min(1, sceneDistance / fullMovement)); |
3200 | } |
3201 | |
3202 | - onDraggingChanged: { |
3203 | - overviewController.enableAnimation = true; |
3204 | - overviewController.progress = (overviewController.progress > 0.7) ? 1 : 0; |
3205 | + property int previousStatus: -1 |
3206 | + property int currentStatus: DirectionalDragArea.WaitingForTouch |
3207 | + property real initialSceneDistance: -1 |
3208 | + |
3209 | + onStatusChanged: { |
3210 | + previousStatus = currentStatus; |
3211 | + currentStatus = status; |
3212 | + |
3213 | + if (status == DirectionalDragArea.Recognized) { |
3214 | + initialSceneDistance = sceneDistance; |
3215 | + } else if (status == DirectionalDragArea.WaitingForTouch && |
3216 | + previousStatus == DirectionalDragArea.Recognized) { |
3217 | + overviewController.enableAnimation = true; |
3218 | + overviewController.progress = (overviewController.progress > 0.7) ? 1 : 0; |
3219 | + initialSceneDistance = -1; |
3220 | + } |
3221 | } |
3222 | } |
3223 | |
3224 | |
3225 | === modified file 'qml/Dash/DashContent.qml' |
3226 | --- qml/Dash/DashContent.qml 2014-09-22 14:24:34 +0000 |
3227 | +++ qml/Dash/DashContent.qml 2014-10-10 15:05:15 +0000 |
3228 | @@ -23,6 +23,7 @@ |
3229 | Item { |
3230 | id: dashContent |
3231 | |
3232 | + property bool forceNonInteractive: false |
3233 | property alias scopes: dashContentList.model |
3234 | readonly property alias currentIndex: dashContentList.currentIndex |
3235 | readonly property string currentScopeId: dashContentList.currentItem ? dashContentList.currentItem.scopeId : "" |
3236 | @@ -101,7 +102,8 @@ |
3237 | id: dashContentList |
3238 | objectName: "dashContentList" |
3239 | |
3240 | - interactive: dashContent.scopes.loaded && currentItem && !currentItem.moving && !currentItem.navigationShown && !currentItem.subPageShown |
3241 | + interactive: !dashContent.forceNonInteractive && dashContent.scopes.loaded && currentItem |
3242 | + && !currentItem.moving && !currentItem.navigationShown && !currentItem.subPageShown |
3243 | anchors.fill: parent |
3244 | orientation: ListView.Horizontal |
3245 | boundsBehavior: Flickable.DragAndOvershootBounds |
3246 | @@ -180,6 +182,7 @@ |
3247 | item.paginationCount = Qt.binding(function() { return dashContentList.count } ) |
3248 | item.paginationIndex = Qt.binding(function() { return dashContentList.currentIndex } ) |
3249 | item.holdingList = dashContentList; |
3250 | + item.forceNonInteractive = Qt.binding(function() { return dashContent.forceNonInteractive } ) |
3251 | } |
3252 | Connections { |
3253 | target: isCurrent ? scope : null |
3254 | |
3255 | === modified file 'qml/Dash/GenericScopeView.qml' |
3256 | --- qml/Dash/GenericScopeView.qml 2014-10-06 15:54:18 +0000 |
3257 | +++ qml/Dash/GenericScopeView.qml 2014-10-10 15:05:15 +0000 |
3258 | @@ -26,6 +26,7 @@ |
3259 | id: scopeView |
3260 | |
3261 | readonly property bool navigationShown: pageHeaderLoader.item ? pageHeaderLoader.item.bottomItem[0].openList : false |
3262 | + property bool forceNonInteractive: false |
3263 | property var scope: null |
3264 | property SortFilterProxyModel categories: categoryFilter |
3265 | property bool isCurrent: false |
3266 | @@ -149,6 +150,7 @@ |
3267 | ScopeListView { |
3268 | id: categoryView |
3269 | objectName: "categoryListView" |
3270 | + interactive: !forceNonInteractive |
3271 | |
3272 | x: subPageLoader.open ? -width : 0 |
3273 | visible: x != -width |
3274 | |
3275 | === modified file 'qml/Launcher/Launcher.qml' |
3276 | --- qml/Launcher/Launcher.qml 2014-10-06 16:39:10 +0000 |
3277 | +++ qml/Launcher/Launcher.qml 2014-10-10 15:05:15 +0000 |
3278 | @@ -1,5 +1,5 @@ |
3279 | /* |
3280 | - * Copyright (C) 2013 Canonical, Ltd. |
3281 | + * Copyright (C) 2013-2014 Canonical, Ltd. |
3282 | * |
3283 | * This program is free software; you can redistribute it and/or modify |
3284 | * it under the terms of the GNU General Public License as published by |
3285 | @@ -76,6 +76,7 @@ |
3286 | |
3287 | Timer { |
3288 | id: dismissTimer |
3289 | + objectName: "dismissTimer" |
3290 | interval: 5000 |
3291 | onTriggered: { |
3292 | if (!panel.preventHiding) { |
3293 | @@ -218,6 +219,7 @@ |
3294 | |
3295 | EdgeDragArea { |
3296 | id: dragArea |
3297 | + objectName: "launcherDragArea" |
3298 | |
3299 | direction: Direction.Rightwards |
3300 | |
3301 | |
3302 | === modified file 'qml/Launcher/LauncherPanel.qml' |
3303 | --- qml/Launcher/LauncherPanel.qml 2014-10-07 08:25:59 +0000 |
3304 | +++ qml/Launcher/LauncherPanel.qml 2014-10-10 15:05:15 +0000 |
3305 | @@ -45,6 +45,7 @@ |
3306 | } |
3307 | |
3308 | Rectangle { |
3309 | + objectName: "buttonShowDashHome" |
3310 | width: parent.width |
3311 | height: units.gu(7) |
3312 | color: UbuntuColors.orange |
3313 | |
3314 | === modified file 'qml/Stages/ApplicationWindow.qml' |
3315 | --- qml/Stages/ApplicationWindow.qml 2014-09-19 11:20:07 +0000 |
3316 | +++ qml/Stages/ApplicationWindow.qml 2014-10-10 15:05:15 +0000 |
3317 | @@ -61,14 +61,6 @@ |
3318 | // Remove this when possible |
3319 | property bool surfaceInitialized: false |
3320 | |
3321 | - function forceSurfaceActiveFocusIfReady() { |
3322 | - if (sessionContainer.surface !== null && |
3323 | - sessionContainer.surface.focus && |
3324 | - sessionContainer.surface.parent === sessionContainer.surfaceContainer && |
3325 | - sessionContainer.surface.enabled) { |
3326 | - sessionContainer.surface.forceActiveFocus(); |
3327 | - } |
3328 | - } |
3329 | } |
3330 | |
3331 | Timer { |
3332 | @@ -77,15 +69,6 @@ |
3333 | onTriggered: { if (sessionContainer.surface) {d.surfaceInitialized = true;} } |
3334 | } |
3335 | |
3336 | - Connections { |
3337 | - target: sessionContainer.surface |
3338 | - // FIXME: I would rather not need to do this, but currently it doesn't get |
3339 | - // active focus without it and I don't know why. |
3340 | - onFocusChanged: d.forceSurfaceActiveFocusIfReady(); |
3341 | - onParentChanged: d.forceSurfaceActiveFocusIfReady(); |
3342 | - onEnabledChanged: d.forceSurfaceActiveFocusIfReady(); |
3343 | - } |
3344 | - |
3345 | Image { |
3346 | id: screenshotImage |
3347 | objectName: "screenshotImage" |
3348 | @@ -132,7 +115,6 @@ |
3349 | onSurfaceChanged: { |
3350 | if (sessionContainer.surface) { |
3351 | surfaceInitTimer.start(); |
3352 | - d.forceSurfaceActiveFocusIfReady(); |
3353 | } else { |
3354 | d.surfaceInitialized = false; |
3355 | } |
3356 | |
3357 | === modified file 'qml/Stages/PhoneStage.qml' |
3358 | --- qml/Stages/PhoneStage.qml 2014-10-07 18:18:44 +0000 |
3359 | +++ qml/Stages/PhoneStage.qml 2014-10-10 15:05:15 +0000 |
3360 | @@ -131,7 +131,7 @@ |
3361 | |
3362 | // This indicates when the spreadView is active. That means, all the animations |
3363 | // are activated and tiles need to line up for the spread. |
3364 | - readonly property bool active: shiftedContentX > 0 || spreadDragArea.dragging |
3365 | + readonly property bool active: shiftedContentX > 0 || spreadDragArea.status === DirectionalDragArea.Recognized |
3366 | |
3367 | // The flickable needs to fill the screen in order to get touch events all over. |
3368 | // However, we don't want to the user to be able to scroll back all the way. For |
3369 | @@ -418,6 +418,7 @@ |
3370 | |
3371 | EdgeDragArea { |
3372 | id: spreadDragArea |
3373 | + objectName: "spreadDragArea" |
3374 | direction: Direction.Leftwards |
3375 | enabled: spreadView.phase != 2 && root.spreadEnabled |
3376 | |
3377 | @@ -437,9 +438,10 @@ |
3378 | spreadView.phase = 0; |
3379 | spreadView.contentX = -spreadView.shift; |
3380 | } |
3381 | - if (dragging && attachedToView) { |
3382 | + if (dragging && status == DirectionalDragArea.Recognized && attachedToView) { |
3383 | // Gesture recognized. Let's move the spreadView with the finger |
3384 | - spreadView.contentX = -touchX + spreadDragArea.width - spreadView.shift; |
3385 | + var finalX = Math.min(touchX + width, width); |
3386 | + spreadView.contentX = -finalX + spreadDragArea.width - spreadView.shift; |
3387 | } |
3388 | if (attachedToView && spreadView.shiftedContentX >= spreadView.width * spreadView.positionMarker3) { |
3389 | // We passed positionMarker3. Detach from spreadView and snap it. |
3390 | @@ -449,7 +451,13 @@ |
3391 | gesturePoints.push(touchX); |
3392 | } |
3393 | |
3394 | + property int previousStatus: -1 |
3395 | + property int currentStatus: DirectionalDragArea.WaitingForTouch |
3396 | + |
3397 | onStatusChanged: { |
3398 | + previousStatus = currentStatus; |
3399 | + currentStatus = status; |
3400 | + |
3401 | if (status == DirectionalDragArea.Recognized) { |
3402 | attachedToView = true; |
3403 | } |
3404 | @@ -457,7 +465,7 @@ |
3405 | |
3406 | onDraggingChanged: { |
3407 | if (dragging) { |
3408 | - // Gesture recognized. Start recording this gesture |
3409 | + // A potential edge-drag gesture has started. Start recording it |
3410 | gesturePoints = []; |
3411 | return; |
3412 | } |
3413 | @@ -474,7 +482,8 @@ |
3414 | } |
3415 | gesturePoints = []; |
3416 | |
3417 | - if (oneWayFlick && spreadView.shiftedContentX > units.gu(2) && |
3418 | + if (previousStatus == DirectionalDragArea.Recognized && |
3419 | + oneWayFlick && spreadView.shiftedContentX > units.gu(2) && |
3420 | spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) { |
3421 | // If it was a short one-way movement, do the Alt+Tab switch |
3422 | // no matter if we didn't cross positionMarker1 yet. |
3423 | |
3424 | === modified file 'qml/Stages/SessionContainer.qml' |
3425 | --- qml/Stages/SessionContainer.qml 2014-09-11 10:41:11 +0000 |
3426 | +++ qml/Stages/SessionContainer.qml 2014-10-10 15:05:15 +0000 |
3427 | @@ -23,7 +23,7 @@ |
3428 | property QtObject session |
3429 | readonly property var childSessions: session ? session.childSessions : null |
3430 | readonly property alias surface: _surfaceContainer.surface |
3431 | - property bool interactive: true |
3432 | + property alias interactive: _surfaceContainer.interactive |
3433 | property int orientation |
3434 | |
3435 | readonly property alias surfaceContainer: _surfaceContainer |
3436 | @@ -34,18 +34,6 @@ |
3437 | orientation: root.orientation |
3438 | } |
3439 | |
3440 | - Binding { |
3441 | - target: surfaceContainer.surface |
3442 | - when: surfaceContainer.surface |
3443 | - property: "enabled" |
3444 | - value: root.interactive |
3445 | - } |
3446 | - Binding { |
3447 | - target: surfaceContainer.surface |
3448 | - when: surfaceContainer.surface |
3449 | - property: "focus" |
3450 | - value: root.interactive |
3451 | - } |
3452 | |
3453 | Repeater { |
3454 | model: root.childSessions |
3455 | |
3456 | === modified file 'qml/Stages/SurfaceContainer.qml' |
3457 | --- qml/Stages/SurfaceContainer.qml 2014-09-11 10:41:11 +0000 |
3458 | +++ qml/Stages/SurfaceContainer.qml 2014-10-10 15:05:15 +0000 |
3459 | @@ -16,6 +16,7 @@ |
3460 | |
3461 | import QtQuick 2.0 |
3462 | import Ubuntu.Components 1.1 |
3463 | +import Ubuntu.Gestures 0.1 // For TouchGate |
3464 | |
3465 | Item { |
3466 | id: root |
3467 | @@ -23,21 +24,51 @@ |
3468 | property Item surface: null |
3469 | property bool hadSurface: false |
3470 | property int orientation |
3471 | + property bool interactive |
3472 | |
3473 | onSurfaceChanged: { |
3474 | if (surface) { |
3475 | surface.parent = root; |
3476 | + d.forceSurfaceActiveFocusIfReady(); |
3477 | } else { |
3478 | hadSurface = true; |
3479 | } |
3480 | } |
3481 | - Binding { |
3482 | - target: surface |
3483 | - property: "anchors.fill"; value: root |
3484 | - } |
3485 | - Binding { |
3486 | - target: surface |
3487 | - property: "orientation"; value: root.orientation |
3488 | + Binding { target: surface; property: "anchors.fill"; value: root } |
3489 | + Binding { target: surface; property: "orientation"; value: root.orientation } |
3490 | + Binding { target: surface; property: "z"; value: 1 } |
3491 | + Binding { target: surface; property: "enabled"; value: root.interactive; when: surface } |
3492 | + Binding { target: surface; property: "focus"; value: root.interactive; when: surface } |
3493 | + |
3494 | + TouchGate { |
3495 | + targetItem: surface |
3496 | + anchors.fill: root |
3497 | + enabled: root.surface ? root.surface.enabled : false |
3498 | + z: 2 |
3499 | + } |
3500 | + |
3501 | + Connections { |
3502 | + target: root.surface |
3503 | + // FIXME: I would rather not need to do this, but currently it doesn't get |
3504 | + // active focus without it and I don't know why. |
3505 | + // Possibly because if an item get focus=true before it has a parent, once |
3506 | + // it gets a parent QQuickWindow won't check its focus and update its activeFocus |
3507 | + // accordingly. Unlike when you focus=true after the item already has a parent. |
3508 | + onFocusChanged: d.forceSurfaceActiveFocusIfReady(); |
3509 | + onParentChanged: d.forceSurfaceActiveFocusIfReady(); |
3510 | + onEnabledChanged: d.forceSurfaceActiveFocusIfReady(); |
3511 | + } |
3512 | + |
3513 | + QtObject { |
3514 | + id: d |
3515 | + function forceSurfaceActiveFocusIfReady() { |
3516 | + if (root.surface !== null && |
3517 | + root.surface.focus && |
3518 | + root.surface.parent === root && |
3519 | + root.surface.enabled) { |
3520 | + root.surface.forceActiveFocus(); |
3521 | + } |
3522 | + } |
3523 | } |
3524 | |
3525 | states: [ |
3526 | |
3527 | === modified file 'src/CMakeLists.txt' |
3528 | --- src/CMakeLists.txt 2014-09-04 23:11:17 +0000 |
3529 | +++ src/CMakeLists.txt 2014-10-10 15:05:15 +0000 |
3530 | @@ -1,5 +1,6 @@ |
3531 | include_directories( |
3532 | ${Qt5Gui_PRIVATE_INCLUDE_DIRS} |
3533 | + ${CMAKE_SOURCE_DIR}/libs/UbuntuGestures |
3534 | ) |
3535 | |
3536 | file(GLOB_RECURSE QML_FILES |
3537 | @@ -29,6 +30,11 @@ |
3538 | target_link_libraries(${SHELL_APP} ${XCB_LDFLAGS}) |
3539 | endif() |
3540 | |
3541 | +target_link_libraries(${SHELL_APP} UbuntuGestures) |
3542 | + |
3543 | +# For it to find libUbuntuGestures.so |
3544 | +set_target_properties(${SHELL_APP} PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${SHELL_PRIVATE_LIBDIR}") |
3545 | + |
3546 | # install binaries |
3547 | install(TARGETS ${SHELL_APP} |
3548 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} |
3549 | |
3550 | === modified file 'src/Dash/CMakeLists.txt' |
3551 | --- src/Dash/CMakeLists.txt 2014-09-22 15:37:31 +0000 |
3552 | +++ src/Dash/CMakeLists.txt 2014-10-10 15:05:15 +0000 |
3553 | @@ -9,6 +9,11 @@ |
3554 | |
3555 | qt5_use_modules(unity8-dash Gui Qml Quick Test) |
3556 | |
3557 | +# For it to find libUbuntuGestures.so, needed by Ubuntu.Gestures QML module. |
3558 | +set_target_properties(unity8-dash PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${SHELL_PRIVATE_LIBDIR}") |
3559 | + |
3560 | +target_link_libraries(unity8-dash UbuntuGestures) |
3561 | + |
3562 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") |
3563 | |
3564 | # install binaries |
3565 | |
3566 | === modified file 'src/Dash/main.cpp' |
3567 | --- src/Dash/main.cpp 2014-09-18 21:22:37 +0000 |
3568 | +++ src/Dash/main.cpp 2014-10-10 15:05:15 +0000 |
3569 | @@ -31,6 +31,9 @@ |
3570 | #include "../ApplicationArguments.h" |
3571 | #include "../CachingNetworkManagerFactory.h" |
3572 | |
3573 | +// Ubuntu Gestures |
3574 | +#include <TouchRegistry.h> |
3575 | + |
3576 | int main(int argc, const char *argv[]) |
3577 | { |
3578 | QGuiApplication *application = new QGuiApplication(argc, (char**)argv); |
3579 | @@ -88,6 +91,9 @@ |
3580 | view->setTitle("Scopes"); |
3581 | view->rootContext()->setContextProperty("applicationArguments", &qmlArgs); |
3582 | |
3583 | + TouchRegistry touchRegistry; |
3584 | + view->installEventFilter(&touchRegistry); |
3585 | + |
3586 | // You will need this if you want to interact with touch-only components using a mouse |
3587 | // Needed only when manually testing on a desktop. |
3588 | MouseTouchAdaptor *mouseTouchAdaptor = 0; |
3589 | |
3590 | === modified file 'src/main.cpp' |
3591 | --- src/main.cpp 2014-09-01 14:44:30 +0000 |
3592 | +++ src/main.cpp 2014-10-10 15:05:15 +0000 |
3593 | @@ -34,6 +34,9 @@ |
3594 | #include "ApplicationArguments.h" |
3595 | #include "CachingNetworkManagerFactory.h" |
3596 | |
3597 | +// Ubuntu Gestures |
3598 | +#include <TouchRegistry.h> |
3599 | + |
3600 | int main(int argc, const char *argv[]) |
3601 | { |
3602 | bool isMirServer = false; |
3603 | @@ -120,6 +123,8 @@ |
3604 | if (parser.isSet(framelessOption)) { |
3605 | view->setFlags(Qt::FramelessWindowHint); |
3606 | } |
3607 | + TouchRegistry touchRegistry; |
3608 | + view->installEventFilter(&touchRegistry); |
3609 | |
3610 | // You will need this if you want to interact with touch-only components using a mouse |
3611 | // Needed only when manually testing on a desktop. |
3612 | |
3613 | === modified file 'tests/CMakeLists.txt' |
3614 | --- tests/CMakeLists.txt 2014-05-30 09:21:39 +0000 |
3615 | +++ tests/CMakeLists.txt 2014-10-10 15:05:15 +0000 |
3616 | @@ -7,6 +7,7 @@ |
3617 | add_custom_target(qmltests) |
3618 | add_dependencies(qmltests qmlunittests qmluitests) |
3619 | |
3620 | +add_subdirectory(libs) |
3621 | add_subdirectory(qmltests) |
3622 | add_subdirectory(mocks) |
3623 | add_subdirectory(whitespace) |
3624 | |
3625 | === added directory 'tests/libs' |
3626 | === added file 'tests/libs/CMakeLists.txt' |
3627 | --- tests/libs/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
3628 | +++ tests/libs/CMakeLists.txt 2014-10-10 15:05:15 +0000 |
3629 | @@ -0,0 +1,1 @@ |
3630 | +add_subdirectory(UbuntuGestures) |
3631 | |
3632 | === added directory 'tests/libs/UbuntuGestures' |
3633 | === added file 'tests/libs/UbuntuGestures/CMakeLists.txt' |
3634 | --- tests/libs/UbuntuGestures/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
3635 | +++ tests/libs/UbuntuGestures/CMakeLists.txt 2014-10-10 15:05:15 +0000 |
3636 | @@ -0,0 +1,20 @@ |
3637 | +include_directories( |
3638 | + ${CMAKE_SOURCE_DIR}/libs/UbuntuGestures |
3639 | + ${CMAKE_CURRENT_BINARY_DIR} |
3640 | + ) |
3641 | + |
3642 | +macro(add_gesture_test CLASSNAME) |
3643 | + set(testCommand |
3644 | + ${CLASSNAME}TestExec -o ${CMAKE_BINARY_DIR}/test${CLASSNAME}.xml,xunitxml -o -,txt) |
3645 | + |
3646 | + add_test(NAME test${CLASSNAME} COMMAND ${testCommand}) |
3647 | + |
3648 | + set_tests_properties(test${CLASSNAME} PROPERTIES |
3649 | + ENVIRONMENT LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/libs/UbuntuGestures) |
3650 | + |
3651 | + add_executable(${CLASSNAME}TestExec tst_${CLASSNAME}.cpp) |
3652 | + qt5_use_modules(${CLASSNAME}TestExec Test Core Gui Quick) |
3653 | + target_link_libraries(${CLASSNAME}TestExec UbuntuGestures) |
3654 | +endmacro(add_gesture_test) |
3655 | + |
3656 | +add_gesture_test(TouchRegistry) |
3657 | |
3658 | === added file 'tests/libs/UbuntuGestures/tst_TouchRegistry.cpp' |
3659 | --- tests/libs/UbuntuGestures/tst_TouchRegistry.cpp 1970-01-01 00:00:00 +0000 |
3660 | +++ tests/libs/UbuntuGestures/tst_TouchRegistry.cpp 2014-10-10 15:05:15 +0000 |
3661 | @@ -0,0 +1,803 @@ |
3662 | +/* |
3663 | + * Copyright (C) 2014 Canonical, Ltd. |
3664 | + * |
3665 | + * This program is free software; you can redistribute it and/or modify |
3666 | + * it under the terms of the GNU General Public License as published by |
3667 | + * the Free Software Foundation; version 3. |
3668 | + * |
3669 | + * This program is distributed in the hope that it will be useful, |
3670 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3671 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3672 | + * GNU General Public License for more details. |
3673 | + * |
3674 | + * You should have received a copy of the GNU General Public License |
3675 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3676 | + */ |
3677 | + |
3678 | +#include <QtTest> |
3679 | +#include <QSet> |
3680 | +#include <QTouchEvent> |
3681 | + |
3682 | +#include <Timer.h> |
3683 | +#include <TouchOwnershipEvent.h> |
3684 | +#include <TouchRegistry.h> |
3685 | +#include <UnownedTouchEvent.h> |
3686 | + |
3687 | +using namespace UbuntuGestures; |
3688 | + |
3689 | +class TouchMemento { |
3690 | +public: |
3691 | + TouchMemento(const QTouchEvent *touchEvent); |
3692 | + Qt::TouchPointStates touchPointStates; |
3693 | + QList<QTouchEvent::TouchPoint> touchPoints; |
3694 | + |
3695 | + bool containsTouchWithId(int touchId) const; |
3696 | +}; |
3697 | + |
3698 | +class DummyCandidate : public QQuickItem |
3699 | +{ |
3700 | + Q_OBJECT |
3701 | +public: |
3702 | + bool event(QEvent *e) override; |
3703 | + QSet<int> ownedTouches; |
3704 | + QSet<int> lostTouches; |
3705 | + QList<TouchMemento> unownedTouchEvents; |
3706 | +}; |
3707 | + |
3708 | +class tst_TouchRegistry : public QObject |
3709 | +{ |
3710 | + Q_OBJECT |
3711 | +public: |
3712 | + tst_TouchRegistry() : QObject(0) { } |
3713 | +private Q_SLOTS: |
3714 | + void initTestCase() {} // will be called before the first test function is executed |
3715 | + void cleanupTestCase() {} // will be called after the last test function was executed. |
3716 | + |
3717 | + void init() {} // called right before each and every test function is executed |
3718 | + void cleanup() {} // called right after each and every test function is executed |
3719 | + |
3720 | + void requestWithNoCandidates(); |
3721 | + void lateCandidateRequestGetsNothing(); |
3722 | + void lateCandidateGestOwnershipOnceEarlyCandidateQuits(); |
3723 | + void dispatchesTouchEventsToCandidates(); |
3724 | + void dispatchesTouchEventsToWatchers(); |
3725 | + void keepDispatchingToWatchersAfterLastCandidateGivesUp(); |
3726 | + void candidatesAndWatchers_1(); |
3727 | + void candidatesAndWatchers_2(); |
3728 | + void rejectingTouchfterItsEnd(); |
3729 | + void removeOldUndecidedCandidates(); |
3730 | +}; |
3731 | + |
3732 | +void tst_TouchRegistry::requestWithNoCandidates() |
3733 | +{ |
3734 | + TouchRegistry touchRegistry; |
3735 | + DummyCandidate candidate; |
3736 | + |
3737 | + { |
3738 | + QList<QTouchEvent::TouchPoint> touchPoints; |
3739 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
3740 | + touchPoints[0].setState(Qt::TouchPointPressed); |
3741 | + QTouchEvent touchEvent(QEvent::TouchBegin, |
3742 | + 0 /* device */, |
3743 | + Qt::NoModifier, |
3744 | + Qt::TouchPointPressed, |
3745 | + touchPoints); |
3746 | + touchRegistry.update(&touchEvent); |
3747 | + } |
3748 | + |
3749 | + touchRegistry.requestTouchOwnership(0, &candidate); |
3750 | + |
3751 | + QVERIFY(candidate.ownedTouches.contains(0)); |
3752 | +} |
3753 | + |
3754 | +void tst_TouchRegistry::lateCandidateRequestGetsNothing() |
3755 | +{ |
3756 | + TouchRegistry touchRegistry; |
3757 | + DummyCandidate earlyCandidate; |
3758 | + earlyCandidate.setObjectName("early"); |
3759 | + DummyCandidate lateCandidate; |
3760 | + lateCandidate.setObjectName("late"); |
3761 | + |
3762 | + { |
3763 | + QList<QTouchEvent::TouchPoint> touchPoints; |
3764 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
3765 | + touchPoints[0].setState(Qt::TouchPointPressed); |
3766 | + QTouchEvent touchEvent(QEvent::TouchBegin, |
3767 | + 0 /* device */, |
3768 | + Qt::NoModifier, |
3769 | + Qt::TouchPointPressed, |
3770 | + touchPoints); |
3771 | + touchRegistry.update(&touchEvent); |
3772 | + } |
3773 | + |
3774 | + touchRegistry.addCandidateOwnerForTouch(0, &earlyCandidate); |
3775 | + touchRegistry.addCandidateOwnerForTouch(0, &lateCandidate); |
3776 | + |
3777 | + QVERIFY(earlyCandidate.ownedTouches.isEmpty()); |
3778 | + QVERIFY(lateCandidate.ownedTouches.isEmpty()); |
3779 | + |
3780 | + touchRegistry.requestTouchOwnership(0, &lateCandidate); |
3781 | + |
3782 | + QVERIFY(earlyCandidate.ownedTouches.isEmpty()); |
3783 | + QVERIFY(lateCandidate.ownedTouches.isEmpty()); |
3784 | + |
3785 | + touchRegistry.requestTouchOwnership(0, &earlyCandidate); |
3786 | + |
3787 | + QVERIFY(earlyCandidate.ownedTouches.contains(0)); |
3788 | + QVERIFY(lateCandidate.ownedTouches.isEmpty()); |
3789 | +} |
3790 | + |
3791 | +void tst_TouchRegistry::lateCandidateGestOwnershipOnceEarlyCandidateQuits() |
3792 | +{ |
3793 | + TouchRegistry touchRegistry; |
3794 | + DummyCandidate earlyCandidate; |
3795 | + DummyCandidate lateCandidate; |
3796 | + |
3797 | + { |
3798 | + QList<QTouchEvent::TouchPoint> touchPoints; |
3799 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
3800 | + touchPoints[0].setState(Qt::TouchPointPressed); |
3801 | + QTouchEvent touchEvent(QEvent::TouchBegin, |
3802 | + 0 /* device */, |
3803 | + Qt::NoModifier, |
3804 | + Qt::TouchPointPressed, |
3805 | + touchPoints); |
3806 | + touchRegistry.update(&touchEvent); |
3807 | + } |
3808 | + |
3809 | + touchRegistry.addCandidateOwnerForTouch(0, &earlyCandidate); |
3810 | + touchRegistry.addCandidateOwnerForTouch(0, &lateCandidate); |
3811 | + |
3812 | + QVERIFY(earlyCandidate.ownedTouches.isEmpty()); |
3813 | + QVERIFY(lateCandidate.ownedTouches.isEmpty()); |
3814 | + |
3815 | + touchRegistry.requestTouchOwnership(0, &lateCandidate); |
3816 | + |
3817 | + QVERIFY(earlyCandidate.ownedTouches.isEmpty()); |
3818 | + QVERIFY(lateCandidate.ownedTouches.isEmpty()); |
3819 | + |
3820 | + touchRegistry.removeCandidateOwnerForTouch(0, &earlyCandidate); |
3821 | + |
3822 | + QVERIFY(earlyCandidate.ownedTouches.isEmpty()); |
3823 | + QVERIFY(lateCandidate.ownedTouches.contains(0)); |
3824 | +} |
3825 | + |
3826 | +void tst_TouchRegistry::dispatchesTouchEventsToCandidates() |
3827 | +{ |
3828 | + TouchRegistry touchRegistry; |
3829 | + QQuickItem rootItem; |
3830 | + |
3831 | + DummyCandidate candidate0; |
3832 | + candidate0.setObjectName("0"); |
3833 | + candidate0.setParentItem(&rootItem); |
3834 | + candidate0.setX(1); |
3835 | + candidate0.setY(2); |
3836 | + |
3837 | + DummyCandidate candidate1; |
3838 | + candidate1.setObjectName("1"); |
3839 | + candidate0.setParentItem(&rootItem); |
3840 | + candidate1.setX(3); |
3841 | + candidate1.setY(4); |
3842 | + |
3843 | + { |
3844 | + QList<QTouchEvent::TouchPoint> touchPoints; |
3845 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
3846 | + touchPoints[0].setState(Qt::TouchPointPressed); |
3847 | + touchPoints[0].setRect(QRect(10, 10, 0, 0)); |
3848 | + QTouchEvent touchEvent(QEvent::TouchBegin, |
3849 | + 0 /* device */, |
3850 | + Qt::NoModifier, |
3851 | + Qt::TouchPointPressed, |
3852 | + touchPoints); |
3853 | + touchRegistry.update(&touchEvent); |
3854 | + } |
3855 | + |
3856 | + touchRegistry.addCandidateOwnerForTouch(0, &candidate0); |
3857 | + |
3858 | + { |
3859 | + QList<QTouchEvent::TouchPoint> touchPoints; |
3860 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
3861 | + touchPoints[0].setState(Qt::TouchPointMoved); |
3862 | + touchPoints[0].setRect(QRect(11, 11, 0, 0)); |
3863 | + touchPoints.append(QTouchEvent::TouchPoint(1)); |
3864 | + touchPoints[1].setState(Qt::TouchPointPressed); |
3865 | + touchPoints[1].setRect(QRect(20, 20, 0, 0)); |
3866 | + QTouchEvent touchEvent(QEvent::TouchUpdate, |
3867 | + 0 /* device */, |
3868 | + Qt::NoModifier, |
3869 | + Qt::TouchPointPressed | Qt::TouchPointMoved, |
3870 | + touchPoints); |
3871 | + touchRegistry.update(&touchEvent); |
3872 | + } |
3873 | + |
3874 | + // candidate0 should have received an update on the touch he's interested on. |
3875 | + QCOMPARE(candidate0.unownedTouchEvents.count(), 1); |
3876 | + // make sure only touch 0 is there (i.e. no mention of touch 1) |
3877 | + QCOMPARE(candidate0.unownedTouchEvents[0].touchPoints.count(), 1); |
3878 | + QCOMPARE(candidate0.unownedTouchEvents[0].touchPoints[0].id(), 0); |
3879 | + // Check that the points local coordinates have been mapped to candidate0's coordinate system. |
3880 | + QCOMPARE(candidate0.unownedTouchEvents[0].touchPoints[0].rect().x(), 11 - candidate0.x()); |
3881 | + QCOMPARE(candidate0.unownedTouchEvents[0].touchPoints[0].rect().y(), 11 - candidate0.y()); |
3882 | + |
3883 | + touchRegistry.addCandidateOwnerForTouch(1, &candidate1); |
3884 | + |
3885 | + { |
3886 | + QList<QTouchEvent::TouchPoint> touchPoints; |
3887 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
3888 | + touchPoints[0].setState(Qt::TouchPointMoved); |
3889 | + touchPoints[0].setRect(QRect(12, 12, 0, 0)); |
3890 | + touchPoints.append(QTouchEvent::TouchPoint(1)); |
3891 | + touchPoints[1].setState(Qt::TouchPointMoved); |
3892 | + touchPoints[1].setRect(QRect(21, 21, 0, 0)); |
3893 | + QTouchEvent touchEvent(QEvent::TouchUpdate, |
3894 | + 0 /* device */, |
3895 | + Qt::NoModifier, |
3896 | + Qt::TouchPointPressed | Qt::TouchPointMoved, |
3897 | + touchPoints); |
3898 | + touchRegistry.update(&touchEvent); |
3899 | + } |
3900 | + |
3901 | + // candidate0 gets updates only for touch 0 and |
3902 | + // candidate1 gets updates only for touch 1 |
3903 | + |
3904 | + QCOMPARE(candidate0.unownedTouchEvents.count(), 2); |
3905 | + QCOMPARE(candidate0.unownedTouchEvents[1].touchPoints.count(), 1); |
3906 | + QCOMPARE(candidate0.unownedTouchEvents[1].touchPoints[0].id(), 0); |
3907 | + |
3908 | + QCOMPARE(candidate1.unownedTouchEvents.count(), 1); |
3909 | + QCOMPARE(candidate1.unownedTouchEvents[0].touchPoints.count(), 1); |
3910 | + QCOMPARE(candidate1.unownedTouchEvents[0].touchPoints[0].id(), 1); |
3911 | +} |
3912 | + |
3913 | +void tst_TouchRegistry::dispatchesTouchEventsToWatchers() |
3914 | +{ |
3915 | + TouchRegistry touchRegistry; |
3916 | + QQuickItem rootItem; |
3917 | + |
3918 | + DummyCandidate watcher; |
3919 | + watcher.setObjectName("watcher"); |
3920 | + watcher.setParentItem(&rootItem); |
3921 | + watcher.setX(1); |
3922 | + watcher.setY(2); |
3923 | + |
3924 | + { |
3925 | + QList<QTouchEvent::TouchPoint> touchPoints; |
3926 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
3927 | + touchPoints[0].setState(Qt::TouchPointPressed); |
3928 | + touchPoints[0].setRect(QRect(10, 10, 0, 0)); |
3929 | + QTouchEvent touchEvent(QEvent::TouchBegin, |
3930 | + 0 /* device */, |
3931 | + Qt::NoModifier, |
3932 | + Qt::TouchPointPressed, |
3933 | + touchPoints); |
3934 | + touchRegistry.update(&touchEvent); |
3935 | + } |
3936 | + |
3937 | + touchRegistry.addTouchWatcher(0, &watcher); |
3938 | + |
3939 | + { |
3940 | + QList<QTouchEvent::TouchPoint> touchPoints; |
3941 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
3942 | + touchPoints[0].setState(Qt::TouchPointMoved); |
3943 | + touchPoints[0].setRect(QRect(11, 11, 0, 0)); |
3944 | + touchPoints.append(QTouchEvent::TouchPoint(1)); |
3945 | + touchPoints[1].setState(Qt::TouchPointPressed); |
3946 | + touchPoints[1].setRect(QRect(20, 20, 0, 0)); |
3947 | + QTouchEvent touchEvent(QEvent::TouchUpdate, |
3948 | + 0 /* device */, |
3949 | + Qt::NoModifier, |
3950 | + Qt::TouchPointPressed | Qt::TouchPointMoved, |
3951 | + touchPoints); |
3952 | + touchRegistry.update(&touchEvent); |
3953 | + } |
3954 | + |
3955 | + // watcher should have received an update on the touch he's interested on. |
3956 | + QCOMPARE(watcher.unownedTouchEvents.count(), 1); |
3957 | + // make sure only touch 0 is there (i.e. no mention of touch 1) |
3958 | + QCOMPARE(watcher.unownedTouchEvents[0].touchPoints.count(), 1); |
3959 | + QCOMPARE(watcher.unownedTouchEvents[0].touchPoints[0].id(), 0); |
3960 | + // Check that the points local coordinates have been mapped to watcher's coordinate system. |
3961 | + QCOMPARE(watcher.unownedTouchEvents[0].touchPoints[0].rect().x(), 11 - watcher.x()); |
3962 | + QCOMPARE(watcher.unownedTouchEvents[0].touchPoints[0].rect().y(), 11 - watcher.y()); |
3963 | + |
3964 | + { |
3965 | + QList<QTouchEvent::TouchPoint> touchPoints; |
3966 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
3967 | + touchPoints[0].setState(Qt::TouchPointMoved); |
3968 | + touchPoints[0].setRect(QRect(12, 12, 0, 0)); |
3969 | + touchPoints.append(QTouchEvent::TouchPoint(1)); |
3970 | + touchPoints[1].setState(Qt::TouchPointMoved); |
3971 | + touchPoints[1].setRect(QRect(21, 21, 0, 0)); |
3972 | + QTouchEvent touchEvent(QEvent::TouchUpdate, |
3973 | + 0 /* device */, |
3974 | + Qt::NoModifier, |
3975 | + Qt::TouchPointPressed | Qt::TouchPointMoved, |
3976 | + touchPoints); |
3977 | + touchRegistry.update(&touchEvent); |
3978 | + } |
3979 | + |
3980 | + // watcher gets updates only for touch 0 |
3981 | + |
3982 | + QCOMPARE(watcher.unownedTouchEvents.count(), 2); |
3983 | + QCOMPARE(watcher.unownedTouchEvents[1].touchPoints.count(), 1); |
3984 | + QCOMPARE(watcher.unownedTouchEvents[1].touchPoints[0].id(), 0); |
3985 | +} |
3986 | + |
3987 | +void tst_TouchRegistry::keepDispatchingToWatchersAfterLastCandidateGivesUp() |
3988 | +{ |
3989 | + TouchRegistry touchRegistry; |
3990 | + DummyCandidate item; |
3991 | + |
3992 | + { |
3993 | + QList<QTouchEvent::TouchPoint> touchPoints; |
3994 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
3995 | + touchPoints[0].setState(Qt::TouchPointPressed); |
3996 | + QTouchEvent touchEvent(QEvent::TouchBegin, |
3997 | + 0 /* device */, |
3998 | + Qt::NoModifier, |
3999 | + Qt::TouchPointPressed, |
4000 | + touchPoints); |
4001 | + touchRegistry.update(&touchEvent); |
4002 | + } |
4003 | + |
4004 | + touchRegistry.addCandidateOwnerForTouch(0, &item); |
4005 | + |
4006 | + { |
4007 | + QList<QTouchEvent::TouchPoint> touchPoints; |
4008 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
4009 | + touchPoints[0].setState(Qt::TouchPointMoved); |
4010 | + touchPoints.append(QTouchEvent::TouchPoint(1)); |
4011 | + touchPoints[1].setState(Qt::TouchPointPressed); |
4012 | + QTouchEvent touchEvent(QEvent::TouchUpdate, |
4013 | + 0 /* device */, |
4014 | + Qt::NoModifier, |
4015 | + Qt::TouchPointPressed | Qt::TouchPointMoved, |
4016 | + touchPoints); |
4017 | + touchRegistry.update(&touchEvent); |
4018 | + } |
4019 | + |
4020 | + QCOMPARE(item.unownedTouchEvents.count(), 1); |
4021 | + QCOMPARE(item.unownedTouchEvents[0].touchPoints.count(), 1); |
4022 | + QCOMPARE(item.unownedTouchEvents[0].touchPoints[0].id(), 0); |
4023 | + item.unownedTouchEvents.clear(); |
4024 | + |
4025 | + touchRegistry.addTouchWatcher(1, &item); |
4026 | + |
4027 | + { |
4028 | + QList<QTouchEvent::TouchPoint> touchPoints; |
4029 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
4030 | + touchPoints[0].setState(Qt::TouchPointMoved); |
4031 | + touchPoints.append(QTouchEvent::TouchPoint(1)); |
4032 | + touchPoints[1].setState(Qt::TouchPointMoved); |
4033 | + QTouchEvent touchEvent(QEvent::TouchUpdate, |
4034 | + 0 /* device */, |
4035 | + Qt::NoModifier, |
4036 | + Qt::TouchPointMoved, |
4037 | + touchPoints); |
4038 | + touchRegistry.update(&touchEvent); |
4039 | + } |
4040 | + |
4041 | + QCOMPARE(item.unownedTouchEvents.count(), 1); |
4042 | + QCOMPARE(item.unownedTouchEvents[0].touchPoints.count(), 2); |
4043 | + QVERIFY(item.unownedTouchEvents[0].containsTouchWithId(0)); |
4044 | + QVERIFY(item.unownedTouchEvents[0].containsTouchWithId(1)); |
4045 | + item.unownedTouchEvents.clear(); |
4046 | + |
4047 | + { |
4048 | + QList<QTouchEvent::TouchPoint> touchPoints; |
4049 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
4050 | + touchPoints[0].setState(Qt::TouchPointReleased); |
4051 | + touchPoints.append(QTouchEvent::TouchPoint(1)); |
4052 | + touchPoints[1].setState(Qt::TouchPointMoved); |
4053 | + QTouchEvent touchEvent(QEvent::TouchUpdate, |
4054 | + 0 /* device */, |
4055 | + Qt::NoModifier, |
4056 | + Qt::TouchPointReleased | Qt::TouchPointMoved, |
4057 | + touchPoints); |
4058 | + touchRegistry.update(&touchEvent); |
4059 | + } |
4060 | + |
4061 | + QCOMPARE(item.unownedTouchEvents.count(), 1); |
4062 | + QCOMPARE(item.unownedTouchEvents[0].touchPoints.count(), 2); |
4063 | + QVERIFY(item.unownedTouchEvents[0].containsTouchWithId(0)); |
4064 | + QVERIFY(item.unownedTouchEvents[0].containsTouchWithId(1)); |
4065 | + item.unownedTouchEvents.clear(); |
4066 | + |
4067 | + touchRegistry.removeCandidateOwnerForTouch(0, &item); |
4068 | + |
4069 | + { |
4070 | + QList<QTouchEvent::TouchPoint> touchPoints; |
4071 | + touchPoints.append(QTouchEvent::TouchPoint(1)); |
4072 | + touchPoints[0].setState(Qt::TouchPointReleased); |
4073 | + QTouchEvent touchEvent(QEvent::TouchEnd, |
4074 | + 0 /* device */, |
4075 | + Qt::NoModifier, |
4076 | + Qt::TouchPointReleased, |
4077 | + touchPoints); |
4078 | + touchRegistry.update(&touchEvent); |
4079 | + } |
4080 | + |
4081 | + QCOMPARE(item.unownedTouchEvents.count(), 1); |
4082 | + QCOMPARE(item.unownedTouchEvents[0].touchPoints.count(), 1); |
4083 | + QVERIFY(item.unownedTouchEvents[0].containsTouchWithId(1)); |
4084 | + item.unownedTouchEvents.clear(); |
4085 | + |
4086 | + QVERIFY(touchRegistry.m_touchInfoPool.isEmpty()); |
4087 | +} |
4088 | + |
4089 | +/* |
4090 | + Regression test that reproduces a problematic scenario that came up during manual testing. |
4091 | + It reproduces the interaction between TouchRegistry and a DirectionalDragArea |
4092 | + */ |
4093 | +void tst_TouchRegistry::candidatesAndWatchers_1() |
4094 | +{ |
4095 | + TouchRegistry touchRegistry; |
4096 | + DummyCandidate item; |
4097 | + |
4098 | + { |
4099 | + QList<QTouchEvent::TouchPoint> touchPoints; |
4100 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
4101 | + touchPoints[0].setState(Qt::TouchPointPressed); |
4102 | + QTouchEvent touchEvent(QEvent::TouchBegin, |
4103 | + 0 /* device */, |
4104 | + Qt::NoModifier, |
4105 | + Qt::TouchPointPressed, |
4106 | + touchPoints); |
4107 | + touchRegistry.update(&touchEvent); |
4108 | + } |
4109 | + |
4110 | + touchRegistry.addCandidateOwnerForTouch(0, &item); |
4111 | + |
4112 | + { |
4113 | + QList<QTouchEvent::TouchPoint> touchPoints; |
4114 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
4115 | + touchPoints[0].setState(Qt::TouchPointMoved); |
4116 | + touchPoints.append(QTouchEvent::TouchPoint(1)); |
4117 | + touchPoints[1].setState(Qt::TouchPointPressed); |
4118 | + QTouchEvent touchEvent(QEvent::TouchUpdate, |
4119 | + 0 /* device */, |
4120 | + Qt::NoModifier, |
4121 | + Qt::TouchPointPressed | Qt::TouchPointMoved, |
4122 | + touchPoints); |
4123 | + touchRegistry.update(&touchEvent); |
4124 | + } |
4125 | + |
4126 | + touchRegistry.addTouchWatcher(1, &item); |
4127 | + |
4128 | + touchRegistry.removeCandidateOwnerForTouch(0, &item); |
4129 | + |
4130 | + touchRegistry.addTouchWatcher(0, &item); |
4131 | + |
4132 | + { |
4133 | + QList<QTouchEvent::TouchPoint> touchPoints; |
4134 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
4135 | + touchPoints[0].setState(Qt::TouchPointReleased); |
4136 | + touchPoints.append(QTouchEvent::TouchPoint(1)); |
4137 | + touchPoints[1].setState(Qt::TouchPointMoved); |
4138 | + QTouchEvent touchEvent(QEvent::TouchUpdate, |
4139 | + 0 /* device */, |
4140 | + Qt::NoModifier, |
4141 | + Qt::TouchPointReleased | Qt::TouchPointMoved, |
4142 | + touchPoints); |
4143 | + touchRegistry.update(&touchEvent); |
4144 | + } |
4145 | + |
4146 | + { |
4147 | + QList<QTouchEvent::TouchPoint> touchPoints; |
4148 | + touchPoints.append(QTouchEvent::TouchPoint(1)); |
4149 | + touchPoints[0].setState(Qt::TouchPointReleased); |
4150 | + QTouchEvent touchEvent(QEvent::TouchEnd, |
4151 | + 0 /* device */, |
4152 | + Qt::NoModifier, |
4153 | + Qt::TouchPointReleased, |
4154 | + touchPoints); |
4155 | + touchRegistry.update(&touchEvent); |
4156 | + } |
4157 | + |
4158 | + QVERIFY(touchRegistry.m_touchInfoPool.isEmpty()); |
4159 | + |
4160 | + item.unownedTouchEvents.clear(); |
4161 | + |
4162 | + { |
4163 | + QList<QTouchEvent::TouchPoint> touchPoints; |
4164 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
4165 | + touchPoints[0].setState(Qt::TouchPointPressed); |
4166 | + QTouchEvent touchEvent(QEvent::TouchBegin, |
4167 | + 0 /* device */, |
4168 | + Qt::NoModifier, |
4169 | + Qt::TouchPointPressed, |
4170 | + touchPoints); |
4171 | + touchRegistry.update(&touchEvent); |
4172 | + } |
4173 | + |
4174 | + // Haven't made any subscription for that new touch 0. |
4175 | + QCOMPARE(item.unownedTouchEvents.count(), 0); |
4176 | +} |
4177 | + |
4178 | +/* |
4179 | + Regression test that reproduces a problematic scenario that came up during manual testing. |
4180 | + It reproduces the interaction between TouchRegistry, DirectionalDragArea and a TouchGate. |
4181 | + */ |
4182 | +void tst_TouchRegistry::candidatesAndWatchers_2() |
4183 | +{ |
4184 | + TouchRegistry touchRegistry; |
4185 | + |
4186 | + DummyCandidate directionalDragArea; |
4187 | + directionalDragArea.setObjectName("DirectionalDragArea"); |
4188 | + |
4189 | + DummyCandidate touchGate; |
4190 | + touchGate.setObjectName("TouchGate"); |
4191 | + |
4192 | + // [DDA] 1298 TouchBegin (id:0, state:pressed, scenePos:(147,1023)) |
4193 | + { |
4194 | + QList<QTouchEvent::TouchPoint> touchPoints; |
4195 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
4196 | + touchPoints[0].setState(Qt::TouchPointPressed); |
4197 | + QTouchEvent touchEvent(QEvent::TouchBegin, |
4198 | + 0 /* device */, |
4199 | + Qt::NoModifier, |
4200 | + Qt::TouchPointPressed, |
4201 | + touchPoints); |
4202 | + touchRegistry.update(&touchEvent); |
4203 | + } |
4204 | + |
4205 | + // [TouchRegistry] addCandidateOwnerForTouch id 0 candidate DirectionalDragArea |
4206 | + touchRegistry.addCandidateOwnerForTouch(0, &directionalDragArea); |
4207 | + |
4208 | + // [TouchRegistry] requestTouchOwnership id 0 candidate TouchGate |
4209 | + touchRegistry.requestTouchOwnership(0, &touchGate); |
4210 | + |
4211 | + //[TouchRegistry] got TouchUpdate (id:0, state:moved, scenePos:(147,1023)) (id:1, state:pressed, scenePos:(327,792)) |
4212 | + { |
4213 | + QList<QTouchEvent::TouchPoint> touchPoints; |
4214 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
4215 | + touchPoints[0].setState(Qt::TouchPointMoved); |
4216 | + touchPoints.append(QTouchEvent::TouchPoint(1)); |
4217 | + touchPoints[1].setState(Qt::TouchPointPressed); |
4218 | + QTouchEvent touchEvent(QEvent::TouchUpdate, |
4219 | + 0 /* device */, |
4220 | + Qt::NoModifier, |
4221 | + Qt::TouchPointPressed | Qt::TouchPointMoved, |
4222 | + touchPoints); |
4223 | + touchRegistry.update(&touchEvent); |
4224 | + } |
4225 | + |
4226 | + // [TouchRegistry] addTouchWatcher id 1 watcher DirectionalDragArea |
4227 | + touchRegistry.addTouchWatcher(1, &directionalDragArea); |
4228 | + |
4229 | + // [TouchRegistry] removeCandidateOwnerForTouch id 0 candidate DirectionalDragArea |
4230 | + touchRegistry.removeCandidateOwnerForTouch(0, &directionalDragArea); |
4231 | + |
4232 | + //[TouchRegistry] sending TouchWonershipEvent(id = 0 gained) to candidate TouchGate |
4233 | + QCOMPARE(touchGate.ownedTouches.count(), 1); |
4234 | + QVERIFY(touchGate.ownedTouches.contains(0)); |
4235 | + |
4236 | + // [TouchRegistry] addTouchWatcher id 0 watcher DirectionalDragArea |
4237 | + touchRegistry.addTouchWatcher(0, &directionalDragArea); |
4238 | + |
4239 | + // [TouchRegistry] requestTouchOwnership id 1 candidate TouchGate |
4240 | + touchRegistry.requestTouchOwnership(1, &touchGate); |
4241 | + |
4242 | + //[TouchRegistry] sending TouchWonershipEvent(id = 1 gained) to candidate TouchGate |
4243 | + QCOMPARE(touchGate.ownedTouches.count(), 2); |
4244 | + QVERIFY(touchGate.ownedTouches.contains(1)); |
4245 | + |
4246 | + directionalDragArea.unownedTouchEvents.clear(); |
4247 | + touchGate.unownedTouchEvents.clear(); |
4248 | + |
4249 | + //[TouchRegistry] got TouchUpdate (id:0, state:moved, scenePos:(148,1025)) (id:1, state:moved, scenePos:(329,795)) |
4250 | + { |
4251 | + QList<QTouchEvent::TouchPoint> touchPoints; |
4252 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
4253 | + touchPoints[0].setState(Qt::TouchPointMoved); |
4254 | + touchPoints.append(QTouchEvent::TouchPoint(1)); |
4255 | + touchPoints[1].setState(Qt::TouchPointMoved); |
4256 | + QTouchEvent touchEvent(QEvent::TouchUpdate, |
4257 | + 0 /* device */, |
4258 | + Qt::NoModifier, |
4259 | + Qt::TouchPointMoved, |
4260 | + touchPoints); |
4261 | + touchRegistry.update(&touchEvent); |
4262 | + } |
4263 | + |
4264 | + //vvvvv DDA Watchers are being ignored from now on vvvvvvv |
4265 | + |
4266 | + QCOMPARE(directionalDragArea.unownedTouchEvents.count(), 1); |
4267 | + QCOMPARE(directionalDragArea.unownedTouchEvents.first().touchPoints.count(), 2); |
4268 | + QVERIFY(directionalDragArea.unownedTouchEvents.first().containsTouchWithId(0)); |
4269 | + QVERIFY(directionalDragArea.unownedTouchEvents.first().containsTouchWithId(1)); |
4270 | + |
4271 | + QVERIFY(touchGate.unownedTouchEvents.isEmpty()); |
4272 | + |
4273 | + directionalDragArea.unownedTouchEvents.clear(); |
4274 | + |
4275 | + //[TouchRegistry] got TouchUpdate (id:0, state:moved, scenePos:(151,1029)) (id:1, state:released, scenePos:(329,795)) |
4276 | + { |
4277 | + QList<QTouchEvent::TouchPoint> touchPoints; |
4278 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
4279 | + touchPoints[0].setState(Qt::TouchPointMoved); |
4280 | + touchPoints.append(QTouchEvent::TouchPoint(1)); |
4281 | + touchPoints[1].setState(Qt::TouchPointReleased); |
4282 | + QTouchEvent touchEvent(QEvent::TouchUpdate, |
4283 | + 0 /* device */, |
4284 | + Qt::NoModifier, |
4285 | + Qt::TouchPointMoved | Qt::TouchPointReleased, |
4286 | + touchPoints); |
4287 | + touchRegistry.update(&touchEvent); |
4288 | + } |
4289 | + |
4290 | + QCOMPARE(directionalDragArea.unownedTouchEvents.count(), 1); |
4291 | + QCOMPARE(directionalDragArea.unownedTouchEvents.first().touchPoints.count(), 2); |
4292 | + QVERIFY(directionalDragArea.unownedTouchEvents.first().containsTouchWithId(0)); |
4293 | + QVERIFY(directionalDragArea.unownedTouchEvents.first().containsTouchWithId(1)); |
4294 | + |
4295 | + QVERIFY(touchGate.unownedTouchEvents.isEmpty()); |
4296 | + |
4297 | + directionalDragArea.unownedTouchEvents.clear(); |
4298 | + |
4299 | + //[TouchRegistry] got TouchEnd (id:0, state:released, scenePos:(157,1034)) |
4300 | + { |
4301 | + QList<QTouchEvent::TouchPoint> touchPoints; |
4302 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
4303 | + touchPoints[0].setState(Qt::TouchPointReleased); |
4304 | + QTouchEvent touchEvent(QEvent::TouchEnd, |
4305 | + 0 /* device */, |
4306 | + Qt::NoModifier, |
4307 | + Qt::TouchPointReleased, |
4308 | + touchPoints); |
4309 | + touchRegistry.update(&touchEvent); |
4310 | + } |
4311 | + |
4312 | + QCOMPARE(directionalDragArea.unownedTouchEvents.count(), 1); |
4313 | + QCOMPARE(directionalDragArea.unownedTouchEvents.first().touchPoints.count(), 1); |
4314 | + QVERIFY(directionalDragArea.unownedTouchEvents.first().containsTouchWithId(0)); |
4315 | + |
4316 | + QVERIFY(touchGate.unownedTouchEvents.isEmpty()); |
4317 | +} |
4318 | + |
4319 | +void tst_TouchRegistry::rejectingTouchfterItsEnd() |
4320 | +{ |
4321 | + TouchRegistry touchRegistry; |
4322 | + DummyCandidate earlyCandidate; |
4323 | + DummyCandidate lateCandidate; |
4324 | + |
4325 | + { |
4326 | + QList<QTouchEvent::TouchPoint> touchPoints; |
4327 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
4328 | + touchPoints[0].setState(Qt::TouchPointPressed); |
4329 | + QTouchEvent touchEvent(QEvent::TouchBegin, |
4330 | + 0 /* device */, |
4331 | + Qt::NoModifier, |
4332 | + Qt::TouchPointPressed, |
4333 | + touchPoints); |
4334 | + touchRegistry.update(&touchEvent); |
4335 | + } |
4336 | + |
4337 | + touchRegistry.addCandidateOwnerForTouch(0, &earlyCandidate); |
4338 | + touchRegistry.addCandidateOwnerForTouch(0, &lateCandidate); |
4339 | + |
4340 | + QVERIFY(earlyCandidate.ownedTouches.isEmpty()); |
4341 | + QVERIFY(lateCandidate.ownedTouches.isEmpty()); |
4342 | + |
4343 | + touchRegistry.requestTouchOwnership(0, &lateCandidate); |
4344 | + |
4345 | + QVERIFY(earlyCandidate.ownedTouches.isEmpty()); |
4346 | + QVERIFY(lateCandidate.ownedTouches.isEmpty()); |
4347 | + |
4348 | + { |
4349 | + QList<QTouchEvent::TouchPoint> touchPoints; |
4350 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
4351 | + touchPoints[0].setState(Qt::TouchPointReleased); |
4352 | + QTouchEvent touchEvent(QEvent::TouchEnd, |
4353 | + 0 /* device */, |
4354 | + Qt::NoModifier, |
4355 | + Qt::TouchPointPressed, |
4356 | + touchPoints); |
4357 | + touchRegistry.update(&touchEvent); |
4358 | + } |
4359 | + |
4360 | + QVERIFY(earlyCandidate.ownedTouches.isEmpty()); |
4361 | + QVERIFY(lateCandidate.ownedTouches.isEmpty()); |
4362 | + |
4363 | + touchRegistry.removeCandidateOwnerForTouch(0, &earlyCandidate); |
4364 | + |
4365 | + QCOMPARE(lateCandidate.ownedTouches.count(), 1); |
4366 | + QCOMPARE(lateCandidate.ownedTouches.contains(0), 1); |
4367 | + |
4368 | + // Check that there's no trace left of touch 0 as we no longer need to keep tabs on it. |
4369 | + QVERIFY(!touchRegistry.findTouchInfo(0)); |
4370 | +} |
4371 | + |
4372 | +void tst_TouchRegistry::removeOldUndecidedCandidates() |
4373 | +{ |
4374 | + FakeTimerFactory *fakeTimerFactory = new FakeTimerFactory; |
4375 | + TouchRegistry *touchRegistry = new TouchRegistry(nullptr, fakeTimerFactory); |
4376 | + |
4377 | + DummyCandidate undecidedCandidate; |
4378 | + undecidedCandidate.setObjectName("undecided"); |
4379 | + |
4380 | + DummyCandidate candidateThatWantsTouch; |
4381 | + candidateThatWantsTouch.setObjectName("wantsTouch"); |
4382 | + |
4383 | + { |
4384 | + QList<QTouchEvent::TouchPoint> touchPoints; |
4385 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
4386 | + touchPoints[0].setState(Qt::TouchPointPressed); |
4387 | + QTouchEvent touchEvent(QEvent::TouchBegin, |
4388 | + 0 /* device */, |
4389 | + Qt::NoModifier, |
4390 | + Qt::TouchPointPressed, |
4391 | + touchPoints); |
4392 | + touchRegistry->update(&touchEvent); |
4393 | + } |
4394 | + |
4395 | + touchRegistry->addCandidateOwnerForTouch(0, &undecidedCandidate); |
4396 | + |
4397 | + touchRegistry->requestTouchOwnership(0, &candidateThatWantsTouch); |
4398 | + |
4399 | + QVERIFY(undecidedCandidate.ownedTouches.isEmpty()); |
4400 | + QVERIFY(undecidedCandidate.lostTouches.isEmpty()); |
4401 | + QVERIFY(candidateThatWantsTouch.ownedTouches.isEmpty()); |
4402 | + QVERIFY(candidateThatWantsTouch.lostTouches.isEmpty()); |
4403 | + |
4404 | + // Simulate that enough time has passed to cause the CandidateInactivityTimer to timeout, |
4405 | + // making TouchRegistry consider that undecidedCantidate defaulted. |
4406 | + fakeTimerFactory->makeRunningTimersTimeout(); |
4407 | + |
4408 | + QVERIFY(undecidedCandidate.ownedTouches.isEmpty()); |
4409 | + QVERIFY(undecidedCandidate.lostTouches.contains(0)); |
4410 | + QVERIFY(candidateThatWantsTouch.ownedTouches.contains(0)); |
4411 | + QVERIFY(candidateThatWantsTouch.lostTouches.isEmpty()); |
4412 | + |
4413 | + delete touchRegistry; |
4414 | +} |
4415 | + |
4416 | +////////////// TouchMemento ////////// |
4417 | + |
4418 | +TouchMemento::TouchMemento(const QTouchEvent *touchEvent) |
4419 | + : touchPointStates(touchEvent->touchPointStates()), touchPoints(touchEvent->touchPoints()) |
4420 | +{ |
4421 | +} |
4422 | + |
4423 | +bool TouchMemento::containsTouchWithId(int touchId) const |
4424 | +{ |
4425 | + for (int i = 0; i < touchPoints.count(); ++i) { |
4426 | + if (touchPoints.at(i).id() == touchId) { |
4427 | + return true; |
4428 | + } |
4429 | + } |
4430 | + return false; |
4431 | +} |
4432 | + |
4433 | +////////////// DummyCandidate ////////// |
4434 | + |
4435 | +bool DummyCandidate::event(QEvent *e) |
4436 | +{ |
4437 | + if (e->type() == TouchOwnershipEvent::touchOwnershipEventType()) { |
4438 | + TouchOwnershipEvent *touchOwnershipEvent = static_cast<TouchOwnershipEvent *>(e); |
4439 | + |
4440 | + // NB: Cannot use QVERIFY here because the macro doesn't return a boolean and is |
4441 | + // meant for use only directly in the body of a test function |
4442 | + if (ownedTouches.contains(touchOwnershipEvent->touchId())) |
4443 | + qFatal("Sent ownership event for a touch that is already owned."); |
4444 | + if (lostTouches.contains(touchOwnershipEvent->touchId())) |
4445 | + qFatal("Sent ownership event for a touch that has already been lost."); |
4446 | + |
4447 | + if (touchOwnershipEvent->gained()) { |
4448 | + ownedTouches.insert(touchOwnershipEvent->touchId()); |
4449 | + } else { |
4450 | + lostTouches.insert(touchOwnershipEvent->touchId()); |
4451 | + } |
4452 | + return true; |
4453 | + } else if (e->type() == UnownedTouchEvent::unownedTouchEventType()) { |
4454 | + UnownedTouchEvent *unownedTouchEvent = static_cast<UnownedTouchEvent *>(e); |
4455 | + unownedTouchEvents.append(TouchMemento(unownedTouchEvent->touchEvent())); |
4456 | + return true; |
4457 | + } else { |
4458 | + return QObject::event(e); |
4459 | + } |
4460 | +} |
4461 | + |
4462 | +QTEST_GUILESS_MAIN(tst_TouchRegistry) |
4463 | + |
4464 | +#include "tst_TouchRegistry.moc" |
4465 | |
4466 | === modified file 'tests/mocks/Unity/Application/CMakeLists.txt' |
4467 | --- tests/mocks/Unity/Application/CMakeLists.txt 2014-09-29 09:43:18 +0000 |
4468 | +++ tests/mocks/Unity/Application/CMakeLists.txt 2014-10-10 15:05:15 +0000 |
4469 | @@ -13,6 +13,7 @@ |
4470 | SessionManager.cpp |
4471 | SurfaceManager.cpp |
4472 | SessionModel.h |
4473 | + UbuntuKeyboardInfo.cpp |
4474 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h |
4475 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h |
4476 | ) |
4477 | |
4478 | === modified file 'tests/mocks/Unity/Application/MirSurfaceItem.cpp' |
4479 | --- tests/mocks/Unity/Application/MirSurfaceItem.cpp 2014-09-04 10:03:10 +0000 |
4480 | +++ tests/mocks/Unity/Application/MirSurfaceItem.cpp 2014-10-10 15:05:15 +0000 |
4481 | @@ -40,6 +40,8 @@ |
4482 | , m_state(state) |
4483 | , m_live(true) |
4484 | , m_orientation(Qt::PortraitOrientation) |
4485 | + , m_touchPressCount(0) |
4486 | + , m_touchReleaseCount(0) |
4487 | , m_qmlItem(nullptr) |
4488 | , m_screenshotUrl(screenshot) |
4489 | { |
4490 | @@ -144,24 +146,12 @@ |
4491 | } |
4492 | } |
4493 | |
4494 | -void MirSurfaceItem::onQmlWantInputMethodChanged() |
4495 | -{ |
4496 | - QQmlProperty prop(m_qmlItem, "wantInputMethod"); |
4497 | - bool wantInputMethod = prop.read().toBool(); |
4498 | - |
4499 | - if (hasFocus() && wantInputMethod) { |
4500 | - Q_EMIT inputMethodRequested(); |
4501 | - } |
4502 | -} |
4503 | - |
4504 | void MirSurfaceItem::onFocusChanged() |
4505 | { |
4506 | - QQmlProperty prop(m_qmlItem, "wantInputMethod"); |
4507 | - bool wantInputMethod = prop.read().toBool(); |
4508 | - |
4509 | - if (!hasFocus() && wantInputMethod) { |
4510 | - Q_EMIT inputMethodDismissed(); |
4511 | - prop.write(QVariant::fromValue(false)); |
4512 | + if (!hasFocus()) { |
4513 | + // Causes a crash in tst_Shell.qml, inside the mock Unity.Application itself. |
4514 | + // Didn't have time to debug yet. |
4515 | + //Q_EMIT inputMethodDismissed(); |
4516 | } |
4517 | } |
4518 | |
4519 | @@ -194,12 +184,18 @@ |
4520 | QQmlProperty screenshotSource(m_qmlItem, "screenshotSource"); |
4521 | screenshotSource.write(QVariant::fromValue(m_screenshotUrl)); |
4522 | } |
4523 | +} |
4524 | |
4525 | - { |
4526 | - QQmlProperty prop(m_qmlItem, "wantInputMethod"); |
4527 | - if (prop.type() == QQmlProperty::Property) { |
4528 | - bool ok = prop.connectNotifySignal(this, SLOT(onQmlWantInputMethodChanged())); |
4529 | - if (!ok) qCritical("MirSurfaceItem: failed to connect to wantInputMethod notify signal"); |
4530 | - } |
4531 | +void MirSurfaceItem::touchEvent(QTouchEvent * event) |
4532 | +{ |
4533 | + if (event->touchPointStates() & Qt::TouchPointPressed) { |
4534 | + ++m_touchPressCount; |
4535 | + Q_EMIT touchPressCountChanged(m_touchPressCount); |
4536 | + // Causes a crash in tst_Shell.qml, inside the mock Unity.Application itself. |
4537 | + // Didn't have time to debug yet. |
4538 | + // Q_EMIT inputMethodRequested(); |
4539 | + } else if (event->touchPointStates() & Qt::TouchPointReleased) { |
4540 | + ++m_touchReleaseCount; |
4541 | + Q_EMIT touchReleaseCountChanged(m_touchReleaseCount); |
4542 | } |
4543 | } |
4544 | |
4545 | === modified file 'tests/mocks/Unity/Application/MirSurfaceItem.h' |
4546 | --- tests/mocks/Unity/Application/MirSurfaceItem.h 2014-09-04 10:03:10 +0000 |
4547 | +++ tests/mocks/Unity/Application/MirSurfaceItem.h 2014-10-10 15:05:15 +0000 |
4548 | @@ -36,6 +36,11 @@ |
4549 | Q_PROPERTY(bool live READ live NOTIFY liveChanged) |
4550 | Q_PROPERTY(Qt::ScreenOrientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged DESIGNABLE false) |
4551 | |
4552 | + Q_PROPERTY(int touchPressCount READ touchPressCount WRITE setTouchPressCount NOTIFY touchPressCountChanged |
4553 | + DESIGNABLE false) |
4554 | + Q_PROPERTY(int touchReleaseCount READ touchReleaseCount WRITE setTouchReleaseCount NOTIFY touchReleaseCountChanged |
4555 | + DESIGNABLE false) |
4556 | + |
4557 | public: |
4558 | enum Type { |
4559 | Normal, |
4560 | @@ -73,6 +78,12 @@ |
4561 | void setScreenshot(const QUrl& screenshot); |
4562 | void setLive(bool live); |
4563 | |
4564 | + int touchPressCount() const { return m_touchPressCount; } |
4565 | + void setTouchPressCount(int count) { m_touchPressCount = count; Q_EMIT touchPressCountChanged(count); } |
4566 | + |
4567 | + int touchReleaseCount() const { return m_touchReleaseCount; } |
4568 | + void setTouchReleaseCount(int count) { m_touchReleaseCount = count; Q_EMIT touchReleaseCountChanged(count); } |
4569 | + |
4570 | Q_INVOKABLE void setState(State newState); |
4571 | Q_INVOKABLE void release(); |
4572 | |
4573 | @@ -81,6 +92,8 @@ |
4574 | void stateChanged(State); |
4575 | void liveChanged(bool live); |
4576 | void orientationChanged(); |
4577 | + void touchPressCountChanged(int count); |
4578 | + void touchReleaseCountChanged(int count); |
4579 | |
4580 | void inputMethodRequested(); |
4581 | void inputMethodDismissed(); |
4582 | @@ -88,10 +101,12 @@ |
4583 | // internal mock use |
4584 | void deregister(); |
4585 | |
4586 | +protected: |
4587 | + void touchEvent(QTouchEvent * event) override; |
4588 | + |
4589 | private Q_SLOTS: |
4590 | void onFocusChanged(); |
4591 | void onComponentStatusChanged(QQmlComponent::Status status); |
4592 | - void onQmlWantInputMethodChanged(); |
4593 | |
4594 | private: |
4595 | explicit MirSurfaceItem(const QString& name, |
4596 | @@ -110,6 +125,8 @@ |
4597 | State m_state; |
4598 | bool m_live; |
4599 | Qt::ScreenOrientation m_orientation; |
4600 | + int m_touchPressCount; |
4601 | + int m_touchReleaseCount; |
4602 | |
4603 | QQmlComponent *m_qmlContentComponent; |
4604 | QQuickItem *m_qmlItem; |
4605 | |
4606 | === modified file 'tests/mocks/Unity/Application/MirSurfaceItem.qml' |
4607 | --- tests/mocks/Unity/Application/MirSurfaceItem.qml 2014-09-10 17:10:48 +0000 |
4608 | +++ tests/mocks/Unity/Application/MirSurfaceItem.qml 2014-10-10 15:05:15 +0000 |
4609 | @@ -17,7 +17,6 @@ |
4610 | import QtQuick 2.0 |
4611 | |
4612 | Rectangle { |
4613 | - objectName: "fakeSurfaceQML" |
4614 | id: root |
4615 | color: "pink" |
4616 | |
4617 | @@ -38,11 +37,6 @@ |
4618 | property alias screenshotSource: screenshotImage.source |
4619 | property int orientation: Qt.PortraitOrientation |
4620 | |
4621 | - property bool wantInputMethod: false |
4622 | - |
4623 | - property int touchPressCount: 0 |
4624 | - property int touchReleaseCount: 0 |
4625 | - |
4626 | Image { |
4627 | id: screenshotImage |
4628 | anchors.fill: parent |
4629 | @@ -58,10 +52,4 @@ |
4630 | minimumPixelSize: 10; font.pixelSize: 200 |
4631 | verticalAlignment: Text.AlignVCenter |
4632 | } |
4633 | - |
4634 | - MultiPointTouchArea { |
4635 | - anchors.fill: parent |
4636 | - onPressed: { root.wantInputMethod = true; root.touchPressCount++; } |
4637 | - onReleased: { root.touchReleaseCount++; } |
4638 | - } |
4639 | } |
4640 | |
4641 | === added file 'tests/mocks/Unity/Application/UbuntuKeyboardInfo.cpp' |
4642 | --- tests/mocks/Unity/Application/UbuntuKeyboardInfo.cpp 1970-01-01 00:00:00 +0000 |
4643 | +++ tests/mocks/Unity/Application/UbuntuKeyboardInfo.cpp 2014-10-10 15:05:15 +0000 |
4644 | @@ -0,0 +1,28 @@ |
4645 | +/* |
4646 | + * Copyright (C) 2014 Canonical, Ltd. |
4647 | + * |
4648 | + * This program is free software: you can redistribute it and/or modify it under |
4649 | + * the terms of the GNU Lesser General Public License version 3, as published by |
4650 | + * the Free Software Foundation. |
4651 | + * |
4652 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
4653 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
4654 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
4655 | + * Lesser General Public License for more details. |
4656 | + * |
4657 | + * You should have received a copy of the GNU Lesser General Public License |
4658 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4659 | + */ |
4660 | + |
4661 | +#include "UbuntuKeyboardInfo.h" |
4662 | + |
4663 | +UbuntuKeyboardInfo *UbuntuKeyboardInfo::m_instance = nullptr; |
4664 | + |
4665 | +UbuntuKeyboardInfo::UbuntuKeyboardInfo(QObject *parent) |
4666 | + : QObject(parent), |
4667 | + m_x(0), |
4668 | + m_y(0), |
4669 | + m_width(200), |
4670 | + m_height(200) |
4671 | +{ |
4672 | +} |
4673 | |
4674 | === added file 'tests/mocks/Unity/Application/UbuntuKeyboardInfo.h' |
4675 | --- tests/mocks/Unity/Application/UbuntuKeyboardInfo.h 1970-01-01 00:00:00 +0000 |
4676 | +++ tests/mocks/Unity/Application/UbuntuKeyboardInfo.h 2014-10-10 15:05:15 +0000 |
4677 | @@ -0,0 +1,60 @@ |
4678 | +/* |
4679 | + * Copyright (C) 2014 Canonical, Ltd. |
4680 | + * |
4681 | + * This program is free software: you can redistribute it and/or modify it under |
4682 | + * the terms of the GNU Lesser General Public License version 3, as published by |
4683 | + * the Free Software Foundation. |
4684 | + * |
4685 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
4686 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
4687 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
4688 | + * Lesser General Public License for more details. |
4689 | + * |
4690 | + * You should have received a copy of the GNU Lesser General Public License |
4691 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4692 | + */ |
4693 | + |
4694 | +#ifndef UBUNTU_KEYBOARD_INFO_H |
4695 | +#define UBUNTU_KEYBOARD_INFO_H |
4696 | + |
4697 | +#include <QLocalSocket> |
4698 | +#include <QTimer> |
4699 | + |
4700 | +class UbuntuKeyboardInfo : public QObject { |
4701 | + Q_OBJECT |
4702 | + Q_PROPERTY(qreal x READ x NOTIFY xChanged) |
4703 | + Q_PROPERTY(qreal y READ y NOTIFY yChanged) |
4704 | + Q_PROPERTY(qreal width READ width NOTIFY widthChanged) |
4705 | + Q_PROPERTY(qreal height READ height NOTIFY heightChanged) |
4706 | +public: |
4707 | + UbuntuKeyboardInfo(QObject *parent = 0); |
4708 | + virtual ~UbuntuKeyboardInfo() {} |
4709 | + qreal x() const { return m_x; } |
4710 | + qreal y() const { return m_y; } |
4711 | + qreal width() const { return m_width; } |
4712 | + qreal height() const { return m_height; } |
4713 | + |
4714 | + static UbuntuKeyboardInfo *singleton() { |
4715 | + if (!m_instance) { |
4716 | + m_instance = new UbuntuKeyboardInfo; |
4717 | + } |
4718 | + return m_instance; |
4719 | + } |
4720 | + |
4721 | +Q_SIGNALS: |
4722 | + void xChanged(qreal x); |
4723 | + void yChanged(qreal y); |
4724 | + void widthChanged(qreal width); |
4725 | + void heightChanged(qreal height); |
4726 | + |
4727 | +private: |
4728 | + QLocalSocket m_socket; |
4729 | + qint32 m_x; |
4730 | + qint32 m_y; |
4731 | + qint32 m_width; |
4732 | + qint32 m_height; |
4733 | + |
4734 | + static UbuntuKeyboardInfo *m_instance; |
4735 | +}; |
4736 | + |
4737 | +#endif // UBUNTU_KEYBOARD_INFO_H |
4738 | |
4739 | === modified file 'tests/mocks/Unity/Application/plugin.cpp' |
4740 | --- tests/mocks/Unity/Application/plugin.cpp 2014-09-01 16:07:55 +0000 |
4741 | +++ tests/mocks/Unity/Application/plugin.cpp 2014-10-10 15:05:15 +0000 |
4742 | @@ -24,6 +24,7 @@ |
4743 | #include "SurfaceManager.h" |
4744 | #include "SessionManager.h" |
4745 | #include "ApplicationTestInterface.h" |
4746 | +#include "UbuntuKeyboardInfo.h" |
4747 | |
4748 | #include <qqml.h> |
4749 | #include <QQmlEngine> |
4750 | @@ -46,6 +47,11 @@ |
4751 | return SessionManager::singleton(); |
4752 | } |
4753 | |
4754 | +static QObject* ubuntuKeyboardInfoSingleton(QQmlEngine*, QJSEngine*) { |
4755 | + return UbuntuKeyboardInfo::singleton(); |
4756 | +} |
4757 | + |
4758 | + |
4759 | ApplicationTestInterface* s_appTestInterface = nullptr; |
4760 | |
4761 | static QObject* applicationTestInterface(QQmlEngine* engine, QJSEngine* scriptEngine) { |
4762 | @@ -76,6 +82,7 @@ |
4763 | qmlRegisterSingletonType<SurfaceManager>(uri, 0, 1, "SurfaceManager", surfaceManagerSingleton); |
4764 | qmlRegisterSingletonType<SessionManager>(uri, 0, 1, "SessionManager", sessionManagerSingleton); |
4765 | qmlRegisterSingletonType<ApplicationTestInterface>(uri, 0, 1, "ApplicationTest", applicationTestInterface); |
4766 | + qmlRegisterSingletonType<UbuntuKeyboardInfo>(uri, 0, 1, "UbuntuKeyboardInfo", ubuntuKeyboardInfoSingleton); |
4767 | |
4768 | qRegisterMetaType<MirSurfaceItem::Type>("MirSurfaceItem::Type"); |
4769 | qRegisterMetaType<MirSurfaceItem::State>("MirSurfaceItem::State"); |
4770 | |
4771 | === modified file 'tests/plugins/Ubuntu/Gestures/CMakeLists.txt' |
4772 | --- tests/plugins/Ubuntu/Gestures/CMakeLists.txt 2014-09-01 09:13:08 +0000 |
4773 | +++ tests/plugins/Ubuntu/Gestures/CMakeLists.txt 2014-10-10 15:05:15 +0000 |
4774 | @@ -1,7 +1,13 @@ |
4775 | include(QmlTest) |
4776 | |
4777 | +# DANGER! DANGER! Using Qt's private API! |
4778 | +include_directories( |
4779 | + ${Qt5Quick_PRIVATE_INCLUDE_DIRS} |
4780 | +) |
4781 | + |
4782 | include_directories( |
4783 | ${CMAKE_SOURCE_DIR}/plugins/Ubuntu/Gestures |
4784 | + ${CMAKE_SOURCE_DIR}/libs/UbuntuGestures |
4785 | ${CMAKE_CURRENT_BINARY_DIR} |
4786 | ${Qt5Gui_PRIVATE_INCLUDE_DIRS} |
4787 | ) |
4788 | @@ -17,7 +23,7 @@ |
4789 | macro(add_gesture_ui_test CLASSNAME) |
4790 | add_executable(${CLASSNAME}TestExec tst_${CLASSNAME}.cpp GestureTest.cpp) |
4791 | qt5_use_modules(${CLASSNAME}TestExec Test Core Qml Gui Quick) |
4792 | - target_link_libraries(${CLASSNAME}TestExec UbuntuGestureQml) |
4793 | + target_link_libraries(${CLASSNAME}TestExec UbuntuGesturesQml UbuntuGestures) |
4794 | |
4795 | add_binary_qml_test(${CLASSNAME} ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures UbuntuGesturesTestQmlFiles "") |
4796 | add_manual_qml_test(. ${CLASSNAME} IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins) |
4797 | @@ -34,10 +40,11 @@ |
4798 | |
4799 | add_executable(${CLASSNAME}TestExec tst_${CLASSNAME}.cpp) |
4800 | qt5_use_modules(${CLASSNAME}TestExec Test Core) |
4801 | - target_link_libraries(${CLASSNAME}TestExec UbuntuGestureQml) |
4802 | + target_link_libraries(${CLASSNAME}TestExec UbuntuGesturesQml) |
4803 | endmacro(add_gesture_test) |
4804 | |
4805 | add_gesture_ui_test(DirectionalDragArea) |
4806 | add_gesture_ui_test(PressedOutsideNotifier) |
4807 | +add_gesture_ui_test(TouchGate) |
4808 | add_gesture_test(Damper) |
4809 | add_gesture_test(AxisVelocityCalculator) |
4810 | |
4811 | === modified file 'tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml' |
4812 | --- tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml 2014-02-11 20:21:24 +0000 |
4813 | +++ tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml 2014-10-10 15:05:15 +0000 |
4814 | @@ -46,6 +46,9 @@ |
4815 | id: dragArea |
4816 | objectName: "hpDragArea" |
4817 | |
4818 | + // give some room for items to be dynamically stacked right behind him |
4819 | + z: 10.0 |
4820 | + |
4821 | width: units.gu(5) |
4822 | |
4823 | direction: Direction.Rightwards |
4824 | |
4825 | === added file 'tests/plugins/Ubuntu/Gestures/touchGateExample.qml' |
4826 | --- tests/plugins/Ubuntu/Gestures/touchGateExample.qml 1970-01-01 00:00:00 +0000 |
4827 | +++ tests/plugins/Ubuntu/Gestures/touchGateExample.qml 2014-10-10 15:05:15 +0000 |
4828 | @@ -0,0 +1,27 @@ |
4829 | +/* |
4830 | + * Copyright (C) 2014 Canonical, Ltd. |
4831 | + * |
4832 | + * This program is free software; you can redistribute it and/or modify |
4833 | + * it under the terms of the GNU General Public License as published by |
4834 | + * the Free Software Foundation; version 3. |
4835 | + * |
4836 | + * This program is distributed in the hope that it will be useful, |
4837 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4838 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4839 | + * GNU General Public License for more details. |
4840 | + * |
4841 | + * You should have received a copy of the GNU General Public License |
4842 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4843 | + */ |
4844 | + |
4845 | +import QtQuick 2.0 |
4846 | +import Ubuntu.Gestures 0.1 |
4847 | + |
4848 | +Rectangle { |
4849 | + width: 720; height: 720 |
4850 | + |
4851 | + TouchGate { |
4852 | + anchors.fill: parent |
4853 | + objectName: "touchGate" |
4854 | + } |
4855 | +} |
4856 | |
4857 | === modified file 'tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp' |
4858 | --- tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp 2014-03-07 10:49:30 +0000 |
4859 | +++ tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp 2014-10-10 15:05:15 +0000 |
4860 | @@ -1,5 +1,5 @@ |
4861 | /* |
4862 | - * Copyright (C) 2013 Canonical, Ltd. |
4863 | + * Copyright (C) 2013-2014 Canonical, Ltd. |
4864 | * |
4865 | * This program is free software; you can redistribute it and/or modify |
4866 | * it under the terms of the GNU General Public License as published by |
4867 | @@ -19,37 +19,63 @@ |
4868 | #include <QtQuick/QQuickView> |
4869 | #include <QtQml/QQmlEngine> |
4870 | #include <QPointer> |
4871 | +#include <private/qquickmousearea_p.h> |
4872 | + |
4873 | +// C++ std lib |
4874 | +#include <functional> |
4875 | |
4876 | #include <DirectionalDragArea.h> |
4877 | +#include <TouchRegistry.h> |
4878 | |
4879 | #include "GestureTest.h" |
4880 | |
4881 | using namespace UbuntuGestures; |
4882 | |
4883 | -class FakeTimer : public AbstractTimer |
4884 | -{ |
4885 | - Q_OBJECT |
4886 | -public: |
4887 | - FakeTimer(const SharedTimeSource &timeSource, QObject *parent = 0) |
4888 | - : UbuntuGestures::AbstractTimer(parent), |
4889 | +class TouchMemento { |
4890 | +public: |
4891 | + TouchMemento(const QTouchEvent *touchEvent); |
4892 | + Qt::TouchPointStates touchPointStates; |
4893 | + QList<QTouchEvent::TouchPoint> touchPoints; |
4894 | + |
4895 | + bool containsTouchWithId(int touchId) const; |
4896 | +}; |
4897 | + |
4898 | +class DummyItem : public QQuickItem |
4899 | +{ |
4900 | + Q_OBJECT |
4901 | +public: |
4902 | + DummyItem(QQuickItem *parent = 0); |
4903 | + |
4904 | + QList<TouchMemento> touchEvents; |
4905 | + std::function<void(QTouchEvent*)> touchEventHandler; |
4906 | +protected: |
4907 | + void touchEvent(QTouchEvent *event) override; |
4908 | +private: |
4909 | + static void defaultTouchEventHandler(QTouchEvent *event); |
4910 | +}; |
4911 | + |
4912 | +class ComplexFakeTimer : public FakeTimer |
4913 | +{ |
4914 | + Q_OBJECT |
4915 | +public: |
4916 | + ComplexFakeTimer(const SharedTimeSource &timeSource, QObject *parent = 0) |
4917 | + : FakeTimer(parent), |
4918 | m_timeSource(timeSource) |
4919 | {} |
4920 | |
4921 | - int interval() const override { return m_interval; } |
4922 | - void setInterval(int msecs) override { m_interval = msecs; } |
4923 | void start() override { |
4924 | AbstractTimer::start(); |
4925 | - m_nextTimeoutTime = m_timeSource->msecsSinceReference() + (qint64)m_interval; |
4926 | + m_nextTimeoutTime = m_timeSource->msecsSinceReference() + (qint64)interval(); |
4927 | } |
4928 | |
4929 | void emitTimeout() { |
4930 | - m_nextTimeoutTime += m_interval; |
4931 | + m_nextTimeoutTime += interval(); |
4932 | Q_EMIT timeout(); |
4933 | } |
4934 | |
4935 | qint64 nextTimeoutTime() const { return m_nextTimeoutTime; } |
4936 | + |
4937 | private: |
4938 | - int m_interval; |
4939 | SharedTimeSource m_timeSource; |
4940 | qint64 m_nextTimeoutTime; |
4941 | }; |
4942 | @@ -62,6 +88,30 @@ |
4943 | qint64 m_msecsSinceReference; |
4944 | }; |
4945 | |
4946 | +/* |
4947 | + QQuickMouseArea::canceled() signal is not registered in the meta object system. |
4948 | + So using a QSignalSpy to track it won't work. Thus the only way to connect to it |
4949 | + is using its method address directly. |
4950 | + */ |
4951 | +class MouseAreaSpy : public QObject |
4952 | +{ |
4953 | + Q_OBJECT |
4954 | +public: |
4955 | + MouseAreaSpy(QQuickMouseArea *mouseArea) |
4956 | + : canceledCount(0) |
4957 | + { |
4958 | + connect(mouseArea, &QQuickMouseArea::canceled, |
4959 | + this, &MouseAreaSpy::onMouseAreaCanceled); |
4960 | + } |
4961 | + |
4962 | + int canceledCount; |
4963 | + |
4964 | +private Q_SLOTS: |
4965 | + void onMouseAreaCanceled() { |
4966 | + ++canceledCount; |
4967 | + } |
4968 | +}; |
4969 | + |
4970 | class tst_DirectionalDragArea: public GestureTest |
4971 | { |
4972 | Q_OBJECT |
4973 | @@ -87,10 +137,20 @@ |
4974 | void sceneDistance(); |
4975 | void sceneDistance_data(); |
4976 | void disabledWhileDragging(); |
4977 | + void oneFingerDownFollowedByLateSecondFingerDown(); |
4978 | + void givesUpWhenLosesTouch(); |
4979 | + void threeFingerDrag(); |
4980 | + void immediateRecognitionWhenConstraintsDisabled(); |
4981 | + void withdrawTouchOwnershipCandidacyIfDisabledDuringRecognition(); |
4982 | + void withdrawTouchOwnershipCandidacyIfDisabledDuringRecognition_data(); |
4983 | + void tappedSignal(); |
4984 | + void tappedSignal_data(); |
4985 | + void gettingTouchOwnershipMakesMouseAreaBehindGetCanceled(); |
4986 | |
4987 | private: |
4988 | - void passTime(qint64 timeSpan); |
4989 | - FakeTimer *fakeTimer; |
4990 | + void passTime(qint64 timeSpanMs); |
4991 | + TouchRegistry *touchRegistry; |
4992 | + ComplexFakeTimer *fakeTimer; |
4993 | QSharedPointer<FakeTimeSource> fakeTimeSource; |
4994 | }; |
4995 | |
4996 | @@ -111,12 +171,19 @@ |
4997 | QTRY_COMPARE(m_view->width(), (int)m_view->rootObject()->width()); |
4998 | QTRY_COMPARE(m_view->height(), (int)m_view->rootObject()->height()); |
4999 | |
5000 | + touchRegistry = new TouchRegistry; |
FAILED: Continuous integration, rev:1296 jenkins. qa.ubuntu. com/job/ unity8- ci/4415/ jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- utopic- touch/5241/ console jenkins. qa.ubuntu. com/job/ unity-phablet- qmluitests- utopic/ 1411/console jenkins. qa.ubuntu. com/job/ unity8- utopic- amd64-ci/ 1509/console jenkins. qa.ubuntu. com/job/ unity8- utopic- i386-ci/ 1509/console jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- armhf/6493/ console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/unity8- ci/4415/ rebuild
http://