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
1=== modified file 'debian/control'
2--- debian/control 2014-03-24 11:49:06 +0000
3+++ debian/control 2014-03-25 15:08:15 +0000
4@@ -17,7 +17,7 @@
5 libnih-dev,
6 libpulse-dev,
7 libqmenumodel-dev (>= 0.2.7),
8- libunity-api-dev (>= 7.80.4),
9+ libunity-api-dev (>= 7.80.6),
10 libunity-mir-dev,
11 libupstart-dev,
12 libusermetricsoutput1-dev,
13@@ -83,6 +83,7 @@
14 qtdeclarative5-ubuntu-ui-toolkit-plugin,
15 qtdeclarative5-unity-notifications-plugin | unity-notifications-impl,
16 qtdeclarative5-xmllistmodel-plugin,
17+ unity-application-impl-2,
18 unity-launcher-impl-3,
19 unity-notifications-impl-2,
20 unity8-fake-env | qtubuntu-shell,
21@@ -147,6 +148,7 @@
22 ${shlibs:Depends},
23 Provides: unity-launcher-impl,
24 unity-launcher-impl-3,
25+ unity-application-impl-2,
26 Description: Unity 8 private libs
27 The Unity 8 shell is the primary user interface for Ubuntu devices.
28 .
29
30=== modified file 'debian/unity8.install'
31--- debian/unity8.install 2014-03-05 12:52:57 +0000
32+++ debian/unity8.install 2014-03-25 15:08:15 +0000
33@@ -10,6 +10,6 @@
34 usr/share/unity8/Notifications
35 usr/share/unity8/Panel
36 usr/share/unity8/Shell.qml
37-usr/share/unity8/SideStage
38+usr/share/unity8/Stages
39 usr/share/unity8/graphics
40 data/unity8.conf usr/share/upstart/sessions/
41
42=== modified file 'plugins/Utils/CMakeLists.txt'
43--- plugins/Utils/CMakeLists.txt 2014-03-17 15:50:50 +0000
44+++ plugins/Utils/CMakeLists.txt 2014-03-25 15:08:15 +0000
45@@ -18,6 +18,7 @@
46 qsortfilterproxymodelqml.cpp
47 timeformatter.cpp
48 unitymenumodelpaths.cpp
49+ easingcurve.cpp
50 plugin.cpp
51 )
52
53@@ -33,7 +34,7 @@
54 # files directly in targets.
55 set_target_properties(Utils-qml PROPERTIES COMPILE_FLAGS -fvisibility=default)
56
57-qt5_use_modules(Utils-qml Qml Quick DBus Network XmlPatterns Concurrent)
58+qt5_use_modules(Utils-qml Qml Quick DBus Network XmlPatterns Gui Concurrent)
59
60 # export the qmldir qmltypes and plugin files
61 export_qmlfiles(Utils Utils)
62
63=== added file 'plugins/Utils/easingcurve.cpp'
64--- plugins/Utils/easingcurve.cpp 1970-01-01 00:00:00 +0000
65+++ plugins/Utils/easingcurve.cpp 2014-03-25 15:08:15 +0000
66@@ -0,0 +1,67 @@
67+/*
68+ * Copyright 2014 Canonical Ltd.
69+ *
70+ * This program is free software; you can redistribute it and/or modify
71+ * it under the terms of the GNU Lesser General Public License as published by
72+ * the Free Software Foundation; version 3.
73+ *
74+ * This program is distributed in the hope that it will be useful,
75+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
76+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
77+ * GNU Lesser General Public License for more details.
78+ *
79+ * You should have received a copy of the GNU Lesser General Public License
80+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
81+ *
82+ * Authors: Michael Zanetti <michael.zanetti@canonical.com>
83+*/
84+
85+#include "easingcurve.h"
86+
87+
88+EasingCurve::EasingCurve(QObject *parent):
89+ QObject(parent)
90+{
91+
92+}
93+
94+QEasingCurve::Type EasingCurve::type() const
95+{
96+ return m_easingCurve.type();
97+}
98+
99+void EasingCurve::setType(const QEasingCurve::Type &type)
100+{
101+ m_easingCurve.setType(type);
102+ Q_EMIT typeChanged();
103+}
104+
105+qreal EasingCurve::period() const
106+{
107+ return m_easingCurve.period();
108+}
109+
110+void EasingCurve::setPeriod(qreal period)
111+{
112+ m_easingCurve.setPeriod(period);
113+ Q_EMIT periodChanged();
114+}
115+
116+qreal EasingCurve::progress() const
117+{
118+ return m_progress;
119+}
120+
121+void EasingCurve::setProgress(qreal progress)
122+{
123+ if (m_progress != progress) {
124+ m_progress = progress;
125+ m_value = m_easingCurve.valueForProgress(m_progress);
126+ Q_EMIT progressChanged();
127+ }
128+}
129+
130+qreal EasingCurve::value() const
131+{
132+ return m_value;
133+}
134
135=== added file 'plugins/Utils/easingcurve.h'
136--- plugins/Utils/easingcurve.h 1970-01-01 00:00:00 +0000
137+++ plugins/Utils/easingcurve.h 2014-03-25 15:08:15 +0000
138@@ -0,0 +1,70 @@
139+/*
140+ * Copyright 2014 Canonical Ltd.
141+ *
142+ * This program is free software; you can redistribute it and/or modify
143+ * it under the terms of the GNU Lesser General Public License as published by
144+ * the Free Software Foundation; version 3.
145+ *
146+ * This program is distributed in the hope that it will be useful,
147+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
148+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
149+ * GNU Lesser General Public License for more details.
150+ *
151+ * You should have received a copy of the GNU Lesser General Public License
152+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
153+ *
154+ * Authors: Michael Zanetti <michael.zanetti@canonical.com>
155+*/
156+
157+#ifndef EASINGCURVE_H
158+#define EASINGCURVE_H
159+
160+#include <QObject>
161+#include <QEasingCurve>
162+
163+/**
164+ * @brief The EasingCurve class
165+ *
166+ * This class exposes the QEasingCurve C++ API to QML.
167+ * This is useful for user interactive animations. While the QML Animation types
168+ * all require a "from", "to" and "duration", this one is based on "period" and
169+ * "progress". So you can control the position of the aimation by changing the
170+ * progress, also going back and forward in the animation. Depending on the type
171+ * of the easing curve, value will return the transformed progress.
172+ */
173+
174+class EasingCurve: public QObject
175+{
176+ Q_OBJECT
177+ Q_ENUMS(QEasingCurve::Type)
178+ Q_PROPERTY(QEasingCurve::Type type READ type WRITE setType NOTIFY typeChanged)
179+ Q_PROPERTY(qreal period READ period WRITE setPeriod NOTIFY periodChanged)
180+ Q_PROPERTY(qreal progress READ progress WRITE setProgress NOTIFY progressChanged)
181+ Q_PROPERTY(qreal value READ value NOTIFY progressChanged)
182+
183+public:
184+ EasingCurve(QObject *parent = 0);
185+
186+ QEasingCurve::Type type() const;
187+ void setType(const QEasingCurve::Type &type);
188+
189+ qreal period() const;
190+ void setPeriod(qreal period);
191+
192+ qreal progress() const;
193+ void setProgress(qreal progress);
194+
195+ qreal value() const;
196+
197+Q_SIGNALS:
198+ void typeChanged();
199+ void periodChanged();
200+ void progressChanged();
201+
202+private:
203+ QEasingCurve m_easingCurve;
204+ qreal m_progress;
205+ qreal m_value;
206+};
207+
208+#endif
209
210=== modified file 'plugins/Utils/plugin.cpp'
211--- plugins/Utils/plugin.cpp 2014-03-17 15:50:50 +0000
212+++ plugins/Utils/plugin.cpp 2014-03-25 15:08:15 +0000
213@@ -32,6 +32,7 @@
214 #include "qsortfilterproxymodelqml.h"
215 #include "timeformatter.h"
216 #include "unitymenumodelpaths.h"
217+#include "easingcurve.h"
218
219 static const char* BOTTOM_BAR_VISIBILITY_COMMUNICATOR_DBUS_PATH = "/BottomBarVisibilityCommunicator";
220 static const char* DBUS_SERVICE = "com.canonical.Shell.BottomBarVisibilityCommunicator";
221@@ -46,6 +47,7 @@
222 qmlRegisterType<TimeFormatter>(uri, 0, 1, "TimeFormatter");
223 qmlRegisterType<GDateTimeFormatter>(uri, 0, 1, "GDateTimeFormatter");
224 qmlRegisterUncreatableType<BottomBarVisibilityCommunicatorShell>(uri, 0, 1, "BottomBarVisibilityCommunicatorShell", "Can't create BottomBarVisibilityCommunicatorShell");
225+ qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
226 }
227
228 void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
229
230=== modified file 'qml/CMakeLists.txt'
231--- qml/CMakeLists.txt 2013-12-17 16:04:47 +0000
232+++ qml/CMakeLists.txt 2014-03-25 15:08:15 +0000
233@@ -13,7 +13,7 @@
234 Hud
235 Panel
236 Launcher
237- SideStage
238+ Stages
239 Notifications
240 )
241
242
243=== removed file 'qml/Components/ApplicationManagerWrapper.qml'
244--- qml/Components/ApplicationManagerWrapper.qml 2014-03-05 12:52:57 +0000
245+++ qml/Components/ApplicationManagerWrapper.qml 1970-01-01 00:00:00 +0000
246@@ -1,169 +0,0 @@
247-/*
248- * Copyright (C) 2013 Canonical, Ltd.
249- *
250- * This program is free software; you can redistribute it and/or modify
251- * it under the terms of the GNU General Public License as published by
252- * the Free Software Foundation; version 3.
253- *
254- * This program is distributed in the hope that it will be useful,
255- * but WITHOUT ANY WARRANTY; without even the implied warranty of
256- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
257- * GNU General Public License for more details.
258- *
259- * You should have received a copy of the GNU General Public License
260- * along with this program. If not, see <http://www.gnu.org/licenses/>.
261- */
262-
263-import QtQuick 2.0
264-import Unity.Application 0.1
265-
266-Item {
267- id: root
268-
269- property alias mainStageApplications: mainStageModel
270- property alias sideStageApplications: sideStageModel
271- property variant mainStageFocusedApplication: null
272- property variant sideStageFocusedApplication: null
273- property bool sideStageEnabled: true
274- property bool keyboardVisible: ApplicationManager.keyboardVisible
275- property int keyboardHeight: ApplicationManager.keyboardHeight
276-
277- property bool fake: ApplicationManager.fake ? ApplicationManager.fake : false
278-
279- signal focusRequested(string appId)
280-
281- ApplicationsModelStageFiltered {
282- id: mainStageModel
283- stage: ApplicationInfo.MainStage
284- }
285-
286- ApplicationsModelStageFiltered {
287- id: sideStageModel
288- stage: ApplicationInfo.SideStage
289- }
290-
291- Connections {
292- target: ApplicationManager
293- onFocusedApplicationIdChanged: {
294- var app = ApplicationManager.findApplication(ApplicationManager.focusedApplicationId)
295- if (!app) { //nothing at all focused, so clear all
296- mainStageFocusedApplication = null;
297- sideStageFocusedApplication = null;
298- } else {
299- if (app.stage == ApplicationInfo.MainStage) {
300- mainStageFocusedApplication = app;
301- // possible the side stage app being unfocused fired this signal, so check for it
302- if (sideStageFocusedApplication && !sideStageFocusedApplication.focused)
303- sideStageFocusedApplication = null;
304- } else {
305- sideStageFocusedApplication = app;
306- // possible the main stage app being unfocused fired this signal, so check for it
307- if (mainStageFocusedApplication && !mainStageFocusedApplication.focused)
308- mainStageFocusedApplication = null;
309- }
310- }
311- }
312-
313- onFocusRequested: {
314- // if no side stage enabled, override application's stage parameter
315- var app = ApplicationManager.findApplication(appId);
316- if (app && app.stage === ApplicationInfo.SideStage && !sideStageEnabled) {
317- app.stage = ApplicationInfo.MainStage;
318- }
319-
320- root.focusRequested(appId);
321- }
322- }
323-
324- function activateApplication(desktopFile, argument) {
325- var appId;
326-
327- // HACK: Applications identified sometimes with with appId, but mostly with desktopFile.
328- // TODO: convert entire shell to use appId only.
329- if (desktopFile.indexOf(".desktop") >= 0) {
330- appId = desktopFileToAppId(desktopFile);
331- } else {
332- appId = desktopFile;
333- }
334-
335- var application = ApplicationManager.findApplication(appId);
336- if (application !== null) {
337- return application;
338- }
339-
340- var execFlags = sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
341-
342- if (argument) {
343- return ApplicationManager.startApplication(appId, execFlags, [argument]);
344- } else {
345- return ApplicationManager.startApplication(appId, execFlags);
346- }
347- }
348-
349- function stopApplication(application) {
350- var appId;
351-
352- // HACK: Applications identified sometimes with with appId, but mostly with desktopFile.
353- // TODO: convert entire shell to use appId only.
354- if (typeof application == "string") {
355- appId = application;
356- } else {
357- appId = desktopFileToAppId(application.desktopFile);
358- }
359-
360- ApplicationManager.stopApplication(appId);
361- }
362-
363- function focusApplication(application) {
364- if (application == null || application == undefined) {
365- return;
366- }
367-
368- ApplicationManager.focusApplication(application.appId);
369- }
370-
371- function unfocusCurrentApplication() {
372- ApplicationManager.unfocusCurrentApplication();
373- }
374-
375- function moveRunningApplicationStackPosition(from, to, stage) {
376- if (from == to || from < 0 || to < 0) return;
377-
378- if (stage == ApplicationInfo.SideStage) {
379- sideStageModel.move(from, to);
380- } else {
381- mainStageModel.move(from, to);
382- }
383- }
384-
385- function getApplicationFromDesktopFile(desktopFile, stage) {
386- var appId;
387-
388- // HACK: Applications identified sometimes with with appId, but mostly with desktopFile.
389- // TODO: convert entire shell to use appId only.
390- if (desktopFile.indexOf(".desktop") >= 0) {
391- appId = desktopFileToAppId(desktopFile);
392- } else {
393- appId = desktopFile;
394- }
395-
396- for (var i = 0, len = ApplicationManager.count; i < len; i++ ) {
397- var app = ApplicationManager.get(i);
398-
399- // if stage not specified, return whichever app running on either stage
400- if (app.appId == appId && (stage == undefined || app.stage == stage)) {
401- return app;
402- }
403- }
404- }
405-
406- function desktopFileToAppId(desktopFile) {
407- var right = desktopFile.lastIndexOf(".desktop");
408- var left = desktopFile.lastIndexOf("/");
409- if (left == -1 || right == -1 || left == right) {
410- console.log("ApplicationManagerWrapper: unable to extract appId from '" + desktopFile + "'");
411- return "";
412- }
413- return desktopFile.substring(left+1, right);
414- }
415-}
416
417=== removed file 'qml/Components/ApplicationScreenshot.qml'
418--- qml/Components/ApplicationScreenshot.qml 2014-03-05 12:52:57 +0000
419+++ qml/Components/ApplicationScreenshot.qml 1970-01-01 00:00:00 +0000
420@@ -1,61 +0,0 @@
421-/*
422- * Copyright (C) 2013 Canonical, Ltd.
423- *
424- * This program is free software; you can redistribute it and/or modify
425- * it under the terms of the GNU General Public License as published by
426- * the Free Software Foundation; version 3.
427- *
428- * This program is distributed in the hope that it will be useful,
429- * but WITHOUT ANY WARRANTY; without even the implied warranty of
430- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
431- * GNU General Public License for more details.
432- *
433- * You should have received a copy of the GNU General Public License
434- * along with this program. If not, see <http://www.gnu.org/licenses/>.
435- */
436-
437-import QtQuick 2.0
438-import Ubuntu.Components 0.1
439-import Unity.Application 0.1
440-
441-Item {
442- id: applicationScreenshot
443-
444- property var application: null
445- property bool withBackground: false
446- property bool ready: applicationImage.ready || withBackground
447-
448- function setApplication(application) {
449- applicationScreenshot.application = application
450- }
451-
452- function clearApplication() {
453- applicationScreenshot.withBackground = false;
454- applicationScreenshot.application = null
455- applicationScreenshot.scheduleUpdate();
456- }
457-
458- function scheduleUpdate() {
459- applicationImage.scheduleUpdate()
460- }
461-
462- function updateFromCache() {
463- applicationImage.updateFromCache()
464- }
465-
466- Rectangle {
467- id: background
468- anchors.fill: parent
469- color: "white" // FIXME should use normal background color of Suru theme
470- visible: applicationScreenshot.withBackground
471- }
472-
473- ApplicationImage {
474- id: applicationImage
475- objectName: "screenshot image"
476- width: applicationScreenshot.application ? parent.width : 0
477- height: applicationScreenshot.application ? parent.height : 0
478- visible: applicationScreenshot.application != null && ready
479- source: ApplicationManager.findApplication((application) ? application.appId : "")
480- }
481-}
482
483=== removed file 'qml/Components/ApplicationsModelStageFiltered.qml'
484--- qml/Components/ApplicationsModelStageFiltered.qml 2013-09-09 10:20:21 +0000
485+++ qml/Components/ApplicationsModelStageFiltered.qml 1970-01-01 00:00:00 +0000
486@@ -1,49 +0,0 @@
487-/*
488- * Copyright (C) 2013 Canonical, Ltd.
489- *
490- * This program is free software; you can redistribute it and/or modify
491- * it under the terms of the GNU General Public License as published by
492- * the Free Software Foundation; version 3.
493- *
494- * This program is distributed in the hope that it will be useful,
495- * but WITHOUT ANY WARRANTY; without even the implied warranty of
496- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
497- * GNU General Public License for more details.
498- *
499- * You should have received a copy of the GNU General Public License
500- * along with this program. If not, see <http://www.gnu.org/licenses/>.
501- */
502-
503-import QtQuick 2.0
504-import Utils 0.1
505-import Unity.Application 0.1
506-
507-SortFilterProxyModel {
508- id: root
509- property int stage
510-
511- function get(index) {
512- return model.get(mapRowToSource(index));
513- }
514-
515- function move(from, to) {
516- var realFrom = mapRowToSource(from);
517- if (realFrom == -1) {
518- console.log("ERROR; invalid from=" + from + " index in move operation");
519- return;
520- }
521- var realTo = mapRowToSource(to);
522- if (realTo == -1) {
523- // assuming wanting to move to end of list
524- realTo = model.count - 1;
525- }
526-
527- if (realFrom == realTo) return;
528- model.move(realFrom, realTo);
529- }
530-
531- model: ApplicationManager
532- dynamicSortFilter: true
533- filterRole: ApplicationManager.RoleStage
534- filterRegExp: RegExp(stage)
535-}
536
537=== modified file 'qml/Components/ResponsiveFlowView.qml'
538--- qml/Components/ResponsiveFlowView.qml 2013-06-05 22:03:08 +0000
539+++ qml/Components/ResponsiveFlowView.qml 2014-03-25 15:08:15 +0000
540@@ -27,9 +27,8 @@
541 property alias verticalSpacing: flow.verticalSpacing
542 property alias horizontalSpacing: flow.horizontalSpacing
543 property int referenceDelegateWidth
544- property alias firstModel: repeater1.model
545- property alias secondModel: repeater2.model
546- property alias delegate: repeater1.delegate
547+ property alias model: repeater.model
548+ property alias delegate: repeater.delegate
549 readonly property int cellWidth: referenceDelegateWidth + horizontalSpacing
550 readonly property int cellHeight: referenceDelegateWidth + verticalSpacing
551 property alias move: flow.move
552@@ -69,13 +68,7 @@
553 property int margin: allocatableVerticalSpace - columns * horizontalSpacing
554
555 Repeater {
556- id: repeater1
557- model: (root.model) ? root.model[0] : null
558- }
559- Repeater {
560- id: repeater2
561- model: (root.model) ? root.model[1] : null
562- delegate: repeater1.delegate
563+ id: repeater
564 }
565 }
566 }
567
568=== modified file 'qml/Dash/Apps/RunningApplicationTile.qml'
569--- qml/Dash/Apps/RunningApplicationTile.qml 2014-03-12 10:44:18 +0000
570+++ qml/Dash/Apps/RunningApplicationTile.qml 2014-03-25 15:08:15 +0000
571@@ -23,7 +23,6 @@
572 AbstractButton {
573 id: root
574 property var application
575- property bool __sideStageEnabled: shell.applicationManager.sideStageEnabled
576
577 signal requestedApplicationActivation(var application)
578 signal requestedApplicationTermination(var application)
579@@ -34,7 +33,7 @@
580 // To avoid "binding loop" warning
581 height: shapedApplicationImage.height + labelContainer.height
582
583- width: shapedApplicationImage.width
584+ width: shapedApplicationImage.width <= units.gu(11) ? units.gu(11) : height
585
586 property bool terminationModeEnabled: false
587
588@@ -51,32 +50,23 @@
589 }
590 }
591
592- function updateScreenshotFromCache() {
593- applicationImage.updateFromCache();
594- }
595-
596- // FIXME: should use UbuntuShape from SDK
597- UbuntuShapeForItem {
598+ UbuntuShape {
599 id: shapedApplicationImage
600- anchors {
601- top: parent.top
602- horizontalCenter: parent.horizontalCenter
603+ anchors { top: parent.top; horizontalCenter: parent.horizontalCenter }
604+
605+ height: units.gu(17)
606+ width: applicationImage.width
607+ radius: "medium"
608+
609+ image: Image {
610+ id: applicationImage
611+ source: application.screenshot
612+ // height : width = ss.height : ss.width
613+ height: shapedApplicationImage.height
614+ fillMode: Image.PreserveAspectCrop
615+ width: Math.min(height, height * sourceSize.width / sourceSize.height)
616 }
617
618- // FIXME: width and height should be defined according to the
619- // application window's aspect ratio.
620- width: (application.stage === ApplicationInfo.MainStage && __sideStageEnabled) ?
621- units.gu(22) : units.gu(11)
622- height: (__sideStageEnabled) ? units.gu(22) : units.gu(19)
623- radius: "medium"
624- image: applicationImage
625- }
626-
627- ApplicationImage {
628- id: applicationImage
629- source: ApplicationManager.findApplication((application) ? application.appId : "")
630- width: shapedApplicationImage.width
631- height: shapedApplicationImage.height
632 }
633
634 UbuntuShape {
635
636=== modified file 'qml/Dash/Apps/RunningApplicationsGrid.qml'
637--- qml/Dash/Apps/RunningApplicationsGrid.qml 2014-02-26 11:04:54 +0000
638+++ qml/Dash/Apps/RunningApplicationsGrid.qml 2014-03-25 15:08:15 +0000
639@@ -18,6 +18,7 @@
640 import "../../Components"
641
642 import Ubuntu.Gestures 0.1
643+import Unity.Application 0.1
644
645 ResponsiveFlowView {
646 id: root
647@@ -25,13 +26,7 @@
648
649 signal updateScreenshots
650 property alias enableHeightBehavior: heightBehaviour.enabled
651- property bool enableHeightBehaviorOnNextCreation: firstModel.count + secondModel.count == 0
652-
653- Connections {
654- target: shell
655- onDashShownChanged: if (shell.dashShown && shell.stageScreenshotsReady) updateScreenshots();
656- onStageScreenshotsReadyChanged: if (shell.dashShown && shell.stageScreenshotsReady) updateScreenshots();
657- }
658+ property bool enableHeightBehaviorOnNextCreation: model.count === 0
659
660 Behavior on height {
661 id: heightBehaviour
662@@ -40,13 +35,7 @@
663 }
664
665 Connections {
666- target: root.firstModel
667- onCountChanged: {
668- heightBehaviour.enabled = true;
669- }
670- }
671- Connections {
672- target: root.secondModel
673+ target: root.model
674 onCountChanged: {
675 heightBehaviour.enabled = true;
676 }
677@@ -85,17 +74,13 @@
678 root.terminationModeEnabled = true
679 }
680 onRequestedApplicationTermination: {
681- shell.applicationManager.stopApplication(model.appId)
682+ ApplicationManager.stopApplication(model.appId)
683 }
684 onRequestedApplicationActivation: {
685- shell.activateApplication(model.appId)
686+ ApplicationManager.requestFocusApplication(model.appId)
687 }
688
689 terminationModeEnabled: root.terminationModeEnabled
690-
691- Component.onCompleted: {
692- root.updateScreenshots.connect(updateScreenshotFromCache);
693- }
694 }
695 }
696
697
698=== modified file 'qml/Dash/DashApps.qml'
699--- qml/Dash/DashApps.qml 2014-02-19 11:15:01 +0000
700+++ qml/Dash/DashApps.qml 2014-03-25 15:08:15 +0000
701@@ -17,6 +17,7 @@
702 import QtQuick 2.0
703 import Ubuntu.Components 0.1
704 import Utils 0.1
705+import Unity.Application 0.1
706 import "../Components"
707 import "../Components/ListItems"
708 import "Apps"
709@@ -25,17 +26,15 @@
710 id: scopeView
711 objectName: "DashApps"
712
713- // FIXME: a way to aggregate these models would be ideal
714- property var mainStageApplicationsModel: shell.applicationManager.mainStageApplications
715- property var sideStageApplicationModel: shell.applicationManager.sideStageApplications
716+ property var runningApps: ApplicationManager
717
718 QtObject {
719 id: countObject
720- property int count: scopeView.scope.searchQuery.length == 0 ? (mainStageApplicationsModel.count + sideStageApplicationModel.count) : 0
721+ property int count: scopeView.scope.searchQuery.length == 0 ? scopeView.runningApps.count : 0
722 }
723
724 onScopeChanged: {
725 scopeView.scope.categories.addSpecialCategory("running.apps.category", "Recent", "", "{ \"template\": { \"category-layout\": \"running-apps\" } }", countObject);
726- enableHeightBehaviorOnNextCreation = (mainStageApplicationsModel.count + sideStageApplicationModel.count == 0)
727+ enableHeightBehaviorOnNextCreation = scopeView.runningApps.count === 0
728 }
729 }
730
731=== modified file 'qml/Dash/GenericScopeView.qml'
732--- qml/Dash/GenericScopeView.qml 2014-03-17 11:44:05 +0000
733+++ qml/Dash/GenericScopeView.qml 2014-03-25 15:08:15 +0000
734@@ -18,6 +18,7 @@
735 import Ubuntu.Components 0.1
736 import Utils 0.1
737 import Unity 0.2
738+import Unity.Application 0.1
739 import "../Components"
740 import "../Components/ListItems" as ListItems
741
742@@ -167,8 +168,7 @@
743 if (source.toString().indexOf("Apps/RunningApplicationsGrid.qml") != -1) {
744 // TODO: this is still a kludge :D Ideally add some kind of hook so that we
745 // can do this from DashApps.qml or think a better way that needs no special casing
746- item.firstModel = Qt.binding(function() { return mainStageApplicationsModel })
747- item.secondModel = Qt.binding(function() { return sideStageApplicationModel })
748+ item.model = Qt.binding(function() { return runningApps; })
749 item.canEnableTerminationMode = Qt.binding(function() { return scopeView.isCurrent })
750 } else {
751 item.model = Qt.binding(function() { return results })
752
753=== modified file 'qml/Launcher/Launcher.qml'
754--- qml/Launcher/Launcher.qml 2013-12-11 12:57:14 +0000
755+++ qml/Launcher/Launcher.qml 2014-03-25 15:08:15 +0000
756@@ -29,7 +29,8 @@
757 property int dragAreaWidth: units.gu(1)
758 property int minimizeDistance: units.gu(26)
759 property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?
760- (width * (dragArea.touchX-panelWidth) / (width - panelWidth)) : 0
761+ (width * (dragArea.touchX-panelWidth) / (width - panelWidth)) :
762+ (dragArea.dragging ? 0.001 : 0)
763
764 readonly property bool shown: panel.x > -panel.width
765
766
767=== modified file 'qml/Panel/Panel.qml'
768--- qml/Panel/Panel.qml 2014-01-14 15:35:57 +0000
769+++ qml/Panel/Panel.qml 2014-03-25 15:08:15 +0000
770@@ -62,7 +62,7 @@
771 height: __panelMinusSeparatorLineHeight
772 y: 0
773
774- Behavior on y { StandardAnimation {} }
775+ Behavior on y { StandardAnimation { duration: UbuntuAnimation.FastDuration } }
776 }
777
778 PanelSeparatorLine {
779
780=== modified file 'qml/Shell.qml'
781--- qml/Shell.qml 2014-03-19 22:21:57 +0000
782+++ qml/Shell.qml 2014-03-25 15:08:15 +0000
783@@ -31,7 +31,6 @@
784 import "Hud"
785 import "Components"
786 import "Bottombar"
787-import "SideStage"
788 import "Notifications"
789 import Unity.Notifications 1.0 as NotificationBackend
790
791@@ -49,20 +48,24 @@
792 readonly property real panelHeight: panel.panelHeight
793
794 property bool dashShown: dash.shown
795- property bool stageScreenshotsReady: {
796- if (sideStage.shown) {
797- if (mainStage.applications.count > 0) {
798- return mainStage.usingScreenshots && sideStage.usingScreenshots;
799- } else {
800- return sideStage.usingScreenshots;
801+
802+ property bool sideStageEnabled: shell.width >= units.gu(60)
803+ readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
804+
805+ function activateApplication(appId) {
806+ if (ApplicationManager.findApplication(appId)) {
807+ ApplicationManager.requestFocusApplication(appId);
808+ stages.show();
809+ if (stages.locked && ApplicationManager.focusedApplicationId == appId) {
810+ applicationsDisplayLoader.item.select(appId);
811 }
812 } else {
813- return mainStage.usingScreenshots;
814+ var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
815+ ApplicationManager.startApplication(appId, execFlags);
816+ stages.show();
817 }
818 }
819
820- property var applicationManager: ApplicationManagerWrapper {}
821-
822 Binding {
823 target: LauncherModel
824 property: "applicationManager"
825@@ -71,49 +74,6 @@
826
827 Component.onCompleted: {
828 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
829-
830- applicationManager.sideStageEnabled = Qt.binding(function() { return sideStage.enabled })
831-
832- // FIXME: if application focused before shell starts, shell draws on top of it only.
833- // We should detect already running applications on shell start and bring them to the front.
834- applicationManager.unfocusCurrentApplication();
835- }
836-
837- readonly property bool applicationFocused: !!applicationManager.mainStageFocusedApplication
838- || !!applicationManager.sideStageFocusedApplication
839- // Used for autopilot testing.
840- readonly property string currentFocusedAppId: ApplicationManager.focusedApplicationId
841-
842- readonly property bool fullscreenMode: {
843- if (greeter.shown || lockscreen.shown) {
844- return false;
845- } else if (mainStage.usingScreenshots) { // Window Manager animating so want to re-evaluate fullscreen mode
846- return mainStage.switchingFromFullscreenToFullscreen;
847- } else if (applicationManager.mainStageFocusedApplication) {
848- return applicationManager.mainStageFocusedApplication.fullscreen;
849- } else {
850- return false;
851- }
852- }
853-
854- function activateApplication(appId, argument) {
855- if (applicationManager) {
856- // For newly started applications, as it takes them time to draw their first frame
857- // we add a delay before we hide the animation screenshots to compensate.
858- var addDelay = !applicationManager.getApplicationFromDesktopFile(appId);
859-
860- var application;
861- application = applicationManager.activateApplication(appId, argument);
862- if (application == null) {
863- return;
864- }
865- if (application.stage == ApplicationInfo.MainStage || !sideStage.enabled) {
866- mainStage.activateApplication(appId, addDelay);
867- } else {
868- sideStage.activateApplication(appId, addDelay);
869- }
870- stages.show();
871- }
872 }
873
874 GSettings {
875@@ -133,244 +93,219 @@
876 Keys.onVolumeDownPressed: volumeControl.volumeDown()
877
878 Item {
879- id: underlay
880- objectName: "underlay"
881+ id: underlayClipper
882 anchors.fill: parent
883-
884- // Whether the underlay is fully covered by opaque UI elements.
885- property bool fullyCovered: panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth
886-
887- readonly property bool applicationRunning: ((mainStage.applications && mainStage.applications.count > 0)
888- || (sideStage.applications && sideStage.applications.count > 0))
889-
890- // Whether the user should see the topmost application surface (if there's one at all).
891- readonly property bool applicationSurfaceShouldBeSeen: applicationRunning && !stages.fullyHidden
892- && !mainStage.usingScreenshots // but want sideStage animating over app surface
893-
894-
895-
896- // NB! Application surfaces are stacked behing the shell one. So they can only be seen by the user
897- // through the translucent parts of the shell surface.
898- visible: !fullyCovered && !applicationSurfaceShouldBeSeen
899-
900- Rectangle {
901- anchors.fill: parent
902- color: "black"
903- opacity: dash.disappearingAnimationProgress
904- }
905-
906- Image {
907- anchors.fill: dash
908- source: shell.width > shell.height ? "Dash/graphics/paper_landscape.png" : "Dash/graphics/paper_portrait.png"
909- fillMode: Image.PreserveAspectCrop
910- horizontalAlignment: Image.AlignRight
911- verticalAlignment: Image.AlignTop
912- }
913-
914- Dash {
915- id: dash
916- objectName: "dash"
917-
918- available: !greeter.shown && !lockscreen.shown
919- hides: [stages, launcher, panel.indicators]
920- shown: disappearingAnimationProgress !== 1.0
921- enabled: disappearingAnimationProgress === 0.0 && edgeDemo.dashEnabled
922- // FIXME: unfocus all applications when going back to the dash
923- onEnabledChanged: {
924- if (enabled) {
925- shell.applicationManager.unfocusCurrentApplication()
926- }
927- }
928-
929- anchors {
930- fill: parent
931- topMargin: panel.panelHeight
932- }
933-
934- contentScale: 1.0 - 0.2 * disappearingAnimationProgress
935- opacity: 1.0 - disappearingAnimationProgress
936- property real disappearingAnimationProgress: {
937- if (greeter.shown) {
938- return greeter.showProgress;
939+ anchors.rightMargin: stages.overlayWidth
940+ clip: stages.overlayMode && !stages.painting
941+
942+ InputFilterArea {
943+ anchors.fill: parent
944+ blockInput: parent.clip
945+ }
946+
947+ Item {
948+ id: underlay
949+ objectName: "underlay"
950+ anchors.fill: parent
951+ anchors.rightMargin: -parent.anchors.rightMargin
952+
953+ // Whether the underlay is fully covered by opaque UI elements.
954+ property bool fullyCovered: panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth
955+
956+ // Whether the user should see the topmost application surface (if there's one at all).
957+ readonly property bool applicationSurfaceShouldBeSeen: stages.shown && !stages.painting && !stages.overlayMode
958+
959+ // NB! Application surfaces are stacked behind the shell one. So they can only be seen by the user
960+ // through the translucent parts of the shell surface.
961+ visible: !fullyCovered && !applicationSurfaceShouldBeSeen
962+
963+ CrossFadeImage {
964+ id: backgroundImage
965+ objectName: "backgroundImage"
966+
967+ anchors.fill: parent
968+ source: shell.background
969+ fillMode: Image.PreserveAspectCrop
970+ }
971+
972+ Rectangle {
973+ anchors.fill: parent
974+ color: "black"
975+ opacity: dash.disappearingAnimationProgress
976+ }
977+
978+ Image {
979+ anchors.fill: dash
980+ source: shell.width > shell.height ? "Dash/graphics/paper_landscape.png" : "Dash/graphics/paper_portrait.png"
981+ fillMode: Image.PreserveAspectCrop
982+ horizontalAlignment: Image.AlignRight
983+ verticalAlignment: Image.AlignTop
984+ }
985+
986+ Dash {
987+ id: dash
988+ objectName: "dash"
989+
990+ available: !greeter.shown && !lockscreen.shown
991+ hides: [stages, launcher, panel.indicators]
992+ shown: disappearingAnimationProgress !== 1.0
993+ enabled: disappearingAnimationProgress === 0.0 && edgeDemo.dashEnabled
994+
995+ anchors {
996+ fill: parent
997+ topMargin: panel.panelHeight
998+ }
999+
1000+ contentScale: 1.0 - 0.2 * disappearingAnimationProgress
1001+ opacity: 1.0 - disappearingAnimationProgress
1002+ property real disappearingAnimationProgress: {
1003+ if (greeter.shown) {
1004+ return greeter.showProgress;
1005+ } else {
1006+ if (stages.overlayMode) {
1007+ return 0;
1008+ }
1009+ return stages.showProgress
1010+ }
1011+ }
1012+
1013+ // FIXME: only necessary because stages.showProgress and
1014+ // greeterRevealer.animatedProgress are not animated
1015+ Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}
1016+ }
1017+ }
1018+ }
1019+
1020+ EdgeDragArea {
1021+ id: stagesDragHandle
1022+ direction: Direction.Leftwards
1023+
1024+ anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
1025+ width: shell.edgeSize
1026+
1027+ property real progress: stages.width
1028+
1029+ onTouchXChanged: {
1030+ if (status == DirectionalDragArea.Recognized) {
1031+ if (ApplicationManager.count == 0) {
1032+ progress = Math.max(stages.width - stagesDragHandle.width + touchX, stages.width * .3)
1033 } else {
1034- return stagesOuterContainer.showProgress;
1035+ progress = stages.width - stagesDragHandle.width + touchX
1036 }
1037 }
1038+ }
1039
1040- // FIXME: only necessary because stagesOuterContainer.showProgress and
1041- // greeterRevealer.animatedProgress are not animated
1042- Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}
1043+ onDraggingChanged: {
1044+ if (!dragging) {
1045+ if (ApplicationManager.count > 0 && progress < stages.width - units.gu(10)) {
1046+ stages.show()
1047+ }
1048+ stagesDragHandle.progress = stages.width;
1049+ }
1050 }
1051 }
1052
1053 Item {
1054- id: stagesOuterContainer
1055-
1056+ id: stages
1057+ objectName: "stages"
1058 width: parent.width
1059 height: parent.height
1060- x: launcher.progress
1061- Behavior on x {SmoothedAnimation{velocity: 600}}
1062-
1063- property real showProgress:
1064- MathUtils.clamp(1 - (x + stages.x) / shell.width, 0, 1)
1065-
1066- Showable {
1067- id: stages
1068- objectName: "stages"
1069-
1070- x: width
1071-
1072- property bool fullyShown: shown && x == 0 && parent.x == 0
1073- property bool fullyHidden: !shown && x == width
1074- available: !greeter.shown
1075- hides: [panel.indicators]
1076- shown: false
1077- opacity: 1.0
1078- showAnimation: StandardAnimation { property: "x"; duration: 350; to: 0; easing.type: Easing.OutCubic }
1079- hideAnimation: StandardAnimation { property: "x"; duration: 350; to: width; easing.type: Easing.OutCubic }
1080-
1081- width: parent.width
1082- height: parent.height
1083-
1084- // close the stages when no focused application remains
1085- Connections {
1086- target: shell.applicationManager
1087- onMainStageFocusedApplicationChanged: stages.closeIfNoApplications()
1088- onSideStageFocusedApplicationChanged: stages.closeIfNoApplications()
1089- ignoreUnknownSignals: true
1090- }
1091-
1092- function closeIfNoApplications() {
1093- if (!shell.applicationManager.mainStageFocusedApplication
1094- && !shell.applicationManager.sideStageFocusedApplication
1095- && shell.applicationManager.mainStageApplications.count == 0
1096- && shell.applicationManager.sideStageApplications.count == 0) {
1097- stages.hide();
1098- }
1099- }
1100-
1101- // show the stages when an application gets the focus
1102- Connections {
1103- target: shell.applicationManager
1104- onMainStageFocusedApplicationChanged: {
1105- if (shell.applicationManager.mainStageFocusedApplication) {
1106- mainStage.show();
1107- stages.show();
1108- }
1109- }
1110- onSideStageFocusedApplicationChanged: {
1111- if (shell.applicationManager.sideStageFocusedApplication) {
1112- sideStage.show();
1113- stages.show();
1114- }
1115- }
1116- ignoreUnknownSignals: true
1117- }
1118-
1119- Stage {
1120- id: mainStage
1121-
1122- anchors.fill: parent
1123- fullyShown: stages.fullyShown
1124- fullyHidden: stages.fullyHidden
1125- shouldUseScreenshots: !fullyShown
1126- rightEdgeEnabled: !sideStage.enabled
1127-
1128- applicationManager: shell.applicationManager
1129- rightEdgeDraggingAreaWidth: shell.edgeSize
1130- normalApplicationY: shell.panelHeight
1131-
1132- shown: true
1133- function show() {
1134+
1135+ x: {
1136+ if (shown) {
1137+ if (overlayMode || locked) {
1138+ return 0;
1139+ }
1140+ return launcher.progress
1141+ } else {
1142+ return stagesDragHandle.progress
1143+ }
1144+ }
1145+
1146+ Behavior on x { SmoothedAnimation { velocity: 600; duration: UbuntuAnimation.FastDuration } }
1147+
1148+ property bool shown: false
1149+
1150+ property real showProgress: overlayMode ? 0 : MathUtils.clamp(1 - x / shell.width, 0, 1)
1151+
1152+ property bool fullyShown: x == 0
1153+ property bool fullyHidden: x == width
1154+
1155+ property bool painting: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.painting : false
1156+ property bool fullscreen: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.fullscreen : false
1157+ property bool overlayMode: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.overlayMode : false
1158+ property int overlayWidth: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.overlayWidth : false
1159+ property bool locked: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.locked : false
1160+
1161+ function show() {
1162+ shown = true;
1163+ panel.indicators.hide();
1164+ if (!ApplicationManager.focusedApplicationId && ApplicationManager.count > 0) {
1165+ ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
1166+ }
1167+ }
1168+
1169+ function hide() {
1170+ shown = false;
1171+ if (ApplicationManager.focusedApplicationId) {
1172+ ApplicationManager.unfocusCurrentApplication();
1173+ }
1174+ }
1175+
1176+ Connections {
1177+ target: ApplicationManager
1178+
1179+ onFocusRequested: {
1180+ stages.show();
1181+ }
1182+
1183+ onFocusedApplicationIdChanged: {
1184+ if (ApplicationManager.focusedApplicationId.length > 0) {
1185 stages.show();
1186- }
1187- function hide() {
1188- }
1189-
1190- // FIXME: workaround the fact that focusing a main stage application
1191- // raises its surface on top of all other surfaces including the ones
1192- // that belong to side stage applications.
1193- onFocusedApplicationChanged: {
1194- if (focusedApplication && sideStage.focusedApplication && sideStage.fullyShown) {
1195- shell.applicationManager.focusApplication(sideStage.focusedApplication);
1196- }
1197- }
1198- }
1199-
1200- SideStage {
1201- id: sideStage
1202-
1203- applicationManager: shell.applicationManager
1204- rightEdgeDraggingAreaWidth: shell.edgeSize
1205- normalApplicationY: shell.panelHeight
1206-
1207- onShownChanged: {
1208- if (!shown && mainStage.applications.count == 0) {
1209+ } else {
1210+ if (!stages.overlayMode) {
1211 stages.hide();
1212 }
1213 }
1214- // FIXME: when hiding the side stage, refocus the main stage
1215- // application so that it goes in front of the side stage
1216- // application and hides it
1217- onFullyShownChanged: {
1218- if (!fullyShown && stages.fullyShown && sideStage.focusedApplication != null) {
1219- shell.applicationManager.focusApplication(mainStage.focusedApplication);
1220- }
1221+
1222+ // If any app is focused when greeter is open, it's due to a user action
1223+ // like a snap decision (say, an incoming call).
1224+ // TODO: these should be protected to only unlock for certain applications / certain usecases
1225+ // potentially only in connection with a notification.
1226+ greeter.hide()
1227+ }
1228+
1229+ onApplicationAdded: {
1230+ stages.show();
1231+ }
1232+
1233+ onApplicationRemoved: {
1234+ if (ApplicationManager.focusedApplicationId.length == 0) {
1235+ stages.hide();
1236 }
1237-
1238- enabled: shell.width >= units.gu(100)
1239- visible: enabled
1240- fullyShown: stages.fullyShown && shown
1241- && sideStage[sideStageRevealer.boundProperty] == sideStageRevealer.openedValue
1242- shouldUseScreenshots: !fullyShown || mainStage.usingScreenshots || sideStageRevealer.pressed
1243-
1244- available: !greeter.shown && !lockscreen.shown && enabled
1245- hides: [launcher, panel.indicators]
1246- shown: false
1247- showAnimation: StandardAnimation { property: "x"; duration: 350; to: sideStageRevealer.openedValue; easing.type: Easing.OutQuint }
1248- hideAnimation: StandardAnimation { property: "x"; duration: 350; to: sideStageRevealer.closedValue; easing.type: Easing.OutQuint }
1249-
1250- width: units.gu(40)
1251- height: stages.height
1252- handleExpanded: sideStageRevealer.pressed
1253- }
1254-
1255- Revealer {
1256- id: sideStageRevealer
1257-
1258- enabled: mainStage.applications.count > 0 && sideStage.applications.count > 0
1259- && sideStage.available
1260- direction: Qt.RightToLeft
1261- openedValue: parent.width - sideStage.width
1262- hintDisplacement: units.gu(3)
1263- /* The size of the sidestage handle needs to be bigger than the
1264- typical size used for edge detection otherwise it is really
1265- hard to grab.
1266- */
1267- handleSize: sideStage.shown ? units.gu(4) : shell.edgeSize
1268- closedValue: parent.width + sideStage.handleSizeCollapsed
1269- target: sideStage
1270- x: parent.width - width
1271- width: sideStage.width + handleSize * 0.7
1272- height: sideStage.height
1273- orientation: Qt.Horizontal
1274- }
1275-
1276- DragHandle {
1277- id: stagesDragHandle
1278-
1279- anchors.top: parent.top
1280- anchors.bottom: parent.bottom
1281- anchors.right: parent.left
1282-
1283- width: shell.edgeSize
1284- direction: Direction.Leftwards
1285- enabled: greeter.showProgress == 0 && edgeDemo.dashEnabled
1286- property bool haveApps: mainStage.applications.count > 0 || sideStage.applications.count > 0
1287-
1288- maxTotalDragDistance: haveApps ? parent.width : parent.width * 0.7
1289- // Make autocompletion impossible when !haveApps
1290- edgeDragEvaluator.minDragDistance: haveApps ? maxTotalDragDistance * 0.1 : Number.MAX_VALUE
1291+ }
1292+ }
1293+
1294+ Loader {
1295+ id: applicationsDisplayLoader
1296+ anchors.fill: parent
1297+
1298+ source: shell.sideStageEnabled ? "Stages/StageWithSideStage.qml" : "Stages/PhoneStage.qml"
1299+
1300+ Binding {
1301+ target: applicationsDisplayLoader.item
1302+ property: "moving"
1303+ value: !stages.fullyShown
1304+ }
1305+ Binding {
1306+ target: applicationsDisplayLoader.item
1307+ property: "shown"
1308+ value: stages.shown
1309+ }
1310+ Binding {
1311+ target: applicationsDisplayLoader.item
1312+ property: "dragAreaWidth"
1313+ value: shell.edgeSize
1314 }
1315 }
1316 }
1317@@ -447,26 +382,6 @@
1318
1319 dragHandleWidth: shell.edgeSize
1320
1321- property var previousMainApp: null
1322- property var previousSideApp: null
1323-
1324- function removeApplicationFocus() {
1325- greeter.previousMainApp = applicationManager.mainStageFocusedApplication;
1326- greeter.previousSideApp = applicationManager.sideStageFocusedApplication;
1327- applicationManager.unfocusCurrentApplication();
1328- }
1329-
1330- function restoreApplicationFocus() {
1331- if (greeter.previousMainApp) {
1332- applicationManager.focusApplication(greeter.previousMainApp);
1333- greeter.previousMainApp = null;
1334- }
1335- if (greeter.previousSideApp) {
1336- applicationManager.focusApplication(greeter.previousSideApp);
1337- greeter.previousSideApp = null;
1338- }
1339- }
1340-
1341 onShownChanged: {
1342 if (shown) {
1343 lockscreen.reset();
1344@@ -476,9 +391,6 @@
1345 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
1346 }
1347 greeter.forceActiveFocus();
1348- removeApplicationFocus();
1349- } else {
1350- restoreApplicationFocus();
1351 }
1352 }
1353
1354@@ -496,33 +408,16 @@
1355 }
1356 }
1357
1358- Connections {
1359- target: applicationManager
1360- ignoreUnknownSignals: true
1361- // If any app is focused when greeter is open, it's due to a user action
1362- // like a snap decision (say, an incoming call).
1363- // TODO: these should be protected to only unlock for certain applications / certain usecases
1364- // potentially only in connection with a notification.
1365- onMainStageFocusedApplicationChanged: {
1366- if (greeter.shown && applicationManager.mainStageFocusedApplication) {
1367- greeter.previousMainApp = null // make way for new focused app
1368- greeter.previousSideApp = null
1369- greeter.hide()
1370- }
1371- }
1372- onSideStageFocusedApplicationChanged: {
1373- if (greeter.shown && applicationManager.sideStageFocusedApplication) {
1374- greeter.previousMainApp = null // make way for new focused app
1375- greeter.previousSideApp = null
1376- greeter.hide()
1377- }
1378- }
1379+ Binding {
1380+ target: ApplicationManager
1381+ property: "suspended"
1382+ value: greeter.shown && greeter.showProgress == 1
1383 }
1384 }
1385
1386 InputFilterArea {
1387 anchors.fill: parent
1388- blockInput: !applicationFocused || greeter.shown || lockscreen.shown || launcher.shown
1389+ blockInput: ApplicationManager.focusedApplicationId.length === 0 || greeter.shown || lockscreen.shown || launcher.shown
1390 || panel.indicators.shown || hud.shown
1391 }
1392
1393@@ -571,7 +466,9 @@
1394 available: edgeDemo.panelEnabled
1395 contentEnabled: edgeDemo.panelContentEnabled
1396 }
1397- fullscreenMode: shell.fullscreenMode
1398+ property string focusedAppId: ApplicationManager.focusedApplicationId
1399+ property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
1400+ fullscreenMode: focusedApplication && stages.fullscreen && !greeter.shown && !lockscreen.shown
1401 searchVisible: !greeter.shown && !lockscreen.shown && dash.shown && dash.searchable
1402
1403 InputFilterArea {
1404@@ -597,9 +494,8 @@
1405 hideAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: hudRevealer.closedValue; easing.type: Easing.Linear }
1406
1407 Connections {
1408- target: shell.applicationManager
1409- onMainStageFocusedApplicationChanged: hud.hide()
1410- onSideStageFocusedApplicationChanged: hud.hide()
1411+ target: ApplicationManager
1412+ onFocusedApplicationIdChanged: hud.hide()
1413 }
1414 }
1415
1416@@ -624,7 +520,7 @@
1417 theHud: hud
1418 anchors.fill: parent
1419 enabled: hud.available
1420- applicationIsOnForeground: applicationFocused
1421+ applicationIsOnForeground: ApplicationManager.focusedApplicationId
1422 }
1423
1424 InputFilterArea {
1425@@ -655,9 +551,11 @@
1426 showHome()
1427 }
1428 onDash: {
1429- if (stages.shown) {
1430- stages.hide();
1431- launcher.hide();
1432+ if (stages.shown && !stages.overlayMode) {
1433+ if (!stages.locked) {
1434+ stages.hide();
1435+ launcher.hide();
1436+ }
1437 }
1438 }
1439 onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("clickscope", false, true)
1440@@ -750,14 +648,14 @@
1441 anchors.bottom: parent.bottom
1442 anchors.left: parent.left
1443 anchors.right: parent.right
1444- height: shell.applicationManager ? shell.applicationManager.keyboardHeight : 0
1445+ height: ApplicationManager.keyboardVisible ? ApplicationManager.keyboardHeight : 0
1446
1447- enabled: shell.applicationManager && shell.applicationManager.keyboardVisible
1448+ enabled: ApplicationManager.keyboardVisible
1449 }
1450
1451 Label {
1452 anchors.centerIn: parent
1453- visible: applicationManager.fake
1454+ visible: ApplicationManager.fake ? ApplicationManager.fake : false
1455 text: "EARLY ALPHA\nNOT READY FOR USE"
1456 color: "lightgrey"
1457 opacity: 0.2
1458
1459=== removed directory 'qml/SideStage'
1460=== removed file 'qml/SideStage/SideStage.qml'
1461--- qml/SideStage/SideStage.qml 2014-02-03 13:31:28 +0000
1462+++ qml/SideStage/SideStage.qml 1970-01-01 00:00:00 +0000
1463@@ -1,73 +0,0 @@
1464-/*
1465- * Copyright (C) 2013 Canonical, Ltd.
1466- *
1467- * This program is free software; you can redistribute it and/or modify
1468- * it under the terms of the GNU General Public License as published by
1469- * the Free Software Foundation; version 3.
1470- *
1471- * This program is distributed in the hope that it will be useful,
1472- * but WITHOUT ANY WARRANTY; without even the implied warranty of
1473- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1474- * GNU General Public License for more details.
1475- *
1476- * You should have received a copy of the GNU General Public License
1477- * along with this program. If not, see <http://www.gnu.org/licenses/>.
1478- */
1479-
1480-import QtQuick 2.0
1481-import Unity.Application 0.1
1482-import Ubuntu.Components 0.1
1483-import "../Components"
1484-
1485-Stage {
1486- id: stage
1487-
1488- type: ApplicationInfo.SideStage
1489- property real handleSizeCollapsed: units.gu(2.5)
1490- property real handleSizeExpanded: units.gu(4)
1491- property bool handleExpanded
1492-
1493- /* FIXME: workaround so that when a main stage app goes fullscreen
1494- the sidestage's top is not transparent. Proper fix would be to
1495- resize the side stage app.
1496- */
1497- Rectangle {
1498- id: backgroundBeneathPanel
1499- anchors {
1500- top: parent.top
1501- left: parent.left
1502- right: parent.right
1503- }
1504- height: shell.panelHeight
1505- color: background.color
1506- z: -1
1507- }
1508-
1509- Rectangle {
1510- id: background
1511- anchors.fill: parent
1512- color: "#2c2924"
1513- z: -1
1514- visible: stage.usingScreenshots
1515- }
1516-
1517- SidestageHandle {
1518- id: handle
1519- objectName: "sideStageHandle"
1520-
1521- anchors {
1522- top: parent.top
1523- bottom: parent.bottom
1524- left: parent.left
1525- leftMargin: -width
1526- }
1527- width: handleExpanded ? handleSizeExpanded : handleSizeCollapsed
1528- Behavior on width { NumberAnimation { easing.type: Easing.OutQuart} }
1529- z: -1
1530- }
1531-
1532- InputFilterArea {
1533- anchors.fill: handle
1534- blockInput: visible
1535- }
1536-}
1537
1538=== removed directory 'qml/SideStage/graphics'
1539=== added directory 'qml/Stages'
1540=== added file 'qml/Stages/PhoneStage.qml'
1541--- qml/Stages/PhoneStage.qml 1970-01-01 00:00:00 +0000
1542+++ qml/Stages/PhoneStage.qml 2014-03-25 15:08:15 +0000
1543@@ -0,0 +1,501 @@
1544+/*
1545+ * Copyright (C) 2014 Canonical, Ltd.
1546+ *
1547+ * This program is free software; you can redistribute it and/or modify
1548+ * it under the terms of the GNU General Public License as published by
1549+ * the Free Software Foundation; version 3.
1550+ *
1551+ * This program is distributed in the hope that it will be useful,
1552+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1553+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1554+ * GNU General Public License for more details.
1555+ *
1556+ * You should have received a copy of the GNU General Public License
1557+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1558+ */
1559+
1560+import QtQuick 2.0
1561+import Ubuntu.Components 0.1
1562+import Ubuntu.Gestures 0.1
1563+import Unity.Application 0.1
1564+import Utils 0.1
1565+import "../Components"
1566+
1567+Item {
1568+ id: root
1569+
1570+ // Controls to be set from outside
1571+ property bool shown: false
1572+ property bool moving: false
1573+ property int dragAreaWidth
1574+
1575+ // State information propagated to the outside
1576+ readonly property bool painting: mainScreenshotImage.visible || fadeInScreenshotImage.visible || appSplash.visible || spreadView.visible
1577+ property bool fullscreen: priv.focusedApplication ? priv.focusedApplication.fullscreen : false
1578+ property bool locked: spreadView.visible
1579+
1580+ // Not used for PhoneStage, only useful for SideStage and similar
1581+ property bool overlayMode: false
1582+ property int overlayWidth: 0
1583+
1584+ function select(appId) {
1585+ spreadView.snapTo(priv.indexOf(appId))
1586+ }
1587+
1588+ onMovingChanged: {
1589+ if (moving) {
1590+ if (ApplicationManager.focusedApplicationId) {
1591+ priv.requestNewScreenshot();
1592+ } else {
1593+ mainScreenshotImage.anchors.leftMargin = 0;
1594+ mainScreenshotImage.source = ApplicationManager.get(0).screenshot;
1595+ mainScreenshotImage.visible = true;
1596+ }
1597+ } else {
1598+ mainScreenshotImage.visible = false;
1599+ }
1600+ }
1601+
1602+ Connections {
1603+ target: ApplicationManager
1604+
1605+ onFocusRequested: {
1606+ if (spreadView.visible) {
1607+ spreadView.snapTo(priv.indexOf(appId));
1608+ } else {
1609+ priv.switchToApp(appId);
1610+ }
1611+ }
1612+
1613+ onFocusedApplicationIdChanged: {
1614+ if (ApplicationManager.focusedApplicationId.length > 0) {
1615+ if (priv.secondApplicationStarting || priv.applicationStarting) {
1616+ appSplashTimer.start();
1617+ } else {
1618+ var application = priv.focusedApplication;
1619+ root.fullscreen = application.fullscreen;
1620+ mainScreenshotImage.source = application.screenshot;
1621+ }
1622+ } else {
1623+ spreadView.selectedIndex = -1;
1624+ spreadView.phase = 0;
1625+ spreadView.contentX = -spreadView.shift;
1626+ }
1627+ }
1628+
1629+ onApplicationAdded: {
1630+ if (!priv.focusedApplication) {
1631+ mainScreenshotImage.source = "";
1632+ mainScreenshotImage.visible = false;
1633+ priv.applicationStarting = true;
1634+ } else {
1635+ mainScreenshotImage.source = "";
1636+ priv.newFocusedAppId = appId;
1637+ priv.secondApplicationStarting = true;
1638+ priv.requestNewScreenshot();
1639+ }
1640+
1641+ if (spreadView.visible) {
1642+ spreadView.snapTo(0);
1643+ }
1644+ }
1645+
1646+ onApplicationRemoved: {
1647+ if (ApplicationManager.count == 0) {
1648+ mainScreenshotImage.source = ""
1649+ mainScreenshotImage.visible = false;
1650+ } else {
1651+ mainScreenshotImage.source = ApplicationManager.get(0).screenshot;
1652+ }
1653+ }
1654+ }
1655+
1656+ QtObject {
1657+ id: priv
1658+
1659+ property string focusedAppId: ApplicationManager.focusedApplicationId
1660+ property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
1661+ property url focusedScreenshot: focusedApplication ? focusedApplication.screenshot : ""
1662+
1663+ property bool waitingForScreenshot: false
1664+
1665+ property bool applicationStarting: false
1666+ property bool secondApplicationStarting: false
1667+
1668+ property string newFocusedAppId
1669+
1670+ onFocusedScreenshotChanged: {
1671+ if (root.moving && priv.waitingForScreenshot) {
1672+ mainScreenshotImage.anchors.leftMargin = 0;
1673+ mainScreenshotImage.source = priv.focusedScreenshot
1674+ mainScreenshotImage.visible = true;
1675+ } else if (priv.secondApplicationStarting && priv.waitingForScreenshot) {
1676+ applicationSwitchingAnimation.start();
1677+ }
1678+ waitingForScreenshot = false;
1679+ }
1680+
1681+ function requestNewScreenshot() {
1682+ waitingForScreenshot = true;
1683+ ApplicationManager.updateScreenshot(focusedAppId);
1684+ }
1685+
1686+ function switchToApp(appId) {
1687+ if (priv.focusedAppId) {
1688+ priv.newFocusedAppId = appId;
1689+ root.fullscreen = ApplicationManager.findApplication(appId).fullscreen;
1690+ applicationSwitchingAnimation.start();
1691+ } else {
1692+ ApplicationManager.focusApplication(appId);
1693+ }
1694+ }
1695+
1696+ function indexOf(appId) {
1697+ for (var i = 0; i < ApplicationManager.count; i++) {
1698+ if (ApplicationManager.get(i).appId == appId) {
1699+ return i;
1700+ }
1701+ }
1702+ return -1;
1703+ }
1704+
1705+ }
1706+
1707+ // FIXME: the signal connections seems to get lost.
1708+ Connections {
1709+ target: priv.focusedApplication
1710+ onScreenshotChanged: priv.focusedScreenshot = priv.focusedApplication.screenshot
1711+ }
1712+ Binding {
1713+ target: root
1714+ property: "fullscreen"
1715+ value: priv.focusedApplication ? priv.focusedApplication.fullscreen : false
1716+ }
1717+
1718+ Timer {
1719+ id: appSplashTimer
1720+ // FIXME: apart from removing this completely in the future and make the app surface paint
1721+ // meaningful stuff, also check for colin's stuff to land so we can shape 1.4 secs away from here
1722+ // https://code.launchpad.net/~cjwatson/upstart-app-launch/libclick-manifest/+merge/210520
1723+ // https://code.launchpad.net/~cjwatson/upstart-app-launch/libclick-pkgdir/+merge/209909
1724+ interval: 1700
1725+ repeat: false
1726+ onTriggered: {
1727+ priv.applicationStarting = false;
1728+ priv.secondApplicationStarting = false;
1729+ }
1730+ }
1731+
1732+ SequentialAnimation {
1733+ id: applicationSwitchingAnimation
1734+ // setup
1735+ PropertyAction { target: mainScreenshotImage; property: "anchors.leftMargin"; value: 0 }
1736+ PropertyAction { target: mainScreenshotImage; property: "source"; value: priv.focusedScreenshot }
1737+ PropertyAction { targets: [mainScreenshotImage, fadeInScreenshotImage]; property: "visible"; value: true }
1738+ PropertyAction { target: fadeInScreenshotImage; property: "source"; value: {
1739+ var newFocusedApp = ApplicationManager.findApplication(priv.newFocusedAppId);
1740+ return newFocusedApp ? newFocusedApp.screenshot : "" }
1741+ }
1742+ PropertyAction { target: fadeInScreenshotImage; property: "opacity"; value: 0 }
1743+ PropertyAction { target: fadeInScreenshotImage; property: "scale"; value: .8 }
1744+
1745+
1746+ // The actual animation
1747+ ParallelAnimation {
1748+ UbuntuNumberAnimation { target: mainScreenshotImage; property: "anchors.leftMargin"; to: root.width; duration: UbuntuAnimation.SlowDuration }
1749+ UbuntuNumberAnimation { target: fadeInScreenshotImage; properties: "opacity,scale"; to: 1; duration: UbuntuAnimation.SlowDuration }
1750+ }
1751+
1752+ // restore stuff
1753+ ScriptAction { script: ApplicationManager.focusApplication(priv.newFocusedAppId); }
1754+ PropertyAction { target: fadeInScreenshotImage; property: "visible"; value: false }
1755+ PropertyAction { target: mainScreenshotImage; property: "visible"; value: false }
1756+ }
1757+
1758+ // FIXME: Drop this and make the imageprovider show a splashscreen instead
1759+ Rectangle {
1760+ id: appSplash2
1761+ anchors.fill: parent
1762+ color: "white"
1763+ visible: priv.secondApplicationStarting
1764+ }
1765+ Image {
1766+ id: fadeInScreenshotImage
1767+ anchors { left: parent.left; bottom: parent.bottom }
1768+ width: parent.width
1769+ scale: .7
1770+ visible: false
1771+ }
1772+
1773+ Rectangle {
1774+ id: appSplash
1775+ anchors.fill: parent
1776+ color: "white"
1777+ visible: priv.applicationStarting
1778+ }
1779+ Image {
1780+ id: mainScreenshotImage
1781+ anchors { left: parent.left; bottom: parent.bottom }
1782+ width: parent.width
1783+ visible: false
1784+ }
1785+
1786+ EdgeDragArea {
1787+ id: spreadDragArea
1788+ direction: Direction.Leftwards
1789+ enabled: ApplicationManager.count > 1 && spreadView.phase != 2
1790+
1791+ anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
1792+ width: root.dragAreaWidth
1793+
1794+ // Sitting at the right edge of the screen, this EdgeDragArea directly controls the spreadView when
1795+ // attachedToView is true. When the finger movement passes positionMarker3 we detach it from the
1796+ // spreadView and make the spreadView snap to positionMarker4.
1797+ property bool attachedToView: true
1798+
1799+ property var gesturePoints: new Array()
1800+
1801+ onTouchXChanged: {
1802+ if (!dragging && !priv.waitingForScreenshot) {
1803+ // Initial touch. Let's update the screenshot and reset the spreadView to the starting position.
1804+ priv.requestNewScreenshot();
1805+ spreadView.phase = 0;
1806+ spreadView.contentX = -spreadView.shift;
1807+ }
1808+ if (dragging && attachedToView) {
1809+ // Gesture recognized. Let's move the spreadView with the finger
1810+ spreadView.contentX = -touchX - spreadView.shift;
1811+ }
1812+ if (attachedToView && spreadView.shiftedContentX >= spreadView.width * spreadView.positionMarker3) {
1813+ // We passed positionMarker3. Detach from spreadView and snap it.
1814+ attachedToView = false;
1815+ spreadView.snap();
1816+ }
1817+ gesturePoints.push(touchX);
1818+ }
1819+
1820+ onStatusChanged: {
1821+ if (status == DirectionalDragArea.Recognized) {
1822+ attachedToView = true;
1823+ }
1824+ }
1825+
1826+ onDraggingChanged: {
1827+ if (dragging) {
1828+ // Gesture recognized. Start recording this gesture
1829+ gesturePoints = [];
1830+ return;
1831+ }
1832+
1833+ // Ok. The user released. Find out if it was a one-way movement.
1834+ var oneWayFlick = true;
1835+ var smallestX = spreadDragArea.width;
1836+ for (var i = 0; i < gesturePoints.length; i++) {
1837+ if (gesturePoints[i] >= smallestX) {
1838+ oneWayFlick = false;
1839+ break;
1840+ }
1841+ smallestX = gesturePoints[i];
1842+ }
1843+ gesturePoints = [];
1844+
1845+ if (oneWayFlick && spreadView.shiftedContentX > units.gu(2) &&
1846+ spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
1847+ // If it was a short one-way movement, do the Alt+Tab switch
1848+ // no matter if we didn't cross positionMarker1 yet.
1849+ spreadView.snapTo(1);
1850+ } else if (!dragging && attachedToView) {
1851+ // otherwise snap to the closest snap position we can find
1852+ // (might be back to start, to app 1 or to spread)
1853+ spreadView.snap();
1854+ }
1855+ }
1856+ }
1857+
1858+ Rectangle {
1859+ id: coverFlipBackground
1860+ anchors.fill: parent
1861+ color: "black"
1862+ visible: spreadView.visible
1863+ }
1864+
1865+ InputFilterArea {
1866+ anchors.fill: root
1867+ blockInput: spreadView.visible
1868+ }
1869+
1870+ Flickable {
1871+ id: spreadView
1872+ objectName: "spreadView"
1873+ anchors.fill: parent
1874+ visible: spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1 || snapAnimation.running
1875+ contentWidth: spreadRow.width - shift
1876+ contentX: -shift
1877+
1878+ // The flickable needs to fill the screen in order to get touch events all over.
1879+ // However, we don't want to the user to be able to scroll back all the way. For
1880+ // that, the beginning of the gesture starts with a negative value for contentX
1881+ // so the flickable wants to pull it into the view already. "shift" tunes the
1882+ // distance where to "lock" the content.
1883+ property real shift: width / 2
1884+ property real shiftedContentX: contentX + shift
1885+
1886+ property int tileDistance: width / 4
1887+
1888+ // Those markers mark the various positions in the spread (ratio to screen width from right to left):
1889+ // 0 - 1: following finger, snap back to the beginning on release
1890+ property real positionMarker1: 0.3
1891+ // 1 - 2: curved snapping movement, snap to app 1 on release
1892+ property real positionMarker2: 0.45
1893+ // 2 - 3: movement follows finger, snaps back to app 1 on release
1894+ property real positionMarker3: 0.6
1895+ // passing 3, we detach movement from the finger and snap to 4
1896+ property real positionMarker4: 0.9
1897+
1898+ // This is where the first app snaps to when bringing it in from the right edge.
1899+ property real snapPosition: 0.75
1900+
1901+ // Phase of the animation:
1902+ // 0: Starting from right edge, a new app (index 1) comes in from the right
1903+ // 1: The app has reached the first snap position.
1904+ // 2: The list is dragged further and snaps into the spread view when entering phase 2
1905+ property int phase: 0
1906+
1907+ property int selectedIndex: -1
1908+
1909+ onShiftedContentXChanged: {
1910+ switch (phase) {
1911+ case 0:
1912+ if (shiftedContentX > width * positionMarker2) {
1913+ phase = 1;
1914+ }
1915+ break;
1916+ case 1:
1917+ if (shiftedContentX < width * positionMarker2) {
1918+ phase = 0;
1919+ } else if (shiftedContentX >= width * positionMarker4) {
1920+ phase = 2;
1921+ }
1922+ break;
1923+ }
1924+ }
1925+
1926+ function snap() {
1927+ if (shiftedContentX < positionMarker1 * width) {
1928+ snapAnimation.targetContentX = -shift;
1929+ snapAnimation.start();
1930+ } else if (shiftedContentX < positionMarker2 * width) {
1931+ snapTo(1)
1932+ } else if (shiftedContentX < positionMarker3 * width) {
1933+ snapTo(1)
1934+ } else if (phase < 2){
1935+ // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation.
1936+ snapAnimation.targetContentX = width * positionMarker4 + 1 - shift;
1937+ snapAnimation.start();
1938+ }
1939+ }
1940+ function snapTo(index) {
1941+ spreadView.selectedIndex = index;
1942+ root.fullscreen = ApplicationManager.get(index).fullscreen;
1943+ snapAnimation.targetContentX = -shift;
1944+ snapAnimation.start();
1945+ }
1946+
1947+ SequentialAnimation {
1948+ id: snapAnimation
1949+ property int targetContentX: -spreadView.shift
1950+
1951+ UbuntuNumberAnimation {
1952+ target: spreadView
1953+ property: "contentX"
1954+ to: snapAnimation.targetContentX
1955+ duration: UbuntuAnimation.FastDuration
1956+ }
1957+
1958+ ScriptAction {
1959+ script: {
1960+ if (spreadView.selectedIndex >= 0) {
1961+ ApplicationManager.focusApplication(ApplicationManager.get(spreadView.selectedIndex).appId);
1962+ spreadView.selectedIndex = -1
1963+ spreadView.phase = 0;
1964+ spreadView.contentX = -spreadView.shift;
1965+ }
1966+ }
1967+ }
1968+ }
1969+
1970+ Item {
1971+ id: spreadRow
1972+ // This width controls how much the spread can be flicked left/right. It's composed of:
1973+ // tileDistance * app count (with a minimum of 3 apps, in order to also allow moving 1 and 2 apps a bit)
1974+ // + some constant value (still scales with the screen width) which looks good and somewhat fills the screen
1975+ width: Math.max(3, ApplicationManager.count) * spreadView.tileDistance + (spreadView.width - spreadView.tileDistance) * 1.5
1976+
1977+ x: spreadView.contentX
1978+
1979+ Repeater {
1980+ id: spreadRepeater
1981+ model: ApplicationManager
1982+ delegate: TransformedSpreadDelegate {
1983+ id: appDelegate
1984+ objectName: "appDelegate" + index
1985+ startAngle: 45
1986+ endAngle: 5
1987+ startScale: 1.1
1988+ endScale: 0.7
1989+ startDistance: spreadView.tileDistance
1990+ endDistance: units.gu(.5)
1991+ width: spreadView.width
1992+ height: spreadView.height
1993+ selected: spreadView.selectedIndex == index
1994+ otherSelected: spreadView.selectedIndex >= 0 && !selected
1995+
1996+ z: index
1997+ x: index == 0 ? 0 : spreadView.width + (index - 1) * spreadView.tileDistance
1998+
1999+ // Each tile has a different progress value running from 0 to 1.
2000+ // A progress value of 0 means the tile is at the right edge. 1 means the tile has reched the left edge.
2001+ progress: {
2002+ var tileProgress = (spreadView.shiftedContentX - index * spreadView.tileDistance) / spreadView.width;
2003+ // Tile 1 needs to move directly from the beginning...
2004+ if (index == 1 && spreadView.phase < 2) {
2005+ tileProgress += spreadView.tileDistance / spreadView.width;
2006+ }
2007+ return tileProgress;
2008+ }
2009+
2010+ // This mostly is the same as progress, just adds the snapping to phase 1 for tiles 0 and 1
2011+ animatedProgress: {
2012+ if (spreadView.phase == 0 && index < 2) {
2013+ if (progress < spreadView.positionMarker1) {
2014+ return progress;
2015+ } else if (progress < spreadView.positionMarker1 + snappingCurve.period){
2016+ return spreadView.positionMarker1 + snappingCurve.value * 3;
2017+ } else {
2018+ return spreadView.positionMarker2;
2019+ }
2020+ }
2021+ return progress;
2022+ }
2023+
2024+ EasingCurve {
2025+ id: snappingCurve
2026+ type: EasingCurve.OutQuad
2027+ period: 0.05
2028+ progress: appDelegate.progress - spreadView.positionMarker1
2029+ }
2030+
2031+ onClicked: {
2032+ if (spreadView.phase == 2) {
2033+ if (ApplicationManager.focusedApplicationId == ApplicationManager.get(index).appId) {
2034+ spreadView.snapTo(index);
2035+ } else {
2036+ ApplicationManager.requestFocusApplication(ApplicationManager.get(index).appId);
2037+ }
2038+ }
2039+ }
2040+ }
2041+ }
2042+ }
2043+ }
2044+}
2045
2046=== renamed file 'qml/SideStage/SidestageHandle.qml' => 'qml/Stages/SidestageHandle.qml'
2047=== added file 'qml/Stages/SpreadDelegate.qml'
2048--- qml/Stages/SpreadDelegate.qml 1970-01-01 00:00:00 +0000
2049+++ qml/Stages/SpreadDelegate.qml 2014-03-25 15:08:15 +0000
2050@@ -0,0 +1,55 @@
2051+/*
2052+ * Copyright 2014 Canonical Ltd.
2053+ *
2054+ * This program is free software; you can redistribute it and/or modify
2055+ * it under the terms of the GNU Lesser General Public License as published by
2056+ * the Free Software Foundation; version 3.
2057+ *
2058+ * This program is distributed in the hope that it will be useful,
2059+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2060+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2061+ * GNU Lesser General Public License for more details.
2062+ *
2063+ * You should have received a copy of the GNU Lesser General Public License
2064+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2065+ *
2066+ * Authors: Michael Zanetti <michael.zanetti@canonical.com>
2067+*/
2068+
2069+import QtQuick 2.0
2070+
2071+Item {
2072+ id: root
2073+
2074+ signal clicked()
2075+
2076+ property real topMarginProgress
2077+
2078+ QtObject {
2079+ id: priv
2080+ property real heightDifference: root.height - appImage.implicitHeight
2081+ }
2082+
2083+ Image {
2084+ id: dropShadow
2085+ anchors.fill: appImage
2086+ anchors.margins: -units.gu(2)
2087+ source: "graphics/dropshadow.png"
2088+ opacity: .4
2089+ }
2090+ Image {
2091+ id: appImage
2092+ anchors {
2093+ left: parent.left;
2094+ bottom: parent.bottom;
2095+ top: parent.top;
2096+ topMargin: priv.heightDifference * Math.max(0, 1 - root.topMarginProgress)
2097+ }
2098+ source: model.screenshot
2099+ antialiasing: true
2100+ }
2101+ MouseArea {
2102+ anchors.fill: appImage
2103+ onClicked: root.clicked()
2104+ }
2105+}
2106
2107=== added file 'qml/Stages/StageWithSideStage.qml'
2108--- qml/Stages/StageWithSideStage.qml 1970-01-01 00:00:00 +0000
2109+++ qml/Stages/StageWithSideStage.qml 2014-03-25 15:08:15 +0000
2110@@ -0,0 +1,405 @@
2111+/*
2112+ * Copyright (C) 2014 Canonical, Ltd.
2113+ *
2114+ * This program is free software; you can redistribute it and/or modify
2115+ * it under the terms of the GNU General Public License as published by
2116+ * the Free Software Foundation; version 3.
2117+ *
2118+ * This program is distributed in the hope that it will be useful,
2119+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2120+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2121+ * GNU General Public License for more details.
2122+ *
2123+ * You should have received a copy of the GNU General Public License
2124+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2125+ */
2126+
2127+import QtQuick 2.0
2128+import Ubuntu.Components 0.1
2129+import "../Components"
2130+import Unity.Application 0.1
2131+import Ubuntu.Gestures 0.1
2132+
2133+Item {
2134+ id: root
2135+ objectName: "stages"
2136+ anchors.fill: parent
2137+
2138+ // Controls to be set from outside
2139+ property bool shown: false
2140+ property bool moving: false
2141+ property int dragAreaWidth
2142+
2143+ // State information propagated to the outside
2144+ readonly property bool painting: mainStageImage.visible || sideStageImage.visible || sideStageSnapAnimation.running
2145+ property bool fullscreen: priv.focusedApplication ? priv.focusedApplication.fullscreen : false
2146+ property bool overlayMode: (sideStageImage.shown && priv.mainStageAppId.length == 0) || priv.overlayOverride
2147+ || (priv.mainStageAppId.length == 0 && sideStageSnapAnimation.running)
2148+
2149+ readonly property int overlayWidth: priv.overlayOverride ? 0 : priv.sideStageWidth
2150+
2151+ onShownChanged: {
2152+ if (!shown) {
2153+ priv.mainStageAppId = "";
2154+ }
2155+ }
2156+
2157+ onMovingChanged: {
2158+ if (moving) {
2159+ if (!priv.mainStageAppId && !priv.sideStageAppId) {
2160+ // Pulling in from the right, make the last used (topmost) app visible
2161+ var application = ApplicationManager.get(0);
2162+ if (application.stage == ApplicationInfoInterface.SideStage) {
2163+ sideStageImage.application = application;
2164+ sideStageImage.x = root.width - sideStageImage.width
2165+ sideStageImage.visible = true;
2166+ } else {
2167+ mainStageImage.application = application;
2168+ mainStageImage.visible = true;
2169+ }
2170+ } else {
2171+ priv.requestNewScreenshot(ApplicationInfoInterface.MainStage)
2172+ if (priv.focusedApplicationId == priv.sideStageAppId) {
2173+ priv.requestNewScreenshot(ApplicationInfoInterface.SideStage)
2174+ }
2175+ }
2176+ } else {
2177+ mainStageImage.visible = false;
2178+ sideStageImage.visible = false;
2179+ }
2180+ }
2181+
2182+ QtObject {
2183+ id: priv
2184+
2185+ property int sideStageWidth: units.gu(40)
2186+
2187+
2188+ property string sideStageAppId
2189+ property string mainStageAppId
2190+
2191+
2192+ property var sideStageApp: ApplicationManager.findApplication(sideStageAppId)
2193+ property var mainStageApp: ApplicationManager.findApplication(mainStageAppId)
2194+
2195+ property string sideStageScreenshot: sideStageApp ? sideStageApp.screenshot : ""
2196+ property string mainStageScreenshot: mainStageApp ? mainStageApp.screenshot : ""
2197+
2198+ property string focusedApplicationId: ApplicationManager.focusedApplicationId
2199+ property var focusedApplication: ApplicationManager.findApplication(focusedApplicationId)
2200+ property url focusedScreenshot: focusedApplication ? focusedApplication.screenshot : ""
2201+
2202+ property bool waitingForMainScreenshot: false
2203+ property bool waitingForSideScreenshot: false
2204+ property bool waitingForScreenshots: waitingForMainScreenshot || waitingForSideScreenshot
2205+
2206+ property string startingAppId: ""
2207+
2208+ // Keep overlayMode even if there is no focused app (to allow pulling in the sidestage from the right)
2209+ property bool overlayOverride: false
2210+
2211+ onFocusedApplicationChanged: {
2212+ if (focusedApplication) {
2213+ if (focusedApplication.stage == ApplicationInfoInterface.MainStage) {
2214+ mainStageAppId = focusedApplicationId;
2215+ priv.overlayOverride = false;
2216+ if (priv.startingAppId == focusedApplicationId && sideStageImage.shown) {
2217+ // There was already a sidestage app on top. bring it back!
2218+ ApplicationManager.focusApplication(priv.sideStageAppId)
2219+ priv.startingAppId = "";
2220+ }
2221+ } else if (focusedApplication.stage == ApplicationInfoInterface.SideStage) {
2222+ sideStageAppId = focusedApplicationId;
2223+ if (priv.startingAppId == focusedApplicationId && !sideStageImage.shown) {
2224+ sideStageImage.snapToApp(focusedApplication);
2225+ priv.startingAppId = "";
2226+ }
2227+ }
2228+ }
2229+ }
2230+
2231+ onMainStageScreenshotChanged: {
2232+ waitingForMainScreenshot = false;
2233+ }
2234+
2235+ onSideStageScreenshotChanged: {
2236+ waitingForSideScreenshot = false;
2237+ }
2238+
2239+ onFocusedScreenshotChanged: {
2240+ waitingForSideScreenshot = false;
2241+ }
2242+
2243+ onWaitingForScreenshotsChanged: {
2244+ if (waitingForScreenshots) {
2245+ return;
2246+ }
2247+
2248+ if (root.moving) {
2249+ if (mainStageAppId) {
2250+ mainStageImage.application = mainStageApp
2251+ mainStageImage.visible = true;
2252+ }
2253+ if (sideStageAppId && focusedApplicationId == sideStageAppId) {
2254+ sideStageImage.application = sideStageApp;
2255+ sideStageImage.x = root.width - sideStageImage.width
2256+ sideStageImage.visible = true;
2257+ }
2258+ }
2259+ if (sideStageHandleMouseArea.pressed) {
2260+ if (sideStageAppId) {
2261+ sideStageImage.application = sideStageApp;
2262+ sideStageImage.x = root.width - sideStageImage.width
2263+ sideStageImage.visible = true;
2264+ }
2265+ if (mainStageAppId) {
2266+ mainStageImage.application = mainStageApp
2267+ mainStageImage.visible = true;
2268+ }
2269+ }
2270+ }
2271+
2272+ function requestNewScreenshot(stage) {
2273+ if (stage == ApplicationInfoInterface.MainStage && mainStageAppId) {
2274+ waitingForMainScreenshot = true;
2275+ ApplicationManager.updateScreenshot(mainStageAppId);
2276+ } else if (stage == ApplicationInfoInterface.SideStage && sideStageAppId) {
2277+ waitingForSideScreenshot = true;
2278+ ApplicationManager.updateScreenshot(sideStageAppId);
2279+ }
2280+ }
2281+
2282+ }
2283+ // FIXME: the signal connection seems to get lost with the fake application manager.
2284+ Connections {
2285+ target: priv.sideStageApp
2286+ onScreenshotChanged: priv.sideStageScreenshot = priv.sideStageApp.screenshot
2287+ }
2288+ Connections {
2289+ target: priv.mainStageApp
2290+ onScreenshotChanged: priv.mainStageScreenshot = priv.mainStageApp.screenshot
2291+ }
2292+
2293+ Connections {
2294+ target: ApplicationManager
2295+
2296+ onApplicationAdded: {
2297+ priv.startingAppId = appId;
2298+ splashScreenTimer.start();
2299+ var application = ApplicationManager.findApplication(appId)
2300+ if (application.stage == ApplicationInfoInterface.SideStage) {
2301+ sideStageSplash.visible = true;
2302+ } else if (application.stage == ApplicationInfoInterface.MainStage) {
2303+ mainStageSplash.visible = true;
2304+ }
2305+ }
2306+
2307+ onFocusRequested: {
2308+ var application = ApplicationManager.findApplication(appId)
2309+ if (application.stage == ApplicationInfoInterface.SideStage) {
2310+ if (!root.shown) {
2311+ priv.mainStageAppId = "";
2312+ mainStageImage.application = null
2313+ }
2314+ if (sideStageImage.shown) {
2315+ sideStageImage.switchTo(application);
2316+ if (priv.mainStageAppId) {
2317+ mainStageImage.application = priv.mainStageApp;
2318+ mainStageImage.visible = true;
2319+ }
2320+ } else {
2321+ sideStageImage.application = application;
2322+ sideStageImage.snapToApp(application);
2323+ }
2324+ } else if (application.stage == ApplicationInfoInterface.MainStage) {
2325+ if (root.shown) {
2326+ if (sideStageImage.shown) {
2327+ sideStageImage.application = priv.sideStageApp;
2328+ sideStageImage.visible = true;
2329+ }
2330+ priv.mainStageAppId = application.appId;
2331+ mainStageImage.switchTo(application)
2332+ ApplicationManager.focusApplication(appId)
2333+ if (sideStageImage.shown) {
2334+ // There was already a focused SS app. Bring it back
2335+ ApplicationManager.focusApplication(priv.sideStageAppId)
2336+ }
2337+ } else {
2338+ if (sideStageImage.shown) {
2339+ sideStageImage.visible = false;
2340+ sideStageImage.x = root.width;
2341+ }
2342+
2343+ mainStageImage.application = application;
2344+ ApplicationManager.focusApplication(appId)
2345+ }
2346+ }
2347+ }
2348+
2349+ onApplicationRemoved: {
2350+ if (priv.mainStageAppId == appId) {
2351+ priv.mainStageAppId = "";
2352+ }
2353+ if (priv.sideStageAppId == appId) {
2354+ priv.sideStageAppId = "";
2355+ }
2356+ if (priv.sideStageAppId.length == 0) {
2357+ sideStageImage.shown = false;
2358+ priv.overlayOverride = false;
2359+ }
2360+ }
2361+
2362+ }
2363+
2364+ Timer {
2365+ id: splashScreenTimer
2366+ // FIXME: apart from removing this completely in the future and make the app surface paint
2367+ // meaningful stuff, also check for colin's stuff to land so we can shape 1.4 secs away from here
2368+ // https://code.launchpad.net/~cjwatson/upstart-app-launch/libclick-manifest/+merge/210520
2369+ // https://code.launchpad.net/~cjwatson/upstart-app-launch/libclick-pkgdir/+merge/209909
2370+ interval: 1700
2371+ repeat: false
2372+ onTriggered: {
2373+ mainStageSplash.visible = false;
2374+ sideStageSplash.visible = false;
2375+ }
2376+ }
2377+
2378+ SwitchingApplicationImage {
2379+ id: mainStageImage
2380+ anchors.bottom: parent.bottom
2381+ width: parent.width
2382+ visible: false
2383+
2384+ onSwitched: {
2385+ sideStageImage.visible = false;
2386+ }
2387+ }
2388+
2389+ Rectangle {
2390+ id: mainStageSplash
2391+ anchors.fill: root
2392+ anchors.rightMargin: root.width - sideStageImage.x
2393+ color: "white"
2394+ }
2395+
2396+ SidestageHandle {
2397+ id: sideStageHandle
2398+ anchors { top: parent.top; right: sideStageImage.left; bottom: parent.bottom }
2399+ width: root.dragAreaWidth
2400+ visible: root.shown && priv.sideStageAppId && sideStageImage.x < root.width
2401+
2402+ }
2403+ MouseArea {
2404+ id: sideStageHandleMouseArea
2405+ anchors { top: parent.top; right: parent.right; bottom: parent.bottom; rightMargin: sideStageImage.shown ? sideStageImage.width : 0}
2406+ width: root.dragAreaWidth
2407+ visible: priv.sideStageAppId
2408+
2409+ property var dragPoints: new Array()
2410+
2411+ onPressed: {
2412+ priv.requestNewScreenshot(ApplicationInfoInterface.SideStage)
2413+ if (priv.mainStageAppId) {
2414+ priv.requestNewScreenshot(ApplicationInfoInterface.MainStage)
2415+ }
2416+ }
2417+
2418+ onMouseXChanged: {
2419+ dragPoints.push(mouseX)
2420+
2421+ var dragPoint = root.width + mouseX;
2422+ if (sideStageImage.shown) {
2423+ dragPoint -= sideStageImage.width
2424+ }
2425+ sideStageImage.x = Math.max(root.width - sideStageImage.width, dragPoint)
2426+ }
2427+
2428+ onReleased: {
2429+ var distance = 0;
2430+ var lastX = dragPoints[0];
2431+ var oneWayFlick = true;
2432+ for (var i = 0; i < dragPoints.length; ++i) {
2433+ if (dragPoints[i] < lastX) {
2434+ oneWayFlick = false;
2435+ }
2436+ distance += dragPoints[i] - lastX;
2437+ lastX = dragPoints[i];
2438+ }
2439+ dragPoints = [];
2440+
2441+ if (oneWayFlick || distance > sideStageImage.width / 2) {
2442+ sideStageImage.snapTo(root.width)
2443+ } else {
2444+ sideStageImage.snapToApp(priv.sideStageApp)
2445+ }
2446+ }
2447+ }
2448+
2449+ SwitchingApplicationImage {
2450+ id: sideStageImage
2451+ width: priv.sideStageWidth
2452+ height: root.height
2453+ x: root.width
2454+ anchors.bottom: parent.bottom
2455+ visible: true
2456+ property bool shown: false
2457+
2458+ onSwitched: {
2459+ mainStageImage.visible = false;
2460+ ApplicationManager.focusApplication(application.appId)
2461+ }
2462+
2463+ function snapTo(targetX) {
2464+ sideStageSnapAnimation.targetX = targetX
2465+ sideStageImage.visible = true;
2466+ if (priv.mainStageAppId) {
2467+ mainStageImage.application = priv.mainStageApp
2468+ mainStageImage.visible = true;
2469+ }
2470+ sideStageSnapAnimation.start();
2471+ }
2472+
2473+ function snapToApp(application) {
2474+ sideStageImage.application = application
2475+ sideStageSnapAnimation.snapToId = application.appId;
2476+ snapTo(root.width - sideStageImage.width);
2477+ }
2478+
2479+ SequentialAnimation {
2480+ id: sideStageSnapAnimation
2481+ property int targetX: root.width
2482+ property string snapToId
2483+
2484+ UbuntuNumberAnimation { target: sideStageImage; property: "x"; to: sideStageSnapAnimation.targetX; duration: UbuntuAnimation.SlowDuration }
2485+ ScriptAction {
2486+ script: {
2487+ if (sideStageSnapAnimation.targetX == root.width) {
2488+ if (priv.mainStageAppId) {
2489+ ApplicationManager.focusApplication(priv.mainStageAppId)
2490+ } else {
2491+ priv.overlayOverride = true;
2492+ ApplicationManager.unfocusCurrentApplication();
2493+ }
2494+ sideStageImage.shown = false;
2495+ }
2496+ if (sideStageSnapAnimation.snapToId) {
2497+ ApplicationManager.focusApplication(sideStageSnapAnimation.snapToId)
2498+ sideStageSnapAnimation.snapToId = "";
2499+ sideStageImage.shown = true;
2500+ priv.overlayOverride = false;
2501+ }
2502+ sideStageImage.visible = false;
2503+ mainStageImage.visible = false;
2504+ }
2505+ }
2506+ }
2507+ }
2508+
2509+ Rectangle {
2510+ id: sideStageSplash
2511+ anchors.fill: parent
2512+ anchors.leftMargin: sideStageImage.x
2513+ color: "white"
2514+ }
2515+}
2516
2517=== added file 'qml/Stages/SwitchingApplicationImage.qml'
2518--- qml/Stages/SwitchingApplicationImage.qml 1970-01-01 00:00:00 +0000
2519+++ qml/Stages/SwitchingApplicationImage.qml 2014-03-25 15:08:15 +0000
2520@@ -0,0 +1,81 @@
2521+/*
2522+ * Copyright 2014 Canonical Ltd.
2523+ *
2524+ * This program is free software; you can redistribute it and/or modify
2525+ * it under the terms of the GNU Lesser General Public License as published by
2526+ * the Free Software Foundation; version 3.
2527+ *
2528+ * This program is distributed in the hope that it will be useful,
2529+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2530+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2531+ * GNU Lesser General Public License for more details.
2532+ *
2533+ * You should have received a copy of the GNU Lesser General Public License
2534+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2535+ *
2536+ * Authors: Michael Zanetti <michael.zanetti@canonical.com>
2537+*/
2538+
2539+import QtQuick 2.0
2540+import Ubuntu.Components 0.1
2541+
2542+Rectangle {
2543+ id: root
2544+ implicitHeight: image.implicitHeight
2545+ implicitWidth: image.implicitWidth
2546+ color: "black"
2547+
2548+ property var application
2549+
2550+ signal switched()
2551+
2552+ function switchTo(application) {
2553+ if (root.application == application) {
2554+ root.switched();
2555+ return;
2556+ }
2557+
2558+ priv.newApplication = application
2559+ root.visible = true;
2560+ switchToAnimation.start()
2561+ }
2562+
2563+ QtObject {
2564+ id: priv
2565+ property var newApplication
2566+ }
2567+
2568+ Image {
2569+ id: newImage
2570+ anchors.bottom: parent.bottom
2571+ width: root.width
2572+ source: priv.newApplication ? priv.newApplication.screenshot : ""
2573+ }
2574+
2575+ Image {
2576+ id: image
2577+ visible: true
2578+ source: root.application ? root.application.screenshot : ""
2579+ width: root.width
2580+ height: sourceSize.height
2581+ anchors.bottom: parent.bottom
2582+
2583+ }
2584+
2585+ SequentialAnimation {
2586+ id: switchToAnimation
2587+ ParallelAnimation {
2588+ UbuntuNumberAnimation { target: image; property: "x"; from: 0; to: root.width; duration: UbuntuAnimation.SlowDuration }
2589+ UbuntuNumberAnimation { target: newImage; property: "scale"; from: 0.7; to: 1; duration: UbuntuAnimation.SlowDuration }
2590+ }
2591+ ScriptAction {
2592+ script: {
2593+ image.x = 0
2594+ root.application = priv.newApplication
2595+ root.visible = false;
2596+ priv.newApplication = null
2597+ root.switched();
2598+ }
2599+ }
2600+ }
2601+}
2602
2603=== added file 'qml/Stages/TransformedSpreadDelegate.qml'
2604--- qml/Stages/TransformedSpreadDelegate.qml 1970-01-01 00:00:00 +0000
2605+++ qml/Stages/TransformedSpreadDelegate.qml 2014-03-25 15:08:15 +0000
2606@@ -0,0 +1,327 @@
2607+/*
2608+ * Copyright 2014 Canonical Ltd.
2609+ *
2610+ * This program is free software; you can redistribute it and/or modify
2611+ * it under the terms of the GNU Lesser General Public License as published by
2612+ * the Free Software Foundation; version 3.
2613+ *
2614+ * This program is distributed in the hope that it will be useful,
2615+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2616+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2617+ * GNU Lesser General Public License for more details.
2618+ *
2619+ * You should have received a copy of the GNU Lesser General Public License
2620+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2621+ *
2622+ * Authors: Michael Zanetti <michael.zanetti@canonical.com>
2623+*/
2624+
2625+import QtQuick 2.0
2626+import Utils 0.1
2627+import Ubuntu.Components 0.1
2628+
2629+SpreadDelegate {
2630+ id: root
2631+
2632+ property bool selected: false
2633+ property bool otherSelected: false
2634+
2635+ // The progress animates the tiles. A value > 0 makes it appear from the right edge. At 1 it reaches the end position.
2636+ property real progress: 0
2637+ // This is required to snap tile 1 during phase 1 and 2.
2638+ property real animatedProgress: 0
2639+
2640+ property real startAngle: 0
2641+ property real endAngle: 0
2642+
2643+ property real startScale: 1
2644+ property real endScale: 1
2645+
2646+ // Specific to just one tile
2647+ property real tile1StartScale: startScale + .4
2648+ property real tile0SnapAngle: 10
2649+
2650+ property real startDistance: units.gu(5)
2651+ property real endDistance: units.gu(.5)
2652+
2653+ onSelectedChanged: {
2654+ if (selected) {
2655+ priv.snapshot();
2656+ }
2657+ priv.isSelected = selected;
2658+ }
2659+
2660+ onOtherSelectedChanged: {
2661+ if (otherSelected) {
2662+ priv.snapshot();
2663+ }
2664+ priv.otherSelected = otherSelected;
2665+ }
2666+
2667+ Connections {
2668+ target: spreadView
2669+ onPhaseChanged: {
2670+ if (spreadView.phase == 1) {
2671+ if (index == 0) {
2672+ priv.phase2startTranslate = priv.easingAnimation(0, spreadView.positionMarker4, 0, -spreadView.width, spreadView.positionMarker4) + spreadView.width;
2673+ priv.phase2startAngle = priv.easingAnimation(0, spreadView.positionMarker4, root.startAngle, root.endAngle, spreadView.positionMarker4);
2674+ priv.phase2startScale = priv.easingAnimation(0, spreadView.positionMarker4, root.startScale, root.endScale, spreadView.positionMarker4);
2675+ priv.phase2startTopMarginProgress = priv.easingAnimation(0, 1, 0, 1, spreadView.positionMarker4);
2676+ } else if (index == 1) {
2677+ // find where the main easing for Tile 1 would be when reaching phase 2
2678+ var phase2Progress = spreadView.positionMarker4 - spreadView.tileDistance / spreadView.width;
2679+ priv.phase2startTranslate = priv.easingAnimation(0, phase2Progress, 0, -spreadView.width + root.endDistance, phase2Progress);
2680+ priv.phase2startAngle = priv.easingAnimation(0, phase2Progress, root.startAngle, root.endAngle, phase2Progress);
2681+ priv.phase2startScale = priv.easingAnimation(0, phase2Progress, root.startScale, root.endScale, phase2Progress);
2682+ priv.phase2startTopMarginProgress = priv.easingAnimation(0, 1, 0, spreadView.positionMarker4, phase2Progress);
2683+ }
2684+ }
2685+ }
2686+ }
2687+
2688+ QtObject {
2689+ id: priv
2690+ property bool isSelected: false
2691+ property bool otherSelected: false
2692+ property real selectedProgress
2693+ property real selectedXTranslate
2694+ property real selectedAngle
2695+ property real selectedScale
2696+ property real selectedOpacity
2697+ property real selectedTopMarginProgress
2698+
2699+ // Those values are needed as target values for the end of phase 1.
2700+ // As they are static values, lets calculate them once when entering phase 1 instead of calculating them in each animation pass.
2701+ property real phase2startTranslate
2702+ property real phase2startAngle
2703+ property real phase2startScale
2704+ property real phase2startTopMarginProgress
2705+
2706+ function snapshot() {
2707+ selectedProgress = root.progress;
2708+ selectedXTranslate = xTranslate;
2709+ selectedAngle = angle;
2710+ selectedScale = scale;
2711+ selectedOpacity = opacity;
2712+ selectedTopMarginProgress = topMarginProgress;
2713+ }
2714+
2715+ // This calculates how much negative progress there can be if unwinding the spread completely
2716+ // the progress for each tile starts at 0 when it crosses the right edge, so the later a tile comes in,
2717+ // the bigger its negativeProgress can be.
2718+ property real negativeProgress: {
2719+ if (index == 1 && spreadView.phase < 2) {
2720+ return 0;
2721+ }
2722+ return -index * spreadView.tileDistance / spreadView.width;
2723+ }
2724+
2725+ function linearAnimation(startProgress, endProgress, startValue, endValue, progress) {
2726+ // progress : progressDiff = value : valueDiff => value = progress * valueDiff / progressDiff
2727+ return (progress - startProgress) * (endValue - startValue) / (endProgress - startProgress) + startValue;
2728+ }
2729+
2730+ function easingAnimation(startProgress, endProgress, startValue, endValue, progress) {
2731+ helperEasingCurve.progress = progress - startProgress;
2732+ helperEasingCurve.period = endProgress - startProgress;
2733+ return helperEasingCurve.value * (endValue - startValue) + startValue;
2734+ }
2735+
2736+ property real animatedEndDistance: linearAnimation(0, 2, root.endDistance, 0, root.progress)
2737+
2738+ // The following blocks handle the animation of the tile in the spread.
2739+ // At the beginning, each tile is attached at the right edge, outside the screen.
2740+ // The progress for each tile starts at 0 and it reaches its end position at a progress of 1.
2741+ // The first phases are handled special for the first 2 tiles. as we do the alt-tab and snapping in there
2742+ // Once we reached phase 3, the animation is the same for all tiles.
2743+ // When a tile is selected, the animation state is snapshotted, and the spreadView is unwound (progress animates
2744+ // back to negativeProgress). All tiles are kept in place and faded out to 0 opacity except
2745+ // the selected tile, which is animated from the snapshotted position to be fullscreen.
2746+
2747+ property real xTranslate: {
2748+ if (otherSelected) {
2749+ if (spreadView.phase < 2 && index == 0) {
2750+ return linearAnimation(selectedProgress, negativeProgress,
2751+ selectedXTranslate, selectedXTranslate - spreadView.tileDistance, root.progress);
2752+ }
2753+
2754+ return selectedXTranslate;
2755+ }
2756+
2757+ switch (index) {
2758+ case 0:
2759+ if (spreadView.phase == 0) {
2760+ return Math.min(0, linearAnimation(0, spreadView.positionMarker2,
2761+ 0, -spreadView.width * .25, root.animatedProgress));
2762+ } else if (spreadView.phase == 1){
2763+ return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
2764+ -spreadView.width * .25, priv.phase2startTranslate, root.progress);
2765+ } else if (!priv.isSelected){ // phase 2
2766+ // Apply the same animation as with the rest but add spreadView.width to align it with the others.
2767+ return -easingCurve.value * spreadView.width + spreadView.width;
2768+ } else if (priv.isSelected) {
2769+ return linearAnimation(selectedProgress, negativeProgress, selectedXTranslate, 0, root.progress);
2770+ }
2771+
2772+ case 1:
2773+ if (spreadView.phase == 0 && !priv.isSelected) {
2774+ return linearAnimation(0, spreadView.positionMarker2,
2775+ 0, -spreadView.width * spreadView.snapPosition, root.animatedProgress);
2776+ } else if (spreadView.phase == 1 && !priv.isSelected) {
2777+ return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
2778+ -spreadView.width * spreadView.snapPosition, priv.phase2startTranslate,
2779+ root.progress);
2780+ }
2781+ }
2782+
2783+ if (priv.isSelected) {
2784+ // Distance to left edge
2785+ var targetTranslate = -spreadView.width - ((index - 1) * root.startDistance);
2786+ return linearAnimation(selectedProgress, negativeProgress,
2787+ selectedXTranslate, targetTranslate, root.progress);
2788+ }
2789+
2790+ // Fix it at the right edge...
2791+ var rightEdgeOffset = -((index - 1) * root.startDistance);
2792+ // ...and use our easing to move them to the left. Stop a bit earlier for each tile
2793+ return -easingCurve.value * spreadView.width + (index * animatedEndDistance) + rightEdgeOffset;
2794+
2795+ }
2796+
2797+ property real angle: {
2798+ if (priv.otherSelected) {
2799+ return priv.selectedAngle;
2800+ }
2801+ if (priv.isSelected) {
2802+ return linearAnimation(selectedProgress, negativeProgress, selectedAngle, 0, root.progress);
2803+ }
2804+ switch (index) {
2805+ case 0:
2806+ if (spreadView.phase == 0) {
2807+ return Math.max(0, linearAnimation(0, spreadView.positionMarker2,
2808+ 0, root.tile0SnapAngle, root.animatedProgress));
2809+ } else if (spreadView.phase == 1) {
2810+ return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
2811+ root.tile0SnapAngle, phase2startAngle, root.progress);
2812+ }
2813+ case 1:
2814+ if (spreadView.phase == 0) {
2815+ return linearAnimation(0, spreadView.positionMarker2, root.startAngle,
2816+ root.startAngle * (1-spreadView.snapPosition), root.animatedProgress);
2817+ } else if (spreadView.phase == 1) {
2818+ return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
2819+ root.startAngle * (1-spreadView.snapPosition), priv.phase2startAngle,
2820+ root.progress);
2821+ }
2822+ }
2823+ return root.startAngle - easingCurve.value * (root.startAngle - root.endAngle);
2824+ }
2825+
2826+ property real scale: {
2827+ if (priv.otherSelected) {
2828+ return priv.selectedScale;
2829+ }
2830+ if (priv.isSelected) {
2831+ return linearAnimation(selectedProgress, negativeProgress, selectedScale, 1, root.progress);
2832+ }
2833+
2834+ switch (index) {
2835+ case 0:
2836+ if (spreadView.phase == 0) {
2837+ return 1;
2838+ } else if (spreadView.phase == 1) {
2839+ return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
2840+ 1, phase2startScale, root.progress);
2841+ }
2842+ case 1:
2843+ if (spreadView.phase == 0) {
2844+ var targetScale = tile1StartScale - ((tile1StartScale - 1) * spreadView.snapPosition);
2845+ return linearAnimation(0, spreadView.positionMarker2,
2846+ root.tile1StartScale, targetScale, root.animatedProgress);
2847+ } else if (spreadView.phase == 1) {
2848+ var startScale = tile1StartScale - ((tile1StartScale - 1) * spreadView.snapPosition);
2849+ return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
2850+ startScale, priv.phase2startScale, root.progress);
2851+ }
2852+ }
2853+ return root.startScale - easingCurve.value * (root.startScale - root.endScale);
2854+ }
2855+
2856+ property real opacity: {
2857+ if (priv.otherSelected) {
2858+ return linearAnimation (selectedProgress, Math.max(0, selectedProgress - .5),
2859+ selectedOpacity, 0, root.progress);
2860+ }
2861+ if (index == 0) {
2862+ switch (spreadView.phase) {
2863+ case 0:
2864+ return linearAnimation(0, spreadView.positionMarker2, 1, .3, root.animatedProgress);
2865+ case 1:
2866+ return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
2867+ .3, 1, root.animatedProgress);
2868+ }
2869+ }
2870+
2871+ return 1;
2872+ }
2873+
2874+ property real topMarginProgress: {
2875+ if (priv.isSelected) {
2876+ return linearAnimation(selectedProgress, negativeProgress, selectedTopMarginProgress, 0, root.progress);
2877+ }
2878+
2879+ switch (index) {
2880+ case 0:
2881+ if (spreadView.phase == 0) {
2882+ return 0;
2883+ } else if (spreadView.phase == 1) {
2884+ return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
2885+ 0, priv.phase2startTopMarginProgress, root.progress);
2886+ }
2887+ break;
2888+ case 1:
2889+ if (spreadView.phase == 0) {
2890+ return 0;
2891+ } else if (spreadView.phase == 1) {
2892+ return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
2893+ 0, priv.phase2startTopMarginProgress, root.progress);
2894+ }
2895+ }
2896+
2897+ return easingCurve.value;
2898+ }
2899+ }
2900+
2901+ transform: [
2902+ Rotation {
2903+ origin { x: 0; y: spreadView.height / 2 }
2904+ axis { x: 0; y: 1; z: 0 }
2905+ angle: priv.angle
2906+ },
2907+ Scale {
2908+ origin { x: 0; y: spreadView.height / 2 }
2909+ xScale: priv.scale
2910+ yScale: xScale
2911+ },
2912+ Translate {
2913+ x: priv.xTranslate
2914+ }
2915+ ]
2916+ opacity: priv.opacity
2917+ topMarginProgress: priv.topMarginProgress
2918+
2919+ EasingCurve {
2920+ id: easingCurve
2921+ type: EasingCurve.OutSine
2922+ period: 1 - spreadView.positionMarker2
2923+ progress: root.animatedProgress
2924+ }
2925+
2926+ // This is used as a calculation helper to figure values for progress other than the current one
2927+ // Do not bind anything to this...
2928+ EasingCurve {
2929+ id: helperEasingCurve
2930+ type: easingCurve.type
2931+ period: easingCurve.period
2932+ }
2933+}
2934
2935=== added directory 'qml/Stages/graphics'
2936=== added file 'qml/Stages/graphics/dropshadow.png'
2937Binary 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
2938=== renamed file 'qml/SideStage/graphics/sidestage_handle@20.png' => 'qml/Stages/graphics/sidestage_handle@20.png'
2939=== renamed file 'qml/SideStage/graphics/sidestage_handle@20.sci' => 'qml/Stages/graphics/sidestage_handle@20.sci'
2940=== modified file 'run_on_device'
2941--- run_on_device 2013-11-27 11:47:14 +0000
2942+++ run_on_device 2014-03-25 15:08:15 +0000
2943@@ -103,6 +103,7 @@
2944 ARGS="$ARGS -k"
2945 fi
2946
2947+ exec_with_ssh "killall -9 dialer-app; killall -9 gallery-app; killall -9 messaging-app; killall -9 address-book-app; killall -9 webbrowser-app"
2948 exec_with_ssh "stop unity8"
2949 exec_with_ssh "start maliit-server"
2950 exec_with_ssh "cd $CODE_DIR/ && ./run $ARGS -- $RUN_OPTIONS"
2951
2952=== modified file 'tests/autopilot/unity8/shell/emulators/dash.py'
2953--- tests/autopilot/unity8/shell/emulators/dash.py 2014-03-17 10:42:57 +0000
2954+++ tests/autopilot/unity8/shell/emulators/dash.py 2014-03-25 15:08:15 +0000
2955@@ -80,10 +80,7 @@
2956 'No scope found with id {0}'.format(scope_id))
2957
2958 def _get_scope_from_loader(self, loader):
2959- if loader.scopeId == 'clickscope':
2960- return loader.select_single(DashApps)
2961- else:
2962- return loader.select_single(GenericScopeView)
2963+ return loader.select_single(GenericScopeView)
2964
2965 def _open_scope_scrolling(self, scope_loader):
2966 scroll = self._get_scroll_direction(scope_loader)
2967@@ -164,10 +161,6 @@
2968 raise emulators.UnityEmulatorException(
2969 'No category found with name {}'.format(category))
2970
2971-
2972-class DashApps(GenericScopeView):
2973- """Autopilot emulator for the applications scope."""
2974-
2975 def get_applications(self, category):
2976 """Return the list of applications on a category.
2977
2978
2979=== modified file 'tests/autopilot/unity8/shell/emulators/main_window.py'
2980--- tests/autopilot/unity8/shell/emulators/main_window.py 2014-03-02 04:44:30 +0000
2981+++ tests/autopilot/unity8/shell/emulators/main_window.py 2014-03-25 15:08:15 +0000
2982@@ -112,4 +112,4 @@
2983
2984 def get_current_focused_app_id(self):
2985 """Return the id of the focused application."""
2986- return self.select_single('Shell').currentFocusedAppId
2987+ return self.select_single('Shell').focusedApplicationId
2988
2989=== modified file 'tests/autopilot/unity8/shell/tests/test_emulators.py'
2990--- tests/autopilot/unity8/shell/tests/test_emulators.py 2014-03-17 10:42:57 +0000
2991+++ tests/autopilot/unity8/shell/tests/test_emulators.py 2014-03-25 15:08:15 +0000
2992@@ -128,7 +128,7 @@
2993 scope_id = 'clickscope'
2994 scope = self.dash.open_scope(scope_id)
2995 self._assert_scope_is_opened(scope, scope_id)
2996- self.assertIsInstance(scope, dash_emulators.DashApps)
2997+ self.assertIsInstance(scope, dash_emulators.GenericScopeView)
2998
2999
3000 class GenericScopeViewEmulatorTestCase(DashBaseTestCase):
3001
3002=== modified file 'tests/mocks/Unity/Application/ApplicationInfo.cpp'
3003--- tests/mocks/Unity/Application/ApplicationInfo.cpp 2013-09-05 12:21:35 +0000
3004+++ tests/mocks/Unity/Application/ApplicationInfo.cpp 2014-03-25 15:08:15 +0000
3005@@ -20,6 +20,7 @@
3006 #include <QQuickItem>
3007 #include <QQuickView>
3008 #include <QQmlComponent>
3009+#include <QTimer>
3010
3011 ApplicationInfo::ApplicationInfo(const QString &appId, QObject *parent)
3012 : ApplicationInfoInterface(appId, parent)
3013@@ -32,6 +33,7 @@
3014 ,m_windowComponent(0)
3015 ,m_parentItem(0)
3016 {
3017+ QTimer::singleShot(300, this, SLOT(setRunning()));
3018 }
3019
3020 ApplicationInfo::ApplicationInfo(QObject *parent)
3021@@ -44,6 +46,7 @@
3022 ,m_windowComponent(0)
3023 ,m_parentItem(0)
3024 {
3025+ QTimer::singleShot(300, this, SLOT(setRunning()));
3026 }
3027
3028 void ApplicationInfo::onWindowComponentStatusChanged(QQmlComponent::Status status)
3029@@ -52,6 +55,12 @@
3030 doCreateWindowItem();
3031 }
3032
3033+void ApplicationInfo::setRunning()
3034+{
3035+ m_state = Running;
3036+ Q_EMIT stateChanged();
3037+}
3038+
3039 void ApplicationInfo::createWindowComponent()
3040 {
3041 // The assumptions I make here really should hold.
3042
3043=== modified file 'tests/mocks/Unity/Application/ApplicationInfo.h'
3044--- tests/mocks/Unity/Application/ApplicationInfo.h 2013-10-11 11:37:04 +0000
3045+++ tests/mocks/Unity/Application/ApplicationInfo.h 2014-03-25 15:08:15 +0000
3046@@ -74,6 +74,7 @@
3047 IMPLEMENT_PROPERTY(fullscreen, Fullscreen, bool)
3048 IMPLEMENT_PROPERTY(imageQml, ImageQml, QString)
3049 IMPLEMENT_PROPERTY(windowQml, WindowQml, QString)
3050+ IMPLEMENT_PROPERTY(screenshot, Screenshot, QUrl)
3051
3052 #undef IMPLEMENT_PROPERTY
3053
3054@@ -83,6 +84,7 @@
3055
3056 private Q_SLOTS:
3057 void onWindowComponentStatusChanged(QQmlComponent::Status status);
3058+ void setRunning();
3059
3060 private:
3061 void createWindowItem();
3062
3063=== modified file 'tests/mocks/Unity/Application/ApplicationManager.cpp'
3064--- tests/mocks/Unity/Application/ApplicationManager.cpp 2013-12-17 16:04:47 +0000
3065+++ tests/mocks/Unity/Application/ApplicationManager.cpp 2014-03-25 15:08:15 +0000
3066@@ -24,13 +24,17 @@
3067 #include <QQuickItem>
3068 #include <QQuickView>
3069 #include <QQmlComponent>
3070+#include <QTimer>
3071+#include <QDateTime>
3072
3073 ApplicationManager::ApplicationManager(QObject *parent)
3074 : ApplicationManagerInterface(parent)
3075+ , m_suspended(false)
3076 , m_mainStageComponent(0)
3077 , m_mainStage(0)
3078 , m_sideStageComponent(0)
3079 , m_sideStage(0)
3080+ , m_rightMargin(0)
3081 {
3082 buildListOfAvailableApplications();
3083 }
3084@@ -63,6 +67,8 @@
3085 return app->state();
3086 case RoleFocused:
3087 return app->focused();
3088+ case RoleScreenshot:
3089+ return app->screenshot();
3090 default:
3091 return QVariant();
3092 }
3093@@ -84,13 +90,16 @@
3094 }
3095
3096 void ApplicationManager::add(ApplicationInfo *application) {
3097- if (!application)
3098+ if (!application) {
3099 return;
3100+ }
3101
3102 beginInsertRows(QModelIndex(), m_runningApplications.size(), m_runningApplications.size());
3103 m_runningApplications.append(application);
3104 endInsertRows();
3105+ Q_EMIT applicationAdded(application->appId());
3106 Q_EMIT countChanged();
3107+ Q_EMIT focusRequested(application->appId());
3108 }
3109
3110 void ApplicationManager::remove(ApplicationInfo *application) {
3111@@ -99,6 +108,7 @@
3112 beginRemoveRows(QModelIndex(), i, i);
3113 m_runningApplications.removeAt(i);
3114 endRemoveRows();
3115+ Q_EMIT applicationRemoved(application->appId());
3116 Q_EMIT countChanged();
3117 }
3118 }
3119@@ -171,6 +181,8 @@
3120 }
3121 add(application);
3122
3123+ QMetaObject::invokeMethod(this, "focusApplication", Qt::QueuedConnection, Q_ARG(QString, appId));
3124+
3125 return application;
3126 }
3127
3128@@ -180,8 +192,32 @@
3129 if (application == nullptr)
3130 return false;
3131
3132+ if (application->appId() == focusedApplicationId()) {
3133+ unfocusCurrentApplication();
3134+ }
3135 remove(application);
3136- Q_EMIT focusedApplicationIdChanged();
3137+ return true;
3138+}
3139+
3140+bool ApplicationManager::updateScreenshot(const QString &appId)
3141+{
3142+ int idx = -1;
3143+ ApplicationInfo *application = nullptr;
3144+ for (int i = 0; i < m_availableApplications.count(); ++i) {
3145+ application = m_availableApplications.at(i);
3146+ if (application->appId() == appId) {
3147+ idx = i;
3148+ break;
3149+ }
3150+ }
3151+
3152+ if (idx == -1) {
3153+ return false;
3154+ }
3155+
3156+ application->setScreenshot(QString("image://application/%1/%2").arg(appId).arg(QDateTime::currentMSecsSinceEpoch()));
3157+ QModelIndex appIndex = index(idx);
3158+ Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << RoleScreenshot);
3159 return true;
3160 }
3161
3162@@ -194,6 +230,25 @@
3163 return QString();
3164 }
3165
3166+bool ApplicationManager::suspended() const
3167+{
3168+ return m_suspended;
3169+}
3170+
3171+void ApplicationManager::setSuspended(bool suspended)
3172+{
3173+ ApplicationInfo *focusedApp = findApplication(focusedApplicationId());
3174+ if (focusedApp) {
3175+ if (suspended) {
3176+ focusedApp->setState(ApplicationInfo::Suspended);
3177+ } else {
3178+ focusedApp->setState(ApplicationInfo::Running);
3179+ }
3180+ }
3181+ m_suspended = suspended;
3182+ Q_EMIT suspendedChanged();
3183+}
3184+
3185 bool ApplicationManager::focusApplication(const QString &appId)
3186 {
3187 ApplicationInfo *application = findApplication(appId);
3188@@ -242,6 +297,15 @@
3189 return true;
3190 }
3191
3192+bool ApplicationManager::requestFocusApplication(const QString &appId)
3193+{
3194+ if (appId != focusedApplicationId()) {
3195+ QMetaObject::invokeMethod(this, "focusRequested", Qt::QueuedConnection, Q_ARG(QString, appId));
3196+ return true;
3197+ }
3198+ return false;
3199+}
3200+
3201 void ApplicationManager::unfocusCurrentApplication()
3202 {
3203 for (ApplicationInfo *app : m_runningApplications) {
3204@@ -270,10 +334,12 @@
3205 "Image {\n"
3206 " anchors.fill: parent\n"
3207 " anchors.topMargin: %1\n"
3208- " source: \"file://%2/Dash/graphics/phone/screenshots/%3.png\"\n"
3209+ " anchors.rightMargin: %2\n"
3210+ " source: \"file://%3/Dash/graphics/phone/screenshots/%4.png\"\n"
3211 " smooth: true\n"
3212 " fillMode: Image.PreserveAspectCrop\n"
3213 "}").arg(topMargin)
3214+ .arg(m_rightMargin)
3215 .arg(qmlDirectory())
3216 .arg(application->icon().toString());
3217 application->setWindowQml(windowQml);
3218@@ -299,6 +365,7 @@
3219 application->setName("Phone");
3220 application->setIcon(QUrl("phone"));
3221 application->setStage(ApplicationInfo::SideStage);
3222+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
3223 generateQmlStrings(application);
3224 m_availableApplications.append(application);
3225
3226@@ -307,6 +374,7 @@
3227 application->setName("Camera");
3228 application->setIcon(QUrl("camera"));
3229 application->setFullscreen(true);
3230+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
3231 generateQmlStrings(application);
3232 m_availableApplications.append(application);
3233
3234@@ -314,6 +382,7 @@
3235 application->setAppId("gallery-app");
3236 application->setName("Gallery");
3237 application->setIcon(QUrl("gallery"));
3238+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
3239 generateQmlStrings(application);
3240 m_availableApplications.append(application);
3241
3242@@ -322,13 +391,16 @@
3243 application->setName("Facebook");
3244 application->setIcon(QUrl("facebook"));
3245 application->setStage(ApplicationInfo::SideStage);
3246+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
3247 generateQmlStrings(application);
3248 m_availableApplications.append(application);
3249
3250 application = new ApplicationInfo(this);
3251 application->setAppId("webbrowser-app");
3252+ application->setFullscreen(true);
3253 application->setName("Browser");
3254 application->setIcon(QUrl("browser"));
3255+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
3256 generateQmlStrings(application);
3257 m_availableApplications.append(application);
3258
3259@@ -337,6 +409,7 @@
3260 application->setName("Twitter");
3261 application->setIcon(QUrl("twitter"));
3262 application->setStage(ApplicationInfo::SideStage);
3263+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
3264 generateQmlStrings(application);
3265 m_availableApplications.append(application);
3266
3267@@ -344,6 +417,7 @@
3268 application->setAppId("gmail-webapp");
3269 application->setName("GMail");
3270 application->setIcon(QUrl("gmail"));
3271+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
3272 m_availableApplications.append(application);
3273
3274 application = new ApplicationInfo(this);
3275@@ -351,6 +425,7 @@
3276 application->setName("Weather");
3277 application->setIcon(QUrl("weather"));
3278 application->setStage(ApplicationInfo::SideStage);
3279+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
3280 generateQmlStrings(application);
3281 m_availableApplications.append(application);
3282
3283@@ -359,6 +434,7 @@
3284 application->setName("Notepad");
3285 application->setIcon(QUrl("notepad"));
3286 application->setStage(ApplicationInfo::SideStage);
3287+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
3288 m_availableApplications.append(application);
3289
3290 application = new ApplicationInfo(this);
3291@@ -366,6 +442,7 @@
3292 application->setName("Calendar");
3293 application->setIcon(QUrl("calendar"));
3294 application->setStage(ApplicationInfo::SideStage);
3295+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
3296 m_availableApplications.append(application);
3297
3298 application = new ApplicationInfo(this);
3299@@ -373,18 +450,21 @@
3300 application->setName("Media Player");
3301 application->setIcon(QUrl("mediaplayer-app"));
3302 application->setFullscreen(true);
3303+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
3304 m_availableApplications.append(application);
3305
3306 application = new ApplicationInfo(this);
3307 application->setAppId("evernote");
3308 application->setName("Evernote");
3309 application->setIcon(QUrl("evernote"));
3310+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
3311 m_availableApplications.append(application);
3312
3313 application = new ApplicationInfo(this);
3314 application->setAppId("map");
3315 application->setName("Map");
3316 application->setIcon(QUrl("map"));
3317+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
3318 generateQmlStrings(application);
3319 m_availableApplications.append(application);
3320
3321@@ -392,24 +472,28 @@
3322 application->setAppId("pinterest");
3323 application->setName("Pinterest");
3324 application->setIcon(QUrl("pinterest"));
3325+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
3326 m_availableApplications.append(application);
3327
3328 application = new ApplicationInfo(this);
3329 application->setAppId("soundcloud");
3330 application->setName("SoundCloud");
3331 application->setIcon(QUrl("soundcloud"));
3332+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
3333 m_availableApplications.append(application);
3334
3335 application = new ApplicationInfo(this);
3336 application->setAppId("wikipedia");
3337 application->setName("Wikipedia");
3338 application->setIcon(QUrl("wikipedia"));
3339+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
3340 m_availableApplications.append(application);
3341
3342 application = new ApplicationInfo(this);
3343 application->setAppId("youtube");
3344 application->setName("YouTube");
3345 application->setIcon(QUrl("youtube"));
3346+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
3347 m_availableApplications.append(application);
3348 }
3349
3350@@ -484,3 +568,25 @@
3351 m_sideStage->setParentItem(shell);
3352 m_sideStage->setFlag(QQuickItem::ItemHasContents, false);
3353 }
3354+
3355+QStringList ApplicationManager::availableApplications()
3356+{
3357+ QStringList appIds;
3358+ Q_FOREACH(ApplicationInfo *app, m_availableApplications) {
3359+ appIds << app->appId();
3360+ }
3361+ return appIds;
3362+}
3363+
3364+int ApplicationManager::rightMargin() const
3365+{
3366+ return m_rightMargin;
3367+}
3368+
3369+void ApplicationManager::setRightMargin(int rightMargin)
3370+{
3371+ m_rightMargin = rightMargin;
3372+ Q_FOREACH(ApplicationInfo *app, m_availableApplications) {
3373+ generateQmlStrings(app);
3374+ }
3375+}
3376
3377=== modified file 'tests/mocks/Unity/Application/ApplicationManager.h'
3378--- tests/mocks/Unity/Application/ApplicationManager.h 2013-10-11 11:37:04 +0000
3379+++ tests/mocks/Unity/Application/ApplicationManager.h 2014-03-25 15:08:15 +0000
3380@@ -45,6 +45,10 @@
3381
3382 Q_PROPERTY(bool fake READ fake CONSTANT)
3383
3384+ // Only for testing
3385+ // This can be used to place some controls to right, like make tryPhoneStage for example
3386+ Q_PROPERTY(int rightMargin READ rightMargin WRITE setRightMargin)
3387+
3388 public:
3389 ApplicationManager(QObject *parent = NULL);
3390 virtual ~ApplicationManager();
3391@@ -87,13 +91,22 @@
3392 Q_INVOKABLE void move(int from, int to);
3393
3394 // Application control methods
3395+ Q_INVOKABLE bool requestFocusApplication(const QString &appId) override;
3396 Q_INVOKABLE bool focusApplication(const QString &appId) override;
3397 Q_INVOKABLE void unfocusCurrentApplication() override;
3398 Q_INVOKABLE ApplicationInfo *startApplication(const QString &appId, const QStringList &arguments = QStringList()) override;
3399 Q_INVOKABLE ApplicationInfo *startApplication(const QString &appId, ExecFlags flags, const QStringList &arguments = QStringList());
3400 Q_INVOKABLE bool stopApplication(const QString &appId) override;
3401+ Q_INVOKABLE bool updateScreenshot(const QString &appId) override;
3402
3403 QString focusedApplicationId() const override;
3404+ bool suspended() const;
3405+ void setSuspended(bool suspended);
3406+
3407+ // Only for testing
3408+ Q_INVOKABLE QStringList availableApplications();
3409+ int rightMargin() const;
3410+ void setRightMargin(int rightMargin);
3411
3412 Q_SIGNALS:
3413 void keyboardHeightChanged();
3414@@ -113,12 +126,15 @@
3415 void createSideStage();
3416 int m_keyboardHeight;
3417 bool m_keyboardVisible;
3418+ bool m_suspended;
3419 QList<ApplicationInfo*> m_runningApplications;
3420 QList<ApplicationInfo*> m_availableApplications;
3421 QQmlComponent *m_mainStageComponent;
3422 QQuickItem *m_mainStage;
3423 QQmlComponent *m_sideStageComponent;
3424 QQuickItem *m_sideStage;
3425+
3426+ int m_rightMargin;
3427 };
3428
3429 Q_DECLARE_OPERATORS_FOR_FLAGS(ApplicationManager::ExecFlags)
3430
3431=== added file 'tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp'
3432--- tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp 1970-01-01 00:00:00 +0000
3433+++ tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp 2014-03-25 15:08:15 +0000
3434@@ -0,0 +1,74 @@
3435+/*
3436+ * Copyright (C) 2014 Canonical, Ltd.
3437+ *
3438+ * This program is free software: you can redistribute it and/or modify it under
3439+ * the terms of the GNU Lesser General Public License version 3, as published by
3440+ * the Free Software Foundation.
3441+ *
3442+ * This program is distributed in the hope that it will be useful, but WITHOUT
3443+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
3444+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3445+ * Lesser General Public License for more details.
3446+ *
3447+ * You should have received a copy of the GNU Lesser General Public License
3448+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3449+ */
3450+
3451+#include "ApplicationScreenshotProvider.h"
3452+#include "ApplicationManager.h"
3453+#include "ApplicationInfo.h"
3454+
3455+#include "paths.h"
3456+
3457+#include <QDebug>
3458+#include <QGuiApplication>
3459+#include <QWindow>
3460+#include <QQuickWindow>
3461+
3462+ApplicationScreenshotProvider::ApplicationScreenshotProvider(ApplicationManager *appManager)
3463+ : QQuickImageProvider(QQuickImageProvider::Image)
3464+ , m_appManager(appManager)
3465+{
3466+}
3467+
3468+QImage ApplicationScreenshotProvider::requestImage(const QString &imageId, QSize * size,
3469+ const QSize &requestedSize)
3470+{
3471+ // We ignore requestedSize here intentionally to avoid keeping scaled copies around
3472+ Q_UNUSED(requestedSize)
3473+
3474+ QString appId = imageId.split('/').first();
3475+
3476+ ApplicationInfo* app = static_cast<ApplicationInfo*>(m_appManager->findApplication(appId));
3477+ if (app == nullptr) {
3478+ return QImage();
3479+ }
3480+
3481+ QString filePath = QString("%1/Dash/graphics/phone/screenshots/%2@12.png").arg(qmlDirectory()).arg(app->icon().toString());
3482+
3483+ QImage image;
3484+ if (!image.load(filePath)) {
3485+ qWarning() << "failed loading app image" << filePath;
3486+ }
3487+
3488+
3489+ if (app->stage() == ApplicationInfo::SideStage) {
3490+ QByteArray gus = qgetenv("GRID_UNIT_PX");
3491+ image = image.scaledToWidth(gus.toInt() * 48);
3492+ } else {
3493+ // Lets scale main stage applications to be the size of the screen/window.
3494+ QGuiApplication *unity = qobject_cast<QGuiApplication*>(qApp);
3495+ Q_FOREACH (QWindow *win, unity->allWindows()) {
3496+ QQuickWindow *quickWin = qobject_cast<QQuickWindow*>(win);
3497+ if (quickWin) {
3498+ image = image.scaledToWidth(quickWin->width() - m_appManager->rightMargin());
3499+ break;
3500+ }
3501+ }
3502+ }
3503+
3504+ size->setWidth(image.width());
3505+ size->setHeight(image.height());
3506+
3507+ return image;
3508+}
3509
3510=== added file 'tests/mocks/Unity/Application/ApplicationScreenshotProvider.h'
3511--- tests/mocks/Unity/Application/ApplicationScreenshotProvider.h 1970-01-01 00:00:00 +0000
3512+++ tests/mocks/Unity/Application/ApplicationScreenshotProvider.h 2014-03-25 15:08:15 +0000
3513@@ -0,0 +1,34 @@
3514+/*
3515+ * Copyright (C) 201 Canonical, Ltd.
3516+ *
3517+ * This program is free software: you can redistribute it and/or modify it under
3518+ * the terms of the GNU Lesser General Public License version 3, as published by
3519+ * the Free Software Foundation.
3520+ *
3521+ * This program is distributed in the hope that it will be useful, but WITHOUT
3522+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
3523+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3524+ * Lesser General Public License for more details.
3525+ *
3526+ * You should have received a copy of the GNU Lesser General Public License
3527+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3528+ */
3529+
3530+#ifndef APPLICATIONSCREENSHOTPROVIDER_H
3531+#define APPLICATIONSCREENSHOTPROVIDER_H
3532+
3533+#include <QQuickImageProvider>
3534+
3535+class ApplicationManager;
3536+class ApplicationScreenshotProvider : public QQuickImageProvider
3537+{
3538+public:
3539+ explicit ApplicationScreenshotProvider(ApplicationManager *appManager);
3540+
3541+ QImage requestImage(const QString &appId, QSize *size, const QSize &requestedSize) override;
3542+
3543+private:
3544+ ApplicationManager* m_appManager;
3545+};
3546+
3547+#endif // APPLICATIONSCREENSHOTPROVIDER_H
3548
3549=== modified file 'tests/mocks/Unity/Application/CMakeLists.txt'
3550--- tests/mocks/Unity/Application/CMakeLists.txt 2013-12-13 01:02:53 +0000
3551+++ tests/mocks/Unity/Application/CMakeLists.txt 2014-03-25 15:08:15 +0000
3552@@ -5,6 +5,7 @@
3553 ApplicationInfo.cpp
3554 ApplicationImage.cpp
3555 ApplicationManager.cpp
3556+ ApplicationScreenshotProvider.cpp
3557 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
3558 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
3559 )
3560
3561=== modified file 'tests/mocks/Unity/Application/plugin.cpp'
3562--- tests/mocks/Unity/Application/plugin.cpp 2013-09-11 15:33:02 +0000
3563+++ tests/mocks/Unity/Application/plugin.cpp 2014-03-25 15:08:15 +0000
3564@@ -18,13 +18,20 @@
3565 #include "ApplicationInfo.h"
3566 #include "ApplicationImage.h"
3567 #include "ApplicationManager.h"
3568+#include "ApplicationScreenshotProvider.h"
3569
3570 #include <qqml.h>
3571+#include <QQmlEngine>
3572+
3573+ApplicationManager *s_appManager = 0;
3574
3575 static QObject* applicationManagerSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
3576- Q_UNUSED(engine);
3577- Q_UNUSED(scriptEngine);
3578- return new ApplicationManager();
3579+ Q_UNUSED(engine);
3580+ Q_UNUSED(scriptEngine);
3581+ if (!s_appManager) {
3582+ s_appManager = new ApplicationManager();
3583+ }
3584+ return s_appManager;
3585 }
3586
3587 void FakeUnityApplicationQmlPlugin::registerTypes(const char *uri)
3588@@ -37,3 +44,11 @@
3589
3590 qmlRegisterType<ApplicationImage>(uri, 0, 1, "ApplicationImage");
3591 }
3592+
3593+void FakeUnityApplicationQmlPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
3594+{
3595+ QQmlExtensionPlugin::initializeEngine(engine, uri);
3596+
3597+ ApplicationManager* appManager = static_cast<ApplicationManager*>(applicationManagerSingleton(engine, NULL));
3598+ engine->addImageProvider(QLatin1String("application"), new ApplicationScreenshotProvider(appManager));
3599+}
3600
3601=== modified file 'tests/mocks/Unity/Application/plugin.h'
3602--- tests/mocks/Unity/Application/plugin.h 2013-09-04 13:42:27 +0000
3603+++ tests/mocks/Unity/Application/plugin.h 2014-03-25 15:08:15 +0000
3604@@ -25,6 +25,7 @@
3605 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
3606 public:
3607 void registerTypes(const char *uri);
3608+ void initializeEngine(QQmlEngine *engine, const char *uri);
3609 };
3610
3611 #endif
3612
3613=== modified file 'tests/plugins/Unity/Launcher/launchermodeltest.cpp'
3614--- tests/plugins/Unity/Launcher/launchermodeltest.cpp 2013-09-16 17:59:13 +0000
3615+++ tests/plugins/Unity/Launcher/launchermodeltest.cpp 2014-03-25 15:08:15 +0000
3616@@ -39,6 +39,7 @@
3617 ApplicationInfoInterface::Stage stage() const { return ApplicationInfoInterface::MainStage; }
3618 ApplicationInfoInterface::State state() const { return ApplicationInfoInterface::Running; }
3619 bool focused() const { return m_focused; }
3620+ QUrl screenshot() const { return QUrl(); }
3621
3622 // Methods used for mocking (not in the interface)
3623 void setFocused(bool focused) { m_focused = focused; Q_EMIT focusedChanged(focused); }
3624@@ -92,6 +93,10 @@
3625 m_list.takeAt(index)->deleteLater();
3626 endRemoveRows();
3627 }
3628+ bool updateScreenshot(const QString &appId) { Q_UNUSED(appId); return true; }
3629+ bool requestFocusApplication(const QString &appId) { Q_UNUSED(appId); return true; }
3630+ bool suspended() const { return false; }
3631+ void setSuspended(bool) {}
3632
3633 private:
3634 QList<MockApp*> m_list;
3635
3636=== modified file 'tests/qmltests/CMakeLists.txt'
3637--- tests/qmltests/CMakeLists.txt 2014-03-17 11:44:05 +0000
3638+++ tests/qmltests/CMakeLists.txt 2014-03-25 15:08:15 +0000
3639@@ -83,6 +83,6 @@
3640 ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
3641 add_qml_test(Panel/Indicators MenuItemFactory IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)
3642 add_qml_test(Panel/Indicators MessageMenuItemFactory IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)
3643-add_qml_test(SideStage SideStage IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS}
3644+add_qml_test(Stages PhoneStage IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS}
3645 ${CMAKE_BINARY_DIR}/tests/mocks
3646 ${CMAKE_BINARY_DIR}/plugins)
3647
3648=== modified file 'tests/qmltests/Components/tst_ResponsiveFlowView.qml'
3649--- tests/qmltests/Components/tst_ResponsiveFlowView.qml 2013-12-17 16:04:47 +0000
3650+++ tests/qmltests/Components/tst_ResponsiveFlowView.qml 2014-03-25 15:08:15 +0000
3651@@ -82,8 +82,7 @@
3652 ResponsiveFlowView {
3653 id: flow
3654 anchors.fill: parent
3655- firstModel: fakeModel
3656- secondModel: fakeModel
3657+ model: fakeModel
3658 minimumHorizontalSpacing:
3659 minHSpacingSelector.values[minHSpacingSelector.selectedIndex]
3660 verticalSpacing: units.gu(2)
3661
3662=== modified file 'tests/qmltests/Dash/Apps/tst_RunningApplicationsGrid.qml'
3663--- tests/qmltests/Dash/Apps/tst_RunningApplicationsGrid.qml 2013-12-17 16:04:47 +0000
3664+++ tests/qmltests/Dash/Apps/tst_RunningApplicationsGrid.qml 2014-03-25 15:08:15 +0000
3665@@ -18,87 +18,20 @@
3666 import QtTest 1.0
3667 import "../../../../qml/Dash/Apps"
3668 import Unity.Test 0.1 as UT
3669+import Unity.Application 0.1
3670
3671-Item {
3672+// Using Rectangle to have an opaque surface because AppManager paints app surfaces behind it.
3673+Rectangle {
3674 width: units.gu(50)
3675 height: units.gu(40)
3676
3677- QtObject {
3678- id: fakeApplicationManager
3679-
3680- property bool sideStageEnabled: false
3681-
3682- function stopApplication(appId) {
3683- for (var i=0, len=fakeRunningAppsModel.count; i<len; i++) {
3684- if (appId == fakeRunningAppsModel.get(i).appId) {
3685- fakeRunningAppsModel.remove(i)
3686- }
3687- }
3688- }
3689- }
3690-
3691- QtObject {
3692- id: shell
3693- property bool dashShown: true
3694- property bool stageScreenshotsReady: false
3695- property var applicationManager: fakeApplicationManager
3696-
3697- function activateApplication(appId) {
3698- }
3699- }
3700-
3701- ListModel {
3702- id: fakeRunningAppsModel
3703-
3704- function contains(appId) {
3705- for (var i=0, len=fakeRunningAppsModel.count; i<len; i++) {
3706- if (appId == fakeRunningAppsModel.get(i).appId) {
3707- return true;
3708- }
3709- }
3710- return false;
3711- }
3712-
3713- function rePopulate() {
3714- for (var i=0, len=availableAppsModel.count; i<len; i++) {
3715- fakeRunningAppsModel.append(availableAppsModel.get(i));
3716- }
3717- }
3718- }
3719-
3720- ListModel {
3721- id: availableAppsModel
3722- ListElement {
3723- name: "Phone"
3724- icon: "phone-app"
3725- exec: "/usr/bin/phone-app"
3726- appId: "phone"
3727- imageQml: "import QtQuick 2.0\n \
3728- Rectangle { \n \
3729- anchors.fill:parent \n \
3730- color:'darkgreen' \n \
3731- Text { anchors.centerIn: parent; text: 'PHONE' } \n \
3732- }"
3733- }
3734-
3735- ListElement {
3736- name: "Calendar"
3737- icon: "calendar-app"
3738- exec: "/usr/bin/calendar-app"
3739- appId: "calendar"
3740- imageQml: "import QtQuick 2.0\n \
3741- Rectangle { \n \
3742- anchors.fill:parent \n \
3743- color:'darkblue' \n \
3744- Text { anchors.centerIn: parent; text: 'CALENDAR'\n \
3745- color:'white'} \n \
3746- }"
3747- }
3748- }
3749-
3750 function resetRunningApplications() {
3751- fakeRunningAppsModel.clear()
3752- fakeRunningAppsModel.rePopulate()
3753+ while (ApplicationManager.count > 0) {
3754+ ApplicationManager.stopApplication(ApplicationManager.get(0).appId)
3755+ }
3756+
3757+ ApplicationManager.startApplication("phone-app");
3758+ ApplicationManager.startApplication("webbrowser-app");
3759 }
3760
3761 Component.onCompleted: {
3762@@ -109,7 +42,7 @@
3763 RunningApplicationsGrid {
3764 id: runningApplicationsGrid
3765 anchors.fill: parent
3766- firstModel: fakeRunningAppsModel
3767+ model: ApplicationManager
3768 }
3769
3770 UT.UnityTestCase {
3771@@ -121,11 +54,11 @@
3772 resetRunningApplications()
3773 }
3774
3775- property var calendarTile
3776+ property var browserTile
3777 property var phoneTile
3778
3779- property var isCalendarLongPressed: false
3780- function onCalendarLongPressed() {isCalendarLongPressed = true}
3781+ property var isBrowserLongPressed: false
3782+ function onBrowserLongPressed() {isBrowserLongPressed = true}
3783
3784 property var isPhoneLongPressed: false
3785 function onPhoneLongPressed() {isPhoneLongPressed = true}
3786@@ -133,25 +66,25 @@
3787 // Tiles should go to termination mode when any one of them is long-pressed.
3788 // Long-pressing when they're in termination mode brings them back to activation mode
3789 function test_enterTerminationMode() {
3790- calendarTile = findChild(runningApplicationsGrid, "runningAppTile Calendar")
3791- verify(calendarTile != undefined)
3792- calendarTile.onPressAndHold.connect(onCalendarLongPressed)
3793+ browserTile = findChild(runningApplicationsGrid, "runningAppTile Browser")
3794+ verify(browserTile != undefined)
3795+ browserTile.onPressAndHold.connect(onBrowserLongPressed)
3796
3797 phoneTile = findChild(runningApplicationsGrid, "runningAppTile Phone")
3798 verify(phoneTile != undefined)
3799 phoneTile.onPressAndHold.connect(onPhoneLongPressed)
3800
3801- compare(calendarTile.terminationModeEnabled, false)
3802+ compare(browserTile.terminationModeEnabled, false)
3803 compare(phoneTile.terminationModeEnabled, false)
3804 compare(runningApplicationsGrid.terminationModeEnabled, false)
3805
3806- isCalendarLongPressed = false
3807- mousePress(calendarTile, calendarTile.width/2, calendarTile.height/2)
3808+ isBrowserLongPressed = false
3809+ mousePress(browserTile, browserTile.width/2, browserTile.height/2)
3810 tryCompareFunction(checkSwitchToTerminationModeAfterLongPress, true)
3811
3812- mouseRelease(calendarTile, calendarTile.width/2, calendarTile.height/2)
3813+ mouseRelease(browserTile, browserTile.width/2, browserTile.height/2)
3814
3815- compare(calendarTile.terminationModeEnabled, true)
3816+ compare(browserTile.terminationModeEnabled, true)
3817 compare(phoneTile.terminationModeEnabled, true)
3818 compare(runningApplicationsGrid.terminationModeEnabled, true)
3819
3820@@ -161,23 +94,23 @@
3821
3822 mouseRelease(phoneTile, phoneTile.width/2, phoneTile.height/2)
3823
3824- compare(calendarTile.terminationModeEnabled, false)
3825+ compare(browserTile.terminationModeEnabled, false)
3826 compare(phoneTile.terminationModeEnabled, false)
3827 compare(runningApplicationsGrid.terminationModeEnabled, false)
3828
3829- calendarTile.onPressAndHold.disconnect(onCalendarLongPressed)
3830+ browserTile.onPressAndHold.disconnect(onBrowserLongPressed)
3831 phoneTile.onPressAndHold.disconnect(onPhoneLongPressed)
3832 }
3833
3834 // Checks that components swicth to termination mode after (and only after) a long
3835- // press happens on Calendar tile.
3836+ // press happens on Browser tile.
3837 function checkSwitchToTerminationModeAfterLongPress() {
3838- compare(calendarTile.terminationModeEnabled, isCalendarLongPressed)
3839- compare(phoneTile.terminationModeEnabled, isCalendarLongPressed)
3840- compare(runningApplicationsGrid.terminationModeEnabled, isCalendarLongPressed)
3841+ compare(browserTile.terminationModeEnabled, isBrowserLongPressed)
3842+ compare(phoneTile.terminationModeEnabled, isBrowserLongPressed)
3843+ compare(runningApplicationsGrid.terminationModeEnabled, isBrowserLongPressed)
3844
3845- return isCalendarLongPressed &&
3846- calendarTile.terminationModeEnabled &&
3847+ return isBrowserLongPressed &&
3848+ browserTile.terminationModeEnabled &&
3849 phoneTile.terminationModeEnabled &&
3850 runningApplicationsGrid.terminationModeEnabled
3851 }
3852@@ -185,12 +118,12 @@
3853 // Checks that components swicth to activation mode after (and only after) a long
3854 // press happens on Phone tile.
3855 function checkSwitchToActivationModeAfterLongPress() {
3856- compare(calendarTile.terminationModeEnabled, !isPhoneLongPressed)
3857+ compare(browserTile.terminationModeEnabled, !isPhoneLongPressed)
3858 compare(phoneTile.terminationModeEnabled, !isPhoneLongPressed)
3859 compare(runningApplicationsGrid.terminationModeEnabled, !isPhoneLongPressed)
3860
3861 return isPhoneLongPressed &&
3862- !calendarTile.terminationModeEnabled &&
3863+ !browserTile.terminationModeEnabled &&
3864 !phoneTile.terminationModeEnabled &&
3865 !runningApplicationsGrid.terminationModeEnabled
3866 }
3867@@ -200,17 +133,17 @@
3868 function test_clickTileNotClose() {
3869 runningApplicationsGrid.terminationModeEnabled = true
3870
3871- var calendarTile = findChild(runningApplicationsGrid, "runningAppTile Calendar")
3872- verify(calendarTile != undefined)
3873-
3874- verify(fakeRunningAppsModel.contains("calendar"))
3875-
3876- mouseClick(calendarTile, calendarTile.width/2, calendarTile.height/2)
3877-
3878- verify(fakeRunningAppsModel.contains("calendar"))
3879-
3880- // The tile for the Calendar app should stay there
3881- tryCompareFunction(checkCalendarTileExists, true)
3882+ var browserTile = findChild(runningApplicationsGrid, "runningAppTile Browser")
3883+ verify(browserTile != undefined)
3884+
3885+ verify(ApplicationManager.findApplication("webbrowser-app") !== null)
3886+
3887+ mouseClick(browserTile, browserTile.width/2, browserTile.height/2)
3888+
3889+ verify(ApplicationManager.findApplication("webbrowser-app") !== null)
3890+
3891+ // The tile for the Browser app should stay there
3892+ tryCompareFunction(checkBrowserTileExists, true)
3893 }
3894
3895 // While in termination mode, clicking on a running application tile's close icon
3896@@ -218,25 +151,25 @@
3897 function test_clickCloseIconToTerminateApp() {
3898 runningApplicationsGrid.terminationModeEnabled = true
3899
3900- var calendarTile = findChild(runningApplicationsGrid, "runningAppTile Calendar")
3901- var calendarTileCloseButton = findChild(runningApplicationsGrid, "closeIcon Calendar")
3902-
3903- verify(calendarTile != undefined)
3904- verify(calendarTileCloseButton != undefined)
3905- verify(fakeRunningAppsModel.contains("calendar"))
3906-
3907- mouseClick(calendarTileCloseButton, calendarTileCloseButton.width/2, calendarTileCloseButton.height/2)
3908+ var browserTile = findChild(runningApplicationsGrid, "runningAppTile Browser")
3909+ var browserTileCloseButton = findChild(runningApplicationsGrid, "closeIcon Browser")
3910+
3911+ verify(browserTile != undefined)
3912+ verify(browserTileCloseButton != undefined)
3913+ verify(ApplicationManager.findApplication("webbrowser-app") !== 0)
3914+
3915+ mouseClick(browserTileCloseButton, browserTileCloseButton.width/2, browserTileCloseButton.height/2)
3916 wait(0) // spin event loop to start any pending animation
3917
3918- verify(!fakeRunningAppsModel.contains("calendar"))
3919+ verify(ApplicationManager.findApplication("webbrowser-app") === null)
3920
3921- // The tile for the Calendar app should eventually vanish since the
3922+ // The tile for the Browser app should eventually vanish since the
3923 // application has been terminated.
3924- tryCompareFunction(checkCalendarTileExists, false)
3925+ tryCompareFunction(checkBrowserTileExists, false)
3926 }
3927
3928- function checkCalendarTileExists() {
3929- return findChild(runningApplicationsGrid, "runningAppTile Calendar")
3930+ function checkBrowserTileExists() {
3931+ return findChild(runningApplicationsGrid, "runningAppTile Browser")
3932 != undefined
3933 }
3934
3935@@ -245,8 +178,8 @@
3936 function test_clickOutsideTilesDisablesTerminationMode() {
3937 runningApplicationsGrid.terminationModeEnabled = true
3938
3939- var calendarTile = findChild(runningApplicationsGrid, "runningAppTile Calendar")
3940- verify(calendarTile != undefined)
3941+ var browserTile = findChild(runningApplicationsGrid, "runningAppTile Browser")
3942+ verify(browserTile != undefined)
3943
3944 verify(runningApplicationsGrid.terminationModeEnabled);
3945
3946
3947=== added directory 'tests/qmltests/Stages'
3948=== added file 'tests/qmltests/Stages/tst_PhoneStage.qml'
3949--- tests/qmltests/Stages/tst_PhoneStage.qml 1970-01-01 00:00:00 +0000
3950+++ tests/qmltests/Stages/tst_PhoneStage.qml 2014-03-25 15:08:15 +0000
3951@@ -0,0 +1,288 @@
3952+/*
3953+ * Copyright 2014 Canonical Ltd.
3954+ *
3955+ * This program is free software; you can redistribute it and/or modify
3956+ * it under the terms of the GNU General Public License as published by
3957+ * the Free Software Foundation; version 3.
3958+ *
3959+ * This program is distributed in the hope that it will be useful,
3960+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3961+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3962+ * GNU General Public License for more details.
3963+ *
3964+ * You should have received a copy of the GNU General Public License
3965+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3966+ */
3967+
3968+import QtQuick 2.0
3969+import QtTest 1.0
3970+import Unity.Test 0.1 as UT
3971+import ".."
3972+import "../../../qml/Stages"
3973+import Ubuntu.Components 0.1
3974+import Unity.Application 0.1
3975+
3976+Item {
3977+ width: units.gu(70)
3978+ height: units.gu(70)
3979+
3980+ Rectangle {
3981+
3982+ }
3983+
3984+ PhoneStage {
3985+ id: phoneStage
3986+ anchors { fill: parent; rightMargin: units.gu(30) }
3987+ shown: true
3988+ dragAreaWidth: units.gu(2)
3989+ }
3990+
3991+ Binding {
3992+ target: ApplicationManager
3993+ property: "rightMargin"
3994+ value: phoneStage.anchors.rightMargin
3995+ }
3996+
3997+ Rectangle {
3998+ anchors { fill: parent; leftMargin: phoneStage.width }
3999+// color: "blue"
4000+
4001+ Column {
4002+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
4003+ spacing: units.gu(1)
4004+ Button {
4005+ anchors { left: parent.left; right: parent.right }
4006+ text: "Add App"
4007+ onClicked: {
4008+ testCase.addApps();
4009+ }
4010+ }
4011+ Button {
4012+ anchors { left: parent.left; right: parent.right }
4013+ text: "Add App"
4014+ }
4015+ }
4016+ }
4017+
4018+ UT.UnityTestCase {
4019+ id: testCase
4020+ name: "PhoneStage"
4021+ when: windowShown
4022+
4023+ function addApps(count) {
4024+ if (count == undefined) count = 1;
4025+ for (var i = 0; i < count; i++) {
4026+ var app = ApplicationManager.startApplication(ApplicationManager.availableApplications()[ApplicationManager.count])
4027+ tryCompare(app, "state", ApplicationInfoInterface.Running)
4028+ // Fixme: Right now there is a timeout in the PhoneStage that displays a white splash
4029+ // screen rectangle when an app starts. This is because we don't yet have a way of
4030+ // knowing when an app has finished launching. That workaround and this wait() should
4031+ // go away at some point and the app's state only changing to Running when ready for real.
4032+// wait(1000)
4033+ waitForRendering(phoneStage)
4034+ }
4035+ }
4036+
4037+ function goToSpread() {
4038+ var spreadView = findChild(phoneStage, "spreadView");
4039+
4040+ var startX = phoneStage.width;
4041+ var startY = phoneStage.height / 2;
4042+ var endY = startY;
4043+ var endX = units.gu(2);
4044+
4045+ touchFlick(phoneStage, startX, startY, endX, endY,
4046+ true /* beginTouch */, true /* endTouch */, units.gu(10), 50);
4047+ }
4048+
4049+ function test_shortFlick() {
4050+ addApps(2)
4051+ var startX = phoneStage.width - units.gu(1);
4052+ var startY = phoneStage.height / 2;
4053+ var endX = phoneStage.width / 2;
4054+ var endY = startY;
4055+
4056+ var activeApp = ApplicationManager.get(0);
4057+ var inactiveApp = ApplicationManager.get(1);
4058+
4059+ touchFlick(phoneStage, startX, startY, endX, endY,
4060+ true /* beginTouch */, true /* endTouch */, units.gu(10), 50);
4061+
4062+ tryCompare(ApplicationManager, "focusedApplicationId", inactiveApp.appId)
4063+
4064+ touchFlick(phoneStage, startX, startY, endX, endY,
4065+ true /* beginTouch */, true /* endTouch */, units.gu(10), 50);
4066+
4067+ tryCompare(ApplicationManager, "focusedApplicationId", activeApp.appId)
4068+
4069+ tryCompare(phoneStage, "painting", false);
4070+ }
4071+
4072+ function test_enterSpread_data() {
4073+ return [
4074+ {tag: "<position1 (linear movement)", positionMarker: "positionMarker1", linear: true, offset: -1, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
4075+ {tag: "<position1 (non-linear movement)", positionMarker: "positionMarker1", linear: false, offset: -1, endPhase: 0, targetPhase: 0, newFocusedIndex: 0 },
4076+ {tag: ">position1", positionMarker: "positionMarker1", linear: true, offset: +1, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
4077+ {tag: "<position2 (linear)", positionMarker: "positionMarker2", linear: true, offset: -1, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
4078+ {tag: "<position2 (non-linear)", positionMarker: "positionMarker2", linear: false, offset: -1, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
4079+ {tag: ">position2", positionMarker: "positionMarker2", linear: true, offset: +1, endPhase: 1, targetPhase: 0, newFocusedIndex: 1 },
4080+ {tag: "<position3", positionMarker: "positionMarker3", linear: true, offset: -1, endPhase: 1, targetPhase: 0, newFocusedIndex: 1 },
4081+ {tag: ">position3", positionMarker: "positionMarker3", linear: true, offset: +1, endPhase: 2, targetPhase: 2, newFocusedIndex: 2 },
4082+ ];
4083+ }
4084+
4085+ function test_enterSpread(data) {
4086+ addApps(5)
4087+
4088+ var spreadView = findChild(phoneStage, "spreadView");
4089+
4090+ var startX = phoneStage.width;
4091+ var startY = phoneStage.height / 2;
4092+ var endY = startY;
4093+ var endX = spreadView.width - (spreadView.width * spreadView[data.positionMarker]) - data.offset - phoneStage.dragAreaWidth;
4094+
4095+ var oldFocusedApp = ApplicationManager.get(0);
4096+ var newFocusedApp = ApplicationManager.get(data.newFocusedIndex);
4097+
4098+ touchFlick(phoneStage, startX, startY, endX, endY,
4099+ true /* beginTouch */, false /* endTouch */, units.gu(10), 50);
4100+
4101+ tryCompare(spreadView, "phase", data.endPhase)
4102+
4103+ if (!data.linear) {
4104+ touchFlick(phoneStage, endX, endY, endX + units.gu(.5), endY,
4105+ false /* beginTouch */, false /* endTouch */, units.gu(10), 50);
4106+ touchFlick(phoneStage, endY + units.gu(.5), endY, endX, endY,
4107+ false /* beginTouch */, false /* endTouch */, units.gu(10), 50);
4108+ }
4109+
4110+ touchRelease(phoneStage, endX, endY);
4111+
4112+ tryCompare(spreadView, "phase", data.targetPhase)
4113+
4114+ if (data.targetPhase == 2) {
4115+ var app2 = findChild(spreadView, "appDelegate2");
4116+ mouseClick(app2, units.gu(1), units.gu(1));
4117+ }
4118+
4119+ tryCompare(phoneStage, "painting", false);
4120+ tryCompare(ApplicationManager, "focusedApplicationId", newFocusedApp.appId);
4121+ }
4122+
4123+ function test_selectAppFromSpread_data() {
4124+ var appsToTest = 6;
4125+ var apps = new Array();
4126+ for (var i = 0; i < appsToTest; i++) {
4127+ var item = new Object();
4128+ item.tag = "App " + i;
4129+ item.index = i;
4130+ item.total = appsToTest;
4131+ apps.push(item)
4132+ }
4133+ return apps;
4134+ }
4135+
4136+ function test_selectAppFromSpread(data) {
4137+ addApps(data.total)
4138+
4139+ var spreadView = findChild(phoneStage, "spreadView");
4140+
4141+ goToSpread();
4142+
4143+ tryCompare(spreadView, "phase", 2);
4144+
4145+ var tile = findChild(spreadView, "appDelegate" + data.index);
4146+ var appId = ApplicationManager.get(data.index).appId;
4147+
4148+ if (tile.mapToItem(spreadView).x > spreadView.width) {
4149+ // Item is not visible... Need to flick the spread
4150+ var startX = phoneStage.width - units.gu(1);
4151+ var startY = phoneStage.height / 2;
4152+ var endY = startY;
4153+ var endX = units.gu(2);
4154+ touchFlick(phoneStage, startX, startY, endX, endY, true, true, units.gu(10), 50)
4155+ tryCompare(spreadView, "flicking", false);
4156+ tryCompare(spreadView, "moving", false);
4157+// waitForRendering(phoneStage);
4158+ }
4159+
4160+ console.log("clicking app", data.index, "(", appId, ")")
4161+ mouseClick(spreadView, tile.mapToItem(spreadView).x + units.gu(1), spreadView.height / 2)
4162+ tryCompare(ApplicationManager, "focusedApplicationId", appId);
4163+ tryCompare(spreadView, "phase", 0);
4164+ }
4165+
4166+ function test_animateAppStartup() {
4167+ compare(phoneStage.painting, false);
4168+ addApps(2);
4169+ tryCompare(phoneStage, "painting", true);
4170+ tryCompare(phoneStage, "painting", false);
4171+ addApps(1);
4172+ tryCompare(phoneStage, "painting", true);
4173+ tryCompare(phoneStage, "painting", false);
4174+ }
4175+
4176+ function test_select_data() {
4177+ return [
4178+ { tag: "0", index: 0 },
4179+ { tag: "2", index: 2 },
4180+ { tag: "4", index: 4 },
4181+ ]
4182+ }
4183+
4184+ function test_select(data) {
4185+ addApps(5);
4186+
4187+ var spreadView = findChild(phoneStage, "spreadView");
4188+ var selectedApp = ApplicationManager.get(data.index);
4189+
4190+ goToSpread();
4191+
4192+ phoneStage.select(selectedApp.appId);
4193+
4194+ tryCompare(phoneStage, "painting", false);
4195+ compare(ApplicationManager.focusedApplicationId, selectedApp.appId);
4196+ }
4197+
4198+ function test_fullscreenMode() {
4199+ var fullscreenApp = null;
4200+ var normalApp = null;
4201+
4202+ for (var i = 0; i < 5; i++) {
4203+ addApps(1);
4204+ var newApp = ApplicationManager.get(0);
4205+ tryCompare(phoneStage, "fullscreen", newApp.fullscreen);
4206+ if (newApp.fullscreen && fullscreenApp == null) {
4207+ fullscreenApp = newApp;
4208+ } else if (!newApp.fullscreen && normalApp == null){
4209+ normalApp = newApp;
4210+ }
4211+ }
4212+ verify(fullscreenApp != null); // Can't continue the test without having a fullscreen app
4213+ verify(normalApp != null); // Can't continue the test without having a non-fullscreen app
4214+
4215+ // Select a normal app
4216+ goToSpread();
4217+ phoneStage.select(normalApp.appId);
4218+ tryCompare(phoneStage, "fullscreen", false);
4219+
4220+ // Select a fullscreen app
4221+ goToSpread();
4222+ phoneStage.select(fullscreenApp.appId);
4223+ tryCompare(phoneStage, "fullscreen", true);
4224+
4225+ // Select a normal app
4226+ goToSpread();
4227+ phoneStage.select(normalApp.appId);
4228+ tryCompare(phoneStage, "fullscreen", false);
4229+ }
4230+
4231+ function cleanup() {
4232+ while (ApplicationManager.count > 0) {
4233+ var oldCount = ApplicationManager.count;
4234+ ApplicationManager.stopApplication(ApplicationManager.get(0).appId)
4235+ tryCompare(ApplicationManager, "count", oldCount - 1)
4236+ }
4237+ }
4238+ }
4239+}
4240
4241=== modified file 'tests/qmltests/tst_Shell.qml'
4242--- tests/qmltests/tst_Shell.qml 2014-03-19 10:48:06 +0000
4243+++ tests/qmltests/tst_Shell.qml 2014-03-25 15:08:15 +0000
4244@@ -117,6 +117,7 @@
4245 while (apps.count > 0) {
4246 ApplicationManager.stopApplication(apps.get(0).appId);
4247 }
4248+ compare(ApplicationManager.count, 0)
4249 }
4250
4251 /*
4252@@ -187,26 +188,34 @@
4253 tapOnAppIconInLauncher();
4254 waitUntilApplicationWindowIsFullyVisible();
4255
4256- var mainApp = ApplicationManager.focusedApplicationId;
4257- verify(mainApp != "");
4258+ var mainAppId = ApplicationManager.focusedApplicationId;
4259+ verify(mainAppId != "");
4260+ var mainApp = ApplicationManager.findApplication(mainAppId);
4261+ verify(mainApp);
4262+ tryCompare(mainApp.state, ApplicationInfo.Running);
4263
4264 // Try to suspend while proximity is engaged...
4265 Powerd.displayPowerStateChange(Powerd.Off, Powerd.UseProximity);
4266 tryCompare(greeter, "showProgress", 0);
4267
4268 // Now really suspend
4269+ print("suspending")
4270 Powerd.displayPowerStateChange(Powerd.Off, 0);
4271+ print("done suspending")
4272 tryCompare(greeter, "showProgress", 1);
4273- tryCompare(ApplicationManager, "focusedApplicationId", "");
4274+
4275+ tryCompare(ApplicationManager, "suspended", true);
4276+ compare(mainApp.state, ApplicationInfo.Suspended);
4277
4278 // And wake up
4279 Powerd.displayPowerStateChange(Powerd.On, 0);
4280- tryCompare(ApplicationManager, "focusedApplicationId", "");
4281 tryCompare(greeter, "showProgress", 1);
4282
4283 // Swipe away greeter to focus app
4284 swipeAwayGreeter();
4285- tryCompare(ApplicationManager, "focusedApplicationId", mainApp);
4286+ tryCompare(ApplicationManager, "suspended", false);
4287+ compare(mainApp.state, ApplicationInfo.Running);
4288+ tryCompare(ApplicationManager, "focusedApplicationId", mainAppId);
4289 }
4290
4291 function swipeAwayGreeter() {
4292@@ -239,7 +248,7 @@
4293 tryCompare(dash, "opacity", 1.0);
4294
4295 touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY,
4296- true /* beginTouch */, false /* endTouch */);
4297+ true /* beginTouch */, false /* endTouch */, units.gu(10), 50);
4298
4299 // check that Dash has been scaled down and had its opacity reduced
4300 tryCompareFunction(function() { return dash.contentScale <= 0.9; }, true);
4301@@ -270,7 +279,7 @@
4302 tryCompare(dash, "opacity", 1.0);
4303
4304 touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY,
4305- true /* beginTouch */, false /* endTouch */);
4306+ true /* beginTouch */, false /* endTouch */, units.gu(10), 50);
4307
4308 // check that Dash has been scaled down and had its opacity reduced
4309 tryCompareFunction(function() { return dash.contentScale <= 0.9; }, true);
4310@@ -486,7 +495,6 @@
4311 }
4312
4313 function test_DashShown(data) {
4314-
4315 if (data.greeter) {
4316 // Swipe the greeter in
4317 var greeter = findChild(shell, "greeter");

Subscribers

People subscribed via source and target branches