Merge lp:~nick-dedekind/unity8/side-stage-redesign into lp:unity8
- side-stage-redesign
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~nick-dedekind/unity8/side-stage-redesign |
Merge into: | lp:unity8 |
Prerequisite: | lp:~mzanetti/unity8/external-deviceconfig |
Diff against target: |
5646 lines (+3671/-447) (has conflicts) 55 files modified
CMakeLists.txt (+1/-1) CODING (+2/-0) debian/control (+7/-3) libs/UbuntuGestures/Timer.h (+5/-0) libs/UbuntuGestures/TouchRegistry.cpp (+15/-10) plugins/Cursor/Cursor.qmltypes (+78/-0) plugins/GlobalShortcut/GlobalShortcut.qmltypes (+31/-0) plugins/Ubuntu/Gestures/CMakeLists.txt (+1/-0) plugins/Ubuntu/Gestures/DirectionalDragArea.cpp (+3/-0) plugins/Ubuntu/Gestures/Gestures.qmltypes (+87/-46) plugins/Ubuntu/Gestures/TouchGestureArea.cpp (+875/-0) plugins/Ubuntu/Gestures/TouchGestureArea.h (+229/-0) plugins/Ubuntu/Gestures/plugin.cpp (+3/-0) plugins/Unity/InputInfo/InputInfo.qmltypes (+71/-0) plugins/Unity/Platform/Platform.qmltypes (+20/-0) plugins/Utils/CMakeLists.txt (+1/-0) plugins/Utils/globalfunctions.cpp (+56/-0) plugins/Utils/globalfunctions.h (+42/-0) plugins/Utils/plugin.cpp (+9/-0) plugins/Utils/windowstatestorage.cpp (+36/-4) plugins/Utils/windowstatestorage.h (+3/-0) qml/Components/Showable.qml (+10/-0) qml/Shell.qml (+2/-6) qml/Stages/AbstractStage.qml (+1/-1) qml/Stages/ApplicationWindow.qml (+3/-1) qml/Stages/SideStage.qml (+118/-0) qml/Stages/SpreadDelegate.qml (+10/-1) qml/Stages/SurfaceContainer.qml (+1/-0) qml/Stages/TabletSideStageTouchGesture.qml (+154/-0) qml/Stages/TabletStage.qml (+402/-191) qml/Stages/TransformedTabletSpreadDelegate.qml (+86/-58) qml/Stages/graphics/sidestage_drag.svg (+207/-0) qml/Stages/graphics/sidestage_open.svg (+181/-0) src/MouseTouchAdaptor.cpp (+53/-9) src/MouseTouchAdaptor.h (+5/-3) tests/libs/UbuntuGestures/tst_TouchRegistry.cpp (+59/-0) tests/mocks/Unity/Application/ApplicationManager.cpp (+10/-25) tests/mocks/Unity/Application/ApplicationManager.h (+0/-8) tests/mocks/Unity/Application/MirSurfaceItem.cpp (+17/-0) tests/mocks/Unity/Application/MirSurfaceItem.h (+1/-0) tests/mocks/Unity/Application/resources/MirSurfaceItem.qml (+59/-0) tests/mocks/Utils/CMakeLists.txt (+1/-0) tests/mocks/Utils/plugin.cpp (+9/-0) tests/mocks/Utils/windowstatestorage.cpp (+13/-0) tests/mocks/Utils/windowstatestorage.h (+4/-0) tests/plugins/Ubuntu/Gestures/CMakeLists.txt (+7/-1) tests/plugins/Ubuntu/Gestures/GestureTest.cpp (+44/-0) tests/plugins/Ubuntu/Gestures/GestureTest.h (+13/-1) tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp (+0/-56) tests/plugins/Ubuntu/Gestures/tst_TouchGestureArea.cpp (+270/-0) tests/plugins/Ubuntu/Gestures/tst_TouchGestureArea.qml (+90/-0) tests/plugins/Unity/Launcher/launchermodeltest.cpp (+1/-0) tests/qmltests/Stages/tst_TabletStage.qml (+143/-0) tests/qmltests/tst_OrientedShell.qml (+55/-11) tests/utils/modules/Unity/Test/UnityTestCase.qml (+67/-11) Text conflict in debian/control |
To merge this branch: | bzr merge lp:~nick-dedekind/unity8/side-stage-redesign |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Daniel d'Andrada (community) | Approve | ||
Unity8 CI Bot | continuous-integration | Needs Fixing | |
PS Jenkins bot (community) | continuous-integration | Needs Fixing | |
Michael Zanetti (community) | Needs Fixing | ||
Lukáš Tinkl | code-review | Pending | |
Review via email: mp+286194@code.launchpad.net |
This proposal supersedes a proposal from 2016-01-04.
This proposal has been superseded by a proposal from 2016-03-11.
Commit message
Sidestage load/unload redesign
Description of the change
Side stage redesign phase 1
* Are there any related MPs required for this MP to build/function as expected? Please list.
https:/
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?
N/A
* If you changed the UI, has there been a design review?
yes
Lukáš Tinkl (lukas-kde) wrote : Posted in a previous version of this proposal | # |
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2035
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Nick Dedekind (nick-dedekind) wrote : Posted in a previous version of this proposal | # |
Fixed review comments.
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2036
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2037
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Nick Dedekind (nick-dedekind) wrote : Posted in a previous version of this proposal | # |
Added tests
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2040
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Michael Terry (mterry) wrote : Posted in a previous version of this proposal | # |
A testing silo would be convenient (since qtmir is involved and jenkins can't figure it out). But I can build myself in the meantime.
Michael Terry (mterry) wrote : Posted in a previous version of this proposal | # |
Ah, silo 1 already exists.
Michael Terry (mterry) wrote : Posted in a previous version of this proposal | # |
I started looking at this, but realized that my only tablet suitable for testing this is a manta, and it's busted these days. :( So maybe someone else should actually do the review. But just one quick comment:
+ const QString queryString = QStringLiteral(
+ .arg(appId)
+ .arg((int)stage);
I know that appIds are not the wild west of string input, but it makes me very nervous to see an unescaped sprintf fed into SQL. Is there an easy QString call or similar that we can make to give us 100% future proof peace of mind?
I mean, I assume the appId rules are sane -- only alphanumeric and hyphen maybe? But is u8 actually enforcing that, or are we just trusting that other components have? Wouldn't hurt to either add a comment here explaining exactly why we trust this input or simply escape it ourselves.
Same for getStage right below this.
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2040
https:/
Executed test runs:
Click here to trigger a rebuild:
https:/
Nick Dedekind (nick-dedekind) wrote : Posted in a previous version of this proposal | # |
> I started looking at this, but realized that my only tablet suitable for
> testing this is a manta, and it's busted these days. :( So maybe someone
> else should actually do the review. But just one quick comment:
>
> + const QString queryString = QStringLiteral(
> (appId, stage) values ('%1', '%2');")
> + .arg(appId)
> + .arg((int)stage);
>
> I know that appIds are not the wild west of string input, but it makes me very
> nervous to see an unescaped sprintf fed into SQL. Is there an easy QString
> call or similar that we can make to give us 100% future proof peace of mind?
>
> I mean, I assume the appId rules are sane -- only alphanumeric and hyphen
> maybe? But is u8 actually enforcing that, or are we just trusting that other
> components have? Wouldn't hurt to either add a comment here explaining
> exactly why we trust this input or simply escape it ourselves.
>
> Same for getStage right below this.
I've added some string sanitising to the sql input. Removed the offensive chars rather escaping them.
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2041
https:/
Executed test runs:
Click here to trigger a rebuild:
https:/
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2041
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Albert Astals Cid (aacid) wrote : Posted in a previous version of this proposal | # |
plugins/
plugins/
qml/Stages/
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2041
https:/
Executed test runs:
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2041
https:/
Executed test runs:
Click here to trigger a rebuild:
https:/
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
About the TouchRegistry change: Please write a regression test to cover this fix and please also propose it to the TouchRegistry copy in ubuntu-ui-toolkit. We will soon move to use it (s/DirectionalD
I feared such reentrancy issues and was considering s/sendEvent/
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
In TouchGestureArea.h:
"""
Q_PROPERTY(int minimumTouchPoints READ minimumTouchPoints WRITE setMinimumTouch
Q_PROPERTY(int maximumTouchPoints READ maximumTouchPoints WRITE setMaximumTouch
"""
Doing that way would save you from the boredom of writing down standard getters and setters yourself:
Q_PROPERTY(int minimumTouchPoints MEMBER m_minimumTouchP
Q_PROPERTY(int maximumTouchPoints MEMBER m_maximumTouchP
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
Please write some documentation on TouchGestureArea class (eg, why use it instead of MultiPointTouch
copy-and-paste leftover in TouchGestureArea.h:
"""
// Describes the state of the directional drag gesture.
"""
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
TouchGestureAre
"""
TouchGestureAre
"""
Typo: "intenral"
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
Also some tests for TouchGestureArea would be nice. Exercising minimum/maximum touch points and touch ownership. Logic in this class is not trivial...
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
TouchGestureArea is an interesting class. We might eventually move it to uitk just like we did for DDA...
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
TouchGestureArea:
I think a better name for InternalStatus:
You effectively split DDA's original Undecided into WaitingForMoreT
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
Actually TouchGestureAre
-----
In TouchGestureAre
"""
event-
"""
This call doesn't make sense and has no effect there.
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
In TouchGestureAre
"""
TouchRegistry:
m_watchedTouche
"""
There's no point in watching touches that you own. You will already get its updates from regular QTouchEvents.
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
TouchGestureAre
"""
GestureTouchPoint* gtp = static_
"""
I see no reason for this cast.
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
In TouchGestureAre
"""
return static_
"""
Another unnecessary cast.
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
"make tryTabletStage" is broken.
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2043
https:/
Executed test runs:
Click here to trigger a rebuild:
https:/
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2043
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Albert Astals Cid (aacid) wrote : Posted in a previous version of this proposal | # |
Text conflict in plugins/
Text conflict in plugins/
Text conflict in qml/Shell.qml
Text conflict in qml/Stages/
Text conflict in qml/Stages/
Text conflict in tests/mocks/
Text conflict in tests/mocks/
7 conflicts encountered.
Nick Dedekind (nick-dedekind) wrote : Posted in a previous version of this proposal | # |
> In TouchGestureAre
>
> """
> TouchRegistry:
> m_watchedTouche
> """
>
> There's no point in watching touches that you own. You will already get its
> updates from regular QTouchEvents.
I've found that sometimes we will get an event stolen and we don't get the "touch leave" event through. I can't remember the circumstances.
The component caches the last touch points all the time, in case we get a momentary release of one or more of the points. If I don't monitor all the points from start to finish, we can end up with "zombie points" in the cache map. So I'm monitoring all touches from start to finish of the gesture.
Nick Dedekind (nick-dedekind) wrote : Posted in a previous version of this proposal | # |
review comments fixed.
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2046
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Nick Dedekind (nick-dedekind) wrote : Posted in a previous version of this proposal | # |
> "make tryTabletStage" is broken.
Broken how? Seems to be working on my side. Was getting a seg fault but clean compiled and it's fine now :/
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2047
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2046
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2047
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Nick Dedekind (nick-dedekind) wrote : Posted in a previous version of this proposal | # |
Tests added.
Nick Dedekind (nick-dedekind) wrote : Posted in a previous version of this proposal | # |
> In TouchGestureArea.h:
>
> """
> Q_PROPERTY(int minimumTouchPoints READ minimumTouchPoints WRITE
> setMinimumTouch
> Q_PROPERTY(int maximumTouchPoints READ maximumTouchPoints WRITE
> setMaximumTouch
> """
>
> Doing that way would save you from the boredom of writing down standard
> getters and setters yourself:
>
> Q_PROPERTY(int minimumTouchPoints MEMBER m_minimumTouchP
> minimumTouchPoi
> Q_PROPERTY(int maximumTouchPoints MEMBER m_maximumTouchP
> maximumTouchPoi
I did this, but reverted because I needed the set for tests.
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2049
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
> > "make tryTabletStage" is broken.
>
> Broken how? Seems to be working on my side. Was getting a seg fault but clean
> compiled and it's fine now :/
It's working now.
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
Can't see any application in "make tryShell".
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
In qml/Stages/
" * Copyright (C) 2014-2015 Canonical, Ltd."
Should be just 2016.
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
In qml/Stages/
"""
property bool enableDrag: true
"""
Doesn't seem to ever change to false (at least couldn't find code doing it). So maybe this is redundant now (and thus could be removed)?
"""
property bool wasRecognisedPress: false
property bool wasRecognisedDrag: false
readonly property bool recognisedPress: status == TouchGestureAre
readonly property bool recognisedDrag: wasRecognisedPress && dragging
"""
They all seem to be private properties. If so, please move them to the priv object.
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
In qml/Stages/
"""
signal drag
"""
It's named like a function. Suggestions: "signal dragStarted" or "property bool dragging" (like in DirectionalDrag
Also couldn't find any code using it. Maybe we could just remove it?
"""
signal drop
"""
It's named like a function. Suggestion: "dropped" (would a "property bool dragging" cover both signals?). Also the only code I found using it was TabletSideStage
"""
signal cancel
"""
It's named like a function. Also the only code I found using it was TabletSideStage
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
"""
property bool surfaceDragging: triGestureArea.
"""
Ok so recognisedDrag is being used externally. Sorry for that.
Nick Dedekind (nick-dedekind) wrote : Posted in a previous version of this proposal | # |
> In qml/Stages/
>
> """
> property bool enableDrag: true
> """
>
> Doesn't seem to ever change to false (at least couldn't find code doing it).
> So maybe this is redundant now (and thus could be removed)?
It's being used by the tutorial branch associated with this one.
https:/
>
> """
> property bool wasRecognisedPress: false
> property bool wasRecognisedDrag: false
> readonly property bool recognisedPress: status ==
> TouchGestureAre
> touchPoints.length >=
> minimumTouchPoints &&
> touchPoints.length <=
> maximumTouchPoints
> readonly property bool recognisedDrag: wasRecognisedPress && dragging
> """
>
> They all seem to be private properties. If so, please move them to the priv
> object.
Privatized the wasRecognised[
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2049
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Nick Dedekind (nick-dedekind) wrote : Posted in a previous version of this proposal | # |
> In qml/Stages/
>
> """
> signal drag
> """
>
> It's named like a function. Suggestions: "signal dragStarted" or "property
> bool dragging" (like in DirectionalDrag
>
> Also couldn't find any code using it. Maybe we could just remove it?
>
> """
> signal drop
> """
>
> It's named like a function. Suggestion: "dropped" (would a "property bool
> dragging" cover both signals?). Also the only code I found using it was
> TabletSideStage
>
>
> """
> signal cancel
> """
>
> It's named like a function. Also the only code I found using it was
> TabletSideStage
Changed the names.
I'm using clicked, dropped & cancelled signals in the tutorial branch. Drag signal is for API completeness (drop without a drag isn't great API).
Nick Dedekind (nick-dedekind) wrote : Posted in a previous version of this proposal | # |
> Can't see any application in "make tryShell".
Fixed
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2051
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2049
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
"""
void topAreaRecevesO
void topAreaRecevesO
"""
typo:
s/Receves/Receives
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
In Shell.qml, you're reformatting finishStartUpTimer for no good reason. That unnecessarily bloats the diff and increases the chances for conflicts with other MPs when merging trunk etc.
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
In Shell.qml:
In the supportedOrient
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
Again on Shell.qml diff bloat: you added a newline in applicationsDis
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2052
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2051
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
Unnecessary reformatting also in qml/Stages/
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
qml/Stages/
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2053
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
Getting lots of false negatives with the side stage drag handle. eg: lay a finger over it and hold it for some 2 seconds. then drag rightwards. You're no longer dragging the handle.
You have to configure this drag handle for immediate recognition as this is a visible target. There's no point in doing gesture recognition for a target the user can see.
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
1 - launch a main-stage app
2 - go back to dash
3 - launch another main-stage app
4 - drag that app to the side-stage
5 - hide the side-stage (drag it to the right)
6 - perform a long drag from the right edge (to get to the spread). Do it slowly so you can better see the animation details
untiy8-dash (the top-most app when you perform the right-edge drag) will move far too much to the right initially, not aligning nicely with the other 2 application in the dash.
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2052
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2053
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2054
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
"""
- void tst_TouchGestur
+ void tst_TouchGestur
"""
:-D
Tried to fix the typo but ended up making another typo!
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
"""
Text conflict in CMakeLists.txt
Text conflict in debian/control
Text conflict in qml/Stages/
Text conflict in tests/mocks/
"""
A bunch of conflicts with latest trunk now.
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2057
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2054
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2060
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Nick Dedekind (nick-dedekind) wrote : | # |
Fixed the stage dragging bug.
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2057
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2061
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2060
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Daniel d'Andrada (dandrader) wrote : | # |
Went to test on my Nexus 7. It seems to interpret a single-finger drag as a 3-finger drag. Maybe you forgot to change back the minimum touch count again?
By the way, there *must* be a way to play with dragging apps around in "make tryFoo" tests which doesn't involve tweaking the code (like setting minimumTouchCount to 1). Something like when you have some modifier (like Ctrl or Shift or Right-Alt) while the mouse is pressed MouseTouchAdaptor will generate 3 separate touch points instead of a single one out of the mouse event.
Daniel d'Andrada (dandrader) wrote : | # |
Went to test on my Nexus 7. It seems to interpret a single-finger drag as a 3-finger drag. Maybe you forgot to change back the minimum touch count again?
By the way, there *must* be a way to play with dragging apps around in "make tryFoo" tests which doesn't involve tweaking the code (like setting minimumTouchCount to 1). Something like when you have some modifier (like Ctrl or Shift or Right-Alt) while the mouse is pressed MouseTouchAdaptor will generate 3 separate touch points instead of a single one out of the mouse event.
Daniel d'Andrada (dandrader) wrote : | # |
Copy-and-paste leftover: Copyright year in tst_TouchGestur
Daniel d'Andrada (dandrader) wrote : | # |
And this comment in tst_TouchGestur
"""
/*
NB: If you change positions or sizes here make sure
tst_
*/
"""
Daniel d'Andrada (dandrader) wrote : | # |
tst_TouchGestur
Look at this sequence:
"""
QTest::
QTest::
QTest::
"""
On each subsequent QTouchEvent you're forgetting about the previouly pressed touch points. So this sequence is invalid. It should be something like this:
"""
QTest::
QTest::
.move(0, touchPoint0)
.press(1, touchPoint.
QTest::
.move(0, touchPoint0)
.move(1, touchPoint1);
.press(2, touchPoint.
"""
Ie, a QTouchEvent must mention every single active touch point. See tst_Directional
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2061
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Nick Dedekind (nick-dedekind) wrote : | # |
> Went to test on my Nexus 7. It seems to interpret a single-finger drag as a
> 3-finger drag. Maybe you forgot to change back the minimum touch count again?
>
> By the way, there *must* be a way to play with dragging apps around in "make
> tryFoo" tests which doesn't involve tweaking the code (like setting
> minimumTouchCount to 1). Something like when you have some modifier (like Ctrl
> or Shift or Right-Alt) while the mouse is pressed MouseTouchAdaptor will
> generate 3 separate touch points instead of a single one out of the mouse
> event.
Fixed.
I'll look into adding a modifier.
Nick Dedekind (nick-dedekind) wrote : | # |
> tst_TouchGestur
>
> Look at this sequence:
>
> """
> QTest::
>
> QTest::
>
> QTest::
> """
>
> On each subsequent QTouchEvent you're forgetting about the previouly pressed
> touch points. So this sequence is invalid. It should be something like this:
>
> """
> QTest::
>
> QTest::
> .move(0, touchPoint0)
> .press(1, touchPoint.
>
> QTest::
> .move(0, touchPoint0)
> .move(1, touchPoint1);
> .press(2, touchPoint.
> """
>
> Ie, a QTouchEvent must mention every single active touch point. See
> tst_Directional
Yeah, forgot about this; i've done it before. used TouchEvent:
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2063
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2063
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Nick Dedekind (nick-dedekind) wrote : | # |
> > Went to test on my Nexus 7. It seems to interpret a single-finger drag as a
> > 3-finger drag. Maybe you forgot to change back the minimum touch count
> again?
> >
> > By the way, there *must* be a way to play with dragging apps around in "make
> > tryFoo" tests which doesn't involve tweaking the code (like setting
> > minimumTouchCount to 1). Something like when you have some modifier (like
> Ctrl
> > or Shift or Right-Alt) while the mouse is pressed MouseTouchAdaptor will
> > generate 3 separate touch points instead of a single one out of the mouse
> > event.
>
> Fixed.
> I'll look into adding a modifier.
I've added a MouseTouchAdaptor tripress modifier. Control & Shift & Alt for 3 touch events. All three at same time.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2064
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Daniel d'Andrada (dandrader) wrote : | # |
On 17/02/2016 08:32, Nick Dedekind wrote:
>>> Went to test on my Nexus 7. It seems to interpret a single-finger drag as a
>>> 3-finger drag. Maybe you forgot to change back the minimum touch count
>> again?
>>> By the way, there *must* be a way to play with dragging apps around in "make
>>> tryFoo" tests which doesn't involve tweaking the code (like setting
>>> minimumTouchCount to 1). Something like when you have some modifier (like
>> Ctrl
>>> or Shift or Right-Alt) while the mouse is pressed MouseTouchAdaptor will
>>> generate 3 separate touch points instead of a single one out of the mouse
>>> event.
>> Fixed.
>> I'll look into adding a modifier.
> I've added a MouseTouchAdaptor tripress modifier. Control & Shift & Alt for 3 touch events. All three at same time.
Wow, that was fast! Awesome! Will try it out. Thanks!
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2064
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Daniel d'Andrada (dandrader) wrote : | # |
Ctrl+Shift+Alt for 3-finger touch emulation is working nicely.
There seems to be something wrong in the Unity.Application mock though.
1 - run "make tryOrientedShell"
2 - Select "manta" in the "Device Name" field
3 - Unlock screen
4 - launch dialer-app
5 - 3-finger drag it onto the side stage
Expected outcome:
Dialer goes to side stage
Actual outcome:
Nothing happens and instead I get this in the terminal:
"""
qml/Stages/
"""
Daniel d'Andrada (dandrader) wrote : | # |
Again on the 3-finger touch emulation, I think it would be good to advertise it in our CODING file, under the "Running tests" section. As it's one of those things you cannot discover by yourself.
Daniel d'Andrada (dandrader) wrote : | # |
The side stage help graphics appears rapidly (shows up and quickly fades out) when you do a right-edge swipe to go to the spread mode.
https:/
Nick Dedekind (nick-dedekind) wrote : | # |
> Ctrl+Shift+Alt for 3-finger touch emulation is working nicely.
>
> There seems to be something wrong in the Unity.Application mock though.
>
> 1 - run "make tryOrientedShell"
> 2 - Select "manta" in the "Device Name" field
> 3 - Unlock screen
> 4 - launch dialer-app
> 5 - 3-finger drag it onto the side stage
>
> Expected outcome:
> Dialer goes to side stage
>
> Actual outcome:
>
> Nothing happens and instead I get this in the terminal:
> """
> qml/Stages/
> "stage"
> """
Think you may need to remoc or install the unity-api that this branch comes with?
Daniel d'Andrada (dandrader) wrote : | # |
On 17/02/2016 14:43, Nick Dedekind wrote:
>> Ctrl+Shift+Alt for 3-finger touch emulation is working nicely.
>>
>> There seems to be something wrong in the Unity.Application mock though.
>>
>> 1 - run "make tryOrientedShell"
>> 2 - Select "manta" in the "Device Name" field
>> 3 - Unlock screen
>> 4 - launch dialer-app
>> 5 - 3-finger drag it onto the side stage
>>
>> Expected outcome:
>> Dialer goes to side stage
>>
>> Actual outcome:
>>
>> Nothing happens and instead I get this in the terminal:
>> """
>> qml/Stages/
>> "stage"
>> """
> Think you may need to remoc or install the unity-api that this branch comes with?
Rebuilding the untiy8 branch from scratch did the trick. Thanks.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2065
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Nick Dedekind (nick-dedekind) wrote : | # |
> The side stage help graphics appears rapidly (shows up and quickly fades out)
> when you do a right-edge swipe to go to the spread mode.
>
> https:/
The side stage fades out when not available.
I've changed it to use the spreadView.active property rather than the phase; so it should disappear sooner now.
Nick Dedekind (nick-dedekind) wrote : | # |
> The side stage help graphics appears rapidly (shows up and quickly fades out)
> when you do a right-edge swipe to go to the spread mode.
>
> https:/
The side stage fades out when not available.
I've changed it to use the spreadView.active property rather than the phase; so it should disappear sooner now.
Nick Dedekind (nick-dedekind) wrote : | # |
> Again on the 3-finger touch emulation, I think it would be good to advertise
> it in our CODING file, under the "Running tests" section. As it's one of those
> things you cannot discover by yourself.
I've added it under "Running Unity 8 on your desktop". There's already a note on mouse emu there.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2066
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2066
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Daniel d'Andrada (dandrader) wrote : | # |
It's getting there...
In TouchGestureAre
If you insist in watching touches you already own (as in QQuickWindow already sends updates to you), would it be possible to at least updateTouchPoints() only once per QTouchEvent? As opposed to in both touchEvent() and unownedTouchEve
Also I think it's important to have a comment in the code (probably a TODO or FIXME) explaining why the heck do we watch touch points we already own. It doesn't make sense unless you're working around some issue. Eventually this issue should be properly understood.
Daniel d'Andrada (dandrader) wrote : | # |
Missing copyright header:
plugins/
s/2014/2016 in tests/plugins/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2067
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Nick Dedekind (nick-dedekind) wrote : | # |
> It's getting there...
>
> In TouchGestureAre
>
> If you insist in watching touches you already own (as in QQuickWindow already
> sends updates to you), would it be possible to at least updateTouchPoints()
> only once per QTouchEvent? As opposed to in both touchEvent() and
> unownedTouchEve
I think we can ignore unowned touch updates unless they're presses or releases.
I'll have to test on device.
>
> Also I think it's important to have a comment in the code (probably a TODO or
> FIXME) explaining why the heck do we watch touch points we already own. It
> doesn't make sense unless you're working around some issue. Eventually this
> issue should be properly understood.
Done.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2068
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2068
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Michael Zanetti (mzanetti) wrote : | # |
Small visual issue:
* Open something that can go fullscreen on the main stage (e.g. gallery with picture)
* Open a side stage app
Now do a slow long right edge swipe. When the side stage app starts moving, it will jump upwards.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2069
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2069
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2070
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Nick Dedekind (nick-dedekind) wrote : | # |
> Small visual issue:
>
> * Open something that can go fullscreen on the main stage (e.g. gallery with
> picture)
> * Open a side stage app
>
> Now do a slow long right edge swipe. When the side stage app starts moving, it
> will jump upwards.
Arg. little bit complicated in there but I think i got it sorted. :)
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2071
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2070
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
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:2071
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2074
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2074
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Daniel d'Andrada (dandrader) wrote : | # |
Looks like some bitrotting happened overtime.
- run "make tryTabletStage"
- launch a bunch of apps
- go to spread
- tap on an application
during the animation to bring the selected application to foreground, the other apps in the spread are kept in front of that selected one (Z ordering issue).
-------
Some miscellaneous clean up (revisions 2217 to 2221):
lp:~dandrader/unity8/side-stage-redesign
Nick Dedekind (nick-dedekind) wrote : | # |
> Looks like some bitrotting happened overtime.
>
> - run "make tryTabletStage"
> - launch a bunch of apps
> - go to spread
> - tap on an application
>
> during the animation to bring the selected application to foreground, the
> other apps in the spread are kept in front of that selected one (Z ordering
> issue).
Fixed.
>
> -------
>
> Some miscellaneous clean up (revisions 2217 to 2221):
> lp:~dandrader/unity8/side-stage-redesign
Merged
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2076
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Daniel d'Andrada (dandrader) 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.
No, due to unity-api dependency.
* Did you make sure that the branch does not contain spurious tags?
Yes
Daniel d'Andrada (dandrader) wrote : | # |
OrientedShell:
http://
- 2077. By Nick Dedekind
-
fixed tests
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2077
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Daniel d'Andrada (dandrader) : | # |
- 2078. By Nick Dedekind
-
version bump
Unmerged revisions
- 2078. By Nick Dedekind
-
version bump
- 2077. By Nick Dedekind
-
fixed tests
- 2076. By Nick Dedekind
-
fixed opacicty
- 2075. By Daniel d'Andrada
-
review comments
- 2074. By Nick Dedekind
-
better visible values
- 2073. By Nick Dedekind
-
translate so shadows dont overlap
- 2072. By Nick Dedekind
-
added compiler suggestion
- 2071. By Nick Dedekind
-
Updated fullscreen app margin progress
- 2070. By Nick Dedekind
-
only set drag if all points are dragging
- 2069. By Nick Dedekind
-
dont update touch on unowned event if not pressed/released
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2016-02-12 00:12:30 +0000 |
3 | +++ CMakeLists.txt 2016-03-11 12:07:48 +0000 |
4 | @@ -57,7 +57,7 @@ |
5 | find_package(Qt5Concurrent 5.4 REQUIRED) |
6 | find_package(Qt5Sql 5.4 REQUIRED) |
7 | |
8 | -pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=13) |
9 | +pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=14) |
10 | pkg_check_modules(GIO REQUIRED gio-2.0>=2.32) |
11 | pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32) |
12 | pkg_check_modules(QMENUMODEL REQUIRED qmenumodel) |
13 | |
14 | === modified file 'CODING' |
15 | --- CODING 2015-06-23 07:51:27 +0000 |
16 | +++ CODING 2016-03-11 12:07:48 +0000 |
17 | @@ -54,6 +54,8 @@ |
18 | Notes |
19 | ----- |
20 | - to navigate, utilize the mouse left button as you would your finger |
21 | +- a three point drag (for side stage operations) can be performed by using |
22 | + the Shift+Control+Alt (all together) keyboard modifiers while using the mouse. |
23 | - to get the translations work, currently you have to do make install to |
24 | put the *.mo files into correct structure. We'll look for a better solution |
25 | later. |
26 | |
27 | === modified file 'debian/control' |
28 | --- debian/control 2016-03-04 21:03:43 +0000 |
29 | +++ debian/control 2016-03-11 12:07:48 +0000 |
30 | @@ -29,7 +29,11 @@ |
31 | libqt5xmlpatterns5-dev, |
32 | libsystemsettings-dev, |
33 | libudev-dev, |
34 | +<<<<<<< TREE |
35 | libunity-api-dev (>= 7.107), |
36 | +======= |
37 | + libunity-api-dev (>= 7.108), |
38 | +>>>>>>> MERGE-SOURCE |
39 | libusermetricsoutput1-dev, |
40 | # Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop |
41 | libx11-dev[!armhf], |
42 | @@ -102,7 +106,7 @@ |
43 | qml-module-qtquick-xmllistmodel, |
44 | qml-module-qtsysteminfo, |
45 | qtdeclarative5-gsettings1.0, |
46 | - qtdeclarative5-qtmir-plugin (>= 0.4.5), |
47 | + qtdeclarative5-qtmir-plugin (>= 0.4.8), |
48 | qtdeclarative5-ubuntu-telephony0.1, |
49 | qtdeclarative5-ubuntu-web-plugin, |
50 | ubuntu-system-settings, |
51 | @@ -131,7 +135,7 @@ |
52 | qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.3.1796) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.3.1796), |
53 | qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl, |
54 | ubuntu-thumbnailer-impl-0, |
55 | - unity-application-impl-13, |
56 | + unity-application-impl-14, |
57 | unity-notifications-impl-3, |
58 | unity-plugin-scopes | unity-scopes-impl, |
59 | unity-scopes-impl-10, |
60 | @@ -177,7 +181,7 @@ |
61 | Depends: ${misc:Depends}, |
62 | ${shlibs:Depends}, |
63 | Provides: unity-application-impl, |
64 | - unity-application-impl-13, |
65 | + unity-application-impl-14, |
66 | Replaces: unity8-autopilot (<< 8.02+15.04.20150422-0ubuntu1) |
67 | Description: Fake environment for running Unity 8 shell |
68 | Provides fake implementations of some QML modules used by Unity 8 shell |
69 | |
70 | === modified file 'libs/UbuntuGestures/Timer.h' |
71 | --- libs/UbuntuGestures/Timer.h 2015-04-24 13:19:24 +0000 |
72 | +++ libs/UbuntuGestures/Timer.h 2016-03-11 12:07:48 +0000 |
73 | @@ -35,6 +35,11 @@ |
74 | virtual int interval() const = 0; |
75 | virtual void setInterval(int msecs) = 0; |
76 | virtual void start() { m_isRunning = true; } |
77 | + virtual void start(int msecs) |
78 | + { |
79 | + setInterval(msecs); |
80 | + start(); |
81 | + } |
82 | virtual void stop() { m_isRunning = false; } |
83 | bool isRunning() const { return m_isRunning; } |
84 | virtual bool isSingleShot() const = 0; |
85 | |
86 | === modified file 'libs/UbuntuGestures/TouchRegistry.cpp' |
87 | --- libs/UbuntuGestures/TouchRegistry.cpp 2015-09-14 09:11:08 +0000 |
88 | +++ libs/UbuntuGestures/TouchRegistry.cpp 2016-03-11 12:07:48 +0000 |
89 | @@ -359,10 +359,6 @@ |
90 | |
91 | void TouchRegistry::requestTouchOwnership(int id, QQuickItem *candidate) |
92 | { |
93 | - #if TOUCHREGISTRY_DEBUG |
94 | - UG_DEBUG << "requestTouchOwnership id " << id << "candidate" << candidate; |
95 | - #endif |
96 | - |
97 | Pool<TouchInfo>::Iterator touchInfo = findTouchInfo(id); |
98 | if (!touchInfo) { qFatal("TouchRegistry: Failed to find TouchInfo"); } |
99 | |
100 | @@ -379,6 +375,9 @@ |
101 | break; |
102 | } |
103 | } |
104 | + #if TOUCHREGISTRY_DEBUG |
105 | + UG_DEBUG << "requestTouchOwnership id " << id << "candidate" << candidate << "index: " << candidateIndex; |
106 | + #endif |
107 | |
108 | // add it as a candidate if not present yet |
109 | if (candidateIndex < 0) { |
110 | @@ -533,16 +532,22 @@ |
111 | << " gained) to candidate" << candidates[0].item; |
112 | #endif |
113 | |
114 | + // need to take a copy of the item list in case |
115 | + // we call back in to remove candidate during the lost ownership event. |
116 | + QList<QPointer<QQuickItem>> items; |
117 | + Q_FOREACH(const CandidateInfo& info, candidates) { |
118 | + items << info.item; |
119 | + } |
120 | + |
121 | TouchOwnershipEvent gainedOwnershipEvent(id, true /*gained*/); |
122 | - QCoreApplication::sendEvent(candidates[0].item, &gainedOwnershipEvent); |
123 | - |
124 | + QCoreApplication::sendEvent(items[0], &gainedOwnershipEvent); |
125 | |
126 | TouchOwnershipEvent lostOwnershipEvent(id, false /*gained*/); |
127 | - for (int i = 1; i < candidates.count(); ++i) { |
128 | + for (int i = 1; i < items.count(); ++i) { |
129 | #if TOUCHREGISTRY_DEBUG |
130 | - UG_DEBUG << "sending TouchWonershipEvent(id =" << id << " lost) to candidate" |
131 | - << candidates[i].item; |
132 | + UG_DEBUG << "sending TouchOwnershipEvent(id =" << id << " lost) to candidate" |
133 | + << items[i]; |
134 | #endif |
135 | - QCoreApplication::sendEvent(candidates[i].item, &lostOwnershipEvent); |
136 | + QCoreApplication::sendEvent(items[i], &lostOwnershipEvent); |
137 | } |
138 | } |
139 | |
140 | === added file 'plugins/Cursor/Cursor.qmltypes' |
141 | --- plugins/Cursor/Cursor.qmltypes 1970-01-01 00:00:00 +0000 |
142 | +++ plugins/Cursor/Cursor.qmltypes 2016-03-11 12:07:48 +0000 |
143 | @@ -0,0 +1,78 @@ |
144 | +import QtQuick.tooling 1.1 |
145 | + |
146 | +// This file describes the plugin-supplied types contained in the library. |
147 | +// It is used for QML tooling purposes only. |
148 | +// |
149 | +// This file was auto-generated by: |
150 | +// 'qmlplugindump -notrelocatable Cursor 1.0 plugins' |
151 | + |
152 | +Module { |
153 | + Component { |
154 | + name: "MirMousePointerInterface" |
155 | + defaultProperty: "data" |
156 | + prototype: "QQuickItem" |
157 | + Property { name: "cursorName"; type: "string"; isReadonly: true } |
158 | + Property { name: "themeName"; type: "string"; isReadonly: true } |
159 | + Property { name: "hotspotX"; type: "double"; isReadonly: true } |
160 | + Property { name: "hotspotY"; type: "double"; isReadonly: true } |
161 | + Signal { |
162 | + name: "cursorNameChanged" |
163 | + Parameter { name: "name"; type: "string" } |
164 | + } |
165 | + Signal { |
166 | + name: "themeNameChanged" |
167 | + Parameter { name: "name"; type: "string" } |
168 | + } |
169 | + Signal { |
170 | + name: "hotspotXChanged" |
171 | + Parameter { name: "value"; type: "double" } |
172 | + } |
173 | + Signal { |
174 | + name: "hotspotYChanged" |
175 | + Parameter { name: "value"; type: "double" } |
176 | + } |
177 | + Method { |
178 | + name: "handleMouseEvent" |
179 | + Parameter { name: "timestamp"; type: "ulong" } |
180 | + Parameter { name: "movement"; type: "QPointF" } |
181 | + Parameter { name: "buttons"; type: "Qt::MouseButtons" } |
182 | + Parameter { name: "modifiers"; type: "Qt::KeyboardModifiers" } |
183 | + } |
184 | + Method { |
185 | + name: "handleWheelEvent" |
186 | + Parameter { name: "timestamp"; type: "ulong" } |
187 | + Parameter { name: "angleDelta"; type: "QPoint" } |
188 | + Parameter { name: "modifiers"; type: "Qt::KeyboardModifiers" } |
189 | + } |
190 | + } |
191 | + Component { |
192 | + name: "MousePointer" |
193 | + defaultProperty: "data" |
194 | + prototype: "MirMousePointerInterface" |
195 | + exports: ["Cursor/MousePointer 1.0"] |
196 | + exportMetaObjectRevisions: [0] |
197 | + Signal { |
198 | + name: "pushedLeftBoundary" |
199 | + Parameter { name: "amount"; type: "double" } |
200 | + Parameter { name: "buttons"; type: "Qt::MouseButtons" } |
201 | + } |
202 | + Signal { |
203 | + name: "pushedRightBoundary" |
204 | + Parameter { name: "amount"; type: "double" } |
205 | + Parameter { name: "buttons"; type: "Qt::MouseButtons" } |
206 | + } |
207 | + Method { |
208 | + name: "handleMouseEvent" |
209 | + Parameter { name: "timestamp"; type: "ulong" } |
210 | + Parameter { name: "movement"; type: "QPointF" } |
211 | + Parameter { name: "buttons"; type: "Qt::MouseButtons" } |
212 | + Parameter { name: "modifiers"; type: "Qt::KeyboardModifiers" } |
213 | + } |
214 | + Method { |
215 | + name: "handleWheelEvent" |
216 | + Parameter { name: "timestamp"; type: "ulong" } |
217 | + Parameter { name: "angleDelta"; type: "QPoint" } |
218 | + Parameter { name: "modifiers"; type: "Qt::KeyboardModifiers" } |
219 | + } |
220 | + } |
221 | +} |
222 | |
223 | === added file 'plugins/GlobalShortcut/GlobalShortcut.qmltypes' |
224 | --- plugins/GlobalShortcut/GlobalShortcut.qmltypes 1970-01-01 00:00:00 +0000 |
225 | +++ plugins/GlobalShortcut/GlobalShortcut.qmltypes 2016-03-11 12:07:48 +0000 |
226 | @@ -0,0 +1,31 @@ |
227 | +import QtQuick.tooling 1.1 |
228 | + |
229 | +// This file describes the plugin-supplied types contained in the library. |
230 | +// It is used for QML tooling purposes only. |
231 | +// |
232 | +// This file was auto-generated by: |
233 | +// 'qmlplugindump -notrelocatable GlobalShortcut 1.0 plugins' |
234 | + |
235 | +Module { |
236 | + Component { |
237 | + name: "GlobalShortcut" |
238 | + defaultProperty: "data" |
239 | + prototype: "QQuickItem" |
240 | + exports: ["GlobalShortcut/GlobalShortcut 1.0"] |
241 | + exportMetaObjectRevisions: [0] |
242 | + Property { name: "shortcut"; type: "QVariant" } |
243 | + Property { name: "active"; type: "bool" } |
244 | + Signal { |
245 | + name: "shortcutChanged" |
246 | + Parameter { name: "shortcut"; type: "QVariant" } |
247 | + } |
248 | + Signal { |
249 | + name: "triggered" |
250 | + Parameter { name: "shortcut"; type: "string" } |
251 | + } |
252 | + Signal { |
253 | + name: "activeChanged" |
254 | + Parameter { name: "active"; type: "bool" } |
255 | + } |
256 | + } |
257 | +} |
258 | |
259 | === modified file 'plugins/Ubuntu/Gestures/CMakeLists.txt' |
260 | --- plugins/Ubuntu/Gestures/CMakeLists.txt 2015-05-27 09:37:34 +0000 |
261 | +++ plugins/Ubuntu/Gestures/CMakeLists.txt 2016-03-11 12:07:48 +0000 |
262 | @@ -11,6 +11,7 @@ |
263 | PressedOutsideNotifier.cpp |
264 | TouchDispatcher.cpp |
265 | TouchGate.cpp |
266 | + TouchGestureArea.cpp |
267 | ) |
268 | |
269 | add_definitions(-DUBUNTUGESTURESQML_LIBRARY) |
270 | |
271 | === modified file 'plugins/Ubuntu/Gestures/DirectionalDragArea.cpp' |
272 | --- plugins/Ubuntu/Gestures/DirectionalDragArea.cpp 2015-12-16 18:28:21 +0000 |
273 | +++ plugins/Ubuntu/Gestures/DirectionalDragArea.cpp 2016-03-11 12:07:48 +0000 |
274 | @@ -606,6 +606,9 @@ |
275 | Q_EMIT q->pressedChanged(true); |
276 | break; |
277 | case Recognized: |
278 | + if (oldStatus == WaitingForTouch) { // for immediate recognition |
279 | + Q_EMIT q->pressedChanged(true); |
280 | + } |
281 | Q_EMIT q->draggingChanged(true); |
282 | break; |
283 | default: |
284 | |
285 | === modified file 'plugins/Ubuntu/Gestures/Gestures.qmltypes' |
286 | --- plugins/Ubuntu/Gestures/Gestures.qmltypes 2015-02-13 09:01:16 +0000 |
287 | +++ plugins/Ubuntu/Gestures/Gestures.qmltypes 2016-03-11 12:07:48 +0000 |
288 | @@ -34,7 +34,8 @@ |
289 | "Leftwards": 1, |
290 | "Downwards": 2, |
291 | "Upwards": 3, |
292 | - "Horizontal": 4 |
293 | + "Horizontal": 4, |
294 | + "Vertical": 5 |
295 | } |
296 | } |
297 | Method { |
298 | @@ -59,14 +60,6 @@ |
299 | prototype: "QQuickItem" |
300 | exports: ["Ubuntu.Gestures/DirectionalDragArea 0.1"] |
301 | exportMetaObjectRevisions: [0] |
302 | - Enum { |
303 | - name: "Status" |
304 | - values: { |
305 | - "WaitingForTouch": 0, |
306 | - "Undecided": 1, |
307 | - "Recognized": 2 |
308 | - } |
309 | - } |
310 | Property { name: "direction"; type: "Direction::Type" } |
311 | Property { name: "distance"; type: "double"; isReadonly: true } |
312 | Property { name: "sceneDistance"; type: "double"; isReadonly: true } |
313 | @@ -74,27 +67,22 @@ |
314 | Property { name: "touchY"; type: "double"; isReadonly: true } |
315 | Property { name: "touchSceneX"; type: "double"; isReadonly: true } |
316 | Property { name: "touchSceneY"; type: "double"; isReadonly: true } |
317 | - Property { name: "status"; type: "Status"; isReadonly: true } |
318 | Property { name: "dragging"; type: "bool"; isReadonly: true } |
319 | - Property { name: "maxDeviation"; type: "double" } |
320 | - Property { name: "wideningAngle"; type: "double" } |
321 | - Property { name: "distanceThreshold"; type: "double" } |
322 | - Property { name: "minSpeed"; type: "double" } |
323 | - Property { name: "maxSilenceTime"; type: "int" } |
324 | - Property { name: "compositionTime"; type: "int" } |
325 | + Property { name: "pressed"; type: "bool"; isReadonly: true } |
326 | + Property { name: "immediateRecognition"; type: "bool" } |
327 | Signal { |
328 | name: "directionChanged" |
329 | Parameter { name: "direction"; type: "Direction::Type" } |
330 | } |
331 | Signal { |
332 | - name: "statusChanged" |
333 | - Parameter { name: "value"; type: "Status" } |
334 | - } |
335 | - Signal { |
336 | name: "draggingChanged" |
337 | Parameter { name: "value"; type: "bool" } |
338 | } |
339 | Signal { |
340 | + name: "pressedChanged" |
341 | + Parameter { name: "value"; type: "bool" } |
342 | + } |
343 | + Signal { |
344 | name: "distanceChanged" |
345 | Parameter { name: "value"; type: "double" } |
346 | } |
347 | @@ -103,30 +91,6 @@ |
348 | Parameter { name: "value"; type: "double" } |
349 | } |
350 | Signal { |
351 | - name: "maxDeviationChanged" |
352 | - Parameter { name: "value"; type: "double" } |
353 | - } |
354 | - Signal { |
355 | - name: "wideningAngleChanged" |
356 | - Parameter { name: "value"; type: "double" } |
357 | - } |
358 | - Signal { |
359 | - name: "distanceThresholdChanged" |
360 | - Parameter { name: "value"; type: "double" } |
361 | - } |
362 | - Signal { |
363 | - name: "minSpeedChanged" |
364 | - Parameter { name: "value"; type: "double" } |
365 | - } |
366 | - Signal { |
367 | - name: "maxSilenceTimeChanged" |
368 | - Parameter { name: "value"; type: "int" } |
369 | - } |
370 | - Signal { |
371 | - name: "compositionTimeChanged" |
372 | - Parameter { name: "value"; type: "int" } |
373 | - } |
374 | - Signal { |
375 | name: "touchXChanged" |
376 | Parameter { name: "value"; type: "double" } |
377 | } |
378 | @@ -142,7 +106,35 @@ |
379 | name: "touchSceneYChanged" |
380 | Parameter { name: "value"; type: "double" } |
381 | } |
382 | - Signal { name: "tapped" } |
383 | + Signal { |
384 | + name: "immediateRecognitionChanged" |
385 | + Parameter { name: "value"; type: "bool" } |
386 | + } |
387 | + Method { name: "removeTimeConstraints" } |
388 | + } |
389 | + Component { |
390 | + name: "FloatingFlickable" |
391 | + defaultProperty: "data" |
392 | + prototype: "QQuickItem" |
393 | + exports: ["Ubuntu.Gestures/FloatingFlickable 0.1"] |
394 | + exportMetaObjectRevisions: [0] |
395 | + Property { name: "contentWidth"; type: "double" } |
396 | + Property { name: "contentHeight"; type: "double" } |
397 | + Property { name: "contentX"; type: "double" } |
398 | + Property { name: "contentY"; type: "double" } |
399 | + Property { name: "direction"; type: "Direction::Type" } |
400 | + } |
401 | + Component { |
402 | + name: "GestureTouchPoint" |
403 | + prototype: "QObject" |
404 | + exports: ["Ubuntu.Gestures/GestureTouchPoint 0.1"] |
405 | + isCreatable: false |
406 | + exportMetaObjectRevisions: [0] |
407 | + Property { name: "pointId"; type: "int"; isReadonly: true } |
408 | + Property { name: "pressed"; type: "bool"; isReadonly: true } |
409 | + Property { name: "x"; type: "double"; isReadonly: true } |
410 | + Property { name: "y"; type: "double"; isReadonly: true } |
411 | + Property { name: "dragging"; type: "bool"; isReadonly: true } |
412 | } |
413 | Component { |
414 | name: "PressedOutsideNotifier" |
415 | @@ -163,6 +155,55 @@ |
416 | name: "targetItemChanged" |
417 | Parameter { name: "item"; type: "QQuickItem"; isPointer: true } |
418 | } |
419 | - Signal { name: "pressed" } |
420 | + } |
421 | + Component { |
422 | + name: "TouchGestureArea" |
423 | + defaultProperty: "data" |
424 | + prototype: "QQuickItem" |
425 | + exports: ["Ubuntu.Gestures/TouchGestureArea 0.1"] |
426 | + exportMetaObjectRevisions: [0] |
427 | + Enum { |
428 | + name: "Status" |
429 | + values: { |
430 | + "WaitingForTouch": 0, |
431 | + "Undecided": 1, |
432 | + "Recognized": 2, |
433 | + "Rejected": 3 |
434 | + } |
435 | + } |
436 | + Property { name: "touchPoints"; type: "GestureTouchPoint"; isList: true; isReadonly: true } |
437 | + Property { name: "dragging"; type: "bool"; isReadonly: true } |
438 | + Property { name: "minimumTouchPoints"; type: "int" } |
439 | + Property { name: "maximumTouchPoints"; type: "int" } |
440 | + Signal { |
441 | + name: "statusChanged" |
442 | + Parameter { name: "status"; type: "Status" } |
443 | + } |
444 | + Signal { name: "touchPointsUpdated" } |
445 | + Signal { |
446 | + name: "draggingChanged" |
447 | + Parameter { name: "dragging"; type: "bool" } |
448 | + } |
449 | + Signal { |
450 | + name: "minimumTouchPointsChanged" |
451 | + Parameter { name: "value"; type: "bool" } |
452 | + } |
453 | + Signal { |
454 | + name: "maximumTouchPointsChanged" |
455 | + Parameter { name: "value"; type: "bool" } |
456 | + } |
457 | + Signal { |
458 | + name: "pressed" |
459 | + Parameter { name: "points"; type: "QList<QObject*>" } |
460 | + } |
461 | + Signal { |
462 | + name: "released" |
463 | + Parameter { name: "points"; type: "QList<QObject*>" } |
464 | + } |
465 | + Signal { |
466 | + name: "updated" |
467 | + Parameter { name: "points"; type: "QList<QObject*>" } |
468 | + } |
469 | + Signal { name: "clicked" } |
470 | } |
471 | } |
472 | |
473 | === added file 'plugins/Ubuntu/Gestures/TouchGestureArea.cpp' |
474 | --- plugins/Ubuntu/Gestures/TouchGestureArea.cpp 1970-01-01 00:00:00 +0000 |
475 | +++ plugins/Ubuntu/Gestures/TouchGestureArea.cpp 2016-03-11 12:07:48 +0000 |
476 | @@ -0,0 +1,875 @@ |
477 | +/* |
478 | + * Copyright (C) 2016 Canonical, Ltd. |
479 | + * |
480 | + * This program is free software; you can redistribute it and/or modify |
481 | + * it under the terms of the GNU General Public License as published by |
482 | + * the Free Software Foundation; version 3. |
483 | + * |
484 | + * This program is distributed in the hope that it will be useful, |
485 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
486 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
487 | + * GNU General Public License for more details. |
488 | + * |
489 | + * You should have received a copy of the GNU General Public License |
490 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
491 | + */ |
492 | + |
493 | +#include "TouchGestureArea.h" |
494 | + |
495 | +// local |
496 | +#include "TouchOwnershipEvent.h" |
497 | +#include "TouchRegistry.h" |
498 | +#include "UnownedTouchEvent.h" |
499 | + |
500 | +#include <QGuiApplication> |
501 | +#include <QStyleHints> |
502 | +#include <private/qquickwindow_p.h> |
503 | + |
504 | +#define TOUCHGESTUREAREA_DEBUG 0 |
505 | + |
506 | +// TODO - understand more about why we lose touch event releases. |
507 | +// Workaround for now is to monitor all the touch points from first touch till |
508 | +// all have been released; no matter if we've rejected the gesture. |
509 | + |
510 | +namespace { |
511 | + |
512 | +struct InternalStatus { |
513 | + enum Status { |
514 | + WaitingForTouch, |
515 | + WaitingForMoreTouches, |
516 | + WaitingForOwnership, //Recognizing, |
517 | + Recognized, |
518 | + WaitingForRejection, |
519 | + Rejected |
520 | + }; |
521 | +}; |
522 | + |
523 | +TouchGestureArea::Status internalStatusToGestureStatus(int internalStatus) { |
524 | + switch (internalStatus) { |
525 | + case InternalStatus::WaitingForTouch: return TouchGestureArea::WaitingForTouch; |
526 | + case InternalStatus::WaitingForMoreTouches: return TouchGestureArea::Undecided; |
527 | + case InternalStatus::WaitingForOwnership: return TouchGestureArea::Undecided; |
528 | + case InternalStatus::Recognized: return TouchGestureArea::Recognized; |
529 | + case InternalStatus::WaitingForRejection: return TouchGestureArea::Recognized; |
530 | + case InternalStatus::Rejected: return TouchGestureArea::Rejected; |
531 | + } |
532 | + return TouchGestureArea::WaitingForTouch; |
533 | +} |
534 | + |
535 | +} |
536 | + |
537 | +#if TOUCHGESTUREAREA_DEBUG |
538 | +#define tgaDebug(params) qDebug().nospace() << "[TGA(" << qPrintable(objectName()) << ")] " << params |
539 | +#include "DebugHelpers.h" |
540 | + |
541 | +namespace { |
542 | + |
543 | +const char *statusToString(int status) |
544 | +{ |
545 | + if (status == InternalStatus::WaitingForTouch) { |
546 | + return "WaitingForTouch"; |
547 | + } else if (status == InternalStatus::WaitingForMoreTouches) { |
548 | + return "WaitingForMoreTouches"; |
549 | + } else if (status == InternalStatus::WaitingForOwnership) { |
550 | + return "WaitingForOwnership"; |
551 | + } else if (status == InternalStatus::Rejected) { |
552 | + return "Rejected"; |
553 | + } else if (status == InternalStatus::WaitingForRejection) { |
554 | + return "WaitingForRejection"; |
555 | + } else { |
556 | + return "Recognized"; |
557 | + } |
558 | + return "Unknown"; |
559 | +} |
560 | + |
561 | +QString touchState(Qt::TouchPointState state) { |
562 | + switch (state) { |
563 | + case Qt::TouchPointPressed: return "pressed"; |
564 | + case Qt::TouchPointMoved: return "moved"; |
565 | + case Qt::TouchPointStationary: return "stationary"; |
566 | + case Qt::TouchPointReleased: return "released"; |
567 | + break; |
568 | + } |
569 | + return "unknown"; |
570 | +} |
571 | + |
572 | +QString touchesString(const QList<QObject*> touches) { |
573 | + QString str; |
574 | + Q_FOREACH(QObject* object, touches) { |
575 | + GestureTouchPoint* touchPoint = qobject_cast<GestureTouchPoint*>(object); |
576 | + if (touchPoint) { |
577 | + str += QStringLiteral("[%1 @ (%2, %3)], ").arg(touchPoint->id()) |
578 | + .arg(touchPoint->x()) |
579 | + .arg(touchPoint->y()); |
580 | + } |
581 | + } |
582 | + return str; |
583 | +} |
584 | + |
585 | +QString touchEventString(QTouchEvent* event) { |
586 | + if (!event) return QString(); |
587 | + QString str; |
588 | + Q_FOREACH(const auto& touchPoint, event->touchPoints()) { |
589 | + str += QStringLiteral("[%1:%2 @ (%3, %4)], ").arg(touchPoint.id()) |
590 | + .arg(touchState(touchPoint.state())) |
591 | + .arg(touchPoint.pos().x()) |
592 | + .arg(touchPoint.pos().y()); |
593 | + } |
594 | + return str; |
595 | +} |
596 | + |
597 | + |
598 | +} // namespace { |
599 | +#else // TOUCHGESTUREAREA_DEBUG |
600 | +#define tgaDebug(params) ((void)0) |
601 | +#endif // TOUCHGESTUREAREA_DEBUG |
602 | + |
603 | +TouchGestureArea::TouchGestureArea(QQuickItem* parent) |
604 | + : QQuickItem(parent) |
605 | + , m_status(WaitingForTouch) |
606 | + , m_recognitionTimer(nullptr) |
607 | + , m_dragging(false) |
608 | + , m_minimumTouchPoints(1) |
609 | + , m_maximumTouchPoints(INT_MAX) |
610 | + , m_recognitionPeriod(50) |
611 | + , m_releaseRejectPeriod(100) |
612 | +{ |
613 | + setRecognitionTimer(new UbuntuGestures::Timer(this)); |
614 | + m_recognitionTimer->setInterval(m_recognitionPeriod); |
615 | + m_recognitionTimer->setSingleShot(true); |
616 | +} |
617 | + |
618 | +TouchGestureArea::~TouchGestureArea() |
619 | +{ |
620 | + clearTouchLists(); |
621 | + qDeleteAll(m_liveTouchPoints); |
622 | + m_liveTouchPoints.clear(); |
623 | + qDeleteAll(m_cachedTouchPoints); |
624 | + m_cachedTouchPoints.clear(); |
625 | +} |
626 | + |
627 | +bool TouchGestureArea::event(QEvent *event) |
628 | +{ |
629 | + // Process unowned touch events (handles update/release for incomplete gestures) |
630 | + if (event->type() == TouchOwnershipEvent::touchOwnershipEventType()) { |
631 | + touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(event)); |
632 | + return true; |
633 | + } else if (event->type() == UnownedTouchEvent::unownedTouchEventType()) { |
634 | + unownedTouchEvent(static_cast<UnownedTouchEvent *>(event)->touchEvent()); |
635 | + return true; |
636 | + } |
637 | + |
638 | + return QQuickItem::event(event); |
639 | +} |
640 | + |
641 | +void TouchGestureArea::touchOwnershipEvent(TouchOwnershipEvent *event) |
642 | +{ |
643 | + int touchId = event->touchId(); |
644 | + tgaDebug("touchOwnershipEvent - id:" << touchId << ", gained:" << event->gained()); |
645 | + |
646 | + if (event->gained()) { |
647 | + grabTouchPoints(QVector<int>() << touchId); |
648 | + m_candidateTouches.remove(touchId); |
649 | + TouchRegistry::instance()->addTouchWatcher(touchId, this); |
650 | + m_watchedTouches.insert(touchId); |
651 | + |
652 | + if (m_watchedTouches.count() >= m_minimumTouchPoints) { |
653 | + setInternalStatus(InternalStatus::Recognized); |
654 | + } |
655 | + } else { |
656 | + rejectGesture(); |
657 | + } |
658 | +} |
659 | + |
660 | +void TouchGestureArea::touchEvent(QTouchEvent *event) |
661 | +{ |
662 | + if (!isEnabled() || !isVisible()) { |
663 | + tgaDebug(QString("NOT ENABLED touchEvent(%1) %2").arg(statusToString(m_status)).arg(touchEventString(event))); |
664 | + QQuickItem::touchEvent(event); |
665 | + return; |
666 | + } |
667 | + |
668 | + tgaDebug(QString("touchEvent(%1) %2").arg(statusToString(m_status)).arg(touchEventString(event))); |
669 | + |
670 | + switch (m_status) { |
671 | + case InternalStatus::WaitingForTouch: |
672 | + touchEvent_waitingForTouch(event); |
673 | + break; |
674 | + case InternalStatus::WaitingForMoreTouches: |
675 | + touchEvent_waitingForMoreTouches(event); |
676 | + break; |
677 | + case InternalStatus::WaitingForOwnership: |
678 | + touchEvent_waitingForOwnership(event); |
679 | + break; |
680 | + case InternalStatus::Recognized: |
681 | + case InternalStatus::WaitingForRejection: |
682 | + touchEvent_recognized(event); |
683 | + break; |
684 | + case InternalStatus::Rejected: |
685 | + touchEvent_rejected(event); |
686 | + break; |
687 | + default: // Recognized: |
688 | + break; |
689 | + } |
690 | + |
691 | + updateTouchPoints(event); |
692 | +} |
693 | + |
694 | +void TouchGestureArea::touchEvent_waitingForTouch(QTouchEvent *event) |
695 | +{ |
696 | + Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) { |
697 | + Qt::TouchPointState touchPointState = touchPoint.state(); |
698 | + int touchId = touchPoint.id(); |
699 | + |
700 | + if (touchPointState == Qt::TouchPointPressed) { |
701 | + if (!m_candidateTouches.contains(touchId)) { |
702 | + TouchRegistry::instance()->addCandidateOwnerForTouch(touchId, this); |
703 | + m_candidateTouches.insert(touchId); |
704 | + } |
705 | + } |
706 | + } |
707 | + event->ignore(); |
708 | + |
709 | + if (m_candidateTouches.count() > m_maximumTouchPoints) { |
710 | + rejectGesture(); |
711 | + } else if (m_candidateTouches.count() >= m_minimumTouchPoints) { |
712 | + setInternalStatus(InternalStatus::WaitingForOwnership); |
713 | + |
714 | + QSet<int> tmpCandidates(m_candidateTouches); |
715 | + Q_FOREACH(int candidateTouchId, tmpCandidates) { |
716 | + TouchRegistry::instance()->requestTouchOwnership(candidateTouchId, this); |
717 | + } |
718 | + // We accept the gesture; so don't pass to lower items |
719 | + event->accept(); |
720 | + } else if (m_candidateTouches.count() > 0) { |
721 | + setInternalStatus(InternalStatus::WaitingForMoreTouches); |
722 | + } |
723 | +} |
724 | + |
725 | +void TouchGestureArea::touchEvent_waitingForMoreTouches(QTouchEvent *event) |
726 | +{ |
727 | + Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) { |
728 | + Qt::TouchPointState touchPointState = touchPoint.state(); |
729 | + int touchId = touchPoint.id(); |
730 | + |
731 | + if (touchPointState == Qt::TouchPointPressed) { |
732 | + if (!m_candidateTouches.contains(touchId)) { |
733 | + TouchRegistry::instance()->addCandidateOwnerForTouch(touchId, this); |
734 | + m_candidateTouches.insert(touchId); |
735 | + } |
736 | + } |
737 | + } |
738 | + event->ignore(); |
739 | + |
740 | + if (m_candidateTouches.count() > m_maximumTouchPoints) { |
741 | + rejectGesture(); |
742 | + } else if (m_candidateTouches.count() >= m_minimumTouchPoints) { |
743 | + setInternalStatus(InternalStatus::WaitingForOwnership); |
744 | + |
745 | + QSet<int> tmpCandidates(m_candidateTouches); |
746 | + Q_FOREACH(int candidateTouchId, tmpCandidates) { |
747 | + TouchRegistry::instance()->requestTouchOwnership(candidateTouchId, this); |
748 | + } |
749 | + // We accept the gesture; so don't pass to lower items |
750 | + event->accept(); |
751 | + } |
752 | +} |
753 | + |
754 | +void TouchGestureArea::touchEvent_waitingForOwnership(QTouchEvent *event) |
755 | +{ |
756 | + Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) { |
757 | + Qt::TouchPointState touchPointState = touchPoint.state(); |
758 | + int touchId = touchPoint.id(); |
759 | + |
760 | + if (touchPointState == Qt::TouchPointPressed) { |
761 | + if (!m_watchedTouches.contains(touchId)) { |
762 | + TouchRegistry::instance()->addTouchWatcher(touchId, this); |
763 | + m_watchedTouches.insert(touchId); |
764 | + } |
765 | + } |
766 | + } |
767 | +} |
768 | + |
769 | +void TouchGestureArea::touchEvent_recognized(QTouchEvent *event) |
770 | +{ |
771 | + Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) { |
772 | + Qt::TouchPointState touchPointState = touchPoint.state(); |
773 | + int touchId = touchPoint.id(); |
774 | + |
775 | + if (touchPointState == Qt::TouchPointPressed) { |
776 | + if (!m_watchedTouches.contains(touchId)) { |
777 | + TouchRegistry::instance()->addTouchWatcher(touchId, this); |
778 | + m_watchedTouches.insert(touchId); |
779 | + } |
780 | + } |
781 | + } |
782 | + |
783 | + if (m_watchedTouches.count() > m_maximumTouchPoints) { |
784 | + rejectGesture(); |
785 | + } else if (m_watchedTouches.count() >= m_minimumTouchPoints && |
786 | + m_status==InternalStatus::WaitingForRejection) { |
787 | + setInternalStatus(InternalStatus::Recognized); |
788 | + } |
789 | +} |
790 | + |
791 | +void TouchGestureArea::touchEvent_rejected(QTouchEvent *event) |
792 | +{ |
793 | + Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) { |
794 | + Qt::TouchPointState touchPointState = touchPoint.state(); |
795 | + int touchId = touchPoint.id(); |
796 | + |
797 | + if (touchPointState == Qt::TouchPointPressed) { |
798 | + if (!m_watchedTouches.contains(touchId)) { |
799 | + TouchRegistry::instance()->addTouchWatcher(touchId, this); |
800 | + m_watchedTouches.insert(touchId); |
801 | + } |
802 | + } |
803 | + } |
804 | +} |
805 | + |
806 | +void TouchGestureArea::unownedTouchEvent(QTouchEvent *unownedTouchEvent) |
807 | +{ |
808 | + tgaDebug(QString("unownedTouchEvent(%1) %2").arg(statusToString(m_status)).arg(touchEventString(unownedTouchEvent))); |
809 | + |
810 | + // Only monitor unowned touch events for presses/releases |
811 | + if ((unownedTouchEvent->touchPointStates() & (Qt::TouchPointPressed|Qt::TouchPointReleased)) == 0) { |
812 | + return; |
813 | + } |
814 | + |
815 | + switch (m_status) { |
816 | + case InternalStatus::WaitingForTouch: |
817 | + break; |
818 | + case InternalStatus::WaitingForMoreTouches: |
819 | + unownedTouchEvent_waitingForMoreTouches(unownedTouchEvent); |
820 | + // do nothing |
821 | + break; |
822 | + case InternalStatus::WaitingForOwnership: |
823 | + unownedTouchEvent_waitingForOwnership(unownedTouchEvent); |
824 | + break; |
825 | + case InternalStatus::Recognized: |
826 | + case InternalStatus::WaitingForRejection: |
827 | + unownedTouchEvent_recognised(unownedTouchEvent); |
828 | + break; |
829 | + case InternalStatus::Rejected: |
830 | + unownedTouchEvent_rejected(unownedTouchEvent); |
831 | + break; |
832 | + default: |
833 | + break; |
834 | + } |
835 | + |
836 | + updateTouchPoints(unownedTouchEvent); |
837 | +} |
838 | + |
839 | +void TouchGestureArea::unownedTouchEvent_waitingForMoreTouches(QTouchEvent *event) |
840 | +{ |
841 | + Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) { |
842 | + Qt::TouchPointState touchPointState = touchPoint.state(); |
843 | + int touchId = touchPoint.id(); |
844 | + |
845 | + if (touchPointState == Qt::TouchPointReleased) { |
846 | + if (m_candidateTouches.contains(touchId)) { |
847 | + TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, this); |
848 | + m_candidateTouches.remove(touchId); |
849 | + } |
850 | + } |
851 | + } |
852 | + |
853 | + if (m_candidateTouches.count() == 0) { |
854 | + setInternalStatus(InternalStatus::WaitingForTouch); |
855 | + } |
856 | +} |
857 | + |
858 | +void TouchGestureArea::unownedTouchEvent_waitingForOwnership(QTouchEvent *event) |
859 | +{ |
860 | + Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) { |
861 | + Qt::TouchPointState touchPointState = touchPoint.state(); |
862 | + int touchId = touchPoint.id(); |
863 | + |
864 | + if (touchPointState == Qt::TouchPointReleased) { |
865 | + if (m_candidateTouches.contains(touchId)) { |
866 | + TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, this); |
867 | + m_candidateTouches.remove(touchId); |
868 | + } |
869 | + if (m_watchedTouches.contains(touchId)) { |
870 | + m_watchedTouches.remove(touchId); |
871 | + } |
872 | + } |
873 | + } |
874 | + |
875 | + if (m_candidateTouches.count() + m_watchedTouches.count() == 0) { |
876 | + setInternalStatus(InternalStatus::WaitingForTouch); |
877 | + } |
878 | +} |
879 | + |
880 | +void TouchGestureArea::unownedTouchEvent_recognised(QTouchEvent *event) |
881 | +{ |
882 | + Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) { |
883 | + Qt::TouchPointState touchPointState = touchPoint.state(); |
884 | + int touchId = touchPoint.id(); |
885 | + |
886 | + if (touchPointState == Qt::TouchPointReleased) { |
887 | + if (m_watchedTouches.contains(touchId)) { |
888 | + m_watchedTouches.remove(touchId); |
889 | + } |
890 | + } |
891 | + } |
892 | + |
893 | + if (m_watchedTouches.count() < m_minimumTouchPoints && m_status==InternalStatus::Recognized) { |
894 | + setInternalStatus(InternalStatus::WaitingForRejection); |
895 | + } |
896 | +} |
897 | + |
898 | +void TouchGestureArea::unownedTouchEvent_rejected(QTouchEvent *event) |
899 | +{ |
900 | + Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, event->touchPoints()) { |
901 | + Qt::TouchPointState touchPointState = touchPoint.state(); |
902 | + int touchId = touchPoint.id(); |
903 | + |
904 | + if (touchPointState == Qt::TouchPointPressed) { |
905 | + if (!m_watchedTouches.contains(touchId)) { |
906 | + TouchRegistry::instance()->addTouchWatcher(touchId, this); |
907 | + m_watchedTouches.insert(touchId); |
908 | + } |
909 | + } |
910 | + if (touchPointState == Qt::TouchPointReleased) { |
911 | + if (m_watchedTouches.contains(touchId)) { |
912 | + m_watchedTouches.remove(touchId); |
913 | + } |
914 | + } |
915 | + } |
916 | + |
917 | + if (m_watchedTouches.count() == 0) { |
918 | + setInternalStatus(InternalStatus::WaitingForTouch); |
919 | + } |
920 | +} |
921 | + |
922 | +void TouchGestureArea::updateTouchPoints(QTouchEvent *touchEvent) |
923 | +{ |
924 | + bool added = false; |
925 | + bool ended = false; |
926 | + bool moved = false; |
927 | + |
928 | + const int dragThreshold = qApp->styleHints()->startDragDistance(); |
929 | + const int dragVelocity = qApp->styleHints()->startDragVelocity(); |
930 | + |
931 | + clearTouchLists(); |
932 | + bool updateable = m_status != InternalStatus::WaitingForRejection; |
933 | + |
934 | + Q_FOREACH(const QTouchEvent::TouchPoint& touchPoint, touchEvent->touchPoints()) { |
935 | + Qt::TouchPointState touchPointState = touchPoint.state(); |
936 | + int touchId = touchPoint.id(); |
937 | + |
938 | + if (touchPointState & Qt::TouchPointReleased) { |
939 | + GestureTouchPoint* gtp = m_liveTouchPoints.value(touchId); |
940 | + if (!gtp) continue; |
941 | + |
942 | + gtp->setPos(touchPoint.pos()); |
943 | + gtp->setPressed(false); |
944 | + m_releasedTouchPoints.append(gtp); |
945 | + m_liveTouchPoints.remove(touchId); |
946 | + |
947 | + if (updateable) { |
948 | + if (m_cachedTouchPoints.contains(touchId)) { |
949 | + GestureTouchPoint* cachedPoint = m_cachedTouchPoints.take(touchId); |
950 | + cachedPoint->deleteLater(); |
951 | + } |
952 | + } |
953 | + ended = true; |
954 | + } else { |
955 | + GestureTouchPoint* gtp = m_liveTouchPoints.value(touchPoint.id(), nullptr); |
956 | + if (!gtp) { |
957 | + gtp = addTouchPoint(&touchPoint); |
958 | + m_pressedTouchPoints.append(gtp); |
959 | + |
960 | + if (updateable) { |
961 | + if (m_cachedTouchPoints.contains(touchId)) { |
962 | + m_cachedTouchPoints[touchId]->setPos(touchPoint.pos()); |
963 | + } else { |
964 | + m_cachedTouchPoints[touchId] = new GestureTouchPoint(*gtp); |
965 | + } |
966 | + } |
967 | + added = true; |
968 | + } else if (touchPointState & Qt::TouchPointMoved) { |
969 | + gtp->setPos(touchPoint.pos()); |
970 | + m_movedTouchPoints.append(gtp); |
971 | + moved = true; |
972 | + |
973 | + const QPointF ¤tPos = touchPoint.scenePos(); |
974 | + const QPointF &startPos = touchPoint.startScenePos(); |
975 | + |
976 | + bool overDragThreshold = false; |
977 | + bool supportsVelocity = (touchEvent->device()->capabilities() & QTouchDevice::Velocity) && dragVelocity; |
978 | + overDragThreshold |= qAbs(currentPos.x() - startPos.x()) > dragThreshold || |
979 | + qAbs(currentPos.y() - startPos.y()) > dragThreshold; |
980 | + if (supportsVelocity) { |
981 | + QVector2D velocityVec = touchPoint.velocity(); |
982 | + overDragThreshold |= qAbs(velocityVec.x()) > dragVelocity; |
983 | + overDragThreshold |= qAbs(velocityVec.y()) > dragVelocity; |
984 | + } |
985 | + |
986 | + if (overDragThreshold) { |
987 | + gtp->setDragging(true); |
988 | + } |
989 | + |
990 | + if (updateable) { |
991 | + if (m_cachedTouchPoints.contains(touchId)) { |
992 | + m_cachedTouchPoints[touchId]->setPos(touchPoint.pos()); |
993 | + if (overDragThreshold) { |
994 | + m_cachedTouchPoints[touchId]->setDragging(true); |
995 | + } |
996 | + } |
997 | + } |
998 | + } |
999 | + } |
1000 | + } |
1001 | + |
1002 | + if (updateable) { |
1003 | + if (!dragging() && m_status == InternalStatus::Recognized) { |
1004 | + bool allWantDrag = !m_liveTouchPoints.isEmpty(); |
1005 | + Q_FOREACH(auto point, m_liveTouchPoints) { |
1006 | + allWantDrag &= point->dragging(); |
1007 | + } |
1008 | + // only dragging if all points are dragging. |
1009 | + if (allWantDrag) { |
1010 | + setDragging(true); |
1011 | + } |
1012 | + } |
1013 | + |
1014 | + if (ended) { |
1015 | + if (m_liveTouchPoints.isEmpty()) { |
1016 | + if (!dragging()) Q_EMIT clicked(); |
1017 | + setDragging(false); |
1018 | + } |
1019 | + tgaDebug("Released " << touchesString(m_releasedTouchPoints)); |
1020 | + Q_EMIT released(m_releasedTouchPoints); |
1021 | + } |
1022 | + if (added) { |
1023 | + tgaDebug("Pressed " << touchesString(m_pressedTouchPoints)); |
1024 | + Q_EMIT pressed(m_pressedTouchPoints); |
1025 | + } |
1026 | + if (moved) { |
1027 | + tgaDebug("Updated " << touchesString(m_movedTouchPoints)); |
1028 | + Q_EMIT updated(m_movedTouchPoints); |
1029 | + } |
1030 | + if (added || ended || moved) { |
1031 | + Q_EMIT touchPointsUpdated(); |
1032 | + } |
1033 | + } |
1034 | +} |
1035 | + |
1036 | +void TouchGestureArea::clearTouchLists() |
1037 | +{ |
1038 | + Q_FOREACH (QObject *gtp, m_releasedTouchPoints) { |
1039 | + delete gtp; |
1040 | + } |
1041 | + m_releasedTouchPoints.clear(); |
1042 | + m_pressedTouchPoints.clear(); |
1043 | + m_movedTouchPoints.clear(); |
1044 | +} |
1045 | + |
1046 | +void TouchGestureArea::setInternalStatus(uint newStatus) |
1047 | +{ |
1048 | + if (newStatus == m_status) |
1049 | + return; |
1050 | + |
1051 | + uint oldStatus = m_status; |
1052 | + |
1053 | + m_status = newStatus; |
1054 | + Q_EMIT statusChanged(status()); |
1055 | + |
1056 | + if (oldStatus == InternalStatus::WaitingForMoreTouches || oldStatus == InternalStatus::WaitingForRejection) { |
1057 | + m_recognitionTimer->stop(); |
1058 | + } |
1059 | + |
1060 | + tgaDebug(statusToString(oldStatus) << " -> " << statusToString(newStatus)); |
1061 | + |
1062 | + switch (newStatus) { |
1063 | + case InternalStatus::WaitingForTouch: |
1064 | + resyncCachedTouchPoints(); |
1065 | + break; |
1066 | + case InternalStatus::WaitingForMoreTouches: |
1067 | + m_recognitionTimer->start(m_recognitionPeriod); |
1068 | + break; |
1069 | + case InternalStatus::Recognized: |
1070 | + resyncCachedTouchPoints(); |
1071 | + break; |
1072 | + case InternalStatus::WaitingForRejection: |
1073 | + m_recognitionTimer->start(m_releaseRejectPeriod); |
1074 | + break; |
1075 | + case InternalStatus::Rejected: |
1076 | + resyncCachedTouchPoints(); |
1077 | + break; |
1078 | + default: |
1079 | + // no-op |
1080 | + break; |
1081 | + } |
1082 | +} |
1083 | + |
1084 | +void TouchGestureArea::setRecognitionTimer(UbuntuGestures::AbstractTimer *timer) |
1085 | +{ |
1086 | + int interval = 0; |
1087 | + bool timerWasRunning = false; |
1088 | + bool wasSingleShot = false; |
1089 | + |
1090 | + // can be null when called from the constructor |
1091 | + if (m_recognitionTimer) { |
1092 | + interval = m_recognitionTimer->interval(); |
1093 | + timerWasRunning = m_recognitionTimer->isRunning(); |
1094 | + if (m_recognitionTimer->parent() == this) { |
1095 | + delete m_recognitionTimer; |
1096 | + } |
1097 | + } |
1098 | + |
1099 | + m_recognitionTimer = timer; |
1100 | + timer->setInterval(interval); |
1101 | + timer->setSingleShot(wasSingleShot); |
1102 | + connect(timer, SIGNAL(timeout()), |
1103 | + this, SLOT(rejectGesture())); |
1104 | + if (timerWasRunning) { |
1105 | + m_recognitionTimer->start(); |
1106 | + } |
1107 | +} |
1108 | + |
1109 | +int TouchGestureArea::status() const |
1110 | +{ |
1111 | + return internalStatusToGestureStatus(m_status); |
1112 | +} |
1113 | + |
1114 | +bool TouchGestureArea::dragging() const |
1115 | +{ |
1116 | + return m_dragging; |
1117 | +} |
1118 | + |
1119 | +QQmlListProperty<GestureTouchPoint> TouchGestureArea::touchPoints() |
1120 | +{ |
1121 | + return QQmlListProperty<GestureTouchPoint>(this, |
1122 | + 0, |
1123 | + nullptr, |
1124 | + TouchGestureArea::touchPoint_count, |
1125 | + TouchGestureArea::touchPoint_at, |
1126 | + 0); |
1127 | +} |
1128 | + |
1129 | +int TouchGestureArea::minimumTouchPoints() const |
1130 | +{ |
1131 | + return m_minimumTouchPoints; |
1132 | +} |
1133 | + |
1134 | +void TouchGestureArea::setMinimumTouchPoints(int value) |
1135 | +{ |
1136 | + if (m_minimumTouchPoints != value) { |
1137 | + m_minimumTouchPoints = value; |
1138 | + Q_EMIT minimumTouchPointsChanged(value); |
1139 | + } |
1140 | +} |
1141 | + |
1142 | +int TouchGestureArea::maximumTouchPoints() const |
1143 | +{ |
1144 | + return m_maximumTouchPoints; |
1145 | +} |
1146 | + |
1147 | +void TouchGestureArea::setMaximumTouchPoints(int value) |
1148 | +{ |
1149 | + if (m_maximumTouchPoints != value) { |
1150 | + m_maximumTouchPoints = value; |
1151 | + Q_EMIT maximumTouchPointsChanged(value); |
1152 | + } |
1153 | +} |
1154 | + |
1155 | +int TouchGestureArea::recognitionPeriod() const |
1156 | +{ |
1157 | + return m_recognitionPeriod; |
1158 | +} |
1159 | + |
1160 | +void TouchGestureArea::setRecognitionPeriod(int value) |
1161 | +{ |
1162 | + if (value != m_recognitionPeriod) { |
1163 | + m_recognitionPeriod = value; |
1164 | + Q_EMIT recognitionPeriodChanged(value); |
1165 | + } |
1166 | +} |
1167 | + |
1168 | +int TouchGestureArea::releaseRejectPeriod() const |
1169 | +{ |
1170 | + return m_releaseRejectPeriod; |
1171 | +} |
1172 | + |
1173 | +void TouchGestureArea::setReleaseRejectPeriod(int value) |
1174 | +{ |
1175 | + if (value != m_releaseRejectPeriod) { |
1176 | + m_releaseRejectPeriod = value; |
1177 | + Q_EMIT releaseRejectPeriodChanged(value); |
1178 | + } |
1179 | +} |
1180 | + |
1181 | +void TouchGestureArea::rejectGesture() |
1182 | +{ |
1183 | + tgaDebug("rejectGesture"); |
1184 | + ungrabTouchPoints(); |
1185 | + |
1186 | + Q_FOREACH(int touchId, m_candidateTouches) { |
1187 | + TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, this); |
1188 | + } |
1189 | + |
1190 | + // Monitor the candidates |
1191 | + Q_FOREACH(int touchId, m_candidateTouches) { |
1192 | + TouchRegistry::instance()->addTouchWatcher(touchId, this); |
1193 | + m_watchedTouches.insert(touchId); |
1194 | + } |
1195 | + m_candidateTouches.clear(); |
1196 | + |
1197 | + if (m_watchedTouches.count() == 0) { |
1198 | + setInternalStatus(InternalStatus::WaitingForTouch); |
1199 | + } else { |
1200 | + setInternalStatus(InternalStatus::Rejected); |
1201 | + } |
1202 | +} |
1203 | + |
1204 | +void TouchGestureArea::resyncCachedTouchPoints() |
1205 | +{ |
1206 | + clearTouchLists(); |
1207 | + |
1208 | + bool added = false; |
1209 | + bool ended = false; |
1210 | + bool moved = false; |
1211 | + bool wantsDrag = false; |
1212 | + |
1213 | + // list of deletes |
1214 | + QMutableHashIterator<int, GestureTouchPoint*> removeIter(m_cachedTouchPoints); |
1215 | + while(removeIter.hasNext()) { |
1216 | + removeIter.next(); |
1217 | + if (!m_liveTouchPoints.contains(removeIter.key())) { |
1218 | + m_releasedTouchPoints.append(removeIter.value()); |
1219 | + removeIter.remove(); |
1220 | + ended = true; |
1221 | + } |
1222 | + } |
1223 | + |
1224 | + // list of adds/moves |
1225 | + Q_FOREACH(GestureTouchPoint* touchPoint, m_liveTouchPoints) { |
1226 | + if (m_cachedTouchPoints.contains(touchPoint->id())) { |
1227 | + GestureTouchPoint* cachedPoint = m_cachedTouchPoints[touchPoint->id()]; |
1228 | + |
1229 | + if (*cachedPoint != *touchPoint) { |
1230 | + *cachedPoint = *touchPoint; |
1231 | + m_movedTouchPoints.append(touchPoint); |
1232 | + moved = true; |
1233 | + } |
1234 | + } else { |
1235 | + m_cachedTouchPoints.insert(touchPoint->id(), new GestureTouchPoint(*touchPoint)); |
1236 | + m_pressedTouchPoints.append(touchPoint); |
1237 | + added = true; |
1238 | + } |
1239 | + } |
1240 | + |
1241 | + if (wantsDrag && !dragging()) { |
1242 | + setDragging(true); |
1243 | + } |
1244 | + |
1245 | + if (ended) { |
1246 | + if (m_cachedTouchPoints.isEmpty()) { |
1247 | + if (!dragging()) Q_EMIT clicked(); |
1248 | + setDragging(false); |
1249 | + } |
1250 | + tgaDebug("Cached Release " << touchesString(m_releasedTouchPoints)); |
1251 | + Q_EMIT released(m_releasedTouchPoints); |
1252 | + } |
1253 | + if (added) { |
1254 | + tgaDebug("Cached Press " << touchesString(m_pressedTouchPoints)); |
1255 | + Q_EMIT pressed(m_pressedTouchPoints); |
1256 | + } |
1257 | + if (moved) { |
1258 | + tgaDebug("Cached Update " << touchesString(m_movedTouchPoints)); |
1259 | + Q_EMIT updated(m_movedTouchPoints); |
1260 | + } |
1261 | + if (added || ended || moved) Q_EMIT touchPointsUpdated(); |
1262 | +} |
1263 | + |
1264 | +int TouchGestureArea::touchPoint_count(QQmlListProperty<GestureTouchPoint> *list) |
1265 | +{ |
1266 | + TouchGestureArea *q = static_cast<TouchGestureArea*>(list->object); |
1267 | + return q->m_cachedTouchPoints.count(); |
1268 | +} |
1269 | + |
1270 | +GestureTouchPoint *TouchGestureArea::touchPoint_at(QQmlListProperty<GestureTouchPoint> *list, int index) |
1271 | +{ |
1272 | + TouchGestureArea *q = static_cast<TouchGestureArea*>(list->object); |
1273 | + return (q->m_cachedTouchPoints.begin()+index).value(); |
1274 | +} |
1275 | + |
1276 | +GestureTouchPoint* TouchGestureArea::addTouchPoint(QTouchEvent::TouchPoint const* tp) |
1277 | +{ |
1278 | + GestureTouchPoint* gtp = new GestureTouchPoint(); |
1279 | + gtp->setId(tp->id()); |
1280 | + gtp->setPressed(true); |
1281 | + gtp->setPos(tp->pos()); |
1282 | + m_liveTouchPoints.insert(tp->id(), gtp); |
1283 | + return gtp; |
1284 | +} |
1285 | + |
1286 | +void TouchGestureArea::itemChange(ItemChange change, const ItemChangeData &value) |
1287 | +{ |
1288 | + if (change == QQuickItem::ItemSceneChange) { |
1289 | + if (value.window != nullptr) { |
1290 | + value.window->installEventFilter(TouchRegistry::instance()); |
1291 | + } |
1292 | + } |
1293 | +} |
1294 | + |
1295 | +void TouchGestureArea::setDragging(bool dragging) |
1296 | +{ |
1297 | + if (m_dragging == dragging) |
1298 | + return; |
1299 | + |
1300 | + tgaDebug("setDragging " << dragging); |
1301 | + |
1302 | + m_dragging = dragging; |
1303 | + Q_EMIT draggingChanged(m_dragging); |
1304 | +} |
1305 | + |
1306 | +void GestureTouchPoint::setId(int id) |
1307 | +{ |
1308 | + if (m_id == id) |
1309 | + return; |
1310 | + m_id = id; |
1311 | + Q_EMIT idChanged(); |
1312 | +} |
1313 | + |
1314 | +void GestureTouchPoint::setPressed(bool pressed) |
1315 | +{ |
1316 | + if (m_pressed == pressed) |
1317 | + return; |
1318 | + m_pressed = pressed; |
1319 | + Q_EMIT pressedChanged(); |
1320 | +} |
1321 | + |
1322 | +void GestureTouchPoint::setX(qreal x) |
1323 | +{ |
1324 | + if (m_x == x) |
1325 | + return; |
1326 | + m_x = x; |
1327 | + Q_EMIT xChanged(); |
1328 | +} |
1329 | + |
1330 | +void GestureTouchPoint::setY(qreal y) |
1331 | +{ |
1332 | + if (m_y == y) |
1333 | + return; |
1334 | + m_y = y; |
1335 | + Q_EMIT yChanged(); |
1336 | +} |
1337 | + |
1338 | +void GestureTouchPoint::setDragging(bool dragging) |
1339 | +{ |
1340 | + if (m_dragging == dragging) |
1341 | + return; |
1342 | + |
1343 | + m_dragging = dragging; |
1344 | + Q_EMIT draggingChanged(); |
1345 | +} |
1346 | + |
1347 | +void GestureTouchPoint::setPos(const QPointF &pos) |
1348 | +{ |
1349 | + setX(pos.x()); |
1350 | + setY(pos.y()); |
1351 | +} |
1352 | |
1353 | === added file 'plugins/Ubuntu/Gestures/TouchGestureArea.h' |
1354 | --- plugins/Ubuntu/Gestures/TouchGestureArea.h 1970-01-01 00:00:00 +0000 |
1355 | +++ plugins/Ubuntu/Gestures/TouchGestureArea.h 2016-03-11 12:07:48 +0000 |
1356 | @@ -0,0 +1,229 @@ |
1357 | +/* |
1358 | + * Copyright (C) 2016 Canonical, Ltd. |
1359 | + * |
1360 | + * This program is free software; you can redistribute it and/or modify |
1361 | + * it under the terms of the GNU General Public License as published by |
1362 | + * the Free Software Foundation; version 3. |
1363 | + * |
1364 | + * This program is distributed in the hope that it will be useful, |
1365 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1366 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1367 | + * GNU General Public License for more details. |
1368 | + * |
1369 | + * You should have received a copy of the GNU General Public License |
1370 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1371 | + */ |
1372 | + |
1373 | +#ifndef TOUCHGESTUREAREA_H |
1374 | +#define TOUCHGESTUREAREA_H |
1375 | + |
1376 | +#include "UbuntuGesturesQmlGlobal.h" |
1377 | + |
1378 | +#include <QQuickItem> |
1379 | + |
1380 | +// lib UbuntuGestures |
1381 | +#include <Timer.h> |
1382 | + |
1383 | +class TouchOwnershipEvent; |
1384 | +class UnownedTouchEvent; |
1385 | + |
1386 | +class GestureTouchPoint : public QObject |
1387 | +{ |
1388 | + Q_OBJECT |
1389 | + Q_PROPERTY(int id READ id NOTIFY idChanged) |
1390 | + Q_PROPERTY(bool pressed READ pressed NOTIFY pressedChanged) |
1391 | + Q_PROPERTY(qreal x READ x NOTIFY xChanged) |
1392 | + Q_PROPERTY(qreal y READ y NOTIFY yChanged) |
1393 | + Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged) |
1394 | +public: |
1395 | + GestureTouchPoint() |
1396 | + : m_id(-1) |
1397 | + , m_pressed(false) |
1398 | + , m_x(0) |
1399 | + , m_y(0) |
1400 | + , m_dragging(false) |
1401 | + { |
1402 | + } |
1403 | + |
1404 | + GestureTouchPoint(const GestureTouchPoint& other) |
1405 | + : QObject(nullptr) |
1406 | + { |
1407 | + operator=(other); |
1408 | + } |
1409 | + |
1410 | + int id() const { return m_id; } |
1411 | + void setId(int id); |
1412 | + |
1413 | + bool pressed() const { return m_pressed; } |
1414 | + void setPressed(bool pressed); |
1415 | + |
1416 | + qreal x() const { return m_x; } |
1417 | + void setX(qreal x); |
1418 | + |
1419 | + qreal y() const { return m_y; } |
1420 | + void setY(qreal y); |
1421 | + |
1422 | + bool dragging() const { return m_dragging; } |
1423 | + void setDragging(bool dragging); |
1424 | + |
1425 | + GestureTouchPoint& operator=(const GestureTouchPoint& rhs) { |
1426 | + if (&rhs == this) return *this; |
1427 | + m_id = rhs.m_id; |
1428 | + m_pressed = rhs.m_pressed; |
1429 | + m_x = rhs.m_x; |
1430 | + m_y = rhs.m_y; |
1431 | + m_dragging = rhs.m_dragging; |
1432 | + return *this; |
1433 | + } |
1434 | + |
1435 | + bool operator=(const GestureTouchPoint& rhs) const { |
1436 | + if (&rhs == this) return true; |
1437 | + return m_id == rhs.m_id && |
1438 | + m_pressed == rhs.m_pressed && |
1439 | + m_x == rhs.m_x && |
1440 | + m_y == rhs.m_y && |
1441 | + m_dragging == rhs.m_dragging; |
1442 | + } |
1443 | + bool operator!=(const GestureTouchPoint& rhs) const { return !operator=(rhs); } |
1444 | + |
1445 | + void setPos(const QPointF &pos); |
1446 | + |
1447 | +Q_SIGNALS: |
1448 | + void idChanged(); |
1449 | + void pressedChanged(); |
1450 | + void xChanged(); |
1451 | + void yChanged(); |
1452 | + void draggingChanged(); |
1453 | + |
1454 | +private: |
1455 | + int m_id; |
1456 | + bool m_pressed; |
1457 | + qreal m_x; |
1458 | + qreal m_y; |
1459 | + bool m_dragging; |
1460 | +}; |
1461 | + |
1462 | +/* |
1463 | + An area that detects multi-finger gestures. |
1464 | + |
1465 | + We can use this to detect gestures contstrained by a minimim and/or maximum number of touch points. |
1466 | + This components uses the touch registry to apply for ownership of touch points. |
1467 | + This way we can use the component in conjuntion with the directional drag area to compete for ownwership |
1468 | + or gestures; unlike the MultiPointTouchArea. |
1469 | + */ |
1470 | +class UBUNTUGESTURESQML_EXPORT TouchGestureArea : public QQuickItem |
1471 | +{ |
1472 | + Q_OBJECT |
1473 | + Q_ENUMS(Status) |
1474 | + |
1475 | + Q_PROPERTY(int status READ status NOTIFY statusChanged) |
1476 | + Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged) |
1477 | + Q_PROPERTY(QQmlListProperty<GestureTouchPoint> touchPoints READ touchPoints NOTIFY touchPointsUpdated) |
1478 | + |
1479 | + Q_PROPERTY(int minimumTouchPoints READ minimumTouchPoints WRITE setMinimumTouchPoints NOTIFY minimumTouchPointsChanged) |
1480 | + Q_PROPERTY(int maximumTouchPoints READ maximumTouchPoints WRITE setMaximumTouchPoints NOTIFY maximumTouchPointsChanged) |
1481 | + |
1482 | + // Time(ms) the component will wait for after receiving an initial touch to recognise a gesutre before rejecting it. |
1483 | + Q_PROPERTY(int recognitionPeriod READ recognitionPeriod WRITE setRecognitionPeriod NOTIFY recognitionPeriodChanged) |
1484 | + // Time(ms) the component will allow a recognised gesture to intermitently release a touch point before rejecting the gesture. |
1485 | + // This is so we will not immediately reject a gesture if there are fleeting touch point releases while dragging. |
1486 | + Q_PROPERTY(int releaseRejectPeriod READ releaseRejectPeriod WRITE setReleaseRejectPeriod NOTIFY releaseRejectPeriodChanged) |
1487 | + |
1488 | +public: |
1489 | + // Describes the state of the touch gesture area. |
1490 | + enum Status { |
1491 | + WaitingForTouch, |
1492 | + Undecided, |
1493 | + Recognized, |
1494 | + Rejected |
1495 | + }; |
1496 | + TouchGestureArea(QQuickItem* parent = NULL); |
1497 | + ~TouchGestureArea(); |
1498 | + |
1499 | + bool event(QEvent *e) override; |
1500 | + |
1501 | + void setRecognitionTimer(UbuntuGestures::AbstractTimer *timer); |
1502 | + |
1503 | + int status() const; |
1504 | + bool dragging() const; |
1505 | + QQmlListProperty<GestureTouchPoint> touchPoints(); |
1506 | + |
1507 | + int minimumTouchPoints() const; |
1508 | + void setMinimumTouchPoints(int value); |
1509 | + |
1510 | + int maximumTouchPoints() const; |
1511 | + void setMaximumTouchPoints(int value); |
1512 | + |
1513 | + int recognitionPeriod() const; |
1514 | + void setRecognitionPeriod(int value); |
1515 | + |
1516 | + int releaseRejectPeriod() const; |
1517 | + void setReleaseRejectPeriod(int value); |
1518 | + |
1519 | +Q_SIGNALS: |
1520 | + void statusChanged(int status); |
1521 | + |
1522 | + void touchPointsUpdated(); |
1523 | + void draggingChanged(bool dragging); |
1524 | + void minimumTouchPointsChanged(bool value); |
1525 | + void maximumTouchPointsChanged(bool value); |
1526 | + void recognitionPeriodChanged(bool value); |
1527 | + void releaseRejectPeriodChanged(bool value); |
1528 | + |
1529 | + void pressed(const QList<QObject*>& points); |
1530 | + void released(const QList<QObject*>& points); |
1531 | + void updated(const QList<QObject*>& points); |
1532 | + void clicked(); |
1533 | + |
1534 | +protected: |
1535 | + void itemChange(ItemChange change, const ItemChangeData &value); |
1536 | + |
1537 | +private Q_SLOTS: |
1538 | + void rejectGesture(); |
1539 | + |
1540 | +private: |
1541 | + void touchEvent(QTouchEvent *event) override; |
1542 | + void touchEvent_waitingForTouch(QTouchEvent *event); |
1543 | + void touchEvent_waitingForMoreTouches(QTouchEvent *event); |
1544 | + void touchEvent_waitingForOwnership(QTouchEvent *event); |
1545 | + void touchEvent_recognized(QTouchEvent *event); |
1546 | + void touchEvent_rejected(QTouchEvent *event); |
1547 | + |
1548 | + void unownedTouchEvent(QTouchEvent *unownedTouchEvent); |
1549 | + void unownedTouchEvent_waitingForMoreTouches(QTouchEvent *unownedTouchEvent); |
1550 | + void unownedTouchEvent_waitingForOwnership(QTouchEvent *unownedTouchEvent); |
1551 | + void unownedTouchEvent_recognised(QTouchEvent *unownedTouchEvent); |
1552 | + void unownedTouchEvent_rejected(QTouchEvent *unownedTouchEvent); |
1553 | + |
1554 | + void touchOwnershipEvent(TouchOwnershipEvent *event); |
1555 | + void updateTouchPoints(QTouchEvent *event); |
1556 | + |
1557 | + GestureTouchPoint* addTouchPoint(const QTouchEvent::TouchPoint *tp); |
1558 | + void clearTouchLists(); |
1559 | + void setDragging(bool dragging); |
1560 | + void setInternalStatus(uint status); |
1561 | + void resyncCachedTouchPoints(); |
1562 | + |
1563 | + static int touchPoint_count(QQmlListProperty<GestureTouchPoint> *list); |
1564 | + static GestureTouchPoint* touchPoint_at(QQmlListProperty<GestureTouchPoint> *list, int index); |
1565 | + |
1566 | + uint m_status; |
1567 | + QSet<int> m_candidateTouches; |
1568 | + QSet<int> m_watchedTouches; |
1569 | + UbuntuGestures::AbstractTimer *m_recognitionTimer; |
1570 | + |
1571 | + bool m_dragging; |
1572 | + QHash<int, GestureTouchPoint*> m_liveTouchPoints; |
1573 | + QHash<int, GestureTouchPoint*> m_cachedTouchPoints; |
1574 | + QList<QObject*> m_releasedTouchPoints; |
1575 | + QList<QObject*> m_pressedTouchPoints; |
1576 | + QList<QObject*> m_movedTouchPoints; |
1577 | + int m_minimumTouchPoints; |
1578 | + int m_maximumTouchPoints; |
1579 | + int m_recognitionPeriod; |
1580 | + int m_releaseRejectPeriod; |
1581 | +}; |
1582 | + |
1583 | +QML_DECLARE_TYPE(GestureTouchPoint) |
1584 | + |
1585 | +#endif // TOUCHGESTUREAREA_H |
1586 | |
1587 | === modified file 'plugins/Ubuntu/Gestures/plugin.cpp' |
1588 | --- plugins/Ubuntu/Gestures/plugin.cpp 2015-05-27 09:37:34 +0000 |
1589 | +++ plugins/Ubuntu/Gestures/plugin.cpp 2016-03-11 12:07:48 +0000 |
1590 | @@ -21,6 +21,7 @@ |
1591 | #include "FloatingFlickable.h" |
1592 | #include "PressedOutsideNotifier.h" |
1593 | #include "TouchGate.h" |
1594 | +#include "TouchGestureArea.h" |
1595 | |
1596 | #include <qqml.h> |
1597 | |
1598 | @@ -38,4 +39,6 @@ |
1599 | qmlRegisterType<FloatingFlickable>(uri, 0, 1, "FloatingFlickable"); |
1600 | qmlRegisterType<PressedOutsideNotifier>(uri, 0, 1, "PressedOutsideNotifier"); |
1601 | qmlRegisterType<TouchGate>(uri, 0, 1, "TouchGate"); |
1602 | + qmlRegisterType<TouchGestureArea>(uri, 0, 1, "TouchGestureArea"); |
1603 | + qmlRegisterUncreatableType<GestureTouchPoint>(uri, 0, 1, "GestureTouchPoint", "Cannot create GestureTouchPoints"); |
1604 | } |
1605 | |
1606 | === added file 'plugins/Unity/InputInfo/InputInfo.qmltypes' |
1607 | --- plugins/Unity/InputInfo/InputInfo.qmltypes 1970-01-01 00:00:00 +0000 |
1608 | +++ plugins/Unity/InputInfo/InputInfo.qmltypes 2016-03-11 12:07:48 +0000 |
1609 | @@ -0,0 +1,71 @@ |
1610 | +import QtQuick.tooling 1.1 |
1611 | + |
1612 | +// This file describes the plugin-supplied types contained in the library. |
1613 | +// It is used for QML tooling purposes only. |
1614 | +// |
1615 | +// This file was auto-generated by: |
1616 | +// 'qmlplugindump -notrelocatable Unity.InputInfo 0.1 plugins' |
1617 | + |
1618 | +Module { |
1619 | + Component { |
1620 | + name: "QDeclarativeInputDeviceModel" |
1621 | + prototype: "QAbstractListModel" |
1622 | + exports: ["Unity.InputInfo/InputDeviceModel 0.1"] |
1623 | + exportMetaObjectRevisions: [0] |
1624 | + Property { name: "deviceFilter"; type: "QInputDevice::InputType" } |
1625 | + Property { name: "count"; type: "int"; isReadonly: true } |
1626 | + Signal { |
1627 | + name: "deviceAdded" |
1628 | + Parameter { name: "devicePath"; type: "string" } |
1629 | + } |
1630 | + Signal { |
1631 | + name: "deviceRemoved" |
1632 | + Parameter { name: "devicePath"; type: "string" } |
1633 | + } |
1634 | + Signal { |
1635 | + name: "deviceFilterChanged" |
1636 | + Parameter { name: "filter"; type: "QInputDevice::InputType" } |
1637 | + } |
1638 | + Method { name: "updateDeviceList" } |
1639 | + Method { |
1640 | + name: "indexOf" |
1641 | + type: "int" |
1642 | + Parameter { name: "devicePath"; type: "string" } |
1643 | + } |
1644 | + Method { |
1645 | + name: "get" |
1646 | + type: "QInputDevice*" |
1647 | + Parameter { name: "index"; type: "int" } |
1648 | + } |
1649 | + } |
1650 | + Component { |
1651 | + name: "QInputDevice" |
1652 | + prototype: "QObject" |
1653 | + exports: ["Unity.InputInfo/InputInfo 0.1"] |
1654 | + exportMetaObjectRevisions: [0] |
1655 | + Enum { |
1656 | + name: "InputType" |
1657 | + values: { |
1658 | + "Unknown": 0, |
1659 | + "Button": 1, |
1660 | + "Mouse": 2, |
1661 | + "TouchPad": 4, |
1662 | + "TouchScreen": 8, |
1663 | + "Keyboard": 16, |
1664 | + "Switch": 32 |
1665 | + } |
1666 | + } |
1667 | + Enum { |
1668 | + name: "InputTypeFlags" |
1669 | + values: { |
1670 | + "Unknown": 0, |
1671 | + "Button": 1, |
1672 | + "Mouse": 2, |
1673 | + "TouchPad": 4, |
1674 | + "TouchScreen": 8, |
1675 | + "Keyboard": 16, |
1676 | + "Switch": 32 |
1677 | + } |
1678 | + } |
1679 | + } |
1680 | +} |
1681 | |
1682 | === added file 'plugins/Unity/Platform/Platform.qmltypes' |
1683 | --- plugins/Unity/Platform/Platform.qmltypes 1970-01-01 00:00:00 +0000 |
1684 | +++ plugins/Unity/Platform/Platform.qmltypes 2016-03-11 12:07:48 +0000 |
1685 | @@ -0,0 +1,20 @@ |
1686 | +import QtQuick.tooling 1.1 |
1687 | + |
1688 | +// This file describes the plugin-supplied types contained in the library. |
1689 | +// It is used for QML tooling purposes only. |
1690 | +// |
1691 | +// This file was auto-generated by: |
1692 | +// 'qmlplugindump -notrelocatable Unity.Platform 1.0 plugins' |
1693 | + |
1694 | +Module { |
1695 | + Component { |
1696 | + name: "Platform" |
1697 | + prototype: "QObject" |
1698 | + exports: ["Unity.Platform/Platform 1.0"] |
1699 | + isCreatable: false |
1700 | + isSingleton: true |
1701 | + exportMetaObjectRevisions: [0] |
1702 | + Property { name: "chassis"; type: "string"; isReadonly: true } |
1703 | + Property { name: "isPC"; type: "bool"; isReadonly: true } |
1704 | + } |
1705 | +} |
1706 | |
1707 | === modified file 'plugins/Utils/CMakeLists.txt' |
1708 | --- plugins/Utils/CMakeLists.txt 2016-03-11 12:07:48 +0000 |
1709 | +++ plugins/Utils/CMakeLists.txt 2016-03-11 12:07:48 +0000 |
1710 | @@ -24,6 +24,7 @@ |
1711 | timezoneFormatter.cpp |
1712 | inputeventgenerator.cpp |
1713 | deviceconfigparser.cpp |
1714 | + globalfunctions.cpp |
1715 | plugin.cpp |
1716 | ) |
1717 | |
1718 | |
1719 | === added file 'plugins/Utils/globalfunctions.cpp' |
1720 | --- plugins/Utils/globalfunctions.cpp 1970-01-01 00:00:00 +0000 |
1721 | +++ plugins/Utils/globalfunctions.cpp 2016-03-11 12:07:48 +0000 |
1722 | @@ -0,0 +1,56 @@ |
1723 | +/* |
1724 | + * Copyright 2015 Canonical Ltd. |
1725 | + * |
1726 | + * This program is free software; you can redistribute it and/or modify |
1727 | + * it under the terms of the GNU Lesser General Public License as published by |
1728 | + * the Free Software Foundation; version 3. |
1729 | + * |
1730 | + * This program is distributed in the hope that it will be useful, |
1731 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1732 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1733 | + * GNU Lesser General Public License for more details. |
1734 | + * |
1735 | + * You should have received a copy of the GNU Lesser General Public License |
1736 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1737 | +*/ |
1738 | + |
1739 | +#include "globalfunctions.h" |
1740 | + |
1741 | +#pragma GCC diagnostic push |
1742 | +#pragma GCC diagnostic ignored "-pedantic" |
1743 | +#include <private/qquickitem_p.h> |
1744 | +#pragma GCC diagnostic pop |
1745 | +#include <QQmlEngine> |
1746 | + |
1747 | +GlobalFunctions::GlobalFunctions(QObject *parent) |
1748 | + : QObject(parent) |
1749 | +{ |
1750 | +} |
1751 | + |
1752 | +QQuickItem *GlobalFunctions::itemAt(QQuickItem* parent, int x, int y, QJSValue matcher) |
1753 | +{ |
1754 | + if (!parent) return nullptr; |
1755 | + QList<QQuickItem *> children = QQuickItemPrivate::get(parent)->paintOrderChildItems(); |
1756 | + |
1757 | + for (int i = children.count() - 1; i >= 0; --i) { |
1758 | + QQuickItem *child = children.at(i); |
1759 | + |
1760 | + // Map coordinates to the child element's coordinate space |
1761 | + QPointF point = parent->mapToItem(child, QPointF(x, y)); |
1762 | + if (child->isVisible() && point.x() >= 0 |
1763 | + && child->width() >= point.x() |
1764 | + && point.y() >= 0 |
1765 | + && child->height() >= point.y()) { |
1766 | + if (!matcher.isCallable()) return child; |
1767 | + |
1768 | + QQmlEngine* engine = qmlEngine(child); |
1769 | + if (!engine) return child; |
1770 | + |
1771 | + QJSValue newObj = engine->newQObject(child); |
1772 | + if (matcher.call(QJSValueList() << newObj).toBool()) { |
1773 | + return child; |
1774 | + } |
1775 | + } |
1776 | + } |
1777 | + return nullptr; |
1778 | +} |
1779 | |
1780 | === added file 'plugins/Utils/globalfunctions.h' |
1781 | --- plugins/Utils/globalfunctions.h 1970-01-01 00:00:00 +0000 |
1782 | +++ plugins/Utils/globalfunctions.h 2016-03-11 12:07:48 +0000 |
1783 | @@ -0,0 +1,42 @@ |
1784 | +/* |
1785 | + * Copyright 2015 Canonical Ltd. |
1786 | + * |
1787 | + * This program is free software; you can redistribute it and/or modify |
1788 | + * it under the terms of the GNU Lesser General Public License as published by |
1789 | + * the Free Software Foundation; version 3. |
1790 | + * |
1791 | + * This program is distributed in the hope that it will be useful, |
1792 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1793 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1794 | + * GNU Lesser General Public License for more details. |
1795 | + * |
1796 | + * You should have received a copy of the GNU Lesser General Public License |
1797 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1798 | +*/ |
1799 | + |
1800 | +#ifndef GLOBALFUNCTIONS_H |
1801 | +#define GLOBALFUNCTIONS_H |
1802 | + |
1803 | +#include <QObject> |
1804 | +#include <QJSValue> |
1805 | +class QQuickItem; |
1806 | + |
1807 | +/** |
1808 | + * @brief The GlobalFunctions class |
1809 | + * |
1810 | + * This singleton class exposes utility functions to QML |
1811 | + * |
1812 | + */ |
1813 | +class GlobalFunctions : public QObject |
1814 | +{ |
1815 | + Q_OBJECT |
1816 | +public: |
1817 | + explicit GlobalFunctions(QObject *parent = 0); |
1818 | + |
1819 | + static Q_INVOKABLE QQuickItem* itemAt(QQuickItem* parent, |
1820 | + int x, |
1821 | + int y, |
1822 | + QJSValue matcher); |
1823 | +}; |
1824 | + |
1825 | +#endif // GLOBALFUNCTIONS_H |
1826 | |
1827 | === modified file 'plugins/Utils/plugin.cpp' |
1828 | --- plugins/Utils/plugin.cpp 2016-03-11 12:07:48 +0000 |
1829 | +++ plugins/Utils/plugin.cpp 2016-03-11 12:07:48 +0000 |
1830 | @@ -38,6 +38,7 @@ |
1831 | #include "applicationsfiltermodel.h" |
1832 | #include "inputeventgenerator.h" |
1833 | #include "deviceconfigparser.h" |
1834 | +#include "globalfunctions.h" |
1835 | |
1836 | static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine) |
1837 | { |
1838 | @@ -53,6 +54,13 @@ |
1839 | return new Constants(); |
1840 | } |
1841 | |
1842 | +static QObject *createGlobalFunctions(QQmlEngine *engine, QJSEngine *scriptEngine) |
1843 | +{ |
1844 | + Q_UNUSED(engine) |
1845 | + Q_UNUSED(scriptEngine) |
1846 | + return new GlobalFunctions(); |
1847 | +} |
1848 | + |
1849 | void UtilsPlugin::registerTypes(const char *uri) |
1850 | { |
1851 | Q_ASSERT(uri == QLatin1String("Utils")); |
1852 | @@ -72,6 +80,7 @@ |
1853 | qmlRegisterType<ApplicationsFilterModel>(uri, 0, 1, "ApplicationsFilterModel"); |
1854 | qmlRegisterType<InputEventGenerator>(uri, 0, 1, "InputEventGenerator"); |
1855 | qmlRegisterType<DeviceConfigParser>(uri, 0, 1, "DeviceConfigParser"); |
1856 | + qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions); |
1857 | } |
1858 | |
1859 | void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri) |
1860 | |
1861 | === modified file 'plugins/Utils/windowstatestorage.cpp' |
1862 | --- plugins/Utils/windowstatestorage.cpp 2015-11-20 15:01:39 +0000 |
1863 | +++ plugins/Utils/windowstatestorage.cpp 2016-03-11 12:07:48 +0000 |
1864 | @@ -23,9 +23,14 @@ |
1865 | #include <QSqlError> |
1866 | #include <QSqlResult> |
1867 | #include <QRect> |
1868 | +#include <unity/shell/application/ApplicationInfoInterface.h> |
1869 | |
1870 | QMutex WindowStateStorage::s_mutex; |
1871 | |
1872 | +inline QString sanitiseString(QString string) { |
1873 | + return string.remove("\"").remove("'").remove("\\"); |
1874 | +} |
1875 | + |
1876 | WindowStateStorage::WindowStateStorage(QObject *parent): |
1877 | QObject(parent) |
1878 | { |
1879 | @@ -50,7 +55,7 @@ |
1880 | void WindowStateStorage::saveState(const QString &windowId, WindowStateStorage::WindowState state) |
1881 | { |
1882 | const QString queryString = QStringLiteral("INSERT OR REPLACE INTO state (windowId, state) values ('%1', '%2');") |
1883 | - .arg(windowId) |
1884 | + .arg(sanitiseString(windowId)) |
1885 | .arg((int)state); |
1886 | |
1887 | saveValue(queryString); |
1888 | @@ -59,7 +64,7 @@ |
1889 | WindowStateStorage::WindowState WindowStateStorage::getState(const QString &windowId, WindowStateStorage::WindowState defaultValue) const |
1890 | { |
1891 | const QString queryString = QStringLiteral("SELECT * FROM state WHERE windowId = '%1';") |
1892 | - .arg(windowId); |
1893 | + .arg(sanitiseString(windowId)); |
1894 | |
1895 | QSqlQuery query = getValue(queryString); |
1896 | |
1897 | @@ -72,7 +77,7 @@ |
1898 | void WindowStateStorage::saveGeometry(const QString &windowId, const QRect rect) |
1899 | { |
1900 | const QString queryString = QStringLiteral("INSERT OR REPLACE INTO geometry (windowId, x, y, width, height) values ('%1', '%2', '%3', '%4', '%5');") |
1901 | - .arg(windowId) |
1902 | + .arg(sanitiseString(windowId)) |
1903 | .arg(rect.x()) |
1904 | .arg(rect.y()) |
1905 | .arg(rect.width()) |
1906 | @@ -81,6 +86,28 @@ |
1907 | saveValue(queryString); |
1908 | } |
1909 | |
1910 | +void WindowStateStorage::saveStage(const QString &appId, int stage) |
1911 | +{ |
1912 | + const QString queryString = QStringLiteral("INSERT OR REPLACE INTO stage (appId, stage) values ('%1', '%2');") |
1913 | + .arg(sanitiseString(appId)) |
1914 | + .arg((int)stage); |
1915 | + |
1916 | + saveValue(queryString); |
1917 | +} |
1918 | + |
1919 | +int WindowStateStorage::getStage(const QString &appId) const |
1920 | +{ |
1921 | + const QString queryString = QStringLiteral("SELECT * FROM stage WHERE appId = '%1';") |
1922 | + .arg(sanitiseString(appId)); |
1923 | + |
1924 | + QSqlQuery query = getValue(queryString); |
1925 | + |
1926 | + if (!query.first()) { |
1927 | + return unity::shell::application::ApplicationInfoInterface::MainStage; |
1928 | + } |
1929 | + return query.value("stage").toInt(); |
1930 | +} |
1931 | + |
1932 | void WindowStateStorage::executeAsyncQuery(const QString &queryString) |
1933 | { |
1934 | QMutexLocker l(&s_mutex); |
1935 | @@ -97,7 +124,7 @@ |
1936 | QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect defaultValue) const |
1937 | { |
1938 | QString queryString = QStringLiteral("SELECT * FROM geometry WHERE windowId = '%1';") |
1939 | - .arg(windowId); |
1940 | + .arg(sanitiseString(windowId)); |
1941 | |
1942 | QSqlQuery query = getValue(queryString); |
1943 | |
1944 | @@ -124,6 +151,11 @@ |
1945 | QSqlQuery query; |
1946 | query.exec(QStringLiteral("CREATE TABLE state(windowId TEXT UNIQUE, state INTEGER);")); |
1947 | } |
1948 | + |
1949 | + if (!m_db.tables().contains(QStringLiteral("stage"))) { |
1950 | + QSqlQuery query; |
1951 | + query.exec(QStringLiteral("CREATE TABLE stage(appId TEXT UNIQUE, stage INTEGER);")); |
1952 | + } |
1953 | } |
1954 | |
1955 | void WindowStateStorage::saveValue(const QString &queryString) |
1956 | |
1957 | === modified file 'plugins/Utils/windowstatestorage.h' |
1958 | --- plugins/Utils/windowstatestorage.h 2015-11-20 15:01:39 +0000 |
1959 | +++ plugins/Utils/windowstatestorage.h 2016-03-11 12:07:48 +0000 |
1960 | @@ -38,6 +38,9 @@ |
1961 | Q_INVOKABLE void saveGeometry(const QString &windowId, const QRect rect); |
1962 | Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect defaultValue) const; |
1963 | |
1964 | + Q_INVOKABLE void saveStage(const QString &appId, int stage); |
1965 | + Q_INVOKABLE int getStage(const QString &appId) const; |
1966 | + |
1967 | private: |
1968 | void initdb(); |
1969 | |
1970 | |
1971 | === modified file 'qml/Components/Showable.qml' |
1972 | --- qml/Components/Showable.qml 2015-07-15 15:07:19 +0000 |
1973 | +++ qml/Components/Showable.qml 2016-03-11 12:07:48 +0000 |
1974 | @@ -31,6 +31,7 @@ |
1975 | property bool required |
1976 | property bool __shouldShow: false |
1977 | property bool __skipShowAnimation: false |
1978 | + property bool __skipHideAnimation: false |
1979 | |
1980 | property list<QtObject> hides |
1981 | property var showAnimation |
1982 | @@ -120,12 +121,21 @@ |
1983 | if (!hideAnimation.running) { |
1984 | hideAnimation.restart() |
1985 | } |
1986 | + if (__skipHideAnimation) { |
1987 | + hideAnimation.complete(); |
1988 | + } |
1989 | } else { |
1990 | visible = false |
1991 | required = false |
1992 | } |
1993 | |
1994 | shown = false |
1995 | + __skipHideAnimation = false; |
1996 | + } |
1997 | + |
1998 | + function hideNow() { |
1999 | + __skipHideAnimation = true; |
2000 | + hide(); |
2001 | } |
2002 | |
2003 | Connections { |
2004 | |
2005 | === modified file 'qml/Shell.qml' |
2006 | --- qml/Shell.qml 2016-03-08 20:59:08 +0000 |
2007 | +++ qml/Shell.qml 2016-03-11 12:07:48 +0000 |
2008 | @@ -119,9 +119,7 @@ |
2009 | if (ApplicationManager.findApplication(appId)) { |
2010 | ApplicationManager.requestFocusApplication(appId); |
2011 | } else { |
2012 | - var execFlags = shell.usageScenario === "phone" ? ApplicationManager.ForceMainStage |
2013 | - : ApplicationManager.NoFlag; |
2014 | - ApplicationManager.startApplication(appId, execFlags); |
2015 | + ApplicationManager.startApplication(appId); |
2016 | } |
2017 | } |
2018 | |
2019 | @@ -532,9 +530,7 @@ |
2020 | greeterShown: greeter.shown |
2021 | } |
2022 | |
2023 | - readonly property bool topmostApplicationIsFullscreen: |
2024 | - ApplicationManager.focusedApplicationId && |
2025 | - ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen |
2026 | + readonly property bool topmostApplicationIsFullscreen: mainApp && mainApp.fullscreen |
2027 | |
2028 | fullscreenMode: (topmostApplicationIsFullscreen && !lightDM.greeter.active && launcher.progress == 0) |
2029 | || greeter.hasLockedApp |
2030 | |
2031 | === modified file 'qml/Stages/AbstractStage.qml' |
2032 | --- qml/Stages/AbstractStage.qml 2016-01-14 13:03:20 +0000 |
2033 | +++ qml/Stages/AbstractStage.qml 2016-03-11 12:07:48 +0000 |
2034 | @@ -42,7 +42,7 @@ |
2035 | |
2036 | // To be read from outside |
2037 | property var mainApp: null |
2038 | - property int mainAppWindowOrientationAngle |
2039 | + property int mainAppWindowOrientationAngle: 0 |
2040 | property bool orientationChangesEnabled |
2041 | property int supportedOrientations: Qt.PortraitOrientation |
2042 | | Qt.LandscapeOrientation |
2043 | |
2044 | === modified file 'qml/Stages/ApplicationWindow.qml' |
2045 | --- qml/Stages/ApplicationWindow.qml 2016-02-12 00:10:54 +0000 |
2046 | +++ qml/Stages/ApplicationWindow.qml 2016-03-11 12:07:48 +0000 |
2047 | @@ -24,12 +24,14 @@ |
2048 | implicitHeight: sessionContainer.implicitHeight |
2049 | |
2050 | // to be read from outside |
2051 | - readonly property bool fullscreen: application ? application.fullscreen : false |
2052 | property alias interactive: sessionContainer.interactive |
2053 | property bool orientationChangesEnabled: d.supportsSurfaceResize ? d.surfaceOldEnoughToBeResized : true |
2054 | readonly property string title: sessionContainer.surface && sessionContainer.surface.name !== "" ? |
2055 | sessionContainer.surface.name : d.name |
2056 | |
2057 | + // overridable from outside |
2058 | + property bool fullscreen: application ? application.fullscreen : false |
2059 | + |
2060 | // to be set from outside |
2061 | property QtObject application |
2062 | property int surfaceOrientationAngle |
2063 | |
2064 | === added file 'qml/Stages/SideStage.qml' |
2065 | --- qml/Stages/SideStage.qml 1970-01-01 00:00:00 +0000 |
2066 | +++ qml/Stages/SideStage.qml 2016-03-11 12:07:48 +0000 |
2067 | @@ -0,0 +1,118 @@ |
2068 | +/* |
2069 | + * Copyright (C) 2016 Canonical, Ltd. |
2070 | + * |
2071 | + * This program is free software; you can redistribute it and/or modify |
2072 | + * it under the terms of the GNU General Public License as published by |
2073 | + * the Free Software Foundation; version 3. |
2074 | + * |
2075 | + * This program is distributed in the hope that it will be useful, |
2076 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2077 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2078 | + * GNU General Public License for more details. |
2079 | + * |
2080 | + * You should have received a copy of the GNU General Public License |
2081 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2082 | + */ |
2083 | + |
2084 | +import QtQuick 2.4 |
2085 | +import QtQuick.Layouts 1.1 |
2086 | +import Ubuntu.Components 1.3 |
2087 | +import Ubuntu.Gestures 0.1 |
2088 | +import "../Components" |
2089 | + |
2090 | +Showable { |
2091 | + id: root |
2092 | + property bool showHint: true |
2093 | + property int panelWidth: units.gu(40) |
2094 | + readonly property alias dragging: hideSideStageDragArea.dragging |
2095 | + readonly property real progress: width / panelWidth |
2096 | + |
2097 | + width: 0 |
2098 | + shown: false |
2099 | + |
2100 | + Item { |
2101 | + id: sideStageDragHandle |
2102 | + anchors { |
2103 | + right: root.left |
2104 | + top: root.top |
2105 | + bottom: root.bottom |
2106 | + } |
2107 | + width: units.gu(2) |
2108 | + |
2109 | + opacity: root.shown ? 1 : 0 |
2110 | + Behavior on opacity { UbuntuNumberAnimation {} } |
2111 | + |
2112 | + Image { |
2113 | + anchors.centerIn: parent |
2114 | + width: hideSideStageDragArea.pressed ? parent.width * 2 : parent.width |
2115 | + height: parent.height |
2116 | + source: "graphics/sidestage_handle@20.png" |
2117 | + Behavior on width { UbuntuNumberAnimation {} } |
2118 | + } |
2119 | + } |
2120 | + |
2121 | + Rectangle { |
2122 | + anchors.fill: parent |
2123 | + color: Qt.rgba(0,0,0,0.95) |
2124 | + } |
2125 | + |
2126 | + Column { |
2127 | + anchors.verticalCenter: parent.verticalCenter |
2128 | + width: panelWidth - units.gu(6) |
2129 | + x: panelWidth/2 - width/2 |
2130 | + spacing: units.gu(3) |
2131 | + opacity: 0.8 |
2132 | + visible: showHint |
2133 | + |
2134 | + Icon { |
2135 | + width: units.gu(30) |
2136 | + anchors.horizontalCenter: parent.horizontalCenter |
2137 | + source: "graphics/sidestage_drag.svg" |
2138 | + color: enabled ? Qt.rgba(1,1,1,1) : Qt.rgba(1,0,0,1) |
2139 | + keyColor: Qt.rgba(1,1,1,1) |
2140 | + } |
2141 | + |
2142 | + Label { |
2143 | + text: i18n.tr("Drag using 3 fingers any application from one window to the other") |
2144 | + width: parent.width |
2145 | + wrapMode: Text.WordWrap |
2146 | + color: enabled ? Qt.rgba(1,1,1,1) : Qt.rgba(1,0,0,1) |
2147 | + } |
2148 | + } |
2149 | + |
2150 | + showAnimation: NumberAnimation { |
2151 | + property: "width" |
2152 | + to: panelWidth |
2153 | + duration: UbuntuAnimation.BriskDuration |
2154 | + easing.type: Easing.OutCubic |
2155 | + } |
2156 | + |
2157 | + hideAnimation: NumberAnimation { |
2158 | + property: "width" |
2159 | + to: 0 |
2160 | + duration: UbuntuAnimation.BriskDuration |
2161 | + easing.type: Easing.OutCubic |
2162 | + } |
2163 | + |
2164 | + DragHandle { |
2165 | + id: hideSideStageDragArea |
2166 | + objectName: "hideSideStageDragArea" |
2167 | + |
2168 | + direction: Direction.Leftwards |
2169 | + rotation: 180 |
2170 | + enabled: root.shown |
2171 | + anchors.right: root.left |
2172 | + width: sideStageDragHandle.width |
2173 | + height: root.height |
2174 | + stretch: true |
2175 | + |
2176 | + immediateRecognition: true |
2177 | + maxTotalDragDistance: panelWidth |
2178 | + autoCompleteDragThreshold: panelWidth / 2 |
2179 | + } |
2180 | + |
2181 | + // SideStage mouse event eater |
2182 | + MouseArea { |
2183 | + anchors.fill: parent |
2184 | + } |
2185 | +} |
2186 | |
2187 | === modified file 'qml/Stages/SpreadDelegate.qml' |
2188 | --- qml/Stages/SpreadDelegate.qml 2015-11-04 14:57:45 +0000 |
2189 | +++ qml/Stages/SpreadDelegate.qml 2016-03-11 12:07:48 +0000 |
2190 | @@ -32,6 +32,11 @@ |
2191 | readonly property alias appWindowOrientationAngle: appWindowWithShadow.orientationAngle |
2192 | readonly property alias appWindowRotation: appWindowWithShadow.rotation |
2193 | readonly property alias orientationChangesEnabled: appWindow.orientationChangesEnabled |
2194 | + property int supportedOrientations: application ? application.supportedOrientations : |
2195 | + Qt.PortraitOrientation |
2196 | + | Qt.LandscapeOrientation |
2197 | + | Qt.InvertedPortraitOrientation |
2198 | + | Qt.InvertedLandscapeOrientation |
2199 | |
2200 | // to be set from outside |
2201 | property bool interactive: true |
2202 | @@ -44,6 +49,9 @@ |
2203 | property int shellOrientation |
2204 | property QtObject orientations |
2205 | |
2206 | + // overrideable from outside |
2207 | + property alias fullscreen: appWindow.fullscreen |
2208 | + |
2209 | function matchShellOrientation() { |
2210 | if (!root.application) |
2211 | return; |
2212 | @@ -153,7 +161,8 @@ |
2213 | if (!root.application || root.application.rotatesWindowContents) { |
2214 | return 0; |
2215 | } |
2216 | - var supportedOrientations = root.application.supportedOrientations; |
2217 | + |
2218 | + var supportedOrientations = root.supportedOrientations; |
2219 | |
2220 | if (supportedOrientations === Qt.PrimaryOrientation) { |
2221 | supportedOrientations = root.orientations.primary; |
2222 | |
2223 | === modified file 'qml/Stages/SurfaceContainer.qml' |
2224 | --- qml/Stages/SurfaceContainer.qml 2015-11-30 12:18:40 +0000 |
2225 | +++ qml/Stages/SurfaceContainer.qml 2016-03-11 12:07:48 +0000 |
2226 | @@ -118,6 +118,7 @@ |
2227 | |
2228 | |
2229 | TouchGate { |
2230 | + objectName: "touchGate-"+name |
2231 | targetItem: surfaceItem |
2232 | anchors.fill: root |
2233 | enabled: surfaceItem.enabled |
2234 | |
2235 | === added file 'qml/Stages/TabletSideStageTouchGesture.qml' |
2236 | --- qml/Stages/TabletSideStageTouchGesture.qml 1970-01-01 00:00:00 +0000 |
2237 | +++ qml/Stages/TabletSideStageTouchGesture.qml 2016-03-11 12:07:48 +0000 |
2238 | @@ -0,0 +1,154 @@ |
2239 | +/* |
2240 | + * Copyright (C) 2016 Canonical, Ltd. |
2241 | + * |
2242 | + * This program is free software; you can redistribute it and/or modify |
2243 | + * it under the terms of the GNU General Public License as published by |
2244 | + * the Free Software Foundation; version 3. |
2245 | + * |
2246 | + * This program is distributed in the hope that it will be useful, |
2247 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2248 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2249 | + * GNU General Public License for more details. |
2250 | + * |
2251 | + * You should have received a copy of the GNU General Public License |
2252 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2253 | + */ |
2254 | + |
2255 | +import QtQuick 2.4 |
2256 | +import Ubuntu.Gestures 0.1 |
2257 | + |
2258 | +TouchGestureArea { |
2259 | + id: root |
2260 | + minimumTouchPoints: 3 |
2261 | + maximumTouchPoints: 3 |
2262 | + |
2263 | + property bool enableDrag: true |
2264 | + property Component dragComponent |
2265 | + property var dragComponentProperties: undefined |
2266 | + |
2267 | + readonly property bool recognisedPress: status == TouchGestureArea.Recognized && |
2268 | + touchPoints.length >= minimumTouchPoints && |
2269 | + touchPoints.length <= maximumTouchPoints |
2270 | + readonly property bool recognisedDrag: priv.wasRecognisedPress && dragging |
2271 | + |
2272 | + signal pressed(int x, int y) |
2273 | + signal clicked |
2274 | + signal dragStarted |
2275 | + signal dropped |
2276 | + signal cancelled |
2277 | + |
2278 | + onEnabledChanged: { |
2279 | + if (!enabled) { |
2280 | + if (priv.dragObject) root.cancelled(); |
2281 | + priv.wasRecognisedDrag = false; |
2282 | + priv.wasRecognisedPress = false; |
2283 | + } |
2284 | + } |
2285 | + |
2286 | + onRecognisedPressChanged: { |
2287 | + if (recognisedPress) { |
2288 | + // get the app at the center of the gesture |
2289 | + var centerX = 0; |
2290 | + var centerY = 0; |
2291 | + for (var i = 0; i < touchPoints.length; i++) { |
2292 | + centerX += touchPoints[i].x; |
2293 | + centerY += touchPoints[i].y; |
2294 | + } |
2295 | + centerX = centerX/touchPoints.length; |
2296 | + centerY = centerY/touchPoints.length; |
2297 | + |
2298 | + pressed(centerX, centerY); |
2299 | + priv.wasRecognisedPress = true; |
2300 | + } |
2301 | + } |
2302 | + |
2303 | + onStatusChanged: { |
2304 | + if (status != TouchGestureArea.Recognized) { |
2305 | + if (status == TouchGestureArea.Rejected) { |
2306 | + root.cancelled(); |
2307 | + } else if (status == TouchGestureArea.WaitingForTouch) { |
2308 | + if (priv.wasRecognisedPress) { |
2309 | + if (!priv.wasRecognisedDrag) { |
2310 | + root.clicked(); |
2311 | + } else { |
2312 | + root.dropped(); |
2313 | + } |
2314 | + } |
2315 | + } |
2316 | + priv.wasRecognisedDrag = false; |
2317 | + priv.wasRecognisedPress = false; |
2318 | + } |
2319 | + } |
2320 | + |
2321 | + onRecognisedDragChanged: { |
2322 | + if (enableDrag && recognisedDrag) { |
2323 | + priv.wasRecognisedDrag = true; |
2324 | + root.dragStarted() |
2325 | + } |
2326 | + } |
2327 | + |
2328 | + QtObject { |
2329 | + id: priv |
2330 | + property var dragObject: null |
2331 | + |
2332 | + property bool wasRecognisedPress: false |
2333 | + property bool wasRecognisedDrag: false |
2334 | + } |
2335 | + |
2336 | + onCancelled: { |
2337 | + if (priv.dragObject) { |
2338 | + var obj = priv.dragObject; |
2339 | + priv.dragObject = null; |
2340 | + |
2341 | + obj.Drag.cancel(); |
2342 | + obj.destroy(); |
2343 | + } |
2344 | + } |
2345 | + |
2346 | + onDragStarted: { |
2347 | + if (dragComponentProperties) { |
2348 | + priv.dragObject = dragComponent.createObject(root, dragComponentProperties); |
2349 | + } else { |
2350 | + priv.dragObject = dragComponent.createObject(root); |
2351 | + } |
2352 | + priv.dragObject.Drag.start(); |
2353 | + } |
2354 | + |
2355 | + onDropped: { |
2356 | + if (priv.dragObject) { |
2357 | + var obj = priv.dragObject; |
2358 | + priv.dragObject = null; |
2359 | + |
2360 | + obj.Drag.drop(); |
2361 | + obj.destroy(); |
2362 | + } |
2363 | + } |
2364 | + |
2365 | + Binding { |
2366 | + target: priv.dragObject |
2367 | + when: priv.dragObject && priv.wasRecognisedDrag |
2368 | + property: "x" |
2369 | + value: { |
2370 | + if (!priv.dragObject) return 0; |
2371 | + var sum = 0; |
2372 | + for (var i = 0; i < root.touchPoints.length; i++) { |
2373 | + sum += root.touchPoints[i].x; |
2374 | + } |
2375 | + return sum/root.touchPoints.length - priv.dragObject.width/2; |
2376 | + } |
2377 | + } |
2378 | + |
2379 | + Binding { |
2380 | + target: priv.dragObject |
2381 | + when: priv.dragObject && priv.wasRecognisedDrag |
2382 | + property: "y" |
2383 | + value: { |
2384 | + if (!priv.dragObject) return 0; |
2385 | + var sum = 0; |
2386 | + for (var i = 0; i < root.touchPoints.length; i++) { |
2387 | + sum += root.touchPoints[i].y; |
2388 | + } |
2389 | + return sum/root.touchPoints.length - priv.dragObject.height/2; |
2390 | + } |
2391 | + } |
2392 | +} |
2393 | |
2394 | === modified file 'qml/Stages/TabletStage.qml' |
2395 | --- qml/Stages/TabletStage.qml 2016-01-14 13:03:20 +0000 |
2396 | +++ qml/Stages/TabletStage.qml 2016-03-11 12:07:48 +0000 |
2397 | @@ -46,7 +46,7 @@ |
2398 | if (delta < 0) { delta += 360; } |
2399 | delta = delta % 360; |
2400 | |
2401 | - var supportedOrientations = spreadDelegate.application.supportedOrientations; |
2402 | + var supportedOrientations = spreadDelegate.supportedOrientations; |
2403 | if (supportedOrientations === Qt.PrimaryOrientation) { |
2404 | supportedOrientations = spreadDelegate.orientations.primary; |
2405 | } |
2406 | @@ -72,9 +72,24 @@ |
2407 | |
2408 | orientationChangesEnabled: priv.mainAppOrientationChangesEnabled |
2409 | |
2410 | - supportedOrientations: mainApp ? mainApp.supportedOrientations |
2411 | - : (Qt.PortraitOrientation | Qt.LandscapeOrientation |
2412 | - | Qt.InvertedPortraitOrientation | Qt.InvertedLandscapeOrientation) |
2413 | + supportedOrientations: { |
2414 | + if (mainApp) { |
2415 | + var orientations = mainApp.supportedOrientations; |
2416 | + orientations |= Qt.LandscapeOrientation | Qt.InvertedLandscapeOrientation; |
2417 | + if (priv.sideStageAppId && !spreadView.surfaceDragging) { |
2418 | + // If we have a sidestage app, support Portrait orientation |
2419 | + // so that it will switch the sidestage app to mainstage on rotate |
2420 | + orientations |= Qt.PortraitOrientation|Qt.InvertedPortraitOrientation; |
2421 | + } |
2422 | + return orientations; |
2423 | + } else { |
2424 | + // we just don't care |
2425 | + return Qt.PortraitOrientation | |
2426 | + Qt.LandscapeOrientation | |
2427 | + Qt.InvertedPortraitOrientation | |
2428 | + Qt.InvertedLandscapeOrientation; |
2429 | + } |
2430 | + } |
2431 | |
2432 | onWidthChanged: { |
2433 | spreadView.selectedIndex = -1; |
2434 | @@ -82,13 +97,6 @@ |
2435 | spreadView.contentX = -spreadView.shift; |
2436 | } |
2437 | |
2438 | - onShellOrientationChanged: { |
2439 | - if (shellOrientation == Qt.PortraitOrientation || shellOrientation == Qt.InvertedPortraitOrientation) { |
2440 | - ApplicationManager.focusApplication(priv.mainStageAppId); |
2441 | - priv.sideStageAppId = ""; |
2442 | - } |
2443 | - } |
2444 | - |
2445 | onInverseProgressChanged: { |
2446 | // This can't be a simple binding because that would be triggered after this handler |
2447 | // while we need it active before doing the anition left/right |
2448 | @@ -130,20 +138,7 @@ |
2449 | |
2450 | property int oldInverseProgress: 0 |
2451 | |
2452 | - onFocusedAppIdChanged: { |
2453 | - if (priv.focusedAppId.length > 0) { |
2454 | - var focusedApp = ApplicationManager.findApplication(focusedAppId); |
2455 | - if (focusedApp.stage == ApplicationInfoInterface.SideStage) { |
2456 | - priv.sideStageAppId = focusedAppId; |
2457 | - } else { |
2458 | - priv.mainStageAppId = focusedAppId; |
2459 | - root.mainApp = focusedApp; |
2460 | - } |
2461 | - } |
2462 | - |
2463 | - appId0 = ApplicationManager.count >= 1 ? ApplicationManager.get(0).appId : ""; |
2464 | - appId1 = ApplicationManager.count > 1 ? ApplicationManager.get(1).appId : ""; |
2465 | - } |
2466 | + onFocusedAppIdChanged: updateStageApps() |
2467 | |
2468 | onFocusedAppDelegateChanged: { |
2469 | if (focusedAppDelegate) { |
2470 | @@ -185,6 +180,46 @@ |
2471 | } |
2472 | return oneWayFlick; |
2473 | } |
2474 | + |
2475 | + function getTopApp(stage) { |
2476 | + for (var i = 0; i < ApplicationManager.count; i++) { |
2477 | + var app = ApplicationManager.get(i) |
2478 | + if (app.stage === stage) { |
2479 | + return app; |
2480 | + } |
2481 | + } |
2482 | + return null; |
2483 | + } |
2484 | + |
2485 | + function setAppStage(appId, stage, save) { |
2486 | + var app = ApplicationManager.findApplication(appId); |
2487 | + if (app) { |
2488 | + app.stage = stage; |
2489 | + if (save) { |
2490 | + WindowStateStorage.saveStage(appId, stage); |
2491 | + } |
2492 | + } |
2493 | + } |
2494 | + |
2495 | + function updateStageApps() { |
2496 | + var app = priv.getTopApp(ApplicationInfoInterface.MainStage); |
2497 | + priv.mainStageAppId = app ? app.appId : "" |
2498 | + root.mainApp = app; |
2499 | + |
2500 | + if (sideStage.shown) { |
2501 | + app = priv.getTopApp(ApplicationInfoInterface.SideStage); |
2502 | + priv.sideStageAppId = app ? app.appId : "" |
2503 | + } else { |
2504 | + priv.sideStageAppId = ""; |
2505 | + } |
2506 | + |
2507 | + appId0 = ApplicationManager.count >= 1 ? ApplicationManager.get(0).appId : ""; |
2508 | + appId1 = ApplicationManager.count > 1 ? ApplicationManager.get(1).appId : ""; |
2509 | + } |
2510 | + |
2511 | + readonly property bool sideStageEnabled: root.shellOrientation == Qt.LandscapeOrientation || |
2512 | + root.shellOrientation == Qt.InvertedLandscapeOrientation |
2513 | + Component.onCompleted: updateStageApps(); |
2514 | } |
2515 | |
2516 | Connections { |
2517 | @@ -212,7 +247,8 @@ |
2518 | ApplicationManager.focusApplication("unity8-dash") |
2519 | } |
2520 | if (priv.sideStageAppId == appId) { |
2521 | - priv.sideStageAppId = ""; |
2522 | + var app = priv.getTopApp(ApplicationInfoInterface.SideStage); |
2523 | + priv.sideStageAppId = app === null ? "" : app.appId; |
2524 | } |
2525 | |
2526 | if (ApplicationManager.count == 0) { |
2527 | @@ -223,6 +259,7 @@ |
2528 | // lets make sure the spread doesn't mess up by the changing app list. |
2529 | spreadView.phase = 0; |
2530 | spreadView.contentX = -spreadView.shift; |
2531 | + |
2532 | ApplicationManager.focusApplication(ApplicationManager.get(0).appId); |
2533 | } |
2534 | } |
2535 | @@ -278,6 +315,7 @@ |
2536 | property int selectedIndex: -1 |
2537 | property int draggedDelegateCount: 0 |
2538 | property int closingIndex: -1 |
2539 | + property var selectedApplication: selectedIndex !== -1 ? ApplicationManager.get(selectedIndex) : null |
2540 | |
2541 | // FIXME: Workaround Flickable's not keepping its contentX still when resized |
2542 | onContentXChanged: { forceItToRemainStillIfBeingResized(); } |
2543 | @@ -299,15 +337,8 @@ |
2544 | } |
2545 | } |
2546 | |
2547 | - property bool sideStageDragging: sideStageDragHandle.dragging |
2548 | - property real sideStageDragProgress: sideStageDragHandle.progress |
2549 | - |
2550 | - onSideStageDragProgressChanged: { |
2551 | - if (sideStageDragProgress == 1) { |
2552 | - ApplicationManager.focusApplication(priv.mainStageAppId); |
2553 | - priv.sideStageAppId = ""; |
2554 | - } |
2555 | - } |
2556 | + property real sideStageDragProgress: sideStage.progress |
2557 | + property bool surfaceDragging: triGestureArea.recognisedDrag |
2558 | |
2559 | // In case the ApplicationManager already holds an app when starting up we're missing animations |
2560 | // Make sure we end up in the same state |
2561 | @@ -358,7 +389,7 @@ |
2562 | } |
2563 | ] |
2564 | state: { |
2565 | - if (priv.mainStageAppId && !priv.sideStageAppId) { |
2566 | + if ((priv.mainStageAppId && !priv.sideStageAppId) || !priv.sideStageEnabled) { |
2567 | return "main"; |
2568 | } |
2569 | if (!priv.mainStageAppId && priv.sideStageAppId) { |
2570 | @@ -375,12 +406,26 @@ |
2571 | // Flickabe.contentX wiggles during resizes. Don't react to it. |
2572 | return; |
2573 | } |
2574 | - if (spreadView.phase == 0 && spreadView.shiftedContentX > spreadView.width * spreadView.positionMarker2) { |
2575 | - spreadView.phase = 1; |
2576 | - } else if (spreadView.phase == 1 && spreadView.shiftedContentX > spreadView.width * spreadView.positionMarker4) { |
2577 | - spreadView.phase = 2; |
2578 | - } else if (spreadView.phase == 1 && spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2) { |
2579 | - spreadView.phase = 0; |
2580 | + |
2581 | + switch (phase) { |
2582 | + case 0: |
2583 | + // the "spreadEnabled" part is because when code does "phase = 0; contentX = -shift" to |
2584 | + // dismiss the spread because spreadEnabled went to false, for some reason, during tests, |
2585 | + // Flickable might jump in and change contentX value back, causing the code below to do |
2586 | + // "phase = 1" which will make the spread stay. |
2587 | + // It sucks that we have no control whatsoever over whether or when Flickable animates its |
2588 | + // contentX. |
2589 | + if (root.spreadEnabled && shiftedContentX > width * positionMarker2) { |
2590 | + phase = 1; |
2591 | + } |
2592 | + break; |
2593 | + case 1: |
2594 | + if (shiftedContentX < width * positionMarker2) { |
2595 | + phase = 0; |
2596 | + } else if (shiftedContentX >= width * positionMarker4 && !spreadDragArea.dragging) { |
2597 | + phase = 2; |
2598 | + } |
2599 | + break; |
2600 | } |
2601 | } |
2602 | |
2603 | @@ -397,6 +442,7 @@ |
2604 | } |
2605 | } |
2606 | function snapTo(index) { |
2607 | + snapAnimation.stop(); |
2608 | spreadView.selectedIndex = index; |
2609 | snapAnimation.targetContentX = -shift; |
2610 | snapAnimation.start(); |
2611 | @@ -406,55 +452,41 @@ |
2612 | // We don't want to really reorder them in the model because that allows us to keep track |
2613 | // of the last focused order. |
2614 | function indexToZIndex(index) { |
2615 | + // only shuffle when we've got a main and overlay |
2616 | + if (state !== "mainAndOverlay") return index; |
2617 | + |
2618 | var app = ApplicationManager.get(index); |
2619 | if (!app) { |
2620 | return index; |
2621 | } |
2622 | |
2623 | - var active = app.appId == priv.mainStageAppId || app.appId == priv.sideStageAppId; |
2624 | - if (active && app.stage == ApplicationInfoInterface.MainStage) { |
2625 | - // if this app is active, and its the MainStage, always put it to index 0 |
2626 | + // don't shuffle indexes greater than "actives or next" |
2627 | + if (index > 2) return index; |
2628 | + |
2629 | + if (app.appId === priv.mainStageAppId) { |
2630 | + // Active main stage always at 0 |
2631 | return 0; |
2632 | } |
2633 | - if (active && app.stage == ApplicationInfoInterface.SideStage) { |
2634 | - if (!priv.mainStageAppId) { |
2635 | - // Only have SS apps running. Put the active one at 0 |
2636 | - return 0; |
2637 | - } |
2638 | - |
2639 | - // Precondition now: There's an active MS app and this is SS app: |
2640 | - if (spreadView.nextInStack >= 0 && ApplicationManager.get(spreadView.nextInStack).stage == ApplicationInfoInterface.MainStage) { |
2641 | - // If the next app coming from the right is a MS app, we need to elevate this SS ap above it. |
2642 | - // Put it to at least level 2, or higher if there's more apps coming in before this one. |
2643 | - return Math.max(index, 2); |
2644 | - } else { |
2645 | - // if this is no next app to come in from the right, place this one at index 1, just on top the active MS app. |
2646 | - return 1; |
2647 | - } |
2648 | - } |
2649 | - if (index <= 2 && app.stage == ApplicationInfoInterface.MainStage && priv.sideStageAppId) { |
2650 | - // Ok, this is an inactive MS app. If there's an active SS app around, we need to place this one |
2651 | - // in between the active MS app and the active SS app, so that it comes in from there when dragging from the right. |
2652 | - // If there's now active SS app, just leave it where it is. |
2653 | - return priv.indexOf(priv.sideStageAppId) < index ? index - 1 : index; |
2654 | - } |
2655 | - if (index == spreadView.nextInStack && app.stage == ApplicationInfoInterface.SideStage) { |
2656 | - // This is a SS app and the next one to come in from the right: |
2657 | - if (priv.sideStageAppId && priv.mainStageAppId) { |
2658 | - // If there's both, an active MS and an active SS app, put this one right on top of that |
2659 | - return 2; |
2660 | - } |
2661 | - // Or if there's only one other active app, put it on top of that. |
2662 | - // The case that there isn't any other active app is already handled above. |
2663 | - return 1; |
2664 | - } |
2665 | - if (index == 2 && spreadView.nextInStack == 1 && priv.sideStageAppId) { |
2666 | - // If its index 2 but not the next one to come in, it means |
2667 | - // we've pulled another one down to index 2. Move this one up to 2 instead. |
2668 | - return 3; |
2669 | - } |
2670 | - // don't touch all others... (mostly index > 3 + simple cases where the above doesn't shuffle much) |
2671 | - return index; |
2672 | + |
2673 | + if (spreadView.nextInStack > 0) { |
2674 | + var nextAppInStack = ApplicationManager.get(spreadView.nextInStack); |
2675 | + |
2676 | + if (index === spreadView.nextInStack) { |
2677 | + // this is the next app in stack. |
2678 | + |
2679 | + if (app.stage === ApplicationInfoInterface.SideStage) { |
2680 | + // if the next app in stack is a sidestage app, it must order on top of other side stage app |
2681 | + return Math.min(2, ApplicationManager.count-1); |
2682 | + } |
2683 | + return 1; |
2684 | + } |
2685 | + if (nextAppInStack.stage === ApplicationInfoInterface.SideStage) { |
2686 | + // if the next app in stack is a sidestage app, it must order on top of other side stage app |
2687 | + return 1; |
2688 | + } |
2689 | + return Math.min(2, ApplicationManager.count-1); |
2690 | + } |
2691 | + return Math.min(index+1, ApplicationManager.count-1); |
2692 | } |
2693 | |
2694 | SequentialAnimation { |
2695 | @@ -472,8 +504,12 @@ |
2696 | script: { |
2697 | if (spreadView.selectedIndex >= 0) { |
2698 | var newIndex = spreadView.selectedIndex; |
2699 | + var application = ApplicationManager.get(newIndex); |
2700 | + if (application.stage === ApplicationInfoInterface.SideStage) { |
2701 | + sideStage.showNow(); |
2702 | + } |
2703 | spreadView.selectedIndex = -1; |
2704 | - ApplicationManager.focusApplication(ApplicationManager.get(newIndex).appId); |
2705 | + ApplicationManager.focusApplication(application.appId); |
2706 | spreadView.phase = 0; |
2707 | spreadView.contentX = -spreadView.shift; |
2708 | } |
2709 | @@ -491,87 +527,82 @@ |
2710 | spreadView.snapTo(0); |
2711 | } |
2712 | |
2713 | - Rectangle { |
2714 | - id: sideStageBackground |
2715 | - color: "black" |
2716 | - width: spreadView.sideStageWidth * (1 - sideStageDragHandle.progress) |
2717 | + DropArea { |
2718 | + objectName: "MainStageDropArea" |
2719 | + anchors { |
2720 | + left: parent.left |
2721 | + top: parent.top |
2722 | + bottom: parent.bottom |
2723 | + } |
2724 | + width: spreadView.width - sideStage.width |
2725 | + enabled: priv.sideStageEnabled |
2726 | + |
2727 | + onDropped: { |
2728 | + priv.setAppStage(drag.source.appId, ApplicationInfoInterface.MainStage, true); |
2729 | + ApplicationManager.focusApplication(drag.source.appId); |
2730 | + } |
2731 | + keys: "SideStage" |
2732 | + } |
2733 | + |
2734 | + SideStage { |
2735 | + id: sideStage |
2736 | + objectName: "sideStage" |
2737 | height: priv.landscapeHeight |
2738 | x: spreadView.width - width |
2739 | - z: spreadView.indexToZIndex(priv.indexOf(priv.sideStageAppId)) |
2740 | - opacity: spreadView.phase == 0 ? 1 : 0 |
2741 | - Behavior on opacity { UbuntuNumberAnimation {} } |
2742 | - } |
2743 | - |
2744 | - Item { |
2745 | - id: sideStageDragHandle |
2746 | - anchors.right: sideStageBackground.left |
2747 | - anchors.top: sideStageBackground.top |
2748 | - width: units.gu(2) |
2749 | - height: priv.landscapeHeight |
2750 | - z: sideStageBackground.z |
2751 | - opacity: spreadView.phase <= 0 && spreadView.sideStageVisible ? 1 : 0 |
2752 | - property real progress: 0 |
2753 | - property bool dragging: false |
2754 | - |
2755 | - Behavior on opacity { UbuntuNumberAnimation {} } |
2756 | - |
2757 | - Connections { |
2758 | - target: spreadView |
2759 | - onSideStageVisibleChanged: { |
2760 | - if (spreadView.sideStageVisible) { |
2761 | - sideStageDragHandle.progress = 0; |
2762 | + z: { |
2763 | + if (!priv.mainStageAppId) return 0; |
2764 | + |
2765 | + if (priv.sideStageAppId && spreadView.nextInStack > 0) { |
2766 | + var nextAppInStack = ApplicationManager.get(spreadView.nextInStack); |
2767 | + |
2768 | + if (nextAppInStack.stage === ApplicationInfoInterface.MainStage) { |
2769 | + // if the next app in stack is a main stage app, put the sidestage on top of it. |
2770 | + return 2; |
2771 | } |
2772 | - } |
2773 | - } |
2774 | - |
2775 | - Image { |
2776 | - anchors.centerIn: parent |
2777 | - width: sideStageDragHandleMouseArea.pressed ? parent.width * 2 : parent.width |
2778 | - height: parent.height |
2779 | - source: "graphics/sidestage_handle@20.png" |
2780 | - Behavior on width { UbuntuNumberAnimation {} } |
2781 | - } |
2782 | - |
2783 | - MouseArea { |
2784 | - id: sideStageDragHandleMouseArea |
2785 | + return 1; |
2786 | + } |
2787 | + |
2788 | + return 1; |
2789 | + } |
2790 | + visible: progress != 0 |
2791 | + enabled: priv.sideStageEnabled && sideStageDropArea.dropAllowed |
2792 | + opacity: priv.sideStageEnabled && !spreadView.active ? 1 : 0 |
2793 | + Behavior on opacity { UbuntuNumberAnimation {} } |
2794 | + |
2795 | + onShownChanged: { |
2796 | + if (!shown && ApplicationManager.focusedApplicationId == priv.sideStageAppId) { |
2797 | + ApplicationManager.requestFocusApplication(priv.mainStageAppId); |
2798 | + } |
2799 | + priv.updateStageApps(); |
2800 | + if (shown && priv.sideStageAppId) { |
2801 | + ApplicationManager.requestFocusApplication(priv.sideStageAppId); |
2802 | + } |
2803 | + } |
2804 | + |
2805 | + DropArea { |
2806 | + id: sideStageDropArea |
2807 | + objectName: "SideStageDropArea" |
2808 | anchors.fill: parent |
2809 | - enabled: spreadView.shiftedContentX == 0 |
2810 | - property int startX |
2811 | - property var gesturePoints: new Array() |
2812 | - property real totalDiff |
2813 | - |
2814 | - onPressed: { |
2815 | - gesturePoints = []; |
2816 | - startX = mouseX; |
2817 | - totalDiff = 0.0; |
2818 | - sideStageDragHandle.progress = 0; |
2819 | - sideStageDragHandle.dragging = true; |
2820 | - } |
2821 | - onMouseXChanged: { |
2822 | - totalDiff += mouseX - startX; |
2823 | - if (priv.mainStageAppId) { |
2824 | - sideStageDragHandle.progress = Math.max(0, totalDiff / spreadView.sideStageWidth); |
2825 | - } |
2826 | - gesturePoints.push(mouseX); |
2827 | - } |
2828 | - onReleased: { |
2829 | - if (priv.mainStageAppId) { |
2830 | - var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints); |
2831 | - sideStageDragSnapAnimation.to = sideStageDragHandle.progress > 0.5 || oneWayFlick ? 1 : 0; |
2832 | - sideStageDragSnapAnimation.start(); |
2833 | - } else { |
2834 | - sideStageDragHandle.dragging = false; |
2835 | - } |
2836 | - } |
2837 | - } |
2838 | - UbuntuNumberAnimation { |
2839 | - id: sideStageDragSnapAnimation |
2840 | - target: sideStageDragHandle |
2841 | - property: "progress" |
2842 | - |
2843 | - onRunningChanged: { |
2844 | - if (!running) { |
2845 | - sideStageDragHandle.dragging = false; |
2846 | + |
2847 | + property bool dropAllowed: true |
2848 | + |
2849 | + onEntered: { |
2850 | + dropAllowed = drag.keys != "Disabled"; |
2851 | + } |
2852 | + onExited: { |
2853 | + dropAllowed = true; |
2854 | + } |
2855 | + onDropped: { |
2856 | + if (drop.keys == "MainStage") { |
2857 | + priv.setAppStage(drop.source.appId, ApplicationInfoInterface.SideStage, true); |
2858 | + ApplicationManager.requestFocusApplication(drop.source.appId); |
2859 | + } |
2860 | + } |
2861 | + drag { |
2862 | + onSourceChanged: { |
2863 | + if (!sideStageDropArea.drag.source) { |
2864 | + dropAllowed = true; |
2865 | + } |
2866 | } |
2867 | } |
2868 | } |
2869 | @@ -586,35 +617,47 @@ |
2870 | id: spreadTile |
2871 | objectName: model.appId ? "tabletSpreadDelegate_" + model.appId |
2872 | : "tabletSpreadDelegate_null"; |
2873 | - width: { |
2874 | - if (wantsMainStage) { |
2875 | - return spreadView.width; |
2876 | - } else { |
2877 | - return spreadView.sideStageWidth; |
2878 | - } |
2879 | - } |
2880 | - height: { |
2881 | - if (wantsMainStage) { |
2882 | - return spreadView.height; |
2883 | - } else { |
2884 | - return priv.landscapeHeight; |
2885 | - } |
2886 | - } |
2887 | - active: model.appId == priv.mainStageAppId || model.appId == priv.sideStageAppId |
2888 | - zIndex: spreadView.indexToZIndex(index) |
2889 | + width: spreadView.width |
2890 | + height: spreadView.height |
2891 | + active: appId == priv.mainStageAppId || appId == priv.sideStageAppId |
2892 | + zIndex: selected && stage == ApplicationInfoInterface.MainStage ? 0 : spreadView.indexToZIndex(index) |
2893 | selected: spreadView.selectedIndex == index |
2894 | otherSelected: spreadView.selectedIndex >= 0 && !selected |
2895 | - isInSideStage: priv.sideStageAppId == model.appId |
2896 | + isInSideStage: priv.sideStageAppId === appId |
2897 | interactive: !spreadView.interactive && spreadView.phase === 0 && root.interactive |
2898 | swipeToCloseEnabled: spreadView.interactive && !snapAnimation.running |
2899 | maximizedAppTopMargin: root.maximizedAppTopMargin |
2900 | - dragOffset: !isDash && model.appId == priv.mainStageAppId && root.inverseProgress > 0 && spreadView.phase === 0 ? root.inverseProgress : 0 |
2901 | + dragOffset: !isDash && appId == priv.mainStageAppId && root.inverseProgress > 0 && spreadView.phase === 0 ? root.inverseProgress : 0 |
2902 | application: ApplicationManager.get(index) |
2903 | closeable: !isDash |
2904 | - |
2905 | - readonly property bool wantsMainStage: model.stage == ApplicationInfoInterface.MainStage |
2906 | - |
2907 | - readonly property bool isDash: model.appId == "unity8-dash" |
2908 | + stage: model.stage |
2909 | + fullscreen: { |
2910 | + if (mainApp && stage === ApplicationInfoInterface.SideStage) { |
2911 | + return mainApp.fullscreen; |
2912 | + } |
2913 | + return application ? application.fullscreen : false; |
2914 | + } |
2915 | + |
2916 | + supportedOrientations: { |
2917 | + if (application) { |
2918 | + var orientations = application.supportedOrientations; |
2919 | + if (stage == ApplicationInfoInterface.MainStage) { |
2920 | + // When an app is in the mainstage, it always supports Landscape|InvertedLandscape |
2921 | + // so that we can drag it from the main stage to the side stage |
2922 | + orientations |= Qt.LandscapeOrientation | Qt.InvertedLandscapeOrientation; |
2923 | + } |
2924 | + return orientations; |
2925 | + } else { |
2926 | + // we just don't care |
2927 | + return Qt.PortraitOrientation | |
2928 | + Qt.LandscapeOrientation | |
2929 | + Qt.InvertedPortraitOrientation | |
2930 | + Qt.InvertedLandscapeOrientation; |
2931 | + } |
2932 | + } |
2933 | + |
2934 | + readonly property string appId: model.appId |
2935 | + readonly property bool isDash: appId == "unity8-dash" |
2936 | |
2937 | Binding { |
2938 | target: spreadTile.application |
2939 | @@ -646,6 +689,28 @@ |
2940 | enabled: spreadView.closingIndex >= 0 |
2941 | UbuntuNumberAnimation {} |
2942 | } |
2943 | + Connections { |
2944 | + target: priv |
2945 | + onSideStageEnabledChanged: refreshStage() |
2946 | + } |
2947 | + |
2948 | + Component.onCompleted: { |
2949 | + refreshStage() |
2950 | + stageChanged.connect(priv.updateStageApps); |
2951 | + } |
2952 | + |
2953 | + function refreshStage() { |
2954 | + var stage = ApplicationInfoInterface.MainStage; |
2955 | + if (priv.sideStageEnabled) { |
2956 | + if (application && application.supportedOrientations & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) { |
2957 | + stage = WindowStateStorage.getStage(appId); |
2958 | + } |
2959 | + } |
2960 | + |
2961 | + if (model.stage !== stage) { |
2962 | + priv.setAppStage(appId, stage, false); |
2963 | + } |
2964 | + } |
2965 | |
2966 | // This is required because none of the bindings are triggered in some cases: |
2967 | // When an app is closed, it might happen that ApplicationManager.get(nextInStack) |
2968 | @@ -670,7 +735,7 @@ |
2969 | |
2970 | // TODO: Hiding tile when progress is such that it will be off screen. |
2971 | property bool occluded: { |
2972 | - if (spreadView.active) return false; |
2973 | + if (spreadView.active && !offScreen) return false; |
2974 | else if (spreadTile.active) return false; |
2975 | else if (xTranslateAnimating) return false; |
2976 | else if (z <= 1 && priv.focusedAppDelegateIsDislocated) return false; |
2977 | @@ -694,17 +759,87 @@ |
2978 | return progress; |
2979 | } |
2980 | |
2981 | - shellOrientationAngle: wantsMainStage ? root.shellOrientationAngle : 0 |
2982 | - shellOrientation: wantsMainStage ? root.shellOrientation : Qt.PortraitOrientation |
2983 | - orientations: Orientations { |
2984 | - primary: spreadTile.wantsMainStage ? root.orientations.primary : Qt.PortraitOrientation |
2985 | - native_: spreadTile.wantsMainStage ? root.orientations.native_ : Qt.PortraitOrientation |
2986 | + shellOrientationAngle: root.shellOrientationAngle |
2987 | + shellOrientation: root.shellOrientation |
2988 | + orientations: root.orientations |
2989 | + |
2990 | + states: [ |
2991 | + State { |
2992 | + name: "MainStage" |
2993 | + when: spreadTile.stage == ApplicationInfoInterface.MainStage |
2994 | + }, |
2995 | + State { |
2996 | + name: "SideStage" |
2997 | + when: spreadTile.stage == ApplicationInfoInterface.SideStage |
2998 | + |
2999 | + PropertyChanges { |
3000 | + target: spreadTile |
3001 | + width: spreadView.sideStageWidth |
3002 | + height: priv.landscapeHeight |
3003 | + |
3004 | + supportedOrientations: Qt.PortraitOrientation |
3005 | + shellOrientationAngle: 0 |
3006 | + shellOrientation: Qt.PortraitOrientation |
3007 | + orientations: sideStageOrientations |
3008 | + } |
3009 | + } |
3010 | + ] |
3011 | + |
3012 | + Orientations { |
3013 | + id: sideStageOrientations |
3014 | + primary: Qt.PortraitOrientation |
3015 | + native_: Qt.PortraitOrientation |
3016 | portrait: root.orientations.portrait |
3017 | invertedPortrait: root.orientations.invertedPortrait |
3018 | landscape: root.orientations.landscape |
3019 | invertedLandscape: root.orientations.invertedLandscape |
3020 | } |
3021 | |
3022 | + transitions: [ |
3023 | + Transition { |
3024 | + to: "SideStage" |
3025 | + SequentialAnimation { |
3026 | + PropertyAction { |
3027 | + target: spreadTile |
3028 | + properties: "width,height,supportedOrientations,shellOrientationAngle,shellOrientation,orientations" |
3029 | + } |
3030 | + ScriptAction { |
3031 | + script: { |
3032 | + // rotate immediately. |
3033 | + spreadTile.matchShellOrientation(); |
3034 | + if (ApplicationManager.focusedApplicationId === spreadTile.appId && |
3035 | + priv.sideStageEnabled && !sideStage.shown) { |
3036 | + // Sidestage was focused, so show the side stage. |
3037 | + sideStage.show(); |
3038 | + // if we've switched to a main app which doesnt support portrait, hide the side stage. |
3039 | + } else if (mainApp && (mainApp.supportedOrientations & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) == 0) { |
3040 | + sideStage.hideNow(); |
3041 | + } |
3042 | + } |
3043 | + } |
3044 | + } |
3045 | + }, |
3046 | + Transition { |
3047 | + from: "SideStage" |
3048 | + SequentialAnimation { |
3049 | + ScriptAction { |
3050 | + script: { |
3051 | + if (priv.sideStageAppId === spreadTile.appId && |
3052 | + mainApp && (mainApp.supportedOrientations & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) == 0) { |
3053 | + // The mainstage app did not natively support portrait orientation, so focus the sidestage. |
3054 | + ApplicationManager.requestFocusApplication(spreadTile.appId); |
3055 | + } |
3056 | + } |
3057 | + } |
3058 | + PropertyAction { |
3059 | + target: spreadTile |
3060 | + properties: "width,height,supportedOrientations,shellOrientationAngle,shellOrientation,orientations" |
3061 | + } |
3062 | + ScriptAction { script: { spreadTile.matchShellOrientation(); } } |
3063 | + } |
3064 | + } |
3065 | + ] |
3066 | + |
3067 | onClicked: { |
3068 | if (spreadView.phase == 2) { |
3069 | spreadView.snapTo(index); |
3070 | @@ -724,6 +859,16 @@ |
3071 | ApplicationManager.stopApplication(ApplicationManager.get(index).appId); |
3072 | } |
3073 | |
3074 | + onFocusChanged: { |
3075 | + if (focus && ApplicationManager.focusedApplicationId !== appId) { |
3076 | + ApplicationManager.focusApplication(appId); |
3077 | + } |
3078 | + |
3079 | + if (focus && priv.sideStageEnabled && stage === ApplicationInfoInterface.SideStage) { |
3080 | + sideStage.show(); |
3081 | + } |
3082 | + } |
3083 | + |
3084 | Binding { |
3085 | target: root |
3086 | when: model.appId == priv.mainStageAppId |
3087 | @@ -748,6 +893,71 @@ |
3088 | } |
3089 | } |
3090 | |
3091 | + TabletSideStageTouchGesture { |
3092 | + id: triGestureArea |
3093 | + anchors.fill: parent |
3094 | + enabled: priv.sideStageEnabled && !spreadView.active |
3095 | + property var dragObject: null |
3096 | + property string appId: "" |
3097 | + dragComponent: dragComponent |
3098 | + dragComponentProperties: { "appId": appId } |
3099 | + |
3100 | + onPressed: { |
3101 | + function matchDelegate(obj) { return String(obj.objectName).indexOf("tabletSpreadDelegate") >= 0; } |
3102 | + |
3103 | + var delegateAtCenter = Functions.itemAt(spreadRow, x, y, matchDelegate); |
3104 | + if (!delegateAtCenter) return; |
3105 | + |
3106 | + appId = delegateAtCenter.appId; |
3107 | + } |
3108 | + |
3109 | + onClicked: { |
3110 | + if (sideStage.shown) { |
3111 | + sideStage.hide(); |
3112 | + } else { |
3113 | + sideStage.show(); |
3114 | + } |
3115 | + } |
3116 | + |
3117 | + onDragStarted: { |
3118 | + // If we're dragging to the sidestage. |
3119 | + if (!sideStage.shown) { |
3120 | + sideStage.show(); |
3121 | + } |
3122 | + } |
3123 | + |
3124 | + Component { |
3125 | + id: dragComponent |
3126 | + SessionContainer { |
3127 | + property string appId: "" |
3128 | + property var application: ApplicationManager.findApplication(appId) |
3129 | + |
3130 | + session: application ? application.session : null |
3131 | + interactive: false |
3132 | + resizeSurface: false |
3133 | + focus: false |
3134 | + |
3135 | + width: units.gu(40) |
3136 | + height: units.gu(40) |
3137 | + |
3138 | + Drag.hotSpot.x: width/2 |
3139 | + Drag.hotSpot.y: height/2 |
3140 | + // only accept opposite stage. |
3141 | + Drag.keys: { |
3142 | + if (!application) return "Disabled"; |
3143 | + |
3144 | + if (application.stage === ApplicationInfo.MainStage) { |
3145 | + if (application.supportedOrientations & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) { |
3146 | + return "MainStage"; |
3147 | + } |
3148 | + return "Disabled"; |
3149 | + } |
3150 | + return "SideStage"; |
3151 | + } |
3152 | + } |
3153 | + } |
3154 | + } |
3155 | + |
3156 | //eat touch events during the right edge gesture |
3157 | MouseArea { |
3158 | anchors.fill: parent |
3159 | @@ -757,7 +967,8 @@ |
3160 | DirectionalDragArea { |
3161 | id: spreadDragArea |
3162 | objectName: "spreadDragArea" |
3163 | - anchors { top: parent.top; right: parent.right; bottom: parent.bottom } |
3164 | + x: parent.width - root.dragAreaWidth |
3165 | + anchors { top: parent.top; bottom: parent.bottom } |
3166 | width: root.dragAreaWidth |
3167 | direction: Direction.Leftwards |
3168 | enabled: (spreadView.phase != 2 && root.spreadEnabled) || dragging |
3169 | |
3170 | === modified file 'qml/Stages/TransformedTabletSpreadDelegate.qml' |
3171 | --- qml/Stages/TransformedTabletSpreadDelegate.qml 2015-11-04 14:58:05 +0000 |
3172 | +++ qml/Stages/TransformedTabletSpreadDelegate.qml 2016-03-11 12:07:48 +0000 |
3173 | @@ -31,6 +31,7 @@ |
3174 | // Set this to true when this tile a currently active on either the MS or the SS. |
3175 | property bool active: false |
3176 | |
3177 | + property int stage |
3178 | property int zIndex |
3179 | property real progress: 0 |
3180 | property real animatedProgress: 0 |
3181 | @@ -49,10 +50,11 @@ |
3182 | |
3183 | property int dragOffset: 0 |
3184 | readonly property alias xTranslateAnimating: xTranslateAnimation.running |
3185 | + readonly property bool offScreen: priv.xTranslate >= 0 |
3186 | |
3187 | dropShadow: spreadView.active || |
3188 | (active |
3189 | - && (model.stage == ApplicationInfoInterface.MainStage || !priv.shellIsLandscape) |
3190 | + && (stage == ApplicationInfoInterface.MainStage || !priv.shellIsLandscape) |
3191 | && priv.xTranslate != 0) |
3192 | |
3193 | onSelectedChanged: { |
3194 | @@ -141,9 +143,9 @@ |
3195 | enabled: !spreadView.active && |
3196 | !snapAnimation.running && |
3197 | model.appId !== "unity8-dash" && |
3198 | - !spreadView.sideStageDragging && |
3199 | spreadView.animateX && |
3200 | - !spreadView.beingResized |
3201 | + !spreadView.beingResized && |
3202 | + priv.state !== "sideStage" |
3203 | UbuntuNumberAnimation { |
3204 | id: xTranslateAnimation |
3205 | duration: UbuntuAnimation.FastDuration |
3206 | @@ -153,21 +155,20 @@ |
3207 | property real xTranslate: { |
3208 | var newTranslate = 0; |
3209 | |
3210 | - if (otherSelected) { |
3211 | - return priv.selectedXTranslate; |
3212 | - } |
3213 | - |
3214 | - if (isSelected) { |
3215 | - if (model.stage == ApplicationInfoInterface.MainStage) { |
3216 | + // selected app or opposite stage active app. |
3217 | + if (isSelected || (otherSelected && root.active && spreadView.selectedApplication && spreadView.selectedApplication.stage !== stage)) { |
3218 | + if (stage == ApplicationInfoInterface.MainStage) { |
3219 | return linearAnimation(selectedProgress, negativeProgress, selectedXTranslate, -spreadView.width, root.progress); |
3220 | } else { |
3221 | return linearAnimation(selectedProgress, negativeProgress, selectedXTranslate, -spreadView.sideStageWidth, root.progress); |
3222 | } |
3223 | + } else if (otherSelected) { |
3224 | + return selectedXTranslate; |
3225 | } |
3226 | |
3227 | // The tile should move a bit to the left if a new one comes on top of it, but not for the Side Stage and not |
3228 | // when we're only dragging the side stage in on top of a main stage app |
3229 | - var shouldMoveAway = spreadView.nextInStack >= 0 && priv.movedActive && model.stage === ApplicationInfoInterface.MainStage && |
3230 | + var shouldMoveAway = spreadView.nextInStack >= 0 && priv.movedActive && stage === ApplicationInfoInterface.MainStage && |
3231 | ApplicationManager.get(spreadView.nextInStack).stage === ApplicationInfoInterface.MainStage; |
3232 | |
3233 | if (active) { |
3234 | @@ -177,36 +178,45 @@ |
3235 | newTranslate += linearAnimation(0, spreadView.positionMarker2, 0, -units.gu(4), root.animatedProgress); |
3236 | } |
3237 | newTranslate += root.dragOffset; |
3238 | - } |
3239 | - if (!spreadView.active && model.appId == "unity8-dash" && !root.active) { |
3240 | + } else if (!spreadView.active && model.appId == "unity8-dash") { |
3241 | newTranslate -= root.width; |
3242 | } |
3243 | |
3244 | - if (nextInStack && spreadView.phase == 0) { |
3245 | - if (model.stage == ApplicationInfoInterface.MainStage) { |
3246 | - if (spreadView.sideStageVisible && root.progress > 0) { |
3247 | - // Move it so it appears from behind the side stage immediately |
3248 | - newTranslate += -spreadView.sideStageWidth; |
3249 | - } |
3250 | - } |
3251 | - |
3252 | - if (model.stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) { |
3253 | - // This is when we only drag the side stage in, without rotation or snapping |
3254 | - newTranslate = linearAnimation(0, spreadView.positionMarker2, 0, -spreadView.sideStageWidth, root.progress); |
3255 | - } else { |
3256 | - newTranslate += linearAnimation(0, spreadView.positionMarker2, 0, -spreadView.sideStageWidth * spreadView.snapPosition, root.animatedProgress); |
3257 | + if (!spreadView.active) { |
3258 | + return newTranslate; |
3259 | + } |
3260 | + |
3261 | + var shadowOffset = units.gu(2); |
3262 | + |
3263 | + if (spreadView.phase == 0) { |
3264 | + if (nextInStack) { |
3265 | + if (stage == ApplicationInfoInterface.MainStage) { |
3266 | + if (spreadView.sideStageVisible && root.progress > 0) { |
3267 | + // Move it so it appears from behind the side stage immediately |
3268 | + newTranslate += -spreadView.sideStageWidth; |
3269 | + } |
3270 | + } |
3271 | + |
3272 | + if (stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) { |
3273 | + // This is when we only drag the side stage in, without rotation or snapping |
3274 | + newTranslate = linearAnimation(0, spreadView.positionMarker2, 0, -spreadView.sideStageWidth, root.animatedProgress); |
3275 | + } else { |
3276 | + newTranslate += linearAnimation(0, spreadView.positionMarker2, 0, -spreadView.sideStageWidth * spreadView.snapPosition, root.animatedProgress); |
3277 | + } |
3278 | + } else if (!root.active) { |
3279 | + newTranslate += shadowOffset; |
3280 | } |
3281 | } |
3282 | |
3283 | if (spreadView.phase == 1) { |
3284 | if (nextInStack) { |
3285 | - if (model.stage == ApplicationInfoInterface.MainStage) { |
3286 | + if (stage == ApplicationInfoInterface.MainStage) { |
3287 | var startValue = -spreadView.sideStageWidth * spreadView.snapPosition + (spreadView.sideStageVisible ? -spreadView.sideStageWidth : 0); |
3288 | newTranslate += linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, startValue, priv.phase2StartTranslate, root.animatedProgress); |
3289 | } else { |
3290 | var endValue = -spreadView.width + spreadView.width * root.zIndex / 6; |
3291 | if (!spreadView.sideStageVisible) { |
3292 | - newTranslate += linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, -spreadView.sideStageWidth, priv.phase2StartTranslate, root.progress); |
3293 | + newTranslate += linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, -spreadView.sideStageWidth, priv.phase2StartTranslate, root.animatedProgress); |
3294 | } else { |
3295 | newTranslate += linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, -spreadView.sideStageWidth * spreadView.snapPosition, priv.phase2StartTranslate, root.animatedProgress); |
3296 | } |
3297 | @@ -215,16 +225,20 @@ |
3298 | var startProgress = spreadView.positionMarker2 - (zIndex * spreadView.positionMarker2 / 2); |
3299 | var endProgress = spreadView.positionMarker4 - (zIndex * spreadView.tileDistance / spreadView.width); |
3300 | var startTranslate = -root.width + (shouldMoveAway ? -units.gu(4) : 0); |
3301 | - newTranslate = linearAnimation(startProgress, endProgress, startTranslate, priv.phase2StartTranslate, root.progress); |
3302 | + newTranslate = linearAnimation(startProgress, endProgress, startTranslate, priv.phase2StartTranslate, root.animatedProgress); |
3303 | } else { |
3304 | var startProgress = spreadView.positionMarker2 - (zIndex * spreadView.positionMarker2 / 2); |
3305 | var endProgress = spreadView.positionMarker4 - (zIndex * spreadView.tileDistance / spreadView.width); |
3306 | - newTranslate = linearAnimation(startProgress, endProgress, 0, priv.phase2StartTranslate, root.progress); |
3307 | + newTranslate = linearAnimation(startProgress, endProgress, shadowOffset * Math.max(0, zIndex-2), priv.phase2StartTranslate, root.animatedProgress); |
3308 | } |
3309 | } |
3310 | |
3311 | if (spreadView.phase == 2) { |
3312 | - newTranslate = -easingCurve.value * (spreadView.width - root.zIndex * animatedEndDistance); |
3313 | + if (easingCurve.value == 0 && !nextInStack) { |
3314 | + newTranslate = shadowOffset; |
3315 | + } else { |
3316 | + newTranslate = -easingCurve.value * (spreadView.width - root.zIndex * animatedEndDistance); |
3317 | + } |
3318 | } |
3319 | |
3320 | return newTranslate; |
3321 | @@ -235,33 +249,30 @@ |
3322 | return 1; |
3323 | } |
3324 | |
3325 | - if (otherSelected) { |
3326 | + // selected app or opposite stage active app. |
3327 | + if (isSelected || (otherSelected && root.active && spreadView.selectedApplication && spreadView.selectedApplication.stage !== stage)) { |
3328 | + return linearAnimation(selectedProgress, negativeProgress, selectedScale, 1, root.progress); |
3329 | + } else if (otherSelected) { |
3330 | return selectedScale; |
3331 | } |
3332 | |
3333 | - if (isSelected) { |
3334 | - return linearAnimation(selectedProgress, negativeProgress, selectedScale, 1, root.progress); |
3335 | - } |
3336 | - |
3337 | if (spreadView.phase == 0) { |
3338 | if (nextInStack) { |
3339 | - if (model.stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) { |
3340 | + if (stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) { |
3341 | return 1; |
3342 | } else { |
3343 | var targetScale = root.dragStartScale - ((root.dragStartScale - 1) * spreadView.snapPosition); |
3344 | return linearAnimation(0, spreadView.positionMarker2, root.dragStartScale, targetScale, root.animatedProgress); |
3345 | } |
3346 | - } else if (active) { |
3347 | + } else { |
3348 | return 1; |
3349 | - } else { |
3350 | - return linearAnimation(0, spreadView.positionMarker2, root.startScale, root.endScale, root.progress); |
3351 | } |
3352 | } |
3353 | |
3354 | if (spreadView.phase == 1) { |
3355 | if (nextInStack) { |
3356 | var startScale = 1; |
3357 | - if (model.stage !== ApplicationInfoInterface.SideStage || spreadView.sideStageVisible) { |
3358 | + if (stage !== ApplicationInfoInterface.SideStage || spreadView.sideStageVisible) { |
3359 | startScale = root.dragStartScale - ((root.dragStartScale - 1) * spreadView.snapPosition); |
3360 | } |
3361 | return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, startScale, priv.phase2StartScale, root.animatedProgress); |
3362 | @@ -283,21 +294,22 @@ |
3363 | return 0; |
3364 | } |
3365 | |
3366 | - if (otherSelected) { |
3367 | + // selected app or opposite stage active app. |
3368 | + if (isSelected || (otherSelected && root.active && spreadView.selectedApplication && spreadView.selectedApplication.stage !== stage)) { |
3369 | + return linearAnimation(selectedProgress, negativeProgress, selectedAngle, 0, root.progress); |
3370 | + } else if (otherSelected) { |
3371 | return selectedAngle; |
3372 | } |
3373 | - if (isSelected) { |
3374 | - return linearAnimation(selectedProgress, negativeProgress, selectedAngle, 0, root.progress); |
3375 | - } |
3376 | |
3377 | // The tile should rotate a bit when another one comes on top, but not when only dragging the side stage in |
3378 | - var shouldMoveAway = spreadView.nextInStack >= 0 && movedActive && |
3379 | + var shouldMoveAway = spreadView.nextInStack == -1 || |
3380 | + spreadView.nextInStack >= 0 && priv.movedActive && |
3381 | (ApplicationManager.get(spreadView.nextInStack).stage === ApplicationInfoInterface.MainStage || |
3382 | - model.stage == ApplicationInfoInterface.SideStage); |
3383 | + stage == ApplicationInfoInterface.SideStage); |
3384 | |
3385 | if (spreadView.phase == 0) { |
3386 | if (nextInStack) { |
3387 | - if (model.stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) { |
3388 | + if (stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) { |
3389 | return 0; |
3390 | } else { |
3391 | return linearAnimation(0, spreadView.positionMarker2, root.startAngle, root.startAngle * (1-spreadView.snapPosition), root.animatedProgress); |
3392 | @@ -309,7 +321,7 @@ |
3393 | } |
3394 | if (spreadView.phase == 1) { |
3395 | if (nextInStack) { |
3396 | - if (model.stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) { |
3397 | + if (stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) { |
3398 | return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, 0, priv.phase2StartAngle, root.animatedProgress); |
3399 | } else { |
3400 | return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, root.startAngle * (1-spreadView.snapPosition), priv.phase2StartAngle, root.animatedProgress); |
3401 | @@ -328,31 +340,47 @@ |
3402 | } |
3403 | |
3404 | property real opacityTransform: { |
3405 | - if (otherSelected && spreadView.phase == 2) { |
3406 | + // animate opacity for items not snapping into view. |
3407 | + if (spreadView.phase !== 2) return 1; |
3408 | + if (root.isSelected) return 1; |
3409 | + |
3410 | + if (otherSelected) { |
3411 | + if (root.active && spreadView.selectedApplication && spreadView.selectedApplication.stage !== stage) { |
3412 | + return 1; |
3413 | + } |
3414 | return linearAnimation(selectedProgress, negativeProgress, selectedOpacity, 0, root.progress); |
3415 | } |
3416 | - |
3417 | return 1; |
3418 | } |
3419 | |
3420 | property real topMarginProgress: { |
3421 | - if (priv.isSelected) { |
3422 | + // selected app or opposite stage active app. |
3423 | + if (isSelected || (otherSelected && root.active && spreadView.selectedApplication && spreadView.selectedApplication.stage !== stage)) { |
3424 | return linearAnimation(selectedProgress, negativeProgress, selectedTopMarginProgress, 0, root.progress); |
3425 | } |
3426 | - switch (spreadView.phase) { |
3427 | - case 0: |
3428 | + |
3429 | + if (spreadView.phase == 0) { |
3430 | return 0; |
3431 | - case 1: |
3432 | - return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, |
3433 | - 0, priv.phase2StartTopMarginProgress, root.progress); |
3434 | + } else if (spreadView.phase == 1) { |
3435 | + if (nextInStack) { |
3436 | + return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, |
3437 | + 0, 1, root.progress); |
3438 | + } |
3439 | + var startProgress = spreadView.positionMarker2 - (zIndex * spreadView.positionMarker2 / 2); |
3440 | + var endProgress = spreadView.positionMarker4 - (zIndex * spreadView.tileDistance / spreadView.width); |
3441 | + return linearAnimation(startProgress, endProgress, 0, 1, root.progress); |
3442 | } |
3443 | return 1; |
3444 | } |
3445 | |
3446 | states: [ |
3447 | State { |
3448 | - name: "sideStageDragging"; when: spreadView.sideStageDragging && root.isInSideStage |
3449 | - PropertyChanges { target: priv; xTranslate: -spreadView.sideStageWidth + spreadView.sideStageWidth * spreadView.sideStageDragProgress } |
3450 | + name: "sideStage"; |
3451 | + when: root.isInSideStage && spreadView.shiftedContentX == 0 && spreadView.phase == 0 |
3452 | + PropertyChanges { |
3453 | + target: priv; |
3454 | + xTranslate: -spreadView.sideStageWidth + spreadView.sideStageWidth * (1-spreadView.sideStageDragProgress) |
3455 | + } |
3456 | } |
3457 | ] |
3458 | } |
3459 | |
3460 | === added file 'qml/Stages/graphics/sidestage_drag.svg' |
3461 | --- qml/Stages/graphics/sidestage_drag.svg 1970-01-01 00:00:00 +0000 |
3462 | +++ qml/Stages/graphics/sidestage_drag.svg 2016-03-11 12:07:48 +0000 |
3463 | @@ -0,0 +1,207 @@ |
3464 | +<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
3465 | +<!-- Created with Inkscape (http://www.inkscape.org/) --> |
3466 | + |
3467 | +<svg |
3468 | + xmlns:dc="http://purl.org/dc/elements/1.1/" |
3469 | + xmlns:cc="http://creativecommons.org/ns#" |
3470 | + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" |
3471 | + xmlns:svg="http://www.w3.org/2000/svg" |
3472 | + xmlns="http://www.w3.org/2000/svg" |
3473 | + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" |
3474 | + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" |
3475 | + width="540" |
3476 | + height="324" |
3477 | + id="svg4874" |
3478 | + version="1.1" |
3479 | + inkscape:version="0.91+devel r" |
3480 | + viewBox="0 0 540 324" |
3481 | + sodipodi:docname="gesture_3f_touch.svg"> |
3482 | + <defs |
3483 | + id="defs4876" /> |
3484 | + <sodipodi:namedview |
3485 | + id="base" |
3486 | + pagecolor="#292929" |
3487 | + bordercolor="#666666" |
3488 | + borderopacity="1.0" |
3489 | + inkscape:pageopacity="1" |
3490 | + inkscape:pageshadow="2" |
3491 | + inkscape:zoom="1.1785993" |
3492 | + inkscape:cx="-19.084613" |
3493 | + inkscape:cy="11.707485" |
3494 | + inkscape:document-units="px" |
3495 | + inkscape:current-layer="g4780" |
3496 | + showgrid="false" |
3497 | + showborder="true" |
3498 | + fit-margin-top="0" |
3499 | + fit-margin-left="0" |
3500 | + fit-margin-right="0" |
3501 | + fit-margin-bottom="0" |
3502 | + inkscape:snap-bbox="true" |
3503 | + inkscape:bbox-paths="true" |
3504 | + inkscape:bbox-nodes="true" |
3505 | + inkscape:snap-bbox-edge-midpoints="true" |
3506 | + inkscape:snap-bbox-midpoints="true" |
3507 | + inkscape:object-paths="true" |
3508 | + inkscape:snap-intersection-paths="true" |
3509 | + inkscape:object-nodes="true" |
3510 | + inkscape:snap-smooth-nodes="true" |
3511 | + inkscape:snap-midpoints="true" |
3512 | + inkscape:snap-object-midpoints="true" |
3513 | + inkscape:snap-center="true" |
3514 | + showguides="false" |
3515 | + inkscape:guide-bbox="true" |
3516 | + inkscape:snap-global="true" |
3517 | + inkscape:snap-others="false" |
3518 | + inkscape:snap-page="true"> |
3519 | + <inkscape:grid |
3520 | + type="xygrid" |
3521 | + id="grid5451" |
3522 | + empspacing="8" |
3523 | + originx="-2.7715014e-06" |
3524 | + originy="-2.5124622e-06" /> |
3525 | + <sodipodi:guide |
3526 | + orientation="1,0" |
3527 | + position="7.9999972,-8.0000025" |
3528 | + id="guide4063" |
3529 | + inkscape:locked="false" /> |
3530 | + <sodipodi:guide |
3531 | + orientation="1,0" |
3532 | + position="3.9999972,-8.0000025" |
3533 | + id="guide4065" |
3534 | + inkscape:locked="false" /> |
3535 | + <sodipodi:guide |
3536 | + orientation="0,1" |
3537 | + position="-8.0000027,87.999998" |
3538 | + id="guide4067" |
3539 | + inkscape:locked="false" /> |
3540 | + <sodipodi:guide |
3541 | + orientation="0,1" |
3542 | + position="-8.0000027,91.999998" |
3543 | + id="guide4069" |
3544 | + inkscape:locked="false" /> |
3545 | + <sodipodi:guide |
3546 | + orientation="0,1" |
3547 | + position="104,3.9999975" |
3548 | + id="guide4071" |
3549 | + inkscape:locked="false" /> |
3550 | + <sodipodi:guide |
3551 | + orientation="0,1" |
3552 | + position="-5.0000027,7.9999975" |
3553 | + id="guide4073" |
3554 | + inkscape:locked="false" /> |
3555 | + <sodipodi:guide |
3556 | + orientation="1,0" |
3557 | + position="91.999996,-8.0000025" |
3558 | + id="guide4075" |
3559 | + inkscape:locked="false" /> |
3560 | + <sodipodi:guide |
3561 | + orientation="1,0" |
3562 | + position="87.999997,-8.0000025" |
3563 | + id="guide4077" |
3564 | + inkscape:locked="false" /> |
3565 | + <sodipodi:guide |
3566 | + orientation="0,1" |
3567 | + position="-8.0000027,83.999998" |
3568 | + id="guide4074" |
3569 | + inkscape:locked="false" /> |
3570 | + <sodipodi:guide |
3571 | + orientation="1,0" |
3572 | + position="11.999997,-8.0000025" |
3573 | + id="guide4076" |
3574 | + inkscape:locked="false" /> |
3575 | + <sodipodi:guide |
3576 | + orientation="0,1" |
3577 | + position="-5.0000027,11.999997" |
3578 | + id="guide4078" |
3579 | + inkscape:locked="false" /> |
3580 | + <sodipodi:guide |
3581 | + orientation="1,0" |
3582 | + position="83.999997,-9.0000025" |
3583 | + id="guide4080" |
3584 | + inkscape:locked="false" /> |
3585 | + <sodipodi:guide |
3586 | + position="47.999997,-8.0000025" |
3587 | + orientation="1,0" |
3588 | + id="guide4170" |
3589 | + inkscape:locked="false" /> |
3590 | + <sodipodi:guide |
3591 | + position="-8.0000027,47.999997" |
3592 | + orientation="0,1" |
3593 | + id="guide4172" |
3594 | + inkscape:locked="false" /> |
3595 | + </sodipodi:namedview> |
3596 | + <metadata |
3597 | + id="metadata4879"> |
3598 | + <rdf:RDF> |
3599 | + <cc:Work |
3600 | + rdf:about=""> |
3601 | + <dc:format>image/svg+xml</dc:format> |
3602 | + <dc:type |
3603 | + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> |
3604 | + <dc:title></dc:title> |
3605 | + </cc:Work> |
3606 | + </rdf:RDF> |
3607 | + </metadata> |
3608 | + <g |
3609 | + inkscape:label="Layer 1" |
3610 | + inkscape:groupmode="layer" |
3611 | + id="layer1" |
3612 | + transform="translate(67.857143,149.49498)"> |
3613 | + <g |
3614 | + transform="matrix(0,-1,-1,0,373.50506,516.50504)" |
3615 | + id="g4845" |
3616 | + style="display:inline"> |
3617 | + <g |
3618 | + inkscape:export-ydpi="90" |
3619 | + inkscape:export-xdpi="90" |
3620 | + inkscape:export-filename="next01.png" |
3621 | + transform="matrix(-0.9996045,0,0,1,575.94296,-611.00001)" |
3622 | + id="g4778" |
3623 | + inkscape:label="Layer 1"> |
3624 | + <g |
3625 | + transform="matrix(-1,0,0,1,575.99999,611)" |
3626 | + id="g4780" |
3627 | + style="display:inline"> |
3628 | + <path |
3629 | + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:53.6204567px;line-height:125%;font-family:Ubuntu;-inkscape-font-specification:Ubuntu;text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;display:inline;fill:none;fill-opacity:1;stroke:#f7f7f7;stroke-width:4.00079107;stroke-miterlimit:4;stroke-dasharray:none" |
3630 | + d="m 526.90578,343.72628 c 14.3803,3.85163 8.37809,26.10701 -5.96811,22.26448 l -10e-6,10e-6 -50.91437,-13.63709 67.92084,18.21108 c 15.93089,4.26693 9.28148,28.92202 -6.61162,24.66516 l -10e-6,1e-5 -67.93497,-18.15834 58.38757,15.63874 c 15.85637,4.24702 9.23761,28.78845 -6.58115,24.5515 v -2e-5 l 1e-5,-2e-5 -71.03537,-19.00646 -33.09607,-8.85524 -0.18781,-0.0503 v 0 l -6.91408,-1.85189 18.07353,23.47068 c 8.45364,10.48525 11.9521,17.66446 -3.54852,27.89697 l -43.91145,-37.31132 c -28.38683,-22.97811 -35.17793,-41.86133 -27.95127,-70.42985 10.25002,-40.5206 40.5632,-49.89857 76.24862,-40.34047 4.5224,1.21129 36.24839,9.63672 40.29164,10.71968 14.0412,3.76084 12.54552,13.74563 6.76936,26.96549 z" |
3631 | + id="path4845-6" |
3632 | + inkscape:connector-curvature="0" |
3633 | + sodipodi:nodetypes="ccccccccccccccccccccssscc" /> |
3634 | + <path |
3635 | + style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#f7f7f7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.51758671;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" |
3636 | + d="m 571.27587,383.27233 a 35.064884,35.05101 0 0 1 -16.02978,29.39649 35.064884,35.05101 0 0 1 -34.38665,28.3418 35.064884,35.05101 0 0 1 -34.85362,-31.56055 l 29.19904,7.8125 c 15.81876,4.23695 22.4371,-20.30571 6.58073,-24.55274 l -28.91769,-7.74609 a 35.064884,35.05101 0 0 1 0.01,-0.0137 l 38.45466,10.2793 c 15.8931,4.25686 22.54288,-20.39714 6.61199,-24.66407 l -48.41758,-12.98242 a 35.064884,35.05101 0 0 1 0,-0.006 l 31.41086,8.41407 c 14.3462,3.84253 20.34945,-18.414 5.96915,-22.26563 l -31.70199,-8.49023 a 35.064884,35.05101 0 0 1 29.21468,-15.70704 35.064884,35.05101 0 0 1 35.06465,35.05274 35.064884,35.05101 0 0 1 -0.0879,2.45312 35.064884,35.05101 0 0 1 11.8797,26.23828 z" |
3637 | + id="ellipse4239-1" |
3638 | + inkscape:connector-curvature="0" /> |
3639 | + <g |
3640 | + style="display:inline" |
3641 | + id="g5379" |
3642 | + transform="matrix(-0.99999722,0,0,-0.99999722,645.25394,919.46958)"> |
3643 | + <path |
3644 | + style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:#f7f7f7;stroke-width:4.00079107;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" |
3645 | + id="path4116" |
3646 | + sodipodi:type="arc" |
3647 | + sodipodi:cx="808.72699" |
3648 | + sodipodi:cy="244.79436" |
3649 | + sodipodi:rx="250.36082" |
3650 | + sodipodi:ry="263.63275" |
3651 | + sodipodi:start="3.8048178" |
3652 | + sodipodi:end="5.6286868" |
3653 | + sodipodi:open="true" |
3654 | + d="M 611.43997,82.485822 A 250.36082,263.63275 0 0 1 809.81939,-18.835885 250.36082,263.63275 0 0 1 1007.3516,84.304899" |
3655 | + inkscape:transform-center-x="-17.044876" |
3656 | + inkscape:transform-center-y="-0.0015034578" |
3657 | + transform="matrix(0,1,1,0,0,0)" /> |
3658 | + <path |
3659 | + inkscape:transform-center-y="4.9738279" |
3660 | + inkscape:transform-center-x="2.0443183" |
3661 | + inkscape:connector-curvature="0" |
3662 | + id="path4198" |
3663 | + d="m 73.559638,1014.0226 14.78221,-18.91287 c 0,0 6.80763,12.50667 9.95238,23.00047 -10.9469,-0.5114 -24.73459,-4.0876 -24.73459,-4.0876 z" |
3664 | + style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#f7f7f7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.00059342;marker:none;enable-background:accumulate" /> |
3665 | + </g> |
3666 | + </g> |
3667 | + </g> |
3668 | + </g> |
3669 | + </g> |
3670 | +</svg> |
3671 | |
3672 | === added file 'qml/Stages/graphics/sidestage_open.svg' |
3673 | --- qml/Stages/graphics/sidestage_open.svg 1970-01-01 00:00:00 +0000 |
3674 | +++ qml/Stages/graphics/sidestage_open.svg 2016-03-11 12:07:48 +0000 |
3675 | @@ -0,0 +1,181 @@ |
3676 | +<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
3677 | +<!-- Created with Inkscape (http://www.inkscape.org/) --> |
3678 | + |
3679 | +<svg |
3680 | + xmlns:dc="http://purl.org/dc/elements/1.1/" |
3681 | + xmlns:cc="http://creativecommons.org/ns#" |
3682 | + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" |
3683 | + xmlns:svg="http://www.w3.org/2000/svg" |
3684 | + xmlns="http://www.w3.org/2000/svg" |
3685 | + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" |
3686 | + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" |
3687 | + width="162" |
3688 | + height="230" |
3689 | + id="svg4874" |
3690 | + version="1.1" |
3691 | + inkscape:version="0.91+devel r" |
3692 | + viewBox="0 0 162 230" |
3693 | + sodipodi:docname="gesture_3f_tap.svg" |
3694 | + style="shape-rendering:crispEdges"> |
3695 | + <defs |
3696 | + id="defs4876" /> |
3697 | + <sodipodi:namedview |
3698 | + id="base" |
3699 | + pagecolor="#292929" |
3700 | + bordercolor="#666666" |
3701 | + borderopacity="1.0" |
3702 | + inkscape:pageopacity="1" |
3703 | + inkscape:pageshadow="2" |
3704 | + inkscape:zoom="1.8415614" |
3705 | + inkscape:cx="162.13161" |
3706 | + inkscape:cy="116.01485" |
3707 | + inkscape:document-units="px" |
3708 | + inkscape:current-layer="g4780" |
3709 | + showgrid="false" |
3710 | + showborder="true" |
3711 | + fit-margin-top="0" |
3712 | + fit-margin-left="0" |
3713 | + fit-margin-right="0" |
3714 | + fit-margin-bottom="0" |
3715 | + inkscape:snap-bbox="true" |
3716 | + inkscape:bbox-paths="true" |
3717 | + inkscape:bbox-nodes="true" |
3718 | + inkscape:snap-bbox-edge-midpoints="true" |
3719 | + inkscape:snap-bbox-midpoints="true" |
3720 | + inkscape:object-paths="true" |
3721 | + inkscape:snap-intersection-paths="true" |
3722 | + inkscape:object-nodes="true" |
3723 | + inkscape:snap-smooth-nodes="true" |
3724 | + inkscape:snap-midpoints="true" |
3725 | + inkscape:snap-object-midpoints="true" |
3726 | + inkscape:snap-center="true" |
3727 | + showguides="false" |
3728 | + inkscape:guide-bbox="true" |
3729 | + inkscape:snap-global="false" |
3730 | + inkscape:snap-others="false" |
3731 | + inkscape:snap-page="true"> |
3732 | + <inkscape:grid |
3733 | + type="xygrid" |
3734 | + id="grid5451" |
3735 | + empspacing="8" |
3736 | + originx="-2.7715014e-06" |
3737 | + originy="-2.5124622e-06" /> |
3738 | + <sodipodi:guide |
3739 | + orientation="1,0" |
3740 | + position="7.9999972,-8.0000026" |
3741 | + id="guide4063" |
3742 | + inkscape:locked="false" /> |
3743 | + <sodipodi:guide |
3744 | + orientation="1,0" |
3745 | + position="3.9999972,-8.0000026" |
3746 | + id="guide4065" |
3747 | + inkscape:locked="false" /> |
3748 | + <sodipodi:guide |
3749 | + orientation="0,1" |
3750 | + position="-8.0000027,87.999998" |
3751 | + id="guide4067" |
3752 | + inkscape:locked="false" /> |
3753 | + <sodipodi:guide |
3754 | + orientation="0,1" |
3755 | + position="-8.0000027,91.999998" |
3756 | + id="guide4069" |
3757 | + inkscape:locked="false" /> |
3758 | + <sodipodi:guide |
3759 | + orientation="0,1" |
3760 | + position="104,3.9999975" |
3761 | + id="guide4071" |
3762 | + inkscape:locked="false" /> |
3763 | + <sodipodi:guide |
3764 | + orientation="0,1" |
3765 | + position="-5.0000027,7.9999975" |
3766 | + id="guide4073" |
3767 | + inkscape:locked="false" /> |
3768 | + <sodipodi:guide |
3769 | + orientation="1,0" |
3770 | + position="91.999997,-8.0000026" |
3771 | + id="guide4075" |
3772 | + inkscape:locked="false" /> |
3773 | + <sodipodi:guide |
3774 | + orientation="1,0" |
3775 | + position="87.999997,-8.0000026" |
3776 | + id="guide4077" |
3777 | + inkscape:locked="false" /> |
3778 | + <sodipodi:guide |
3779 | + orientation="0,1" |
3780 | + position="-8.0000027,83.999998" |
3781 | + id="guide4074" |
3782 | + inkscape:locked="false" /> |
3783 | + <sodipodi:guide |
3784 | + orientation="1,0" |
3785 | + position="11.999997,-8.0000026" |
3786 | + id="guide4076" |
3787 | + inkscape:locked="false" /> |
3788 | + <sodipodi:guide |
3789 | + orientation="0,1" |
3790 | + position="-5.0000027,11.999997" |
3791 | + id="guide4078" |
3792 | + inkscape:locked="false" /> |
3793 | + <sodipodi:guide |
3794 | + orientation="1,0" |
3795 | + position="83.999997,-9.0000026" |
3796 | + id="guide4080" |
3797 | + inkscape:locked="false" /> |
3798 | + <sodipodi:guide |
3799 | + position="47.999997,-8.0000026" |
3800 | + orientation="1,0" |
3801 | + id="guide4170" |
3802 | + inkscape:locked="false" /> |
3803 | + <sodipodi:guide |
3804 | + position="-8.0000027,47.999997" |
3805 | + orientation="0,1" |
3806 | + id="guide4172" |
3807 | + inkscape:locked="false" /> |
3808 | + </sodipodi:namedview> |
3809 | + <metadata |
3810 | + id="metadata4879"> |
3811 | + <rdf:RDF> |
3812 | + <cc:Work |
3813 | + rdf:about=""> |
3814 | + <dc:format>image/svg+xml</dc:format> |
3815 | + <dc:type |
3816 | + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> |
3817 | + <dc:title></dc:title> |
3818 | + </cc:Work> |
3819 | + </rdf:RDF> |
3820 | + </metadata> |
3821 | + <g |
3822 | + inkscape:label="Layer 1" |
3823 | + inkscape:groupmode="layer" |
3824 | + id="layer1" |
3825 | + transform="translate(67.857143,55.494966)"> |
3826 | + <g |
3827 | + transform="matrix(0,-1,-1,0,373.50506,516.50504)" |
3828 | + id="g4845" |
3829 | + style="display:inline"> |
3830 | + <g |
3831 | + inkscape:export-ydpi="90" |
3832 | + inkscape:export-xdpi="90" |
3833 | + inkscape:export-filename="next01.png" |
3834 | + transform="matrix(-0.9996045,0,0,1,575.94296,-611.00001)" |
3835 | + id="g4778" |
3836 | + inkscape:label="Layer 1"> |
3837 | + <g |
3838 | + transform="matrix(-1,0,0,1,575.99999,611)" |
3839 | + id="g4780" |
3840 | + style="display:inline"> |
3841 | + <path |
3842 | + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:53.6204567px;line-height:125%;font-family:Ubuntu;-inkscape-font-specification:Ubuntu;text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;display:inline;fill:none;fill-opacity:1;stroke:#f7f7f7;stroke-width:4.00079107;stroke-miterlimit:4;stroke-dasharray:none" |
3843 | + d="m 526.90578,343.72628 c 14.3803,3.85163 8.37809,26.10701 -5.96811,22.26448 l -10e-6,10e-6 -50.91437,-13.63709 67.92084,18.21108 c 15.93089,4.26693 9.28148,28.92202 -6.61162,24.66516 l -10e-6,1e-5 -67.93497,-18.15834 58.38757,15.63874 c 15.85637,4.24702 9.23761,28.78845 -6.58115,24.5515 v -2e-5 l 1e-5,-2e-5 -71.03537,-19.00646 -33.09607,-8.85524 -0.18781,-0.0503 v 0 l -6.91408,-1.85189 18.07353,23.47068 c 8.45364,10.48525 11.9521,17.66446 -3.54852,27.89697 l -43.91145,-37.31132 c -28.38683,-22.97811 -35.17793,-41.86133 -27.95127,-70.42985 10.25002,-40.5206 40.5632,-49.89857 76.24862,-40.34047 4.5224,1.21129 36.24839,9.63672 40.29164,10.71968 14.0412,3.76084 12.54552,13.74563 6.76936,26.96549 z" |
3844 | + id="path4845-6" |
3845 | + inkscape:connector-curvature="0" |
3846 | + sodipodi:nodetypes="ccccccccccccccccccccssscc" /> |
3847 | + <path |
3848 | + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.5;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#f7f7f7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.99999976;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" |
3849 | + d="m 571.45563,383.2743 c -0.0175,12.04505 -6.22338,23.19765 -16.35804,29.64062 -3.3052,16.24493 -17.57029,28.04101 -34.23815,28.04493 -18.13447,4.3e-4 -33.08059,-13.83538 -34.83995,-31.50586 l 4.16766,1.11523 c 2.22233,14.95164 15.08799,26.39099 30.67229,26.39063 15.06147,-0.004 27.92054,-10.79228 30.53552,-25.61915 l 0.15631,-0.89648 0.78156,-0.46875 c 9.37156,-5.64255 15.10396,-15.76543 15.12121,-26.70117 v -0.004 c -0.0108,-9.30873 -4.17362,-18.12357 -11.35605,-24.04883 l -0.8265,-0.68359 0.10942,-1.06641 c 0.10013,-0.96319 0.1562,-1.9301 0.16608,-2.89843 -0.004,-17.20453 -13.91519,-31.10752 -31.12755,-31.10743 -10.37812,-5e-5 -19.55027,5.05891 -25.20333,12.84375 l -4.0856,-1.09375 c 6.29481,-9.48451 17.06827,-15.75006 29.28893,-15.75 19.37764,-1e-4 35.12924,15.74527 35.12913,35.11524 v 0.01 0.01 c -0.008,0.77822 -0.0716,1.55426 -0.13091,2.33008 7.57765,6.66645 12.02745,16.21382 12.03797,26.33984 v 0.002 z m -74.3634,2.80273 c -0.004,0.005 -0.008,0.0106 -0.0117,0.0156 l -4.15595,-1.11328 c 0.003,-0.004 0.006,-0.009 0.01,-0.0137 z m -3.51507,-27.4082 -4.14421,-1.10938 c -1.8e-4,-0.002 1.7e-4,-0.004 0,-0.006 l 4.14226,1.10937 c 2.7e-4,0.002 0.002,0.004 0.002,0.006 z" |
3850 | + id="ellipse4488" |
3851 | + inkscape:connector-curvature="0" /> |
3852 | + </g> |
3853 | + </g> |
3854 | + </g> |
3855 | + </g> |
3856 | +</svg> |
3857 | |
3858 | === modified file 'src/MouseTouchAdaptor.cpp' |
3859 | --- src/MouseTouchAdaptor.cpp 2015-12-15 19:55:26 +0000 |
3860 | +++ src/MouseTouchAdaptor.cpp 2016-03-11 12:07:48 +0000 |
3861 | @@ -71,6 +71,8 @@ |
3862 | namespace { |
3863 | MouseTouchAdaptor *g_instance = nullptr; |
3864 | |
3865 | +const Qt::KeyboardModifiers TRI_PRESS_MODIFIER = Qt::ShiftModifier|Qt::ControlModifier|Qt::AltModifier; |
3866 | + |
3867 | Qt::MouseButton translateMouseButton(xcb_button_t detail) |
3868 | { |
3869 | switch (detail) { |
3870 | @@ -81,10 +83,25 @@ |
3871 | default: return Qt::NoButton; |
3872 | } |
3873 | } |
3874 | + |
3875 | +Qt::KeyboardModifiers translateMofidier(uint32_t mod) |
3876 | +{ |
3877 | + Qt::KeyboardModifiers qtMod = Qt::NoModifier; |
3878 | + |
3879 | + if (mod & 0x01) qtMod |= Qt::ShiftModifier; |
3880 | + if (mod & 0x04) qtMod |= Qt::ControlModifier; |
3881 | + if (mod & 0x08) qtMod |= Qt::AltModifier; |
3882 | + if (mod & 0x80) qtMod |= Qt::MetaModifier; |
3883 | + |
3884 | + return qtMod; |
3885 | +} |
3886 | } // end of anonymous namespace |
3887 | |
3888 | MouseTouchAdaptor::MouseTouchAdaptor() |
3889 | - : QObject(nullptr), m_leftButtonIsPressed(false), m_enabled(true) |
3890 | + : QObject(nullptr) |
3891 | + , m_leftButtonIsPressed(false) |
3892 | + , m_triPressModifier(false) |
3893 | + , m_enabled(true) |
3894 | { |
3895 | QCoreApplication::instance()->installNativeEventFilter(this); |
3896 | |
3897 | @@ -192,17 +209,20 @@ |
3898 | return handleButtonPress( |
3899 | static_cast<WId>(xiDeviceEvent->event), |
3900 | xiDeviceEvent->detail, |
3901 | + xiDeviceEvent->mods.base_mods, |
3902 | fixed1616ToReal(xiDeviceEvent->event_x), |
3903 | fixed1616ToReal(xiDeviceEvent->event_y)); |
3904 | case XI_ButtonRelease: |
3905 | return handleButtonRelease( |
3906 | static_cast<WId>(xiDeviceEvent->event), |
3907 | xiDeviceEvent->detail, |
3908 | + xiDeviceEvent->mods.base_mods, |
3909 | fixed1616ToReal(xiDeviceEvent->event_x), |
3910 | fixed1616ToReal(xiDeviceEvent->event_y)); |
3911 | case XI_Motion: |
3912 | return handleMotionNotify( |
3913 | static_cast<WId>(xiDeviceEvent->event), |
3914 | + xiDeviceEvent->mods.base_mods, |
3915 | fixed1616ToReal(xiDeviceEvent->event_x), |
3916 | fixed1616ToReal(xiDeviceEvent->event_y)); |
3917 | return true; |
3918 | @@ -232,17 +252,18 @@ |
3919 | switch (xcbEvent->response_type & ~0x80) { |
3920 | case XCB_BUTTON_PRESS: { |
3921 | auto pressEvent = reinterpret_cast<xcb_button_press_event_t *>(xcbEvent); |
3922 | - return handleButtonPress(static_cast<WId>(pressEvent->event), pressEvent->detail, |
3923 | + return handleButtonPress(static_cast<WId>(pressEvent->event), pressEvent->detail, 0, |
3924 | pressEvent->event_x, pressEvent->event_y); |
3925 | } |
3926 | case XCB_BUTTON_RELEASE: { |
3927 | auto releaseEvent = reinterpret_cast<xcb_button_release_event_t *>(xcbEvent); |
3928 | - return handleButtonRelease(static_cast<WId>(releaseEvent->event), releaseEvent->detail, |
3929 | + return handleButtonRelease(static_cast<WId>(releaseEvent->event), releaseEvent->detail, 0, |
3930 | releaseEvent->event_x, releaseEvent->event_y); |
3931 | } |
3932 | case XCB_MOTION_NOTIFY: { |
3933 | auto motionEvent = reinterpret_cast<xcb_motion_notify_event_t *>(xcbEvent); |
3934 | - return handleMotionNotify(static_cast<WId>(motionEvent->event), motionEvent->event_x, motionEvent->event_y); |
3935 | + return handleMotionNotify(static_cast<WId>(motionEvent->event), 0, |
3936 | + motionEvent->event_x, motionEvent->event_y); |
3937 | } |
3938 | case XCB_GE_GENERIC: |
3939 | if (m_xi2Enabled) { |
3940 | @@ -255,9 +276,10 @@ |
3941 | }; |
3942 | } |
3943 | |
3944 | -bool MouseTouchAdaptor::handleButtonPress(WId windowId, uint32_t detail, int x, int y) |
3945 | +bool MouseTouchAdaptor::handleButtonPress(WId windowId, uint32_t detail, uint32_t modifiers, int x, int y) |
3946 | { |
3947 | Qt::MouseButton button = translateMouseButton(detail); |
3948 | + Qt::KeyboardModifiers qtMod = translateMofidier(modifiers); |
3949 | |
3950 | // Just eat the event if it wasn't a left mouse press |
3951 | if (button != Qt::LeftButton) |
3952 | @@ -270,19 +292,24 @@ |
3953 | QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice, |
3954 | false /* autoCommit */); |
3955 | touchEvent.press(0 /* touchId */, windowPos); |
3956 | + if (qtMod == TRI_PRESS_MODIFIER) { |
3957 | + touchEvent.press(1, windowPos); |
3958 | + touchEvent.press(2, windowPos); |
3959 | + m_triPressModifier = true; |
3960 | + } |
3961 | touchEvent.commit(false /* processEvents */); |
3962 | |
3963 | m_leftButtonIsPressed = true; |
3964 | return true; |
3965 | } |
3966 | |
3967 | -bool MouseTouchAdaptor::handleButtonRelease(WId windowId, uint32_t detail, int x, int y) |
3968 | +bool MouseTouchAdaptor::handleButtonRelease(WId windowId, uint32_t detail, uint32_t, int x, int y) |
3969 | { |
3970 | Qt::MouseButton button = translateMouseButton(detail); |
3971 | |
3972 | - // Just eat the event if it wasn't a left mouse release |
3973 | + // Don't eat the event if it wasn't a left mouse press |
3974 | if (button != Qt::LeftButton) |
3975 | - return true; |
3976 | + return false; |
3977 | |
3978 | QWindow *targetWindow = findQWindowWithXWindowID(windowId); |
3979 | |
3980 | @@ -291,17 +318,23 @@ |
3981 | QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice, |
3982 | false /* autoCommit */); |
3983 | touchEvent.release(0 /* touchId */, windowPos); |
3984 | + if (m_triPressModifier) { |
3985 | + touchEvent.release(1, windowPos); |
3986 | + touchEvent.release(2, windowPos); |
3987 | + } |
3988 | touchEvent.commit(false /* processEvents */); |
3989 | |
3990 | m_leftButtonIsPressed = false; |
3991 | + m_triPressModifier = false; |
3992 | return true; |
3993 | } |
3994 | |
3995 | -bool MouseTouchAdaptor::handleMotionNotify(WId windowId, int x, int y) |
3996 | +bool MouseTouchAdaptor::handleMotionNotify(WId windowId, uint32_t modifiers, int x, int y) |
3997 | { |
3998 | if (!m_leftButtonIsPressed) { |
3999 | return true; |
4000 | } |
4001 | + Qt::KeyboardModifiers qtMod = translateMofidier(modifiers); |
4002 | |
4003 | QWindow *targetWindow = findQWindowWithXWindowID(windowId); |
4004 | |
4005 | @@ -310,6 +343,17 @@ |
4006 | QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice, |
4007 | false /* autoCommit */); |
4008 | touchEvent.move(0 /* touchId */, windowPos); |
4009 | + if (m_triPressModifier) { |
4010 | + if (qtMod == TRI_PRESS_MODIFIER) { |
4011 | + touchEvent.move(1, windowPos); |
4012 | + touchEvent.move(2, windowPos); |
4013 | + } else { |
4014 | + // released modifiers |
4015 | + touchEvent.release(1, windowPos); |
4016 | + touchEvent.release(2, windowPos); |
4017 | + m_triPressModifier = false; |
4018 | + } |
4019 | + } |
4020 | touchEvent.commit(false /* processEvents */); |
4021 | |
4022 | return true; |
4023 | |
4024 | === modified file 'src/MouseTouchAdaptor.h' |
4025 | --- src/MouseTouchAdaptor.h 2015-12-15 19:55:26 +0000 |
4026 | +++ src/MouseTouchAdaptor.h 2016-03-11 12:07:48 +0000 |
4027 | @@ -51,13 +51,15 @@ |
4028 | void fetchXInput2Info(); |
4029 | bool xi2HandleEvent(xcb_ge_event_t *event); |
4030 | |
4031 | - bool handleButtonPress(WId windowId, uint32_t detail, int x, int y); |
4032 | - bool handleButtonRelease(WId windowId, uint32_t detail, int x, int y); |
4033 | - bool handleMotionNotify(WId windowId, int x, int y); |
4034 | + bool handleButtonPress(WId windowId, uint32_t detail, uint32_t modifiers, int x, int y); |
4035 | + bool handleButtonRelease(WId windowId, uint32_t detail, uint32_t modifiers, int x, int y); |
4036 | + bool handleMotionNotify(WId windowId, uint32_t modifiers, int x, int y); |
4037 | QWindow *findQWindowWithXWindowID(WId windowId); |
4038 | |
4039 | QTouchDevice *m_touchDevice; |
4040 | bool m_leftButtonIsPressed; |
4041 | + bool m_triPressModifier; |
4042 | + |
4043 | |
4044 | bool m_enabled; |
4045 | |
4046 | |
4047 | === modified file 'tests/libs/UbuntuGestures/tst_TouchRegistry.cpp' |
4048 | --- tests/libs/UbuntuGestures/tst_TouchRegistry.cpp 2015-07-20 16:30:40 +0000 |
4049 | +++ tests/libs/UbuntuGestures/tst_TouchRegistry.cpp 2016-03-11 12:07:48 +0000 |
4050 | @@ -42,6 +42,10 @@ |
4051 | QSet<int> ownedTouches; |
4052 | QSet<int> lostTouches; |
4053 | QList<TouchMemento> unownedTouchEvents; |
4054 | + |
4055 | +Q_SIGNALS: |
4056 | + void gainedOwnership(); |
4057 | + void lostOwnership(); |
4058 | }; |
4059 | |
4060 | class tst_TouchRegistry : public QObject |
4061 | @@ -68,6 +72,7 @@ |
4062 | void removeOldUndecidedCandidates(); |
4063 | void interimOwnerWontGetUnownedTouchEvents(); |
4064 | void candidateVanishes(); |
4065 | + void candicateOwnershipReentrace(); |
4066 | |
4067 | private: |
4068 | TouchRegistry *touchRegistry; |
4069 | @@ -864,6 +869,58 @@ |
4070 | QVERIFY(mainCandidate.ownedTouches.contains(0)); |
4071 | } |
4072 | |
4073 | +/* |
4074 | + Regression test for canidate reentrance |
4075 | + |
4076 | + Bug caused by candidates getting removed during ownership resolution |
4077 | + */ |
4078 | +void tst_TouchRegistry::candicateOwnershipReentrace() |
4079 | +{ |
4080 | + DummyCandidate mainCandidate; |
4081 | + DummyCandidate candicate2; |
4082 | + DummyCandidate candicate3; |
4083 | + |
4084 | + { |
4085 | + QList<QTouchEvent::TouchPoint> touchPoints; |
4086 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
4087 | + touchPoints[0].setState(Qt::TouchPointPressed); |
4088 | + QTouchEvent touchEvent(QEvent::TouchBegin, |
4089 | + 0 /* device */, |
4090 | + Qt::NoModifier, |
4091 | + Qt::TouchPointPressed, |
4092 | + touchPoints); |
4093 | + touchRegistry->update(&touchEvent); |
4094 | + } |
4095 | + |
4096 | + // Re-entrance! |
4097 | + connect(&candicate2, &DummyCandidate::lostOwnership, this, [&]() { |
4098 | + touchRegistry->removeCandidateOwnerForTouch(0, &candicate2); |
4099 | + }); |
4100 | + |
4101 | + touchRegistry->addCandidateOwnerForTouch(0, &mainCandidate); |
4102 | + touchRegistry->addCandidateOwnerForTouch(0, &candicate2); |
4103 | + touchRegistry->addCandidateOwnerForTouch(0, &candicate3); |
4104 | + |
4105 | + { |
4106 | + QList<QTouchEvent::TouchPoint> touchPoints; |
4107 | + touchPoints.append(QTouchEvent::TouchPoint(0)); |
4108 | + touchPoints[0].setState(Qt::TouchPointMoved); |
4109 | + QTouchEvent touchEvent(QEvent::TouchUpdate, |
4110 | + 0 /* device */, |
4111 | + Qt::NoModifier, |
4112 | + Qt::TouchPointMoved, |
4113 | + touchPoints); |
4114 | + touchRegistry->update(&touchEvent); |
4115 | + } |
4116 | + |
4117 | + touchRegistry->requestTouchOwnership(0, &mainCandidate); |
4118 | + |
4119 | + QCOMPARE(mainCandidate.ownedTouches.count(), 1); |
4120 | + QCOMPARE(candicate2.lostTouches.count(), 1); |
4121 | + QCOMPARE(candicate3.lostTouches.count(), 1); |
4122 | +} |
4123 | + |
4124 | + |
4125 | ////////////// TouchMemento ////////// |
4126 | |
4127 | TouchMemento::TouchMemento(const QTouchEvent *touchEvent) |
4128 | @@ -897,8 +954,10 @@ |
4129 | |
4130 | if (touchOwnershipEvent->gained()) { |
4131 | ownedTouches.insert(touchOwnershipEvent->touchId()); |
4132 | + Q_EMIT gainedOwnership(); |
4133 | } else { |
4134 | lostTouches.insert(touchOwnershipEvent->touchId()); |
4135 | + Q_EMIT lostOwnership(); |
4136 | } |
4137 | return true; |
4138 | } else if (e->type() == UnownedTouchEvent::unownedTouchEventType()) { |
4139 | |
4140 | === modified file 'tests/mocks/Unity/Application/ApplicationManager.cpp' |
4141 | --- tests/mocks/Unity/Application/ApplicationManager.cpp 2015-12-03 18:10:39 +0000 |
4142 | +++ tests/mocks/Unity/Application/ApplicationManager.cpp 2016-03-11 12:07:48 +0000 |
4143 | @@ -151,10 +151,6 @@ |
4144 | |
4145 | beginInsertRows(QModelIndex(), m_runningApplications.size(), m_runningApplications.size()); |
4146 | m_runningApplications.append(application); |
4147 | - endInsertRows(); |
4148 | - Q_EMIT applicationAdded(application->appId()); |
4149 | - Q_EMIT countChanged(); |
4150 | - if (count() == 1) Q_EMIT emptyChanged(isEmpty()); // was empty but not anymore |
4151 | |
4152 | connect(application, &ApplicationInfo::sessionChanged, this, [application, this]() { |
4153 | QModelIndex appIndex = findIndex(application); |
4154 | @@ -171,6 +167,16 @@ |
4155 | if (!appIndex.isValid()) return; |
4156 | Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << ApplicationManager::RoleState); |
4157 | }); |
4158 | + connect(application, &ApplicationInfo::stageChanged, this, [application, this]() { |
4159 | + QModelIndex appIndex = findIndex(application); |
4160 | + if (!appIndex.isValid()) return; |
4161 | + Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << ApplicationManager::RoleStage); |
4162 | + }); |
4163 | + |
4164 | + endInsertRows(); |
4165 | + Q_EMIT applicationAdded(application->appId()); |
4166 | + Q_EMIT countChanged(); |
4167 | + if (count() == 1) Q_EMIT emptyChanged(isEmpty()); // was empty but not anymore |
4168 | } |
4169 | |
4170 | void ApplicationManager::remove(ApplicationInfo *application) { |
4171 | @@ -203,22 +209,10 @@ |
4172 | ApplicationInfo* ApplicationManager::startApplication(const QString &appId, |
4173 | const QStringList &arguments) |
4174 | { |
4175 | - return startApplication(appId, NoFlag, arguments); |
4176 | -} |
4177 | - |
4178 | -ApplicationInfo* ApplicationManager::startApplication(const QString &appId, |
4179 | - ExecFlags flags, |
4180 | - const QStringList &arguments) |
4181 | -{ |
4182 | Q_UNUSED(arguments) |
4183 | ApplicationInfo *application = add(appId); |
4184 | if (!application) |
4185 | return 0; |
4186 | - |
4187 | - if (flags.testFlag(ApplicationManager::ForceMainStage) |
4188 | - && application->stage() == ApplicationInfo::SideStage) { |
4189 | - application->setStage(ApplicationInfo::MainStage); |
4190 | - } |
4191 | application->setState(ApplicationInfo::Starting); |
4192 | |
4193 | return application; |
4194 | @@ -317,7 +311,6 @@ |
4195 | application->setAppId("unity8-dash"); |
4196 | application->setName("Unity 8 Mock Dash"); |
4197 | application->setScreenshotId("unity8-dash"); |
4198 | - application->setStage(ApplicationInfo::MainStage); |
4199 | application->setSupportedOrientations(Qt::PrimaryOrientation); |
4200 | m_availableApplications.append(application); |
4201 | |
4202 | @@ -326,7 +319,6 @@ |
4203 | application->setName("Dialer"); |
4204 | application->setScreenshotId("dialer"); |
4205 | application->setIconId("dialer-app"); |
4206 | - application->setStage(ApplicationInfo::SideStage); |
4207 | application->setSupportedOrientations(Qt::PortraitOrientation |
4208 | | Qt::InvertedPortraitOrientation); |
4209 | m_availableApplications.append(application); |
4210 | @@ -350,7 +342,6 @@ |
4211 | application->setScreenshotId("gallery"); |
4212 | application->setIconId("gallery"); |
4213 | application->setFullscreen(true); |
4214 | - application->setStage(ApplicationInfo::MainStage); |
4215 | m_availableApplications.append(application); |
4216 | |
4217 | application = new ApplicationInfo(this); |
4218 | @@ -358,7 +349,6 @@ |
4219 | application->setName("Facebook"); |
4220 | application->setScreenshotId("facebook"); |
4221 | application->setIconId("facebook"); |
4222 | - application->setStage(ApplicationInfo::SideStage); |
4223 | m_availableApplications.append(application); |
4224 | |
4225 | application = new ApplicationInfo(this); |
4226 | @@ -374,7 +364,6 @@ |
4227 | application->setName("Twitter"); |
4228 | application->setScreenshotId("twitter"); |
4229 | application->setIconId("twitter"); |
4230 | - application->setStage(ApplicationInfo::SideStage); |
4231 | m_availableApplications.append(application); |
4232 | |
4233 | application = new ApplicationInfo(this); |
4234 | @@ -390,7 +379,6 @@ |
4235 | application->setIconId("gmail"); |
4236 | application->setScreenshotId("gmail-webapp.svg"); |
4237 | application->setFullscreen(false); |
4238 | - application->setStage(ApplicationInfo::MainStage); |
4239 | application->setSupportedOrientations(Qt::PortraitOrientation |
4240 | | Qt::LandscapeOrientation |
4241 | | Qt::InvertedPortraitOrientation |
4242 | @@ -403,7 +391,6 @@ |
4243 | application->setIconId("soundcloud"); |
4244 | application->setScreenshotId("music"); |
4245 | application->setFullscreen(false); |
4246 | - application->setStage(ApplicationInfo::MainStage); |
4247 | application->setSupportedOrientations(Qt::PortraitOrientation |
4248 | | Qt::LandscapeOrientation |
4249 | | Qt::InvertedPortraitOrientation |
4250 | @@ -423,14 +410,12 @@ |
4251 | application->setAppId("notes-app"); |
4252 | application->setName("Notepad"); |
4253 | application->setIconId("notepad"); |
4254 | - application->setStage(ApplicationInfo::SideStage); |
4255 | m_availableApplications.append(application); |
4256 | |
4257 | application = new ApplicationInfo(this); |
4258 | application->setAppId("calendar-app"); |
4259 | application->setName("Calendar"); |
4260 | application->setIconId("calendar"); |
4261 | - application->setStage(ApplicationInfo::SideStage); |
4262 | m_availableApplications.append(application); |
4263 | |
4264 | application = new ApplicationInfo(this); |
4265 | |
4266 | === modified file 'tests/mocks/Unity/Application/ApplicationManager.h' |
4267 | --- tests/mocks/Unity/Application/ApplicationManager.h 2015-12-03 18:10:39 +0000 |
4268 | +++ tests/mocks/Unity/Application/ApplicationManager.h 2016-03-11 12:07:48 +0000 |
4269 | @@ -46,11 +46,6 @@ |
4270 | RoleSession = RoleExemptFromLifecycle+1, |
4271 | RoleFullscreen, |
4272 | }; |
4273 | - enum Flag { |
4274 | - NoFlag = 0x0, |
4275 | - ForceMainStage = 0x1, |
4276 | - }; |
4277 | - Q_DECLARE_FLAGS(ExecFlags, Flag) |
4278 | |
4279 | // QAbstractItemModel methods. |
4280 | int rowCount(const QModelIndex& parent = QModelIndex()) const override; |
4281 | @@ -65,7 +60,6 @@ |
4282 | Q_INVOKABLE bool focusApplication(const QString &appId) override; |
4283 | Q_INVOKABLE void unfocusCurrentApplication() override; |
4284 | Q_INVOKABLE ApplicationInfo *startApplication(const QString &appId, const QStringList &arguments = QStringList()) override; |
4285 | - Q_INVOKABLE ApplicationInfo *startApplication(const QString &appId, ExecFlags flags, const QStringList &arguments = QStringList()); |
4286 | Q_INVOKABLE bool stopApplication(const QString &appId) override; |
4287 | |
4288 | QString focusedApplicationId() const override; |
4289 | @@ -98,8 +92,6 @@ |
4290 | static ApplicationManager *the_application_manager; |
4291 | }; |
4292 | |
4293 | -Q_DECLARE_OPERATORS_FOR_FLAGS(ApplicationManager::ExecFlags) |
4294 | - |
4295 | Q_DECLARE_METATYPE(ApplicationManager*) |
4296 | |
4297 | #endif // APPLICATION_MANAGER_H |
4298 | |
4299 | === modified file 'tests/mocks/Unity/Application/MirSurfaceItem.cpp' |
4300 | --- tests/mocks/Unity/Application/MirSurfaceItem.cpp 2016-01-25 15:00:31 +0000 |
4301 | +++ tests/mocks/Unity/Application/MirSurfaceItem.cpp 2016-03-11 12:07:48 +0000 |
4302 | @@ -156,6 +156,10 @@ |
4303 | |
4304 | void MirSurfaceItem::touchEvent(QTouchEvent * event) |
4305 | { |
4306 | + if (event->type() == QEvent::TouchBegin) { |
4307 | + m_touchTrail.clear(); |
4308 | + } |
4309 | + |
4310 | if (event->touchPointStates() & Qt::TouchPointPressed) { |
4311 | ++m_touchPressCount; |
4312 | Q_EMIT touchPressCountChanged(m_touchPressCount); |
4313 | @@ -163,6 +167,19 @@ |
4314 | ++m_touchReleaseCount; |
4315 | Q_EMIT touchReleaseCountChanged(m_touchReleaseCount); |
4316 | } |
4317 | + |
4318 | + Q_FOREACH(QTouchEvent::TouchPoint touchPoint, event->touchPoints()) { |
4319 | + QString id(touchPoint.id()); |
4320 | + QVariantList list = m_touchTrail[id].toList(); |
4321 | + list.append(QVariant::fromValue(touchPoint.pos())); |
4322 | + if (list.count() > 100) list.pop_front(); |
4323 | + m_touchTrail[id] = list; |
4324 | + } |
4325 | + |
4326 | + if (m_qmlItem) { |
4327 | + QQmlProperty touchTrail(m_qmlItem, "touchTrail"); |
4328 | + touchTrail.write(m_touchTrail); |
4329 | + } |
4330 | } |
4331 | |
4332 | void MirSurfaceItem::mousePressEvent(QMouseEvent * event) |
4333 | |
4334 | === modified file 'tests/mocks/Unity/Application/MirSurfaceItem.h' |
4335 | --- tests/mocks/Unity/Application/MirSurfaceItem.h 2016-01-25 15:00:31 +0000 |
4336 | +++ tests/mocks/Unity/Application/MirSurfaceItem.h 2016-03-11 12:07:48 +0000 |
4337 | @@ -123,6 +123,7 @@ |
4338 | int m_touchReleaseCount; |
4339 | int m_mousePressCount; |
4340 | int m_mouseReleaseCount; |
4341 | + QVariantMap m_touchTrail; |
4342 | |
4343 | FillMode m_fillMode{Stretch}; |
4344 | |
4345 | |
4346 | === modified file 'tests/mocks/Unity/Application/resources/MirSurfaceItem.qml' |
4347 | --- tests/mocks/Unity/Application/resources/MirSurfaceItem.qml 2015-09-29 12:28:10 +0000 |
4348 | +++ tests/mocks/Unity/Application/resources/MirSurfaceItem.qml 2016-03-11 12:07:48 +0000 |
4349 | @@ -29,6 +29,9 @@ |
4350 | property alias screenshotSource: screenshotImage.source |
4351 | property int orientationAngle |
4352 | |
4353 | + property var touchTrail |
4354 | + onTouchTrailChanged: canvas.requestPaint() |
4355 | + |
4356 | Image { |
4357 | id: screenshotImage |
4358 | anchors.fill: parent |
4359 | @@ -67,4 +70,60 @@ |
4360 | width: (rotation == 0 || rotation == 180 ? parent.width : parent.height) |
4361 | height:(rotation == 0 || rotation == 180 ? parent.height : parent.width) |
4362 | } |
4363 | + |
4364 | + Canvas { |
4365 | + id: canvas |
4366 | + anchors.fill: parent |
4367 | + onPaint: { |
4368 | + // get context to draw with |
4369 | + var ctx = getContext("2d") |
4370 | + ctx.reset(0, 0, canvas.width, canvas.height); |
4371 | + ctx.lineWidth = 20; |
4372 | + |
4373 | + if (touchTrail === undefined) return; |
4374 | + |
4375 | + var i = 0; |
4376 | + for (var prop in touchTrail) { |
4377 | + var trail = touchTrail[prop]; |
4378 | + |
4379 | + if (trail.length > 0) { |
4380 | + ctx.fillStyle = Qt.rgba(0, 0, 1, 0.3); |
4381 | + ctx.strokeStyle = Qt.rgba(0, 0, 1, 0.3); |
4382 | + ctx.beginPath(); |
4383 | + ctx.moveTo(trail[0].x, trail[0].y); |
4384 | + ctx.ellipse(trail[0].x-1, trail[0].y-1, 2, 2); |
4385 | + ctx.stroke(); |
4386 | + |
4387 | + ctx.beginPath(); |
4388 | + if (i == 0) { |
4389 | + ctx.strokeStyle = Qt.rgba(1, 1, 0, 0.3); |
4390 | + } else if (i == 1) { |
4391 | + ctx.strokeStyle = Qt.rgba(0, 1, 1, 0.3); |
4392 | + |
4393 | + } else if (i == 2) { |
4394 | + ctx.strokeStyle = Qt.rgba(1, 0, 1, 0.3); |
4395 | + |
4396 | + } else if (i == 3) { |
4397 | + ctx.strokeStyle = Qt.rgba(0, 0.5, 0.5, 0.3); |
4398 | + |
4399 | + } else { |
4400 | + ctx.strokeStyle = Qt.rgba(0.7, 0.5, 0, 0.3); |
4401 | + } |
4402 | + |
4403 | + for (var i = 1; i < trail.length; i++) { |
4404 | + ctx.lineTo(trail[i].x, trail[i].y) |
4405 | + } |
4406 | + ctx.stroke(); |
4407 | + |
4408 | + ctx.beginPath(); |
4409 | + ctx.fillStyle = Qt.rgba(1, 0, 0, 0.3); |
4410 | + ctx.strokeStyle = Qt.rgba(1, 0, 0, 0.3); |
4411 | + ctx.ellipse(trail[i-1].x-1, trail[i-1].y-1, 2, 2); |
4412 | + ctx.stroke(); |
4413 | + } |
4414 | + i++; |
4415 | + } |
4416 | + |
4417 | + } |
4418 | + } |
4419 | } |
4420 | |
4421 | === modified file 'tests/mocks/Utils/CMakeLists.txt' |
4422 | --- tests/mocks/Utils/CMakeLists.txt 2016-03-11 12:07:48 +0000 |
4423 | +++ tests/mocks/Utils/CMakeLists.txt 2016-03-11 12:07:48 +0000 |
4424 | @@ -19,6 +19,7 @@ |
4425 | ${CMAKE_SOURCE_DIR}/plugins/Utils/applicationsfiltermodel.cpp |
4426 | ${CMAKE_SOURCE_DIR}/plugins/Utils/inputeventgenerator.cpp |
4427 | ${CMAKE_SOURCE_DIR}/plugins/Utils/deviceconfigparser.cpp |
4428 | + ${CMAKE_SOURCE_DIR}/plugins/Utils/globalfunctions.cpp |
4429 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h |
4430 | constants.cpp |
4431 | plugin.cpp |
4432 | |
4433 | === modified file 'tests/mocks/Utils/plugin.cpp' |
4434 | --- tests/mocks/Utils/plugin.cpp 2016-03-11 12:07:48 +0000 |
4435 | +++ tests/mocks/Utils/plugin.cpp 2016-03-11 12:07:48 +0000 |
4436 | @@ -38,6 +38,7 @@ |
4437 | #include <applicationsfiltermodel.h> |
4438 | #include <inputeventgenerator.h> |
4439 | #include <deviceconfigparser.h> |
4440 | +#include <globalfunctions.h> |
4441 | |
4442 | static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine) |
4443 | { |
4444 | @@ -53,6 +54,13 @@ |
4445 | return new Constants(); |
4446 | } |
4447 | |
4448 | +static QObject *createGlobalFunctions(QQmlEngine *engine, QJSEngine *scriptEngine) |
4449 | +{ |
4450 | + Q_UNUSED(engine) |
4451 | + Q_UNUSED(scriptEngine) |
4452 | + return new GlobalFunctions(); |
4453 | +} |
4454 | + |
4455 | void FakeUtilsPlugin::registerTypes(const char *uri) |
4456 | { |
4457 | Q_ASSERT(uri == QLatin1String("Utils")); |
4458 | @@ -69,6 +77,7 @@ |
4459 | qmlRegisterType<ApplicationsFilterModel>(uri, 0, 1, "ApplicationsFilterModel"); |
4460 | qmlRegisterType<InputEventGenerator>(uri, 0, 1, "InputEventGenerator"); |
4461 | qmlRegisterType<DeviceConfigParser>(uri, 0, 1, "DeviceConfigParser"); |
4462 | + qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions); |
4463 | } |
4464 | |
4465 | void FakeUtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri) |
4466 | |
4467 | === modified file 'tests/mocks/Utils/windowstatestorage.cpp' |
4468 | --- tests/mocks/Utils/windowstatestorage.cpp 2015-10-27 11:16:31 +0000 |
4469 | +++ tests/mocks/Utils/windowstatestorage.cpp 2016-03-11 12:07:48 +0000 |
4470 | @@ -16,6 +16,8 @@ |
4471 | |
4472 | #include "windowstatestorage.h" |
4473 | |
4474 | +#include <unity/shell/application/ApplicationInfoInterface.h> |
4475 | + |
4476 | WindowStateStorage::WindowStateStorage(QObject *parent): |
4477 | QObject(parent) |
4478 | { |
4479 | @@ -45,10 +47,21 @@ |
4480 | return m_geometry.value(windowId).toRect(); |
4481 | } |
4482 | |
4483 | +void WindowStateStorage::saveStage(const QString &appId, int stage) |
4484 | +{ |
4485 | + m_stage[appId] = stage; |
4486 | +} |
4487 | + |
4488 | +int WindowStateStorage::getStage(const QString &appId) const |
4489 | +{ |
4490 | + return m_stage.value(appId, unity::shell::application::ApplicationInfoInterface::MainStage); |
4491 | +} |
4492 | + |
4493 | void WindowStateStorage::clear() |
4494 | { |
4495 | m_state.clear(); |
4496 | m_geometry.clear(); |
4497 | + m_stage.clear(); |
4498 | } |
4499 | |
4500 | void WindowStateStorage::saveState(const QString &windowId, WindowState state) |
4501 | |
4502 | === modified file 'tests/mocks/Utils/windowstatestorage.h' |
4503 | --- tests/mocks/Utils/windowstatestorage.h 2015-10-27 11:16:31 +0000 |
4504 | +++ tests/mocks/Utils/windowstatestorage.h 2016-03-11 12:07:48 +0000 |
4505 | @@ -36,6 +36,9 @@ |
4506 | Q_INVOKABLE void saveGeometry(const QString &windowId, const QRect &rect); |
4507 | Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect &defaultValue); |
4508 | |
4509 | + Q_INVOKABLE void saveStage(const QString &appId, int stage); |
4510 | + Q_INVOKABLE int getStage(const QString &appId) const; |
4511 | + |
4512 | // Only in the mock, to easily restore a fresh state |
4513 | Q_INVOKABLE void clear(); |
4514 | |
4515 | @@ -47,5 +50,6 @@ |
4516 | QVariantMap geometry() const; |
4517 | |
4518 | QHash<QString, WindowState> m_state; |
4519 | + QHash<QString, int> m_stage; |
4520 | QVariantMap m_geometry; |
4521 | }; |
4522 | |
4523 | === modified file 'tests/plugins/Ubuntu/Gestures/CMakeLists.txt' |
4524 | --- tests/plugins/Ubuntu/Gestures/CMakeLists.txt 2015-08-03 13:49:42 +0000 |
4525 | +++ tests/plugins/Ubuntu/Gestures/CMakeLists.txt 2016-03-11 12:07:48 +0000 |
4526 | @@ -20,6 +20,11 @@ |
4527 | target_link_libraries(${CLASSNAME}TestExec UbuntuGesturesQml UbuntuGestures) |
4528 | endmacro() |
4529 | |
4530 | +set(UNITY_IMPORT_PATHS |
4531 | + ${CMAKE_BINARY_DIR}/tests/utils/modules |
4532 | + ${UNITY_PLUGINPATH} |
4533 | +) |
4534 | + |
4535 | macro(add_gesture_unit_test CLASSNAME) |
4536 | build_gesture_test(${CLASSNAME}) |
4537 | add_unity8_unittest(${CLASSNAME} ${CLASSNAME}TestExec |
4538 | @@ -36,7 +41,7 @@ |
4539 | DEPENDS UbuntuGesturesTest-qmlfiles |
4540 | ${ARGN} |
4541 | ) |
4542 | - add_manual_qml_test(. ${CLASSNAME} IMPORT_PATHS ${UNITY_PLUGINPATH}) |
4543 | + add_manual_qml_test(. ${CLASSNAME} IMPORT_PATHS ${UNITY_IMPORT_PATHS}) |
4544 | endmacro() |
4545 | |
4546 | add_gesture_ui_test(DirectionalDragArea) |
4547 | @@ -46,3 +51,4 @@ |
4548 | add_gesture_ui_test(TouchGate) |
4549 | add_gesture_unit_test(Damper) |
4550 | add_gesture_unit_test(AxisVelocityCalculator) |
4551 | +add_gesture_ui_test(TouchGestureArea) |
4552 | |
4553 | === modified file 'tests/plugins/Ubuntu/Gestures/GestureTest.cpp' |
4554 | --- tests/plugins/Ubuntu/Gestures/GestureTest.cpp 2015-05-21 15:52:48 +0000 |
4555 | +++ tests/plugins/Ubuntu/Gestures/GestureTest.cpp 2016-03-11 12:07:48 +0000 |
4556 | @@ -17,6 +17,7 @@ |
4557 | #include "GestureTest.h" |
4558 | |
4559 | #include <qpa/qwindowsysteminterface.h> |
4560 | +#include <private/qquickwindow_p.h> |
4561 | #include <QQmlEngine> |
4562 | #include <QQuickView> |
4563 | #include <QtTest> |
4564 | @@ -72,6 +73,49 @@ |
4565 | m_view = nullptr; |
4566 | } |
4567 | |
4568 | +void GestureTest::sendTouchPress(qint64 timestamp, int id, QPointF pos) |
4569 | +{ |
4570 | + sendTouch(timestamp, id, pos, Qt::TouchPointPressed, QEvent::TouchBegin); |
4571 | +} |
4572 | + |
4573 | +void GestureTest::sendTouchUpdate(qint64 timestamp, int id, QPointF pos) |
4574 | +{ |
4575 | + sendTouch(timestamp, id, pos, Qt::TouchPointMoved, QEvent::TouchUpdate); |
4576 | +} |
4577 | + |
4578 | +void GestureTest::sendTouchRelease(qint64 timestamp, int id, QPointF pos) |
4579 | +{ |
4580 | + sendTouch(timestamp, id, pos, Qt::TouchPointReleased, QEvent::TouchEnd); |
4581 | +} |
4582 | + |
4583 | +void GestureTest::sendTouch(qint64 timestamp, int id, QPointF pos, |
4584 | + Qt::TouchPointState pointState, QEvent::Type eventType) |
4585 | +{ |
4586 | + m_fakeTimerFactory->updateTime(timestamp); |
4587 | + |
4588 | + QTouchEvent::TouchPoint point; |
4589 | + |
4590 | + point.setState(pointState); |
4591 | + point.setId(id); |
4592 | + point.setScenePos(pos); |
4593 | + point.setPos(pos); |
4594 | + |
4595 | + QList<QTouchEvent::TouchPoint> points; |
4596 | + points << point; |
4597 | + |
4598 | + QTouchEvent touchEvent(eventType, m_device, Qt::NoModifier, Qt::TouchPointPressed, points); |
4599 | + QCoreApplication::sendEvent(m_view, &touchEvent); |
4600 | + |
4601 | + QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(m_view); |
4602 | + windowPrivate->flushDelayedTouchEvent(); |
4603 | +} |
4604 | + |
4605 | +void GestureTest::passTime(qint64 timeSpanMs) |
4606 | +{ |
4607 | + qint64 finalTime = m_fakeTimerFactory->timeSource()->msecsSinceReference() + timeSpanMs; |
4608 | + m_fakeTimerFactory->updateTime(finalTime); |
4609 | +} |
4610 | + |
4611 | ////////////////////////// TouchMemento ///////////////////////////// |
4612 | |
4613 | TouchMemento::TouchMemento(const QTouchEvent *touchEvent) |
4614 | |
4615 | === modified file 'tests/plugins/Ubuntu/Gestures/GestureTest.h' |
4616 | --- tests/plugins/Ubuntu/Gestures/GestureTest.h 2015-04-10 21:16:37 +0000 |
4617 | +++ tests/plugins/Ubuntu/Gestures/GestureTest.h 2016-03-11 12:07:48 +0000 |
4618 | @@ -72,7 +72,7 @@ |
4619 | { |
4620 | Q_OBJECT |
4621 | public: |
4622 | - // \param qmlFilename name of the qml file to be loaded by the QQuickView |
4623 | + // \param qmlFilename name of the qml file to be loaded by the QQuickView |
4624 | GestureTest(const QString &qmlFilename); |
4625 | |
4626 | protected Q_SLOTS: |
4627 | @@ -81,6 +81,18 @@ |
4628 | virtual void cleanup(); // called right after each and every test function is executed |
4629 | |
4630 | protected: |
4631 | + // QTest::touchEvent takes QPoint instead of QPointF and I don't want to |
4632 | + // lose precision due to rounding. |
4633 | + // Besides, those helper functions lead to more compact code. |
4634 | + void sendTouchPress(qint64 timestamp, int id, QPointF pos); |
4635 | + void sendTouchUpdate(qint64 timestamp, int id, QPointF pos); |
4636 | + void sendTouchRelease(qint64 timestamp, int id, QPointF pos); |
4637 | + void sendTouch(qint64 timestamp, int id, QPointF pos, |
4638 | + Qt::TouchPointState pointState, QEvent::Type eventType); |
4639 | + |
4640 | + void passTime(qint64 timeSpanMs); |
4641 | + |
4642 | +protected: |
4643 | QTouchDevice *m_device; |
4644 | QQuickView *m_view; |
4645 | TouchRegistry *m_touchRegistry; |
4646 | |
4647 | === modified file 'tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp' |
4648 | --- tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp 2015-08-19 14:24:07 +0000 |
4649 | +++ tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp 2016-03-11 12:07:48 +0000 |
4650 | @@ -20,7 +20,6 @@ |
4651 | #include <QtQml/QQmlEngine> |
4652 | #include <QPointer> |
4653 | #include <private/qquickmousearea_p.h> |
4654 | -#include <private/qquickwindow_p.h> |
4655 | |
4656 | |
4657 | #include <DirectionalDragArea.h> |
4658 | @@ -109,18 +108,6 @@ |
4659 | void makoRightEdgeDrag_verticalDownwards(); |
4660 | void makoLeftEdgeDrag_slowStart(); |
4661 | void makoLeftEdgeDrag_movesSlightlyBackwardsOnStart(); |
4662 | - |
4663 | -private: |
4664 | - // QTest::touchEvent takes QPoint instead of QPointF and I don't want to |
4665 | - // lose precision due to rounding. |
4666 | - // Besides, those helper functions lead to more compact code. |
4667 | - void sendTouchPress(qint64 timestamp, int id, QPointF pos); |
4668 | - void sendTouchUpdate(qint64 timestamp, int id, QPointF pos); |
4669 | - void sendTouchRelease(qint64 timestamp, int id, QPointF pos); |
4670 | - void sendTouch(qint64 timestamp, int id, QPointF pos, |
4671 | - Qt::TouchPointState pointState, QEvent::Type eventType); |
4672 | - |
4673 | - void passTime(qint64 timeSpanMs); |
4674 | }; |
4675 | |
4676 | tst_DirectionalDragArea::tst_DirectionalDragArea() |
4677 | @@ -141,49 +128,6 @@ |
4678 | QTRY_COMPARE(m_view->height(), (int)m_view->rootObject()->height()); |
4679 | } |
4680 | |
4681 | -void tst_DirectionalDragArea::sendTouchPress(qint64 timestamp, int id, QPointF pos) |
4682 | -{ |
4683 | - sendTouch(timestamp, id, pos, Qt::TouchPointPressed, QEvent::TouchBegin); |
4684 | -} |
4685 | - |
4686 | -void tst_DirectionalDragArea::sendTouchUpdate(qint64 timestamp, int id, QPointF pos) |
4687 | -{ |
4688 | - sendTouch(timestamp, id, pos, Qt::TouchPointMoved, QEvent::TouchUpdate); |
4689 | -} |
4690 | - |
4691 | -void tst_DirectionalDragArea::sendTouchRelease(qint64 timestamp, int id, QPointF pos) |
4692 | -{ |
4693 | - sendTouch(timestamp, id, pos, Qt::TouchPointReleased, QEvent::TouchEnd); |
4694 | -} |
4695 | - |
4696 | -void tst_DirectionalDragArea::sendTouch(qint64 timestamp, int id, QPointF pos, |
4697 | - Qt::TouchPointState pointState, QEvent::Type eventType) |
4698 | -{ |
4699 | - m_fakeTimerFactory->updateTime(timestamp); |
4700 | - |
4701 | - QTouchEvent::TouchPoint point; |
4702 | - |
4703 | - point.setState(pointState); |
4704 | - point.setId(id); |
4705 | - point.setScenePos(pos); |
4706 | - point.setPos(pos); |
4707 | - |
4708 | - QList<QTouchEvent::TouchPoint> points; |
4709 | - points << point; |
4710 | - |
4711 | - QTouchEvent touchEvent(eventType, m_device, Qt::NoModifier, Qt::TouchPointPressed, points); |
4712 | - QCoreApplication::sendEvent(m_view, &touchEvent); |
4713 | - |
4714 | - QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(m_view); |
4715 | - windowPrivate->flushDelayedTouchEvent(); |
4716 | -} |
4717 | - |
4718 | -void tst_DirectionalDragArea::passTime(qint64 timeSpanMs) |
4719 | -{ |
4720 | - qint64 finalTime = m_fakeTimerFactory->timeSource()->msecsSinceReference() + timeSpanMs; |
4721 | - m_fakeTimerFactory->updateTime(finalTime); |
4722 | -} |
4723 | - |
4724 | namespace { |
4725 | QPointF calculateInitialTouchPos(DirectionalDragArea *edgeDragArea) |
4726 | { |
4727 | |
4728 | === added file 'tests/plugins/Ubuntu/Gestures/tst_TouchGestureArea.cpp' |
4729 | --- tests/plugins/Ubuntu/Gestures/tst_TouchGestureArea.cpp 1970-01-01 00:00:00 +0000 |
4730 | +++ tests/plugins/Ubuntu/Gestures/tst_TouchGestureArea.cpp 2016-03-11 12:07:48 +0000 |
4731 | @@ -0,0 +1,270 @@ |
4732 | +/* |
4733 | + * Copyright (C) 2016 Canonical, Ltd. |
4734 | + * |
4735 | + * This program is free software; you can redistribute it and/or modify |
4736 | + * it under the terms of the GNU General Public License as published by |
4737 | + * the Free Software Foundation; version 3. |
4738 | + * |
4739 | + * This program is distributed in the hope that it will be useful, |
4740 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4741 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4742 | + * GNU General Public License for more details. |
4743 | + * |
4744 | + * You should have received a copy of the GNU General Public License |
4745 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4746 | + */ |
4747 | + |
4748 | +#include "GestureTest.h" |
4749 | + |
4750 | +#include <TouchGestureArea.h> |
4751 | + |
4752 | +#include <QQuickItem> |
4753 | +#include <QQuickView> |
4754 | +#include <QtTest> |
4755 | + |
4756 | +namespace { |
4757 | +QPointF calculateInitialTouchPos(TouchGestureArea *edgeDragArea) |
4758 | +{ |
4759 | + QPointF localCenter(edgeDragArea->width() / 2., edgeDragArea->height() / 2.); |
4760 | + return edgeDragArea->mapToScene(localCenter); |
4761 | +} |
4762 | +} |
4763 | + |
4764 | +class tst_TouchGestureArea: public GestureTest |
4765 | +{ |
4766 | + Q_OBJECT |
4767 | +public: |
4768 | + tst_TouchGestureArea(); |
4769 | +private Q_SLOTS: |
4770 | + void init() override; // called right before each and every test function is executed |
4771 | + |
4772 | + void minimumTouchPoints(); |
4773 | + void maximumTouchPoints(); |
4774 | + void minimumAndMaximumTouchPoints(); |
4775 | + void rejectGestureAfterRecognitionPeriod(); |
4776 | + void releaseAndPressRecognisedGestureDoesNotRejectForPeriod(); |
4777 | + void topAreaReceivesOwnershipFirstWithEqualPoints(); |
4778 | + void topAreaReceivesOwnershipFirstWithMorePoints(); |
4779 | + |
4780 | +private: |
4781 | + void initGestureComponent(TouchGestureArea *area); |
4782 | + |
4783 | + QQuickItem *m_blueRect; |
4784 | + TouchGestureArea *m_gestureBottom; |
4785 | + TouchGestureArea *m_gestureMiddle; |
4786 | + TouchGestureArea *m_gestureTop; |
4787 | +}; |
4788 | + |
4789 | +tst_TouchGestureArea::tst_TouchGestureArea() |
4790 | + : GestureTest(QStringLiteral("tst_TouchGestureArea.qml")) |
4791 | +{ |
4792 | +} |
4793 | + |
4794 | +inline void tst_TouchGestureArea::initGestureComponent(TouchGestureArea* area) |
4795 | +{ |
4796 | + area->setRecognitionTimer(m_fakeTimerFactory->createTimer(area)); |
4797 | + area->setMinimumTouchPoints(1); |
4798 | + area->setMaximumTouchPoints(INT_MAX); |
4799 | + area->setRecognitionPeriod(50); |
4800 | + area->setReleaseRejectPeriod(100); |
4801 | + // start tests with area disabled (enable as desired) |
4802 | + area->setEnabled(false); |
4803 | +} |
4804 | + |
4805 | +void tst_TouchGestureArea::init() |
4806 | +{ |
4807 | + GestureTest::init(); |
4808 | + |
4809 | + m_blueRect = m_view->rootObject()->findChild<QQuickItem*>("blueRect"); |
4810 | + Q_ASSERT(m_blueRect != nullptr); |
4811 | + |
4812 | + m_gestureBottom = |
4813 | + m_view->rootObject()->findChild<TouchGestureArea*>("touchGestureAreaBottom"); |
4814 | + Q_ASSERT(m_gestureBottom != nullptr); |
4815 | + |
4816 | + m_gestureMiddle = |
4817 | + m_view->rootObject()->findChild<TouchGestureArea*>("touchGestureAreaMiddle"); |
4818 | + Q_ASSERT(m_gestureMiddle != nullptr); |
4819 | + |
4820 | + m_gestureTop = |
4821 | + m_view->rootObject()->findChild<TouchGestureArea*>("touchGestureAreaTop"); |
4822 | + Q_ASSERT(m_gestureTop != nullptr); |
4823 | + |
4824 | + initGestureComponent(m_gestureBottom); |
4825 | + initGestureComponent(m_gestureMiddle); |
4826 | + initGestureComponent(m_gestureTop); |
4827 | +} |
4828 | + |
4829 | +void tst_TouchGestureArea::minimumTouchPoints() |
4830 | +{ |
4831 | + m_gestureBottom->setEnabled(true); |
4832 | + m_gestureBottom->setMinimumTouchPoints(4); |
4833 | + |
4834 | + QPointF touchPoint = calculateInitialTouchPos(m_gestureBottom); |
4835 | + |
4836 | + QCOMPARE((int)m_gestureBottom->status(), (int)TouchGestureArea::WaitingForTouch); |
4837 | + QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint()); |
4838 | + QCOMPARE((int)m_gestureBottom->status(), (int)TouchGestureArea::Undecided); |
4839 | + QTest::touchEvent(m_view, m_device).stationary(0) |
4840 | + .press(1, touchPoint.toPoint()); |
4841 | + QCOMPARE((int)m_gestureBottom->status(), (int)TouchGestureArea::Undecided); |
4842 | + QTest::touchEvent(m_view, m_device).stationary(0) |
4843 | + .stationary(1) |
4844 | + .press(2, touchPoint.toPoint()); |
4845 | + QCOMPARE((int)m_gestureBottom->status(), (int)TouchGestureArea::Undecided); |
4846 | + QTest::touchEvent(m_view, m_device).stationary(0) |
4847 | + .stationary(1) |
4848 | + .stationary(2) |
4849 | + .press(3, touchPoint.toPoint()); |
4850 | + QCOMPARE((int)m_gestureBottom->status(), (int)TouchGestureArea::Recognized); |
4851 | + // test minimum overflow |
4852 | + QTest::touchEvent(m_view, m_device).stationary(0) |
4853 | + .stationary(1) |
4854 | + .stationary(2) |
4855 | + .stationary(3) |
4856 | + .press(4, touchPoint.toPoint()); |
4857 | + QCOMPARE((int)m_gestureBottom->status(), (int)TouchGestureArea::Recognized); |
4858 | +} |
4859 | + |
4860 | +void tst_TouchGestureArea::maximumTouchPoints() |
4861 | +{ |
4862 | + m_gestureBottom->setEnabled(true); |
4863 | + m_gestureBottom->setMaximumTouchPoints(2); |
4864 | + |
4865 | + QPointF touchPoint = calculateInitialTouchPos(m_gestureBottom); |
4866 | + |
4867 | + QCOMPARE((int)m_gestureBottom->status(), (int)TouchGestureArea::WaitingForTouch); |
4868 | + QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint()); |
4869 | + QCOMPARE((int)m_gestureBottom->status(), (int)TouchGestureArea::Recognized); |
4870 | + QTest::touchEvent(m_view, m_device).stationary(0) |
4871 | + .press(1, touchPoint.toPoint()); |
4872 | + QCOMPARE((int)m_gestureBottom->status(), (int)TouchGestureArea::Recognized); |
4873 | + |
4874 | + // test maximum overflow |
4875 | + QTest::touchEvent(m_view, m_device).stationary(0) |
4876 | + .stationary(1) |
4877 | + .press(2, touchPoint.toPoint()); |
4878 | + QCOMPARE((int)m_gestureBottom->status(), (int)TouchGestureArea::Rejected); |
4879 | + // test still rejected |
4880 | + QTest::touchEvent(m_view, m_device).stationary(0) |
4881 | + .stationary(1) |
4882 | + .press(3, touchPoint.toPoint()); |
4883 | + QCOMPARE((int)m_gestureBottom->status(), (int)TouchGestureArea::Rejected); |
4884 | +} |
4885 | + |
4886 | +void tst_TouchGestureArea::minimumAndMaximumTouchPoints() |
4887 | +{ |
4888 | + m_gestureBottom->setEnabled(true); |
4889 | + m_gestureBottom->setMinimumTouchPoints(2); |
4890 | + m_gestureBottom->setMaximumTouchPoints(2); |
4891 | + |
4892 | + QPointF touchPoint = calculateInitialTouchPos(m_gestureBottom); |
4893 | + |
4894 | + QCOMPARE((int)m_gestureBottom->status(), (int)TouchGestureArea::WaitingForTouch); |
4895 | + QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint()); |
4896 | + QCOMPARE((int)m_gestureBottom->status(), (int)TouchGestureArea::Undecided); |
4897 | + QTest::touchEvent(m_view, m_device).stationary(0) |
4898 | + .press(1, touchPoint.toPoint()); |
4899 | + QCOMPARE((int)m_gestureBottom->status(), (int)TouchGestureArea::Recognized); |
4900 | + |
4901 | + // test maximum overflow |
4902 | + QTest::touchEvent(m_view, m_device).stationary(0) |
4903 | + .stationary(1) |
4904 | + .press(2, touchPoint.toPoint()); |
4905 | + QCOMPARE((int)m_gestureBottom->status(), (int)TouchGestureArea::Rejected); |
4906 | +} |
4907 | + |
4908 | +void tst_TouchGestureArea::rejectGestureAfterRecognitionPeriod() |
4909 | +{ |
4910 | + m_gestureBottom->setEnabled(true); |
4911 | + m_gestureBottom->setMinimumTouchPoints(2); |
4912 | + m_gestureBottom->setMaximumTouchPoints(2); |
4913 | + |
4914 | + QPointF touchPoint = calculateInitialTouchPos(m_gestureBottom); |
4915 | + |
4916 | + QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint()); |
4917 | + QCOMPARE((int)m_gestureBottom->status(), (int)TouchGestureArea::Undecided); // Recognition period is 50. |
4918 | + passTime(40); |
4919 | + QCOMPARE((int)m_gestureBottom->status(), (int)TouchGestureArea::Undecided); |
4920 | + passTime(10); |
4921 | + QCOMPARE((int)m_gestureBottom->status(), (int)TouchGestureArea::Rejected); |
4922 | +} |
4923 | + |
4924 | +void tst_TouchGestureArea::releaseAndPressRecognisedGestureDoesNotRejectForPeriod() |
4925 | +{ |
4926 | + m_gestureBottom->setEnabled(true); |
4927 | + m_gestureBottom->setMinimumTouchPoints(2); |
4928 | + m_gestureBottom->setMaximumTouchPoints(2); |
4929 | + |
4930 | + bool wasRejected = false; |
4931 | + connect(m_gestureBottom, &TouchGestureArea::statusChanged, |
4932 | + this, [&wasRejected](int status) { |
4933 | + if (status == TouchGestureArea::Rejected) wasRejected = true; |
4934 | + }); |
4935 | + |
4936 | + QPointF touchPoint = calculateInitialTouchPos(m_gestureBottom); |
4937 | + |
4938 | + QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint()) |
4939 | + .press(1, touchPoint.toPoint()); |
4940 | + QCOMPARE((int)m_gestureBottom->status(), (int)TouchGestureArea::Recognized); |
4941 | + passTime(5); |
4942 | + QTest::touchEvent(m_view, m_device).stationary(0) |
4943 | + .release(1, touchPoint.toPoint()); // Release period is 100. |
4944 | + QCOMPARE((int)m_gestureBottom->status(), (int)TouchGestureArea::Recognized); |
4945 | + passTime(70); |
4946 | + QCOMPARE((int)m_gestureBottom->status(), (int)TouchGestureArea::Recognized); |
4947 | + QCOMPARE(wasRejected, false); |
4948 | + passTime(29); |
4949 | + QCOMPARE(wasRejected, false); |
4950 | + QCOMPARE((int)m_gestureBottom->status(), (int)TouchGestureArea::Recognized); |
4951 | + passTime(1); |
4952 | + QCOMPARE((int)m_gestureBottom->status(), (int)TouchGestureArea::Rejected); |
4953 | +} |
4954 | + |
4955 | +void tst_TouchGestureArea::topAreaReceivesOwnershipFirstWithEqualPoints() |
4956 | +{ |
4957 | + m_gestureBottom->setEnabled(true); |
4958 | + m_gestureMiddle->setEnabled(true); |
4959 | + m_gestureTop->setEnabled(true); |
4960 | + |
4961 | + m_gestureBottom->setMinimumTouchPoints(1); |
4962 | + m_gestureMiddle->setMinimumTouchPoints(1); |
4963 | + m_gestureTop->setMinimumTouchPoints(1); |
4964 | + |
4965 | + QPointF touchPoint = calculateInitialTouchPos(m_gestureBottom); |
4966 | + |
4967 | + QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint()); |
4968 | + |
4969 | + QCOMPARE((int)m_gestureTop->status(), (int)TouchGestureArea::Recognized); |
4970 | + // Lower items will not recieve the touch events |
4971 | + QCOMPARE((int)m_gestureMiddle->status(), (int)TouchGestureArea::WaitingForTouch); |
4972 | + QCOMPARE((int)m_gestureBottom->status(), (int)TouchGestureArea::WaitingForTouch); |
4973 | +} |
4974 | + |
4975 | +void tst_TouchGestureArea::topAreaReceivesOwnershipFirstWithMorePoints() |
4976 | +{ |
4977 | + m_gestureBottom->setEnabled(true); |
4978 | + m_gestureMiddle->setEnabled(true); |
4979 | + m_gestureTop->setEnabled(true); |
4980 | + |
4981 | + m_gestureBottom->setMinimumTouchPoints(1); |
4982 | + m_gestureMiddle->setMinimumTouchPoints(1); |
4983 | + m_gestureTop->setMinimumTouchPoints(2); |
4984 | + |
4985 | + QPointF touchPoint = calculateInitialTouchPos(m_gestureBottom); |
4986 | + |
4987 | + QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint()); |
4988 | + QCOMPARE((int)m_gestureTop->status(), (int)TouchGestureArea::Undecided); |
4989 | + QCOMPARE((int)m_gestureMiddle->status(), (int)TouchGestureArea::Undecided); |
4990 | + // The middle item will accept the events; so the bottom item will not get a chance. |
4991 | + QCOMPARE((int)m_gestureBottom->status(), (int)TouchGestureArea::WaitingForTouch); |
4992 | + |
4993 | + QTest::touchEvent(m_view, m_device).stationary(0) |
4994 | + .press(1, touchPoint.toPoint()); |
4995 | + QCOMPARE((int)m_gestureTop->status(), (int)TouchGestureArea::Recognized); |
4996 | + QCOMPARE((int)m_gestureMiddle->status(), (int)TouchGestureArea::Rejected); |
4997 | +} |
4998 | + |
4999 | +QTEST_MAIN(tst_TouchGestureArea) |
5000 | + |
Just a couple of inline code comments