Merge lp:~mzanetti/unity8/right-edge-2 into lp:unity8

Proposed by Michael Zanetti
Status: Superseded
Proposed branch: lp:~mzanetti/unity8/right-edge-2
Merge into: lp:unity8
Diff against target: 4317 lines (+2412/-902)
43 files modified
debian/control (+3/-1)
debian/unity8.install (+1/-1)
plugins/Utils/CMakeLists.txt (+2/-1)
plugins/Utils/easingcurve.cpp (+67/-0)
plugins/Utils/easingcurve.h (+70/-0)
plugins/Utils/plugin.cpp (+2/-0)
qml/CMakeLists.txt (+1/-1)
qml/Components/ApplicationManagerWrapper.qml (+0/-169)
qml/Components/ApplicationScreenshot.qml (+0/-61)
qml/Components/ApplicationsModelStageFiltered.qml (+0/-49)
qml/Components/ResponsiveFlowView.qml (+3/-10)
qml/Dash/Apps/RunningApplicationTile.qml (+15/-25)
qml/Dash/Apps/RunningApplicationsGrid.qml (+5/-20)
qml/Dash/DashApps.qml (+4/-5)
qml/Dash/GenericScopeView.qml (+2/-2)
qml/Launcher/Launcher.qml (+2/-1)
qml/Panel/Panel.qml (+1/-1)
qml/Shell.qml (+229/-331)
qml/SideStage/SideStage.qml (+0/-73)
qml/Stages/PhoneStage.qml (+501/-0)
qml/Stages/SpreadDelegate.qml (+55/-0)
qml/Stages/StageWithSideStage.qml (+405/-0)
qml/Stages/SwitchingApplicationImage.qml (+81/-0)
qml/Stages/TransformedSpreadDelegate.qml (+327/-0)
run_on_device (+1/-0)
tests/autopilot/unity8/shell/emulators/dash.py (+1/-8)
tests/autopilot/unity8/shell/emulators/main_window.py (+1/-1)
tests/autopilot/unity8/shell/tests/test_emulators.py (+1/-1)
tests/mocks/Unity/Application/ApplicationInfo.cpp (+9/-0)
tests/mocks/Unity/Application/ApplicationInfo.h (+2/-0)
tests/mocks/Unity/Application/ApplicationManager.cpp (+109/-3)
tests/mocks/Unity/Application/ApplicationManager.h (+16/-0)
tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp (+74/-0)
tests/mocks/Unity/Application/ApplicationScreenshotProvider.h (+34/-0)
tests/mocks/Unity/Application/CMakeLists.txt (+1/-0)
tests/mocks/Unity/Application/plugin.cpp (+18/-3)
tests/mocks/Unity/Application/plugin.h (+1/-0)
tests/plugins/Unity/Launcher/launchermodeltest.cpp (+5/-0)
tests/qmltests/CMakeLists.txt (+1/-1)
tests/qmltests/Components/tst_ResponsiveFlowView.qml (+1/-2)
tests/qmltests/Dash/Apps/tst_RunningApplicationsGrid.qml (+57/-124)
tests/qmltests/Stages/tst_PhoneStage.qml (+288/-0)
tests/qmltests/tst_Shell.qml (+16/-8)
To merge this branch: bzr merge lp:~mzanetti/unity8/right-edge-2
Reviewer Review Type Date Requested Status
Gerry Boland (community) Approve
Mirco Müller (community) Approve
PS Jenkins bot (community) continuous-integration Needs Fixing
Albert Astals Cid (community) Needs Fixing
Ubuntu Unity PS integration team packaging Pending
Vesa Rautiainen Pending
Review via email: mp+204798@code.launchpad.net

This proposal has been superseded by a proposal from 2014-03-28.

Commit message

Implement the right edge app switching with an App Spread.

* Reworks interaction with ApplicationManager (This will end SurfaceFlinger support)
* Reworks Stage and SideStage
* Implements the App Spread for the Phone form factor right edge
* Reimplements the overlay mode side stage for the tablet form factor

Description of the change

* Are there any related MPs required for this MP to build/function as expected? Please list.

https://code.launchpad.net/~mzanetti/unity-api/new-screenshot-and-focusing-api/+merge/199810
https://code.launchpad.net/~mzanetti/unity-mir/screenshotting-focusing-api/+merge/199811
https://code.launchpad.net/~mzanetti/unity-scopes-shell/activate-appid-2/+merge/205443
https://code.launchpad.net/~gerboland/unity-mir/phone-surface-bad-position/+merge/212695
https://code.launchpad.net/~mzanetti/qtubuntu/drop-appmanager/+merge/212697

Here are packages for everything related: https://chinstrap.canonical.com/~mzanetti/right-edge-pkgs/

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

Yes (MWC image)

 * If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?

Yes

 * If you changed the UI, has there been a design review?

lots of them :)

To post a comment you must log in.
lp:~mzanetti/unity8/right-edge-2 updated
660. By Michael Zanetti

drop print()s

661. By Michael Zanetti

add missing copyright headers

662. By Michael Zanetti

drop old sidestage code

663. By Michael Zanetti

drop prints()s in StageWithSideStage

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mzanetti/unity8/right-edge-2 updated
664. By Michael Zanetti

add missing copyright headers

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mzanetti/unity8/right-edge-2 updated
665. By Michael Zanetti

ooops. Add missing files

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mzanetti/unity8/right-edge-2 updated
666. By Michael Zanetti

make it start up in phone form factor again

667. By Michael Zanetti

merge trunk

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

1785 +// Rectangle { anchors.fill: parent; color: "blue"; opacity: 0.5 }

Leftover

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

1901 + onApplicationAdded: {
1902 + priv.startingAppId = appId;
1903 +// var application = ApplicationManager.findApplication(appId)
1904 +// print("Application added:", appId, application.state)
1905 +// if (application.stage == ApplicationInfoInterface.SideStage) {
1906 +// priv.sideStageAppId = appId;
1907 +// mainStageImage.switchTo(application)
1908 +// mainStageImage.visible = true;
1909 +// } else if (application.stage == ApplicationInfoInterface.MainStage) {
1910 +// priv.mainStageAppId = appId;
1911 +// sideStageImage.switchTo(application)
1912 +// sideStageImage.visible = true;
1913 +// }
1914 + }

Do we want to have this commented-out code?

lp:~mzanetti/unity8/right-edge-2 updated
668. By Michael Zanetti

remove leftover Rectangle

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mzanetti/unity8/right-edge-2 updated
669. By Michael Zanetti

update according to latest unity-api changes

670. By Michael Zanetti

also update launchertest's mocked appmanager to the new api

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mzanetti/unity8/right-edge-2 updated
671. By Michael Zanetti

fix whitespace tests

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote :

Please mention in the commit message that this branch marks the end of SurfaceFlinger support, so unity8 is not Mir only.

Revision history for this message
Gerry Boland (gerboland) wrote :

> Please mention in the commit message that this branch marks the end of
> SurfaceFlinger support, so unity8 is not Mir only.
I meant: *now* Mir only
/hittip tsdgeos

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

In PhoneStage.qml:

"""
+ property bool fullscreen: ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
"""

That generates errors, polluting the console, when there's no focused application. Having it the following way is more concise and solves this issue:

property bool fullscreen: priv.focusedApplication ? priv.focusedApplication.fullscreen : false

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

Likewise here:

"""
        PropertyAction { target: fadeInScreenshotImage; property: "source"; value: ApplicationManager.findApplication(priv.newFocusedAppId).screenshot }
"""

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

I'm getting the following error on start up:

qml/Stages/PhoneStage.qml:356: ReferenceError: shift is not defined

Which is solved by:

"""
         SequentialAnimation {
             id: snapAnimation
- property int targetContentX: -shift
+ property int targetContentX: -spreadView.shift

             UbuntuNumberAnimation {
                 target: spreadView
"""

Revision history for this message
Gerry Boland (gerboland) wrote :

+++ qml/Shell.qml
+ property bool sideStageEnabled: shell.width >= units.gu(60)
Can be readonly.

+ property bool fullyCovered: panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth
Do no harm here either

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

And this other error on start up:

qml/Stages/PhoneStage.qml:47: TypeError: Cannot read property 'screenshot' of null

"""
                mainScreenshotImage.src = ApplicationManager.get(0).screenshot
"""

So gotta check if there's indeed a running app before querying its screenshot.

Revision history for this message
Gerry Boland (gerboland) wrote :

+ readonly property bool applicationRunning: ApplicationManager.focusedApplicationId.length > 0
I don't see this used anywhere actually.

Revision history for this message
Gerry Boland (gerboland) wrote :

Functional bugs (phone testing only)
1. I launch Contacts app from dash. Wait for it to appear. Left edge swipe it away. I'm not able to bring back Contacts by tapping its preview, nor its dahs or launcher icon.

2. I launch Contacts app from dash. Wait for it to appear. Left edge swipe it away. I then launch Dialer app from dash, I don't get a starting animation: instead Contacts app immediately shown, and eventually Dialer pops up on top

review: Needs Fixing
Revision history for this message
Gerry Boland (gerboland) wrote :

3. With 2 apps open, doing a full right edge swipe, the animation is linked to my finger distance from the right edge, until about 70% of the way, when it just stops. Then only finger release completes the animation. It feels like the animation locks up. Did Design want this?

4. Have app open and active. Press power to turn off display, and again to show greeter. Do a slow right-edge swipe - a black rectangle is revealed. Only on finger release does the app appear.

5. I've 2 apps open. I open a third from the dash. Once it has appeared (giving it a second more just in case), I start a right edge gesture. I only see 2 apps in the spread, the latest one has disappeared

review: Needs Fixing
Revision history for this message
Gerry Boland (gerboland) wrote :

Sorry about the hopeless picture, but this was something minor I noticed:
http://imgur.com/O6EFlQh
I opened 3 apps, dialer, contacts & system settings. With the order in the spread of contacts, settings, dialer, I grabbed the dialer screenshot and brought it as far left as I could. I noticed that a bit of the system settings image could be made visible over the dialer. See the image above, I marked it in red

Revision history for this message
Gerry Boland (gerboland) wrote :

6. With spread open, opening launcher and tapping dash icon does nothing

7. Have a couple of apps open, and engage the spread. Now open the (as example) sound indicator, and tap the "Sound settings" entry. Indicators close, but spread remains. Tapping the system settings does bring me to the correct screen.

8. Strangely, if I open System Settings app via indicators (same steps as in 7 above), it often opens with incorrect x position of the surface (i.e there's a 2-3GU gap on the left). Need to check with trunk.

Revision history for this message
Gerry Boland (gerboland) wrote :

9. Have system settings & dialer app open, dialer app to the front. Open indicators, select Sound and tap "Sound settings". You see the dialer app slide to the right, but a black rectangle remains, until suddenly system settings appears

Revision history for this message
Gerry Boland (gerboland) wrote :

http://imgur.com/gz5p3Gg I managed to get somehow, not sure how

Revision history for this message
Gerry Boland (gerboland) wrote :

> http://imgur.com/gz5p3Gg I managed to get somehow, not sure how
Ah I had started Gmail app. Seems it took an ultra long time to start up, so appeared in the running apps list, but no screenshot for it existed?? So the delegate was too narrow?

Revision history for this message
Gerry Boland (gerboland) wrote :

10. Starting Gallery app does not make panel hide (which it should)

11. Quick right edge swipe can be a little flickery. I'd forgive this however, I know what's going on.

12. With multiple apps open, bring up the OSK somehow. Then do right edge swipe to show spread. OSK stuck open

Revision history for this message
Gerry Boland (gerboland) wrote :

13. seems running apps icons are not all the same width: http://imgur.com/altNLT7 The second row doesn't match the first, column-wise. Note that not all apps are fullscreen any more.

Revision history for this message
Gerry Boland (gerboland) wrote :

Question: in the spread, the edges of the transformed screenshots are not antialiased. Turning on smoothing bad?

Revision history for this message
Gerry Boland (gerboland) wrote :

14. this is a tricky one to reproduce: open like 8+ apps. Right edge to open spread. Flick list to the left so you see top-most screenshot. Now flick it again, and while it is bouncing, do a right-edge swipe. When I do this, I switch to the second last app in the list (like the quick right-edge Alt-Tab swipe)

Revision history for this message
Gerry Boland (gerboland) wrote :

nitpick: with 8+ apps open, when you flick spread to the the left, so you see top-most screenshot, the framerate drops a bit. Optimisation can be left for later naturally

Revision history for this message
Gerry Boland (gerboland) wrote :

15. with spread open (3+ apps running), a long left swipe causes launcher to open, then close again. Would be better if it stayed open IMO. Short left swipe open it fine

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

> > Please mention in the commit message that this branch marks the end of
> > SurfaceFlinger support, so unity8 is not Mir only.
> I meant: *now* Mir only
> /hittip tsdgeos

done

lp:~mzanetti/unity8/right-edge-2 updated
672. By Michael Zanetti

add some comments to EasingCurve

673. By Michael Zanetti

merge trunk

674. By Michael Zanetti

get rid of warning in case there is no fullscreen app

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

> In PhoneStage.qml:
>
> """
> + property bool fullscreen: ApplicationManager.findApplication(ApplicationMana
> ger.focusedApplicationId).fullscreen
> """
>
> That generates errors, polluting the console, when there's no focused
> application. Having it the following way is more concise and solves this
> issue:
>
> property bool fullscreen: priv.focusedApplication ?
> priv.focusedApplication.fullscreen : false

done

lp:~mzanetti/unity8/right-edge-2 updated
675. By Michael Zanetti

get rid of another wanring that may happen in some certain circumstances

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

> Likewise here:
>
> """
> PropertyAction { target: fadeInScreenshotImage; property: "source";
> value: ApplicationManager.findApplication(priv.newFocusedAppId).screenshot }
> """

done

lp:~mzanetti/unity8/right-edge-2 updated
676. By Michael Zanetti

make it initialize correctly

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

> I'm getting the following error on start up:
>
> qml/Stages/PhoneStage.qml:356: ReferenceError: shift is not defined
>
> Which is solved by:
>
> """
> SequentialAnimation {
> id: snapAnimation
> - property int targetContentX: -shift
> + property int targetContentX: -spreadView.shift
>
> UbuntuNumberAnimation {
> target: spreadView
> """

Dammit. I fixed this already and forgot to push it to this branch... Too many branches floating around right now. Thanks for pointing out!

fixed.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mzanetti/unity8/right-edge-2 updated
677. By Michael Zanetti

activateApplication -> requestFocusApplication as per unity-api definition

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

> Functional bugs (phone testing only)
> 1. I launch Contacts app from dash. Wait for it to appear. Left edge swipe it
> away. I'm not able to bring back Contacts by tapping its preview, nor its dahs
> or launcher icon.
>
> 2. I launch Contacts app from dash. Wait for it to appear. Left edge swipe it
> away. I then launch Dialer app from dash, I don't get a starting animation:
> instead Contacts app immediately shown, and eventually Dialer pops up on top

Both were caused because unity8 still called activateApplication() which was changed to reqeastFocusApplication() in the other branches by now.

Both fixed.

lp:~mzanetti/unity8/right-edge-2 updated
678. By Michael Zanetti

reenable workaround timer

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

> > Functional bugs (phone testing only)
> > 1. I launch Contacts app from dash. Wait for it to appear. Left edge swipe
> it
> > away. I'm not able to bring back Contacts by tapping its preview, nor its
> dahs
> > or launcher icon.
> >
> > 2. I launch Contacts app from dash. Wait for it to appear. Left edge swipe
> it
> > away. I then launch Dialer app from dash, I don't get a starting animation:
> > instead Contacts app immediately shown, and eventually Dialer pops up on top
>
> Both were caused because unity8 still called activateApplication() which was
> changed to reqeastFocusApplication() in the other branches by now.
>
> Both fixed.

Sorry #2 was something else. I disabled one of the screenshotting workarounds in order to be faster writing the tests and forgot to reenable it back. Now both fixed for real.

lp:~mzanetti/unity8/right-edge-2 updated
679. By Michael Zanetti

remove unused leftover property

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

> + readonly property bool applicationRunning:
> ApplicationManager.focusedApplicationId.length > 0
> I don't see this used anywhere actually.

correct. removed it

lp:~mzanetti/unity8/right-edge-2 updated
680. By Michael Zanetti

don't disable the spread for 2 running apps

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mzanetti/unity8/right-edge-2 updated
681. By Michael Zanetti

resume app immediately when the user starts moving the greeter, otherwise it won't paint anything

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

> 3. With 2 apps open, doing a full right edge swipe, the animation is linked to
> my finger distance from the right edge, until about 70% of the way, when it
> just stops. Then only finger release completes the animation. It feels like
> the animation locks up. Did Design want this?

Turns out the spread shouldn't be disabled at all if there are only 2 apps. => fixed.

>
> 4. Have app open and active. Press power to turn off display, and again to
> show greeter. Do a slow right-edge swipe - a black rectangle is revealed. Only
> on finger release does the app appear.

Fixed. good catch. The app was still suspended by ApplicationManager.suspended. I'm waking things now as soon as the greeter is moved, not only when we commit to unlock it.

>
> 5. I've 2 apps open. I open a third from the dash. Once it has appeared
> (giving it a second more just in case), I start a right edge gesture. I only
> see 2 apps in the spread, the latest one has disappeared

Caused by a dumb mistake in the latest unity-mir change. fixed, updated pacakges will show up on chinstrap soon.

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

> 13. seems running apps icons are not all the same width:
> http://imgur.com/altNLT7 The second row doesn't match the first, column-wise.
> Note that not all apps are fullscreen any more.

Hmm... True. That's because they keep their aspect ratio now. I could wrap then in an Item to keep them aligned but that would look quite bad in the tablet scenario where portrait and landscape apps mixed in this view. Not really sure how to deal with this tbh. Any suggestions?

lp:~mzanetti/unity8/right-edge-2 updated
682. By Michael Zanetti

make the topMargin follow the same easing curve as the rest

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

> Sorry about the hopeless picture, but this was something minor I noticed:
> http://imgur.com/O6EFlQh
> I opened 3 apps, dialer, contacts & system settings. With the order in the
> spread of contacts, settings, dialer, I grabbed the dialer screenshot and
> brought it as far left as I could. I noticed that a bit of the system settings
> image could be made visible over the dialer. See the image above, I marked it
> in red

fixed.

lp:~mzanetti/unity8/right-edge-2 updated
683. By Michael Zanetti

fix home button not working while in spread mode

684. By Michael Zanetti

drop print

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mzanetti/unity8/right-edge-2 updated
685. By Michael Zanetti

fix tiny animation mismatch after adjusting the topMargin scaling to do an easingcurve instead of being linear

686. By Michael Zanetti

some fixes if apps are activated from outside the spread while we're in spread mode

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

> 6. With spread open, opening launcher and tapping dash icon does nothing

fixed.

>
> 7. Have a couple of apps open, and engage the spread. Now open the (as
> example) sound indicator, and tap the "Sound settings" entry. Indicators
> close, but spread remains. Tapping the system settings does bring me to the
> correct screen.

Should be fixed now.

>
> 8. Strangely, if I open System Settings app via indicators (same steps as in 7
> above), it often opens with incorrect x position of the surface (i.e there's a
> 2-3GU gap on the left). Need to check with trunk.

hmm... quite unlikely this has anything to do with this branches. the misplaced surface is the real app surface whereas this branch only deals with screenshots which are placed correctly.

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

> 9. Have system settings & dialer app open, dialer app to the front. Open
> indicators, select Sound and tap "Sound settings". You see the dialer app
> slide to the right, but a black rectangle remains, until suddenly system
> settings appears

This is the default app starting white rectangle which will eventually be replaced by a splash screen I believe, but a bit out of scope of this branch imo.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mzanetti/unity8/right-edge-2 updated
687. By Michael Zanetti

fix panel not always hiding when it should

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

The problem handles by spreadDragArea's code (collecting gesturePoints, etc) looks remarkably like what EdgeDragEvaluator does. Have you considered using it before you went with this implementation?

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

> The problem handles by spreadDragArea's code (collecting gesturePoints, etc)
> looks remarkably like what EdgeDragEvaluator does. Have you considered using
> it before you went with this implementation?

Look how DragHandle uses it.

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

> 10. Starting Gallery app does not make panel hide (which it should)

Fixed.

>
> 11. Quick right edge swipe can be a little flickery. I'd forgive this however,
> I know what's going on.

Hmm... seems to work perfectly fine here... But I guess if you flick really quick and the device is busy otherwise you might manage to see some screenshot updating action.

>
> 12. With multiple apps open, bring up the OSK somehow. Then do right edge
> swipe to show spread. OSK stuck open

Yeah... that's a bit of an issue. Right now there's no way to hide the OSK except unfocing the app. As that causes the whole state machine to update it causes effects like the panel coming in for half a second (you you transition from fullscreen to fullscreen) and some more such issues. Changing the code to deal with that would be quite intrusive and afaik the plan is to make the OSK part of the app surface anyways. Can we ignore this for this merge?

lp:~mzanetti/unity8/right-edge-2 updated
688. By Michael Zanetti

enable antialiasing on spread tiles

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

> Question: in the spread, the edges of the transformed screenshots are not
> antialiased. Turning on smoothing bad?

I've enabled antialiasing on the tiles now. Doesn't seem to badly impact performance. Thanks for the hint. Didn't even know that property existed since QtQuick 2.0

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mzanetti/unity8/right-edge-2 updated
689. By Michael Zanetti

Disabling DDA when Spread is in stage 2, now that DDA can handle being disabled while being dragged

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

> 14. this is a tricky one to reproduce: open like 8+ apps. Right edge to open
> spread. Flick list to the left so you see top-most screenshot. Now flick it
> again, and while it is bouncing, do a right-edge swipe. When I do this, I
> switch to the second last app in the list (like the quick right-edge Alt-Tab
> swipe)

Ok, now that the DirectionalDragArea can handle being disabled while being used, I disabled it when entering stage 2. That prevents this from happening, however there's still a possibility to mess it a little up. If you quickly put down the Finger into the "empty" area while the Flickable overshoots and keep the finger there, the Flickable seems to get a mouseMove even, but releasing the finger now does not produce the mouseReleased event. If you now touch the flickable somewhere, it counts the distance as a movement and you can make it jump a bit before it follows the finger again. I'll try to improve it further but it doesn't seem as bad any more as before.

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

> nitpick: with 8+ apps open, when you flick spread to the the left, so you see
> top-most screenshot, the framerate drops a bit. Optimisation can be left for
> later naturally

The thing is, if you open 8+ apps, the overall framerate drops. Just opening 8+ apps without even using the spread (or even in trunk) makes all animations (Launcher, Dash etc) become sluggish. AlbertA and Anpok have improved this a lot already (used to happen with 4+ apps a month ago) and I believe there's still some improvements upcoming (e.g. opaque surfaces).

While there might well be room for improvements in the Spread, I think this particular issue is rather Mir related.

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

> > http://imgur.com/gz5p3Gg I managed to get somehow, not sure how
> Ah I had started Gmail app. Seems it took an ultra long time to start up, so
> appeared in the running apps list, but no screenshot for it existed?? So the
> delegate was too narrow?

Yeah... There is still a little issue if you minimize an app before it paints on the screen for the first time. In that case we can't grab a screenshot. Current trunk has empty tiles in here which I guess is a bit better than this. Again, the fact that we're keeping the aspect ration here doesn't ease things up. But I can try to find a workaround for the time being (until we get rid of the screenshotting stuff) if you want me to.

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

> > The problem handles by spreadDragArea's code (collecting gesturePoints, etc)
> > looks remarkably like what EdgeDragEvaluator does. Have you considered using
> > it before you went with this implementation?
>
> Look how DragHandle uses it.

As discussed on IRC, design wants this to complete the gesture if the user does a one-way flick but they explicitly said that the gesture speed should not have any impact on it. I'm struggling a bit to make the EdgeDragEvaluator do exactly that, so we agreed to keep it as is.

However, I'll bring up the topic of consistent edge gestures behavior (in terms of speed, distance and direction) in the next design meeting.

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

Doesn't merge with current unity8 trunk
Text conflict in plugins/Utils/plugin.cpp
Text conflict in qml/Shell.qml
2 conflicts encountered.

review: Needs Fixing
lp:~mzanetti/unity8/right-edge-2 updated
690. By Michael Zanetti

merge trunk

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

> Doesn't merge with current unity8 trunk
> Text conflict in plugins/Utils/plugin.cpp
> Text conflict in qml/Shell.qml
> 2 conflicts encountered.

fixed

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote :

+ function hide() {
+ if (locked) {
+ return;
+ }
+ forceHide();
+ }
Why does hide not do anything if the spreadView is visible? Well more like, it sounds like an error that hide is called when spreadView is visible.

+ blockInput: ApplicationManager.focusedApplicationId.length === 0
Nitpick: checking length of the appId to be zero to prove there's no focused app? Not wrong, just reads a bit funny. Maybe String::isEmpty() works in QML, never tried it.

+ property string focusedAppId: ApplicationManager.focusedApplicationId
+ property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
Just checking: the extra focusedAppId property needed to properly update the focusedApplication property? (you do this in a few places, so just curious why)

+++ qml/Stages/PhoneStage.qml
+ onMovingChanged: {
...
+ } else {
+ mainScreenshotImage.visible = false;
Maybe setting mainScreenshotImage.src = "" might free a little memory when spread not open? Probably more GPU memory than system memory. It might have cost too though, so just an idea

+ Image {
+ id: mainScreenshotImage
+ property string src
+ source: src
why not just use source everywhere?

Revision history for this message
Gerry Boland (gerboland) wrote :

General JS comment: sometimes you use semicolons, other times you don't. Think it better to just use them.

Revision history for this message
Gerry Boland (gerboland) wrote :

+++ qml/Stages/PhoneStage.qml
+ onFocusedApplicationIdChanged: {
+ var application = ApplicationManager.findApplication(ApplicationManager.focusedApplicationId)
Might as well use priv.focusedApplication, no?

+ id: priv
+ property string focusedAppId: ApplicationManager.focusedApplicationId
+ property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
+ property url focusedScreenshot: focusedApplication ? focusedApplication.screenshot : ""
Times like these I wish QML properties were readonly by default, and you had to use a specifier to specify otherwise

+ onFocusedScreenshotChanged: {
+ mainScreenshotImage.src = ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).screenshot;
mainScreenshotImage.src = focusedApplication.screenshot would do, no?

+ function requestNewScreenshot() {
+ ApplicationManager.updateScreenshot(ApplicationManager.focusedApplicationId);
ApplicationManager.updateScreenshot(focusedAppId)

+ function indexOf(appId) {
I see why you need it, but not a giant fan of scanning the whole model for an index, just to reposition the spreadView. Not that I have a better idea however!

+ // FIXME: the signal connections seems to get lost.
+ // Check with Qt 5.2, see if we can remove this Connections and Binding objects
5.2 landed, fancy giving this a look?

+ // PropertyAction seems to fail when secondApplicationStarting and we didn't have another screenshot before
+ ScriptAction { script: mainScreenshotImage.src = priv.focusedScreenshot }
+ spreadView.snapTo(0)
Weird, I don't see why a PropertyAction would be incorrect here. Did you try setting a binding instead of a value, just as an experiment?

+ PropertyAction { target: fadeInScreenshotImage; property: "source"; value: ApplicationManager.findApplication(priv.newFocusedAppId) ? ApplicationManager.findApplication(priv.newFocusedAppId).screenshot : "" }
pity you need to search twice here, could you not store the value temporarily?

+ PropertyAction { target: fadeInScreenshotImage; property: "visible"; value: false }
+ PropertyAction { target: mainScreenshotImage; property: "visible"; value: false }
Fun fact: can replace this with
  PropertyAction { targets: [fadeInScreenshotImage, mainScreenshotImage]; property: "visible"; value: false }
not a demand though, both ways are fine.

+ UbuntuNumberAnimation { target: fadeInScreenshotImage; property: "opacity"; to: 1; }
+ UbuntuNumberAnimation { target: fadeInScreenshotImage; property: "scale"; to: 1; }
similarly, this can be
  UbuntuNumberAnimation { target: fadeInScreenshotImage; properties: ["opacity", "scale"]; to: 1; }

Revision history for this message
Gerry Boland (gerboland) wrote :

Questions: there are 2 appSplash instances? Is that if user starts 2 apps quickly?

+ property bool attachedToView: true
a comment explaining this would be nice. In fact, the logic in the EdgeDragArea could do with the same.

+ if (oneWayFlick && spreadView.shiftedContentX > units.gu(2) && spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
please wrap that line a bit

+ property real positionMarker3: 0.6
+ property real positionMarker4: .9
Consistency please

+ // Stage of the animation:
+ property int stage: not a fan of the term, as "Stage" has a particular meaning for us. Would the Flickable's "States" system be useful here, as instead of numbers, can give the animation stages names?

+ onShiftedContentXChanged: {
+ case 1:
/me pedant, but adding "break" here too might spare a future coder some pain

+// duration: UbuntuAnimation.SleepyDuration
remove?

+ spreadView.stage = 4;
leftover?

+ if (spreadView.shiftedContentX == spreadView.width * spreadView.positionMarker2) {
what's going on here? floating point equality comparison makes me suspicious

+ id: spreadRow
+ width: Math.max(3, ApplicationManager.count) * spreadView.tileDistance + (spreadView.width - spreadView.tileDistance) * 1.5
fancy maths could do with a comment explaining it :)

+++ qml/Stages/SpreadDelegate.qml
+ anchors { left: parent.left; bottom: parent.bottom; top: parent.top; topMargin: priv.heightDifference * Math.max(0, 1 - root.topMarginProgress) }
please wrap so topMargin is on its own line, as that's the nontrivial bit.

+ scale: 1
unnecessary, that's the default value.

Revision history for this message
Gerry Boland (gerboland) wrote :

+++ qml/Stages/StageWithSideStage.qml
How closely would you like me to review this? Since there's plenty of commented out lines and stuff, would you prefer a functional & code-sanity review only?

review: Needs Information
lp:~mzanetti/unity8/right-edge-2 updated
691. By Michael Zanetti

add comment and link to bug report

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

> + function hide() {
> + if (locked) {
> + return;
> + }
> + forceHide();
> + }
> Why does hide not do anything if the spreadView is visible? Well more like, it
> sounds like an error that hide is called when spreadView is visible.

It's needed to be able to press the home button in the launcher and return to the dash even while the spread is visible. Usually something calling hide() in the shell should not be able to hide the spread with a few exceptions. The user pressing the home button in the launcher is one of them.

>
> + blockInput: ApplicationManager.focusedApplicationId.length === 0
> Nitpick: checking length of the appId to be zero to prove there's no focused
> app? Not wrong, just reads a bit funny. Maybe String::isEmpty() works in QML,
> never tried it.

No, isEmpty() doesn't work as it's not really a QString (QString is not a QObject). It's the JavaScript string class.

>
> + property string focusedAppId: ApplicationManager.focusedApplicationId
> + property var focusedApplication:
> ApplicationManager.findApplication(focusedAppId)
> Just checking: the extra focusedAppId property needed to properly update the
> focusedApplication property? (you do this in a few places, so just curious
> why)

No... One is not to properly update the other, but rather to have everything at hands in the surrounding code without having to call functions in ApplicationManager each time and reduce length of the code.

>
> +++ qml/Stages/PhoneStage.qml
> + onMovingChanged: {
> ...
> + } else {
> + mainScreenshotImage.visible = false;
> Maybe setting mainScreenshotImage.src = "" might free a little memory when
> spread not open? Probably more GPU memory than system memory. It might have
> cost too though, so just an idea
>

That causes an issue: When you swipe the stage away with a left edge swipe and then pull it back in, this is the screenshot that is visible. If you think it's worth reworking this to unset and re-set the image for that case I'll do, but I'm not sure we should "polish" the screenshotting stuff that's supposed to go away.

> + Image {
> + id: mainScreenshotImage
> + property string src
> + source: src
> why not just use source everywhere?

There is a bug in our imageprovider. changing source does not emit sourceChanged.
https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1194778
I've added a FIXME comment with the link to the bug report.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mzanetti/unity8/right-edge-2 updated
692. By Michael Zanetti

added missing ; at imperative line endings

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

> General JS comment: sometimes you use semicolons, other times you don't. Think
> it better to just use them.

ack. added them.

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

> +++ qml/Stages/StageWithSideStage.qml
> How closely would you like me to review this? Since there's plenty of
> commented out lines and stuff, would you prefer a functional & code-sanity
> review only?

Yeah, this is really the part that's subject to be reworked when the right edge animation for the tablet comes in. So all I'd ask you to do here is to check out if its "good enough" to not totally break the tablet experience. It definitely has issues.

Revision history for this message
Gerry Boland (gerboland) wrote :

+++ qml/Stages/SwitchingApplicationImage.qml
+ onApplicationChanged: print("setting app to", application.appId, application.screenshot)
debug info necessary?

+ signal switched()
I prefer roughly sticking to the guidelines (http://qt-project.org/doc/qt-4.8/qml-coding-conventions.html) and declaring signals after properties, but before javascript function definitions

+ Image {
+ visible: true
should not be necessary

You have a couple of commented out lines in this file too, please remove them.

Revision history for this message
Gerry Boland (gerboland) wrote :

+++ qml/Stages/TransformedSpreadDelegate.qml

Since this relies heavily on the animation "stages" reported by the spreadView, a comment explaining the different behaviours of the tile in each stage (mentioning the position markers) would help a lot.

+ property real animatedProgress: 0
purpose not obvious to me how it's different to "progress", add comment please

+ property real tile1StartScale: startScale + .4
+ property real tile0SnapAngle: 10
could be made readonly, or private actually.

+ priv.stage2startTranslate = priv.easingAnimation(0, spreadView.positionMarker4, 0, -spreadView.width, spreadView.positionMarker4) + spreadView.width;
confuses me, why the -spreadView.width offset for the animation?

+ // As they are static values, lets caluclate
typo

+ property real xTranslate: {
in many of the calculations in this block, there's a "-spreadView.width" being used in the calculation of the start or end value in a linearAnimation. Could you add a note justifying it?

+ return linearAnimation(selectedProgress, negativeProgress, selectedXTranslate, selectedXTranslate - spreadView.tileDistance, root.progress);
please wrap

+ } else if(spreadView.stage == 1) {
space needed after "if"

Revision history for this message
Gerry Boland (gerboland) wrote :

+++ tests/mocks/Unity/Application/ApplicationInfo.cpp
- ,m_state(Starting)
+ ,m_state(Running)
looks wrong to me, why would an app start immediately in a "running" state. Real AppMan does not do that.

+++ tests/mocks/Unity/Application/ApplicationInfo.h
+#include <QDebug>
not needed

+++ tests/mocks/Unity/Application/ApplicationManager.cpp
do we need the qDebugs?

Revision history for this message
Gerry Boland (gerboland) wrote :

+++ tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp

+ * Copyright (C) 2013 Canonical, Ltd.
2014

+ if (app == NULL) {
nullptr

+ QGuiApplication *unity = qobject_cast<QGuiApplication*>(qApp);
can go inside the else block where it's actually used. Quick comment explaining what's going on would be handy too for the causal reader (you're scaling the image the the width of the window for all main stage apps).

qDebug needed?

Revision history for this message
Gerry Boland (gerboland) wrote :

+++ tests/mocks/Unity/Application/ApplicationScreenshotProvider.h
+ * Copyright (C) 2013 Canonical, Ltd.
2014

lp:~mzanetti/unity8/right-edge-2 updated
693. By Michael Zanetti

fix nasty issue that appeard with Qt 5.2 (was bad code already, just happened to work for 5.0)

694. By Michael Zanetti

use existing property instead of doing the function call again

695. By Michael Zanetti

make some props readonly

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

> +++ qml/Stages/PhoneStage.qml
> + onFocusedApplicationIdChanged: {
> + var application =
> ApplicationManager.findApplication(ApplicationManager.focusedApplicationId)
> Might as well use priv.focusedApplication, no?

yes. fixed.

>
> + id: priv
> + property string focusedAppId: ApplicationManager.focusedApplicationId
> + property var focusedApplication:
> ApplicationManager.findApplication(focusedAppId)
> + property url focusedScreenshot: focusedApplication ?
> focusedApplication.screenshot : ""
> Times like these I wish QML properties were readonly by default, and you had
> to use a specifier to specify otherwise

Hmm... due to a workaround for something I believe is a bug in Qt/QML I need to write them. Besides its not public API so I don't think its much of an issue.

>
> + onFocusedScreenshotChanged: {
> + mainScreenshotImage.src = ApplicationManager.findApplication(ApplicationMana
> ger.focusedApplicationId).screenshot;
> mainScreenshotImage.src = focusedApplication.screenshot would do, no?

yep. fixed.

>
> + function requestNewScreenshot() {
> +
> ApplicationManager.updateScreenshot(ApplicationManager.focusedApplicationId);
> ApplicationManager.updateScreenshot(focusedAppId)

fixed

> + function indexOf(appId) {
> I see why you need it, but not a giant fan of scanning the whole model for an
> index, just to reposition the spreadView. Not that I have a better idea
> however!

Hmm... This is only used in the corner case that the user launches an app using the launcher while in spread view. So not really critical performance wise. I can see why you dislike it, but as you said: not really a better idea around.

>
>
> + // FIXME: the signal connections seems to get lost.
> + // Check with Qt 5.2, see if we can remove this Connections and Binding
> objects
> 5.2 landed, fancy giving this a look?

Yep, checked. still an issue. I removed the "check with 5.2 phrase".

>
> + // PropertyAction seems to fail when secondApplicationStarting and we didn't
> have another screenshot before
> + ScriptAction { script: mainScreenshotImage.src = priv.focusedScreenshot }
> + spreadView.snapTo(0)
> Weird, I don't see why a PropertyAction would be incorrect here. Did you try
> setting a binding instead of a value, just as an experiment?

Oh yeah... this one gave me quite a headache to figure why the heck it was failing. Tried the binding as you proposed too. Doesn't work. I've added a FIXME to the comment.

>
> + PropertyAction { target: fadeInScreenshotImage; property: "source"; value:
> ApplicationManager.findApplication(priv.newFocusedAppId) ?
> ApplicationManager.findApplication(priv.newFocusedAppId).screenshot : "" }
> pity you need to search twice here, could you not store the value temporarily?

done.

>
> + PropertyAction { target: fadeInScreenshotImage; property: "visible"; value:
> false }
> + PropertyAction { target: mainScreenshotImage; property: "visible"; value:
> false }
> Fun fact: can replace this with
> PropertyAction { targets: [fadeInScreenshotImage, mainScreenshotImage];
> property: "visible"; value: false }
> not a demand though, both ways are fine.

nice one. didn't know this. Love it!

>
> + UbuntuNumberAnimation { target:...

Read more...

lp:~mzanetti/unity8/right-edge-2 updated
696. By Michael Zanetti

fix stuff from review

697. By Michael Zanetti

add some comments

698. By Michael Zanetti

rename stage to phase to avoid the obvious naming collision

699. By Michael Zanetti

add a break for future proveness

700. By Michael Zanetti

remove leftover

701. By Michael Zanetti

seems there's more leftover :D

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

> Questions: there are 2 appSplash instances? Is that if user starts 2 apps
> quickly?

Yeah, it mostly catches the case that the screenshot is not available for some reason, for example the one you mentioned.

>
>
> + property bool attachedToView: true
> a comment explaining this would be nice. In fact, the logic in the
> EdgeDragArea could do with the same.

very valid. added.

>
> + if (oneWayFlick && spreadView.shiftedContentX > units.gu(2) &&
> spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
> please wrap that line a bit

wrapped all to 120 chars now.

>
> + property real positionMarker3: 0.6
> + property real positionMarker4: .9
> Consistency please

fixed.

>
> + // Stage of the animation:
> + property int stage: not a fan of the term, as "Stage" has a particular
> meaning for us. Would the Flickable's "States" system be useful here, as
> instead of numbers, can give the animation stages names?

Don't really want to go for strings as I do <, > comparison etc. While that would still work with strings its imo much more fail prone if someone renames or adds a stage. I did, however rename it to "phase" now to avoid the naming collision.

>
> + onShiftedContentXChanged: {
> + case 1:
> /me pedant, but adding "break" here too might spare a future coder some pain

fair enough, fixed.

>
> +// duration: UbuntuAnimation.SleepyDuration
> remove?

done.

>
> + spreadView.stage = 4;
> leftover?

seems so... dropped it.
>
> + if (spreadView.shiftedContentX == spreadView.width *
> spreadView.positionMarker2) {
> what's going on here? floating point equality comparison makes me suspicious

Turns out its not needed any more at all as I later changed the logic to always hit the branch before in this case already.

>
> + id: spreadRow
> + width: Math.max(3, ApplicationManager.count) * spreadView.tileDistance +
> (spreadView.width - spreadView.tileDistance) * 1.5
> fancy maths could do with a comment explaining it :)

done.

> +++ qml/Stages/SpreadDelegate.qml
> + anchors { left: parent.left; bottom: parent.bottom; top: parent.top;
> topMargin: priv.heightDifference * Math.max(0, 1 - root.topMarginProgress) }
> please wrap so topMargin is on its own line, as that's the nontrivial bit.

done.
>
> + scale: 1
> unnecessary, that's the default value.

done

lp:~mzanetti/unity8/right-edge-2 updated
702. By Michael Zanetti

some more commenting

703. By Michael Zanetti

drop unnecessary line

704. By Michael Zanetti

fix tests

705. By Michael Zanetti

cleanup

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

> +++ qml/Stages/SwitchingApplicationImage.qml
> + onApplicationChanged: print("setting app to", application.appId,
> application.screenshot)
> debug info necessary?
>
> + signal switched()
> I prefer roughly sticking to the guidelines (http://qt-project.org/doc/qt-4.8
> /qml-coding-conventions.html) and declaring signals after properties, but
> before javascript function definitions
>
> + Image {
> + visible: true
> should not be necessary
>
>
> You have a couple of commented out lines in this file too, please remove them.

all done. Sorry for that. Forgot to clean this one up before putting up for review.

Revision history for this message
Gerry Boland (gerboland) wrote :

<greyback> old query of mine too: with spread open, long left edge swipe has launcher appear, then disappear, and nothing else happens
<greyback> that ok?
<mzanetti> ah right... so yes, the spread should not go away, that is wanted
<mzanetti> now the question is whether the launcher should hide again (as it does normally) or not
* greyback thinks it should stay open
<mzanetti> probably, yes

Revision history for this message
Gerry Boland (gerboland) wrote :

<greyback> mind checking this: open 1 app, left swipe it away. Then tap it's entry in running apps. The slide in animation seems wrong
<mzanetti> mhm... true... wonder when that happened. will fix

lp:~mzanetti/unity8/right-edge-2 updated
706. By Michael Zanetti

more docs

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

> +++ qml/Stages/TransformedSpreadDelegate.qml
>
> Since this relies heavily on the animation "stages" reported by the
> spreadView, a comment explaining the different behaviours of the tile in each
> stage (mentioning the position markers) would help a lot.

done

>
> + property real animatedProgress: 0
> purpose not obvious to me how it's different to "progress", add comment please

done.

>
> + property real tile1StartScale: startScale + .4
> + property real tile0SnapAngle: 10
> could be made readonly, or private actually.

actually intentional in public api as I hope to be able to reuse the TransformedAppDelegate for the tablet which has a less 3D look and feel because of the bigger screen space.

>
> + priv.stage2startTranslate = priv.easingAnimation(0,
> spreadView.positionMarker4, 0, -spreadView.width, spreadView.positionMarker4)
> + spreadView.width;
> confuses me, why the -spreadView.width offset for the animation?

tiles are attached to the right edge (outside) in the beginning and travel a distance of -spreadView.width during the progress of 0..1. So -spreadView.width is the end position. Should be more clear now that I added tons of comments all over the place.

>
> + // As they are static values, lets caluclate
> typo

fixed.

>
> + property real xTranslate: {
> in many of the calculations in this block, there's a "-spreadView.width" being
> used in the calculation of the start or end value in a linearAnimation. Could
> you add a note justifying it?

as explained above.

>
> + return linearAnimation(selectedProgress, negativeProgress,
> selectedXTranslate, selectedXTranslate - spreadView.tileDistance,
> root.progress);
> please wrap

done

>
> + } else if(spreadView.stage == 1) {
> space needed after "if"

done

lp:~mzanetti/unity8/right-edge-2 updated
707. By Michael Zanetti

some more minor fixes

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mzanetti/unity8/right-edge-2 updated
708. By Michael Zanetti

drop some debug prints from mocked applicationmanager

709. By Michael Zanetti

only change app's status to running after some timeout

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

> +++ tests/mocks/Unity/Application/ApplicationInfo.cpp
> - ,m_state(Starting)
> + ,m_state(Running)
> looks wrong to me, why would an app start immediately in a "running" state.
> Real AppMan does not do that.

Ok. changed to start 'em up "Starting" and added a timer that switches them to "Running" after 300ms.

>
> +++ tests/mocks/Unity/Application/ApplicationInfo.h
> +#include <QDebug>
> not needed

gone

>
> +++ tests/mocks/Unity/Application/ApplicationManager.cpp
> do we need the qDebugs?

gone

lp:~mzanetti/unity8/right-edge-2 updated
710. By Michael Zanetti

tiny cleanup as requested by review

711. By Michael Zanetti

update year in copyright header

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

> +++ tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp
>
> + * Copyright (C) 2013 Canonical, Ltd.
> 2014

fixed.

>
> + if (app == NULL) {
> nullptr

fixed.

>
> + QGuiApplication *unity = qobject_cast<QGuiApplication*>(qApp);
> can go inside the else block where it's actually used. Quick comment
> explaining what's going on would be handy too for the causal reader (you're
> scaling the image the the width of the window for all main stage apps).

fixed.

>
> qDebug needed?

removed most debugs. changed one to be a qWarning in case we can't find an image for the given app.

lp:~mzanetti/unity8/right-edge-2 updated
712. By Michael Zanetti

don't hide the launcher when the stages are locked and user does the long left edge swipe

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

> <greyback> old query of mine too: with spread open, long left edge swipe has
> launcher appear, then disappear, and nothing else happens
> <greyback> that ok?
> <mzanetti> ah right... so yes, the spread should not go away, that is wanted
> <mzanetti> now the question is whether the launcher should hide again (as it
> does normally) or not
> * greyback thinks it should stay open
> <mzanetti> probably, yes

fixed. actually it even got me around the hide() vs forceHide() you disliked in an earlier comment

lp:~mzanetti/unity8/right-edge-2 updated
713. By Michael Zanetti

fix animation issue with focusing a running app when there's no focused app

714. By Michael Zanetti

get rid of src -> source mapping for mainScreenshotImage and with that the fixme for the PropertyAction not working

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote :

Tablet testing issues [MS=main stage, SS=side stage]
1) opening just 1 SS app, unable to swipe it away with SS handle
2) open some apps, then close them all, "Running apps" section of apps lens does not disappear like it used to
3) with 1 SS app open, unable to interact with Dash (missing InputFilterArea perhaps)
4) with only SS app open, open MS app (via launcher) - MS starting animation incorrect
5) with only SS app open, open MS app (via launcher) - SS dismissed
6) dismissed SS keeps a shadow on the right of the screen
7) 1 MS & 1 SS app open. Return to dash. Tap SS recent app - only SS app appears. Used to be that both MS & SS app appeared.
8) App lens - MS app preview much wider than it used to be, it used to be intentionally cropped
9) not sure why, but SS app preview image in the apps lens sometimes incorrect, sometimes stretched, sometimes offset wrong and being tiled inside the preview, and sometimes the wrong image entirely. Possibly a Nexus10 bug, just strange as the window animation screenshots seem to always look fine
10) 2 MS apps open: Clock & Browser, Browser focused. Return to dash and tap the Clock entry. The animation to bring Clock to the front isn't right, looks like mix of spread animation with usual slide in animation.
11) no right edge gesture at all now (for switching apps, think it was SS app switching)?

review: Needs Fixing (functional: tablet)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote :

Phone functional testing pass 2 issues
1) Open 2 apps, return to dash. Tap preview on one app - the "bring to front" animation looks wrong
2) open 2 apps, go to spread. Open launcher and tap icon of focused app - nothing happens. Tapping icon of other app focuses it.
3)

>
> 12. With multiple apps open, bring up the OSK somehow. Then do right edge
> swipe to show spread. OSK stuck open

Yeah... that's a bit of an issue. Right now there's no way to hide the OSK except unfocing the app. ... Can we ignore this for this merge?
Ok, I can live with that.

review: Needs Fixing (functional: phone)
lp:~mzanetti/unity8/right-edge-2 updated
715. By Michael Zanetti

add possibility to select an app from the spread from the outside.

716. By Michael Zanetti

add tests for the new select() function

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

> <greyback> mind checking this: open 1 app, left swipe it away. Then tap
> it's entry in running apps. The slide in animation seems wrong
> <mzanetti> mhm... true... wonder when that happened. will fix

fixed.

> + Image {
> + id: mainScreenshotImage
> + property string src
> + source: src
> why not just use source everywhere?

along the way I got rid of this too, which also get away with this

+ // PropertyAction seems to fail when secondApplicationStarting and we didn't have another screenshot before

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

> Phone functional testing pass 2 issues
> 1) Open 2 apps, return to dash. Tap preview on one app - the "bring to front"
> animation looks wrong

fixed

> 2) open 2 apps, go to spread. Open launcher and tap icon of focused app -
> nothing happens. Tapping icon of other app focuses it.
> 3)

fixed.

lp:~mzanetti/unity8/right-edge-2 updated
717. By Michael Zanetti

drop ApplicationManagerWrapper & Co

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mzanetti/unity8/right-edge-2 updated
718. By Michael Zanetti

fix runningapplicationsgrid tile width,
fix dash not intaractive when side stage overlaying

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mzanetti/unity8/right-edge-2 updated
719. By Michael Zanetti

fix hiding of recent apps section when there's not app running

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mzanetti/unity8/right-edge-2 updated
720. By Michael Zanetti

hide stage if no focused app is around any more

721. By Michael Zanetti

don't dismiss the SS if the user starts a MS app

722. By Michael Zanetti

hide the SS drag handle when SS is outside of the screen

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

> Tablet testing issues [MS=main stage, SS=side stage]
> 1) opening just 1 SS app, unable to swipe it away with SS handle

fixed.

> 2) open some apps, then close them all, "Running apps" section of apps lens
> does not disappear like it used to

fixed.

> 3) with 1 SS app open, unable to interact with Dash (missing InputFilterArea
> perhaps)

fixed.

> 4) with only SS app open, open MS app (via launcher) - MS starting animation
> incorrect

Hmm... I did change something in this regard but not sure if this is h

> 5) with only SS app open, open MS app (via launcher) - SS dismissed

Uhm... ok. reverted the "fix" :)

> 6) dismissed SS keeps a shadow on the right of the screen

fixed.

> 7) 1 MS & 1 SS app open. Return to dash. Tap SS recent app - only SS app
> appears. Used to be that both MS & SS app appeared.

Yeah, but this time I really believe trunk's behavior is the wrong one :D

> 8) App lens - MS app preview much wider than it used to be, it used to be
> intentionally cropped

fixed.

> 9) not sure why, but SS app preview image in the apps lens sometimes
> incorrect, sometimes stretched, sometimes offset wrong and being tiled inside
> the preview, and sometimes the wrong image entirely. Possibly a Nexus10 bug,
> just strange as the window animation screenshots seem to always look fine

uhm... happens very rarely - I've seen it too today. Can we let this go and focus on eliminating the screenshots?

> 10) 2 MS apps open: Clock & Browser, Browser focused. Return to dash and tap
> the Clock entry. The animation to bring Clock to the front isn't right, looks
> like mix of spread animation with usual slide in animation.

Hmm.. not sure if I fixed this with other changed, but looks correct here.

> 11) no right edge gesture at all now (for switching apps, think it was SS app
> switching)?

I hoped to get a bypass ticket on that one instead of juggling screenshots around just to remove them with my next task again. But if it turns out this is too much breakage, I'll obviously do my best to fix this too.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mzanetti/unity8/right-edge-2 updated
723. By Michael Zanetti

added a test to check for correct fullscreen behavior

724. By Michael Zanetti

improve test a bit

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mzanetti/unity8/right-edge-2 updated
725. By Michael Zanetti

add splash screen to tablet stuff

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote :

Running AP tests, here are my results: http://studio.sketchpad.cc/Cggnfp2Eiq
Unity8 fails some tests

review: Needs Fixing
lp:~mzanetti/unity8/right-edge-2 updated
726. By Michael Zanetti

some improvements in the sidestage

727. By Michael Zanetti

bring back workaround for unlocking the greeter when an app is started "somehow"

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mzanetti/unity8/right-edge-2 updated
728. By Michael Zanetti

increase splash timer a little, add todo

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mzanetti/unity8/right-edge-2 updated
729. By Michael Zanetti

make AP work again

730. By Michael Zanetti

fix newline at EOF

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

Needs merging with trunk
  Text conflict in qml/Dash/DashApps.qml
  Contents conflict in qml/Dash/DashPreview.qml
  Text conflict in qml/Dash/GenericScopeView.qml
  Text conflict in qml/Shell.qml
  Text conflict in tests/autopilot/unity8/shell/emulators/dash.py

review: Needs Fixing
lp:~mzanetti/unity8/right-edge-2 updated
731. By Michael Zanetti

bump required unity-api version

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mzanetti/unity8/right-edge-2 updated
732. By Michael Zanetti

merge trunk

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mzanetti/unity8/right-edge-2 updated
733. By Michael Zanetti

bring back the paper background that accidentally got dropped in the last merge

734. By Michael Zanetti

fix autopilot tests after merge

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mzanetti/unity8/right-edge-2 updated
735. By Michael Zanetti

make it a bit better, still broken tho :/

736. By Michael Zanetti

more improvements, still not working

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Mirco Müller (macslow) wrote :

I ran ppa:ci-train-ppa-service/landing-015 with r261 on my N4 through https://wiki.ubuntu.com/Process/Merges/TestPlans/Unity8 and it held up pretty good. Especially the new right-edge feels robust and slick. But I came across this issue with starting the Notes app...

http://ubuntuone.com/6TmrkBPbLkqPiVdhkXalvQ
http://ubuntuone.com/1zSsYq6czLOiPI58Y6yGeL
http://ubuntuone.com/2DwRiTX61Pbmrosd0xlA3p
http://ubuntuone.com/4SKadILKJ6RqirwYqreCsn

Before that I had up to ten applications running (among them camera-app and browser) and there was no single glitch. I'm still trying to find a way to trigger this bug in a reproducable manner.

review: Needs Fixing
Revision history for this message
Mirco Müller (macslow) wrote :

Here's a screencast better showing off the "invisible" apps and the empty app-previews: https://www.youtube.com/watch?v=C66Y0vNSboI

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

> I ran ppa:ci-train-ppa-service/landing-015 with r261 on my N4 through
> https://wiki.ubuntu.com/Process/Merges/TestPlans/Unity8 and it held up pretty
> good. Especially the new right-edge feels robust and slick. But I came across
> this issue with starting the Notes app...
>
> http://ubuntuone.com/6TmrkBPbLkqPiVdhkXalvQ
> http://ubuntuone.com/1zSsYq6czLOiPI58Y6yGeL
> http://ubuntuone.com/2DwRiTX61Pbmrosd0xlA3p
> http://ubuntuone.com/4SKadILKJ6RqirwYqreCsn
>
> Before that I had up to ten applications running (among them camera-app and
> browser) and there was no single glitch. I'm still trying to find a way to
> trigger this bug in a reproducable manner.

The misplaced surface is fixed in another branch in unity-mir. I've added it to the silo. Upgrading it should get it fixed. The missing image however, happens if an app fails to start. Its the same behavior as current trunk.

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

> The missing image however,
> happens if an app fails to start. Its the same behavior as current trunk.

Some more details: This is in unity-mir and possibly even lower layers. The situation will improve once we have the live surfaces (it will fix the case where the screenshot is missing if you swipe it away before its loaded), and for apps not starting at all, we need to figure a solution in ApplicationManager. I don't adding a workaround in here is the way to go.

Revision history for this message
Mirco Müller (macslow) wrote :

After the second run through the test-plan with the reconfigure-fixes in the PPA, I'm ok with the results. All works nicely. Good to go.

review: Approve
Revision history for this message
Gerry Boland (gerboland) wrote :

Code-wise I'm happy to go too. I did a quick test of phone and it was good too. Not done a last tablet check however

review: Approve
Revision history for this message
Gerry Boland (gerboland) wrote :

 * Did you perform an exploratory manual test run of the code change and any related functionality?
Hell yeah
 * Did CI run pass? If not, please explain why.
Dependant branches, listed in description

review: Approve

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'debian/control'
--- debian/control 2014-03-24 11:49:06 +0000
+++ debian/control 2014-03-25 15:08:15 +0000
@@ -17,7 +17,7 @@
17 libnih-dev,17 libnih-dev,
18 libpulse-dev,18 libpulse-dev,
19 libqmenumodel-dev (>= 0.2.7),19 libqmenumodel-dev (>= 0.2.7),
20 libunity-api-dev (>= 7.80.4),20 libunity-api-dev (>= 7.80.6),
21 libunity-mir-dev,21 libunity-mir-dev,
22 libupstart-dev,22 libupstart-dev,
23 libusermetricsoutput1-dev,23 libusermetricsoutput1-dev,
@@ -83,6 +83,7 @@
83 qtdeclarative5-ubuntu-ui-toolkit-plugin,83 qtdeclarative5-ubuntu-ui-toolkit-plugin,
84 qtdeclarative5-unity-notifications-plugin | unity-notifications-impl,84 qtdeclarative5-unity-notifications-plugin | unity-notifications-impl,
85 qtdeclarative5-xmllistmodel-plugin,85 qtdeclarative5-xmllistmodel-plugin,
86 unity-application-impl-2,
86 unity-launcher-impl-3,87 unity-launcher-impl-3,
87 unity-notifications-impl-2,88 unity-notifications-impl-2,
88 unity8-fake-env | qtubuntu-shell,89 unity8-fake-env | qtubuntu-shell,
@@ -147,6 +148,7 @@
147 ${shlibs:Depends},148 ${shlibs:Depends},
148Provides: unity-launcher-impl,149Provides: unity-launcher-impl,
149 unity-launcher-impl-3,150 unity-launcher-impl-3,
151 unity-application-impl-2,
150Description: Unity 8 private libs152Description: Unity 8 private libs
151 The Unity 8 shell is the primary user interface for Ubuntu devices.153 The Unity 8 shell is the primary user interface for Ubuntu devices.
152 .154 .
153155
=== modified file 'debian/unity8.install'
--- debian/unity8.install 2014-03-05 12:52:57 +0000
+++ debian/unity8.install 2014-03-25 15:08:15 +0000
@@ -10,6 +10,6 @@
10usr/share/unity8/Notifications10usr/share/unity8/Notifications
11usr/share/unity8/Panel11usr/share/unity8/Panel
12usr/share/unity8/Shell.qml12usr/share/unity8/Shell.qml
13usr/share/unity8/SideStage13usr/share/unity8/Stages
14usr/share/unity8/graphics14usr/share/unity8/graphics
15data/unity8.conf usr/share/upstart/sessions/15data/unity8.conf usr/share/upstart/sessions/
1616
=== modified file 'plugins/Utils/CMakeLists.txt'
--- plugins/Utils/CMakeLists.txt 2014-03-17 15:50:50 +0000
+++ plugins/Utils/CMakeLists.txt 2014-03-25 15:08:15 +0000
@@ -18,6 +18,7 @@
18 qsortfilterproxymodelqml.cpp18 qsortfilterproxymodelqml.cpp
19 timeformatter.cpp19 timeformatter.cpp
20 unitymenumodelpaths.cpp20 unitymenumodelpaths.cpp
21 easingcurve.cpp
21 plugin.cpp22 plugin.cpp
22 )23 )
2324
@@ -33,7 +34,7 @@
33# files directly in targets.34# files directly in targets.
34set_target_properties(Utils-qml PROPERTIES COMPILE_FLAGS -fvisibility=default)35set_target_properties(Utils-qml PROPERTIES COMPILE_FLAGS -fvisibility=default)
3536
36qt5_use_modules(Utils-qml Qml Quick DBus Network XmlPatterns Concurrent)37qt5_use_modules(Utils-qml Qml Quick DBus Network XmlPatterns Gui Concurrent)
3738
38# export the qmldir qmltypes and plugin files39# export the qmldir qmltypes and plugin files
39export_qmlfiles(Utils Utils)40export_qmlfiles(Utils Utils)
4041
=== added file 'plugins/Utils/easingcurve.cpp'
--- plugins/Utils/easingcurve.cpp 1970-01-01 00:00:00 +0000
+++ plugins/Utils/easingcurve.cpp 2014-03-25 15:08:15 +0000
@@ -0,0 +1,67 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors: Michael Zanetti <michael.zanetti@canonical.com>
17*/
18
19#include "easingcurve.h"
20
21
22EasingCurve::EasingCurve(QObject *parent):
23 QObject(parent)
24{
25
26}
27
28QEasingCurve::Type EasingCurve::type() const
29{
30 return m_easingCurve.type();
31}
32
33void EasingCurve::setType(const QEasingCurve::Type &type)
34{
35 m_easingCurve.setType(type);
36 Q_EMIT typeChanged();
37}
38
39qreal EasingCurve::period() const
40{
41 return m_easingCurve.period();
42}
43
44void EasingCurve::setPeriod(qreal period)
45{
46 m_easingCurve.setPeriod(period);
47 Q_EMIT periodChanged();
48}
49
50qreal EasingCurve::progress() const
51{
52 return m_progress;
53}
54
55void EasingCurve::setProgress(qreal progress)
56{
57 if (m_progress != progress) {
58 m_progress = progress;
59 m_value = m_easingCurve.valueForProgress(m_progress);
60 Q_EMIT progressChanged();
61 }
62}
63
64qreal EasingCurve::value() const
65{
66 return m_value;
67}
068
=== added file 'plugins/Utils/easingcurve.h'
--- plugins/Utils/easingcurve.h 1970-01-01 00:00:00 +0000
+++ plugins/Utils/easingcurve.h 2014-03-25 15:08:15 +0000
@@ -0,0 +1,70 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors: Michael Zanetti <michael.zanetti@canonical.com>
17*/
18
19#ifndef EASINGCURVE_H
20#define EASINGCURVE_H
21
22#include <QObject>
23#include <QEasingCurve>
24
25/**
26 * @brief The EasingCurve class
27 *
28 * This class exposes the QEasingCurve C++ API to QML.
29 * This is useful for user interactive animations. While the QML Animation types
30 * all require a "from", "to" and "duration", this one is based on "period" and
31 * "progress". So you can control the position of the aimation by changing the
32 * progress, also going back and forward in the animation. Depending on the type
33 * of the easing curve, value will return the transformed progress.
34 */
35
36class EasingCurve: public QObject
37{
38 Q_OBJECT
39 Q_ENUMS(QEasingCurve::Type)
40 Q_PROPERTY(QEasingCurve::Type type READ type WRITE setType NOTIFY typeChanged)
41 Q_PROPERTY(qreal period READ period WRITE setPeriod NOTIFY periodChanged)
42 Q_PROPERTY(qreal progress READ progress WRITE setProgress NOTIFY progressChanged)
43 Q_PROPERTY(qreal value READ value NOTIFY progressChanged)
44
45public:
46 EasingCurve(QObject *parent = 0);
47
48 QEasingCurve::Type type() const;
49 void setType(const QEasingCurve::Type &type);
50
51 qreal period() const;
52 void setPeriod(qreal period);
53
54 qreal progress() const;
55 void setProgress(qreal progress);
56
57 qreal value() const;
58
59Q_SIGNALS:
60 void typeChanged();
61 void periodChanged();
62 void progressChanged();
63
64private:
65 QEasingCurve m_easingCurve;
66 qreal m_progress;
67 qreal m_value;
68};
69
70#endif
071
=== modified file 'plugins/Utils/plugin.cpp'
--- plugins/Utils/plugin.cpp 2014-03-17 15:50:50 +0000
+++ plugins/Utils/plugin.cpp 2014-03-25 15:08:15 +0000
@@ -32,6 +32,7 @@
32#include "qsortfilterproxymodelqml.h"32#include "qsortfilterproxymodelqml.h"
33#include "timeformatter.h"33#include "timeformatter.h"
34#include "unitymenumodelpaths.h"34#include "unitymenumodelpaths.h"
35#include "easingcurve.h"
3536
36static const char* BOTTOM_BAR_VISIBILITY_COMMUNICATOR_DBUS_PATH = "/BottomBarVisibilityCommunicator";37static const char* BOTTOM_BAR_VISIBILITY_COMMUNICATOR_DBUS_PATH = "/BottomBarVisibilityCommunicator";
37static const char* DBUS_SERVICE = "com.canonical.Shell.BottomBarVisibilityCommunicator";38static const char* DBUS_SERVICE = "com.canonical.Shell.BottomBarVisibilityCommunicator";
@@ -46,6 +47,7 @@
46 qmlRegisterType<TimeFormatter>(uri, 0, 1, "TimeFormatter");47 qmlRegisterType<TimeFormatter>(uri, 0, 1, "TimeFormatter");
47 qmlRegisterType<GDateTimeFormatter>(uri, 0, 1, "GDateTimeFormatter");48 qmlRegisterType<GDateTimeFormatter>(uri, 0, 1, "GDateTimeFormatter");
48 qmlRegisterUncreatableType<BottomBarVisibilityCommunicatorShell>(uri, 0, 1, "BottomBarVisibilityCommunicatorShell", "Can't create BottomBarVisibilityCommunicatorShell");49 qmlRegisterUncreatableType<BottomBarVisibilityCommunicatorShell>(uri, 0, 1, "BottomBarVisibilityCommunicatorShell", "Can't create BottomBarVisibilityCommunicatorShell");
50 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
49}51}
5052
51void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)53void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
5254
=== modified file 'qml/CMakeLists.txt'
--- qml/CMakeLists.txt 2013-12-17 16:04:47 +0000
+++ qml/CMakeLists.txt 2014-03-25 15:08:15 +0000
@@ -13,7 +13,7 @@
13 Hud13 Hud
14 Panel14 Panel
15 Launcher15 Launcher
16 SideStage16 Stages
17 Notifications17 Notifications
18 )18 )
1919
2020
=== removed file 'qml/Components/ApplicationManagerWrapper.qml'
--- qml/Components/ApplicationManagerWrapper.qml 2014-03-05 12:52:57 +0000
+++ qml/Components/ApplicationManagerWrapper.qml 1970-01-01 00:00:00 +0000
@@ -1,169 +0,0 @@
1/*
2 * Copyright (C) 2013 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18import Unity.Application 0.1
19
20Item {
21 id: root
22
23 property alias mainStageApplications: mainStageModel
24 property alias sideStageApplications: sideStageModel
25 property variant mainStageFocusedApplication: null
26 property variant sideStageFocusedApplication: null
27 property bool sideStageEnabled: true
28 property bool keyboardVisible: ApplicationManager.keyboardVisible
29 property int keyboardHeight: ApplicationManager.keyboardHeight
30
31 property bool fake: ApplicationManager.fake ? ApplicationManager.fake : false
32
33 signal focusRequested(string appId)
34
35 ApplicationsModelStageFiltered {
36 id: mainStageModel
37 stage: ApplicationInfo.MainStage
38 }
39
40 ApplicationsModelStageFiltered {
41 id: sideStageModel
42 stage: ApplicationInfo.SideStage
43 }
44
45 Connections {
46 target: ApplicationManager
47 onFocusedApplicationIdChanged: {
48 var app = ApplicationManager.findApplication(ApplicationManager.focusedApplicationId)
49 if (!app) { //nothing at all focused, so clear all
50 mainStageFocusedApplication = null;
51 sideStageFocusedApplication = null;
52 } else {
53 if (app.stage == ApplicationInfo.MainStage) {
54 mainStageFocusedApplication = app;
55 // possible the side stage app being unfocused fired this signal, so check for it
56 if (sideStageFocusedApplication && !sideStageFocusedApplication.focused)
57 sideStageFocusedApplication = null;
58 } else {
59 sideStageFocusedApplication = app;
60 // possible the main stage app being unfocused fired this signal, so check for it
61 if (mainStageFocusedApplication && !mainStageFocusedApplication.focused)
62 mainStageFocusedApplication = null;
63 }
64 }
65 }
66
67 onFocusRequested: {
68 // if no side stage enabled, override application's stage parameter
69 var app = ApplicationManager.findApplication(appId);
70 if (app && app.stage === ApplicationInfo.SideStage && !sideStageEnabled) {
71 app.stage = ApplicationInfo.MainStage;
72 }
73
74 root.focusRequested(appId);
75 }
76 }
77
78 function activateApplication(desktopFile, argument) {
79 var appId;
80
81 // HACK: Applications identified sometimes with with appId, but mostly with desktopFile.
82 // TODO: convert entire shell to use appId only.
83 if (desktopFile.indexOf(".desktop") >= 0) {
84 appId = desktopFileToAppId(desktopFile);
85 } else {
86 appId = desktopFile;
87 }
88
89 var application = ApplicationManager.findApplication(appId);
90 if (application !== null) {
91 return application;
92 }
93
94 var execFlags = sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
95
96 if (argument) {
97 return ApplicationManager.startApplication(appId, execFlags, [argument]);
98 } else {
99 return ApplicationManager.startApplication(appId, execFlags);
100 }
101 }
102
103 function stopApplication(application) {
104 var appId;
105
106 // HACK: Applications identified sometimes with with appId, but mostly with desktopFile.
107 // TODO: convert entire shell to use appId only.
108 if (typeof application == "string") {
109 appId = application;
110 } else {
111 appId = desktopFileToAppId(application.desktopFile);
112 }
113
114 ApplicationManager.stopApplication(appId);
115 }
116
117 function focusApplication(application) {
118 if (application == null || application == undefined) {
119 return;
120 }
121
122 ApplicationManager.focusApplication(application.appId);
123 }
124
125 function unfocusCurrentApplication() {
126 ApplicationManager.unfocusCurrentApplication();
127 }
128
129 function moveRunningApplicationStackPosition(from, to, stage) {
130 if (from == to || from < 0 || to < 0) return;
131
132 if (stage == ApplicationInfo.SideStage) {
133 sideStageModel.move(from, to);
134 } else {
135 mainStageModel.move(from, to);
136 }
137 }
138
139 function getApplicationFromDesktopFile(desktopFile, stage) {
140 var appId;
141
142 // HACK: Applications identified sometimes with with appId, but mostly with desktopFile.
143 // TODO: convert entire shell to use appId only.
144 if (desktopFile.indexOf(".desktop") >= 0) {
145 appId = desktopFileToAppId(desktopFile);
146 } else {
147 appId = desktopFile;
148 }
149
150 for (var i = 0, len = ApplicationManager.count; i < len; i++ ) {
151 var app = ApplicationManager.get(i);
152
153 // if stage not specified, return whichever app running on either stage
154 if (app.appId == appId && (stage == undefined || app.stage == stage)) {
155 return app;
156 }
157 }
158 }
159
160 function desktopFileToAppId(desktopFile) {
161 var right = desktopFile.lastIndexOf(".desktop");
162 var left = desktopFile.lastIndexOf("/");
163 if (left == -1 || right == -1 || left == right) {
164 console.log("ApplicationManagerWrapper: unable to extract appId from '" + desktopFile + "'");
165 return "";
166 }
167 return desktopFile.substring(left+1, right);
168 }
169}
1700
=== removed file 'qml/Components/ApplicationScreenshot.qml'
--- qml/Components/ApplicationScreenshot.qml 2014-03-05 12:52:57 +0000
+++ qml/Components/ApplicationScreenshot.qml 1970-01-01 00:00:00 +0000
@@ -1,61 +0,0 @@
1/*
2 * Copyright (C) 2013 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18import Ubuntu.Components 0.1
19import Unity.Application 0.1
20
21Item {
22 id: applicationScreenshot
23
24 property var application: null
25 property bool withBackground: false
26 property bool ready: applicationImage.ready || withBackground
27
28 function setApplication(application) {
29 applicationScreenshot.application = application
30 }
31
32 function clearApplication() {
33 applicationScreenshot.withBackground = false;
34 applicationScreenshot.application = null
35 applicationScreenshot.scheduleUpdate();
36 }
37
38 function scheduleUpdate() {
39 applicationImage.scheduleUpdate()
40 }
41
42 function updateFromCache() {
43 applicationImage.updateFromCache()
44 }
45
46 Rectangle {
47 id: background
48 anchors.fill: parent
49 color: "white" // FIXME should use normal background color of Suru theme
50 visible: applicationScreenshot.withBackground
51 }
52
53 ApplicationImage {
54 id: applicationImage
55 objectName: "screenshot image"
56 width: applicationScreenshot.application ? parent.width : 0
57 height: applicationScreenshot.application ? parent.height : 0
58 visible: applicationScreenshot.application != null && ready
59 source: ApplicationManager.findApplication((application) ? application.appId : "")
60 }
61}
620
=== removed file 'qml/Components/ApplicationsModelStageFiltered.qml'
--- qml/Components/ApplicationsModelStageFiltered.qml 2013-09-09 10:20:21 +0000
+++ qml/Components/ApplicationsModelStageFiltered.qml 1970-01-01 00:00:00 +0000
@@ -1,49 +0,0 @@
1/*
2 * Copyright (C) 2013 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18import Utils 0.1
19import Unity.Application 0.1
20
21SortFilterProxyModel {
22 id: root
23 property int stage
24
25 function get(index) {
26 return model.get(mapRowToSource(index));
27 }
28
29 function move(from, to) {
30 var realFrom = mapRowToSource(from);
31 if (realFrom == -1) {
32 console.log("ERROR; invalid from=" + from + " index in move operation");
33 return;
34 }
35 var realTo = mapRowToSource(to);
36 if (realTo == -1) {
37 // assuming wanting to move to end of list
38 realTo = model.count - 1;
39 }
40
41 if (realFrom == realTo) return;
42 model.move(realFrom, realTo);
43 }
44
45 model: ApplicationManager
46 dynamicSortFilter: true
47 filterRole: ApplicationManager.RoleStage
48 filterRegExp: RegExp(stage)
49}
500
=== modified file 'qml/Components/ResponsiveFlowView.qml'
--- qml/Components/ResponsiveFlowView.qml 2013-06-05 22:03:08 +0000
+++ qml/Components/ResponsiveFlowView.qml 2014-03-25 15:08:15 +0000
@@ -27,9 +27,8 @@
27 property alias verticalSpacing: flow.verticalSpacing27 property alias verticalSpacing: flow.verticalSpacing
28 property alias horizontalSpacing: flow.horizontalSpacing28 property alias horizontalSpacing: flow.horizontalSpacing
29 property int referenceDelegateWidth29 property int referenceDelegateWidth
30 property alias firstModel: repeater1.model30 property alias model: repeater.model
31 property alias secondModel: repeater2.model31 property alias delegate: repeater.delegate
32 property alias delegate: repeater1.delegate
33 readonly property int cellWidth: referenceDelegateWidth + horizontalSpacing32 readonly property int cellWidth: referenceDelegateWidth + horizontalSpacing
34 readonly property int cellHeight: referenceDelegateWidth + verticalSpacing33 readonly property int cellHeight: referenceDelegateWidth + verticalSpacing
35 property alias move: flow.move34 property alias move: flow.move
@@ -69,13 +68,7 @@
69 property int margin: allocatableVerticalSpace - columns * horizontalSpacing68 property int margin: allocatableVerticalSpace - columns * horizontalSpacing
7069
71 Repeater {70 Repeater {
72 id: repeater171 id: repeater
73 model: (root.model) ? root.model[0] : null
74 }
75 Repeater {
76 id: repeater2
77 model: (root.model) ? root.model[1] : null
78 delegate: repeater1.delegate
79 }72 }
80 }73 }
81}74}
8275
=== modified file 'qml/Dash/Apps/RunningApplicationTile.qml'
--- qml/Dash/Apps/RunningApplicationTile.qml 2014-03-12 10:44:18 +0000
+++ qml/Dash/Apps/RunningApplicationTile.qml 2014-03-25 15:08:15 +0000
@@ -23,7 +23,6 @@
23AbstractButton {23AbstractButton {
24 id: root24 id: root
25 property var application25 property var application
26 property bool __sideStageEnabled: shell.applicationManager.sideStageEnabled
2726
28 signal requestedApplicationActivation(var application)27 signal requestedApplicationActivation(var application)
29 signal requestedApplicationTermination(var application)28 signal requestedApplicationTermination(var application)
@@ -34,7 +33,7 @@
34 // To avoid "binding loop" warning33 // To avoid "binding loop" warning
35 height: shapedApplicationImage.height + labelContainer.height34 height: shapedApplicationImage.height + labelContainer.height
3635
37 width: shapedApplicationImage.width36 width: shapedApplicationImage.width <= units.gu(11) ? units.gu(11) : height
3837
39 property bool terminationModeEnabled: false38 property bool terminationModeEnabled: false
4039
@@ -51,32 +50,23 @@
51 }50 }
52 }51 }
5352
54 function updateScreenshotFromCache() {53 UbuntuShape {
55 applicationImage.updateFromCache();
56 }
57
58 // FIXME: should use UbuntuShape from SDK
59 UbuntuShapeForItem {
60 id: shapedApplicationImage54 id: shapedApplicationImage
61 anchors {55 anchors { top: parent.top; horizontalCenter: parent.horizontalCenter }
62 top: parent.top56
63 horizontalCenter: parent.horizontalCenter57 height: units.gu(17)
58 width: applicationImage.width
59 radius: "medium"
60
61 image: Image {
62 id: applicationImage
63 source: application.screenshot
64 // height : width = ss.height : ss.width
65 height: shapedApplicationImage.height
66 fillMode: Image.PreserveAspectCrop
67 width: Math.min(height, height * sourceSize.width / sourceSize.height)
64 }68 }
6569
66 // FIXME: width and height should be defined according to the
67 // application window's aspect ratio.
68 width: (application.stage === ApplicationInfo.MainStage && __sideStageEnabled) ?
69 units.gu(22) : units.gu(11)
70 height: (__sideStageEnabled) ? units.gu(22) : units.gu(19)
71 radius: "medium"
72 image: applicationImage
73 }
74
75 ApplicationImage {
76 id: applicationImage
77 source: ApplicationManager.findApplication((application) ? application.appId : "")
78 width: shapedApplicationImage.width
79 height: shapedApplicationImage.height
80 }70 }
8171
82 UbuntuShape {72 UbuntuShape {
8373
=== modified file 'qml/Dash/Apps/RunningApplicationsGrid.qml'
--- qml/Dash/Apps/RunningApplicationsGrid.qml 2014-02-26 11:04:54 +0000
+++ qml/Dash/Apps/RunningApplicationsGrid.qml 2014-03-25 15:08:15 +0000
@@ -18,6 +18,7 @@
18import "../../Components"18import "../../Components"
1919
20import Ubuntu.Gestures 0.120import Ubuntu.Gestures 0.1
21import Unity.Application 0.1
2122
22ResponsiveFlowView {23ResponsiveFlowView {
23 id: root24 id: root
@@ -25,13 +26,7 @@
2526
26 signal updateScreenshots27 signal updateScreenshots
27 property alias enableHeightBehavior: heightBehaviour.enabled28 property alias enableHeightBehavior: heightBehaviour.enabled
28 property bool enableHeightBehaviorOnNextCreation: firstModel.count + secondModel.count == 029 property bool enableHeightBehaviorOnNextCreation: model.count === 0
29
30 Connections {
31 target: shell
32 onDashShownChanged: if (shell.dashShown && shell.stageScreenshotsReady) updateScreenshots();
33 onStageScreenshotsReadyChanged: if (shell.dashShown && shell.stageScreenshotsReady) updateScreenshots();
34 }
3530
36 Behavior on height {31 Behavior on height {
37 id: heightBehaviour32 id: heightBehaviour
@@ -40,13 +35,7 @@
40 }35 }
4136
42 Connections {37 Connections {
43 target: root.firstModel38 target: root.model
44 onCountChanged: {
45 heightBehaviour.enabled = true;
46 }
47 }
48 Connections {
49 target: root.secondModel
50 onCountChanged: {39 onCountChanged: {
51 heightBehaviour.enabled = true;40 heightBehaviour.enabled = true;
52 }41 }
@@ -85,17 +74,13 @@
85 root.terminationModeEnabled = true74 root.terminationModeEnabled = true
86 }75 }
87 onRequestedApplicationTermination: {76 onRequestedApplicationTermination: {
88 shell.applicationManager.stopApplication(model.appId)77 ApplicationManager.stopApplication(model.appId)
89 }78 }
90 onRequestedApplicationActivation: {79 onRequestedApplicationActivation: {
91 shell.activateApplication(model.appId)80 ApplicationManager.requestFocusApplication(model.appId)
92 }81 }
9382
94 terminationModeEnabled: root.terminationModeEnabled83 terminationModeEnabled: root.terminationModeEnabled
95
96 Component.onCompleted: {
97 root.updateScreenshots.connect(updateScreenshotFromCache);
98 }
99 }84 }
100 }85 }
10186
10287
=== modified file 'qml/Dash/DashApps.qml'
--- qml/Dash/DashApps.qml 2014-02-19 11:15:01 +0000
+++ qml/Dash/DashApps.qml 2014-03-25 15:08:15 +0000
@@ -17,6 +17,7 @@
17import QtQuick 2.017import QtQuick 2.0
18import Ubuntu.Components 0.118import Ubuntu.Components 0.1
19import Utils 0.119import Utils 0.1
20import Unity.Application 0.1
20import "../Components"21import "../Components"
21import "../Components/ListItems"22import "../Components/ListItems"
22import "Apps"23import "Apps"
@@ -25,17 +26,15 @@
25 id: scopeView26 id: scopeView
26 objectName: "DashApps"27 objectName: "DashApps"
2728
28 // FIXME: a way to aggregate these models would be ideal29 property var runningApps: ApplicationManager
29 property var mainStageApplicationsModel: shell.applicationManager.mainStageApplications
30 property var sideStageApplicationModel: shell.applicationManager.sideStageApplications
3130
32 QtObject {31 QtObject {
33 id: countObject32 id: countObject
34 property int count: scopeView.scope.searchQuery.length == 0 ? (mainStageApplicationsModel.count + sideStageApplicationModel.count) : 033 property int count: scopeView.scope.searchQuery.length == 0 ? scopeView.runningApps.count : 0
35 }34 }
3635
37 onScopeChanged: {36 onScopeChanged: {
38 scopeView.scope.categories.addSpecialCategory("running.apps.category", "Recent", "", "{ \"template\": { \"category-layout\": \"running-apps\" } }", countObject);37 scopeView.scope.categories.addSpecialCategory("running.apps.category", "Recent", "", "{ \"template\": { \"category-layout\": \"running-apps\" } }", countObject);
39 enableHeightBehaviorOnNextCreation = (mainStageApplicationsModel.count + sideStageApplicationModel.count == 0)38 enableHeightBehaviorOnNextCreation = scopeView.runningApps.count === 0
40 }39 }
41}40}
4241
=== modified file 'qml/Dash/GenericScopeView.qml'
--- qml/Dash/GenericScopeView.qml 2014-03-17 11:44:05 +0000
+++ qml/Dash/GenericScopeView.qml 2014-03-25 15:08:15 +0000
@@ -18,6 +18,7 @@
18import Ubuntu.Components 0.118import Ubuntu.Components 0.1
19import Utils 0.119import Utils 0.1
20import Unity 0.220import Unity 0.2
21import Unity.Application 0.1
21import "../Components"22import "../Components"
22import "../Components/ListItems" as ListItems23import "../Components/ListItems" as ListItems
2324
@@ -167,8 +168,7 @@
167 if (source.toString().indexOf("Apps/RunningApplicationsGrid.qml") != -1) {168 if (source.toString().indexOf("Apps/RunningApplicationsGrid.qml") != -1) {
168 // TODO: this is still a kludge :D Ideally add some kind of hook so that we169 // TODO: this is still a kludge :D Ideally add some kind of hook so that we
169 // can do this from DashApps.qml or think a better way that needs no special casing170 // can do this from DashApps.qml or think a better way that needs no special casing
170 item.firstModel = Qt.binding(function() { return mainStageApplicationsModel })171 item.model = Qt.binding(function() { return runningApps; })
171 item.secondModel = Qt.binding(function() { return sideStageApplicationModel })
172 item.canEnableTerminationMode = Qt.binding(function() { return scopeView.isCurrent })172 item.canEnableTerminationMode = Qt.binding(function() { return scopeView.isCurrent })
173 } else {173 } else {
174 item.model = Qt.binding(function() { return results })174 item.model = Qt.binding(function() { return results })
175175
=== modified file 'qml/Launcher/Launcher.qml'
--- qml/Launcher/Launcher.qml 2013-12-11 12:57:14 +0000
+++ qml/Launcher/Launcher.qml 2014-03-25 15:08:15 +0000
@@ -29,7 +29,8 @@
29 property int dragAreaWidth: units.gu(1)29 property int dragAreaWidth: units.gu(1)
30 property int minimizeDistance: units.gu(26)30 property int minimizeDistance: units.gu(26)
31 property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?31 property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?
32 (width * (dragArea.touchX-panelWidth) / (width - panelWidth)) : 032 (width * (dragArea.touchX-panelWidth) / (width - panelWidth)) :
33 (dragArea.dragging ? 0.001 : 0)
3334
34 readonly property bool shown: panel.x > -panel.width35 readonly property bool shown: panel.x > -panel.width
3536
3637
=== modified file 'qml/Panel/Panel.qml'
--- qml/Panel/Panel.qml 2014-01-14 15:35:57 +0000
+++ qml/Panel/Panel.qml 2014-03-25 15:08:15 +0000
@@ -62,7 +62,7 @@
62 height: __panelMinusSeparatorLineHeight62 height: __panelMinusSeparatorLineHeight
63 y: 063 y: 0
6464
65 Behavior on y { StandardAnimation {} }65 Behavior on y { StandardAnimation { duration: UbuntuAnimation.FastDuration } }
66 }66 }
6767
68 PanelSeparatorLine {68 PanelSeparatorLine {
6969
=== modified file 'qml/Shell.qml'
--- qml/Shell.qml 2014-03-19 22:21:57 +0000
+++ qml/Shell.qml 2014-03-25 15:08:15 +0000
@@ -31,7 +31,6 @@
31import "Hud"31import "Hud"
32import "Components"32import "Components"
33import "Bottombar"33import "Bottombar"
34import "SideStage"
35import "Notifications"34import "Notifications"
36import Unity.Notifications 1.0 as NotificationBackend35import Unity.Notifications 1.0 as NotificationBackend
3736
@@ -49,20 +48,24 @@
49 readonly property real panelHeight: panel.panelHeight48 readonly property real panelHeight: panel.panelHeight
5049
51 property bool dashShown: dash.shown50 property bool dashShown: dash.shown
52 property bool stageScreenshotsReady: {51
53 if (sideStage.shown) {52 property bool sideStageEnabled: shell.width >= units.gu(60)
54 if (mainStage.applications.count > 0) {53 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
55 return mainStage.usingScreenshots && sideStage.usingScreenshots;54
56 } else {55 function activateApplication(appId) {
57 return sideStage.usingScreenshots;56 if (ApplicationManager.findApplication(appId)) {
57 ApplicationManager.requestFocusApplication(appId);
58 stages.show();
59 if (stages.locked && ApplicationManager.focusedApplicationId == appId) {
60 applicationsDisplayLoader.item.select(appId);
58 }61 }
59 } else {62 } else {
60 return mainStage.usingScreenshots;63 var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
64 ApplicationManager.startApplication(appId, execFlags);
65 stages.show();
61 }66 }
62 }67 }
6368
64 property var applicationManager: ApplicationManagerWrapper {}
65
66 Binding {69 Binding {
67 target: LauncherModel70 target: LauncherModel
68 property: "applicationManager"71 property: "applicationManager"
@@ -71,49 +74,6 @@
7174
72 Component.onCompleted: {75 Component.onCompleted: {
73 Theme.name = "Ubuntu.Components.Themes.SuruGradient"76 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
74
75 applicationManager.sideStageEnabled = Qt.binding(function() { return sideStage.enabled })
76
77 // FIXME: if application focused before shell starts, shell draws on top of it only.
78 // We should detect already running applications on shell start and bring them to the front.
79 applicationManager.unfocusCurrentApplication();
80 }
81
82 readonly property bool applicationFocused: !!applicationManager.mainStageFocusedApplication
83 || !!applicationManager.sideStageFocusedApplication
84 // Used for autopilot testing.
85 readonly property string currentFocusedAppId: ApplicationManager.focusedApplicationId
86
87 readonly property bool fullscreenMode: {
88 if (greeter.shown || lockscreen.shown) {
89 return false;
90 } else if (mainStage.usingScreenshots) { // Window Manager animating so want to re-evaluate fullscreen mode
91 return mainStage.switchingFromFullscreenToFullscreen;
92 } else if (applicationManager.mainStageFocusedApplication) {
93 return applicationManager.mainStageFocusedApplication.fullscreen;
94 } else {
95 return false;
96 }
97 }
98
99 function activateApplication(appId, argument) {
100 if (applicationManager) {
101 // For newly started applications, as it takes them time to draw their first frame
102 // we add a delay before we hide the animation screenshots to compensate.
103 var addDelay = !applicationManager.getApplicationFromDesktopFile(appId);
104
105 var application;
106 application = applicationManager.activateApplication(appId, argument);
107 if (application == null) {
108 return;
109 }
110 if (application.stage == ApplicationInfo.MainStage || !sideStage.enabled) {
111 mainStage.activateApplication(appId, addDelay);
112 } else {
113 sideStage.activateApplication(appId, addDelay);
114 }
115 stages.show();
116 }
117 }77 }
11878
119 GSettings {79 GSettings {
@@ -133,244 +93,219 @@
133 Keys.onVolumeDownPressed: volumeControl.volumeDown()93 Keys.onVolumeDownPressed: volumeControl.volumeDown()
13494
135 Item {95 Item {
136 id: underlay96 id: underlayClipper
137 objectName: "underlay"
138 anchors.fill: parent97 anchors.fill: parent
13998 anchors.rightMargin: stages.overlayWidth
140 // Whether the underlay is fully covered by opaque UI elements.99 clip: stages.overlayMode && !stages.painting
141 property bool fullyCovered: panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth100
142101 InputFilterArea {
143 readonly property bool applicationRunning: ((mainStage.applications && mainStage.applications.count > 0)102 anchors.fill: parent
144 || (sideStage.applications && sideStage.applications.count > 0))103 blockInput: parent.clip
145104 }
146 // Whether the user should see the topmost application surface (if there's one at all).105
147 readonly property bool applicationSurfaceShouldBeSeen: applicationRunning && !stages.fullyHidden106 Item {
148 && !mainStage.usingScreenshots // but want sideStage animating over app surface107 id: underlay
149108 objectName: "underlay"
150109 anchors.fill: parent
151110 anchors.rightMargin: -parent.anchors.rightMargin
152 // NB! Application surfaces are stacked behing the shell one. So they can only be seen by the user111
153 // through the translucent parts of the shell surface.112 // Whether the underlay is fully covered by opaque UI elements.
154 visible: !fullyCovered && !applicationSurfaceShouldBeSeen113 property bool fullyCovered: panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth
155114
156 Rectangle {115 // Whether the user should see the topmost application surface (if there's one at all).
157 anchors.fill: parent116 readonly property bool applicationSurfaceShouldBeSeen: stages.shown && !stages.painting && !stages.overlayMode
158 color: "black"117
159 opacity: dash.disappearingAnimationProgress118 // NB! Application surfaces are stacked behind the shell one. So they can only be seen by the user
160 }119 // through the translucent parts of the shell surface.
161120 visible: !fullyCovered && !applicationSurfaceShouldBeSeen
162 Image {121
163 anchors.fill: dash122 CrossFadeImage {
164 source: shell.width > shell.height ? "Dash/graphics/paper_landscape.png" : "Dash/graphics/paper_portrait.png"123 id: backgroundImage
165 fillMode: Image.PreserveAspectCrop124 objectName: "backgroundImage"
166 horizontalAlignment: Image.AlignRight125
167 verticalAlignment: Image.AlignTop126 anchors.fill: parent
168 }127 source: shell.background
169128 fillMode: Image.PreserveAspectCrop
170 Dash {129 }
171 id: dash130
172 objectName: "dash"131 Rectangle {
173132 anchors.fill: parent
174 available: !greeter.shown && !lockscreen.shown133 color: "black"
175 hides: [stages, launcher, panel.indicators]134 opacity: dash.disappearingAnimationProgress
176 shown: disappearingAnimationProgress !== 1.0135 }
177 enabled: disappearingAnimationProgress === 0.0 && edgeDemo.dashEnabled136
178 // FIXME: unfocus all applications when going back to the dash137 Image {
179 onEnabledChanged: {138 anchors.fill: dash
180 if (enabled) {139 source: shell.width > shell.height ? "Dash/graphics/paper_landscape.png" : "Dash/graphics/paper_portrait.png"
181 shell.applicationManager.unfocusCurrentApplication()140 fillMode: Image.PreserveAspectCrop
182 }141 horizontalAlignment: Image.AlignRight
183 }142 verticalAlignment: Image.AlignTop
184143 }
185 anchors {144
186 fill: parent145 Dash {
187 topMargin: panel.panelHeight146 id: dash
188 }147 objectName: "dash"
189148
190 contentScale: 1.0 - 0.2 * disappearingAnimationProgress149 available: !greeter.shown && !lockscreen.shown
191 opacity: 1.0 - disappearingAnimationProgress150 hides: [stages, launcher, panel.indicators]
192 property real disappearingAnimationProgress: {151 shown: disappearingAnimationProgress !== 1.0
193 if (greeter.shown) {152 enabled: disappearingAnimationProgress === 0.0 && edgeDemo.dashEnabled
194 return greeter.showProgress;153
154 anchors {
155 fill: parent
156 topMargin: panel.panelHeight
157 }
158
159 contentScale: 1.0 - 0.2 * disappearingAnimationProgress
160 opacity: 1.0 - disappearingAnimationProgress
161 property real disappearingAnimationProgress: {
162 if (greeter.shown) {
163 return greeter.showProgress;
164 } else {
165 if (stages.overlayMode) {
166 return 0;
167 }
168 return stages.showProgress
169 }
170 }
171
172 // FIXME: only necessary because stages.showProgress and
173 // greeterRevealer.animatedProgress are not animated
174 Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}
175 }
176 }
177 }
178
179 EdgeDragArea {
180 id: stagesDragHandle
181 direction: Direction.Leftwards
182
183 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
184 width: shell.edgeSize
185
186 property real progress: stages.width
187
188 onTouchXChanged: {
189 if (status == DirectionalDragArea.Recognized) {
190 if (ApplicationManager.count == 0) {
191 progress = Math.max(stages.width - stagesDragHandle.width + touchX, stages.width * .3)
195 } else {192 } else {
196 return stagesOuterContainer.showProgress;193 progress = stages.width - stagesDragHandle.width + touchX
197 }194 }
198 }195 }
196 }
199197
200 // FIXME: only necessary because stagesOuterContainer.showProgress and198 onDraggingChanged: {
201 // greeterRevealer.animatedProgress are not animated199 if (!dragging) {
202 Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}200 if (ApplicationManager.count > 0 && progress < stages.width - units.gu(10)) {
201 stages.show()
202 }
203 stagesDragHandle.progress = stages.width;
204 }
203 }205 }
204 }206 }
205207
206 Item {208 Item {
207 id: stagesOuterContainer209 id: stages
208210 objectName: "stages"
209 width: parent.width211 width: parent.width
210 height: parent.height212 height: parent.height
211 x: launcher.progress213
212 Behavior on x {SmoothedAnimation{velocity: 600}}214 x: {
213215 if (shown) {
214 property real showProgress:216 if (overlayMode || locked) {
215 MathUtils.clamp(1 - (x + stages.x) / shell.width, 0, 1)217 return 0;
216218 }
217 Showable {219 return launcher.progress
218 id: stages220 } else {
219 objectName: "stages"221 return stagesDragHandle.progress
220222 }
221 x: width223 }
222224
223 property bool fullyShown: shown && x == 0 && parent.x == 0225 Behavior on x { SmoothedAnimation { velocity: 600; duration: UbuntuAnimation.FastDuration } }
224 property bool fullyHidden: !shown && x == width226
225 available: !greeter.shown227 property bool shown: false
226 hides: [panel.indicators]228
227 shown: false229 property real showProgress: overlayMode ? 0 : MathUtils.clamp(1 - x / shell.width, 0, 1)
228 opacity: 1.0230
229 showAnimation: StandardAnimation { property: "x"; duration: 350; to: 0; easing.type: Easing.OutCubic }231 property bool fullyShown: x == 0
230 hideAnimation: StandardAnimation { property: "x"; duration: 350; to: width; easing.type: Easing.OutCubic }232 property bool fullyHidden: x == width
231233
232 width: parent.width234 property bool painting: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.painting : false
233 height: parent.height235 property bool fullscreen: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.fullscreen : false
234236 property bool overlayMode: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.overlayMode : false
235 // close the stages when no focused application remains237 property int overlayWidth: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.overlayWidth : false
236 Connections {238 property bool locked: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.locked : false
237 target: shell.applicationManager239
238 onMainStageFocusedApplicationChanged: stages.closeIfNoApplications()240 function show() {
239 onSideStageFocusedApplicationChanged: stages.closeIfNoApplications()241 shown = true;
240 ignoreUnknownSignals: true242 panel.indicators.hide();
241 }243 if (!ApplicationManager.focusedApplicationId && ApplicationManager.count > 0) {
242244 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
243 function closeIfNoApplications() {245 }
244 if (!shell.applicationManager.mainStageFocusedApplication246 }
245 && !shell.applicationManager.sideStageFocusedApplication247
246 && shell.applicationManager.mainStageApplications.count == 0248 function hide() {
247 && shell.applicationManager.sideStageApplications.count == 0) {249 shown = false;
248 stages.hide();250 if (ApplicationManager.focusedApplicationId) {
249 }251 ApplicationManager.unfocusCurrentApplication();
250 }252 }
251253 }
252 // show the stages when an application gets the focus254
253 Connections {255 Connections {
254 target: shell.applicationManager256 target: ApplicationManager
255 onMainStageFocusedApplicationChanged: {257
256 if (shell.applicationManager.mainStageFocusedApplication) {258 onFocusRequested: {
257 mainStage.show();259 stages.show();
258 stages.show();260 }
259 }261
260 }262 onFocusedApplicationIdChanged: {
261 onSideStageFocusedApplicationChanged: {263 if (ApplicationManager.focusedApplicationId.length > 0) {
262 if (shell.applicationManager.sideStageFocusedApplication) {
263 sideStage.show();
264 stages.show();
265 }
266 }
267 ignoreUnknownSignals: true
268 }
269
270 Stage {
271 id: mainStage
272
273 anchors.fill: parent
274 fullyShown: stages.fullyShown
275 fullyHidden: stages.fullyHidden
276 shouldUseScreenshots: !fullyShown
277 rightEdgeEnabled: !sideStage.enabled
278
279 applicationManager: shell.applicationManager
280 rightEdgeDraggingAreaWidth: shell.edgeSize
281 normalApplicationY: shell.panelHeight
282
283 shown: true
284 function show() {
285 stages.show();264 stages.show();
286 }265 } else {
287 function hide() {266 if (!stages.overlayMode) {
288 }
289
290 // FIXME: workaround the fact that focusing a main stage application
291 // raises its surface on top of all other surfaces including the ones
292 // that belong to side stage applications.
293 onFocusedApplicationChanged: {
294 if (focusedApplication && sideStage.focusedApplication && sideStage.fullyShown) {
295 shell.applicationManager.focusApplication(sideStage.focusedApplication);
296 }
297 }
298 }
299
300 SideStage {
301 id: sideStage
302
303 applicationManager: shell.applicationManager
304 rightEdgeDraggingAreaWidth: shell.edgeSize
305 normalApplicationY: shell.panelHeight
306
307 onShownChanged: {
308 if (!shown && mainStage.applications.count == 0) {
309 stages.hide();267 stages.hide();
310 }268 }
311 }269 }
312 // FIXME: when hiding the side stage, refocus the main stage270
313 // application so that it goes in front of the side stage271 // If any app is focused when greeter is open, it's due to a user action
314 // application and hides it272 // like a snap decision (say, an incoming call).
315 onFullyShownChanged: {273 // TODO: these should be protected to only unlock for certain applications / certain usecases
316 if (!fullyShown && stages.fullyShown && sideStage.focusedApplication != null) {274 // potentially only in connection with a notification.
317 shell.applicationManager.focusApplication(mainStage.focusedApplication);275 greeter.hide()
318 }276 }
277
278 onApplicationAdded: {
279 stages.show();
280 }
281
282 onApplicationRemoved: {
283 if (ApplicationManager.focusedApplicationId.length == 0) {
284 stages.hide();
319 }285 }
320286 }
321 enabled: shell.width >= units.gu(100)287 }
322 visible: enabled288
323 fullyShown: stages.fullyShown && shown289 Loader {
324 && sideStage[sideStageRevealer.boundProperty] == sideStageRevealer.openedValue290 id: applicationsDisplayLoader
325 shouldUseScreenshots: !fullyShown || mainStage.usingScreenshots || sideStageRevealer.pressed291 anchors.fill: parent
326292
327 available: !greeter.shown && !lockscreen.shown && enabled293 source: shell.sideStageEnabled ? "Stages/StageWithSideStage.qml" : "Stages/PhoneStage.qml"
328 hides: [launcher, panel.indicators]294
329 shown: false295 Binding {
330 showAnimation: StandardAnimation { property: "x"; duration: 350; to: sideStageRevealer.openedValue; easing.type: Easing.OutQuint }296 target: applicationsDisplayLoader.item
331 hideAnimation: StandardAnimation { property: "x"; duration: 350; to: sideStageRevealer.closedValue; easing.type: Easing.OutQuint }297 property: "moving"
332298 value: !stages.fullyShown
333 width: units.gu(40)299 }
334 height: stages.height300 Binding {
335 handleExpanded: sideStageRevealer.pressed301 target: applicationsDisplayLoader.item
336 }302 property: "shown"
337303 value: stages.shown
338 Revealer {304 }
339 id: sideStageRevealer305 Binding {
340306 target: applicationsDisplayLoader.item
341 enabled: mainStage.applications.count > 0 && sideStage.applications.count > 0307 property: "dragAreaWidth"
342 && sideStage.available308 value: shell.edgeSize
343 direction: Qt.RightToLeft
344 openedValue: parent.width - sideStage.width
345 hintDisplacement: units.gu(3)
346 /* The size of the sidestage handle needs to be bigger than the
347 typical size used for edge detection otherwise it is really
348 hard to grab.
349 */
350 handleSize: sideStage.shown ? units.gu(4) : shell.edgeSize
351 closedValue: parent.width + sideStage.handleSizeCollapsed
352 target: sideStage
353 x: parent.width - width
354 width: sideStage.width + handleSize * 0.7
355 height: sideStage.height
356 orientation: Qt.Horizontal
357 }
358
359 DragHandle {
360 id: stagesDragHandle
361
362 anchors.top: parent.top
363 anchors.bottom: parent.bottom
364 anchors.right: parent.left
365
366 width: shell.edgeSize
367 direction: Direction.Leftwards
368 enabled: greeter.showProgress == 0 && edgeDemo.dashEnabled
369 property bool haveApps: mainStage.applications.count > 0 || sideStage.applications.count > 0
370
371 maxTotalDragDistance: haveApps ? parent.width : parent.width * 0.7
372 // Make autocompletion impossible when !haveApps
373 edgeDragEvaluator.minDragDistance: haveApps ? maxTotalDragDistance * 0.1 : Number.MAX_VALUE
374 }309 }
375 }310 }
376 }311 }
@@ -447,26 +382,6 @@
447382
448 dragHandleWidth: shell.edgeSize383 dragHandleWidth: shell.edgeSize
449384
450 property var previousMainApp: null
451 property var previousSideApp: null
452
453 function removeApplicationFocus() {
454 greeter.previousMainApp = applicationManager.mainStageFocusedApplication;
455 greeter.previousSideApp = applicationManager.sideStageFocusedApplication;
456 applicationManager.unfocusCurrentApplication();
457 }
458
459 function restoreApplicationFocus() {
460 if (greeter.previousMainApp) {
461 applicationManager.focusApplication(greeter.previousMainApp);
462 greeter.previousMainApp = null;
463 }
464 if (greeter.previousSideApp) {
465 applicationManager.focusApplication(greeter.previousSideApp);
466 greeter.previousSideApp = null;
467 }
468 }
469
470 onShownChanged: {385 onShownChanged: {
471 if (shown) {386 if (shown) {
472 lockscreen.reset();387 lockscreen.reset();
@@ -476,9 +391,6 @@
476 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));391 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
477 }392 }
478 greeter.forceActiveFocus();393 greeter.forceActiveFocus();
479 removeApplicationFocus();
480 } else {
481 restoreApplicationFocus();
482 }394 }
483 }395 }
484396
@@ -496,33 +408,16 @@
496 }408 }
497 }409 }
498410
499 Connections {411 Binding {
500 target: applicationManager412 target: ApplicationManager
501 ignoreUnknownSignals: true413 property: "suspended"
502 // If any app is focused when greeter is open, it's due to a user action414 value: greeter.shown && greeter.showProgress == 1
503 // like a snap decision (say, an incoming call).
504 // TODO: these should be protected to only unlock for certain applications / certain usecases
505 // potentially only in connection with a notification.
506 onMainStageFocusedApplicationChanged: {
507 if (greeter.shown && applicationManager.mainStageFocusedApplication) {
508 greeter.previousMainApp = null // make way for new focused app
509 greeter.previousSideApp = null
510 greeter.hide()
511 }
512 }
513 onSideStageFocusedApplicationChanged: {
514 if (greeter.shown && applicationManager.sideStageFocusedApplication) {
515 greeter.previousMainApp = null // make way for new focused app
516 greeter.previousSideApp = null
517 greeter.hide()
518 }
519 }
520 }415 }
521 }416 }
522417
523 InputFilterArea {418 InputFilterArea {
524 anchors.fill: parent419 anchors.fill: parent
525 blockInput: !applicationFocused || greeter.shown || lockscreen.shown || launcher.shown420 blockInput: ApplicationManager.focusedApplicationId.length === 0 || greeter.shown || lockscreen.shown || launcher.shown
526 || panel.indicators.shown || hud.shown421 || panel.indicators.shown || hud.shown
527 }422 }
528423
@@ -571,7 +466,9 @@
571 available: edgeDemo.panelEnabled466 available: edgeDemo.panelEnabled
572 contentEnabled: edgeDemo.panelContentEnabled467 contentEnabled: edgeDemo.panelContentEnabled
573 }468 }
574 fullscreenMode: shell.fullscreenMode469 property string focusedAppId: ApplicationManager.focusedApplicationId
470 property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
471 fullscreenMode: focusedApplication && stages.fullscreen && !greeter.shown && !lockscreen.shown
575 searchVisible: !greeter.shown && !lockscreen.shown && dash.shown && dash.searchable472 searchVisible: !greeter.shown && !lockscreen.shown && dash.shown && dash.searchable
576473
577 InputFilterArea {474 InputFilterArea {
@@ -597,9 +494,8 @@
597 hideAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: hudRevealer.closedValue; easing.type: Easing.Linear }494 hideAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: hudRevealer.closedValue; easing.type: Easing.Linear }
598495
599 Connections {496 Connections {
600 target: shell.applicationManager497 target: ApplicationManager
601 onMainStageFocusedApplicationChanged: hud.hide()498 onFocusedApplicationIdChanged: hud.hide()
602 onSideStageFocusedApplicationChanged: hud.hide()
603 }499 }
604 }500 }
605501
@@ -624,7 +520,7 @@
624 theHud: hud520 theHud: hud
625 anchors.fill: parent521 anchors.fill: parent
626 enabled: hud.available522 enabled: hud.available
627 applicationIsOnForeground: applicationFocused523 applicationIsOnForeground: ApplicationManager.focusedApplicationId
628 }524 }
629525
630 InputFilterArea {526 InputFilterArea {
@@ -655,9 +551,11 @@
655 showHome()551 showHome()
656 }552 }
657 onDash: {553 onDash: {
658 if (stages.shown) {554 if (stages.shown && !stages.overlayMode) {
659 stages.hide();555 if (!stages.locked) {
660 launcher.hide();556 stages.hide();
557 launcher.hide();
558 }
661 }559 }
662 }560 }
663 onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("clickscope", false, true)561 onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("clickscope", false, true)
@@ -750,14 +648,14 @@
750 anchors.bottom: parent.bottom648 anchors.bottom: parent.bottom
751 anchors.left: parent.left649 anchors.left: parent.left
752 anchors.right: parent.right650 anchors.right: parent.right
753 height: shell.applicationManager ? shell.applicationManager.keyboardHeight : 0651 height: ApplicationManager.keyboardVisible ? ApplicationManager.keyboardHeight : 0
754652
755 enabled: shell.applicationManager && shell.applicationManager.keyboardVisible653 enabled: ApplicationManager.keyboardVisible
756 }654 }
757655
758 Label {656 Label {
759 anchors.centerIn: parent657 anchors.centerIn: parent
760 visible: applicationManager.fake658 visible: ApplicationManager.fake ? ApplicationManager.fake : false
761 text: "EARLY ALPHA\nNOT READY FOR USE"659 text: "EARLY ALPHA\nNOT READY FOR USE"
762 color: "lightgrey"660 color: "lightgrey"
763 opacity: 0.2661 opacity: 0.2
764662
=== removed directory 'qml/SideStage'
=== removed file 'qml/SideStage/SideStage.qml'
--- qml/SideStage/SideStage.qml 2014-02-03 13:31:28 +0000
+++ qml/SideStage/SideStage.qml 1970-01-01 00:00:00 +0000
@@ -1,73 +0,0 @@
1/*
2 * Copyright (C) 2013 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18import Unity.Application 0.1
19import Ubuntu.Components 0.1
20import "../Components"
21
22Stage {
23 id: stage
24
25 type: ApplicationInfo.SideStage
26 property real handleSizeCollapsed: units.gu(2.5)
27 property real handleSizeExpanded: units.gu(4)
28 property bool handleExpanded
29
30 /* FIXME: workaround so that when a main stage app goes fullscreen
31 the sidestage's top is not transparent. Proper fix would be to
32 resize the side stage app.
33 */
34 Rectangle {
35 id: backgroundBeneathPanel
36 anchors {
37 top: parent.top
38 left: parent.left
39 right: parent.right
40 }
41 height: shell.panelHeight
42 color: background.color
43 z: -1
44 }
45
46 Rectangle {
47 id: background
48 anchors.fill: parent
49 color: "#2c2924"
50 z: -1
51 visible: stage.usingScreenshots
52 }
53
54 SidestageHandle {
55 id: handle
56 objectName: "sideStageHandle"
57
58 anchors {
59 top: parent.top
60 bottom: parent.bottom
61 left: parent.left
62 leftMargin: -width
63 }
64 width: handleExpanded ? handleSizeExpanded : handleSizeCollapsed
65 Behavior on width { NumberAnimation { easing.type: Easing.OutQuart} }
66 z: -1
67 }
68
69 InputFilterArea {
70 anchors.fill: handle
71 blockInput: visible
72 }
73}
740
=== removed directory 'qml/SideStage/graphics'
=== added directory 'qml/Stages'
=== added file 'qml/Stages/PhoneStage.qml'
--- qml/Stages/PhoneStage.qml 1970-01-01 00:00:00 +0000
+++ qml/Stages/PhoneStage.qml 2014-03-25 15:08:15 +0000
@@ -0,0 +1,501 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18import Ubuntu.Components 0.1
19import Ubuntu.Gestures 0.1
20import Unity.Application 0.1
21import Utils 0.1
22import "../Components"
23
24Item {
25 id: root
26
27 // Controls to be set from outside
28 property bool shown: false
29 property bool moving: false
30 property int dragAreaWidth
31
32 // State information propagated to the outside
33 readonly property bool painting: mainScreenshotImage.visible || fadeInScreenshotImage.visible || appSplash.visible || spreadView.visible
34 property bool fullscreen: priv.focusedApplication ? priv.focusedApplication.fullscreen : false
35 property bool locked: spreadView.visible
36
37 // Not used for PhoneStage, only useful for SideStage and similar
38 property bool overlayMode: false
39 property int overlayWidth: 0
40
41 function select(appId) {
42 spreadView.snapTo(priv.indexOf(appId))
43 }
44
45 onMovingChanged: {
46 if (moving) {
47 if (ApplicationManager.focusedApplicationId) {
48 priv.requestNewScreenshot();
49 } else {
50 mainScreenshotImage.anchors.leftMargin = 0;
51 mainScreenshotImage.source = ApplicationManager.get(0).screenshot;
52 mainScreenshotImage.visible = true;
53 }
54 } else {
55 mainScreenshotImage.visible = false;
56 }
57 }
58
59 Connections {
60 target: ApplicationManager
61
62 onFocusRequested: {
63 if (spreadView.visible) {
64 spreadView.snapTo(priv.indexOf(appId));
65 } else {
66 priv.switchToApp(appId);
67 }
68 }
69
70 onFocusedApplicationIdChanged: {
71 if (ApplicationManager.focusedApplicationId.length > 0) {
72 if (priv.secondApplicationStarting || priv.applicationStarting) {
73 appSplashTimer.start();
74 } else {
75 var application = priv.focusedApplication;
76 root.fullscreen = application.fullscreen;
77 mainScreenshotImage.source = application.screenshot;
78 }
79 } else {
80 spreadView.selectedIndex = -1;
81 spreadView.phase = 0;
82 spreadView.contentX = -spreadView.shift;
83 }
84 }
85
86 onApplicationAdded: {
87 if (!priv.focusedApplication) {
88 mainScreenshotImage.source = "";
89 mainScreenshotImage.visible = false;
90 priv.applicationStarting = true;
91 } else {
92 mainScreenshotImage.source = "";
93 priv.newFocusedAppId = appId;
94 priv.secondApplicationStarting = true;
95 priv.requestNewScreenshot();
96 }
97
98 if (spreadView.visible) {
99 spreadView.snapTo(0);
100 }
101 }
102
103 onApplicationRemoved: {
104 if (ApplicationManager.count == 0) {
105 mainScreenshotImage.source = ""
106 mainScreenshotImage.visible = false;
107 } else {
108 mainScreenshotImage.source = ApplicationManager.get(0).screenshot;
109 }
110 }
111 }
112
113 QtObject {
114 id: priv
115
116 property string focusedAppId: ApplicationManager.focusedApplicationId
117 property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
118 property url focusedScreenshot: focusedApplication ? focusedApplication.screenshot : ""
119
120 property bool waitingForScreenshot: false
121
122 property bool applicationStarting: false
123 property bool secondApplicationStarting: false
124
125 property string newFocusedAppId
126
127 onFocusedScreenshotChanged: {
128 if (root.moving && priv.waitingForScreenshot) {
129 mainScreenshotImage.anchors.leftMargin = 0;
130 mainScreenshotImage.source = priv.focusedScreenshot
131 mainScreenshotImage.visible = true;
132 } else if (priv.secondApplicationStarting && priv.waitingForScreenshot) {
133 applicationSwitchingAnimation.start();
134 }
135 waitingForScreenshot = false;
136 }
137
138 function requestNewScreenshot() {
139 waitingForScreenshot = true;
140 ApplicationManager.updateScreenshot(focusedAppId);
141 }
142
143 function switchToApp(appId) {
144 if (priv.focusedAppId) {
145 priv.newFocusedAppId = appId;
146 root.fullscreen = ApplicationManager.findApplication(appId).fullscreen;
147 applicationSwitchingAnimation.start();
148 } else {
149 ApplicationManager.focusApplication(appId);
150 }
151 }
152
153 function indexOf(appId) {
154 for (var i = 0; i < ApplicationManager.count; i++) {
155 if (ApplicationManager.get(i).appId == appId) {
156 return i;
157 }
158 }
159 return -1;
160 }
161
162 }
163
164 // FIXME: the signal connections seems to get lost.
165 Connections {
166 target: priv.focusedApplication
167 onScreenshotChanged: priv.focusedScreenshot = priv.focusedApplication.screenshot
168 }
169 Binding {
170 target: root
171 property: "fullscreen"
172 value: priv.focusedApplication ? priv.focusedApplication.fullscreen : false
173 }
174
175 Timer {
176 id: appSplashTimer
177 // FIXME: apart from removing this completely in the future and make the app surface paint
178 // meaningful stuff, also check for colin's stuff to land so we can shape 1.4 secs away from here
179 // https://code.launchpad.net/~cjwatson/upstart-app-launch/libclick-manifest/+merge/210520
180 // https://code.launchpad.net/~cjwatson/upstart-app-launch/libclick-pkgdir/+merge/209909
181 interval: 1700
182 repeat: false
183 onTriggered: {
184 priv.applicationStarting = false;
185 priv.secondApplicationStarting = false;
186 }
187 }
188
189 SequentialAnimation {
190 id: applicationSwitchingAnimation
191 // setup
192 PropertyAction { target: mainScreenshotImage; property: "anchors.leftMargin"; value: 0 }
193 PropertyAction { target: mainScreenshotImage; property: "source"; value: priv.focusedScreenshot }
194 PropertyAction { targets: [mainScreenshotImage, fadeInScreenshotImage]; property: "visible"; value: true }
195 PropertyAction { target: fadeInScreenshotImage; property: "source"; value: {
196 var newFocusedApp = ApplicationManager.findApplication(priv.newFocusedAppId);
197 return newFocusedApp ? newFocusedApp.screenshot : "" }
198 }
199 PropertyAction { target: fadeInScreenshotImage; property: "opacity"; value: 0 }
200 PropertyAction { target: fadeInScreenshotImage; property: "scale"; value: .8 }
201
202
203 // The actual animation
204 ParallelAnimation {
205 UbuntuNumberAnimation { target: mainScreenshotImage; property: "anchors.leftMargin"; to: root.width; duration: UbuntuAnimation.SlowDuration }
206 UbuntuNumberAnimation { target: fadeInScreenshotImage; properties: "opacity,scale"; to: 1; duration: UbuntuAnimation.SlowDuration }
207 }
208
209 // restore stuff
210 ScriptAction { script: ApplicationManager.focusApplication(priv.newFocusedAppId); }
211 PropertyAction { target: fadeInScreenshotImage; property: "visible"; value: false }
212 PropertyAction { target: mainScreenshotImage; property: "visible"; value: false }
213 }
214
215 // FIXME: Drop this and make the imageprovider show a splashscreen instead
216 Rectangle {
217 id: appSplash2
218 anchors.fill: parent
219 color: "white"
220 visible: priv.secondApplicationStarting
221 }
222 Image {
223 id: fadeInScreenshotImage
224 anchors { left: parent.left; bottom: parent.bottom }
225 width: parent.width
226 scale: .7
227 visible: false
228 }
229
230 Rectangle {
231 id: appSplash
232 anchors.fill: parent
233 color: "white"
234 visible: priv.applicationStarting
235 }
236 Image {
237 id: mainScreenshotImage
238 anchors { left: parent.left; bottom: parent.bottom }
239 width: parent.width
240 visible: false
241 }
242
243 EdgeDragArea {
244 id: spreadDragArea
245 direction: Direction.Leftwards
246 enabled: ApplicationManager.count > 1 && spreadView.phase != 2
247
248 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
249 width: root.dragAreaWidth
250
251 // Sitting at the right edge of the screen, this EdgeDragArea directly controls the spreadView when
252 // attachedToView is true. When the finger movement passes positionMarker3 we detach it from the
253 // spreadView and make the spreadView snap to positionMarker4.
254 property bool attachedToView: true
255
256 property var gesturePoints: new Array()
257
258 onTouchXChanged: {
259 if (!dragging && !priv.waitingForScreenshot) {
260 // Initial touch. Let's update the screenshot and reset the spreadView to the starting position.
261 priv.requestNewScreenshot();
262 spreadView.phase = 0;
263 spreadView.contentX = -spreadView.shift;
264 }
265 if (dragging && attachedToView) {
266 // Gesture recognized. Let's move the spreadView with the finger
267 spreadView.contentX = -touchX - spreadView.shift;
268 }
269 if (attachedToView && spreadView.shiftedContentX >= spreadView.width * spreadView.positionMarker3) {
270 // We passed positionMarker3. Detach from spreadView and snap it.
271 attachedToView = false;
272 spreadView.snap();
273 }
274 gesturePoints.push(touchX);
275 }
276
277 onStatusChanged: {
278 if (status == DirectionalDragArea.Recognized) {
279 attachedToView = true;
280 }
281 }
282
283 onDraggingChanged: {
284 if (dragging) {
285 // Gesture recognized. Start recording this gesture
286 gesturePoints = [];
287 return;
288 }
289
290 // Ok. The user released. Find out if it was a one-way movement.
291 var oneWayFlick = true;
292 var smallestX = spreadDragArea.width;
293 for (var i = 0; i < gesturePoints.length; i++) {
294 if (gesturePoints[i] >= smallestX) {
295 oneWayFlick = false;
296 break;
297 }
298 smallestX = gesturePoints[i];
299 }
300 gesturePoints = [];
301
302 if (oneWayFlick && spreadView.shiftedContentX > units.gu(2) &&
303 spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
304 // If it was a short one-way movement, do the Alt+Tab switch
305 // no matter if we didn't cross positionMarker1 yet.
306 spreadView.snapTo(1);
307 } else if (!dragging && attachedToView) {
308 // otherwise snap to the closest snap position we can find
309 // (might be back to start, to app 1 or to spread)
310 spreadView.snap();
311 }
312 }
313 }
314
315 Rectangle {
316 id: coverFlipBackground
317 anchors.fill: parent
318 color: "black"
319 visible: spreadView.visible
320 }
321
322 InputFilterArea {
323 anchors.fill: root
324 blockInput: spreadView.visible
325 }
326
327 Flickable {
328 id: spreadView
329 objectName: "spreadView"
330 anchors.fill: parent
331 visible: spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1 || snapAnimation.running
332 contentWidth: spreadRow.width - shift
333 contentX: -shift
334
335 // The flickable needs to fill the screen in order to get touch events all over.
336 // However, we don't want to the user to be able to scroll back all the way. For
337 // that, the beginning of the gesture starts with a negative value for contentX
338 // so the flickable wants to pull it into the view already. "shift" tunes the
339 // distance where to "lock" the content.
340 property real shift: width / 2
341 property real shiftedContentX: contentX + shift
342
343 property int tileDistance: width / 4
344
345 // Those markers mark the various positions in the spread (ratio to screen width from right to left):
346 // 0 - 1: following finger, snap back to the beginning on release
347 property real positionMarker1: 0.3
348 // 1 - 2: curved snapping movement, snap to app 1 on release
349 property real positionMarker2: 0.45
350 // 2 - 3: movement follows finger, snaps back to app 1 on release
351 property real positionMarker3: 0.6
352 // passing 3, we detach movement from the finger and snap to 4
353 property real positionMarker4: 0.9
354
355 // This is where the first app snaps to when bringing it in from the right edge.
356 property real snapPosition: 0.75
357
358 // Phase of the animation:
359 // 0: Starting from right edge, a new app (index 1) comes in from the right
360 // 1: The app has reached the first snap position.
361 // 2: The list is dragged further and snaps into the spread view when entering phase 2
362 property int phase: 0
363
364 property int selectedIndex: -1
365
366 onShiftedContentXChanged: {
367 switch (phase) {
368 case 0:
369 if (shiftedContentX > width * positionMarker2) {
370 phase = 1;
371 }
372 break;
373 case 1:
374 if (shiftedContentX < width * positionMarker2) {
375 phase = 0;
376 } else if (shiftedContentX >= width * positionMarker4) {
377 phase = 2;
378 }
379 break;
380 }
381 }
382
383 function snap() {
384 if (shiftedContentX < positionMarker1 * width) {
385 snapAnimation.targetContentX = -shift;
386 snapAnimation.start();
387 } else if (shiftedContentX < positionMarker2 * width) {
388 snapTo(1)
389 } else if (shiftedContentX < positionMarker3 * width) {
390 snapTo(1)
391 } else if (phase < 2){
392 // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation.
393 snapAnimation.targetContentX = width * positionMarker4 + 1 - shift;
394 snapAnimation.start();
395 }
396 }
397 function snapTo(index) {
398 spreadView.selectedIndex = index;
399 root.fullscreen = ApplicationManager.get(index).fullscreen;
400 snapAnimation.targetContentX = -shift;
401 snapAnimation.start();
402 }
403
404 SequentialAnimation {
405 id: snapAnimation
406 property int targetContentX: -spreadView.shift
407
408 UbuntuNumberAnimation {
409 target: spreadView
410 property: "contentX"
411 to: snapAnimation.targetContentX
412 duration: UbuntuAnimation.FastDuration
413 }
414
415 ScriptAction {
416 script: {
417 if (spreadView.selectedIndex >= 0) {
418 ApplicationManager.focusApplication(ApplicationManager.get(spreadView.selectedIndex).appId);
419 spreadView.selectedIndex = -1
420 spreadView.phase = 0;
421 spreadView.contentX = -spreadView.shift;
422 }
423 }
424 }
425 }
426
427 Item {
428 id: spreadRow
429 // This width controls how much the spread can be flicked left/right. It's composed of:
430 // tileDistance * app count (with a minimum of 3 apps, in order to also allow moving 1 and 2 apps a bit)
431 // + some constant value (still scales with the screen width) which looks good and somewhat fills the screen
432 width: Math.max(3, ApplicationManager.count) * spreadView.tileDistance + (spreadView.width - spreadView.tileDistance) * 1.5
433
434 x: spreadView.contentX
435
436 Repeater {
437 id: spreadRepeater
438 model: ApplicationManager
439 delegate: TransformedSpreadDelegate {
440 id: appDelegate
441 objectName: "appDelegate" + index
442 startAngle: 45
443 endAngle: 5
444 startScale: 1.1
445 endScale: 0.7
446 startDistance: spreadView.tileDistance
447 endDistance: units.gu(.5)
448 width: spreadView.width
449 height: spreadView.height
450 selected: spreadView.selectedIndex == index
451 otherSelected: spreadView.selectedIndex >= 0 && !selected
452
453 z: index
454 x: index == 0 ? 0 : spreadView.width + (index - 1) * spreadView.tileDistance
455
456 // Each tile has a different progress value running from 0 to 1.
457 // A progress value of 0 means the tile is at the right edge. 1 means the tile has reched the left edge.
458 progress: {
459 var tileProgress = (spreadView.shiftedContentX - index * spreadView.tileDistance) / spreadView.width;
460 // Tile 1 needs to move directly from the beginning...
461 if (index == 1 && spreadView.phase < 2) {
462 tileProgress += spreadView.tileDistance / spreadView.width;
463 }
464 return tileProgress;
465 }
466
467 // This mostly is the same as progress, just adds the snapping to phase 1 for tiles 0 and 1
468 animatedProgress: {
469 if (spreadView.phase == 0 && index < 2) {
470 if (progress < spreadView.positionMarker1) {
471 return progress;
472 } else if (progress < spreadView.positionMarker1 + snappingCurve.period){
473 return spreadView.positionMarker1 + snappingCurve.value * 3;
474 } else {
475 return spreadView.positionMarker2;
476 }
477 }
478 return progress;
479 }
480
481 EasingCurve {
482 id: snappingCurve
483 type: EasingCurve.OutQuad
484 period: 0.05
485 progress: appDelegate.progress - spreadView.positionMarker1
486 }
487
488 onClicked: {
489 if (spreadView.phase == 2) {
490 if (ApplicationManager.focusedApplicationId == ApplicationManager.get(index).appId) {
491 spreadView.snapTo(index);
492 } else {
493 ApplicationManager.requestFocusApplication(ApplicationManager.get(index).appId);
494 }
495 }
496 }
497 }
498 }
499 }
500 }
501}
0502
=== renamed file 'qml/SideStage/SidestageHandle.qml' => 'qml/Stages/SidestageHandle.qml'
=== added file 'qml/Stages/SpreadDelegate.qml'
--- qml/Stages/SpreadDelegate.qml 1970-01-01 00:00:00 +0000
+++ qml/Stages/SpreadDelegate.qml 2014-03-25 15:08:15 +0000
@@ -0,0 +1,55 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors: Michael Zanetti <michael.zanetti@canonical.com>
17*/
18
19import QtQuick 2.0
20
21Item {
22 id: root
23
24 signal clicked()
25
26 property real topMarginProgress
27
28 QtObject {
29 id: priv
30 property real heightDifference: root.height - appImage.implicitHeight
31 }
32
33 Image {
34 id: dropShadow
35 anchors.fill: appImage
36 anchors.margins: -units.gu(2)
37 source: "graphics/dropshadow.png"
38 opacity: .4
39 }
40 Image {
41 id: appImage
42 anchors {
43 left: parent.left;
44 bottom: parent.bottom;
45 top: parent.top;
46 topMargin: priv.heightDifference * Math.max(0, 1 - root.topMarginProgress)
47 }
48 source: model.screenshot
49 antialiasing: true
50 }
51 MouseArea {
52 anchors.fill: appImage
53 onClicked: root.clicked()
54 }
55}
056
=== added file 'qml/Stages/StageWithSideStage.qml'
--- qml/Stages/StageWithSideStage.qml 1970-01-01 00:00:00 +0000
+++ qml/Stages/StageWithSideStage.qml 2014-03-25 15:08:15 +0000
@@ -0,0 +1,405 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18import Ubuntu.Components 0.1
19import "../Components"
20import Unity.Application 0.1
21import Ubuntu.Gestures 0.1
22
23Item {
24 id: root
25 objectName: "stages"
26 anchors.fill: parent
27
28 // Controls to be set from outside
29 property bool shown: false
30 property bool moving: false
31 property int dragAreaWidth
32
33 // State information propagated to the outside
34 readonly property bool painting: mainStageImage.visible || sideStageImage.visible || sideStageSnapAnimation.running
35 property bool fullscreen: priv.focusedApplication ? priv.focusedApplication.fullscreen : false
36 property bool overlayMode: (sideStageImage.shown && priv.mainStageAppId.length == 0) || priv.overlayOverride
37 || (priv.mainStageAppId.length == 0 && sideStageSnapAnimation.running)
38
39 readonly property int overlayWidth: priv.overlayOverride ? 0 : priv.sideStageWidth
40
41 onShownChanged: {
42 if (!shown) {
43 priv.mainStageAppId = "";
44 }
45 }
46
47 onMovingChanged: {
48 if (moving) {
49 if (!priv.mainStageAppId && !priv.sideStageAppId) {
50 // Pulling in from the right, make the last used (topmost) app visible
51 var application = ApplicationManager.get(0);
52 if (application.stage == ApplicationInfoInterface.SideStage) {
53 sideStageImage.application = application;
54 sideStageImage.x = root.width - sideStageImage.width
55 sideStageImage.visible = true;
56 } else {
57 mainStageImage.application = application;
58 mainStageImage.visible = true;
59 }
60 } else {
61 priv.requestNewScreenshot(ApplicationInfoInterface.MainStage)
62 if (priv.focusedApplicationId == priv.sideStageAppId) {
63 priv.requestNewScreenshot(ApplicationInfoInterface.SideStage)
64 }
65 }
66 } else {
67 mainStageImage.visible = false;
68 sideStageImage.visible = false;
69 }
70 }
71
72 QtObject {
73 id: priv
74
75 property int sideStageWidth: units.gu(40)
76
77
78 property string sideStageAppId
79 property string mainStageAppId
80
81
82 property var sideStageApp: ApplicationManager.findApplication(sideStageAppId)
83 property var mainStageApp: ApplicationManager.findApplication(mainStageAppId)
84
85 property string sideStageScreenshot: sideStageApp ? sideStageApp.screenshot : ""
86 property string mainStageScreenshot: mainStageApp ? mainStageApp.screenshot : ""
87
88 property string focusedApplicationId: ApplicationManager.focusedApplicationId
89 property var focusedApplication: ApplicationManager.findApplication(focusedApplicationId)
90 property url focusedScreenshot: focusedApplication ? focusedApplication.screenshot : ""
91
92 property bool waitingForMainScreenshot: false
93 property bool waitingForSideScreenshot: false
94 property bool waitingForScreenshots: waitingForMainScreenshot || waitingForSideScreenshot
95
96 property string startingAppId: ""
97
98 // Keep overlayMode even if there is no focused app (to allow pulling in the sidestage from the right)
99 property bool overlayOverride: false
100
101 onFocusedApplicationChanged: {
102 if (focusedApplication) {
103 if (focusedApplication.stage == ApplicationInfoInterface.MainStage) {
104 mainStageAppId = focusedApplicationId;
105 priv.overlayOverride = false;
106 if (priv.startingAppId == focusedApplicationId && sideStageImage.shown) {
107 // There was already a sidestage app on top. bring it back!
108 ApplicationManager.focusApplication(priv.sideStageAppId)
109 priv.startingAppId = "";
110 }
111 } else if (focusedApplication.stage == ApplicationInfoInterface.SideStage) {
112 sideStageAppId = focusedApplicationId;
113 if (priv.startingAppId == focusedApplicationId && !sideStageImage.shown) {
114 sideStageImage.snapToApp(focusedApplication);
115 priv.startingAppId = "";
116 }
117 }
118 }
119 }
120
121 onMainStageScreenshotChanged: {
122 waitingForMainScreenshot = false;
123 }
124
125 onSideStageScreenshotChanged: {
126 waitingForSideScreenshot = false;
127 }
128
129 onFocusedScreenshotChanged: {
130 waitingForSideScreenshot = false;
131 }
132
133 onWaitingForScreenshotsChanged: {
134 if (waitingForScreenshots) {
135 return;
136 }
137
138 if (root.moving) {
139 if (mainStageAppId) {
140 mainStageImage.application = mainStageApp
141 mainStageImage.visible = true;
142 }
143 if (sideStageAppId && focusedApplicationId == sideStageAppId) {
144 sideStageImage.application = sideStageApp;
145 sideStageImage.x = root.width - sideStageImage.width
146 sideStageImage.visible = true;
147 }
148 }
149 if (sideStageHandleMouseArea.pressed) {
150 if (sideStageAppId) {
151 sideStageImage.application = sideStageApp;
152 sideStageImage.x = root.width - sideStageImage.width
153 sideStageImage.visible = true;
154 }
155 if (mainStageAppId) {
156 mainStageImage.application = mainStageApp
157 mainStageImage.visible = true;
158 }
159 }
160 }
161
162 function requestNewScreenshot(stage) {
163 if (stage == ApplicationInfoInterface.MainStage && mainStageAppId) {
164 waitingForMainScreenshot = true;
165 ApplicationManager.updateScreenshot(mainStageAppId);
166 } else if (stage == ApplicationInfoInterface.SideStage && sideStageAppId) {
167 waitingForSideScreenshot = true;
168 ApplicationManager.updateScreenshot(sideStageAppId);
169 }
170 }
171
172 }
173 // FIXME: the signal connection seems to get lost with the fake application manager.
174 Connections {
175 target: priv.sideStageApp
176 onScreenshotChanged: priv.sideStageScreenshot = priv.sideStageApp.screenshot
177 }
178 Connections {
179 target: priv.mainStageApp
180 onScreenshotChanged: priv.mainStageScreenshot = priv.mainStageApp.screenshot
181 }
182
183 Connections {
184 target: ApplicationManager
185
186 onApplicationAdded: {
187 priv.startingAppId = appId;
188 splashScreenTimer.start();
189 var application = ApplicationManager.findApplication(appId)
190 if (application.stage == ApplicationInfoInterface.SideStage) {
191 sideStageSplash.visible = true;
192 } else if (application.stage == ApplicationInfoInterface.MainStage) {
193 mainStageSplash.visible = true;
194 }
195 }
196
197 onFocusRequested: {
198 var application = ApplicationManager.findApplication(appId)
199 if (application.stage == ApplicationInfoInterface.SideStage) {
200 if (!root.shown) {
201 priv.mainStageAppId = "";
202 mainStageImage.application = null
203 }
204 if (sideStageImage.shown) {
205 sideStageImage.switchTo(application);
206 if (priv.mainStageAppId) {
207 mainStageImage.application = priv.mainStageApp;
208 mainStageImage.visible = true;
209 }
210 } else {
211 sideStageImage.application = application;
212 sideStageImage.snapToApp(application);
213 }
214 } else if (application.stage == ApplicationInfoInterface.MainStage) {
215 if (root.shown) {
216 if (sideStageImage.shown) {
217 sideStageImage.application = priv.sideStageApp;
218 sideStageImage.visible = true;
219 }
220 priv.mainStageAppId = application.appId;
221 mainStageImage.switchTo(application)
222 ApplicationManager.focusApplication(appId)
223 if (sideStageImage.shown) {
224 // There was already a focused SS app. Bring it back
225 ApplicationManager.focusApplication(priv.sideStageAppId)
226 }
227 } else {
228 if (sideStageImage.shown) {
229 sideStageImage.visible = false;
230 sideStageImage.x = root.width;
231 }
232
233 mainStageImage.application = application;
234 ApplicationManager.focusApplication(appId)
235 }
236 }
237 }
238
239 onApplicationRemoved: {
240 if (priv.mainStageAppId == appId) {
241 priv.mainStageAppId = "";
242 }
243 if (priv.sideStageAppId == appId) {
244 priv.sideStageAppId = "";
245 }
246 if (priv.sideStageAppId.length == 0) {
247 sideStageImage.shown = false;
248 priv.overlayOverride = false;
249 }
250 }
251
252 }
253
254 Timer {
255 id: splashScreenTimer
256 // FIXME: apart from removing this completely in the future and make the app surface paint
257 // meaningful stuff, also check for colin's stuff to land so we can shape 1.4 secs away from here
258 // https://code.launchpad.net/~cjwatson/upstart-app-launch/libclick-manifest/+merge/210520
259 // https://code.launchpad.net/~cjwatson/upstart-app-launch/libclick-pkgdir/+merge/209909
260 interval: 1700
261 repeat: false
262 onTriggered: {
263 mainStageSplash.visible = false;
264 sideStageSplash.visible = false;
265 }
266 }
267
268 SwitchingApplicationImage {
269 id: mainStageImage
270 anchors.bottom: parent.bottom
271 width: parent.width
272 visible: false
273
274 onSwitched: {
275 sideStageImage.visible = false;
276 }
277 }
278
279 Rectangle {
280 id: mainStageSplash
281 anchors.fill: root
282 anchors.rightMargin: root.width - sideStageImage.x
283 color: "white"
284 }
285
286 SidestageHandle {
287 id: sideStageHandle
288 anchors { top: parent.top; right: sideStageImage.left; bottom: parent.bottom }
289 width: root.dragAreaWidth
290 visible: root.shown && priv.sideStageAppId && sideStageImage.x < root.width
291
292 }
293 MouseArea {
294 id: sideStageHandleMouseArea
295 anchors { top: parent.top; right: parent.right; bottom: parent.bottom; rightMargin: sideStageImage.shown ? sideStageImage.width : 0}
296 width: root.dragAreaWidth
297 visible: priv.sideStageAppId
298
299 property var dragPoints: new Array()
300
301 onPressed: {
302 priv.requestNewScreenshot(ApplicationInfoInterface.SideStage)
303 if (priv.mainStageAppId) {
304 priv.requestNewScreenshot(ApplicationInfoInterface.MainStage)
305 }
306 }
307
308 onMouseXChanged: {
309 dragPoints.push(mouseX)
310
311 var dragPoint = root.width + mouseX;
312 if (sideStageImage.shown) {
313 dragPoint -= sideStageImage.width
314 }
315 sideStageImage.x = Math.max(root.width - sideStageImage.width, dragPoint)
316 }
317
318 onReleased: {
319 var distance = 0;
320 var lastX = dragPoints[0];
321 var oneWayFlick = true;
322 for (var i = 0; i < dragPoints.length; ++i) {
323 if (dragPoints[i] < lastX) {
324 oneWayFlick = false;
325 }
326 distance += dragPoints[i] - lastX;
327 lastX = dragPoints[i];
328 }
329 dragPoints = [];
330
331 if (oneWayFlick || distance > sideStageImage.width / 2) {
332 sideStageImage.snapTo(root.width)
333 } else {
334 sideStageImage.snapToApp(priv.sideStageApp)
335 }
336 }
337 }
338
339 SwitchingApplicationImage {
340 id: sideStageImage
341 width: priv.sideStageWidth
342 height: root.height
343 x: root.width
344 anchors.bottom: parent.bottom
345 visible: true
346 property bool shown: false
347
348 onSwitched: {
349 mainStageImage.visible = false;
350 ApplicationManager.focusApplication(application.appId)
351 }
352
353 function snapTo(targetX) {
354 sideStageSnapAnimation.targetX = targetX
355 sideStageImage.visible = true;
356 if (priv.mainStageAppId) {
357 mainStageImage.application = priv.mainStageApp
358 mainStageImage.visible = true;
359 }
360 sideStageSnapAnimation.start();
361 }
362
363 function snapToApp(application) {
364 sideStageImage.application = application
365 sideStageSnapAnimation.snapToId = application.appId;
366 snapTo(root.width - sideStageImage.width);
367 }
368
369 SequentialAnimation {
370 id: sideStageSnapAnimation
371 property int targetX: root.width
372 property string snapToId
373
374 UbuntuNumberAnimation { target: sideStageImage; property: "x"; to: sideStageSnapAnimation.targetX; duration: UbuntuAnimation.SlowDuration }
375 ScriptAction {
376 script: {
377 if (sideStageSnapAnimation.targetX == root.width) {
378 if (priv.mainStageAppId) {
379 ApplicationManager.focusApplication(priv.mainStageAppId)
380 } else {
381 priv.overlayOverride = true;
382 ApplicationManager.unfocusCurrentApplication();
383 }
384 sideStageImage.shown = false;
385 }
386 if (sideStageSnapAnimation.snapToId) {
387 ApplicationManager.focusApplication(sideStageSnapAnimation.snapToId)
388 sideStageSnapAnimation.snapToId = "";
389 sideStageImage.shown = true;
390 priv.overlayOverride = false;
391 }
392 sideStageImage.visible = false;
393 mainStageImage.visible = false;
394 }
395 }
396 }
397 }
398
399 Rectangle {
400 id: sideStageSplash
401 anchors.fill: parent
402 anchors.leftMargin: sideStageImage.x
403 color: "white"
404 }
405}
0406
=== added file 'qml/Stages/SwitchingApplicationImage.qml'
--- qml/Stages/SwitchingApplicationImage.qml 1970-01-01 00:00:00 +0000
+++ qml/Stages/SwitchingApplicationImage.qml 2014-03-25 15:08:15 +0000
@@ -0,0 +1,81 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors: Michael Zanetti <michael.zanetti@canonical.com>
17*/
18
19import QtQuick 2.0
20import Ubuntu.Components 0.1
21
22Rectangle {
23 id: root
24 implicitHeight: image.implicitHeight
25 implicitWidth: image.implicitWidth
26 color: "black"
27
28 property var application
29
30 signal switched()
31
32 function switchTo(application) {
33 if (root.application == application) {
34 root.switched();
35 return;
36 }
37
38 priv.newApplication = application
39 root.visible = true;
40 switchToAnimation.start()
41 }
42
43 QtObject {
44 id: priv
45 property var newApplication
46 }
47
48 Image {
49 id: newImage
50 anchors.bottom: parent.bottom
51 width: root.width
52 source: priv.newApplication ? priv.newApplication.screenshot : ""
53 }
54
55 Image {
56 id: image
57 visible: true
58 source: root.application ? root.application.screenshot : ""
59 width: root.width
60 height: sourceSize.height
61 anchors.bottom: parent.bottom
62
63 }
64
65 SequentialAnimation {
66 id: switchToAnimation
67 ParallelAnimation {
68 UbuntuNumberAnimation { target: image; property: "x"; from: 0; to: root.width; duration: UbuntuAnimation.SlowDuration }
69 UbuntuNumberAnimation { target: newImage; property: "scale"; from: 0.7; to: 1; duration: UbuntuAnimation.SlowDuration }
70 }
71 ScriptAction {
72 script: {
73 image.x = 0
74 root.application = priv.newApplication
75 root.visible = false;
76 priv.newApplication = null
77 root.switched();
78 }
79 }
80 }
81}
082
=== added file 'qml/Stages/TransformedSpreadDelegate.qml'
--- qml/Stages/TransformedSpreadDelegate.qml 1970-01-01 00:00:00 +0000
+++ qml/Stages/TransformedSpreadDelegate.qml 2014-03-25 15:08:15 +0000
@@ -0,0 +1,327 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors: Michael Zanetti <michael.zanetti@canonical.com>
17*/
18
19import QtQuick 2.0
20import Utils 0.1
21import Ubuntu.Components 0.1
22
23SpreadDelegate {
24 id: root
25
26 property bool selected: false
27 property bool otherSelected: false
28
29 // The progress animates the tiles. A value > 0 makes it appear from the right edge. At 1 it reaches the end position.
30 property real progress: 0
31 // This is required to snap tile 1 during phase 1 and 2.
32 property real animatedProgress: 0
33
34 property real startAngle: 0
35 property real endAngle: 0
36
37 property real startScale: 1
38 property real endScale: 1
39
40 // Specific to just one tile
41 property real tile1StartScale: startScale + .4
42 property real tile0SnapAngle: 10
43
44 property real startDistance: units.gu(5)
45 property real endDistance: units.gu(.5)
46
47 onSelectedChanged: {
48 if (selected) {
49 priv.snapshot();
50 }
51 priv.isSelected = selected;
52 }
53
54 onOtherSelectedChanged: {
55 if (otherSelected) {
56 priv.snapshot();
57 }
58 priv.otherSelected = otherSelected;
59 }
60
61 Connections {
62 target: spreadView
63 onPhaseChanged: {
64 if (spreadView.phase == 1) {
65 if (index == 0) {
66 priv.phase2startTranslate = priv.easingAnimation(0, spreadView.positionMarker4, 0, -spreadView.width, spreadView.positionMarker4) + spreadView.width;
67 priv.phase2startAngle = priv.easingAnimation(0, spreadView.positionMarker4, root.startAngle, root.endAngle, spreadView.positionMarker4);
68 priv.phase2startScale = priv.easingAnimation(0, spreadView.positionMarker4, root.startScale, root.endScale, spreadView.positionMarker4);
69 priv.phase2startTopMarginProgress = priv.easingAnimation(0, 1, 0, 1, spreadView.positionMarker4);
70 } else if (index == 1) {
71 // find where the main easing for Tile 1 would be when reaching phase 2
72 var phase2Progress = spreadView.positionMarker4 - spreadView.tileDistance / spreadView.width;
73 priv.phase2startTranslate = priv.easingAnimation(0, phase2Progress, 0, -spreadView.width + root.endDistance, phase2Progress);
74 priv.phase2startAngle = priv.easingAnimation(0, phase2Progress, root.startAngle, root.endAngle, phase2Progress);
75 priv.phase2startScale = priv.easingAnimation(0, phase2Progress, root.startScale, root.endScale, phase2Progress);
76 priv.phase2startTopMarginProgress = priv.easingAnimation(0, 1, 0, spreadView.positionMarker4, phase2Progress);
77 }
78 }
79 }
80 }
81
82 QtObject {
83 id: priv
84 property bool isSelected: false
85 property bool otherSelected: false
86 property real selectedProgress
87 property real selectedXTranslate
88 property real selectedAngle
89 property real selectedScale
90 property real selectedOpacity
91 property real selectedTopMarginProgress
92
93 // Those values are needed as target values for the end of phase 1.
94 // As they are static values, lets calculate them once when entering phase 1 instead of calculating them in each animation pass.
95 property real phase2startTranslate
96 property real phase2startAngle
97 property real phase2startScale
98 property real phase2startTopMarginProgress
99
100 function snapshot() {
101 selectedProgress = root.progress;
102 selectedXTranslate = xTranslate;
103 selectedAngle = angle;
104 selectedScale = scale;
105 selectedOpacity = opacity;
106 selectedTopMarginProgress = topMarginProgress;
107 }
108
109 // This calculates how much negative progress there can be if unwinding the spread completely
110 // the progress for each tile starts at 0 when it crosses the right edge, so the later a tile comes in,
111 // the bigger its negativeProgress can be.
112 property real negativeProgress: {
113 if (index == 1 && spreadView.phase < 2) {
114 return 0;
115 }
116 return -index * spreadView.tileDistance / spreadView.width;
117 }
118
119 function linearAnimation(startProgress, endProgress, startValue, endValue, progress) {
120 // progress : progressDiff = value : valueDiff => value = progress * valueDiff / progressDiff
121 return (progress - startProgress) * (endValue - startValue) / (endProgress - startProgress) + startValue;
122 }
123
124 function easingAnimation(startProgress, endProgress, startValue, endValue, progress) {
125 helperEasingCurve.progress = progress - startProgress;
126 helperEasingCurve.period = endProgress - startProgress;
127 return helperEasingCurve.value * (endValue - startValue) + startValue;
128 }
129
130 property real animatedEndDistance: linearAnimation(0, 2, root.endDistance, 0, root.progress)
131
132 // The following blocks handle the animation of the tile in the spread.
133 // At the beginning, each tile is attached at the right edge, outside the screen.
134 // The progress for each tile starts at 0 and it reaches its end position at a progress of 1.
135 // The first phases are handled special for the first 2 tiles. as we do the alt-tab and snapping in there
136 // Once we reached phase 3, the animation is the same for all tiles.
137 // When a tile is selected, the animation state is snapshotted, and the spreadView is unwound (progress animates
138 // back to negativeProgress). All tiles are kept in place and faded out to 0 opacity except
139 // the selected tile, which is animated from the snapshotted position to be fullscreen.
140
141 property real xTranslate: {
142 if (otherSelected) {
143 if (spreadView.phase < 2 && index == 0) {
144 return linearAnimation(selectedProgress, negativeProgress,
145 selectedXTranslate, selectedXTranslate - spreadView.tileDistance, root.progress);
146 }
147
148 return selectedXTranslate;
149 }
150
151 switch (index) {
152 case 0:
153 if (spreadView.phase == 0) {
154 return Math.min(0, linearAnimation(0, spreadView.positionMarker2,
155 0, -spreadView.width * .25, root.animatedProgress));
156 } else if (spreadView.phase == 1){
157 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
158 -spreadView.width * .25, priv.phase2startTranslate, root.progress);
159 } else if (!priv.isSelected){ // phase 2
160 // Apply the same animation as with the rest but add spreadView.width to align it with the others.
161 return -easingCurve.value * spreadView.width + spreadView.width;
162 } else if (priv.isSelected) {
163 return linearAnimation(selectedProgress, negativeProgress, selectedXTranslate, 0, root.progress);
164 }
165
166 case 1:
167 if (spreadView.phase == 0 && !priv.isSelected) {
168 return linearAnimation(0, spreadView.positionMarker2,
169 0, -spreadView.width * spreadView.snapPosition, root.animatedProgress);
170 } else if (spreadView.phase == 1 && !priv.isSelected) {
171 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
172 -spreadView.width * spreadView.snapPosition, priv.phase2startTranslate,
173 root.progress);
174 }
175 }
176
177 if (priv.isSelected) {
178 // Distance to left edge
179 var targetTranslate = -spreadView.width - ((index - 1) * root.startDistance);
180 return linearAnimation(selectedProgress, negativeProgress,
181 selectedXTranslate, targetTranslate, root.progress);
182 }
183
184 // Fix it at the right edge...
185 var rightEdgeOffset = -((index - 1) * root.startDistance);
186 // ...and use our easing to move them to the left. Stop a bit earlier for each tile
187 return -easingCurve.value * spreadView.width + (index * animatedEndDistance) + rightEdgeOffset;
188
189 }
190
191 property real angle: {
192 if (priv.otherSelected) {
193 return priv.selectedAngle;
194 }
195 if (priv.isSelected) {
196 return linearAnimation(selectedProgress, negativeProgress, selectedAngle, 0, root.progress);
197 }
198 switch (index) {
199 case 0:
200 if (spreadView.phase == 0) {
201 return Math.max(0, linearAnimation(0, spreadView.positionMarker2,
202 0, root.tile0SnapAngle, root.animatedProgress));
203 } else if (spreadView.phase == 1) {
204 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
205 root.tile0SnapAngle, phase2startAngle, root.progress);
206 }
207 case 1:
208 if (spreadView.phase == 0) {
209 return linearAnimation(0, spreadView.positionMarker2, root.startAngle,
210 root.startAngle * (1-spreadView.snapPosition), root.animatedProgress);
211 } else if (spreadView.phase == 1) {
212 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
213 root.startAngle * (1-spreadView.snapPosition), priv.phase2startAngle,
214 root.progress);
215 }
216 }
217 return root.startAngle - easingCurve.value * (root.startAngle - root.endAngle);
218 }
219
220 property real scale: {
221 if (priv.otherSelected) {
222 return priv.selectedScale;
223 }
224 if (priv.isSelected) {
225 return linearAnimation(selectedProgress, negativeProgress, selectedScale, 1, root.progress);
226 }
227
228 switch (index) {
229 case 0:
230 if (spreadView.phase == 0) {
231 return 1;
232 } else if (spreadView.phase == 1) {
233 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
234 1, phase2startScale, root.progress);
235 }
236 case 1:
237 if (spreadView.phase == 0) {
238 var targetScale = tile1StartScale - ((tile1StartScale - 1) * spreadView.snapPosition);
239 return linearAnimation(0, spreadView.positionMarker2,
240 root.tile1StartScale, targetScale, root.animatedProgress);
241 } else if (spreadView.phase == 1) {
242 var startScale = tile1StartScale - ((tile1StartScale - 1) * spreadView.snapPosition);
243 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
244 startScale, priv.phase2startScale, root.progress);
245 }
246 }
247 return root.startScale - easingCurve.value * (root.startScale - root.endScale);
248 }
249
250 property real opacity: {
251 if (priv.otherSelected) {
252 return linearAnimation (selectedProgress, Math.max(0, selectedProgress - .5),
253 selectedOpacity, 0, root.progress);
254 }
255 if (index == 0) {
256 switch (spreadView.phase) {
257 case 0:
258 return linearAnimation(0, spreadView.positionMarker2, 1, .3, root.animatedProgress);
259 case 1:
260 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
261 .3, 1, root.animatedProgress);
262 }
263 }
264
265 return 1;
266 }
267
268 property real topMarginProgress: {
269 if (priv.isSelected) {
270 return linearAnimation(selectedProgress, negativeProgress, selectedTopMarginProgress, 0, root.progress);
271 }
272
273 switch (index) {
274 case 0:
275 if (spreadView.phase == 0) {
276 return 0;
277 } else if (spreadView.phase == 1) {
278 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
279 0, priv.phase2startTopMarginProgress, root.progress);
280 }
281 break;
282 case 1:
283 if (spreadView.phase == 0) {
284 return 0;
285 } else if (spreadView.phase == 1) {
286 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
287 0, priv.phase2startTopMarginProgress, root.progress);
288 }
289 }
290
291 return easingCurve.value;
292 }
293 }
294
295 transform: [
296 Rotation {
297 origin { x: 0; y: spreadView.height / 2 }
298 axis { x: 0; y: 1; z: 0 }
299 angle: priv.angle
300 },
301 Scale {
302 origin { x: 0; y: spreadView.height / 2 }
303 xScale: priv.scale
304 yScale: xScale
305 },
306 Translate {
307 x: priv.xTranslate
308 }
309 ]
310 opacity: priv.opacity
311 topMarginProgress: priv.topMarginProgress
312
313 EasingCurve {
314 id: easingCurve
315 type: EasingCurve.OutSine
316 period: 1 - spreadView.positionMarker2
317 progress: root.animatedProgress
318 }
319
320 // This is used as a calculation helper to figure values for progress other than the current one
321 // Do not bind anything to this...
322 EasingCurve {
323 id: helperEasingCurve
324 type: easingCurve.type
325 period: easingCurve.period
326 }
327}
0328
=== added directory 'qml/Stages/graphics'
=== added file 'qml/Stages/graphics/dropshadow.png'
1Binary files qml/Stages/graphics/dropshadow.png 1970-01-01 00:00:00 +0000 and qml/Stages/graphics/dropshadow.png 2014-03-25 15:08:15 +0000 differ329Binary files qml/Stages/graphics/dropshadow.png 1970-01-01 00:00:00 +0000 and qml/Stages/graphics/dropshadow.png 2014-03-25 15:08:15 +0000 differ
=== renamed file 'qml/SideStage/graphics/sidestage_handle@20.png' => 'qml/Stages/graphics/sidestage_handle@20.png'
=== renamed file 'qml/SideStage/graphics/sidestage_handle@20.sci' => 'qml/Stages/graphics/sidestage_handle@20.sci'
=== modified file 'run_on_device'
--- run_on_device 2013-11-27 11:47:14 +0000
+++ run_on_device 2014-03-25 15:08:15 +0000
@@ -103,6 +103,7 @@
103 ARGS="$ARGS -k"103 ARGS="$ARGS -k"
104 fi104 fi
105105
106 exec_with_ssh "killall -9 dialer-app; killall -9 gallery-app; killall -9 messaging-app; killall -9 address-book-app; killall -9 webbrowser-app"
106 exec_with_ssh "stop unity8"107 exec_with_ssh "stop unity8"
107 exec_with_ssh "start maliit-server"108 exec_with_ssh "start maliit-server"
108 exec_with_ssh "cd $CODE_DIR/ && ./run $ARGS -- $RUN_OPTIONS"109 exec_with_ssh "cd $CODE_DIR/ && ./run $ARGS -- $RUN_OPTIONS"
109110
=== modified file 'tests/autopilot/unity8/shell/emulators/dash.py'
--- tests/autopilot/unity8/shell/emulators/dash.py 2014-03-17 10:42:57 +0000
+++ tests/autopilot/unity8/shell/emulators/dash.py 2014-03-25 15:08:15 +0000
@@ -80,10 +80,7 @@
80 'No scope found with id {0}'.format(scope_id))80 'No scope found with id {0}'.format(scope_id))
8181
82 def _get_scope_from_loader(self, loader):82 def _get_scope_from_loader(self, loader):
83 if loader.scopeId == 'clickscope':83 return loader.select_single(GenericScopeView)
84 return loader.select_single(DashApps)
85 else:
86 return loader.select_single(GenericScopeView)
8784
88 def _open_scope_scrolling(self, scope_loader):85 def _open_scope_scrolling(self, scope_loader):
89 scroll = self._get_scroll_direction(scope_loader)86 scroll = self._get_scroll_direction(scope_loader)
@@ -164,10 +161,6 @@
164 raise emulators.UnityEmulatorException(161 raise emulators.UnityEmulatorException(
165 'No category found with name {}'.format(category))162 'No category found with name {}'.format(category))
166163
167
168class DashApps(GenericScopeView):
169 """Autopilot emulator for the applications scope."""
170
171 def get_applications(self, category):164 def get_applications(self, category):
172 """Return the list of applications on a category.165 """Return the list of applications on a category.
173166
174167
=== modified file 'tests/autopilot/unity8/shell/emulators/main_window.py'
--- tests/autopilot/unity8/shell/emulators/main_window.py 2014-03-02 04:44:30 +0000
+++ tests/autopilot/unity8/shell/emulators/main_window.py 2014-03-25 15:08:15 +0000
@@ -112,4 +112,4 @@
112112
113 def get_current_focused_app_id(self):113 def get_current_focused_app_id(self):
114 """Return the id of the focused application."""114 """Return the id of the focused application."""
115 return self.select_single('Shell').currentFocusedAppId115 return self.select_single('Shell').focusedApplicationId
116116
=== modified file 'tests/autopilot/unity8/shell/tests/test_emulators.py'
--- tests/autopilot/unity8/shell/tests/test_emulators.py 2014-03-17 10:42:57 +0000
+++ tests/autopilot/unity8/shell/tests/test_emulators.py 2014-03-25 15:08:15 +0000
@@ -128,7 +128,7 @@
128 scope_id = 'clickscope'128 scope_id = 'clickscope'
129 scope = self.dash.open_scope(scope_id)129 scope = self.dash.open_scope(scope_id)
130 self._assert_scope_is_opened(scope, scope_id)130 self._assert_scope_is_opened(scope, scope_id)
131 self.assertIsInstance(scope, dash_emulators.DashApps)131 self.assertIsInstance(scope, dash_emulators.GenericScopeView)
132132
133133
134class GenericScopeViewEmulatorTestCase(DashBaseTestCase):134class GenericScopeViewEmulatorTestCase(DashBaseTestCase):
135135
=== modified file 'tests/mocks/Unity/Application/ApplicationInfo.cpp'
--- tests/mocks/Unity/Application/ApplicationInfo.cpp 2013-09-05 12:21:35 +0000
+++ tests/mocks/Unity/Application/ApplicationInfo.cpp 2014-03-25 15:08:15 +0000
@@ -20,6 +20,7 @@
20#include <QQuickItem>20#include <QQuickItem>
21#include <QQuickView>21#include <QQuickView>
22#include <QQmlComponent>22#include <QQmlComponent>
23#include <QTimer>
2324
24ApplicationInfo::ApplicationInfo(const QString &appId, QObject *parent)25ApplicationInfo::ApplicationInfo(const QString &appId, QObject *parent)
25 : ApplicationInfoInterface(appId, parent)26 : ApplicationInfoInterface(appId, parent)
@@ -32,6 +33,7 @@
32 ,m_windowComponent(0)33 ,m_windowComponent(0)
33 ,m_parentItem(0)34 ,m_parentItem(0)
34{35{
36 QTimer::singleShot(300, this, SLOT(setRunning()));
35}37}
3638
37ApplicationInfo::ApplicationInfo(QObject *parent)39ApplicationInfo::ApplicationInfo(QObject *parent)
@@ -44,6 +46,7 @@
44 ,m_windowComponent(0)46 ,m_windowComponent(0)
45 ,m_parentItem(0)47 ,m_parentItem(0)
46{48{
49 QTimer::singleShot(300, this, SLOT(setRunning()));
47}50}
4851
49void ApplicationInfo::onWindowComponentStatusChanged(QQmlComponent::Status status)52void ApplicationInfo::onWindowComponentStatusChanged(QQmlComponent::Status status)
@@ -52,6 +55,12 @@
52 doCreateWindowItem();55 doCreateWindowItem();
53}56}
5457
58void ApplicationInfo::setRunning()
59{
60 m_state = Running;
61 Q_EMIT stateChanged();
62}
63
55void ApplicationInfo::createWindowComponent()64void ApplicationInfo::createWindowComponent()
56{65{
57 // The assumptions I make here really should hold.66 // The assumptions I make here really should hold.
5867
=== modified file 'tests/mocks/Unity/Application/ApplicationInfo.h'
--- tests/mocks/Unity/Application/ApplicationInfo.h 2013-10-11 11:37:04 +0000
+++ tests/mocks/Unity/Application/ApplicationInfo.h 2014-03-25 15:08:15 +0000
@@ -74,6 +74,7 @@
74 IMPLEMENT_PROPERTY(fullscreen, Fullscreen, bool)74 IMPLEMENT_PROPERTY(fullscreen, Fullscreen, bool)
75 IMPLEMENT_PROPERTY(imageQml, ImageQml, QString)75 IMPLEMENT_PROPERTY(imageQml, ImageQml, QString)
76 IMPLEMENT_PROPERTY(windowQml, WindowQml, QString)76 IMPLEMENT_PROPERTY(windowQml, WindowQml, QString)
77 IMPLEMENT_PROPERTY(screenshot, Screenshot, QUrl)
7778
78 #undef IMPLEMENT_PROPERTY79 #undef IMPLEMENT_PROPERTY
7980
@@ -83,6 +84,7 @@
8384
84 private Q_SLOTS:85 private Q_SLOTS:
85 void onWindowComponentStatusChanged(QQmlComponent::Status status);86 void onWindowComponentStatusChanged(QQmlComponent::Status status);
87 void setRunning();
8688
87 private:89 private:
88 void createWindowItem();90 void createWindowItem();
8991
=== modified file 'tests/mocks/Unity/Application/ApplicationManager.cpp'
--- tests/mocks/Unity/Application/ApplicationManager.cpp 2013-12-17 16:04:47 +0000
+++ tests/mocks/Unity/Application/ApplicationManager.cpp 2014-03-25 15:08:15 +0000
@@ -24,13 +24,17 @@
24#include <QQuickItem>24#include <QQuickItem>
25#include <QQuickView>25#include <QQuickView>
26#include <QQmlComponent>26#include <QQmlComponent>
27#include <QTimer>
28#include <QDateTime>
2729
28ApplicationManager::ApplicationManager(QObject *parent)30ApplicationManager::ApplicationManager(QObject *parent)
29 : ApplicationManagerInterface(parent)31 : ApplicationManagerInterface(parent)
32 , m_suspended(false)
30 , m_mainStageComponent(0)33 , m_mainStageComponent(0)
31 , m_mainStage(0)34 , m_mainStage(0)
32 , m_sideStageComponent(0)35 , m_sideStageComponent(0)
33 , m_sideStage(0)36 , m_sideStage(0)
37 , m_rightMargin(0)
34{38{
35 buildListOfAvailableApplications();39 buildListOfAvailableApplications();
36}40}
@@ -63,6 +67,8 @@
63 return app->state();67 return app->state();
64 case RoleFocused:68 case RoleFocused:
65 return app->focused();69 return app->focused();
70 case RoleScreenshot:
71 return app->screenshot();
66 default:72 default:
67 return QVariant();73 return QVariant();
68 }74 }
@@ -84,13 +90,16 @@
84}90}
8591
86void ApplicationManager::add(ApplicationInfo *application) {92void ApplicationManager::add(ApplicationInfo *application) {
87 if (!application)93 if (!application) {
88 return;94 return;
95 }
8996
90 beginInsertRows(QModelIndex(), m_runningApplications.size(), m_runningApplications.size());97 beginInsertRows(QModelIndex(), m_runningApplications.size(), m_runningApplications.size());
91 m_runningApplications.append(application);98 m_runningApplications.append(application);
92 endInsertRows();99 endInsertRows();
100 Q_EMIT applicationAdded(application->appId());
93 Q_EMIT countChanged();101 Q_EMIT countChanged();
102 Q_EMIT focusRequested(application->appId());
94}103}
95104
96void ApplicationManager::remove(ApplicationInfo *application) {105void ApplicationManager::remove(ApplicationInfo *application) {
@@ -99,6 +108,7 @@
99 beginRemoveRows(QModelIndex(), i, i);108 beginRemoveRows(QModelIndex(), i, i);
100 m_runningApplications.removeAt(i);109 m_runningApplications.removeAt(i);
101 endRemoveRows();110 endRemoveRows();
111 Q_EMIT applicationRemoved(application->appId());
102 Q_EMIT countChanged();112 Q_EMIT countChanged();
103 }113 }
104}114}
@@ -171,6 +181,8 @@
171 }181 }
172 add(application);182 add(application);
173183
184 QMetaObject::invokeMethod(this, "focusApplication", Qt::QueuedConnection, Q_ARG(QString, appId));
185
174 return application;186 return application;
175}187}
176188
@@ -180,8 +192,32 @@
180 if (application == nullptr)192 if (application == nullptr)
181 return false;193 return false;
182194
195 if (application->appId() == focusedApplicationId()) {
196 unfocusCurrentApplication();
197 }
183 remove(application);198 remove(application);
184 Q_EMIT focusedApplicationIdChanged();199 return true;
200}
201
202bool ApplicationManager::updateScreenshot(const QString &appId)
203{
204 int idx = -1;
205 ApplicationInfo *application = nullptr;
206 for (int i = 0; i < m_availableApplications.count(); ++i) {
207 application = m_availableApplications.at(i);
208 if (application->appId() == appId) {
209 idx = i;
210 break;
211 }
212 }
213
214 if (idx == -1) {
215 return false;
216 }
217
218 application->setScreenshot(QString("image://application/%1/%2").arg(appId).arg(QDateTime::currentMSecsSinceEpoch()));
219 QModelIndex appIndex = index(idx);
220 Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << RoleScreenshot);
185 return true;221 return true;
186}222}
187223
@@ -194,6 +230,25 @@
194 return QString();230 return QString();
195}231}
196232
233bool ApplicationManager::suspended() const
234{
235 return m_suspended;
236}
237
238void ApplicationManager::setSuspended(bool suspended)
239{
240 ApplicationInfo *focusedApp = findApplication(focusedApplicationId());
241 if (focusedApp) {
242 if (suspended) {
243 focusedApp->setState(ApplicationInfo::Suspended);
244 } else {
245 focusedApp->setState(ApplicationInfo::Running);
246 }
247 }
248 m_suspended = suspended;
249 Q_EMIT suspendedChanged();
250}
251
197bool ApplicationManager::focusApplication(const QString &appId)252bool ApplicationManager::focusApplication(const QString &appId)
198{253{
199 ApplicationInfo *application = findApplication(appId);254 ApplicationInfo *application = findApplication(appId);
@@ -242,6 +297,15 @@
242 return true;297 return true;
243}298}
244299
300bool ApplicationManager::requestFocusApplication(const QString &appId)
301{
302 if (appId != focusedApplicationId()) {
303 QMetaObject::invokeMethod(this, "focusRequested", Qt::QueuedConnection, Q_ARG(QString, appId));
304 return true;
305 }
306 return false;
307}
308
245void ApplicationManager::unfocusCurrentApplication()309void ApplicationManager::unfocusCurrentApplication()
246{310{
247 for (ApplicationInfo *app : m_runningApplications) {311 for (ApplicationInfo *app : m_runningApplications) {
@@ -270,10 +334,12 @@
270 "Image {\n"334 "Image {\n"
271 " anchors.fill: parent\n"335 " anchors.fill: parent\n"
272 " anchors.topMargin: %1\n"336 " anchors.topMargin: %1\n"
273 " source: \"file://%2/Dash/graphics/phone/screenshots/%3.png\"\n"337 " anchors.rightMargin: %2\n"
338 " source: \"file://%3/Dash/graphics/phone/screenshots/%4.png\"\n"
274 " smooth: true\n"339 " smooth: true\n"
275 " fillMode: Image.PreserveAspectCrop\n"340 " fillMode: Image.PreserveAspectCrop\n"
276 "}").arg(topMargin)341 "}").arg(topMargin)
342 .arg(m_rightMargin)
277 .arg(qmlDirectory())343 .arg(qmlDirectory())
278 .arg(application->icon().toString());344 .arg(application->icon().toString());
279 application->setWindowQml(windowQml);345 application->setWindowQml(windowQml);
@@ -299,6 +365,7 @@
299 application->setName("Phone");365 application->setName("Phone");
300 application->setIcon(QUrl("phone"));366 application->setIcon(QUrl("phone"));
301 application->setStage(ApplicationInfo::SideStage);367 application->setStage(ApplicationInfo::SideStage);
368 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
302 generateQmlStrings(application);369 generateQmlStrings(application);
303 m_availableApplications.append(application);370 m_availableApplications.append(application);
304371
@@ -307,6 +374,7 @@
307 application->setName("Camera");374 application->setName("Camera");
308 application->setIcon(QUrl("camera"));375 application->setIcon(QUrl("camera"));
309 application->setFullscreen(true);376 application->setFullscreen(true);
377 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
310 generateQmlStrings(application);378 generateQmlStrings(application);
311 m_availableApplications.append(application);379 m_availableApplications.append(application);
312380
@@ -314,6 +382,7 @@
314 application->setAppId("gallery-app");382 application->setAppId("gallery-app");
315 application->setName("Gallery");383 application->setName("Gallery");
316 application->setIcon(QUrl("gallery"));384 application->setIcon(QUrl("gallery"));
385 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
317 generateQmlStrings(application);386 generateQmlStrings(application);
318 m_availableApplications.append(application);387 m_availableApplications.append(application);
319388
@@ -322,13 +391,16 @@
322 application->setName("Facebook");391 application->setName("Facebook");
323 application->setIcon(QUrl("facebook"));392 application->setIcon(QUrl("facebook"));
324 application->setStage(ApplicationInfo::SideStage);393 application->setStage(ApplicationInfo::SideStage);
394 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
325 generateQmlStrings(application);395 generateQmlStrings(application);
326 m_availableApplications.append(application);396 m_availableApplications.append(application);
327397
328 application = new ApplicationInfo(this);398 application = new ApplicationInfo(this);
329 application->setAppId("webbrowser-app");399 application->setAppId("webbrowser-app");
400 application->setFullscreen(true);
330 application->setName("Browser");401 application->setName("Browser");
331 application->setIcon(QUrl("browser"));402 application->setIcon(QUrl("browser"));
403 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
332 generateQmlStrings(application);404 generateQmlStrings(application);
333 m_availableApplications.append(application);405 m_availableApplications.append(application);
334406
@@ -337,6 +409,7 @@
337 application->setName("Twitter");409 application->setName("Twitter");
338 application->setIcon(QUrl("twitter"));410 application->setIcon(QUrl("twitter"));
339 application->setStage(ApplicationInfo::SideStage);411 application->setStage(ApplicationInfo::SideStage);
412 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
340 generateQmlStrings(application);413 generateQmlStrings(application);
341 m_availableApplications.append(application);414 m_availableApplications.append(application);
342415
@@ -344,6 +417,7 @@
344 application->setAppId("gmail-webapp");417 application->setAppId("gmail-webapp");
345 application->setName("GMail");418 application->setName("GMail");
346 application->setIcon(QUrl("gmail"));419 application->setIcon(QUrl("gmail"));
420 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
347 m_availableApplications.append(application);421 m_availableApplications.append(application);
348422
349 application = new ApplicationInfo(this);423 application = new ApplicationInfo(this);
@@ -351,6 +425,7 @@
351 application->setName("Weather");425 application->setName("Weather");
352 application->setIcon(QUrl("weather"));426 application->setIcon(QUrl("weather"));
353 application->setStage(ApplicationInfo::SideStage);427 application->setStage(ApplicationInfo::SideStage);
428 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
354 generateQmlStrings(application);429 generateQmlStrings(application);
355 m_availableApplications.append(application);430 m_availableApplications.append(application);
356431
@@ -359,6 +434,7 @@
359 application->setName("Notepad");434 application->setName("Notepad");
360 application->setIcon(QUrl("notepad"));435 application->setIcon(QUrl("notepad"));
361 application->setStage(ApplicationInfo::SideStage);436 application->setStage(ApplicationInfo::SideStage);
437 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
362 m_availableApplications.append(application);438 m_availableApplications.append(application);
363439
364 application = new ApplicationInfo(this);440 application = new ApplicationInfo(this);
@@ -366,6 +442,7 @@
366 application->setName("Calendar");442 application->setName("Calendar");
367 application->setIcon(QUrl("calendar"));443 application->setIcon(QUrl("calendar"));
368 application->setStage(ApplicationInfo::SideStage);444 application->setStage(ApplicationInfo::SideStage);
445 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
369 m_availableApplications.append(application);446 m_availableApplications.append(application);
370447
371 application = new ApplicationInfo(this);448 application = new ApplicationInfo(this);
@@ -373,18 +450,21 @@
373 application->setName("Media Player");450 application->setName("Media Player");
374 application->setIcon(QUrl("mediaplayer-app"));451 application->setIcon(QUrl("mediaplayer-app"));
375 application->setFullscreen(true);452 application->setFullscreen(true);
453 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
376 m_availableApplications.append(application);454 m_availableApplications.append(application);
377455
378 application = new ApplicationInfo(this);456 application = new ApplicationInfo(this);
379 application->setAppId("evernote");457 application->setAppId("evernote");
380 application->setName("Evernote");458 application->setName("Evernote");
381 application->setIcon(QUrl("evernote"));459 application->setIcon(QUrl("evernote"));
460 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
382 m_availableApplications.append(application);461 m_availableApplications.append(application);
383462
384 application = new ApplicationInfo(this);463 application = new ApplicationInfo(this);
385 application->setAppId("map");464 application->setAppId("map");
386 application->setName("Map");465 application->setName("Map");
387 application->setIcon(QUrl("map"));466 application->setIcon(QUrl("map"));
467 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
388 generateQmlStrings(application);468 generateQmlStrings(application);
389 m_availableApplications.append(application);469 m_availableApplications.append(application);
390470
@@ -392,24 +472,28 @@
392 application->setAppId("pinterest");472 application->setAppId("pinterest");
393 application->setName("Pinterest");473 application->setName("Pinterest");
394 application->setIcon(QUrl("pinterest"));474 application->setIcon(QUrl("pinterest"));
475 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
395 m_availableApplications.append(application);476 m_availableApplications.append(application);
396477
397 application = new ApplicationInfo(this);478 application = new ApplicationInfo(this);
398 application->setAppId("soundcloud");479 application->setAppId("soundcloud");
399 application->setName("SoundCloud");480 application->setName("SoundCloud");
400 application->setIcon(QUrl("soundcloud"));481 application->setIcon(QUrl("soundcloud"));
482 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
401 m_availableApplications.append(application);483 m_availableApplications.append(application);
402484
403 application = new ApplicationInfo(this);485 application = new ApplicationInfo(this);
404 application->setAppId("wikipedia");486 application->setAppId("wikipedia");
405 application->setName("Wikipedia");487 application->setName("Wikipedia");
406 application->setIcon(QUrl("wikipedia"));488 application->setIcon(QUrl("wikipedia"));
489 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
407 m_availableApplications.append(application);490 m_availableApplications.append(application);
408491
409 application = new ApplicationInfo(this);492 application = new ApplicationInfo(this);
410 application->setAppId("youtube");493 application->setAppId("youtube");
411 application->setName("YouTube");494 application->setName("YouTube");
412 application->setIcon(QUrl("youtube"));495 application->setIcon(QUrl("youtube"));
496 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
413 m_availableApplications.append(application);497 m_availableApplications.append(application);
414}498}
415499
@@ -484,3 +568,25 @@
484 m_sideStage->setParentItem(shell);568 m_sideStage->setParentItem(shell);
485 m_sideStage->setFlag(QQuickItem::ItemHasContents, false);569 m_sideStage->setFlag(QQuickItem::ItemHasContents, false);
486}570}
571
572QStringList ApplicationManager::availableApplications()
573{
574 QStringList appIds;
575 Q_FOREACH(ApplicationInfo *app, m_availableApplications) {
576 appIds << app->appId();
577 }
578 return appIds;
579}
580
581int ApplicationManager::rightMargin() const
582{
583 return m_rightMargin;
584}
585
586void ApplicationManager::setRightMargin(int rightMargin)
587{
588 m_rightMargin = rightMargin;
589 Q_FOREACH(ApplicationInfo *app, m_availableApplications) {
590 generateQmlStrings(app);
591 }
592}
487593
=== modified file 'tests/mocks/Unity/Application/ApplicationManager.h'
--- tests/mocks/Unity/Application/ApplicationManager.h 2013-10-11 11:37:04 +0000
+++ tests/mocks/Unity/Application/ApplicationManager.h 2014-03-25 15:08:15 +0000
@@ -45,6 +45,10 @@
4545
46 Q_PROPERTY(bool fake READ fake CONSTANT)46 Q_PROPERTY(bool fake READ fake CONSTANT)
4747
48 // Only for testing
49 // This can be used to place some controls to right, like make tryPhoneStage for example
50 Q_PROPERTY(int rightMargin READ rightMargin WRITE setRightMargin)
51
48 public:52 public:
49 ApplicationManager(QObject *parent = NULL);53 ApplicationManager(QObject *parent = NULL);
50 virtual ~ApplicationManager();54 virtual ~ApplicationManager();
@@ -87,13 +91,22 @@
87 Q_INVOKABLE void move(int from, int to);91 Q_INVOKABLE void move(int from, int to);
8892
89 // Application control methods93 // Application control methods
94 Q_INVOKABLE bool requestFocusApplication(const QString &appId) override;
90 Q_INVOKABLE bool focusApplication(const QString &appId) override;95 Q_INVOKABLE bool focusApplication(const QString &appId) override;
91 Q_INVOKABLE void unfocusCurrentApplication() override;96 Q_INVOKABLE void unfocusCurrentApplication() override;
92 Q_INVOKABLE ApplicationInfo *startApplication(const QString &appId, const QStringList &arguments = QStringList()) override;97 Q_INVOKABLE ApplicationInfo *startApplication(const QString &appId, const QStringList &arguments = QStringList()) override;
93 Q_INVOKABLE ApplicationInfo *startApplication(const QString &appId, ExecFlags flags, const QStringList &arguments = QStringList());98 Q_INVOKABLE ApplicationInfo *startApplication(const QString &appId, ExecFlags flags, const QStringList &arguments = QStringList());
94 Q_INVOKABLE bool stopApplication(const QString &appId) override;99 Q_INVOKABLE bool stopApplication(const QString &appId) override;
100 Q_INVOKABLE bool updateScreenshot(const QString &appId) override;
95101
96 QString focusedApplicationId() const override;102 QString focusedApplicationId() const override;
103 bool suspended() const;
104 void setSuspended(bool suspended);
105
106 // Only for testing
107 Q_INVOKABLE QStringList availableApplications();
108 int rightMargin() const;
109 void setRightMargin(int rightMargin);
97110
98 Q_SIGNALS:111 Q_SIGNALS:
99 void keyboardHeightChanged();112 void keyboardHeightChanged();
@@ -113,12 +126,15 @@
113 void createSideStage();126 void createSideStage();
114 int m_keyboardHeight;127 int m_keyboardHeight;
115 bool m_keyboardVisible;128 bool m_keyboardVisible;
129 bool m_suspended;
116 QList<ApplicationInfo*> m_runningApplications;130 QList<ApplicationInfo*> m_runningApplications;
117 QList<ApplicationInfo*> m_availableApplications;131 QList<ApplicationInfo*> m_availableApplications;
118 QQmlComponent *m_mainStageComponent;132 QQmlComponent *m_mainStageComponent;
119 QQuickItem *m_mainStage;133 QQuickItem *m_mainStage;
120 QQmlComponent *m_sideStageComponent;134 QQmlComponent *m_sideStageComponent;
121 QQuickItem *m_sideStage;135 QQuickItem *m_sideStage;
136
137 int m_rightMargin;
122};138};
123139
124Q_DECLARE_OPERATORS_FOR_FLAGS(ApplicationManager::ExecFlags)140Q_DECLARE_OPERATORS_FOR_FLAGS(ApplicationManager::ExecFlags)
125141
=== added file 'tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp'
--- tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp 1970-01-01 00:00:00 +0000
+++ tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp 2014-03-25 15:08:15 +0000
@@ -0,0 +1,74 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "ApplicationScreenshotProvider.h"
18#include "ApplicationManager.h"
19#include "ApplicationInfo.h"
20
21#include "paths.h"
22
23#include <QDebug>
24#include <QGuiApplication>
25#include <QWindow>
26#include <QQuickWindow>
27
28ApplicationScreenshotProvider::ApplicationScreenshotProvider(ApplicationManager *appManager)
29 : QQuickImageProvider(QQuickImageProvider::Image)
30 , m_appManager(appManager)
31{
32}
33
34QImage ApplicationScreenshotProvider::requestImage(const QString &imageId, QSize * size,
35 const QSize &requestedSize)
36{
37 // We ignore requestedSize here intentionally to avoid keeping scaled copies around
38 Q_UNUSED(requestedSize)
39
40 QString appId = imageId.split('/').first();
41
42 ApplicationInfo* app = static_cast<ApplicationInfo*>(m_appManager->findApplication(appId));
43 if (app == nullptr) {
44 return QImage();
45 }
46
47 QString filePath = QString("%1/Dash/graphics/phone/screenshots/%2@12.png").arg(qmlDirectory()).arg(app->icon().toString());
48
49 QImage image;
50 if (!image.load(filePath)) {
51 qWarning() << "failed loading app image" << filePath;
52 }
53
54
55 if (app->stage() == ApplicationInfo::SideStage) {
56 QByteArray gus = qgetenv("GRID_UNIT_PX");
57 image = image.scaledToWidth(gus.toInt() * 48);
58 } else {
59 // Lets scale main stage applications to be the size of the screen/window.
60 QGuiApplication *unity = qobject_cast<QGuiApplication*>(qApp);
61 Q_FOREACH (QWindow *win, unity->allWindows()) {
62 QQuickWindow *quickWin = qobject_cast<QQuickWindow*>(win);
63 if (quickWin) {
64 image = image.scaledToWidth(quickWin->width() - m_appManager->rightMargin());
65 break;
66 }
67 }
68 }
69
70 size->setWidth(image.width());
71 size->setHeight(image.height());
72
73 return image;
74}
075
=== added file 'tests/mocks/Unity/Application/ApplicationScreenshotProvider.h'
--- tests/mocks/Unity/Application/ApplicationScreenshotProvider.h 1970-01-01 00:00:00 +0000
+++ tests/mocks/Unity/Application/ApplicationScreenshotProvider.h 2014-03-25 15:08:15 +0000
@@ -0,0 +1,34 @@
1/*
2 * Copyright (C) 201 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef APPLICATIONSCREENSHOTPROVIDER_H
18#define APPLICATIONSCREENSHOTPROVIDER_H
19
20#include <QQuickImageProvider>
21
22class ApplicationManager;
23class ApplicationScreenshotProvider : public QQuickImageProvider
24{
25public:
26 explicit ApplicationScreenshotProvider(ApplicationManager *appManager);
27
28 QImage requestImage(const QString &appId, QSize *size, const QSize &requestedSize) override;
29
30private:
31 ApplicationManager* m_appManager;
32};
33
34#endif // APPLICATIONSCREENSHOTPROVIDER_H
035
=== modified file 'tests/mocks/Unity/Application/CMakeLists.txt'
--- tests/mocks/Unity/Application/CMakeLists.txt 2013-12-13 01:02:53 +0000
+++ tests/mocks/Unity/Application/CMakeLists.txt 2014-03-25 15:08:15 +0000
@@ -5,6 +5,7 @@
5 ApplicationInfo.cpp5 ApplicationInfo.cpp
6 ApplicationImage.cpp6 ApplicationImage.cpp
7 ApplicationManager.cpp7 ApplicationManager.cpp
8 ApplicationScreenshotProvider.cpp
8 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h9 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
9 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h10 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
10)11)
1112
=== modified file 'tests/mocks/Unity/Application/plugin.cpp'
--- tests/mocks/Unity/Application/plugin.cpp 2013-09-11 15:33:02 +0000
+++ tests/mocks/Unity/Application/plugin.cpp 2014-03-25 15:08:15 +0000
@@ -18,13 +18,20 @@
18#include "ApplicationInfo.h"18#include "ApplicationInfo.h"
19#include "ApplicationImage.h"19#include "ApplicationImage.h"
20#include "ApplicationManager.h"20#include "ApplicationManager.h"
21#include "ApplicationScreenshotProvider.h"
2122
22#include <qqml.h>23#include <qqml.h>
24#include <QQmlEngine>
25
26ApplicationManager *s_appManager = 0;
2327
24static QObject* applicationManagerSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {28static QObject* applicationManagerSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
25 Q_UNUSED(engine);29 Q_UNUSED(engine);
26 Q_UNUSED(scriptEngine);30 Q_UNUSED(scriptEngine);
27 return new ApplicationManager();31 if (!s_appManager) {
32 s_appManager = new ApplicationManager();
33 }
34 return s_appManager;
28}35}
2936
30void FakeUnityApplicationQmlPlugin::registerTypes(const char *uri)37void FakeUnityApplicationQmlPlugin::registerTypes(const char *uri)
@@ -37,3 +44,11 @@
3744
38 qmlRegisterType<ApplicationImage>(uri, 0, 1, "ApplicationImage");45 qmlRegisterType<ApplicationImage>(uri, 0, 1, "ApplicationImage");
39}46}
47
48void FakeUnityApplicationQmlPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
49{
50 QQmlExtensionPlugin::initializeEngine(engine, uri);
51
52 ApplicationManager* appManager = static_cast<ApplicationManager*>(applicationManagerSingleton(engine, NULL));
53 engine->addImageProvider(QLatin1String("application"), new ApplicationScreenshotProvider(appManager));
54}
4055
=== modified file 'tests/mocks/Unity/Application/plugin.h'
--- tests/mocks/Unity/Application/plugin.h 2013-09-04 13:42:27 +0000
+++ tests/mocks/Unity/Application/plugin.h 2014-03-25 15:08:15 +0000
@@ -25,6 +25,7 @@
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
26public:26public:
27 void registerTypes(const char *uri);27 void registerTypes(const char *uri);
28 void initializeEngine(QQmlEngine *engine, const char *uri);
28};29};
2930
30#endif31#endif
3132
=== modified file 'tests/plugins/Unity/Launcher/launchermodeltest.cpp'
--- tests/plugins/Unity/Launcher/launchermodeltest.cpp 2013-09-16 17:59:13 +0000
+++ tests/plugins/Unity/Launcher/launchermodeltest.cpp 2014-03-25 15:08:15 +0000
@@ -39,6 +39,7 @@
39 ApplicationInfoInterface::Stage stage() const { return ApplicationInfoInterface::MainStage; }39 ApplicationInfoInterface::Stage stage() const { return ApplicationInfoInterface::MainStage; }
40 ApplicationInfoInterface::State state() const { return ApplicationInfoInterface::Running; }40 ApplicationInfoInterface::State state() const { return ApplicationInfoInterface::Running; }
41 bool focused() const { return m_focused; }41 bool focused() const { return m_focused; }
42 QUrl screenshot() const { return QUrl(); }
4243
43 // Methods used for mocking (not in the interface)44 // Methods used for mocking (not in the interface)
44 void setFocused(bool focused) { m_focused = focused; Q_EMIT focusedChanged(focused); }45 void setFocused(bool focused) { m_focused = focused; Q_EMIT focusedChanged(focused); }
@@ -92,6 +93,10 @@
92 m_list.takeAt(index)->deleteLater();93 m_list.takeAt(index)->deleteLater();
93 endRemoveRows();94 endRemoveRows();
94 }95 }
96 bool updateScreenshot(const QString &appId) { Q_UNUSED(appId); return true; }
97 bool requestFocusApplication(const QString &appId) { Q_UNUSED(appId); return true; }
98 bool suspended() const { return false; }
99 void setSuspended(bool) {}
95100
96private:101private:
97 QList<MockApp*> m_list;102 QList<MockApp*> m_list;
98103
=== modified file 'tests/qmltests/CMakeLists.txt'
--- tests/qmltests/CMakeLists.txt 2014-03-17 11:44:05 +0000
+++ tests/qmltests/CMakeLists.txt 2014-03-25 15:08:15 +0000
@@ -83,6 +83,6 @@
83 ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")83 ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
84add_qml_test(Panel/Indicators MenuItemFactory IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)84add_qml_test(Panel/Indicators MenuItemFactory IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)
85add_qml_test(Panel/Indicators MessageMenuItemFactory IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)85add_qml_test(Panel/Indicators MessageMenuItemFactory IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)
86add_qml_test(SideStage SideStage IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS}86add_qml_test(Stages PhoneStage IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS}
87 ${CMAKE_BINARY_DIR}/tests/mocks87 ${CMAKE_BINARY_DIR}/tests/mocks
88 ${CMAKE_BINARY_DIR}/plugins)88 ${CMAKE_BINARY_DIR}/plugins)
8989
=== modified file 'tests/qmltests/Components/tst_ResponsiveFlowView.qml'
--- tests/qmltests/Components/tst_ResponsiveFlowView.qml 2013-12-17 16:04:47 +0000
+++ tests/qmltests/Components/tst_ResponsiveFlowView.qml 2014-03-25 15:08:15 +0000
@@ -82,8 +82,7 @@
82 ResponsiveFlowView {82 ResponsiveFlowView {
83 id: flow83 id: flow
84 anchors.fill: parent84 anchors.fill: parent
85 firstModel: fakeModel85 model: fakeModel
86 secondModel: fakeModel
87 minimumHorizontalSpacing:86 minimumHorizontalSpacing:
88 minHSpacingSelector.values[minHSpacingSelector.selectedIndex]87 minHSpacingSelector.values[minHSpacingSelector.selectedIndex]
89 verticalSpacing: units.gu(2)88 verticalSpacing: units.gu(2)
9089
=== modified file 'tests/qmltests/Dash/Apps/tst_RunningApplicationsGrid.qml'
--- tests/qmltests/Dash/Apps/tst_RunningApplicationsGrid.qml 2013-12-17 16:04:47 +0000
+++ tests/qmltests/Dash/Apps/tst_RunningApplicationsGrid.qml 2014-03-25 15:08:15 +0000
@@ -18,87 +18,20 @@
18import QtTest 1.018import QtTest 1.0
19import "../../../../qml/Dash/Apps"19import "../../../../qml/Dash/Apps"
20import Unity.Test 0.1 as UT20import Unity.Test 0.1 as UT
21import Unity.Application 0.1
2122
22Item {23// Using Rectangle to have an opaque surface because AppManager paints app surfaces behind it.
24Rectangle {
23 width: units.gu(50)25 width: units.gu(50)
24 height: units.gu(40)26 height: units.gu(40)
2527
26 QtObject {
27 id: fakeApplicationManager
28
29 property bool sideStageEnabled: false
30
31 function stopApplication(appId) {
32 for (var i=0, len=fakeRunningAppsModel.count; i<len; i++) {
33 if (appId == fakeRunningAppsModel.get(i).appId) {
34 fakeRunningAppsModel.remove(i)
35 }
36 }
37 }
38 }
39
40 QtObject {
41 id: shell
42 property bool dashShown: true
43 property bool stageScreenshotsReady: false
44 property var applicationManager: fakeApplicationManager
45
46 function activateApplication(appId) {
47 }
48 }
49
50 ListModel {
51 id: fakeRunningAppsModel
52
53 function contains(appId) {
54 for (var i=0, len=fakeRunningAppsModel.count; i<len; i++) {
55 if (appId == fakeRunningAppsModel.get(i).appId) {
56 return true;
57 }
58 }
59 return false;
60 }
61
62 function rePopulate() {
63 for (var i=0, len=availableAppsModel.count; i<len; i++) {
64 fakeRunningAppsModel.append(availableAppsModel.get(i));
65 }
66 }
67 }
68
69 ListModel {
70 id: availableAppsModel
71 ListElement {
72 name: "Phone"
73 icon: "phone-app"
74 exec: "/usr/bin/phone-app"
75 appId: "phone"
76 imageQml: "import QtQuick 2.0\n \
77 Rectangle { \n \
78 anchors.fill:parent \n \
79 color:'darkgreen' \n \
80 Text { anchors.centerIn: parent; text: 'PHONE' } \n \
81 }"
82 }
83
84 ListElement {
85 name: "Calendar"
86 icon: "calendar-app"
87 exec: "/usr/bin/calendar-app"
88 appId: "calendar"
89 imageQml: "import QtQuick 2.0\n \
90 Rectangle { \n \
91 anchors.fill:parent \n \
92 color:'darkblue' \n \
93 Text { anchors.centerIn: parent; text: 'CALENDAR'\n \
94 color:'white'} \n \
95 }"
96 }
97 }
98
99 function resetRunningApplications() {28 function resetRunningApplications() {
100 fakeRunningAppsModel.clear()29 while (ApplicationManager.count > 0) {
101 fakeRunningAppsModel.rePopulate()30 ApplicationManager.stopApplication(ApplicationManager.get(0).appId)
31 }
32
33 ApplicationManager.startApplication("phone-app");
34 ApplicationManager.startApplication("webbrowser-app");
102 }35 }
10336
104 Component.onCompleted: {37 Component.onCompleted: {
@@ -109,7 +42,7 @@
109 RunningApplicationsGrid {42 RunningApplicationsGrid {
110 id: runningApplicationsGrid43 id: runningApplicationsGrid
111 anchors.fill: parent44 anchors.fill: parent
112 firstModel: fakeRunningAppsModel45 model: ApplicationManager
113 }46 }
11447
115 UT.UnityTestCase {48 UT.UnityTestCase {
@@ -121,11 +54,11 @@
121 resetRunningApplications()54 resetRunningApplications()
122 }55 }
12356
124 property var calendarTile57 property var browserTile
125 property var phoneTile58 property var phoneTile
12659
127 property var isCalendarLongPressed: false60 property var isBrowserLongPressed: false
128 function onCalendarLongPressed() {isCalendarLongPressed = true}61 function onBrowserLongPressed() {isBrowserLongPressed = true}
12962
130 property var isPhoneLongPressed: false63 property var isPhoneLongPressed: false
131 function onPhoneLongPressed() {isPhoneLongPressed = true}64 function onPhoneLongPressed() {isPhoneLongPressed = true}
@@ -133,25 +66,25 @@
133 // Tiles should go to termination mode when any one of them is long-pressed.66 // Tiles should go to termination mode when any one of them is long-pressed.
134 // Long-pressing when they're in termination mode brings them back to activation mode67 // Long-pressing when they're in termination mode brings them back to activation mode
135 function test_enterTerminationMode() {68 function test_enterTerminationMode() {
136 calendarTile = findChild(runningApplicationsGrid, "runningAppTile Calendar")69 browserTile = findChild(runningApplicationsGrid, "runningAppTile Browser")
137 verify(calendarTile != undefined)70 verify(browserTile != undefined)
138 calendarTile.onPressAndHold.connect(onCalendarLongPressed)71 browserTile.onPressAndHold.connect(onBrowserLongPressed)
13972
140 phoneTile = findChild(runningApplicationsGrid, "runningAppTile Phone")73 phoneTile = findChild(runningApplicationsGrid, "runningAppTile Phone")
141 verify(phoneTile != undefined)74 verify(phoneTile != undefined)
142 phoneTile.onPressAndHold.connect(onPhoneLongPressed)75 phoneTile.onPressAndHold.connect(onPhoneLongPressed)
14376
144 compare(calendarTile.terminationModeEnabled, false)77 compare(browserTile.terminationModeEnabled, false)
145 compare(phoneTile.terminationModeEnabled, false)78 compare(phoneTile.terminationModeEnabled, false)
146 compare(runningApplicationsGrid.terminationModeEnabled, false)79 compare(runningApplicationsGrid.terminationModeEnabled, false)
14780
148 isCalendarLongPressed = false81 isBrowserLongPressed = false
149 mousePress(calendarTile, calendarTile.width/2, calendarTile.height/2)82 mousePress(browserTile, browserTile.width/2, browserTile.height/2)
150 tryCompareFunction(checkSwitchToTerminationModeAfterLongPress, true)83 tryCompareFunction(checkSwitchToTerminationModeAfterLongPress, true)
15184
152 mouseRelease(calendarTile, calendarTile.width/2, calendarTile.height/2)85 mouseRelease(browserTile, browserTile.width/2, browserTile.height/2)
15386
154 compare(calendarTile.terminationModeEnabled, true)87 compare(browserTile.terminationModeEnabled, true)
155 compare(phoneTile.terminationModeEnabled, true)88 compare(phoneTile.terminationModeEnabled, true)
156 compare(runningApplicationsGrid.terminationModeEnabled, true)89 compare(runningApplicationsGrid.terminationModeEnabled, true)
15790
@@ -161,23 +94,23 @@
16194
162 mouseRelease(phoneTile, phoneTile.width/2, phoneTile.height/2)95 mouseRelease(phoneTile, phoneTile.width/2, phoneTile.height/2)
16396
164 compare(calendarTile.terminationModeEnabled, false)97 compare(browserTile.terminationModeEnabled, false)
165 compare(phoneTile.terminationModeEnabled, false)98 compare(phoneTile.terminationModeEnabled, false)
166 compare(runningApplicationsGrid.terminationModeEnabled, false)99 compare(runningApplicationsGrid.terminationModeEnabled, false)
167100
168 calendarTile.onPressAndHold.disconnect(onCalendarLongPressed)101 browserTile.onPressAndHold.disconnect(onBrowserLongPressed)
169 phoneTile.onPressAndHold.disconnect(onPhoneLongPressed)102 phoneTile.onPressAndHold.disconnect(onPhoneLongPressed)
170 }103 }
171104
172 // Checks that components swicth to termination mode after (and only after) a long105 // Checks that components swicth to termination mode after (and only after) a long
173 // press happens on Calendar tile.106 // press happens on Browser tile.
174 function checkSwitchToTerminationModeAfterLongPress() {107 function checkSwitchToTerminationModeAfterLongPress() {
175 compare(calendarTile.terminationModeEnabled, isCalendarLongPressed)108 compare(browserTile.terminationModeEnabled, isBrowserLongPressed)
176 compare(phoneTile.terminationModeEnabled, isCalendarLongPressed)109 compare(phoneTile.terminationModeEnabled, isBrowserLongPressed)
177 compare(runningApplicationsGrid.terminationModeEnabled, isCalendarLongPressed)110 compare(runningApplicationsGrid.terminationModeEnabled, isBrowserLongPressed)
178111
179 return isCalendarLongPressed &&112 return isBrowserLongPressed &&
180 calendarTile.terminationModeEnabled &&113 browserTile.terminationModeEnabled &&
181 phoneTile.terminationModeEnabled &&114 phoneTile.terminationModeEnabled &&
182 runningApplicationsGrid.terminationModeEnabled115 runningApplicationsGrid.terminationModeEnabled
183 }116 }
@@ -185,12 +118,12 @@
185 // Checks that components swicth to activation mode after (and only after) a long118 // Checks that components swicth to activation mode after (and only after) a long
186 // press happens on Phone tile.119 // press happens on Phone tile.
187 function checkSwitchToActivationModeAfterLongPress() {120 function checkSwitchToActivationModeAfterLongPress() {
188 compare(calendarTile.terminationModeEnabled, !isPhoneLongPressed)121 compare(browserTile.terminationModeEnabled, !isPhoneLongPressed)
189 compare(phoneTile.terminationModeEnabled, !isPhoneLongPressed)122 compare(phoneTile.terminationModeEnabled, !isPhoneLongPressed)
190 compare(runningApplicationsGrid.terminationModeEnabled, !isPhoneLongPressed)123 compare(runningApplicationsGrid.terminationModeEnabled, !isPhoneLongPressed)
191124
192 return isPhoneLongPressed &&125 return isPhoneLongPressed &&
193 !calendarTile.terminationModeEnabled &&126 !browserTile.terminationModeEnabled &&
194 !phoneTile.terminationModeEnabled &&127 !phoneTile.terminationModeEnabled &&
195 !runningApplicationsGrid.terminationModeEnabled128 !runningApplicationsGrid.terminationModeEnabled
196 }129 }
@@ -200,17 +133,17 @@
200 function test_clickTileNotClose() {133 function test_clickTileNotClose() {
201 runningApplicationsGrid.terminationModeEnabled = true134 runningApplicationsGrid.terminationModeEnabled = true
202135
203 var calendarTile = findChild(runningApplicationsGrid, "runningAppTile Calendar")136 var browserTile = findChild(runningApplicationsGrid, "runningAppTile Browser")
204 verify(calendarTile != undefined)137 verify(browserTile != undefined)
205138
206 verify(fakeRunningAppsModel.contains("calendar"))139 verify(ApplicationManager.findApplication("webbrowser-app") !== null)
207140
208 mouseClick(calendarTile, calendarTile.width/2, calendarTile.height/2)141 mouseClick(browserTile, browserTile.width/2, browserTile.height/2)
209142
210 verify(fakeRunningAppsModel.contains("calendar"))143 verify(ApplicationManager.findApplication("webbrowser-app") !== null)
211144
212 // The tile for the Calendar app should stay there145 // The tile for the Browser app should stay there
213 tryCompareFunction(checkCalendarTileExists, true)146 tryCompareFunction(checkBrowserTileExists, true)
214 }147 }
215148
216 // While in termination mode, clicking on a running application tile's close icon149 // While in termination mode, clicking on a running application tile's close icon
@@ -218,25 +151,25 @@
218 function test_clickCloseIconToTerminateApp() {151 function test_clickCloseIconToTerminateApp() {
219 runningApplicationsGrid.terminationModeEnabled = true152 runningApplicationsGrid.terminationModeEnabled = true
220153
221 var calendarTile = findChild(runningApplicationsGrid, "runningAppTile Calendar")154 var browserTile = findChild(runningApplicationsGrid, "runningAppTile Browser")
222 var calendarTileCloseButton = findChild(runningApplicationsGrid, "closeIcon Calendar")155 var browserTileCloseButton = findChild(runningApplicationsGrid, "closeIcon Browser")
223156
224 verify(calendarTile != undefined)157 verify(browserTile != undefined)
225 verify(calendarTileCloseButton != undefined)158 verify(browserTileCloseButton != undefined)
226 verify(fakeRunningAppsModel.contains("calendar"))159 verify(ApplicationManager.findApplication("webbrowser-app") !== 0)
227160
228 mouseClick(calendarTileCloseButton, calendarTileCloseButton.width/2, calendarTileCloseButton.height/2)161 mouseClick(browserTileCloseButton, browserTileCloseButton.width/2, browserTileCloseButton.height/2)
229 wait(0) // spin event loop to start any pending animation162 wait(0) // spin event loop to start any pending animation
230163
231 verify(!fakeRunningAppsModel.contains("calendar"))164 verify(ApplicationManager.findApplication("webbrowser-app") === null)
232165
233 // The tile for the Calendar app should eventually vanish since the166 // The tile for the Browser app should eventually vanish since the
234 // application has been terminated.167 // application has been terminated.
235 tryCompareFunction(checkCalendarTileExists, false)168 tryCompareFunction(checkBrowserTileExists, false)
236 }169 }
237170
238 function checkCalendarTileExists() {171 function checkBrowserTileExists() {
239 return findChild(runningApplicationsGrid, "runningAppTile Calendar")172 return findChild(runningApplicationsGrid, "runningAppTile Browser")
240 != undefined173 != undefined
241 }174 }
242175
@@ -245,8 +178,8 @@
245 function test_clickOutsideTilesDisablesTerminationMode() {178 function test_clickOutsideTilesDisablesTerminationMode() {
246 runningApplicationsGrid.terminationModeEnabled = true179 runningApplicationsGrid.terminationModeEnabled = true
247180
248 var calendarTile = findChild(runningApplicationsGrid, "runningAppTile Calendar")181 var browserTile = findChild(runningApplicationsGrid, "runningAppTile Browser")
249 verify(calendarTile != undefined)182 verify(browserTile != undefined)
250183
251 verify(runningApplicationsGrid.terminationModeEnabled);184 verify(runningApplicationsGrid.terminationModeEnabled);
252185
253186
=== added directory 'tests/qmltests/Stages'
=== added file 'tests/qmltests/Stages/tst_PhoneStage.qml'
--- tests/qmltests/Stages/tst_PhoneStage.qml 1970-01-01 00:00:00 +0000
+++ tests/qmltests/Stages/tst_PhoneStage.qml 2014-03-25 15:08:15 +0000
@@ -0,0 +1,288 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18import QtTest 1.0
19import Unity.Test 0.1 as UT
20import ".."
21import "../../../qml/Stages"
22import Ubuntu.Components 0.1
23import Unity.Application 0.1
24
25Item {
26 width: units.gu(70)
27 height: units.gu(70)
28
29 Rectangle {
30
31 }
32
33 PhoneStage {
34 id: phoneStage
35 anchors { fill: parent; rightMargin: units.gu(30) }
36 shown: true
37 dragAreaWidth: units.gu(2)
38 }
39
40 Binding {
41 target: ApplicationManager
42 property: "rightMargin"
43 value: phoneStage.anchors.rightMargin
44 }
45
46 Rectangle {
47 anchors { fill: parent; leftMargin: phoneStage.width }
48// color: "blue"
49
50 Column {
51 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
52 spacing: units.gu(1)
53 Button {
54 anchors { left: parent.left; right: parent.right }
55 text: "Add App"
56 onClicked: {
57 testCase.addApps();
58 }
59 }
60 Button {
61 anchors { left: parent.left; right: parent.right }
62 text: "Add App"
63 }
64 }
65 }
66
67 UT.UnityTestCase {
68 id: testCase
69 name: "PhoneStage"
70 when: windowShown
71
72 function addApps(count) {
73 if (count == undefined) count = 1;
74 for (var i = 0; i < count; i++) {
75 var app = ApplicationManager.startApplication(ApplicationManager.availableApplications()[ApplicationManager.count])
76 tryCompare(app, "state", ApplicationInfoInterface.Running)
77 // Fixme: Right now there is a timeout in the PhoneStage that displays a white splash
78 // screen rectangle when an app starts. This is because we don't yet have a way of
79 // knowing when an app has finished launching. That workaround and this wait() should
80 // go away at some point and the app's state only changing to Running when ready for real.
81// wait(1000)
82 waitForRendering(phoneStage)
83 }
84 }
85
86 function goToSpread() {
87 var spreadView = findChild(phoneStage, "spreadView");
88
89 var startX = phoneStage.width;
90 var startY = phoneStage.height / 2;
91 var endY = startY;
92 var endX = units.gu(2);
93
94 touchFlick(phoneStage, startX, startY, endX, endY,
95 true /* beginTouch */, true /* endTouch */, units.gu(10), 50);
96 }
97
98 function test_shortFlick() {
99 addApps(2)
100 var startX = phoneStage.width - units.gu(1);
101 var startY = phoneStage.height / 2;
102 var endX = phoneStage.width / 2;
103 var endY = startY;
104
105 var activeApp = ApplicationManager.get(0);
106 var inactiveApp = ApplicationManager.get(1);
107
108 touchFlick(phoneStage, startX, startY, endX, endY,
109 true /* beginTouch */, true /* endTouch */, units.gu(10), 50);
110
111 tryCompare(ApplicationManager, "focusedApplicationId", inactiveApp.appId)
112
113 touchFlick(phoneStage, startX, startY, endX, endY,
114 true /* beginTouch */, true /* endTouch */, units.gu(10), 50);
115
116 tryCompare(ApplicationManager, "focusedApplicationId", activeApp.appId)
117
118 tryCompare(phoneStage, "painting", false);
119 }
120
121 function test_enterSpread_data() {
122 return [
123 {tag: "<position1 (linear movement)", positionMarker: "positionMarker1", linear: true, offset: -1, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
124 {tag: "<position1 (non-linear movement)", positionMarker: "positionMarker1", linear: false, offset: -1, endPhase: 0, targetPhase: 0, newFocusedIndex: 0 },
125 {tag: ">position1", positionMarker: "positionMarker1", linear: true, offset: +1, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
126 {tag: "<position2 (linear)", positionMarker: "positionMarker2", linear: true, offset: -1, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
127 {tag: "<position2 (non-linear)", positionMarker: "positionMarker2", linear: false, offset: -1, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
128 {tag: ">position2", positionMarker: "positionMarker2", linear: true, offset: +1, endPhase: 1, targetPhase: 0, newFocusedIndex: 1 },
129 {tag: "<position3", positionMarker: "positionMarker3", linear: true, offset: -1, endPhase: 1, targetPhase: 0, newFocusedIndex: 1 },
130 {tag: ">position3", positionMarker: "positionMarker3", linear: true, offset: +1, endPhase: 2, targetPhase: 2, newFocusedIndex: 2 },
131 ];
132 }
133
134 function test_enterSpread(data) {
135 addApps(5)
136
137 var spreadView = findChild(phoneStage, "spreadView");
138
139 var startX = phoneStage.width;
140 var startY = phoneStage.height / 2;
141 var endY = startY;
142 var endX = spreadView.width - (spreadView.width * spreadView[data.positionMarker]) - data.offset - phoneStage.dragAreaWidth;
143
144 var oldFocusedApp = ApplicationManager.get(0);
145 var newFocusedApp = ApplicationManager.get(data.newFocusedIndex);
146
147 touchFlick(phoneStage, startX, startY, endX, endY,
148 true /* beginTouch */, false /* endTouch */, units.gu(10), 50);
149
150 tryCompare(spreadView, "phase", data.endPhase)
151
152 if (!data.linear) {
153 touchFlick(phoneStage, endX, endY, endX + units.gu(.5), endY,
154 false /* beginTouch */, false /* endTouch */, units.gu(10), 50);
155 touchFlick(phoneStage, endY + units.gu(.5), endY, endX, endY,
156 false /* beginTouch */, false /* endTouch */, units.gu(10), 50);
157 }
158
159 touchRelease(phoneStage, endX, endY);
160
161 tryCompare(spreadView, "phase", data.targetPhase)
162
163 if (data.targetPhase == 2) {
164 var app2 = findChild(spreadView, "appDelegate2");
165 mouseClick(app2, units.gu(1), units.gu(1));
166 }
167
168 tryCompare(phoneStage, "painting", false);
169 tryCompare(ApplicationManager, "focusedApplicationId", newFocusedApp.appId);
170 }
171
172 function test_selectAppFromSpread_data() {
173 var appsToTest = 6;
174 var apps = new Array();
175 for (var i = 0; i < appsToTest; i++) {
176 var item = new Object();
177 item.tag = "App " + i;
178 item.index = i;
179 item.total = appsToTest;
180 apps.push(item)
181 }
182 return apps;
183 }
184
185 function test_selectAppFromSpread(data) {
186 addApps(data.total)
187
188 var spreadView = findChild(phoneStage, "spreadView");
189
190 goToSpread();
191
192 tryCompare(spreadView, "phase", 2);
193
194 var tile = findChild(spreadView, "appDelegate" + data.index);
195 var appId = ApplicationManager.get(data.index).appId;
196
197 if (tile.mapToItem(spreadView).x > spreadView.width) {
198 // Item is not visible... Need to flick the spread
199 var startX = phoneStage.width - units.gu(1);
200 var startY = phoneStage.height / 2;
201 var endY = startY;
202 var endX = units.gu(2);
203 touchFlick(phoneStage, startX, startY, endX, endY, true, true, units.gu(10), 50)
204 tryCompare(spreadView, "flicking", false);
205 tryCompare(spreadView, "moving", false);
206// waitForRendering(phoneStage);
207 }
208
209 console.log("clicking app", data.index, "(", appId, ")")
210 mouseClick(spreadView, tile.mapToItem(spreadView).x + units.gu(1), spreadView.height / 2)
211 tryCompare(ApplicationManager, "focusedApplicationId", appId);
212 tryCompare(spreadView, "phase", 0);
213 }
214
215 function test_animateAppStartup() {
216 compare(phoneStage.painting, false);
217 addApps(2);
218 tryCompare(phoneStage, "painting", true);
219 tryCompare(phoneStage, "painting", false);
220 addApps(1);
221 tryCompare(phoneStage, "painting", true);
222 tryCompare(phoneStage, "painting", false);
223 }
224
225 function test_select_data() {
226 return [
227 { tag: "0", index: 0 },
228 { tag: "2", index: 2 },
229 { tag: "4", index: 4 },
230 ]
231 }
232
233 function test_select(data) {
234 addApps(5);
235
236 var spreadView = findChild(phoneStage, "spreadView");
237 var selectedApp = ApplicationManager.get(data.index);
238
239 goToSpread();
240
241 phoneStage.select(selectedApp.appId);
242
243 tryCompare(phoneStage, "painting", false);
244 compare(ApplicationManager.focusedApplicationId, selectedApp.appId);
245 }
246
247 function test_fullscreenMode() {
248 var fullscreenApp = null;
249 var normalApp = null;
250
251 for (var i = 0; i < 5; i++) {
252 addApps(1);
253 var newApp = ApplicationManager.get(0);
254 tryCompare(phoneStage, "fullscreen", newApp.fullscreen);
255 if (newApp.fullscreen && fullscreenApp == null) {
256 fullscreenApp = newApp;
257 } else if (!newApp.fullscreen && normalApp == null){
258 normalApp = newApp;
259 }
260 }
261 verify(fullscreenApp != null); // Can't continue the test without having a fullscreen app
262 verify(normalApp != null); // Can't continue the test without having a non-fullscreen app
263
264 // Select a normal app
265 goToSpread();
266 phoneStage.select(normalApp.appId);
267 tryCompare(phoneStage, "fullscreen", false);
268
269 // Select a fullscreen app
270 goToSpread();
271 phoneStage.select(fullscreenApp.appId);
272 tryCompare(phoneStage, "fullscreen", true);
273
274 // Select a normal app
275 goToSpread();
276 phoneStage.select(normalApp.appId);
277 tryCompare(phoneStage, "fullscreen", false);
278 }
279
280 function cleanup() {
281 while (ApplicationManager.count > 0) {
282 var oldCount = ApplicationManager.count;
283 ApplicationManager.stopApplication(ApplicationManager.get(0).appId)
284 tryCompare(ApplicationManager, "count", oldCount - 1)
285 }
286 }
287 }
288}
0289
=== modified file 'tests/qmltests/tst_Shell.qml'
--- tests/qmltests/tst_Shell.qml 2014-03-19 10:48:06 +0000
+++ tests/qmltests/tst_Shell.qml 2014-03-25 15:08:15 +0000
@@ -117,6 +117,7 @@
117 while (apps.count > 0) {117 while (apps.count > 0) {
118 ApplicationManager.stopApplication(apps.get(0).appId);118 ApplicationManager.stopApplication(apps.get(0).appId);
119 }119 }
120 compare(ApplicationManager.count, 0)
120 }121 }
121122
122 /*123 /*
@@ -187,26 +188,34 @@
187 tapOnAppIconInLauncher();188 tapOnAppIconInLauncher();
188 waitUntilApplicationWindowIsFullyVisible();189 waitUntilApplicationWindowIsFullyVisible();
189190
190 var mainApp = ApplicationManager.focusedApplicationId;191 var mainAppId = ApplicationManager.focusedApplicationId;
191 verify(mainApp != "");192 verify(mainAppId != "");
193 var mainApp = ApplicationManager.findApplication(mainAppId);
194 verify(mainApp);
195 tryCompare(mainApp.state, ApplicationInfo.Running);
192196
193 // Try to suspend while proximity is engaged...197 // Try to suspend while proximity is engaged...
194 Powerd.displayPowerStateChange(Powerd.Off, Powerd.UseProximity);198 Powerd.displayPowerStateChange(Powerd.Off, Powerd.UseProximity);
195 tryCompare(greeter, "showProgress", 0);199 tryCompare(greeter, "showProgress", 0);
196200
197 // Now really suspend201 // Now really suspend
202 print("suspending")
198 Powerd.displayPowerStateChange(Powerd.Off, 0);203 Powerd.displayPowerStateChange(Powerd.Off, 0);
204 print("done suspending")
199 tryCompare(greeter, "showProgress", 1);205 tryCompare(greeter, "showProgress", 1);
200 tryCompare(ApplicationManager, "focusedApplicationId", "");206
207 tryCompare(ApplicationManager, "suspended", true);
208 compare(mainApp.state, ApplicationInfo.Suspended);
201209
202 // And wake up210 // And wake up
203 Powerd.displayPowerStateChange(Powerd.On, 0);211 Powerd.displayPowerStateChange(Powerd.On, 0);
204 tryCompare(ApplicationManager, "focusedApplicationId", "");
205 tryCompare(greeter, "showProgress", 1);212 tryCompare(greeter, "showProgress", 1);
206213
207 // Swipe away greeter to focus app214 // Swipe away greeter to focus app
208 swipeAwayGreeter();215 swipeAwayGreeter();
209 tryCompare(ApplicationManager, "focusedApplicationId", mainApp);216 tryCompare(ApplicationManager, "suspended", false);
217 compare(mainApp.state, ApplicationInfo.Running);
218 tryCompare(ApplicationManager, "focusedApplicationId", mainAppId);
210 }219 }
211220
212 function swipeAwayGreeter() {221 function swipeAwayGreeter() {
@@ -239,7 +248,7 @@
239 tryCompare(dash, "opacity", 1.0);248 tryCompare(dash, "opacity", 1.0);
240249
241 touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY,250 touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY,
242 true /* beginTouch */, false /* endTouch */);251 true /* beginTouch */, false /* endTouch */, units.gu(10), 50);
243252
244 // check that Dash has been scaled down and had its opacity reduced253 // check that Dash has been scaled down and had its opacity reduced
245 tryCompareFunction(function() { return dash.contentScale <= 0.9; }, true);254 tryCompareFunction(function() { return dash.contentScale <= 0.9; }, true);
@@ -270,7 +279,7 @@
270 tryCompare(dash, "opacity", 1.0);279 tryCompare(dash, "opacity", 1.0);
271280
272 touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY,281 touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY,
273 true /* beginTouch */, false /* endTouch */);282 true /* beginTouch */, false /* endTouch */, units.gu(10), 50);
274283
275 // check that Dash has been scaled down and had its opacity reduced284 // check that Dash has been scaled down and had its opacity reduced
276 tryCompareFunction(function() { return dash.contentScale <= 0.9; }, true);285 tryCompareFunction(function() { return dash.contentScale <= 0.9; }, true);
@@ -486,7 +495,6 @@
486 }495 }
487496
488 function test_DashShown(data) {497 function test_DashShown(data) {
489
490 if (data.greeter) {498 if (data.greeter) {
491 // Swipe the greeter in499 // Swipe the greeter in
492 var greeter = findChild(shell, "greeter");500 var greeter = findChild(shell, "greeter");

Subscribers

People subscribed via source and target branches