Merge lp:~unity-team/unity8/mirCompositor into lp:unity8

Proposed by kevin gunn
Status: Merged
Approved by: Gerry Boland
Approved revision: 1104
Merged at revision: 1088
Proposed branch: lp:~unity-team/unity8/mirCompositor
Merge into: lp:unity8
Diff against target: 6483 lines (+3413/-1522)
52 files modified
debian/changelog (+14/-0)
debian/control (+1/-2)
plugins/Utils/CMakeLists.txt (+1/-0)
plugins/Utils/easingcurve.cpp (+5/-1)
plugins/Utils/plugin.cpp (+2/-0)
plugins/Utils/windowkeysfilter.cpp (+57/-0)
plugins/Utils/windowkeysfilter.h (+51/-0)
qml/Components/Dialogs.qml (+217/-0)
qml/Components/DraggingArea.qml (+4/-0)
qml/Components/InputMethod.qml (+79/-0)
qml/Components/PageHeader.qml (+3/-1)
qml/Dash/Apps/RunningApplicationTile.qml (+45/-37)
qml/Dash/DashContent.qml (+4/-0)
qml/Launcher/Launcher.qml (+1/-1)
qml/Panel/Indicators.qml (+3/-0)
qml/Shell.qml (+171/-393)
qml/Stages/Animations/BaseSurfaceAnimation.qml (+93/-0)
qml/Stages/Animations/DarkenAndFadeInAnimation.qml (+48/-0)
qml/Stages/Animations/SwipeFromBottomAnimation.qml (+48/-0)
qml/Stages/Animations/SwipeUpAnimation.qml (+49/-0)
qml/Stages/PhoneStage.qml (+173/-265)
qml/Stages/SidestageHandle.qml (+0/-22)
qml/Stages/Splash.qml (+68/-0)
qml/Stages/SpreadDelegate.qml (+193/-29)
qml/Stages/StageWithSideStage.qml (+0/-413)
qml/Stages/SurfaceContainer.qml (+123/-0)
qml/Stages/SwitchingApplicationImage.qml (+0/-81)
qml/Stages/TabletStage.qml (+568/-0)
qml/Stages/TransformedSpreadDelegate.qml (+36/-30)
qml/Stages/TransformedTabletSpreadDelegate.qml (+369/-0)
src/CMakeLists.txt (+0/-3)
src/main.cpp (+14/-89)
tests/mocks/Unity/Application/Application.qmltypes (+0/-2)
tests/mocks/Unity/Application/ApplicationDBusAdaptor.cpp (+75/-0)
tests/mocks/Unity/Application/ApplicationDBusAdaptor.h (+43/-0)
tests/mocks/Unity/Application/ApplicationInfo.cpp (+71/-23)
tests/mocks/Unity/Application/ApplicationInfo.h (+22/-9)
tests/mocks/Unity/Application/ApplicationManager.cpp (+60/-31)
tests/mocks/Unity/Application/ApplicationManager.h (+15/-9)
tests/mocks/Unity/Application/CMakeLists.txt (+6/-2)
tests/mocks/Unity/Application/MirSurfaceItem.cpp (+172/-0)
tests/mocks/Unity/Application/MirSurfaceItem.h (+121/-0)
tests/mocks/Unity/Application/SurfaceManager.cpp (+73/-0)
tests/mocks/Unity/Application/SurfaceManager.h (+54/-0)
tests/mocks/Unity/Application/VirtualKeyboard.cpp (+55/-0)
tests/mocks/Unity/Application/VirtualKeyboard.h (+35/-0)
tests/mocks/Unity/Application/plugin.cpp (+16/-9)
tests/qmltests/Dash/Apps/tst_RunningApplicationsGrid.qml (+23/-2)
tests/qmltests/Launcher/tst_Launcher.qml (+11/-2)
tests/qmltests/Panel/tst_ActiveCallHint.qml (+1/-0)
tests/qmltests/Stages/tst_PhoneStage.qml (+9/-56)
tests/qmltests/tst_Shell.qml (+111/-10)
To merge this branch: bzr merge lp:~unity-team/unity8/mirCompositor
Reviewer Review Type Date Requested Status
Gerry Boland (community) Approve
PS Jenkins bot (community) continuous-integration Needs Fixing
Michael Zanetti (community) c++ code review Approve
Michael Terry debian-packaging Approve
Vesa Rautiainen (community) design Approve
Review via email: mp+225378@code.launchpad.net

Commit message

changes for Qtcompositor as a plugin to mir

Description of the change

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

https://code.launchpad.net/~unity-team/platform-api/devel-for-qtmircompositor/+merge/225320
https://code.launchpad.net/~mir-team/qtubuntu/ubuntumirclient-only/+merge/224978
https://code.launchpad.net/~unity-team/ubuntu-system-settings/use-qtComp/+merge/225540
https://code.launchpad.net/~gerboland/unity8-desktop-session/qtcomp/+merge/225884

Also requires lp:qtmir and lp:~gerboland/+junk/ubuntu-touch-meta_qtcomp

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

tons

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

yes

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

yep

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

requested here

To post a comment you must log in.
Revision history for this message
Vesa Rautiainen (vesar) wrote :

Design-approved

review: Approve (design)
Revision history for this message
Michael Terry (mterry) wrote :

I only looked at the debian/* changes, but they look fine provided:
- debian/changelog conflict is fixed
- qtmir actually lands in utopic

Revision history for this message
Michael Terry (mterry) :
review: Approve (debian-packaging)
Revision history for this message
Gerry Boland (gerboland) wrote :

Ok, I opened this up for review. The process is as follows:
 - Gerry reviews the Tablet/Phone stages and anything else Michael Zanetti played with
- Michael Zanetti reviews the remainder (perf tweaks Gerry applied + src/main.cpp)
- we have packaging +1, which is great

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
Michael Zanetti (mzanetti) wrote :

Changes in main.cpp look good to me!

review: Approve (c++ code review)
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
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
Michael Zanetti (mzanetti) wrote :

Revoking my approval until I've reviewed that whole lot of C++ code that appeared over night.

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

Reviewed new KeyHandler code. Looks good.

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

> Reviewed new KeyHandler code. Looks good.

To be clear, I don't think this is the final thing we need in the end (once we need Keyboard navigation through the shell etc). I'm afraid we have to go back to the "focus mayhem" at that point. However, we also lack policy stuff for handling system keys (like Volume Up/Down) which prevents us to put in place the proper solution just yet, so I think this is a fine solution for now.

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 :

=== modified file 'qml/Components/Lockscreen.qml'
- property url background: ""
+ property url background: shell.background
Breaks encapsulation

=== modified file 'qml/Shell.qml'
- background: shell.background
+ //background: shell.background // FIXME: Commenting out as Qt was complaining about this line
I'm not sure breaking encapsulation is better than fixing a warning.

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

=== modified file 'qml/Dash/Apps/RunningApplicationTile.qml'
+ x: 0
+ y: 0
that's the default anyway.

+ property bool isLandscape: sourceSize.width > sourceSize.height
+ property real maxDimension: units.gu(17)
Both could be readonly.

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

=== modified file 'qml/Shell.qml'
-FocusScope {
+Item {
I'd rather we didn't do this. We're not really doing focus management properly, but IMO there's no reason to remove this focusscope.

+ WindowKeysFilter {
Do we have a policy for placing non-UI chunks of QML under the visual ones? Just curious

+ return stagesDragArea.progress
semicolon (just because I see the previous lines in the diff add one)

+ // It might technically not be fullyShown but visually it just looks so.
+ property bool roughlyFullyShown: x >= 0 && x <= units.gu(1)
The purpose if this is a mystery to me. Why is fullyShown not the correct thing to use to decide that the spread is interactive? Can we not do something more obvious?

+ property bool topmostApplicationIsFullscreen:
readonly?

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

=== modified file 'qml/Stages/PhoneStage.qml'
+ property bool locked: spreadView.phase == 2
readonly?

+ spreadView.contentX = -spreadView.shift
+ priv.switchToApp(appId)
nit-pick - semicolons?

+ if (behavioredIndex == 1 ....
are these floating point comparisons safe?

+ // Limiting progress to ~0 and 1.7
1.7 a bit magic, is that roughly 1 + 0.7 = width of the transformed tile on the left?

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

=== modified file 'qml/Stages/SpreadDelegate.qml'
+ // FIXME: I would rather not need to do this, but currently it doesn't get
+ // active focus without it and I don't know why.
+ onFocusChanged: forceSurfaceActiveFocusIfReady();
+ onParentChanged: forceSurfaceActiveFocusIfReady();
+ onEnabledChanged: forceSurfaceActiveFocusIfReady();
+ function forceSurfaceActiveFocusIfReady() {
+ if (surface.focus && surface.parent === surfaceContainer && surface.enabled) {
+ surface.forceActiveFocus();
+ }
+ }
I'm *really* not a fan of this, but as it is a FIXME, and as long as it behaves correctly we'll live with it for now.

+ border { left: 50; right: 50; top: 50; bottom: 50 }
No GU?

+ if ((dragVelocity < -600 && distance < -units.gu(8))
more magic numbers? Are they phone shell specific? Since we use GU for distances, doesn't that mean we need to scale velocities against GU too?
Probably a personal thing, but I do flick apps closed by accident. Can be a later bugfix though.

+ animation.from = dragArea.distance
+ animation.to = 0
+ root.closed()
semicolons or no?

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

> === modified file 'qml/Shell.qml'
> -FocusScope {
> +Item {
> I'd rather we didn't do this. We're not really doing focus management
> properly, but IMO there's no reason to remove this focusscope.

The reason is that there's absolutely no use for this FocusScope there. Getting rid of unused stuff is always a good thing.

> + // It might technically not be fullyShown but visually it just looks so.
> + property bool roughlyFullyShown: x >= 0 && x <= units.gu(1)
> The purpose if this is a mystery to me. Why is fullyShown not the correct
> thing to use to decide that the spread is interactive? Can we not do something
> more obvious?

To avoid fidgety behavior when the stage moves only a pixel or so: imperceptible to the human eye so from the user's POV it's still fully shown be we would be already unfocusing the app etc.

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

=== added file 'qml/Stages/TabletStage.qml'
+ objectName: "stages"
that a good name? Also PhoneStage has no objectName set at all, should we add one?

"overlay" = sidestage effectively, right? Just the term threw me for a bit. Rename isn't needed, but perhaps a comment saying so would be nice.

"invalid" state is when no app is open, so spread should not be shown? That really an "invalid" state, or just empty?

+ property int phase
You were kind enough to add a nice comment in PhoneStage explaining the different stages, could you do the same here please?

+ property bool locked: spreadView.phase == 2
+ property string focusedAppId: ApplicationManager.focusedApplicationId
readonly? there's a bunch of properties in the Flickable that could also be readonly.

+ // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation.
+ snapAnimation.targetContentX = spreadView.width * spreadView.positionMarker4 + 1;
Math.ceil?

"ApplicationInfoInterface.MainStage" <- bit of a mouthful, let's try to shorten that in future :)

+ function indexToZIndex(index) {
a few more comments in here would help, I'm struggling to figure it out.

+ anchors.leftMargin: spreadView.width - spreadView.sideStageWidth + spreadView.sideStageWidth * sideStageDragHandle.progress
can compact the maths a tiny bit

+ } else if (progress < spreadView.positionMarker1 + snappingCurve.period){
nitpick: space before opening brace

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

=== modified file 'qml/Stages/TransformedSpreadDelegate.qml'
+ visible: progress >= 0 && progress < 1.7
that magic 1.7 again. Quick comment would be enough probably.

+ var animatedEndDistance = linearAnimation(0, 2, root.endDistance, 0, root.progress)
semicolon

=== added file 'qml/Stages/TransformedTabletSpreadDelegate.qml'
some more readonly properties...

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 :

> === modified file 'qml/Stages/PhoneStage.qml'
> + property bool locked: spreadView.phase == 2
> readonly?

fixed.

> + spreadView.contentX = -spreadView.shift
> + priv.switchToApp(appId)
> nit-pick - semicolons?

fixed

> + if (behavioredIndex == 1 ....
> are these floating point comparisons safe?

Haven't seen any troubles, also this is straight bound to an integer property, just added a Behavior. No floating point calculations involved. I don't think its worth adding some additional fuzziness checks but if you want me to, I can do.

>
> + // Limiting progress to ~0 and 1.7
> 1.7 a bit magic, is that roughly 1 + 0.7 = width of the transformed tile on
> the left?

I tried to improve the comment a bit. Hope its more clear now.

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

> === modified file 'qml/Stages/SpreadDelegate.qml'
> + border { left: 50; right: 50; top: 50; bottom: 50 }
> No GU?

in this case its pixels in the source image, so no.

>
> + if ((dragVelocity < -600 && distance < -units.gu(8))
> more magic numbers? Are they phone shell specific? Since we use GU for
> distances, doesn't that mean we need to scale velocities against GU too?
> Probably a personal thing, but I do flick apps closed by accident. Can be a
> later bugfix though.

Magic numbers dictated by the design prototype :) Comment added. But you're right, velocity should be given in gu indeed. I've fixed that.

>
> + animation.from = dragArea.distance
> + animation.to = 0
> + root.closed()
> semicolons or no?

yep, added

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

=== added file 'qml/Stages/TransformedTabletSpreadDelegate.qml'
+ property bool otherSelected: false
something selected on the other stage, is it? So both need to animate forward? Quick comment would help.

+ // Required to be an item because we're using states on it.
I think you can use a StateGroup inside a QtObject.

nextInStack, movedActive and animatedEndDistance could be readonly. XTranslate, scale, angle, opacityTransform and topMarginProgress too

+ var shouldMoveAway = spreadView.nextInStack >= 0 && movedActive && (ApplicationManager.get(spreadView.nextInStack).stage === ApplicationInfoInterface.MainStage || model.stage == ApplicationInfoInterface.SideStage);
line too long

Rest of the file looks ok, the math is quite nontrivial but it looks sane. So mainly small issues!

review: Needs Fixing
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 :

> === modified file 'qml/Components/Lockscreen.qml'
> - property url background: ""
> + property url background: shell.background
> Breaks encapsulation
>
> === modified file 'qml/Shell.qml'
> - background: shell.background
> + //background: shell.background // FIXME: Commenting out as Qt was
> complaining about this line
> I'm not sure breaking encapsulation is better than fixing a warning.

I wasn't just a warning. Shell.qml was failing to load because of it. Thankfully the QML engine bug that caused it must have been fixed in the meantime (likely the one that Albert Alstals fixed) and restoring that line no longer causes any problem.

Fixed.

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 :

> > === modified file 'qml/Shell.qml'
> > -FocusScope {
> > +Item {
> > I'd rather we didn't do this. We're not really doing focus management
> > properly, but IMO there's no reason to remove this focusscope.
>
> The reason is that there's absolutely no use for this FocusScope there.
> Getting rid of unused stuff is always a good thing.
>
>
> > + // It might technically not be fullyShown but visually it just looks so.
> > + property bool roughlyFullyShown: x >= 0 && x <= units.gu(1)
> > The purpose if this is a mystery to me. Why is fullyShown not the correct
> > thing to use to decide that the spread is interactive? Can we not do
> something
> > more obvious?
>
> To avoid fidgety behavior when the stage moves only a pixel or so:
> imperceptible to the human eye so from the user's POV it's still fully shown
> be we would be already unfocusing the app etc.

Actual example:
1- launch Gallery
2- lay finger on the left edge of the screen

Without roughlyFullyShown:
Indicators bar comes in immediately. Might even jump back and forth.

With roughlyFullyShown:
Nothing happens

The cause of it is that when you lay your finger there it causes stage to move a pixel or so (or maybe even less then a pixel), which makes fullyShown=false, and that's obviously not the actual situation.

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
Michael Zanetti (mzanetti) wrote :

> === added file 'qml/Stages/TabletStage.qml'
> + objectName: "stages"
> that a good name? Also PhoneStage has no objectName set at all, should we add
> one?

Well, there's tests for PhoneStage but not TabletStage yet. I'll add them when the split mode will be implemented.

>
> "overlay" = sidestage effectively, right? Just the term threw me for a bit.
> Rename isn't needed, but perhaps a comment saying so would be nice.

It is sidestage in overlay mode. Parts of the code are prepared for split mode already. I've added some more comments.

>
> "invalid" state is when no app is open, so spread should not be shown? That
> really an "invalid" state, or just empty?

fair enough. renamed.

>
>
> + property int phase
> You were kind enough to add a nice comment in PhoneStage explaining the
> different stages, could you do the same here please?

done

>
> + property bool locked: spreadView.phase == 2
> + property string focusedAppId: ApplicationManager.focusedApplicationId
> readonly? there's a bunch of properties in the Flickable that could also be
> readonly.

made some more readonly.

>
> + // Add 1 pixel to make sure we definitely hit positionMarker4 even with
> rounding errors of the animation.
> + snapAnimation.targetContentX = spreadView.width * spreadView.positionMarker4
> + 1;
> Math.ceil?

I really want it to step over the marker, not just reach it.

>
> "ApplicationInfoInterface.MainStage" <- bit of a mouthful, let's try to
> shorten that in future :)

Better than guessing what AII.MS might be, no? The "Interface" is a bit odd in deed" but well... That's how our architecture works.

>
> + function indexToZIndex(index) {
> a few more comments in here would help, I'm struggling to figure it out.

done

>
> + anchors.leftMargin: spreadView.width - spreadView.sideStageWidth +
> spreadView.sideStageWidth * sideStageDragHandle.progress
> can compact the maths a tiny bit

I tried for a bunch of hours to boot my N10. No chance, not even with the released images. Don't want to change that blindly without testing it... I'll try to do so when the N10 image comes back.

>
> + } else if (progress < spreadView.positionMarker1 + snappingCurve.period){
> nitpick: space before opening brace

done.

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

> === modified file 'qml/Stages/TransformedSpreadDelegate.qml'
> + visible: progress >= 0 && progress < 1.7
> that magic 1.7 again. Quick comment would be enough probably.

Well there is a comment saying that this the min/max values of progress... Should I duplicate the entire "how I got to those numbers" text?

>
> + var animatedEndDistance = linearAnimation(0, 2, root.endDistance, 0,
> root.progress)
> semicolon

added

>
> === added file 'qml/Stages/TransformedTabletSpreadDelegate.qml'
> some more readonly properties...

I don't really agree with this. There could be some of those made readonly, not all. But tbh seems to cause more mess than anything else. Again this is not public API...

The thing with readonly is that you also can't apply any Behavior or state changes on that property any more... Having "angle" readonly, while keeping xTranslate read-write etc doesn't seem like doing any good to me.

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

> + anchors.leftMargin: spreadView.width - spreadView.sideStageWidth +
> spreadView.sideStageWidth * sideStageDragHandle.progress
> can compact the maths a tiny bit

Thanks to dednick's work on the FakeAppManager I could test this on the Desktop again. I compressed the math a bit, although I'm not sure if it makes it more clear what its doing... Sometimes I intentionally "uncompress" some maths in order to make it more readable....

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

> === added file 'qml/Stages/TransformedTabletSpreadDelegate.qml'
> + property bool otherSelected: false
> something selected on the other stage, is it? So both need to animate forward?
> Quick comment would help.

done.

> + // Required to be an item because we're using states on it.
> I think you can use a StateGroup inside a QtObject.

Tried it, doesn't seem to work...

> nextInStack, movedActive and animatedEndDistance could be readonly.
> XTranslate, scale, angle, opacityTransform and topMarginProgress too

nope. The state wants write access to it. See previous comment on readonly props.

>
> + var shouldMoveAway = spreadView.nextInStack >= 0 && movedActive &&
> (ApplicationManager.get(spreadView.nextInStack).stage ===
> ApplicationInfoInterface.MainStage || model.stage ==
> ApplicationInfoInterface.SideStage);
> line too long

done

> Rest of the file looks ok, the math is quite nontrivial but it looks sane. So
> mainly small issues!

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 :

Thanks for those changes. I've nothing more to add. It works beautifully, thank you!

review: Approve
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
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
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
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
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
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
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
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
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
Gerry Boland (gerboland) wrote :

One final fix, approved

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

Yet another final fix

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/changelog'
2--- debian/changelog 2014-07-25 10:47:35 +0000
3+++ debian/changelog 2014-07-29 14:07:22 +0000
4@@ -1,3 +1,17 @@
5+unity8 (8.00) UNRELEASED; urgency=medium
6+
7+ [ Gerry Boland ]
8+ [ Daniel d'Andrada ]
9+ * Re-architecture unity8 to use the QtMirCompositor library so that
10+ the Qt scenegraph renderer is used as the Mir compositor, and
11+ application surfaces are added to the QML scene as items.
12+
13+ [ Michael Zanetti ]
14+ * Port phone right-edge spread code to use QtCompositor
15+ * Add right-edge spread animation for tablet
16+
17+ -- Gerry Boland <gerry.boland@canonical.com> Thu, 10 Jul 2014 10:34:18 +0100
18+
19 unity8 (7.90+14.10.20140725-0ubuntu1) utopic; urgency=low
20
21 [ CI bot ]
22
23=== modified file 'debian/control'
24--- debian/control 2014-07-24 20:40:44 +0000
25+++ debian/control 2014-07-29 14:07:22 +0000
26@@ -24,7 +24,6 @@
27 libqmenumodel-dev (>= 0.2.8),
28 libqt5xmlpatterns5-dev,
29 libunity-api-dev (>= 7.85),
30- libunity-mir-dev,
31 libusermetricsoutput1-dev,
32 libxcb1-dev,
33 pkg-config,
34@@ -80,10 +79,10 @@
35 Depends: gsettings-desktop-schemas,
36 libcap2-bin,
37 libglib2.0-bin,
38- libunity-mir1 (>= 0.4),
39 qmenumodel-qml (>= 0.2.8),
40 qml-module-qtquick-xmllistmodel,
41 qtdeclarative5-gsettings1.0,
42+ qtdeclarative5-qtmir-plugin (>= 0.4),
43 qtdeclarative5-ubuntu-settings-components (>= 0.3),
44 qtdeclarative5-ubuntu-telephony0.1,
45 unity-launcher-impl-3,
46
47=== modified file 'plugins/Utils/CMakeLists.txt'
48--- plugins/Utils/CMakeLists.txt 2014-06-23 16:20:51 +0000
49+++ plugins/Utils/CMakeLists.txt 2014-07-29 14:07:22 +0000
50@@ -15,6 +15,7 @@
51 qsortfilterproxymodelqml.cpp
52 timeformatter.cpp
53 unitymenumodelpaths.cpp
54+ windowkeysfilter.cpp
55 easingcurve.cpp
56 plugin.cpp
57 )
58
59=== modified file 'plugins/Utils/easingcurve.cpp'
60--- plugins/Utils/easingcurve.cpp 2014-06-30 16:02:05 +0000
61+++ plugins/Utils/easingcurve.cpp 2014-07-29 14:07:22 +0000
62@@ -34,7 +34,11 @@
63
64 void EasingCurve::setType(const QEasingCurve::Type &type)
65 {
66- m_easingCurve.setType(type);
67+ // FIXME: Working around bug https://bugreports.qt-project.org/browse/QTBUG-38686 here
68+ QEasingCurve newCurve;
69+ newCurve.setType(type);
70+ newCurve.setPeriod(m_easingCurve.period());
71+ m_easingCurve = newCurve;
72 Q_EMIT typeChanged();
73 }
74
75
76=== modified file 'plugins/Utils/plugin.cpp'
77--- plugins/Utils/plugin.cpp 2014-06-23 16:20:51 +0000
78+++ plugins/Utils/plugin.cpp 2014-07-29 14:07:22 +0000
79@@ -30,6 +30,7 @@
80 #include "qsortfilterproxymodelqml.h"
81 #include "timeformatter.h"
82 #include "unitymenumodelpaths.h"
83+#include "windowkeysfilter.h"
84 #include "easingcurve.h"
85
86 void UtilsPlugin::registerTypes(const char *uri)
87@@ -40,6 +41,7 @@
88 qmlRegisterType<QSortFilterProxyModelQML>(uri, 0, 1, "SortFilterProxyModel");
89 qmlRegisterType<UnityMenuModelPaths>(uri, 0, 1, "UnityMenuModelPaths");
90 qmlRegisterType<TimeFormatter>(uri, 0, 1, "TimeFormatter");
91+ qmlRegisterType<WindowKeysFilter>(uri, 0, 1, "WindowKeysFilter");
92 qmlRegisterType<GDateTimeFormatter>(uri, 0, 1, "GDateTimeFormatter");
93 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
94 }
95
96=== added file 'plugins/Utils/windowkeysfilter.cpp'
97--- plugins/Utils/windowkeysfilter.cpp 1970-01-01 00:00:00 +0000
98+++ plugins/Utils/windowkeysfilter.cpp 2014-07-29 14:07:22 +0000
99@@ -0,0 +1,57 @@
100+/*
101+ * Copyright (C) 2014 Canonical, Ltd.
102+ *
103+ * This program is free software; you can redistribute it and/or modify
104+ * it under the terms of the GNU General Public License as published by
105+ * the Free Software Foundation; version 3.
106+ *
107+ * This program is distributed in the hope that it will be useful,
108+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
109+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
110+ * GNU General Public License for more details.
111+ *
112+ * You should have received a copy of the GNU General Public License
113+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
114+ *
115+ * Author: Daniel d'Andrada <daniel.dandrada@canonical.com>
116+ */
117+
118+#include "windowkeysfilter.h"
119+
120+#include <QQuickWindow>
121+
122+WindowKeysFilter::WindowKeysFilter(QQuickItem *parent)
123+ : QQuickItem(parent)
124+{
125+ connect(this, &QQuickItem::windowChanged,
126+ this, &WindowKeysFilter::setupFilterOnWindow);
127+}
128+
129+bool WindowKeysFilter::eventFilter(QObject *watched, QEvent *event)
130+{
131+ Q_ASSERT(!m_filteredWindow.isNull());
132+ Q_ASSERT(watched == static_cast<QObject*>(m_filteredWindow.data()));
133+
134+ if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
135+ // Let QML see this event and decide if it does not want it
136+ event->accept();
137+ QCoreApplication::sendEvent(this, event);
138+ return event->isAccepted();
139+ } else {
140+ // Not interested
141+ return false;
142+ }
143+}
144+
145+void WindowKeysFilter::setupFilterOnWindow(QQuickWindow *window)
146+{
147+ if (!m_filteredWindow.isNull()) {
148+ m_filteredWindow->removeEventFilter(this);
149+ m_filteredWindow.clear();
150+ }
151+
152+ if (window) {
153+ window->installEventFilter(this);
154+ m_filteredWindow = window;
155+ }
156+}
157
158=== added file 'plugins/Utils/windowkeysfilter.h'
159--- plugins/Utils/windowkeysfilter.h 1970-01-01 00:00:00 +0000
160+++ plugins/Utils/windowkeysfilter.h 2014-07-29 14:07:22 +0000
161@@ -0,0 +1,51 @@
162+/*
163+ * Copyright (C) 2014 Canonical, Ltd.
164+ *
165+ * This program is free software; you can redistribute it and/or modify
166+ * it under the terms of the GNU General Public License as published by
167+ * the Free Software Foundation; version 3.
168+ *
169+ * This program is distributed in the hope that it will be useful,
170+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
171+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
172+ * GNU General Public License for more details.
173+ *
174+ * You should have received a copy of the GNU General Public License
175+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
176+ *
177+ * Author: Daniel d'Andrada <daniel.dandrada@canonical.com>
178+ */
179+
180+#ifndef UNITY_WINDOWKEYSFILTER_H
181+#define UNITY_WINDOWKEYSFILTER_H
182+
183+#include <QQuickItem>
184+#include <QPointer>
185+
186+/*
187+ Receives all key events that arrive in the QQuickWindow where this item is placed.
188+
189+ Rejected key events will be allowed to be processed normally by the QQuickWindow whereas
190+ accepted ones will be filtered out. Events are accepted by default, so make sure you reject
191+ the keys you're not interested in.
192+
193+ If more than one WindowKeysFilter exist in the same QML scene (and thus in the same QQuickWindow)
194+ they will be called in the order of creation, which can be tricky to assess. So the best practice
195+ is to have at most one WindowKeysFilter per QML scene.
196+ */
197+class WindowKeysFilter : public QQuickItem
198+{
199+ Q_OBJECT
200+public:
201+ WindowKeysFilter(QQuickItem *parent = 0);
202+
203+ bool eventFilter(QObject *watched, QEvent *event);
204+
205+private Q_SLOTS:
206+ void setupFilterOnWindow(QQuickWindow *window);
207+
208+private:
209+ QPointer<QQuickWindow> m_filteredWindow;
210+};
211+
212+#endif // UNITY_WINDOWKEYSFILTER_H
213
214=== added file 'qml/Components/Dialogs.qml'
215--- qml/Components/Dialogs.qml 1970-01-01 00:00:00 +0000
216+++ qml/Components/Dialogs.qml 2014-07-29 14:07:22 +0000
217@@ -0,0 +1,217 @@
218+/*
219+ * Copyright (C) 2014 Canonical, Ltd.
220+ *
221+ * This program is free software; you can redistribute it and/or modify
222+ * it under the terms of the GNU General Public License as published by
223+ * the Free Software Foundation; version 3.
224+ *
225+ * This program is distributed in the hope that it will be useful,
226+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
227+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
228+ * GNU General Public License for more details.
229+ *
230+ * You should have received a copy of the GNU General Public License
231+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
232+ */
233+
234+import QtQuick 2.0
235+
236+import Unity.Application 0.1
237+import Unity.Session 0.1
238+import Ubuntu.Components 0.1
239+import Ubuntu.Components.Popups 0.1
240+
241+Item {
242+ id: root
243+
244+ function onPowerKeyPressed() {
245+ if (!powerKeyTimer.running) {
246+ powerKeyTimer.start();
247+ }
248+ }
249+
250+ function onPowerKeyReleased() {
251+ powerKeyTimer.stop();
252+ }
253+
254+ signal powerOffClicked();
255+
256+ QtObject {
257+ id: d // private stuff
258+
259+ property bool dialogShown: false
260+
261+ function showPowerDialog() {
262+ if (!d.dialogShown) {
263+ d.dialogShown = true;
264+ PopupUtils.open(powerDialog, root, {"z": root.z});
265+ }
266+ }
267+ }
268+
269+ Timer {
270+ id: powerKeyTimer
271+ interval: 2000
272+ repeat: false
273+ triggeredOnStart: false
274+
275+ onTriggered: {
276+ d.showPowerDialog();
277+ }
278+ }
279+
280+ Component {
281+ id: logoutDialog
282+ Dialog {
283+ id: dialogueLogout
284+ title: "Logout"
285+ text: "Are you sure that you want to logout?"
286+ Button {
287+ text: "Cancel"
288+ onClicked: {
289+ PopupUtils.close(dialogueLogout);
290+ d.dialogShown = false;
291+ }
292+ }
293+ Button {
294+ text: "Yes"
295+ onClicked: {
296+ DBusUnitySessionService.Logout();
297+ PopupUtils.close(dialogueLogout);
298+ d.dialogShown = false;
299+ }
300+ }
301+ }
302+ }
303+
304+ Component {
305+ id: shutdownDialog
306+ Dialog {
307+ id: dialogueShutdown
308+ title: "Shutdown"
309+ text: "Are you sure that you want to shutdown?"
310+ Button {
311+ text: "Cancel"
312+ onClicked: {
313+ PopupUtils.close(dialogueShutdown);
314+ d.dialogShown = false;
315+ }
316+ }
317+ Button {
318+ text: "Yes"
319+ onClicked: {
320+ dBusUnitySessionServiceConnection.closeAllApps();
321+ DBusUnitySessionService.Shutdown();
322+ PopupUtils.close(dialogueShutdown);
323+ d.dialogShown = false;
324+ }
325+ }
326+ }
327+ }
328+
329+ Component {
330+ id: rebootDialog
331+ Dialog {
332+ id: dialogueReboot
333+ title: "Reboot"
334+ text: "Are you sure that you want to reboot?"
335+ Button {
336+ text: "Cancel"
337+ onClicked: {
338+ PopupUtils.close(dialogueReboot)
339+ d.dialogShown = false;
340+ }
341+ }
342+ Button {
343+ text: "Yes"
344+ onClicked: {
345+ dBusUnitySessionServiceConnection.closeAllApps();
346+ DBusUnitySessionService.Reboot();
347+ PopupUtils.close(dialogueReboot);
348+ d.dialogShown = false;
349+ }
350+ }
351+ }
352+ }
353+
354+ Component {
355+ id: powerDialog
356+ Dialog {
357+ id: dialoguePower
358+ title: "Power"
359+ text: i18n.tr("Are you sure you would like to turn power off?")
360+ Button {
361+ text: i18n.tr("Power off")
362+ onClicked: {
363+ dBusUnitySessionServiceConnection.closeAllApps();
364+ PopupUtils.close(dialoguePower);
365+ d.dialogShown = false;
366+ root.powerOffClicked();
367+ }
368+ }
369+ Button {
370+ text: i18n.tr("Restart")
371+ onClicked: {
372+ dBusUnitySessionServiceConnection.closeAllApps();
373+ DBusUnitySessionService.Reboot();
374+ PopupUtils.close(dialoguePower);
375+ d.dialogShown = false;
376+ }
377+ }
378+ Button {
379+ text: i18n.tr("Cancel")
380+ onClicked: {
381+ PopupUtils.close(dialoguePower);
382+ d.dialogShown = false;
383+ }
384+ }
385+ }
386+ }
387+
388+
389+ Connections {
390+ id: dBusUnitySessionServiceConnection
391+ objectName: "dBusUnitySessionServiceConnection"
392+ target: DBusUnitySessionService
393+
394+ function closeAllApps() {
395+ while (true) {
396+ var app = ApplicationManager.get(0);
397+ if (app === null) {
398+ break;
399+ }
400+ ApplicationManager.stopApplication(app.appId);
401+ }
402+ }
403+
404+ onLogoutRequested: {
405+ // Display a dialog to ask the user to confirm.
406+ if (!d.dialogShown) {
407+ d.dialogShown = true;
408+ PopupUtils.open(logoutDialog, root, {"z": root.z});
409+ }
410+ }
411+
412+ onShutdownRequested: {
413+ // Display a dialog to ask the user to confirm.
414+ if (!d.dialogShown) {
415+ d.dialogShown = true;
416+ PopupUtils.open(shutdownDialog, root, {"z": root.z});
417+ }
418+ }
419+
420+ onRebootRequested: {
421+ // Display a dialog to ask the user to confirm.
422+ if (!d.dialogShown) {
423+ d.dialogShown = true;
424+ PopupUtils.open(rebootDialog, root, {"z": root.z});
425+ }
426+ }
427+
428+ onLogoutReady: {
429+ closeAllApps();
430+ Qt.quit();
431+ }
432+ }
433+
434+}
435
436=== modified file 'qml/Components/DraggingArea.qml'
437--- qml/Components/DraggingArea.qml 2013-10-25 14:26:29 +0000
438+++ qml/Components/DraggingArea.qml 2014-07-29 14:07:22 +0000
439@@ -117,4 +117,8 @@
440 dragging = false
441 __pressedPosition = Qt.point(mouse.x, mouse.y)
442 }
443+
444+ onCanceled: {
445+ dragging = false
446+ }
447 }
448
449=== added file 'qml/Components/InputMethod.qml'
450--- qml/Components/InputMethod.qml 1970-01-01 00:00:00 +0000
451+++ qml/Components/InputMethod.qml 2014-07-29 14:07:22 +0000
452@@ -0,0 +1,79 @@
453+/*
454+ * Copyright (C) 2014 Canonical, Ltd.
455+ *
456+ * This program is free software; you can redistribute it and/or modify
457+ * it under the terms of the GNU General Public License as published by
458+ * the Free Software Foundation; version 3.
459+ *
460+ * This program is distributed in the hope that it will be useful,
461+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
462+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
463+ * GNU General Public License for more details.
464+ *
465+ * You should have received a copy of the GNU General Public License
466+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
467+ */
468+
469+import QtQuick 2.0
470+import Unity.Application 0.1
471+import Ubuntu.Components 0.1
472+
473+Item {
474+ id: root
475+
476+ property var surface: null
477+
478+ property int transitionDuration: UbuntuAnimation.FastDuration
479+
480+ state: {
481+ if (surface && surface.state != MirSurfaceItem.Minimized) {
482+ return "shown";
483+ } else {
484+ return "hidden";
485+ }
486+ }
487+
488+ states: [
489+ State {
490+ name: "shown"
491+ PropertyChanges {
492+ target: root
493+ visible: true
494+ y: 0
495+ }
496+ },
497+ State {
498+ name: "hidden"
499+ PropertyChanges {
500+ target: root
501+ visible: false
502+ // half-way down because the vkb occupies only the lower half of the surface
503+ // TODO: consider keyboard rotation
504+ y: root.parent.height / 2.0
505+ }
506+ }
507+ ]
508+
509+ transitions: [
510+ Transition {
511+ from: "*"; to: "*"
512+ PropertyAction { property: "visible"; value: true }
513+ UbuntuNumberAnimation { property: "y"; duration: transitionDuration }
514+ }
515+ ]
516+
517+ onSurfaceChanged: {
518+ if (surface) {
519+ surface.parent = root;
520+ surface.anchors.fill = root;
521+ }
522+ }
523+
524+ Component.onDestruction: {
525+ if (surface) {
526+ surface.parent = null;
527+ surface.release();
528+ surface = null;
529+ }
530+ }
531+}
532
533=== modified file 'qml/Components/PageHeader.qml'
534--- qml/Components/PageHeader.qml 2014-07-21 09:49:41 +0000
535+++ qml/Components/PageHeader.qml 2014-07-29 14:07:22 +0000
536@@ -117,7 +117,7 @@
537 Flickable {
538 id: headerContainer
539 objectName: "headerContainer"
540- clip: true
541+ clip: openSearchAnimation.running
542 anchors { left: parent.left; top: parent.top; right: parent.right }
543 height: units.gu(6.5)
544 contentHeight: headersColumn.height
545@@ -155,6 +155,7 @@
546 anchors { left: parent.left; right: parent.right }
547 height: headerContainer.height
548 contentHeight: height
549+ opacity: headerContainer.clip || headerContainer.showSearch ? 1 : 0 // setting visible false cause column to relayout
550 separatorSource: ""
551 // Required to keep PageHeadStyle noise down as it expects the Page's properties around.
552 property var styledItem: searchHeader
553@@ -229,6 +230,7 @@
554 anchors { left: parent.left; right: parent.right }
555 height: headerContainer.height
556 contentHeight: height
557+ opacity: headerContainer.clip || !headerContainer.showSearch ? 1 : 0 // setting visible false cause column to relayout
558 separatorSource: ""
559 textColor: root.scopeStyle ? root.scopeStyle.headerForeground : "grey"
560 property var styledItem: header
561
562=== modified file 'qml/Dash/Apps/RunningApplicationTile.qml'
563--- qml/Dash/Apps/RunningApplicationTile.qml 2014-03-25 11:34:34 +0000
564+++ qml/Dash/Apps/RunningApplicationTile.qml 2014-07-29 14:07:22 +0000
565@@ -29,11 +29,8 @@
566 signal requestedActivationMode()
567 signal requestedTerminationMode()
568
569- // Was: childrenRect.height
570- // To avoid "binding loop" warning
571- height: shapedApplicationImage.height + labelContainer.height
572-
573- width: shapedApplicationImage.width <= units.gu(11) ? units.gu(11) : height
574+ width: thumbnail.width
575+ height: thumbnail.height + labelContainer.height
576
577 property bool terminationModeEnabled: false
578
579@@ -50,42 +47,53 @@
580 }
581 }
582
583- UbuntuShape {
584- id: shapedApplicationImage
585- anchors { top: parent.top; horizontalCenter: parent.horizontalCenter }
586-
587- height: units.gu(17)
588- width: applicationImage.width
589- radius: "medium"
590-
591- image: Image {
592- id: applicationImage
593- source: application.screenshot
594- // height : width = ss.height : ss.width
595- height: shapedApplicationImage.height
596- fillMode: Image.PreserveAspectCrop
597- width: Math.min(height, height * sourceSize.width / sourceSize.height)
598- }
599-
600- }
601-
602- UbuntuShape {
603- id: borderPressed
604-
605- anchors.fill: shapedApplicationImage
606- radius: "medium"
607- borderSource: "radius_pressed.sci"
608- opacity: root.pressed ? 1.0 : 0.0
609- Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutQuint } }
610+ Item {
611+ id: thumbnail
612+
613+ width: shapedApplicationImage.width
614+ height: shapedApplicationImage.height
615+
616+ UbuntuShape {
617+ id: shapedApplicationImage
618+ x: 0
619+ y: 0
620+ height: applicationImage.height
621+ width: applicationImage.width
622+ radius: "medium"
623+
624+ image: Image {
625+ id: applicationImage
626+ source: application.screenshot
627+ // height : width = ss.height : ss.width
628+
629+ property bool isLandscape: sourceSize.width > sourceSize.height
630+ property real maxDimension: units.gu(17)
631+
632+ width: isLandscape ? Math.min(sourceSize.width, maxDimension) : height * (sourceSize.width / sourceSize.height)
633+ height: isLandscape ? width * (sourceSize.height / sourceSize.width) : Math.min(sourceSize.height, maxDimension)
634+ fillMode: Image.Stretch
635+ }
636+
637+ }
638+
639+ UbuntuShape {
640+ id: borderPressed
641+
642+ anchors.fill: shapedApplicationImage
643+ radius: "medium"
644+ borderSource: "radius_pressed.sci"
645+ opacity: root.pressed ? 1.0 : 0.0
646+ Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutQuint } }
647+ }
648 }
649
650 // FIXME: label code duplicated with Tile
651 Item {
652 id: labelContainer
653 anchors {
654- left: parent.left
655- right: parent.right
656- top: shapedApplicationImage.bottom
657+ left: thumbnail.left
658+ right: thumbnail.right
659+ top: thumbnail.bottom
660 }
661 height: units.gu(2)
662
663@@ -110,9 +118,9 @@
664 CloseIcon {
665 objectName: "closeIcon " + model.name
666 anchors {
667- left: shapedApplicationImage.left
668+ left: thumbnail.left
669 leftMargin: -units.gu(1)
670- top: parent.top
671+ top: thumbnail.top
672 topMargin: -units.gu(1)
673 }
674 height: units.gu(6)
675
676=== modified file 'qml/Dash/DashContent.qml'
677--- qml/Dash/DashContent.qml 2014-07-08 19:35:59 +0000
678+++ qml/Dash/DashContent.qml 2014-07-29 14:07:22 +0000
679@@ -121,6 +121,10 @@
680 Loader {
681 width: ListView.view.width
682 height: ListView.view.height
683+ opacity: { // hide delegate if offscreen
684+ var xPositionRelativetoView = ListView.view.contentX - x
685+ return (xPositionRelativetoView > -width && xPositionRelativetoView < width) ? 1 : 0
686+ }
687 asynchronous: true
688 // TODO This if will eventually go away since we're killing DashApps.qml
689 // once we move app closing to the spread
690
691=== added file 'qml/Dash/graphics/phone/screenshots/vkb_portrait.png'
692Binary files qml/Dash/graphics/phone/screenshots/vkb_portrait.png 1970-01-01 00:00:00 +0000 and qml/Dash/graphics/phone/screenshots/vkb_portrait.png 2014-07-29 14:07:22 +0000 differ
693=== modified file 'qml/Launcher/Launcher.qml'
694--- qml/Launcher/Launcher.qml 2014-07-09 13:38:07 +0000
695+++ qml/Launcher/Launcher.qml 2014-07-29 14:07:22 +0000
696@@ -181,7 +181,7 @@
697 bottom: parent.bottom
698 }
699 x: -width
700- opacity: (x == -width && dragArea.status === DirectionalDragArea.WaitingForTouch) ? 0 : 1
701+ visible: x > -width || dragArea.status === DirectionalDragArea.Undecided
702 model: LauncherModel
703
704 property bool animate: true
705
706=== modified file 'qml/Panel/Indicators.qml'
707--- qml/Panel/Indicators.qml 2014-07-10 13:36:41 +0000
708+++ qml/Panel/Indicators.qml 2014-07-29 14:07:22 +0000
709@@ -37,6 +37,7 @@
710 readonly property int lockThreshold: openedHeight / 2
711 property bool fullyOpened: height == openedHeight
712 property bool partiallyOpened: height > panelHeight && !fullyOpened
713+ property bool fullyClosed: height <= panelHeight
714 property bool contentEnabled: true
715 property bool initalizeItem: true
716 readonly property alias content: menuContent
717@@ -187,6 +188,7 @@
718 bottom: handle.top
719 }
720 indicatorsModel: visibleIndicators.model
721+ visible: indicators.partiallyOpened || indicators.fullyOpened
722 clip: !indicators.fullyOpened
723 activeHeader: indicators.state == "hint" || indicators.state == "reveal"
724 enabled: contentEnabled
725@@ -219,6 +221,7 @@
726 }
727 height: Math.max(Math.min(handleImage.height, indicators.height - handleImage.height), 0)
728 clip: height < handleImage.height
729+ visible: menuContent.visible
730
731 BorderImage {
732 id: handleImage
733
734=== modified file 'qml/Shell.qml'
735--- qml/Shell.qml 2014-07-18 15:13:35 +0000
736+++ qml/Shell.qml 2014-07-29 14:07:22 +0000
737@@ -19,9 +19,9 @@
738 import GSettings 1.0
739 import Unity.Application 0.1
740 import Ubuntu.Components 0.1
741-import Ubuntu.Components.Popups 0.1
742 import Ubuntu.Gestures 0.1
743 import Unity.Launcher 0.1
744+import Utils 0.1
745 import LightDM 0.1 as LightDM
746 import Powerd 0.1
747 import SessionBroadcast 0.1
748@@ -31,10 +31,11 @@
749 import "Panel"
750 import "Components"
751 import "Notifications"
752+import "Stages"
753 import Unity.Notifications 1.0 as NotificationBackend
754 import Unity.Session 0.1
755
756-FocusScope {
757+Item {
758 id: shell
759
760 // this is only here to select the width / height of the window if not running fullscreen
761@@ -47,7 +48,7 @@
762 property url background
763 readonly property real panelHeight: panel.panelHeight
764
765- property bool dashShown: dash.shown
766+ property bool dashShown: dash.shown && dash.available && underlay.visible
767
768 property bool sideStageEnabled: shell.width >= units.gu(100)
769 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
770@@ -55,14 +56,10 @@
771 function activateApplication(appId) {
772 if (ApplicationManager.findApplication(appId)) {
773 ApplicationManager.requestFocusApplication(appId);
774- stages.show(true);
775- if (stages.locked && ApplicationManager.focusedApplicationId == appId) {
776- applicationsDisplayLoader.item.select(appId);
777- }
778 } else {
779 var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
780 ApplicationManager.startApplication(appId, execFlags);
781- stages.show(false);
782+ stages.show();
783 }
784 }
785
786@@ -89,82 +86,74 @@
787 id: volumeControl
788 }
789
790- Keys.onVolumeUpPressed: volumeControl.volumeUp()
791- Keys.onVolumeDownPressed: volumeControl.volumeDown()
792+ WindowKeysFilter {
793+ // Handle but do not filter out volume keys
794+ Keys.onVolumeUpPressed: { volumeControl.volumeUp(); event.accepted = false; }
795+ Keys.onVolumeDownPressed: { volumeControl.volumeDown(); event.accepted = false; }
796+
797+ Keys.onPressed: {
798+ if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
799+ dialogs.onPowerKeyPressed();
800+ event.accepted = true;
801+ } else {
802+ event.accepted = false;
803+ }
804+ }
805+
806+ Keys.onReleased: {
807+ if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
808+ dialogs.onPowerKeyReleased();
809+ event.accepted = true;
810+ } else {
811+ event.accepted = false;
812+ }
813+ }
814+ }
815
816 Item {
817- id: underlayClipper
818+ id: underlay
819+ objectName: "underlay"
820 anchors.fill: parent
821- anchors.rightMargin: stages.overlayWidth
822- clip: stages.overlayMode && !stages.painting
823-
824- InputFilterArea {
825- anchors.fill: parent
826- blockInput: parent.clip
827+
828+ // Whether the underlay is fully covered by opaque UI elements.
829+ property bool fullyCovered: (panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth)
830+ || stages.fullyShown || greeterWrapper.fullyShown
831+ visible: !fullyCovered
832+
833+ Image {
834+ anchors.fill: dash
835+ source: shell.width > shell.height ? "Dash/graphics/paper_landscape.png" : "Dash/graphics/paper_portrait.png"
836+ fillMode: Image.PreserveAspectCrop
837+ horizontalAlignment: Image.AlignRight
838+ verticalAlignment: Image.AlignTop
839 }
840
841- Item {
842- id: underlay
843- objectName: "underlay"
844- anchors.fill: parent
845- anchors.rightMargin: -parent.anchors.rightMargin
846-
847- // Whether the underlay is fully covered by opaque UI elements.
848- property bool fullyCovered: panel.indicators.fullyOpened && shell.width <= panel.indicators.width
849-
850- // Whether the user should see the topmost application surface (if there's one at all).
851- readonly property bool applicationSurfaceShouldBeSeen: stages.shown && !stages.painting && !stages.overlayMode
852-
853- // NB! Application surfaces are stacked behind the shell one. So they can only be seen by the user
854- // through the translucent parts of the shell surface.
855- visible: !fullyCovered && !applicationSurfaceShouldBeSeen
856-
857- Rectangle {
858- anchors.fill: parent
859- color: "black"
860- opacity: dash.disappearingAnimationProgress
861- }
862-
863- Image {
864- anchors.fill: dash
865- source: shell.width > shell.height ? "Dash/graphics/paper_landscape.png" : "Dash/graphics/paper_portrait.png"
866- fillMode: Image.PreserveAspectCrop
867- horizontalAlignment: Image.AlignRight
868- verticalAlignment: Image.AlignTop
869- }
870-
871- Dash {
872- id: dash
873- objectName: "dash"
874-
875- available: !LightDM.Greeter.active
876- hides: [stages, launcher, panel.indicators]
877- shown: disappearingAnimationProgress !== 1.0 && greeterWrapper.showProgress !== 1.0
878- enabled: disappearingAnimationProgress === 0.0 && greeterWrapper.showProgress === 0.0 && edgeDemo.dashEnabled
879-
880- anchors {
881- fill: parent
882- topMargin: panel.panelHeight
883- }
884-
885- contentScale: 1.0 - 0.2 * disappearingAnimationProgress
886- opacity: 1.0 - disappearingAnimationProgress
887- property real disappearingAnimationProgress: {
888- if (stages.overlayMode) {
889- return 0;
890- } else {
891- return stages.showProgress
892- }
893- }
894-
895- // FIXME: only necessary because stages.showProgress is not animated
896- Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}
897- }
898+ Dash {
899+ id: dash
900+ objectName: "dash"
901+
902+ available: !LightDM.Greeter.active
903+ hides: [stages, launcher, panel.indicators]
904+ shown: disappearingAnimationProgress !== 1.0 && greeterWrapper.showProgress !== 1.0 &&
905+ !(panel.indicators.fullyOpened && !sideStageEnabled)
906+ enabled: disappearingAnimationProgress === 0.0 && greeterWrapper.showProgress === 0.0 && edgeDemo.dashEnabled
907+
908+ anchors {
909+ fill: parent
910+ topMargin: panel.panelHeight
911+ }
912+
913+ contentScale: 1.0 - 0.2 * disappearingAnimationProgress
914+ opacity: 1.0 - disappearingAnimationProgress
915+ property real disappearingAnimationProgress: stages.showProgress
916+
917+ // FIXME: only necessary because stages.showProgress is not animated
918+ Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}
919 }
920 }
921
922 EdgeDragArea {
923- id: stagesDragHandle
924+ id: stagesDragArea
925 direction: Direction.Leftwards
926
927 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
928@@ -174,20 +163,20 @@
929
930 onTouchXChanged: {
931 if (status == DirectionalDragArea.Recognized) {
932- if (ApplicationManager.count == 0) {
933- progress = Math.max(stages.width - stagesDragHandle.width + touchX, stages.width * .3)
934+ if (ApplicationManager.empty) {
935+ progress = Math.max(stages.width - stagesDragArea.width + touchX, stages.width * .3);
936 } else {
937- progress = stages.width - stagesDragHandle.width + touchX
938+ progress = stages.width - stagesDragArea.width + touchX;
939 }
940 }
941 }
942
943 onDraggingChanged: {
944 if (!dragging) {
945- if (ApplicationManager.count > 0 && progress < stages.width - units.gu(10)) {
946- stages.show(true)
947+ if (!ApplicationManager.empty && progress < stages.width - units.gu(10)) {
948+ stages.show();
949 }
950- stagesDragHandle.progress = stages.width;
951+ stagesDragArea.progress = Qt.binding(function () { return stages.width; });
952 }
953 }
954 }
955@@ -198,253 +187,93 @@
956 width: parent.width
957 height: parent.height
958
959+ visible: !fullyHidden && !ApplicationManager.empty
960+
961 x: {
962 if (shown) {
963- if (overlayMode || locked || greeter.fakeActiveForApp !== "") {
964+ if (locked || greeter.fakeActiveForApp !== "") {
965 return 0;
966 }
967- return launcher.progress
968+ return launcher.progress;
969 } else {
970- return stagesDragHandle.progress
971+ return stagesDragArea.progress
972 }
973 }
974-
975 Behavior on x { SmoothedAnimation { velocity: 600; duration: UbuntuAnimation.FastDuration } }
976
977 property bool shown: false
978+ onShownChanged: {
979+ if (shown) {
980+ if (ApplicationManager.count > 0) {
981+ ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
982+ }
983+ } else {
984+ if (ApplicationManager.focusedApplicationId) {
985+ ApplicationManager.updateScreenshot(ApplicationManager.focusedApplicationId);
986+ ApplicationManager.unfocusCurrentApplication();
987+ }
988+ }
989+ }
990
991- property real showProgress: overlayMode ? 0 : MathUtils.clamp(1 - x / shell.width, 0, 1)
992+ // Avoid a silent "divide by zero -> NaN" situation during init as shell.width will be
993+ // zero. That breaks the property binding and the function won't be reevaluated once
994+ // shell.width is set, with the NaN result staying there for good.
995+ property real showProgress: shell.width ? MathUtils.clamp(1 - x / shell.width, 0, 1) : 0
996
997 property bool fullyShown: x == 0
998 property bool fullyHidden: x == width
999
1000- property bool painting: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.painting : false
1001- property bool fullscreen: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.fullscreen : false
1002- property bool overlayMode: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.overlayMode : false
1003- property int overlayWidth: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.overlayWidth : false
1004 property bool locked: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.locked : false
1005
1006- function show(focusApp) {
1007+ // It might technically not be fullyShown but visually it just looks so.
1008+ property bool roughlyFullyShown: x >= 0 && x <= units.gu(1)
1009+
1010+ function show() {
1011 shown = true;
1012- panel.indicators.hide();
1013- edgeDemo.stopDemo();
1014- greeter.hide();
1015- if (!ApplicationManager.focusedApplicationId && ApplicationManager.count > 0 && focusApp) {
1016- ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
1017- }
1018 }
1019
1020 function hide() {
1021 shown = false;
1022- if (ApplicationManager.focusedApplicationId) {
1023- ApplicationManager.unfocusCurrentApplication();
1024- }
1025 }
1026
1027 Connections {
1028 target: ApplicationManager
1029-
1030 onFocusRequested: {
1031 if (greeter.fakeActiveForApp !== "" && greeter.fakeActiveForApp !== appId) {
1032 lockscreen.show();
1033 }
1034 greeter.hide();
1035- stages.show(true);
1036+ stages.show();
1037 }
1038
1039 onFocusedApplicationIdChanged: {
1040 if (greeter.fakeActiveForApp !== "" && greeter.fakeActiveForApp !== ApplicationManager.focusedApplicationId) {
1041 lockscreen.show();
1042 }
1043- if (ApplicationManager.focusedApplicationId.length > 0) {
1044- stages.show(false);
1045- } else {
1046- if (!stages.overlayMode) {
1047- stages.hide();
1048- }
1049- }
1050+ panel.indicators.hide();
1051 }
1052
1053 onApplicationAdded: {
1054- stages.show(false);
1055+ if (greeter.shown) {
1056+ greeter.hide();
1057+ }
1058+ if (!stages.shown) {
1059+ stages.show();
1060+ }
1061 }
1062
1063- onApplicationRemoved: {
1064- if (ApplicationManager.focusedApplicationId.length == 0) {
1065+ onEmptyChanged: {
1066+ if (ApplicationManager.empty) {
1067 stages.hide();
1068 }
1069 }
1070 }
1071
1072- property bool dialogShown: false
1073-
1074- Component {
1075- id: logoutDialog
1076- Dialog {
1077- id: dialogueLogout
1078- title: "Logout"
1079- text: "Are you sure that you want to logout?"
1080- Button {
1081- text: "Cancel"
1082- onClicked: {
1083- PopupUtils.close(dialogueLogout);
1084- stages.dialogShown = false;
1085- }
1086- }
1087- Button {
1088- text: "Yes"
1089- onClicked: {
1090- DBusUnitySessionService.Logout();
1091- PopupUtils.close(dialogueLogout);
1092- stages.dialogShown = false;
1093- }
1094- }
1095- }
1096- }
1097-
1098- Component {
1099- id: shutdownDialog
1100- Dialog {
1101- id: dialogueShutdown
1102- title: "Shutdown"
1103- text: "Are you sure that you want to shutdown?"
1104- Button {
1105- text: "Cancel"
1106- onClicked: {
1107- PopupUtils.close(dialogueShutdown);
1108- stages.dialogShown = false;
1109- }
1110- }
1111- Button {
1112- text: "Yes"
1113- onClicked: {
1114- dBusUnitySessionServiceConnection.closeAllApps();
1115- DBusUnitySessionService.Shutdown();
1116- PopupUtils.close(dialogueShutdown);
1117- stages.dialogShown = false;
1118- }
1119- }
1120- }
1121- }
1122-
1123- Component {
1124- id: rebootDialog
1125- Dialog {
1126- id: dialogueReboot
1127- title: "Reboot"
1128- text: "Are you sure that you want to reboot?"
1129- Button {
1130- text: "Cancel"
1131- onClicked: {
1132- PopupUtils.close(dialogueReboot)
1133- stages.dialogShown = false;
1134- }
1135- }
1136- Button {
1137- text: "Yes"
1138- onClicked: {
1139- dBusUnitySessionServiceConnection.closeAllApps();
1140- DBusUnitySessionService.Reboot();
1141- PopupUtils.close(dialogueReboot);
1142- stages.dialogShown = false;
1143- }
1144- }
1145- }
1146- }
1147-
1148- Component {
1149- id: powerDialog
1150- Dialog {
1151- id: dialoguePower
1152- title: "Power"
1153- text: i18n.tr("Are you sure you would like to turn power off?")
1154- Button {
1155- text: i18n.tr("Power off")
1156- onClicked: {
1157- dBusUnitySessionServiceConnection.closeAllApps();
1158- PopupUtils.close(dialoguePower);
1159- stages.dialogShown = false;
1160- shutdownFadeOutRectangle.enabled = true;
1161- shutdownFadeOutRectangle.visible = true;
1162- shutdownFadeOut.start();
1163- }
1164- }
1165- Button {
1166- text: i18n.tr("Restart")
1167- onClicked: {
1168- dBusUnitySessionServiceConnection.closeAllApps();
1169- DBusUnitySessionService.Reboot();
1170- PopupUtils.close(dialoguePower);
1171- stages.dialogShown = false;
1172- }
1173- }
1174- Button {
1175- text: i18n.tr("Cancel")
1176- onClicked: {
1177- PopupUtils.close(dialoguePower);
1178- stages.dialogShown = false;
1179- }
1180- }
1181- }
1182- }
1183-
1184- function showPowerDialog() {
1185- if (!stages.dialogShown) {
1186- stages.dialogShown = true;
1187- PopupUtils.open(powerDialog);
1188- }
1189- }
1190-
1191- Connections {
1192- id: dBusUnitySessionServiceConnection
1193- objectName: "dBusUnitySessionServiceConnection"
1194- target: DBusUnitySessionService
1195-
1196- function closeAllApps() {
1197- while (true) {
1198- var app = ApplicationManager.get(0);
1199- if (app === null) {
1200- break;
1201- }
1202- ApplicationManager.stopApplication(app.appId);
1203- }
1204- }
1205-
1206- onLogoutRequested: {
1207- // Display a dialog to ask the user to confirm.
1208- if (!stages.dialogShown) {
1209- stages.dialogShown = true;
1210- PopupUtils.open(logoutDialog);
1211- }
1212- }
1213-
1214- onShutdownRequested: {
1215- // Display a dialog to ask the user to confirm.
1216- if (!stages.dialogShown) {
1217- stages.dialogShown = true;
1218- PopupUtils.open(shutdownDialog);
1219- }
1220- }
1221-
1222- onRebootRequested: {
1223- // Display a dialog to ask the user to confirm.
1224- if (!stages.dialogShown) {
1225- stages.dialogShown = true;
1226- PopupUtils.open(rebootDialog);
1227- }
1228- }
1229-
1230- onLogoutReady: {
1231- closeAllApps();
1232- Qt.quit();
1233- }
1234- }
1235-
1236 Loader {
1237 id: applicationsDisplayLoader
1238 anchors.fill: parent
1239
1240- source: shell.sideStageEnabled ? "Stages/StageWithSideStage.qml" : "Stages/PhoneStage.qml"
1241+ source: shell.sideStageEnabled ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
1242
1243 Binding {
1244 target: applicationsDisplayLoader.item
1245@@ -453,27 +282,56 @@
1246 }
1247 Binding {
1248 target: applicationsDisplayLoader.item
1249- property: "moving"
1250- value: !stages.fullyShown
1251- }
1252- Binding {
1253- target: applicationsDisplayLoader.item
1254- property: "shown"
1255- value: stages.shown
1256- }
1257- Binding {
1258- target: applicationsDisplayLoader.item
1259 property: "dragAreaWidth"
1260 value: shell.edgeSize
1261 }
1262 Binding {
1263 target: applicationsDisplayLoader.item
1264+ property: "maximizedAppTopMargin"
1265+ // Not just using panel.panelHeight as that changes depending on the focused app.
1266+ value: panel.indicators.panelHeight
1267+ }
1268+ Binding {
1269+ target: applicationsDisplayLoader.item
1270+ property: "interactive"
1271+ value: stages.roughlyFullyShown && !greeter.shown && !lockscreen.shown
1272+ && panel.indicators.fullyClosed
1273+ }
1274+ Binding {
1275+ target: applicationsDisplayLoader.item
1276 property: "spreadEnabled"
1277 value: greeter.fakeActiveForApp === "" // to support emergency dialer hack
1278 }
1279 }
1280 }
1281
1282+ InputMethod {
1283+ id: inputMethod
1284+ objectName: "inputMethod"
1285+ anchors { fill: parent; topMargin: panel.panelHeight }
1286+ z: notifications.useModal || panel.indicators.shown ? overlay.z + 1 : overlay.z - 1
1287+ }
1288+
1289+ Connections {
1290+ target: SurfaceManager
1291+ onSurfaceCreated: {
1292+ if (surface.type == MirSurfaceItem.InputMethod) {
1293+ inputMethod.surface = surface;
1294+ }
1295+ }
1296+
1297+ onSurfaceDestroyed: {
1298+ if (inputMethod.surface == surface) {
1299+ inputMethod.surface = null;
1300+ surface.parent = null;
1301+ }
1302+ if (!surface.parent) {
1303+ // there's no one displaying it. delete it right away
1304+ surface.release();
1305+ }
1306+ }
1307+ }
1308+
1309 Lockscreen {
1310 id: lockscreen
1311 objectName: "lockscreen"
1312@@ -486,7 +344,7 @@
1313 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
1314 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
1315 y: panel.panelHeight
1316- x: required ? 0 : - width
1317+ visible: required
1318 width: parent.width
1319 height: parent.height - panel.panelHeight
1320 background: shell.background
1321@@ -566,6 +424,7 @@
1322 StandardAnimation {}
1323 }
1324
1325+ property bool fullyShown: showProgress === 1.0
1326 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
1327 onShowProgressChanged: if (LightDM.Greeter.promptless && showProgress === 0) greeter.login()
1328
1329@@ -632,12 +491,6 @@
1330 }
1331 }
1332
1333- InputFilterArea {
1334- anchors.fill: parent
1335- blockInput: ApplicationManager.focusedApplicationId.length === 0 || greeter.shown || lockscreen.shown || launcher.shown
1336- || panel.indicators.shown
1337- }
1338-
1339 Connections {
1340 id: powerConnection
1341 target: Powerd
1342@@ -680,7 +533,7 @@
1343 return;
1344 }
1345
1346- if (stages.shown && !stages.overlayMode && !stages.locked) {
1347+ if (!stages.locked) {
1348 stages.hide();
1349 launcher.fadeOut();
1350 } else {
1351@@ -695,6 +548,7 @@
1352
1353 Item {
1354 id: overlay
1355+ z: 10
1356
1357 anchors.fill: parent
1358
1359@@ -709,29 +563,13 @@
1360 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
1361 panelHeight: units.gu(3)
1362 }
1363- property string focusedAppId: ApplicationManager.focusedApplicationId
1364- property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
1365- fullscreenMode: (focusedApplication && stages.fullscreen && !LightDM.Greeter.active) || greeter.fakeActiveForApp !== ""
1366-
1367- InputFilterArea {
1368- anchors {
1369- top: parent.top
1370- left: parent.left
1371- right: parent.right
1372- }
1373- height: (panel.fullscreenMode) ? shell.edgeSize : panel.panelHeight
1374- blockInput: true
1375- }
1376- }
1377-
1378- InputFilterArea {
1379- blockInput: launcher.shown
1380- anchors {
1381- top: parent.top
1382- bottom: parent.bottom
1383- left: parent.left
1384- }
1385- width: launcher.width
1386+
1387+ property bool topmostApplicationIsFullscreen:
1388+ ApplicationManager.focusedApplicationId &&
1389+ ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
1390+
1391+ fullscreenMode: (stages.roughlyFullyShown && topmostApplicationIsFullscreen
1392+ && !LightDM.Greeter.active) || greeter.fakeActiveForApp !== ""
1393 }
1394
1395 Launcher {
1396@@ -774,11 +612,6 @@
1397 MouseArea {
1398 anchors.fill: parent
1399 }
1400-
1401- InputFilterArea {
1402- anchors.fill: parent
1403- blockInput: modalNotificationBackground.visible
1404- }
1405 }
1406
1407 Notifications {
1408@@ -804,36 +637,7 @@
1409 PropertyChanges { target: notifications; width: units.gu(38) }
1410 }
1411 ]
1412-
1413- InputFilterArea {
1414- anchors { left: parent.left; right: parent.right }
1415- height: parent.contentHeight
1416- blockInput: height > 0
1417- }
1418- }
1419- }
1420-
1421- focus: true
1422- onFocusChanged: if (!focus) forceActiveFocus();
1423-
1424- InputFilterArea {
1425- anchors {
1426- top: parent.top
1427- bottom: parent.bottom
1428- left: parent.left
1429- }
1430- width: shell.edgeSize
1431- blockInput: true
1432- }
1433-
1434- InputFilterArea {
1435- anchors {
1436- top: parent.top
1437- bottom: parent.bottom
1438- right: parent.right
1439- }
1440- width: shell.edgeSize
1441- blockInput: true
1442+ }
1443 }
1444
1445 Binding {
1446@@ -842,24 +646,22 @@
1447 value: "unity8"
1448 }
1449
1450- OSKController {
1451- anchors.topMargin: panel.panelHeight
1452- anchors.fill: parent // as needs to know the geometry of the shell
1453- }
1454-
1455- //FIXME: This should be handled in the input stack, keyboard shouldnt propagate
1456- MouseArea {
1457- anchors.bottom: parent.bottom
1458- anchors.left: parent.left
1459- anchors.right: parent.right
1460- height: ApplicationManager.keyboardVisible ? ApplicationManager.keyboardHeight : 0
1461-
1462- enabled: ApplicationManager.keyboardVisible
1463+ Dialogs {
1464+ id: dialogs
1465+ anchors.fill: parent
1466+ z: overlay.z + 10
1467+ onPowerOffClicked: {
1468+ shutdownFadeOutRectangle.enabled = true;
1469+ shutdownFadeOutRectangle.visible = true;
1470+ shutdownFadeOut.start();
1471+ }
1472 }
1473
1474 Label {
1475+ id: alphaDisclaimerLabel
1476 anchors.centerIn: parent
1477 visible: ApplicationManager.fake ? ApplicationManager.fake : false
1478+ z: dialogs.z + 10
1479 text: "EARLY ALPHA\nNOT READY FOR USE"
1480 color: "lightgrey"
1481 opacity: 0.2
1482@@ -873,6 +675,7 @@
1483
1484 EdgeDemo {
1485 id: edgeDemo
1486+ z: alphaDisclaimerLabel.z + 10
1487 greeter: greeter
1488 launcher: launcher
1489 dash: dash
1490@@ -885,35 +688,9 @@
1491 onShowHome: showHome()
1492 }
1493
1494- Keys.onPressed: {
1495- if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
1496- if (!powerKeyTimer.running) {
1497- powerKeyTimer.start();
1498- }
1499- event.accepted = true;
1500- }
1501- }
1502-
1503- Keys.onReleased: {
1504- if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
1505- powerKeyTimer.stop();
1506- event.accepted = true;
1507- }
1508- }
1509-
1510- Timer {
1511- id: powerKeyTimer
1512- interval: 2000
1513- repeat: false
1514- triggeredOnStart: false
1515-
1516- onTriggered: {
1517- stages.showPowerDialog();
1518- }
1519- }
1520-
1521 Rectangle {
1522 id: shutdownFadeOutRectangle
1523+ z: edgeDemo.z + 10
1524 enabled: false
1525 visible: false
1526 color: "black"
1527@@ -930,4 +707,5 @@
1528 }
1529 }
1530 }
1531+
1532 }
1533
1534=== added directory 'qml/Stages/Animations'
1535=== added file 'qml/Stages/Animations/BaseSurfaceAnimation.qml'
1536--- qml/Stages/Animations/BaseSurfaceAnimation.qml 1970-01-01 00:00:00 +0000
1537+++ qml/Stages/Animations/BaseSurfaceAnimation.qml 2014-07-29 14:07:22 +0000
1538@@ -0,0 +1,93 @@
1539+/*
1540+ * Copyright (C) 2014 Canonical, Ltd.
1541+ *
1542+ * This program is free software; you can redistribute it and/or modify
1543+ * it under the terms of the GNU General Public License as published by
1544+ * the Free Software Foundation; version 3.
1545+ *
1546+ * This program is distributed in the hope that it will be useful,
1547+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1548+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1549+ * GNU General Public License for more details.
1550+ *
1551+ * You should have received a copy of the GNU General Public License
1552+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1553+ */
1554+
1555+import QtQuick 2.0
1556+
1557+/* This is the base case for surface animations, used when adding/removing * child surfaces.
1558+ * The class is meant to be overridden and changes/animations provided for state changes.
1559+ * NB. It is important to release the surface at the end of the "out" animation.
1560+ *
1561+ * Example - Simple fade in/out
1562+ *
1563+ * BaseSurfaceAnimation {
1564+ * outChanges: [ PropertyChanges { target: animation.surface; opacity: 0.0 } ]
1565+ * outAnimations: [
1566+ SequentialAnimation {
1567+ * NumberAnimation { target: animation.surface; property: "opacity"; duration: 300 }
1568+ * ScriptAction { script: { if (animation.parent.removing) animation.surface.release(); } }
1569+ * }
1570+ * ]
1571+ *
1572+ * inChanges: [ PropertyChanges { target: animation.surface; opacity: 1.0 } ]
1573+ * inAnimations: [ NumberAnimation { target: animation.surface; property: "opacity"; duration: 300 } ]
1574+ * }
1575+ */
1576+Item {
1577+ id: base
1578+ property Item surface: null
1579+
1580+ // changes applied when state changes to "in"
1581+ property list<QtObject> outChanges
1582+ // transition animations when changing state to "in"
1583+ property list<QtObject> outAnimations
1584+
1585+ // changes applied when state changes to "out"
1586+ property list<QtObject> inChanges
1587+ // transition animations when changing state to "out"
1588+ property list<QtObject> inAnimations
1589+
1590+ function start() {
1591+ // "prep" state forces outChanges without transition animations.
1592+ state = "prep"
1593+ state = "in";
1594+ }
1595+ function end() {
1596+ state = "out";
1597+ }
1598+
1599+ states: [
1600+ State {
1601+ name: "baseAnimation"
1602+ PropertyChanges { target: base.surface; anchors.fill: undefined }
1603+ },
1604+
1605+ State {
1606+ name: "prep"
1607+ extend: "baseAnimation"
1608+ changes: outChanges
1609+ },
1610+ State {
1611+ name: "out"
1612+ extend: "prep"
1613+ },
1614+ State {
1615+ name: "in"
1616+ extend: "baseAnimation"
1617+ changes: inChanges
1618+ }
1619+ ]
1620+
1621+ transitions: [
1622+ Transition {
1623+ to: "out"
1624+ animations: outAnimations
1625+ },
1626+ Transition {
1627+ to: "in"
1628+ animations: inAnimations
1629+ }
1630+ ]
1631+}
1632
1633=== added file 'qml/Stages/Animations/DarkenAndFadeInAnimation.qml'
1634--- qml/Stages/Animations/DarkenAndFadeInAnimation.qml 1970-01-01 00:00:00 +0000
1635+++ qml/Stages/Animations/DarkenAndFadeInAnimation.qml 2014-07-29 14:07:22 +0000
1636@@ -0,0 +1,48 @@
1637+/*
1638+ * Copyright (C) 2014 Canonical, Ltd.
1639+ *
1640+ * This program is free software; you can redistribute it and/or modify
1641+ * it under the terms of the GNU General Public License as published by
1642+ * the Free Software Foundation; version 3.
1643+ *
1644+ * This program is distributed in the hope that it will be useful,
1645+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1646+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1647+ * GNU General Public License for more details.
1648+ *
1649+ * You should have received a copy of the GNU General Public License
1650+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1651+ */
1652+
1653+import QtQuick 2.0
1654+import Ubuntu.Components 1.1
1655+
1656+BaseSurfaceAnimation {
1657+ id: animation
1658+
1659+ property Rectangle darkenItem: Rectangle {
1660+ parent: animation.surface.parent
1661+ anchors.fill: parent
1662+ color: Qt.rgba(0,0,0,0.0)
1663+ }
1664+
1665+ outChanges: [ PropertyChanges { target: animation.surface; opacity: 0.0 } ]
1666+ outAnimations: [
1667+ SequentialAnimation {
1668+ UbuntuNumberAnimation { target: animation.surface; property: "opacity"; duration: UbuntuAnimation.FastDuration }
1669+ ColorAnimation { target: darkenItem; duration: UbuntuAnimation.FastDuration }
1670+ ScriptAction { script: { if (animation.parent.removing) animation.surface.release(); } }
1671+ }
1672+ ]
1673+
1674+ inChanges: [
1675+ PropertyChanges { target: darkenItem; color: Qt.rgba(0,0,0,0.7) },
1676+ PropertyChanges { target: animation.surface; opacity: 1.0 }
1677+ ]
1678+ inAnimations: [
1679+ SequentialAnimation {
1680+ ColorAnimation { target: darkenItem; duration: UbuntuAnimation.FastDuration }
1681+ UbuntuNumberAnimation { target: animation.surface; property: "opacity"; duration: UbuntuAnimation.FastDuration }
1682+ }
1683+ ]
1684+}
1685
1686=== added file 'qml/Stages/Animations/SwipeFromBottomAnimation.qml'
1687--- qml/Stages/Animations/SwipeFromBottomAnimation.qml 1970-01-01 00:00:00 +0000
1688+++ qml/Stages/Animations/SwipeFromBottomAnimation.qml 2014-07-29 14:07:22 +0000
1689@@ -0,0 +1,48 @@
1690+/*
1691+ * Copyright (C) 2014 Canonical, Ltd.
1692+ *
1693+ * This program is free software; you can redistribute it and/or modify
1694+ * it under the terms of the GNU General Public License as published by
1695+ * the Free Software Foundation; version 3.
1696+ *
1697+ * This program is distributed in the hope that it will be useful,
1698+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1699+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1700+ * GNU General Public License for more details.
1701+ *
1702+ * You should have received a copy of the GNU General Public License
1703+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1704+ */
1705+
1706+import QtQuick 2.0
1707+
1708+BaseSurfaceAnimation {
1709+ id: animation
1710+
1711+ outChanges: [ AnchorChanges { target: animation.surface; anchors.top: animation.parent.bottom } ]
1712+ outAnimations: [
1713+ SequentialAnimation {
1714+ PropertyAction { target: animation.parent; property: "clip"; value: true }
1715+ AnchorAnimation { easing.type: Easing.InOutQuad; duration: 400 }
1716+ PropertyAction { target: animation.surface; property: "visible"; value: !animation.parent.removing }
1717+ PropertyAction { target: animation.parent; property: "clip"; value: false }
1718+ ScriptAction { script: { if (animation.parent.removing) animation.surface.release(); } }
1719+ }
1720+ ]
1721+
1722+ inChanges: [
1723+ AnchorChanges {
1724+ target: animation.surface;
1725+ anchors.top: animation.parent.top
1726+ anchors.right: undefined
1727+ anchors.bottom: undefined
1728+ anchors.left: undefined
1729+ } ]
1730+ inAnimations: [
1731+ SequentialAnimation {
1732+ PropertyAction { target: animation.parent; property: "clip"; value: true }
1733+ AnchorAnimation { easing.type: Easing.InOutQuad; duration: 400 }
1734+ PropertyAction { target: animation.parent; property: "clip"; value: false }
1735+ }
1736+ ]
1737+}
1738
1739=== added file 'qml/Stages/Animations/SwipeUpAnimation.qml'
1740--- qml/Stages/Animations/SwipeUpAnimation.qml 1970-01-01 00:00:00 +0000
1741+++ qml/Stages/Animations/SwipeUpAnimation.qml 2014-07-29 14:07:22 +0000
1742@@ -0,0 +1,49 @@
1743+/*
1744+ * Copyright (C) 2014 Canonical, Ltd.
1745+ *
1746+ * This program is free software; you can redistribute it and/or modify
1747+ * it under the terms of the GNU General Public License as published by
1748+ * the Free Software Foundation; version 3.
1749+ *
1750+ * This program is distributed in the hope that it will be useful,
1751+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1752+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1753+ * GNU General Public License for more details.
1754+ *
1755+ * You should have received a copy of the GNU General Public License
1756+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1757+ */
1758+
1759+import QtQuick 2.0
1760+
1761+BaseSurfaceAnimation {
1762+ id: animation
1763+
1764+ outAnimations: [
1765+ SequentialAnimation {
1766+ PropertyAction { target: animation.parent; property: "clip"; value: true }
1767+ PropertyAction { target: animation.surface; property: "visible"; value: true }
1768+ AnchorAnimation { easing.type: Easing.InOutQuad; duration: 400 }
1769+ PropertyAction { target: animation.parent; property: "clip"; value: false }
1770+ ScriptAction { script: { if (animation.parent.removing) animation.surface.release(); } }
1771+ }
1772+ ]
1773+
1774+ inChanges: [
1775+ AnchorChanges {
1776+ target: animation.surface;
1777+ anchors.top: undefined
1778+ anchors.right: undefined
1779+ anchors.bottom: animation.parent.top
1780+ anchors.left: undefined
1781+ }
1782+ ]
1783+ inAnimations: [
1784+ SequentialAnimation {
1785+ PropertyAction { target: animation.parent; property: "clip"; value: true }
1786+ AnchorAnimation { easing.type: Easing.InOutQuad; duration: 400 }
1787+ PropertyAction { target: animation.surface; property: "visible"; value: false}
1788+ PropertyAction { target: animation.parent; property: "clip"; value: false }
1789+ }
1790+ ]
1791+}
1792
1793=== modified file 'qml/Stages/PhoneStage.qml'
1794--- qml/Stages/PhoneStage.qml 2014-06-24 23:52:12 +0000
1795+++ qml/Stages/PhoneStage.qml 2014-07-29 14:07:22 +0000
1796@@ -18,7 +18,6 @@
1797 import Ubuntu.Components 0.1
1798 import Ubuntu.Gestures 0.1
1799 import Unity.Application 0.1
1800-import LightDM 0.1 as LightDM
1801 import Utils 0.1
1802 import "../Components"
1803
1804@@ -26,88 +25,51 @@
1805 id: root
1806
1807 // Controls to be set from outside
1808- property bool shown: false
1809- property bool moving: false
1810 property int dragAreaWidth
1811+ property real maximizedAppTopMargin
1812+ property bool interactive
1813+ property bool spreadEnabled: true // If false, animations and right edge will be disabled
1814
1815 // State information propagated to the outside
1816- readonly property bool painting: mainScreenshotImage.visible || fadeInScreenshotImage.visible || appSplash.visible || spreadView.visible
1817- property bool fullscreen: priv.focusedApplication ? priv.focusedApplication.fullscreen : false
1818- property bool locked: spreadView.visible
1819- property bool spreadEnabled: true
1820-
1821- // Not used for PhoneStage, only useful for SideStage and similar
1822- property bool overlayMode: false
1823- property int overlayWidth: 0
1824+ readonly property bool locked: spreadView.phase == 2
1825
1826 function select(appId) {
1827- spreadView.snapTo(priv.indexOf(appId))
1828+ spreadView.snapTo(priv.indexOf(appId));
1829 }
1830
1831- onMovingChanged: {
1832- if (moving) {
1833- if (ApplicationManager.focusedApplicationId) {
1834- priv.requestNewScreenshot();
1835- } else {
1836- mainScreenshotImage.anchors.leftMargin = 0;
1837- mainScreenshotImage.source = ApplicationManager.get(0).screenshot;
1838- mainScreenshotImage.visible = true;
1839- }
1840- } else {
1841- mainScreenshotImage.visible = false;
1842- }
1843+ onWidthChanged: {
1844+ spreadView.selectedIndex = -1;
1845+ spreadView.phase = 0;
1846+ spreadView.contentX = -spreadView.shift;
1847 }
1848
1849 Connections {
1850 target: ApplicationManager
1851
1852 onFocusRequested: {
1853- if (spreadView.visible) {
1854+ if (spreadView.phase > 0) {
1855 spreadView.snapTo(priv.indexOf(appId));
1856 } else {
1857 priv.switchToApp(appId);
1858 }
1859 }
1860
1861- onFocusedApplicationIdChanged: {
1862- if (ApplicationManager.focusedApplicationId.length > 0) {
1863- if (priv.secondApplicationStarting || priv.applicationStarting) {
1864- appSplashTimer.restart();
1865- } else {
1866- var application = priv.focusedApplication;
1867- root.fullscreen = application.fullscreen;
1868- mainScreenshotImage.source = application.screenshot;
1869- }
1870- } else {
1871- spreadView.selectedIndex = -1;
1872- spreadView.phase = 0;
1873- spreadView.contentX = -spreadView.shift;
1874- }
1875- }
1876-
1877 onApplicationAdded: {
1878- if (!priv.focusedApplication) {
1879- mainScreenshotImage.source = "";
1880- mainScreenshotImage.visible = false;
1881- priv.applicationStarting = true;
1882+ if (spreadView.phase == 2) {
1883+ spreadView.snapTo(ApplicationManager.count - 1);
1884 } else {
1885- mainScreenshotImage.source = "";
1886- priv.newFocusedAppId = appId;
1887- priv.secondApplicationStarting = true;
1888- priv.requestNewScreenshot();
1889- }
1890-
1891- if (spreadView.visible) {
1892- spreadView.snapTo(0);
1893+ spreadView.phase = 0;
1894+ spreadView.contentX = -spreadView.shift;
1895+ priv.switchToApp(appId);
1896 }
1897 }
1898
1899 onApplicationRemoved: {
1900- if (ApplicationManager.count == 0) {
1901- mainScreenshotImage.source = ""
1902- mainScreenshotImage.visible = false;
1903- } else {
1904- mainScreenshotImage.source = ApplicationManager.get(0).screenshot;
1905+ // Unless we're closing the app ourselves in the spread,
1906+ // lets make sure the spread doesn't mess up by the changing app list.
1907+ if (spreadView.closingIndex == -1) {
1908+ spreadView.phase = 0;
1909+ spreadView.contentX = -spreadView.shift;
1910 }
1911 }
1912 }
1913@@ -117,46 +79,11 @@
1914
1915 property string focusedAppId: ApplicationManager.focusedApplicationId
1916 property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
1917- property url focusedScreenshot: focusedApplication ? focusedApplication.screenshot : ""
1918-
1919- property bool waitingForScreenshot: false
1920-
1921- property bool applicationStarting: false
1922- property bool secondApplicationStarting: false
1923-
1924- property string newFocusedAppId
1925-
1926- onFocusedScreenshotChanged: {
1927- if (root.moving && priv.waitingForScreenshot) {
1928- mainScreenshotImage.anchors.leftMargin = 0;
1929- mainScreenshotImage.source = priv.focusedScreenshot
1930- mainScreenshotImage.visible = true;
1931- } else if (priv.secondApplicationStarting && priv.waitingForScreenshot) {
1932- applicationSwitchingAnimation.start();
1933- if (LightDM.Greeter.active && !LightDM.Greeter.promptless) {
1934- // When greeter is up, no fancy animations, since user may
1935- // glimpse an app they shouldn't
1936- applicationSwitchingAnimation.complete();
1937- }
1938- }
1939- waitingForScreenshot = false;
1940- }
1941-
1942- function requestNewScreenshot() {
1943- waitingForScreenshot = true;
1944- ApplicationManager.updateScreenshot(focusedAppId);
1945- }
1946
1947 function switchToApp(appId) {
1948 if (priv.focusedAppId) {
1949- priv.newFocusedAppId = appId;
1950- root.fullscreen = ApplicationManager.findApplication(appId).fullscreen;
1951- applicationSwitchingAnimation.start();
1952- if (LightDM.Greeter.active && !LightDM.Greeter.promptless) {
1953- // When greeter is up, no fancy animations, since user may
1954- // glimpse an app they shouldn't
1955- applicationSwitchingAnimation.complete();
1956- }
1957+ spreadView.focusChanging = true;
1958+ ApplicationManager.focusApplication(appId);
1959 } else {
1960 ApplicationManager.focusApplication(appId);
1961 }
1962@@ -173,158 +100,6 @@
1963
1964 }
1965
1966- // FIXME: the signal connections seems to get lost.
1967- Connections {
1968- target: priv.focusedApplication
1969- onScreenshotChanged: priv.focusedScreenshot = priv.focusedApplication.screenshot
1970- }
1971- Binding {
1972- target: root
1973- property: "fullscreen"
1974- value: priv.focusedApplication ? priv.focusedApplication.fullscreen : false
1975- }
1976-
1977- Timer {
1978- id: appSplashTimer
1979- // FIXME: We really need to show something meaningful in the app surface instead of guessing
1980- // when it might be ready
1981- interval: 500
1982- repeat: false
1983- onTriggered: {
1984- priv.applicationStarting = false;
1985- priv.secondApplicationStarting = false;
1986- }
1987- }
1988-
1989- SequentialAnimation {
1990- id: applicationSwitchingAnimation
1991- // setup
1992- PropertyAction { target: mainScreenshotImage; property: "anchors.leftMargin"; value: 0 }
1993- PropertyAction { target: mainScreenshotImage; property: "source"; value: priv.focusedScreenshot }
1994- PropertyAction { targets: [mainScreenshotImage, fadeInScreenshotImage]; property: "visible"; value: true }
1995- PropertyAction { target: fadeInScreenshotImage; property: "source"; value: {
1996- var newFocusedApp = ApplicationManager.findApplication(priv.newFocusedAppId);
1997- return newFocusedApp ? newFocusedApp.screenshot : "" }
1998- }
1999- PropertyAction { target: fadeInScreenshotImage; property: "opacity"; value: 0 }
2000- PropertyAction { target: fadeInScreenshotImage; property: "scale"; value: .8 }
2001-
2002-
2003- // The actual animation
2004- ParallelAnimation {
2005- UbuntuNumberAnimation { target: mainScreenshotImage; property: "anchors.leftMargin"; to: root.width; duration: UbuntuAnimation.SlowDuration }
2006- UbuntuNumberAnimation { target: fadeInScreenshotImage; properties: "opacity,scale"; to: 1; duration: UbuntuAnimation.SlowDuration }
2007- }
2008-
2009- // restore stuff
2010- ScriptAction { script: ApplicationManager.focusApplication(priv.newFocusedAppId); }
2011- PropertyAction { target: fadeInScreenshotImage; property: "visible"; value: false }
2012- PropertyAction { target: mainScreenshotImage; property: "visible"; value: false }
2013- }
2014-
2015- // FIXME: Drop this and make the imageprovider show a splashscreen instead
2016- Rectangle {
2017- id: appSplash2
2018- anchors.fill: parent
2019- color: "black"
2020- visible: priv.secondApplicationStarting
2021- }
2022- Image {
2023- id: fadeInScreenshotImage
2024- anchors { left: parent.left; bottom: parent.bottom }
2025- width: parent.width
2026- scale: .7
2027- visible: false
2028- }
2029-
2030- Rectangle {
2031- id: appSplash
2032- anchors.fill: parent
2033- color: "black"
2034- visible: priv.applicationStarting
2035-
2036- WaitingDots {
2037- }
2038- }
2039- Image {
2040- id: mainScreenshotImage
2041- anchors { left: parent.left; bottom: parent.bottom }
2042- width: parent.width
2043- visible: false
2044- }
2045-
2046- EdgeDragArea {
2047- id: spreadDragArea
2048- direction: Direction.Leftwards
2049- enabled: ApplicationManager.count > 1 && spreadView.phase != 2 && root.spreadEnabled
2050-
2051- anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
2052- width: root.dragAreaWidth
2053-
2054- // Sitting at the right edge of the screen, this EdgeDragArea directly controls the spreadView when
2055- // attachedToView is true. When the finger movement passes positionMarker3 we detach it from the
2056- // spreadView and make the spreadView snap to positionMarker4.
2057- property bool attachedToView: true
2058-
2059- property var gesturePoints: new Array()
2060-
2061- onTouchXChanged: {
2062- if (!dragging && !priv.waitingForScreenshot) {
2063- // Initial touch. Let's update the screenshot and reset the spreadView to the starting position.
2064- priv.requestNewScreenshot();
2065- spreadView.phase = 0;
2066- spreadView.contentX = -spreadView.shift;
2067- }
2068- if (dragging && attachedToView) {
2069- // Gesture recognized. Let's move the spreadView with the finger
2070- spreadView.contentX = -touchX - spreadView.shift;
2071- }
2072- if (attachedToView && spreadView.shiftedContentX >= spreadView.width * spreadView.positionMarker3) {
2073- // We passed positionMarker3. Detach from spreadView and snap it.
2074- attachedToView = false;
2075- spreadView.snap();
2076- }
2077- gesturePoints.push(touchX);
2078- }
2079-
2080- onStatusChanged: {
2081- if (status == DirectionalDragArea.Recognized) {
2082- attachedToView = true;
2083- }
2084- }
2085-
2086- onDraggingChanged: {
2087- if (dragging) {
2088- // Gesture recognized. Start recording this gesture
2089- gesturePoints = [];
2090- return;
2091- }
2092-
2093- // Ok. The user released. Find out if it was a one-way movement.
2094- var oneWayFlick = true;
2095- var smallestX = spreadDragArea.width;
2096- for (var i = 0; i < gesturePoints.length; i++) {
2097- if (gesturePoints[i] >= smallestX) {
2098- oneWayFlick = false;
2099- break;
2100- }
2101- smallestX = gesturePoints[i];
2102- }
2103- gesturePoints = [];
2104-
2105- if (oneWayFlick && spreadView.shiftedContentX > units.gu(2) &&
2106- spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
2107- // If it was a short one-way movement, do the Alt+Tab switch
2108- // no matter if we didn't cross positionMarker1 yet.
2109- spreadView.snapTo(1);
2110- } else if (!dragging && attachedToView) {
2111- // otherwise snap to the closest snap position we can find
2112- // (might be back to start, to app 1 or to spread)
2113- spreadView.snap();
2114- }
2115- }
2116- }
2117-
2118 Rectangle {
2119 id: coverFlipBackground
2120 anchors.fill: parent
2121@@ -332,16 +107,12 @@
2122 visible: spreadView.visible
2123 }
2124
2125- InputFilterArea {
2126- anchors.fill: root
2127- blockInput: spreadView.visible
2128- }
2129
2130 Flickable {
2131 id: spreadView
2132 objectName: "spreadView"
2133 anchors.fill: parent
2134- visible: spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1 || snapAnimation.running
2135+ interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1) && draggedIndex == -1
2136 contentWidth: spreadRow.width - shift
2137 contentX: -shift
2138
2139@@ -350,8 +121,8 @@
2140 // that, the beginning of the gesture starts with a negative value for contentX
2141 // so the flickable wants to pull it into the view already. "shift" tunes the
2142 // distance where to "lock" the content.
2143- property real shift: width / 2
2144- property real shiftedContentX: contentX + shift
2145+ readonly property real shift: width / 2
2146+ readonly property real shiftedContentX: contentX + shift
2147
2148 property int tileDistance: width / 4
2149
2150@@ -375,6 +146,10 @@
2151 property int phase: 0
2152
2153 property int selectedIndex: -1
2154+ property int draggedIndex: -1
2155+ property int closingIndex: -1
2156+
2157+ property bool focusChanging: false
2158
2159 onShiftedContentXChanged: {
2160 switch (phase) {
2161@@ -398,9 +173,9 @@
2162 snapAnimation.targetContentX = -shift;
2163 snapAnimation.start();
2164 } else if (shiftedContentX < positionMarker2 * width) {
2165- snapTo(1)
2166+ snapTo(1);
2167 } else if (shiftedContentX < positionMarker3 * width) {
2168- snapTo(1)
2169+ snapTo(1);
2170 } else if (phase < 2){
2171 // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation.
2172 snapAnimation.targetContentX = width * positionMarker4 + 1 - shift;
2173@@ -408,9 +183,18 @@
2174 }
2175 }
2176 function snapTo(index) {
2177+ if (ApplicationManager.count <= index) {
2178+ // In case we're trying to snap to some non existing app, lets snap back to the first one
2179+ index = 0;
2180+ }
2181 spreadView.selectedIndex = index;
2182- root.fullscreen = ApplicationManager.get(index).fullscreen;
2183- snapAnimation.targetContentX = -shift;
2184+ // If we're not in full spread mode yet, always unwind to start pos
2185+ // otherwise unwind up to progress 0 of the selected index
2186+ if (spreadView.phase < 2) {
2187+ snapAnimation.targetContentX = -shift;
2188+ } else {
2189+ snapAnimation.targetContentX = -shift + index * spreadView.tileDistance;
2190+ }
2191 snapAnimation.start();
2192 }
2193
2194@@ -429,7 +213,8 @@
2195 script: {
2196 if (spreadView.selectedIndex >= 0) {
2197 ApplicationManager.focusApplication(ApplicationManager.get(spreadView.selectedIndex).appId);
2198- spreadView.selectedIndex = -1
2199+
2200+ spreadView.selectedIndex = -1;
2201 spreadView.phase = 0;
2202 spreadView.contentX = -spreadView.shift;
2203 }
2204@@ -443,6 +228,15 @@
2205 // tileDistance * app count (with a minimum of 3 apps, in order to also allow moving 1 and 2 apps a bit)
2206 // + some constant value (still scales with the screen width) which looks good and somewhat fills the screen
2207 width: Math.max(3, ApplicationManager.count) * spreadView.tileDistance + (spreadView.width - spreadView.tileDistance) * 1.5
2208+ Behavior on width {
2209+ enabled: spreadView.closingIndex >= 0
2210+ UbuntuNumberAnimation {}
2211+ }
2212+ onWidthChanged: {
2213+ if (spreadView.closingIndex >= 0) {
2214+ spreadView.contentX = Math.min(spreadView.contentX, width - spreadView.width - spreadView.shift);
2215+ }
2216+ }
2217
2218 x: spreadView.contentX
2219
2220@@ -462,18 +256,55 @@
2221 height: spreadView.height
2222 selected: spreadView.selectedIndex == index
2223 otherSelected: spreadView.selectedIndex >= 0 && !selected
2224+ interactive: !spreadView.interactive && spreadView.phase === 0
2225+ && spreadView.shiftedContentX === 0 && root.interactive && index === 0
2226+ swipeToCloseEnabled: spreadView.interactive
2227+ maximizedAppTopMargin: root.maximizedAppTopMargin
2228+ dropShadow: spreadView.shiftedContentX > 0 || spreadDragArea.status == DirectionalDragArea.Undecided
2229
2230- z: index
2231+ z: behavioredIndex
2232 x: index == 0 ? 0 : spreadView.width + (index - 1) * spreadView.tileDistance
2233+ property real behavioredIndex: index
2234+ Behavior on behavioredIndex {
2235+ enabled: spreadView.closingIndex >= 0
2236+ UbuntuNumberAnimation {
2237+ onRunningChanged: {
2238+ if (!running) {
2239+ spreadView.closingIndex = -1;
2240+ }
2241+ }
2242+ }
2243+ }
2244+
2245+ Behavior on x {
2246+ enabled: spreadView.focusChanging && index == 0 && root.spreadEnabled
2247+ UbuntuNumberAnimation {
2248+ duration: UbuntuAnimation.FastDuration
2249+ onRunningChanged: {
2250+ if (!running) {
2251+ spreadView.focusChanging = false;
2252+ }
2253+ }
2254+ }
2255+ }
2256
2257 // Each tile has a different progress value running from 0 to 1.
2258- // A progress value of 0 means the tile is at the right edge. 1 means the tile has reched the left edge.
2259+ // 0: means the tile is at the right edge.
2260+ // 1: means the tile has finished the main animation towards the left edge.
2261+ // >1: after the main animation has finished, tiles will continue to move very slowly to the left
2262 progress: {
2263- var tileProgress = (spreadView.shiftedContentX - index * spreadView.tileDistance) / spreadView.width;
2264+ var tileProgress = (spreadView.shiftedContentX - behavioredIndex * spreadView.tileDistance) / spreadView.width;
2265 // Tile 1 needs to move directly from the beginning...
2266- if (index == 1 && spreadView.phase < 2) {
2267+ if (behavioredIndex == 1 && spreadView.phase < 2) {
2268 tileProgress += spreadView.tileDistance / spreadView.width;
2269 }
2270+ // Limiting progress to ~0 and 1.7 to avoid binding calculations when tiles are not
2271+ // visible.
2272+ // < 0 : The tile is outside the screen on the right
2273+ // > 1.7: The tile is *very* close to the left edge and covered by other tiles now.
2274+ // Using 0.0001 to differentiate when a tile should still be visible (==0)
2275+ // or we can hide it (< 0)
2276+ tileProgress = Math.max(-0.0001, Math.min(1.7, tileProgress));
2277 return tileProgress;
2278 }
2279
2280@@ -493,7 +324,7 @@
2281
2282 EasingCurve {
2283 id: snappingCurve
2284- type: EasingCurve.OutQuad
2285+ type: EasingCurve.Linear
2286 period: 0.05
2287 progress: appDelegate.progress - spreadView.positionMarker1
2288 }
2289@@ -507,7 +338,84 @@
2290 }
2291 }
2292 }
2293- }
2294+
2295+ onClosed: {
2296+ spreadView.draggedIndex = -1;
2297+ spreadView.closingIndex = index;
2298+ ApplicationManager.stopApplication(ApplicationManager.get(index).appId);
2299+ }
2300+ }
2301+ }
2302+ }
2303+ }
2304+
2305+ EdgeDragArea {
2306+ id: spreadDragArea
2307+ direction: Direction.Leftwards
2308+ enabled: spreadView.phase != 2 && root.spreadEnabled
2309+
2310+ anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
2311+ width: root.dragAreaWidth
2312+
2313+ // Sitting at the right edge of the screen, this EdgeDragArea directly controls the spreadView when
2314+ // attachedToView is true. When the finger movement passes positionMarker3 we detach it from the
2315+ // spreadView and make the spreadView snap to positionMarker4.
2316+ property bool attachedToView: true
2317+
2318+ property var gesturePoints: new Array()
2319+
2320+ onTouchXChanged: {
2321+ if (!dragging) {
2322+ // Initial touch. Let's reset the spreadView to the starting position.
2323+ spreadView.phase = 0;
2324+ spreadView.contentX = -spreadView.shift;
2325+ }
2326+ if (dragging && attachedToView) {
2327+ // Gesture recognized. Let's move the spreadView with the finger
2328+ spreadView.contentX = -touchX + spreadDragArea.width - spreadView.shift;
2329+ }
2330+ if (attachedToView && spreadView.shiftedContentX >= spreadView.width * spreadView.positionMarker3) {
2331+ // We passed positionMarker3. Detach from spreadView and snap it.
2332+ attachedToView = false;
2333+ spreadView.snap();
2334+ }
2335+ gesturePoints.push(touchX);
2336+ }
2337+
2338+ onStatusChanged: {
2339+ if (status == DirectionalDragArea.Recognized) {
2340+ attachedToView = true;
2341+ }
2342+ }
2343+
2344+ onDraggingChanged: {
2345+ if (dragging) {
2346+ // Gesture recognized. Start recording this gesture
2347+ gesturePoints = [];
2348+ return;
2349+ }
2350+
2351+ // Ok. The user released. Find out if it was a one-way movement.
2352+ var oneWayFlick = true;
2353+ var smallestX = spreadDragArea.width;
2354+ for (var i = 0; i < gesturePoints.length; i++) {
2355+ if (gesturePoints[i] >= smallestX) {
2356+ oneWayFlick = false;
2357+ break;
2358+ }
2359+ smallestX = gesturePoints[i];
2360+ }
2361+ gesturePoints = [];
2362+
2363+ if (oneWayFlick && spreadView.shiftedContentX > units.gu(2) &&
2364+ spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
2365+ // If it was a short one-way movement, do the Alt+Tab switch
2366+ // no matter if we didn't cross positionMarker1 yet.
2367+ spreadView.snapTo(1);
2368+ } else if (!dragging && attachedToView) {
2369+ // otherwise snap to the closest snap position we can find
2370+ // (might be back to start, to app 1 or to spread)
2371+ spreadView.snap();
2372 }
2373 }
2374 }
2375
2376=== removed file 'qml/Stages/SidestageHandle.qml'
2377--- qml/Stages/SidestageHandle.qml 2014-03-04 11:49:28 +0000
2378+++ qml/Stages/SidestageHandle.qml 1970-01-01 00:00:00 +0000
2379@@ -1,22 +0,0 @@
2380-/*
2381- * Copyright (C) 2013 Canonical, Ltd.
2382- *
2383- * This program is free software; you can redistribute it and/or modify
2384- * it under the terms of the GNU General Public License as published by
2385- * the Free Software Foundation; version 3.
2386- *
2387- * This program is distributed in the hope that it will be useful,
2388- * but WITHOUT ANY WARRANTY; without even the implied warranty of
2389- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2390- * GNU General Public License for more details.
2391- *
2392- * You should have received a copy of the GNU General Public License
2393- * along with this program. If not, see <http://www.gnu.org/licenses/>.
2394- */
2395-
2396-import QtQuick 2.0
2397-import Ubuntu.Components 0.1
2398-
2399-BorderImage {
2400- source: "graphics/sidestage_handle.sci"
2401-}
2402
2403=== added file 'qml/Stages/Splash.qml'
2404--- qml/Stages/Splash.qml 1970-01-01 00:00:00 +0000
2405+++ qml/Stages/Splash.qml 2014-07-29 14:07:22 +0000
2406@@ -0,0 +1,68 @@
2407+/*
2408+ * Copyright 2014 Canonical Ltd.
2409+ *
2410+ * This program is free software; you can redistribute it and/or modify
2411+ * it under the terms of the GNU Lesser General Public License as published by
2412+ * the Free Software Foundation; version 3.
2413+ *
2414+ * This program is distributed in the hope that it will be useful,
2415+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2416+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2417+ * GNU Lesser General Public License for more details.
2418+ *
2419+ * You should have received a copy of the GNU Lesser General Public License
2420+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2421+*/
2422+
2423+import QtQuick 2.0
2424+import Ubuntu.Components 0.1
2425+import "../Components"
2426+
2427+Rectangle {
2428+ id: root
2429+ color: "black"
2430+
2431+ property string name: ""
2432+ property url image: ""
2433+
2434+ UbuntuShape {
2435+ id: iconShape
2436+ anchors.horizontalCenter: parent.horizontalCenter
2437+ anchors.verticalCenter: parent.verticalCenter
2438+ anchors.verticalCenterOffset: -units.gu(4)
2439+ width: units.gu(8)
2440+ height: units.gu(7.5)
2441+
2442+ radius: "medium"
2443+ borderSource: "none"
2444+
2445+ image: Image {
2446+ id: iconImage
2447+ sourceSize.width: iconShape.width
2448+ sourceSize.height: iconShape.height
2449+ source: root.image
2450+ fillMode: Image.PreserveAspectCrop
2451+ }
2452+ }
2453+
2454+ Label {
2455+ text: root.name
2456+ anchors.horizontalCenter: parent.horizontalCenter
2457+ anchors.top: iconShape.bottom
2458+ anchors.topMargin: units.gu(2)
2459+ fontSize: "large"
2460+ }
2461+
2462+ WaitingDots {
2463+ visible: parent.visible
2464+ anchors.horizontalCenter: parent.horizontalCenter
2465+ anchors.bottom: parent.bottom
2466+ anchors.bottomMargin: units.gu(12)
2467+ }
2468+
2469+ MouseArea {
2470+ anchors.fill: parent
2471+ enabled: parent.visible
2472+ // absorb all mouse events
2473+ }
2474+}
2475
2476=== modified file 'qml/Stages/SpreadDelegate.qml'
2477--- qml/Stages/SpreadDelegate.qml 2014-03-19 17:29:36 +0000
2478+++ qml/Stages/SpreadDelegate.qml 2014-07-29 14:07:22 +0000
2479@@ -17,39 +17,203 @@
2480 */
2481
2482 import QtQuick 2.0
2483+import Unity.Application 0.1
2484+import Ubuntu.Components 1.1
2485+import "../Components"
2486
2487 Item {
2488 id: root
2489
2490+ // to be set from outside
2491+ property bool interactive: true
2492+ property bool dropShadow: true
2493+ property real maximizedAppTopMargin
2494+ property alias swipeToCloseEnabled: dragArea.enabled
2495+
2496+ readonly property bool isFullscreen: surface !== null && surfaceContainer.surface.anchors.topMargin == 0
2497+
2498 signal clicked()
2499-
2500- property real topMarginProgress
2501-
2502- QtObject {
2503- id: priv
2504- property real heightDifference: root.height - appImage.implicitHeight
2505- }
2506-
2507- Image {
2508- id: dropShadow
2509- anchors.fill: appImage
2510- anchors.margins: -units.gu(2)
2511- source: "graphics/dropshadow.png"
2512- opacity: .4
2513- }
2514- Image {
2515- id: appImage
2516- anchors {
2517- left: parent.left;
2518- bottom: parent.bottom;
2519- top: parent.top;
2520- topMargin: priv.heightDifference * Math.max(0, 1 - root.topMarginProgress)
2521- }
2522- source: model.screenshot
2523- antialiasing: true
2524- }
2525- MouseArea {
2526- anchors.fill: appImage
2527- onClicked: root.clicked()
2528+ signal closed()
2529+
2530+ SurfaceContainer {
2531+ id: surfaceContainer
2532+ anchors.fill: parent
2533+ surface: model.surface
2534+ property bool appHasCreatedASurface: false
2535+
2536+ onSurfaceChanged: {
2537+ if (surface) {
2538+ if (!appHasCreatedASurface) {
2539+ surface.visible = false; // hide until splash screen removed
2540+ appHasCreatedASurface = true;
2541+ }
2542+ }
2543+ }
2544+
2545+ function revealSurface() {
2546+ surface.visible = true;
2547+ splashLoader.source = "";
2548+ }
2549+
2550+ Binding {
2551+ target: surfaceContainer.surface
2552+ property: "anchors.topMargin"
2553+ value: {
2554+ return surfaceContainer.surface.state === MirSurfaceItem.Fullscreen ? 0 : maximizedAppTopMargin;
2555+ }
2556+ }
2557+
2558+ Binding {
2559+ target: surface
2560+ property: "enabled"
2561+ value: root.interactive
2562+ }
2563+ Binding {
2564+ target: surface
2565+ property: "focus"
2566+ value: root.interactive
2567+ }
2568+
2569+ Timer { //FIXME - need to delay removing splash screen to allow surface resize to complete
2570+ id: surfaceRevealDelay
2571+ interval: 100
2572+ onTriggered: surfaceContainer.revealSurface()
2573+ }
2574+
2575+ Connections {
2576+ target: surface
2577+ // FIXME: I would rather not need to do this, but currently it doesn't get
2578+ // active focus without it and I don't know why.
2579+ onFocusChanged: forceSurfaceActiveFocusIfReady();
2580+ onParentChanged: forceSurfaceActiveFocusIfReady();
2581+ onEnabledChanged: forceSurfaceActiveFocusIfReady();
2582+ function forceSurfaceActiveFocusIfReady() {
2583+ if (surface.focus && surface.parent === surfaceContainer && surface.enabled) {
2584+ surface.forceActiveFocus();
2585+ }
2586+ }
2587+ }
2588+
2589+ BorderImage {
2590+ id: dropShadowImage
2591+ anchors {
2592+ fill: parent
2593+ leftMargin: -units.gu(2)
2594+ rightMargin: -units.gu(2)
2595+ bottomMargin: -units.gu(2)
2596+ topMargin: -units.gu(2) + (root.isFullscreen ? 0 : maximizedAppTopMargin)
2597+ }
2598+ source: "graphics/dropshadow.png"
2599+ border { left: 50; right: 50; top: 50; bottom: 50 }
2600+ opacity: root.dropShadow ? .4 : 0
2601+ Behavior on opacity { UbuntuNumberAnimation {} }
2602+ }
2603+
2604+ transform: Translate {
2605+ y: dragArea.distance
2606+ }
2607+ }
2608+
2609+
2610+ StateGroup {
2611+ id: appSurfaceState
2612+ states: [
2613+ State {
2614+ name: "noSurfaceYet"
2615+ when: !surfaceContainer.appHasCreatedASurface
2616+ StateChangeScript {
2617+ script: { splashLoader.setSource("Splash.qml", { "name": model.name, "image": model.icon }); }
2618+ }
2619+ },
2620+ State {
2621+ name: "hasSurface"
2622+ when: surfaceContainer.appHasCreatedASurface && (surfaceContainer.surface !== null)
2623+ StateChangeScript { script: { surfaceRevealDelay.start(); } }
2624+ },
2625+ State {
2626+ name: "surfaceLostButAppStillAlive"
2627+ when: surfaceContainer.appHasCreatedASurface && (surfaceContainer.surface === null)
2628+ // TODO - use app snapshot
2629+ }
2630+ ]
2631+ state: "noSurfaceYet"
2632+ }
2633+
2634+ Loader {
2635+ id: splashLoader
2636+ anchors.fill: surfaceContainer
2637+ }
2638+
2639+ DraggingArea {
2640+ id: dragArea
2641+ anchors.fill: parent
2642+
2643+ property bool moving: false
2644+ property real distance: 0
2645+
2646+ onMovingChanged: {
2647+ spreadView.draggedIndex = moving ? index : -1
2648+ }
2649+
2650+ onDragValueChanged: {
2651+ if (!dragging) {
2652+ return;
2653+ }
2654+ moving = moving || Math.abs(dragValue) > units.gu(1)
2655+ if (moving) {
2656+ distance = dragValue;
2657+ }
2658+ }
2659+
2660+ onClicked: {
2661+ if (!moving) {
2662+ root.clicked();
2663+ }
2664+ }
2665+
2666+ onDragEnd: {
2667+ // velocity and distance values specified by design prototype
2668+ if ((dragVelocity < -units.gu(40) && distance < -units.gu(8)) || distance < -root.height / 2) {
2669+ animation.animate("up")
2670+ } else if ((dragVelocity > units.gu(40) && distance > units.gu(8)) || distance > root.height / 2) {
2671+ animation.animate("down")
2672+ } else {
2673+ animation.animate("center")
2674+ }
2675+ }
2676+
2677+ UbuntuNumberAnimation {
2678+ id: animation
2679+ target: dragArea
2680+ property: "distance"
2681+ property bool requestClose: false
2682+
2683+ function animate(direction) {
2684+ animation.from = dragArea.distance;
2685+ switch (direction) {
2686+ case "up":
2687+ animation.to = -root.height * 1.5;
2688+ requestClose = true;
2689+ break;
2690+ case "down":
2691+ animation.to = root.height * 1.5;
2692+ requestClose = true;
2693+ break;
2694+ default:
2695+ animation.to = 0
2696+ }
2697+ animation.start();
2698+ }
2699+
2700+ onRunningChanged: {
2701+ if (!running) {
2702+ dragArea.moving = false;
2703+ dragArea.distance = 0;
2704+ if (requestClose) {
2705+ root.closed();
2706+ }
2707+ }
2708+ }
2709+ }
2710 }
2711 }
2712
2713=== removed file 'qml/Stages/StageWithSideStage.qml'
2714--- qml/Stages/StageWithSideStage.qml 2014-05-26 14:12:20 +0000
2715+++ qml/Stages/StageWithSideStage.qml 1970-01-01 00:00:00 +0000
2716@@ -1,413 +0,0 @@
2717-/*
2718- * Copyright (C) 2014 Canonical, Ltd.
2719- *
2720- * This program is free software; you can redistribute it and/or modify
2721- * it under the terms of the GNU General Public License as published by
2722- * the Free Software Foundation; version 3.
2723- *
2724- * This program is distributed in the hope that it will be useful,
2725- * but WITHOUT ANY WARRANTY; without even the implied warranty of
2726- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2727- * GNU General Public License for more details.
2728- *
2729- * You should have received a copy of the GNU General Public License
2730- * along with this program. If not, see <http://www.gnu.org/licenses/>.
2731- */
2732-
2733-import QtQuick 2.0
2734-import Ubuntu.Components 0.1
2735-import "../Components"
2736-import Unity.Application 0.1
2737-import Ubuntu.Gestures 0.1
2738-
2739-Item {
2740- id: root
2741- objectName: "stages"
2742- anchors.fill: parent
2743-
2744- // Controls to be set from outside
2745- property bool shown: false
2746- property bool moving: false
2747- property int dragAreaWidth
2748-
2749- // State information propagated to the outside
2750- readonly property bool painting: mainStageImage.visible || sideStageImage.visible || sideStageSnapAnimation.running
2751- property bool fullscreen: priv.focusedApplication ? priv.focusedApplication.fullscreen : false
2752- property bool overlayMode: (sideStageImage.shown && priv.mainStageAppId.length == 0) || priv.overlayOverride
2753- || (priv.mainStageAppId.length == 0 && sideStageSnapAnimation.running)
2754-
2755- readonly property int overlayWidth: priv.overlayOverride ? 0 : priv.sideStageWidth
2756-
2757- onShownChanged: {
2758- if (!shown) {
2759- priv.mainStageAppId = "";
2760- }
2761- }
2762-
2763- onMovingChanged: {
2764- if (moving) {
2765- if (!priv.mainStageAppId && !priv.sideStageAppId) {
2766- // Pulling in from the right, make the last used (topmost) app visible
2767- var application = ApplicationManager.get(0);
2768- if (application.stage == ApplicationInfoInterface.SideStage) {
2769- sideStageImage.application = application;
2770- sideStageImage.x = root.width - sideStageImage.width
2771- sideStageImage.visible = true;
2772- } else {
2773- mainStageImage.application = application;
2774- mainStageImage.visible = true;
2775- }
2776- } else {
2777- priv.requestNewScreenshot(ApplicationInfoInterface.MainStage)
2778- if (priv.focusedApplicationId == priv.sideStageAppId) {
2779- priv.requestNewScreenshot(ApplicationInfoInterface.SideStage)
2780- }
2781- }
2782- } else {
2783- mainStageImage.visible = false;
2784- sideStageImage.visible = false;
2785- }
2786- }
2787-
2788- QtObject {
2789- id: priv
2790-
2791- property int sideStageWidth: units.gu(40)
2792-
2793-
2794- property string sideStageAppId
2795- property string mainStageAppId
2796-
2797-
2798- property var sideStageApp: ApplicationManager.findApplication(sideStageAppId)
2799- property var mainStageApp: ApplicationManager.findApplication(mainStageAppId)
2800-
2801- property string sideStageScreenshot: sideStageApp ? sideStageApp.screenshot : ""
2802- property string mainStageScreenshot: mainStageApp ? mainStageApp.screenshot : ""
2803-
2804- property string focusedApplicationId: ApplicationManager.focusedApplicationId
2805- property var focusedApplication: ApplicationManager.findApplication(focusedApplicationId)
2806- property url focusedScreenshot: focusedApplication ? focusedApplication.screenshot : ""
2807-
2808- property bool waitingForMainScreenshot: false
2809- property bool waitingForSideScreenshot: false
2810- property bool waitingForScreenshots: waitingForMainScreenshot || waitingForSideScreenshot
2811-
2812- property string startingAppId: ""
2813-
2814- // Keep overlayMode even if there is no focused app (to allow pulling in the sidestage from the right)
2815- property bool overlayOverride: false
2816-
2817- onFocusedApplicationChanged: {
2818- if (focusedApplication) {
2819- if (focusedApplication.stage == ApplicationInfoInterface.MainStage) {
2820- mainStageAppId = focusedApplicationId;
2821- priv.overlayOverride = false;
2822- if (priv.startingAppId == focusedApplicationId && sideStageImage.shown) {
2823- // There was already a sidestage app on top. bring it back!
2824- ApplicationManager.focusApplication(priv.sideStageAppId)
2825- priv.startingAppId = "";
2826- }
2827- } else if (focusedApplication.stage == ApplicationInfoInterface.SideStage) {
2828- sideStageAppId = focusedApplicationId;
2829- if (priv.startingAppId == focusedApplicationId && !sideStageImage.shown) {
2830- sideStageImage.snapToApp(focusedApplication);
2831- priv.startingAppId = "";
2832- }
2833- }
2834- } else if (root.overlayMode){
2835- sideStageImage.snapTo(root.width)
2836- }
2837- }
2838-
2839- onMainStageScreenshotChanged: {
2840- waitingForMainScreenshot = false;
2841- }
2842-
2843- onSideStageScreenshotChanged: {
2844- waitingForSideScreenshot = false;
2845- }
2846-
2847- onFocusedScreenshotChanged: {
2848- waitingForSideScreenshot = false;
2849- }
2850-
2851- onWaitingForScreenshotsChanged: {
2852- if (waitingForScreenshots) {
2853- return;
2854- }
2855-
2856- if (root.moving) {
2857- if (mainStageAppId) {
2858- mainStageImage.application = mainStageApp
2859- mainStageImage.visible = true;
2860- }
2861- if (sideStageAppId && focusedApplicationId == sideStageAppId) {
2862- sideStageImage.application = sideStageApp;
2863- sideStageImage.x = root.width - sideStageImage.width
2864- sideStageImage.visible = true;
2865- }
2866- }
2867- if (sideStageHandleMouseArea.pressed) {
2868- if (sideStageAppId) {
2869- sideStageImage.application = sideStageApp;
2870- sideStageImage.x = root.width - sideStageImage.width
2871- sideStageImage.visible = true;
2872- }
2873- if (mainStageAppId) {
2874- mainStageImage.application = mainStageApp
2875- mainStageImage.visible = true;
2876- }
2877- }
2878- }
2879-
2880- function requestNewScreenshot(stage) {
2881- if (stage == ApplicationInfoInterface.MainStage && mainStageAppId) {
2882- waitingForMainScreenshot = true;
2883- ApplicationManager.updateScreenshot(mainStageAppId);
2884- } else if (stage == ApplicationInfoInterface.SideStage && sideStageAppId) {
2885- waitingForSideScreenshot = true;
2886- ApplicationManager.updateScreenshot(sideStageAppId);
2887- }
2888- }
2889-
2890- }
2891- // FIXME: the signal connection seems to get lost with the fake application manager.
2892- Connections {
2893- target: priv.sideStageApp
2894- onScreenshotChanged: priv.sideStageScreenshot = priv.sideStageApp.screenshot
2895- }
2896- Connections {
2897- target: priv.mainStageApp
2898- onScreenshotChanged: priv.mainStageScreenshot = priv.mainStageApp.screenshot
2899- }
2900-
2901- Connections {
2902- target: ApplicationManager
2903-
2904- onApplicationAdded: {
2905- priv.startingAppId = appId;
2906- splashScreenTimer.start();
2907- var application = ApplicationManager.findApplication(appId)
2908- if (application.stage == ApplicationInfoInterface.SideStage) {
2909- sideStageSplash.visible = true;
2910- } else if (application.stage == ApplicationInfoInterface.MainStage) {
2911- mainStageSplash.visible = true;
2912- }
2913- }
2914-
2915- onFocusRequested: {
2916- var application = ApplicationManager.findApplication(appId)
2917- if (application.stage == ApplicationInfoInterface.SideStage) {
2918- if (!root.shown) {
2919- priv.mainStageAppId = "";
2920- mainStageImage.application = null
2921- }
2922- if (sideStageImage.shown) {
2923- sideStageImage.switchTo(application);
2924- if (priv.mainStageAppId) {
2925- mainStageImage.application = priv.mainStageApp;
2926- mainStageImage.visible = true;
2927- }
2928- } else {
2929- sideStageImage.application = application;
2930- sideStageImage.snapToApp(application);
2931- }
2932- } else if (application.stage == ApplicationInfoInterface.MainStage) {
2933- if (root.shown) {
2934- if (sideStageImage.shown) {
2935- sideStageImage.application = priv.sideStageApp;
2936- sideStageImage.visible = true;
2937- }
2938- priv.mainStageAppId = application.appId;
2939- mainStageImage.switchTo(application)
2940- ApplicationManager.focusApplication(appId)
2941- if (sideStageImage.shown) {
2942- // There was already a focused SS app. Bring it back
2943- ApplicationManager.focusApplication(priv.sideStageAppId)
2944- }
2945- } else {
2946- if (sideStageImage.shown) {
2947- sideStageImage.visible = false;
2948- sideStageImage.x = root.width;
2949- }
2950-
2951- mainStageImage.application = application;
2952- ApplicationManager.focusApplication(appId)
2953- }
2954- }
2955- }
2956-
2957- onApplicationRemoved: {
2958- if (priv.mainStageAppId == appId) {
2959- priv.mainStageAppId = "";
2960- }
2961- if (priv.sideStageAppId == appId) {
2962- priv.sideStageAppId = "";
2963- }
2964- if (priv.sideStageAppId.length == 0) {
2965- sideStageImage.shown = false;
2966- priv.overlayOverride = false;
2967- }
2968- }
2969-
2970- }
2971-
2972- Timer {
2973- id: splashScreenTimer
2974- // FIXME: apart from removing this completely in the future and make the app surface paint
2975- // meaningful stuff, also check for colin's stuff to land so we can shape 1.4 secs away from here
2976- // https://code.launchpad.net/~cjwatson/upstart-app-launch/libclick-manifest/+merge/210520
2977- // https://code.launchpad.net/~cjwatson/upstart-app-launch/libclick-pkgdir/+merge/209909
2978- interval: 1700
2979- repeat: false
2980- onTriggered: {
2981- mainStageSplash.visible = false;
2982- sideStageSplash.visible = false;
2983- }
2984- }
2985-
2986- SwitchingApplicationImage {
2987- id: mainStageImage
2988- anchors.bottom: parent.bottom
2989- width: parent.width
2990- visible: false
2991-
2992- onSwitched: {
2993- sideStageImage.visible = false;
2994- }
2995- }
2996-
2997- Rectangle {
2998- id: mainStageSplash
2999- anchors.fill: root
3000- anchors.rightMargin: root.width - sideStageImage.x
3001- color: "black"
3002-
3003- WaitingDots {
3004- }
3005- }
3006-
3007- SidestageHandle {
3008- id: sideStageHandle
3009- anchors { top: parent.top; right: sideStageImage.left; bottom: parent.bottom }
3010- width: root.dragAreaWidth
3011- visible: root.shown && priv.sideStageAppId && sideStageImage.x < root.width
3012-
3013- }
3014- MouseArea {
3015- id: sideStageHandleMouseArea
3016- anchors { top: parent.top; right: parent.right; bottom: parent.bottom; rightMargin: sideStageImage.shown ? sideStageImage.width : 0}
3017- width: root.dragAreaWidth
3018- visible: priv.sideStageAppId
3019-
3020- property var dragPoints: new Array()
3021-
3022- onPressed: {
3023- priv.requestNewScreenshot(ApplicationInfoInterface.SideStage)
3024- if (priv.mainStageAppId) {
3025- priv.requestNewScreenshot(ApplicationInfoInterface.MainStage)
3026- }
3027- }
3028-
3029- onMouseXChanged: {
3030- dragPoints.push(mouseX)
3031-
3032- var dragPoint = root.width + mouseX;
3033- if (sideStageImage.shown) {
3034- dragPoint -= sideStageImage.width
3035- }
3036- sideStageImage.x = Math.max(root.width - sideStageImage.width, dragPoint)
3037- }
3038-
3039- onReleased: {
3040- var distance = 0;
3041- var lastX = dragPoints[0];
3042- var oneWayFlick = true;
3043- for (var i = 0; i < dragPoints.length; ++i) {
3044- if (dragPoints[i] < lastX) {
3045- oneWayFlick = false;
3046- }
3047- distance += dragPoints[i] - lastX;
3048- lastX = dragPoints[i];
3049- }
3050- dragPoints = [];
3051-
3052- if (oneWayFlick || distance > sideStageImage.width / 2) {
3053- sideStageImage.snapTo(root.width)
3054- } else {
3055- sideStageImage.snapToApp(priv.sideStageApp)
3056- }
3057- }
3058- }
3059-
3060- SwitchingApplicationImage {
3061- id: sideStageImage
3062- width: priv.sideStageWidth
3063- height: root.height
3064- x: root.width
3065- anchors.bottom: parent.bottom
3066- visible: true
3067- property bool shown: false
3068-
3069- onSwitched: {
3070- mainStageImage.visible = false;
3071- ApplicationManager.focusApplication(application.appId)
3072- }
3073-
3074- function snapTo(targetX) {
3075- sideStageSnapAnimation.targetX = targetX
3076- sideStageImage.visible = true;
3077- if (priv.mainStageAppId) {
3078- mainStageImage.application = priv.mainStageApp
3079- mainStageImage.visible = true;
3080- }
3081- sideStageSnapAnimation.start();
3082- }
3083-
3084- function snapToApp(application) {
3085- sideStageImage.application = application
3086- sideStageSnapAnimation.snapToId = application.appId;
3087- snapTo(root.width - sideStageImage.width);
3088- }
3089-
3090- SequentialAnimation {
3091- id: sideStageSnapAnimation
3092- property int targetX: root.width
3093- property string snapToId
3094-
3095- UbuntuNumberAnimation { target: sideStageImage; property: "x"; to: sideStageSnapAnimation.targetX; duration: UbuntuAnimation.SlowDuration }
3096- ScriptAction {
3097- script: {
3098- if (sideStageSnapAnimation.targetX == root.width) {
3099- if (priv.mainStageAppId) {
3100- ApplicationManager.focusApplication(priv.mainStageAppId)
3101- } else {
3102- priv.overlayOverride = true;
3103- ApplicationManager.unfocusCurrentApplication();
3104- }
3105- sideStageImage.shown = false;
3106- }
3107- if (sideStageSnapAnimation.snapToId) {
3108- ApplicationManager.focusApplication(sideStageSnapAnimation.snapToId)
3109- sideStageSnapAnimation.snapToId = "";
3110- sideStageImage.shown = true;
3111- priv.overlayOverride = false;
3112- }
3113- sideStageImage.visible = false;
3114- mainStageImage.visible = false;
3115- }
3116- }
3117- }
3118- }
3119-
3120- Rectangle {
3121- id: sideStageSplash
3122- anchors.fill: parent
3123- anchors.leftMargin: sideStageImage.x
3124- color: "black"
3125-
3126- WaitingDots {
3127- }
3128- }
3129-}
3130
3131=== added file 'qml/Stages/SurfaceContainer.qml'
3132--- qml/Stages/SurfaceContainer.qml 1970-01-01 00:00:00 +0000
3133+++ qml/Stages/SurfaceContainer.qml 2014-07-29 14:07:22 +0000
3134@@ -0,0 +1,123 @@
3135+/*
3136+ * Copyright 2014 Canonical Ltd.
3137+ *
3138+ * This program is free software; you can redistribute it and/or modify
3139+ * it under the terms of the GNU Lesser General Public License as published by
3140+ * the Free Software Foundation; version 3.
3141+ *
3142+ * This program is distributed in the hope that it will be useful,
3143+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3144+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3145+ * GNU Lesser General Public License for more details.
3146+ *
3147+ * You should have received a copy of the GNU Lesser General Public License
3148+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3149+*/
3150+
3151+import QtQuick 2.0
3152+import "Animations"
3153+
3154+Item {
3155+ id: container
3156+ property Item surface: null
3157+ property bool removing: false
3158+
3159+ onSurfaceChanged: {
3160+ if (surface) {
3161+ surface.parent = container;
3162+ surface.z = 1;
3163+ state = "initial"
3164+ }
3165+ }
3166+
3167+ Connections {
3168+ target: surface
3169+ onRemoved: {
3170+ container.removing = true;
3171+
3172+ var childSurfaces = surface.childSurfaces;
3173+ for (var i=0; i<childSurfaces.length; i++) {
3174+ childSurfaces[i].removed();
3175+ }
3176+
3177+ //if we don't have children, nothing will tell us to animate out, so do it.
3178+ if (childSurfaces.length === 0) {
3179+ animateOut();
3180+ }
3181+ // tell our parent to animate out.
3182+ if (surface.parentSurface) {
3183+ surface.parentSurface.parent.animateOut();
3184+ }
3185+ }
3186+ }
3187+
3188+ Repeater {
3189+ model: container.surface ? container.surface.childSurfaces : 0
3190+
3191+ delegate: Loader {
3192+ z: 2
3193+ anchors {
3194+ fill: container
3195+ topMargin: container.surface.anchors.topMargin
3196+ rightMargin: container.surface.anchors.rightMargin
3197+ bottomMargin: container.surface.anchors.bottomMargin
3198+ leftMargin: container.surface.anchors.leftMargin
3199+ }
3200+
3201+ // Only way to do recursive qml items.
3202+ source: Qt.resolvedUrl("SurfaceContainer.qml")
3203+ onLoaded: {
3204+ item.surface = modelData;
3205+ item.animateIn(swipeFromBottom);
3206+ container.animateIn(swipeUp);
3207+ }
3208+ }
3209+ }
3210+
3211+ function animateIn(component) {
3212+ var animation = component.createObject(container, { "surface": container.surface });
3213+ animation.start();
3214+
3215+ var tmp = d.animations;
3216+ tmp.push(animation);
3217+ d.animations = tmp;
3218+ }
3219+
3220+ function animateOut() {
3221+ if (d.animations.length > 0) {
3222+ var tmp = d.animations;
3223+ var popped = tmp.pop();
3224+ popped.end();
3225+ d.animations = tmp;
3226+ } else {
3227+ container.state = "initial";
3228+ }
3229+ }
3230+
3231+ QtObject {
3232+ id: d
3233+ property var animations: []
3234+ property var currentAnimation: animations.length > 0 ? animations[animations.length-1] : undefined
3235+ }
3236+
3237+ Component {
3238+ id: swipeFromBottom
3239+ SwipeFromBottomAnimation {}
3240+ }
3241+ Component {
3242+ id: swipeUp
3243+ SwipeUpAnimation {}
3244+ }
3245+ Component {
3246+ id: darkenFade
3247+ DarkenAndFadeInAnimation {}
3248+ }
3249+
3250+ states: [
3251+ State {
3252+ name: "initial"
3253+ PropertyChanges { target: surface; anchors.fill: container }
3254+ }
3255+ // TODO: more animations!
3256+ ]
3257+}
3258
3259=== removed file 'qml/Stages/SwitchingApplicationImage.qml'
3260--- qml/Stages/SwitchingApplicationImage.qml 2014-03-19 18:09:27 +0000
3261+++ qml/Stages/SwitchingApplicationImage.qml 1970-01-01 00:00:00 +0000
3262@@ -1,81 +0,0 @@
3263-/*
3264- * Copyright 2014 Canonical Ltd.
3265- *
3266- * This program is free software; you can redistribute it and/or modify
3267- * it under the terms of the GNU Lesser General Public License as published by
3268- * the Free Software Foundation; version 3.
3269- *
3270- * This program is distributed in the hope that it will be useful,
3271- * but WITHOUT ANY WARRANTY; without even the implied warranty of
3272- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3273- * GNU Lesser General Public License for more details.
3274- *
3275- * You should have received a copy of the GNU Lesser General Public License
3276- * along with this program. If not, see <http://www.gnu.org/licenses/>.
3277- *
3278- * Authors: Michael Zanetti <michael.zanetti@canonical.com>
3279-*/
3280-
3281-import QtQuick 2.0
3282-import Ubuntu.Components 0.1
3283-
3284-Rectangle {
3285- id: root
3286- implicitHeight: image.implicitHeight
3287- implicitWidth: image.implicitWidth
3288- color: "black"
3289-
3290- property var application
3291-
3292- signal switched()
3293-
3294- function switchTo(application) {
3295- if (root.application == application) {
3296- root.switched();
3297- return;
3298- }
3299-
3300- priv.newApplication = application
3301- root.visible = true;
3302- switchToAnimation.start()
3303- }
3304-
3305- QtObject {
3306- id: priv
3307- property var newApplication
3308- }
3309-
3310- Image {
3311- id: newImage
3312- anchors.bottom: parent.bottom
3313- width: root.width
3314- source: priv.newApplication ? priv.newApplication.screenshot : ""
3315- }
3316-
3317- Image {
3318- id: image
3319- visible: true
3320- source: root.application ? root.application.screenshot : ""
3321- width: root.width
3322- height: sourceSize.height
3323- anchors.bottom: parent.bottom
3324-
3325- }
3326-
3327- SequentialAnimation {
3328- id: switchToAnimation
3329- ParallelAnimation {
3330- UbuntuNumberAnimation { target: image; property: "x"; from: 0; to: root.width; duration: UbuntuAnimation.SlowDuration }
3331- UbuntuNumberAnimation { target: newImage; property: "scale"; from: 0.7; to: 1; duration: UbuntuAnimation.SlowDuration }
3332- }
3333- ScriptAction {
3334- script: {
3335- image.x = 0
3336- root.application = priv.newApplication
3337- root.visible = false;
3338- priv.newApplication = null
3339- root.switched();
3340- }
3341- }
3342- }
3343-}
3344
3345=== added file 'qml/Stages/TabletStage.qml'
3346--- qml/Stages/TabletStage.qml 1970-01-01 00:00:00 +0000
3347+++ qml/Stages/TabletStage.qml 2014-07-29 14:07:22 +0000
3348@@ -0,0 +1,568 @@
3349+/*
3350+ * Copyright (C) 2014 Canonical, Ltd.
3351+ *
3352+ * This program is free software; you can redistribute it and/or modify
3353+ * it under the terms of the GNU General Public License as published by
3354+ * the Free Software Foundation; version 3.
3355+ *
3356+ * This program is distributed in the hope that it will be useful,
3357+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3358+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3359+ * GNU General Public License for more details.
3360+ *
3361+ * You should have received a copy of the GNU General Public License
3362+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3363+ */
3364+
3365+import QtQuick 2.0
3366+import Ubuntu.Components 0.1
3367+import Ubuntu.Gestures 0.1
3368+import Unity.Application 0.1
3369+import Utils 0.1
3370+import "../Components"
3371+
3372+Item {
3373+ id: root
3374+ objectName: "stages"
3375+ anchors.fill: parent
3376+
3377+ // Controls to be set from outside
3378+ property bool shown: false
3379+ property bool moving: false
3380+ property int dragAreaWidth
3381+ property real maximizedAppTopMargin
3382+ property bool interactive
3383+
3384+ // State information propagated to the outside
3385+ readonly property bool locked: spreadView.phase == 2
3386+
3387+ QtObject {
3388+ id: priv
3389+
3390+ property string focusedAppId: ApplicationManager.focusedApplicationId
3391+ property string oldFocusedAppId: ""
3392+
3393+ property string mainStageAppId
3394+ property string sideStageAppId
3395+
3396+ // For convenience, keep properties of the first two apps in the model
3397+ property string appId0
3398+ property string appId1
3399+
3400+ onFocusedAppIdChanged: {
3401+ if (priv.focusedAppId.length > 0) {
3402+ var focusedApp = ApplicationManager.findApplication(focusedAppId);
3403+ if (focusedApp.stage == ApplicationInfoInterface.SideStage) {
3404+ priv.sideStageAppId = focusedAppId;
3405+ } else {
3406+ priv.mainStageAppId = focusedAppId;
3407+ }
3408+ }
3409+
3410+ appId0 = ApplicationManager.count >= 1 ? ApplicationManager.get(0).appId : "";
3411+ appId1 = ApplicationManager.count > 1 ? ApplicationManager.get(1).appId : "";
3412+ }
3413+
3414+ function indexOf(appId) {
3415+ for (var i = 0; i < ApplicationManager.count; i++) {
3416+ if (ApplicationManager.get(i).appId == appId) {
3417+ return i;
3418+ }
3419+ }
3420+ return -1;
3421+ }
3422+
3423+ function evaluateOneWayFlick(gesturePoints) {
3424+ // Need to have at least 3 points to recognize it as a flick
3425+ if (gesturePoints.length < 3) {
3426+ return false;
3427+ }
3428+ // Need to have a movement of at least 2 grid units to recognize it as a flick
3429+ if (Math.abs(gesturePoints[gesturePoints.length - 1] - gesturePoints[0]) < units.gu(2)) {
3430+ return false;
3431+ }
3432+
3433+ var oneWayFlick = true;
3434+ var smallestX = gesturePoints[0];
3435+ var leftWards = gesturePoints[1] < gesturePoints[0];
3436+ for (var i = 1; i < gesturePoints.length; i++) {
3437+ if ((leftWards && gesturePoints[i] >= smallestX)
3438+ || (!leftWards && gesturePoints[i] <= smallestX)) {
3439+ oneWayFlick = false;
3440+ break;
3441+ }
3442+ smallestX = gesturePoints[i];
3443+ }
3444+ return oneWayFlick;
3445+ }
3446+ }
3447+
3448+ Connections {
3449+ target: ApplicationManager
3450+ onFocusRequested: {
3451+ if (spreadView.interactive) {
3452+ spreadView.snapTo(priv.indexOf(appId));
3453+ } else {
3454+ ApplicationManager.focusApplication(appId);
3455+ }
3456+ }
3457+
3458+ onApplicationRemoved: {
3459+ if (priv.mainStageAppId == appId) {
3460+ priv.mainStageAppId = "";
3461+ }
3462+ if (priv.sideStageAppId == appId) {
3463+ priv.sideStageAppId = "";
3464+ }
3465+ if (ApplicationManager.count == 0) {
3466+ spreadView.phase = 0;
3467+ spreadView.contentX = 0;
3468+ }
3469+ }
3470+ }
3471+
3472+ Flickable {
3473+ id: spreadView
3474+ anchors.fill: parent
3475+ contentWidth: spreadRow.width
3476+ interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1) && draggedIndex == -1
3477+
3478+ property int tileDistance: units.gu(20)
3479+ property int sideStageWidth: units.gu(40)
3480+ property bool sideStageVisible: priv.sideStageAppId
3481+
3482+ // Phase of the animation:
3483+ // 0: Starting from right edge, a new app (index 1) comes in from the right
3484+ // 1: The app has reached the first snap position.
3485+ // 2: The list is dragged further and snaps into the spread view when entering phase 2
3486+ property int phase
3487+
3488+ readonly property int phase0Width: sideStageWidth
3489+ readonly property int phase1Width: sideStageWidth
3490+
3491+ // Those markers mark the various positions in the spread (ratio to screen width from right to left):
3492+ // 0 - 1: following finger, snap back to the beginning on release
3493+ readonly property real positionMarker1: 0.2
3494+ // 1 - 2: curved snapping movement, snap to nextInStack on release
3495+ readonly property real positionMarker2: sideStageWidth / spreadView.width
3496+ // 2 - 3: movement follows finger, snaps to phase 2 (full spread) on release
3497+ readonly property real positionMarker3: 0.6
3498+ // passing 3, we detach movement from the finger and snap to phase 2 (full spread)
3499+ readonly property real positionMarker4: 0.8
3500+
3501+ readonly property int startSnapPosition: phase0Width * 0.5
3502+ readonly property int endSnapPosition: phase0Width * 0.75
3503+ readonly property real snapPosition: 0.75
3504+
3505+ property int selectedIndex: -1
3506+ property int draggedIndex: -1
3507+ property int closingIndex: -1
3508+
3509+ property bool sideStageDragging: sideStageDragHandle.dragging
3510+ property real sideStageDragProgress: sideStageDragHandle.progress
3511+
3512+ onSideStageDragProgressChanged: {
3513+ if (sideStageDragProgress == 1) {
3514+ ApplicationManager.focusApplication(priv.mainStageAppId);
3515+ priv.sideStageAppId = "";
3516+ }
3517+ }
3518+
3519+ property int nextInStack: {
3520+ switch (state) {
3521+ case "main":
3522+ if (ApplicationManager.count > 1) {
3523+ return 1;
3524+ }
3525+ return -1;
3526+ case "mainAndOverlay":
3527+ if (ApplicationManager.count <= 2) {
3528+ return -1;
3529+ }
3530+ if (priv.appId0 == priv.mainStageAppId || priv.appId0 == priv.sideStageAppId) {
3531+ if (priv.appId1 == priv.mainStageAppId || priv.appId1 == priv.sideStageAppId) {
3532+ return 2;
3533+ }
3534+ return 1;
3535+ }
3536+ return 0;
3537+ case "overlay":
3538+ return 1;
3539+ }
3540+ print("Unhandled nextInStack case! This shouldn't happen any more when the Dash is an app!");
3541+ return -1;
3542+ }
3543+ property int nextZInStack: indexToZIndex(nextInStack)
3544+
3545+ states: [
3546+ State {
3547+ name: "empty"
3548+ },
3549+ State {
3550+ name: "main"
3551+ },
3552+ State { // Side Stage only in overlay mode
3553+ name: "overlay"
3554+ },
3555+ State { // Main Stage and Side Stage in overlay mode
3556+ name: "mainAndOverlay"
3557+ },
3558+ State { // Main Stage and Side Stage in split mode
3559+ name: "mainAndSplit"
3560+ }
3561+ ]
3562+ state: {
3563+ if (priv.mainStageAppId && !priv.sideStageAppId) {
3564+ return "main";
3565+ }
3566+ if (!priv.mainStageAppId && priv.sideStageAppId) {
3567+ return "overlay";
3568+ }
3569+ if (priv.mainStageAppId && priv.sideStageAppId) {
3570+ return "mainAndOverlay";
3571+ }
3572+ return "empty";
3573+ }
3574+
3575+ onContentXChanged: {
3576+ if (spreadView.phase == 0 && spreadView.contentX > spreadView.width * spreadView.positionMarker2) {
3577+ spreadView.phase = 1;
3578+ } else if (spreadView.phase == 1 && spreadView.contentX > spreadView.width * spreadView.positionMarker4) {
3579+ spreadView.phase = 2;
3580+ } else if (spreadView.phase == 1 && spreadView.contentX < spreadView.width * spreadView.positionMarker2) {
3581+ spreadView.phase = 0;
3582+ }
3583+ }
3584+
3585+ function snap() {
3586+ if (contentX < phase0Width) {
3587+ snapAnimation.targetContentX = 0;
3588+ snapAnimation.start();
3589+ } else if (contentX < phase1Width) {
3590+ snapTo(1);
3591+ } else {
3592+ // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation.
3593+ snapAnimation.targetContentX = spreadView.width * spreadView.positionMarker4 + 1;
3594+ snapAnimation.start();
3595+ }
3596+ }
3597+ function snapTo(index) {
3598+ spreadView.selectedIndex = index;
3599+ snapAnimation.targetContentX = 0;
3600+ snapAnimation.start();
3601+ }
3602+
3603+ // We need to shuffle z ordering a bit in order to keep side stage apps above main stage apps.
3604+ // We don't want to really reorder them in the model because that allows us to keep track
3605+ // of the last focused order.
3606+ function indexToZIndex(index) {
3607+ var app = ApplicationManager.get(index);
3608+ if (!app) {
3609+ return index;
3610+ }
3611+
3612+ var isActive = app.appId == priv.mainStageAppId || app.appId == priv.sideStageAppId;
3613+ if (isActive && app.stage == ApplicationInfoInterface.MainStage) {
3614+ // if this app is active, and its the MainStage, always put it to index 0
3615+ return 0;
3616+ }
3617+ if (isActive && app.stage == ApplicationInfoInterface.SideStage) {
3618+ if (!priv.mainStageAppId) {
3619+ // Only have SS apps running. Put the active one at 0
3620+ return 0;
3621+ }
3622+
3623+ // Precondition now: There's an active MS app and this is SS app:
3624+ if (spreadView.nextInStack >= 0 && ApplicationManager.get(spreadView.nextInStack).stage == ApplicationInfoInterface.MainStage) {
3625+ // If the next app coming from the right is a MS app, we need to elevate this SS ap above it.
3626+ // Put it to at least level 2, or higher if there's more apps coming in before this one.
3627+ return Math.max(index, 2);
3628+ } else {
3629+ // if this is no next app to come in from the right, place this one at index 1, just on top the active MS app.
3630+ return 1;
3631+ }
3632+ }
3633+ if (index <= 2 && app.stage == ApplicationInfoInterface.MainStage && priv.sideStageAppId) {
3634+ // Ok, this is an inactive MS app. If there's an active SS app around, we need to place this one
3635+ // in between the active MS app and the active SS app, so that it comes in from there when dragging from the right.
3636+ // If there's now active SS app, just leave it where it is.
3637+ return priv.indexOf(priv.sideStageAppId) < index ? index - 1 : index;
3638+ }
3639+ if (index == spreadView.nextInStack && app.stage == ApplicationInfoInterface.SideStage) {
3640+ // This is a SS app and the next one to come in from the right:
3641+ if (priv.sideStageAppId && priv.mainStageAppId) {
3642+ // If there's both, an active MS and an active SS app, put this one right on top of that
3643+ return 2;
3644+ }
3645+ // Or if there's only one other active app, put it on top of that.
3646+ // The case that there isn't any other active app is already handled above.
3647+ return 1;
3648+ }
3649+ if (index == 2 && spreadView.nextInStack == 1 && priv.sideStageAppId) {
3650+ // If its index 2 but not the next one to come in, it means
3651+ // we've pulled another one down to index 2. Move this one up to 2 instead.
3652+ return 3;
3653+ }
3654+ // don't touch all others... (mostly index > 3 + simple cases where the above doesn't shuffle much)
3655+ return index;
3656+ }
3657+
3658+ SequentialAnimation {
3659+ id: snapAnimation
3660+ property int targetContentX: 0
3661+
3662+ UbuntuNumberAnimation {
3663+ target: spreadView
3664+ property: "contentX"
3665+ to: snapAnimation.targetContentX
3666+ duration: UbuntuAnimation.FastDuration
3667+ }
3668+
3669+ ScriptAction {
3670+ script: {
3671+ if (spreadView.selectedIndex >= 0) {
3672+ var newIndex = spreadView.selectedIndex;
3673+ spreadView.selectedIndex = -1;
3674+ ApplicationManager.focusApplication(ApplicationManager.get(newIndex).appId);
3675+ spreadView.phase = 0;
3676+ spreadView.contentX = 0;
3677+ }
3678+ }
3679+ }
3680+ }
3681+
3682+ Rectangle {
3683+ id: spreadRow
3684+ color: "black"
3685+ x: spreadView.contentX
3686+ height: root.height
3687+ width: spreadView.width + Math.max(spreadView.width, ApplicationManager.count * spreadView.tileDistance)
3688+
3689+ Rectangle {
3690+ id: sideStageBackground
3691+ color: "black"
3692+ anchors.fill: parent
3693+ anchors.leftMargin: spreadView.width - (1 - sideStageDragHandle.progress) * spreadView.sideStageWidth
3694+ z: spreadView.indexToZIndex(priv.indexOf(priv.sideStageAppId))
3695+ opacity: spreadView.phase == 0 ? 1 : 0
3696+ Behavior on opacity { UbuntuNumberAnimation {} }
3697+ }
3698+
3699+ Item {
3700+ id: sideStageDragHandle
3701+ anchors { top: parent.top; bottom: parent.bottom; left: parent.left; leftMargin: spreadView.width - spreadView.sideStageWidth - width }
3702+ width: units.gu(2)
3703+ z: sideStageBackground.z
3704+ opacity: spreadView.phase <= 0 && spreadView.sideStageVisible ? 1 : 0
3705+ property real progress: 0
3706+ property bool dragging: false
3707+
3708+ Behavior on opacity { UbuntuNumberAnimation {} }
3709+
3710+ Connections {
3711+ target: spreadView
3712+ onSideStageVisibleChanged: {
3713+ if (spreadView.sideStageVisible) {
3714+ sideStageDragHandle.progress = 0;
3715+ }
3716+ }
3717+ }
3718+
3719+ Image {
3720+ anchors.centerIn: parent
3721+ anchors.horizontalCenterOffset: parent.progress * spreadView.sideStageWidth - (width - parent.width) / 2
3722+ width: sideStageDragHandleMouseArea.pressed ? parent.width * 2 : parent.width
3723+ height: parent.height
3724+ source: "graphics/sidestage_handle@20.png"
3725+ Behavior on width { UbuntuNumberAnimation {} }
3726+ }
3727+
3728+ MouseArea {
3729+ id: sideStageDragHandleMouseArea
3730+ anchors.fill: parent
3731+ enabled: spreadView.contentX == 0
3732+ property int startX
3733+ property var gesturePoints: new Array()
3734+
3735+ onPressed: {
3736+ gesturePoints = [];
3737+ startX = mouseX;
3738+ sideStageDragHandle.progress = 0;
3739+ sideStageDragHandle.dragging = true;
3740+ }
3741+ onMouseXChanged: {
3742+ if (priv.mainStageAppId) {
3743+ sideStageDragHandle.progress = Math.max(0, (-startX + mouseX) / spreadView.sideStageWidth);
3744+ }
3745+ gesturePoints.push(mouseX);
3746+ }
3747+ onReleased: {
3748+ if (priv.mainStageAppId) {
3749+ var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints);
3750+ sideStageDragSnapAnimation.to = sideStageDragHandle.progress > 0.5 || oneWayFlick ? 1 : 0;
3751+ sideStageDragSnapAnimation.start();
3752+ } else {
3753+ sideStageDragHandle.dragging = false;
3754+ }
3755+ }
3756+ }
3757+ UbuntuNumberAnimation {
3758+ id: sideStageDragSnapAnimation
3759+ target: sideStageDragHandle
3760+ property: "progress"
3761+
3762+ onRunningChanged: {
3763+ if (!running) {
3764+ sideStageDragHandle.dragging = false;;
3765+ }
3766+ }
3767+ }
3768+ }
3769+
3770+ Repeater {
3771+ id: spreadRepeater
3772+ model: ApplicationManager
3773+
3774+ delegate: TransformedTabletSpreadDelegate {
3775+ id: spreadTile
3776+ height: spreadView.height
3777+ width: model.stage == ApplicationInfoInterface.MainStage ? spreadView.width : spreadView.sideStageWidth
3778+ x: spreadView.width
3779+ z: spreadView.indexToZIndex(index)
3780+ active: model.appId == priv.mainStageAppId || model.appId == priv.sideStageAppId
3781+ zIndex: z
3782+ selected: spreadView.selectedIndex == index
3783+ otherSelected: spreadView.selectedIndex >= 0 && !selected
3784+ isInSideStage: priv.sideStageAppId == model.appId
3785+ interactive: !spreadView.interactive && spreadView.phase === 0 && root.interactive
3786+ swipeToCloseEnabled: spreadView.interactive
3787+ maximizedAppTopMargin: root.maximizedAppTopMargin
3788+ dropShadow: spreadView.contentX > 0 || spreadDragArea.status == DirectionalDragArea.Undecided
3789+
3790+ property real behavioredZIndex: zIndex
3791+ Behavior on behavioredZIndex {
3792+ enabled: spreadView.closingIndex >= 0
3793+ UbuntuNumberAnimation {}
3794+ }
3795+
3796+ // This is required because none of the bindings are triggered in some cases:
3797+ // When an app is closed, it might happen that ApplicationManager.get(nextInStack)
3798+ // returns a different app even though the nextInStackIndex and all the related
3799+ // bindings (index, mainStageApp, sideStageApp, etc) don't change. Let's force a
3800+ // binding update in that case.
3801+ Connections {
3802+ target: ApplicationManager
3803+ onApplicationRemoved: spreadTile.z = Qt.binding(function() {
3804+ return spreadView.indexToZIndex(index);
3805+ })
3806+ }
3807+
3808+ progress: {
3809+ var tileProgress = (spreadView.contentX - behavioredZIndex * spreadView.tileDistance) / spreadView.width;
3810+ // Some tiles (nextInStack, active) need to move directly from the beginning, normalize progress to immediately start at 0
3811+ if ((index == spreadView.nextInStack && spreadView.phase < 2) || (active && spreadView.phase < 1)) {
3812+ tileProgress += behavioredZIndex * spreadView.tileDistance / spreadView.width;
3813+ }
3814+ return tileProgress;
3815+ }
3816+
3817+ animatedProgress: {
3818+ if (spreadView.phase == 0 && (spreadTile.active || spreadView.nextInStack == index)) {
3819+ if (progress < spreadView.positionMarker1) {
3820+ return progress;
3821+ } else if (progress < spreadView.positionMarker1 + snappingCurve.period) {
3822+ return spreadView.positionMarker1 + snappingCurve.value * 3;
3823+ } else {
3824+ return spreadView.positionMarker2;
3825+ }
3826+ }
3827+ return progress;
3828+ }
3829+
3830+ onClicked: {
3831+ if (spreadView.phase == 2) {
3832+ if (ApplicationManager.focusedApplicationId == ApplicationManager.get(index).appId) {
3833+ spreadView.snapTo(index);
3834+ } else {
3835+ ApplicationManager.requestFocusApplication(ApplicationManager.get(index).appId);
3836+ }
3837+ }
3838+ }
3839+
3840+ onClosed: {
3841+ spreadView.draggedIndex = -1;
3842+ spreadView.closingIndex = index;
3843+ ApplicationManager.stopApplication(ApplicationManager.get(index).appId);
3844+ }
3845+
3846+ EasingCurve {
3847+ id: snappingCurve
3848+ type: EasingCurve.Linear
3849+ period: (spreadView.positionMarker2 - spreadView.positionMarker1) / 3
3850+ progress: spreadTile.progress - spreadView.positionMarker1
3851+ }
3852+ }
3853+ }
3854+ }
3855+ }
3856+
3857+ EdgeDragArea {
3858+ id: spreadDragArea
3859+ anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
3860+ width: root.dragAreaWidth
3861+ direction: Direction.Leftwards
3862+
3863+ property bool attachedToView: false
3864+ property var gesturePoints: new Array()
3865+
3866+ onTouchXChanged: {
3867+ if (!dragging) {
3868+ spreadView.phase = 0;
3869+ spreadView.contentX = 0;
3870+ }
3871+
3872+ if (attachedToView) {
3873+ spreadView.contentX = -touchX + spreadDragArea.width;
3874+ if (spreadView.contentX > spreadView.phase0Width + spreadView.phase1Width / 2) {
3875+ attachedToView = false;
3876+ spreadView.snap();
3877+ }
3878+ }
3879+ gesturePoints.push(touchX);
3880+ }
3881+
3882+ onStatusChanged: {
3883+ if (status == DirectionalDragArea.Recognized) {
3884+ attachedToView = true;
3885+ }
3886+ }
3887+
3888+ onDraggingChanged: {
3889+ if (dragging) {
3890+ // Gesture recognized. Start recording this gesture
3891+ gesturePoints = [];
3892+ return;
3893+ }
3894+
3895+ // Ok. The user released. Find out if it was a one-way movement.
3896+ var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints);
3897+ gesturePoints = [];
3898+
3899+ if (oneWayFlick && spreadView.contentX < spreadView.positionMarker1 * spreadView.width) {
3900+ // If it was a short one-way movement, do the Alt+Tab switch
3901+ // no matter if we didn't cross positionMarker1 yet.
3902+ spreadView.snapTo(spreadView.nextInStack);
3903+ } else if (!dragging && attachedToView) {
3904+ if (spreadView.contentX < spreadView.width * spreadView.positionMarker1) {
3905+ spreadView.snap();
3906+ } else if (spreadView.contentX < spreadView.width * spreadView.positionMarker2) {
3907+ spreadView.snapTo(spreadView.nextInStack);
3908+ } else {
3909+ // otherwise snap to the closest snap position we can find
3910+ // (might be back to start, to app 1 or to spread)
3911+ spreadView.snap();
3912+ }
3913+ }
3914+ }
3915+ }
3916+}
3917
3918=== modified file 'qml/Stages/TransformedSpreadDelegate.qml'
3919--- qml/Stages/TransformedSpreadDelegate.qml 2014-03-19 19:10:30 +0000
3920+++ qml/Stages/TransformedSpreadDelegate.qml 2014-07-29 14:07:22 +0000
3921@@ -23,7 +23,9 @@
3922 SpreadDelegate {
3923 id: root
3924
3925+ // Set this to true when this tile is selected in the spread. The animation will change to bring the tile to front.
3926 property bool selected: false
3927+ // Set this to true when another tile in the spread is selected. The animation will change to fade this tile out.
3928 property bool otherSelected: false
3929
3930 // The progress animates the tiles. A value > 0 makes it appear from the right edge. At 1 it reaches the end position.
3931@@ -44,6 +46,9 @@
3932 property real startDistance: units.gu(5)
3933 property real endDistance: units.gu(.5)
3934
3935+ // Hiding tiles when their progress is negative or reached the maximum
3936+ visible: progress >= 0 && progress < 1.7
3937+
3938 onSelectedChanged: {
3939 if (selected) {
3940 priv.snapshot();
3941@@ -106,16 +111,6 @@
3942 selectedTopMarginProgress = topMarginProgress;
3943 }
3944
3945- // This calculates how much negative progress there can be if unwinding the spread completely
3946- // the progress for each tile starts at 0 when it crosses the right edge, so the later a tile comes in,
3947- // the bigger its negativeProgress can be.
3948- property real negativeProgress: {
3949- if (index == 1 && spreadView.phase < 2) {
3950- return 0;
3951- }
3952- return -index * spreadView.tileDistance / spreadView.width;
3953- }
3954-
3955 function linearAnimation(startProgress, endProgress, startValue, endValue, progress) {
3956 // progress : progressDiff = value : valueDiff => value = progress * valueDiff / progressDiff
3957 return (progress - startProgress) * (endValue - startValue) / (endProgress - startProgress) + startValue;
3958@@ -127,22 +122,20 @@
3959 return helperEasingCurve.value * (endValue - startValue) + startValue;
3960 }
3961
3962- property real animatedEndDistance: linearAnimation(0, 2, root.endDistance, 0, root.progress)
3963-
3964 // The following blocks handle the animation of the tile in the spread.
3965 // At the beginning, each tile is attached at the right edge, outside the screen.
3966 // The progress for each tile starts at 0 and it reaches its end position at a progress of 1.
3967- // The first phases are handled special for the first 2 tiles. as we do the alt-tab and snapping in there
3968- // Once we reached phase 3, the animation is the same for all tiles.
3969- // When a tile is selected, the animation state is snapshotted, and the spreadView is unwound (progress animates
3970- // back to negativeProgress). All tiles are kept in place and faded out to 0 opacity except
3971+ // The first phases are handled special for the first 2 tiles. as we do the alt-tab and snapping
3972+ // in there. Once we reached phase 3, the animation is the same for all tiles.
3973+ // When a tile is selected, the animation state is snapshotted, and the spreadView is unwound.
3974+ // All tiles are kept in place and faded out to 0 opacity except
3975 // the selected tile, which is animated from the snapshotted position to be fullscreen.
3976
3977- property real xTranslate: {
3978+ readonly property real xTranslate: {
3979 if (otherSelected) {
3980 if (spreadView.phase < 2 && index == 0) {
3981- return linearAnimation(selectedProgress, negativeProgress,
3982- selectedXTranslate, selectedXTranslate - spreadView.tileDistance, root.progress);
3983+ return linearAnimation(selectedProgress, 0, selectedXTranslate,
3984+ selectedXTranslate - spreadView.tileDistance, root.progress);
3985 }
3986
3987 return selectedXTranslate;
3988@@ -160,7 +153,7 @@
3989 // Apply the same animation as with the rest but add spreadView.width to align it with the others.
3990 return -easingCurve.value * spreadView.width + spreadView.width;
3991 } else if (priv.isSelected) {
3992- return linearAnimation(selectedProgress, negativeProgress, selectedXTranslate, 0, root.progress);
3993+ return linearAnimation(selectedProgress, 0, selectedXTranslate, 0, root.progress);
3994 }
3995
3996 case 1:
3997@@ -177,23 +170,28 @@
3998 if (priv.isSelected) {
3999 // Distance to left edge
4000 var targetTranslate = -spreadView.width - ((index - 1) * root.startDistance);
4001- return linearAnimation(selectedProgress, negativeProgress,
4002+ return linearAnimation(selectedProgress, 0,
4003 selectedXTranslate, targetTranslate, root.progress);
4004 }
4005
4006 // Fix it at the right edge...
4007 var rightEdgeOffset = -((index - 1) * root.startDistance);
4008 // ...and use our easing to move them to the left. Stop a bit earlier for each tile
4009+ var animatedEndDistance = linearAnimation(0, 2, root.endDistance, 0, root.progress);
4010 return -easingCurve.value * spreadView.width + (index * animatedEndDistance) + rightEdgeOffset;
4011
4012 }
4013
4014- property real angle: {
4015+ readonly property real angle: {
4016+ if (spreadView.focusChanging) {
4017+ return 0;
4018+ }
4019+
4020 if (priv.otherSelected) {
4021 return priv.selectedAngle;
4022 }
4023 if (priv.isSelected) {
4024- return linearAnimation(selectedProgress, negativeProgress, selectedAngle, 0, root.progress);
4025+ return linearAnimation(selectedProgress, 0, selectedAngle, 0, root.progress);
4026 }
4027 switch (index) {
4028 case 0:
4029@@ -217,12 +215,15 @@
4030 return root.startAngle - easingCurve.value * (root.startAngle - root.endAngle);
4031 }
4032
4033- property real scale: {
4034+ readonly property real scale: {
4035+ if (spreadView.focusChanging) {
4036+ return 1;
4037+ }
4038 if (priv.otherSelected) {
4039 return priv.selectedScale;
4040 }
4041 if (priv.isSelected) {
4042- return linearAnimation(selectedProgress, negativeProgress, selectedScale, 1, root.progress);
4043+ return linearAnimation(selectedProgress, 0, selectedScale, 1, root.progress);
4044 }
4045
4046 switch (index) {
4047@@ -247,7 +248,7 @@
4048 return root.startScale - easingCurve.value * (root.startScale - root.endScale);
4049 }
4050
4051- property real opacity: {
4052+ readonly property real opacity: {
4053 if (priv.otherSelected) {
4054 return linearAnimation (selectedProgress, Math.max(0, selectedProgress - .5),
4055 selectedOpacity, 0, root.progress);
4056@@ -265,9 +266,9 @@
4057 return 1;
4058 }
4059
4060- property real topMarginProgress: {
4061+ readonly property real topMarginProgress: {
4062 if (priv.isSelected) {
4063- return linearAnimation(selectedProgress, negativeProgress, selectedTopMarginProgress, 0, root.progress);
4064+ return linearAnimation(selectedProgress, 0, selectedTopMarginProgress, 0, root.progress);
4065 }
4066
4067 switch (index) {
4068@@ -290,6 +291,7 @@
4069
4070 return easingCurve.value;
4071 }
4072+
4073 }
4074
4075 transform: [
4076@@ -303,18 +305,22 @@
4077 xScale: priv.scale
4078 yScale: xScale
4079 },
4080+ Scale {
4081+ origin { x: 0; y: (spreadView.height * priv.scale) + maximizedAppTopMargin * 3 }
4082+ xScale: 1
4083+ yScale: isFullscreen ? 1 - priv.topMarginProgress * maximizedAppTopMargin / spreadView.height : 1
4084+ },
4085 Translate {
4086 x: priv.xTranslate
4087 }
4088 ]
4089 opacity: priv.opacity
4090- topMarginProgress: priv.topMarginProgress
4091
4092 EasingCurve {
4093 id: easingCurve
4094 type: EasingCurve.OutSine
4095 period: 1 - spreadView.positionMarker2
4096- progress: root.animatedProgress
4097+ progress: root.progress
4098 }
4099
4100 // This is used as a calculation helper to figure values for progress other than the current one
4101
4102=== added file 'qml/Stages/TransformedTabletSpreadDelegate.qml'
4103--- qml/Stages/TransformedTabletSpreadDelegate.qml 1970-01-01 00:00:00 +0000
4104+++ qml/Stages/TransformedTabletSpreadDelegate.qml 2014-07-29 14:07:22 +0000
4105@@ -0,0 +1,369 @@
4106+/*
4107+ * Copyright 2014 Canonical Ltd.
4108+ *
4109+ * This program is free software; you can redistribute it and/or modify
4110+ * it under the terms of the GNU Lesser General Public License as published by
4111+ * the Free Software Foundation; version 3.
4112+ *
4113+ * This program is distributed in the hope that it will be useful,
4114+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4115+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4116+ * GNU Lesser General Public License for more details.
4117+ *
4118+ * You should have received a copy of the GNU Lesser General Public License
4119+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4120+ *
4121+ * Authors: Michael Zanetti <michael.zanetti@canonical.com>
4122+*/
4123+
4124+import QtQuick 2.0
4125+import Utils 0.1
4126+import Ubuntu.Components 0.1
4127+import Unity.Application 0.1
4128+
4129+SpreadDelegate {
4130+ id: root
4131+
4132+ // Set this to true when this tile is selected in the spread. The animation will change to bring the tile to front.
4133+ property bool selected: false
4134+ // Set this to true when another tile in the spread is selected. The animation will change to fade this tile out.
4135+ property bool otherSelected: false
4136+ // Set this to true when this tile a currently active on either the MS or the SS.
4137+ property bool active: false
4138+
4139+ property int zIndex
4140+ property real progress: 0
4141+ property real animatedProgress: 0
4142+
4143+ property real startDistance: units.gu(5)
4144+ property real endDistance: units.gu(.5)
4145+
4146+ property real startScale: 1.1
4147+ property real endScale: 0.7
4148+ property real dragStartScale: startScale + .2
4149+
4150+ property real startAngle: 15
4151+ property real endAngle: 5
4152+
4153+ property bool isInSideStage: false
4154+
4155+ onSelectedChanged: {
4156+ if (selected) {
4157+ priv.snapshot();
4158+ }
4159+ priv.isSelected = selected;
4160+ }
4161+
4162+ onOtherSelectedChanged: {
4163+ if (otherSelected) {
4164+ priv.snapshot();
4165+ }
4166+ priv.otherSelected = otherSelected;
4167+ }
4168+
4169+ Connections {
4170+ target: spreadView
4171+
4172+ onPhaseChanged: {
4173+ if (spreadView.phase == 1) {
4174+ var phase2Progress = spreadView.positionMarker4 - (root.zIndex * spreadView.tileDistance / spreadView.width);
4175+ priv.phase2StartTranslate = priv.easingAnimation(0, 1, 0, -spreadView.width + (root.zIndex * root.endDistance), phase2Progress);
4176+ priv.phase2StartScale = priv.easingAnimation(0, 1, root.startScale, root.endScale, phase2Progress);
4177+ priv.phase2StartAngle = priv.easingAnimation(0, 1, root.startAngle, root.endAngle, phase2Progress);
4178+ priv.phase2StartTopMarginProgress = priv.easingAnimation(0, 1, 0, 1, phase2Progress);
4179+ }
4180+ }
4181+ }
4182+
4183+ // Required to be an item because we're using states on it.
4184+ Item {
4185+ id: priv
4186+
4187+ // true if this is the next tile on the stack that comes in when dragging from the right
4188+ property bool nextInStack: spreadView.nextZInStack == zIndex
4189+ // true if the next tile in the stack is the nextInStack one. This one will be moved a bit to the left
4190+ property bool movedActive: spreadView.nextZInStack == zIndex + 1
4191+ property real animatedEndDistance: linearAnimation(0, 2, root.endDistance, 0, root.progress)
4192+
4193+ property real phase2StartTranslate
4194+ property real phase2StartScale
4195+ property real phase2StartAngle
4196+ property real phase2StartTopMarginProgress
4197+
4198+ property bool isSelected: false
4199+ property bool otherSelected: false
4200+ property real selectedProgress
4201+ property real selectedXTranslate
4202+ property real selectedAngle
4203+ property real selectedScale
4204+ property real selectedOpacity
4205+ property real selectedTopMarginProgress
4206+
4207+ function snapshot() {
4208+ selectedProgress = root.progress;
4209+ selectedXTranslate = xTranslate;
4210+ selectedAngle = angle;
4211+ selectedScale = priv.scale;
4212+ selectedOpacity = priv.opacityTransform;
4213+ selectedTopMarginProgress = topMarginProgress;
4214+ }
4215+
4216+ // This calculates how much negative progress there can be if unwinding the spread completely
4217+ // the progress for each tile starts at 0 when it crosses the right edge, so the later a tile comes in,
4218+ // the bigger its negativeProgress can be.
4219+ property real negativeProgress: {
4220+ if (nextInStack && spreadView.phase < 2) {
4221+ return 0;
4222+ }
4223+ return -root.zIndex * spreadView.tileDistance / spreadView.width;
4224+ }
4225+
4226+ function linearAnimation(startProgress, endProgress, startValue, endValue, progress) {
4227+ // progress : progressDiff = value : valueDiff => value = progress * valueDiff / progressDiff
4228+ return (progress - startProgress) * (endValue - startValue) / (endProgress - startProgress) + startValue;
4229+ }
4230+
4231+ function easingAnimation(startProgress, endProgress, startValue, endValue, progress) {
4232+ helperEasingCurve.progress = progress - startProgress;
4233+ helperEasingCurve.period = endProgress - startProgress;
4234+ return helperEasingCurve.value * (endValue - startValue) + startValue;
4235+ }
4236+
4237+ property real xTranslate: {
4238+ var newTranslate = 0;
4239+
4240+ if (otherSelected) {
4241+ return priv.selectedXTranslate;
4242+ }
4243+
4244+ if (isSelected) {
4245+ if (model.stage == ApplicationInfoInterface.MainStage) {
4246+ return linearAnimation(selectedProgress, negativeProgress, selectedXTranslate, -spreadView.width, root.progress);
4247+ } else {
4248+ return linearAnimation(selectedProgress, negativeProgress, selectedXTranslate, -spreadView.sideStageWidth, root.progress);
4249+ }
4250+ }
4251+
4252+ // The tile should move a bit to the left if a new one comes on top of it, but not for the Side Stage and not
4253+ // when we're only dragging the side stage in on top of a main stage app
4254+ var shouldMoveAway = spreadView.nextInStack >= 0 && priv.movedActive && model.stage === ApplicationInfoInterface.MainStage &&
4255+ ApplicationManager.get(spreadView.nextInStack).stage === ApplicationInfoInterface.MainStage;
4256+
4257+ if (active) {
4258+ newTranslate -= root.width
4259+ // Only do the hide animation for active apps in the mainstage, and not if we only drag the ss in
4260+ if (spreadView.phase == 0 && shouldMoveAway) {
4261+ newTranslate += linearAnimation(0, spreadView.positionMarker2, 0, -units.gu(4), root.animatedProgress);
4262+ }
4263+ }
4264+ if (nextInStack && spreadView.phase == 0) {
4265+ if (model.stage == ApplicationInfoInterface.MainStage) {
4266+ if (spreadView.sideStageVisible && root.progress > 0) {
4267+ // Move it so it appears from behind the side stage immediately
4268+ newTranslate += -spreadView.sideStageWidth;
4269+ }
4270+ }
4271+
4272+ if (model.stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) {
4273+ // This is when we only drag the side stage in, without rotation or snapping
4274+ newTranslate = linearAnimation(0, spreadView.positionMarker2, 0, -spreadView.sideStageWidth, root.progress);
4275+ } else {
4276+ newTranslate += linearAnimation(0, spreadView.positionMarker2, 0, -spreadView.sideStageWidth * spreadView.snapPosition, root.animatedProgress);
4277+ }
4278+ }
4279+
4280+ if (spreadView.phase == 1) {
4281+ if (nextInStack) {
4282+ if (model.stage == ApplicationInfoInterface.MainStage) {
4283+ var startValue = -spreadView.sideStageWidth * spreadView.snapPosition + (spreadView.sideStageVisible ? -spreadView.sideStageWidth : 0);
4284+ newTranslate += linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, startValue, priv.phase2StartTranslate, root.animatedProgress);
4285+ } else {
4286+ var endValue = -spreadView.width + spreadView.width * root.zIndex / 6;
4287+ if (!spreadView.sideStageVisible) {
4288+ newTranslate += linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, -spreadView.sideStageWidth, priv.phase2StartTranslate, root.progress);
4289+ } else {
4290+ newTranslate += linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, -spreadView.sideStageWidth * spreadView.snapPosition, priv.phase2StartTranslate, root.animatedProgress);
4291+ }
4292+ }
4293+ } else if (root.active) {
4294+ var startProgress = spreadView.positionMarker2 - (zIndex * spreadView.positionMarker2 / 2);
4295+ var endProgress = spreadView.positionMarker4 - (zIndex * spreadView.tileDistance / spreadView.width);
4296+ var startTranslate = -root.width + (shouldMoveAway ? -units.gu(4) : 0);
4297+ newTranslate = linearAnimation(startProgress, endProgress, startTranslate, priv.phase2StartTranslate, root.progress);
4298+ } else {
4299+ var startProgress = spreadView.positionMarker2 - (zIndex * spreadView.positionMarker2 / 2);
4300+ var endProgress = spreadView.positionMarker4 - (zIndex * spreadView.tileDistance / spreadView.width);
4301+ newTranslate = linearAnimation(startProgress, endProgress, 0, priv.phase2StartTranslate, root.progress);
4302+ }
4303+ }
4304+
4305+ if (spreadView.phase == 2) {
4306+ newTranslate = -easingCurve.value * (spreadView.width - root.zIndex * animatedEndDistance);
4307+ }
4308+
4309+ return newTranslate;
4310+ }
4311+
4312+ property real scale: {
4313+ if (otherSelected) {
4314+ return selectedScale;
4315+ }
4316+
4317+ if (isSelected) {
4318+ return linearAnimation(selectedProgress, negativeProgress, selectedScale, 1, root.progress);
4319+ }
4320+
4321+ if (spreadView.phase == 0) {
4322+ if (nextInStack) {
4323+ if (model.stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) {
4324+ return 1;
4325+ } else {
4326+ var targetScale = root.dragStartScale - ((root.dragStartScale - 1) * spreadView.snapPosition);
4327+ return linearAnimation(0, spreadView.positionMarker2, root.dragStartScale, targetScale, root.animatedProgress);
4328+ }
4329+ } else if (active) {
4330+ return 1;
4331+ } else {
4332+ return linearAnimation(0, spreadView.positionMarker2, root.startScale, root.endScale, root.progress);
4333+ }
4334+ }
4335+
4336+ if (spreadView.phase == 1) {
4337+ if (nextInStack) {
4338+ var startScale = 1;
4339+ if (model.stage !== ApplicationInfoInterface.SideStage || spreadView.sideStageVisible) {
4340+ startScale = root.dragStartScale - ((root.dragStartScale - 1) * spreadView.snapPosition);
4341+ }
4342+ return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, startScale, priv.phase2StartScale, root.animatedProgress);
4343+ }
4344+ var startProgress = spreadView.positionMarker2 - (zIndex * spreadView.positionMarker2 / 2);
4345+ var endProgress = spreadView.positionMarker4 - (zIndex * spreadView.tileDistance / spreadView.width);
4346+ return linearAnimation(startProgress, endProgress, 1, priv.phase2StartScale, root.animatedProgress);
4347+ }
4348+
4349+ if (spreadView.phase == 2) {
4350+ return root.startScale - easingCurve.value * (root.startScale - root.endScale);
4351+ }
4352+
4353+ return 1;
4354+ }
4355+
4356+ property real angle: {
4357+ if (otherSelected) {
4358+ return selectedAngle;
4359+ }
4360+ if (isSelected) {
4361+ return linearAnimation(selectedProgress, negativeProgress, selectedAngle, 0, root.progress);
4362+ }
4363+
4364+ // The tile should rotate a bit when another one comes on top, but not when only dragging the side stage in
4365+ var shouldMoveAway = spreadView.nextInStack >= 0 && movedActive &&
4366+ (ApplicationManager.get(spreadView.nextInStack).stage === ApplicationInfoInterface.MainStage ||
4367+ model.stage == ApplicationInfoInterface.SideStage);
4368+
4369+ if (spreadView.phase == 0) {
4370+ if (nextInStack) {
4371+ if (model.stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) {
4372+ return 0;
4373+ } else {
4374+ return linearAnimation(0, spreadView.positionMarker2, root.startAngle, root.startAngle * (1-spreadView.snapPosition), root.animatedProgress);
4375+ }
4376+ }
4377+ if (shouldMoveAway) {
4378+ return linearAnimation(0, spreadView.positionMarker2, 0, root.startAngle * (1-spreadView.snapPosition), root.animatedProgress);
4379+ }
4380+ }
4381+ if (spreadView.phase == 1) {
4382+ if (nextInStack) {
4383+ if (model.stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) {
4384+ return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, 0, priv.phase2StartAngle, root.animatedProgress);
4385+ } else {
4386+ return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, root.startAngle * (1-spreadView.snapPosition), priv.phase2StartAngle, root.animatedProgress);
4387+ }
4388+ }
4389+ var startProgress = spreadView.positionMarker2 - (zIndex * spreadView.positionMarker2 / 2);
4390+ var endProgress = spreadView.positionMarker4 - (zIndex * spreadView.tileDistance / spreadView.width);
4391+ var startAngle = shouldMoveAway ? root.startAngle * (1-spreadView.snapPosition) : 0;
4392+ return linearAnimation(startProgress, endProgress, startAngle, priv.phase2StartAngle, root.progress);
4393+ }
4394+ if (spreadView.phase == 2) {
4395+ return root.startAngle - easingCurve.value * (root.startAngle - root.endAngle);
4396+ }
4397+
4398+ return 0;
4399+ }
4400+
4401+ property real opacityTransform: {
4402+ if (otherSelected && spreadView.phase == 2) {
4403+ return linearAnimation(selectedProgress, negativeProgress, selectedOpacity, 0, root.progress);
4404+ }
4405+
4406+ return 1;
4407+ }
4408+
4409+ property real topMarginProgress: {
4410+ if (priv.isSelected) {
4411+ return linearAnimation(selectedProgress, negativeProgress, selectedTopMarginProgress, 0, root.progress);
4412+ }
4413+ switch (spreadView.phase) {
4414+ case 0:
4415+ return 0;
4416+ case 1:
4417+ return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
4418+ 0, priv.phase2StartTopMarginProgress, root.progress);
4419+ }
4420+ return 1;
4421+ }
4422+
4423+ states: [
4424+ State {
4425+ name: "sideStageDragging"; when: spreadView.sideStageDragging && root.isInSideStage
4426+ PropertyChanges { target: priv; xTranslate: -spreadView.sideStageWidth + spreadView.sideStageWidth * spreadView.sideStageDragProgress }
4427+ }
4428+ ]
4429+ }
4430+
4431+ transform: [
4432+ Rotation {
4433+ origin { x: 0; y: spreadView.height / 2 }
4434+ axis { x: 0; y: 1; z: 0 }
4435+ angle: priv.angle
4436+ },
4437+ Scale {
4438+ origin { x: 0; y: spreadView.height / 2 }
4439+ xScale: priv.scale
4440+ yScale: xScale
4441+ },
4442+ Scale {
4443+ origin { x: 0; y: (spreadView.height * priv.scale) + maximizedAppTopMargin * 3 }
4444+ xScale: 1
4445+ yScale: isFullscreen ? 1 - priv.topMarginProgress * maximizedAppTopMargin / spreadView.height : 1
4446+ },
4447+ Translate {
4448+ x: priv.xTranslate
4449+ }
4450+ ]
4451+ opacity: priv.opacityTransform
4452+
4453+ UbuntuNumberAnimation {
4454+ id: fadeBackInAnimation
4455+ target: root
4456+ property: "opacity"
4457+ duration: UbuntuAnimation.SlowDuration
4458+ from: 0
4459+ to: 1
4460+ }
4461+
4462+ EasingCurve {
4463+ id: easingCurve
4464+ type: EasingCurve.OutSine
4465+ period: 1
4466+ progress: root.progress
4467+ }
4468+
4469+ EasingCurve {
4470+ id: helperEasingCurve
4471+ type: easingCurve.type
4472+ period: easingCurve.period
4473+ }
4474+}
4475
4476=== modified file 'src/CMakeLists.txt'
4477--- src/CMakeLists.txt 2014-06-30 12:46:04 +0000
4478+++ src/CMakeLists.txt 2014-07-29 14:07:22 +0000
4479@@ -1,8 +1,5 @@
4480-pkg_check_modules(UNITY-MIR REQUIRED unity-mir)
4481-
4482 include_directories(
4483 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
4484- ${UNITY-MIR_INCLUDE_DIRS}
4485 )
4486
4487 file(GLOB_RECURSE QML_FILES
4488
4489=== modified file 'src/main.cpp'
4490--- src/main.cpp 2014-07-22 12:46:06 +0000
4491+++ src/main.cpp 2014-07-29 14:07:22 +0000
4492@@ -1,5 +1,5 @@
4493 /*
4494- * Copyright (C) 2012 Canonical, Ltd.
4495+ * Copyright (C) 2012-2014 Canonical, Ltd.
4496 *
4497 * Authors:
4498 * Gerry Boland <gerry.boland@canonical.com>
4499@@ -23,12 +23,9 @@
4500 #include <QtGui/QGuiApplication>
4501 #include <QtQml/QQmlEngine>
4502 #include <QtQml/QQmlContext>
4503-#include <qpa/qplatformnativeinterface.h>
4504 #include <QLibrary>
4505 #include <QDebug>
4506 #include <libintl.h>
4507-#include <dlfcn.h>
4508-#include <csignal>
4509
4510 // local
4511 #include <paths.h>
4512@@ -36,11 +33,13 @@
4513 #include "ApplicationArguments.h"
4514 #include "CachingNetworkManagerFactory.h"
4515
4516-#include <unity-mir/qmirserver.h>
4517-
4518-int startShell(int argc, const char** argv, void* server)
4519+int main(int argc, const char *argv[])
4520 {
4521- const bool isUbuntuMirServer = qgetenv("QT_QPA_PLATFORM") == "ubuntumirserver";
4522+ bool isMirServer = false;
4523+ if (qgetenv("QT_QPA_PLATFORM") == "ubuntumirclient") {
4524+ setenv("QT_QPA_PLATFORM", "mirserver", 1 /* overwrite */);
4525+ isMirServer = true;
4526+ }
4527
4528 QGuiApplication::setApplicationName("unity8");
4529 QGuiApplication *application;
4530@@ -70,21 +69,7 @@
4531 Load the testability driver");
4532 parser.addOption(testabilityOption);
4533
4534- if (isUbuntuMirServer) {
4535- QLibrary unityMir("unity-mir", 1);
4536- unityMir.load();
4537-
4538- typedef QGuiApplication* (*createServerApplication_t)(int&, const char **, void*);
4539- createServerApplication_t createQMirServerApplication = (createServerApplication_t) unityMir.resolve("createQMirServerApplication");
4540- if (!createQMirServerApplication) {
4541- qDebug() << "unable to resolve symbol: createQMirServerApplication";
4542- return 4;
4543- }
4544-
4545- application = createQMirServerApplication(argc, argv, server);
4546- } else {
4547- application = new QGuiApplication(argc, (char**)argv);
4548- }
4549+ application = new QGuiApplication(argc, (char**)argv);
4550
4551 // Treat args with single dashes the same as arguments with two dashes
4552 // Ex: -fullscreen == --fullscreen
4553@@ -100,8 +85,8 @@
4554 if (parser.isSet(windowGeometryOption) &&
4555 parser.value(windowGeometryOption).split('x').size() == 2)
4556 {
4557- QStringList geom = parser.value(windowGeometryOption).split('x');
4558- qmlArgs.setSize(geom.at(0).toInt(), geom.at(1).toInt());
4559+ QStringList geom = parser.value(windowGeometryOption).split('x');
4560+ qmlArgs.setSize(geom.at(0).toInt(), geom.at(1).toInt());
4561 }
4562
4563 // The testability driver is only loaded by QApplication but not by QGuiApplication.
4564@@ -125,7 +110,8 @@
4565
4566 QQuickView* view = new QQuickView();
4567 view->setResizeMode(QQuickView::SizeRootObjectToView);
4568- view->setTitle("Qml Phone Shell");
4569+ view->setColor("black");
4570+ view->setTitle("Unity8 Shell");
4571 view->engine()->setBaseUrl(QUrl::fromLocalFile(::qmlDirectory()));
4572 view->rootContext()->setContextProperty("applicationArguments", &qmlArgs);
4573 view->rootContext()->setContextProperty("indicatorProfile", indicatorProfile);
4574@@ -141,18 +127,9 @@
4575 application->installNativeEventFilter(mouseTouchAdaptor);
4576 }
4577
4578- QPlatformNativeInterface* nativeInterface = QGuiApplication::platformNativeInterface();
4579- /* Shell is declared as a system session so that it always receives all
4580- input events.
4581- FIXME: use the enum value corresponding to SYSTEM_SESSION_TYPE (= 1)
4582- when it becomes available.
4583- */
4584- nativeInterface->setProperty("ubuntuSessionType", 1);
4585- view->setProperty("role", 2); // INDICATOR_ACTOR_ROLE
4586-
4587 QUrl source(::qmlDirectory()+"Shell.qml");
4588 prependImportPaths(view->engine(), ::overrideImportPaths());
4589- if (!isUbuntuMirServer) {
4590+ if (!isMirServer) {
4591 prependImportPaths(view->engine(), ::nonMirImportPaths());
4592 }
4593 appendImportPaths(view->engine(), ::fallbackImportPaths());
4594@@ -161,10 +138,9 @@
4595 view->engine()->setNetworkAccessManagerFactory(managerFactory);
4596
4597 view->setSource(source);
4598- view->setColor("transparent");
4599 QObject::connect(view->engine(), SIGNAL(quit()), application, SLOT(quit()));
4600
4601- if (qgetenv("QT_QPA_PLATFORM") == "ubuntu" || isUbuntuMirServer || parser.isSet(fullscreenOption)) {
4602+ if (isMirServer || parser.isSet(fullscreenOption)) {
4603 view->showFullScreen();
4604 } else {
4605 view->show();
4606@@ -178,54 +154,3 @@
4607
4608 return result;
4609 }
4610-
4611-int main(int argc, const char *argv[])
4612-{
4613- /* Workaround Qt platform integration plugin not advertising itself
4614- as having the following capabilities:
4615- - QPlatformIntegration::ThreadedOpenGL
4616- - QPlatformIntegration::BufferQueueingOpenGL
4617- */
4618- setenv("QML_FORCE_THREADED_RENDERER", "1", 1);
4619- setenv("QML_FIXED_ANIMATION_STEP", "1", 1);
4620-
4621- // For ubuntumirserver/ubuntumirclient
4622- if (qgetenv("QT_QPA_PLATFORM").startsWith("ubuntumir")) {
4623- setenv("QT_QPA_PLATFORM", "ubuntumirserver", 1);
4624-
4625- // If we use unity-mir directly, we automatically link to the Mir-server
4626- // platform-api bindings, which result in unexpected behaviour when
4627- // running the non-Mir scenario.
4628- QLibrary unityMir("unity-mir", 1);
4629- unityMir.load();
4630- if (!unityMir.isLoaded()) {
4631- qDebug() << "Library unity-mir not found/loaded";
4632- return 1;
4633- }
4634-
4635- typedef QMirServer* (*createServer_t)(int, const char **);
4636- createServer_t createQMirServer = (createServer_t) unityMir.resolve("createQMirServer");
4637- if (!createQMirServer) {
4638- qDebug() << "unable to resolve symbol: createQMirServer";
4639- return 2;
4640- }
4641-
4642- QMirServer* mirServer = createQMirServer(argc, argv);
4643-
4644- typedef int (*runWithClient_t)(QMirServer*, std::function<int(int, const char**, void*)>);
4645- runWithClient_t runWithClient = (runWithClient_t) unityMir.resolve("runQMirServerWithClient");
4646- if (!runWithClient) {
4647- qDebug() << "unable to resolve symbol: runWithClient";
4648- return 3;
4649- }
4650-
4651- return runWithClient(mirServer, startShell);
4652- } else {
4653- if (!qgetenv("UNITY_MIR_EMITS_SIGSTOP").isEmpty()) {
4654- // Emit SIGSTOP as expected by upstart, under Mir it's unity-mir that will raise it.
4655- // see http://upstart.ubuntu.com/cookbook/#expect-stop
4656- raise(SIGSTOP);
4657- }
4658- return startShell(argc, argv, nullptr);
4659- }
4660-}
4661
4662=== modified file 'tests/mocks/Unity/Application/Application.qmltypes'
4663--- tests/mocks/Unity/Application/Application.qmltypes 2014-06-26 08:26:58 +0000
4664+++ tests/mocks/Unity/Application/Application.qmltypes 2014-07-29 14:07:22 +0000
4665@@ -102,8 +102,6 @@
4666 "ForceMainStage": 1
4667 }
4668 }
4669- Property { name: "keyboardHeight"; type: "int"; isReadonly: true }
4670- Property { name: "keyboardVisible"; type: "bool"; isReadonly: true }
4671 Property { name: "sideStageWidth"; type: "int"; isReadonly: true }
4672 Property { name: "stageHint"; type: "StageHint"; isReadonly: true }
4673 Property { name: "formFactorHint"; type: "FormFactorHint"; isReadonly: true }
4674
4675=== added file 'tests/mocks/Unity/Application/ApplicationDBusAdaptor.cpp'
4676--- tests/mocks/Unity/Application/ApplicationDBusAdaptor.cpp 1970-01-01 00:00:00 +0000
4677+++ tests/mocks/Unity/Application/ApplicationDBusAdaptor.cpp 2014-07-29 14:07:22 +0000
4678@@ -0,0 +1,75 @@
4679+/*
4680+ * Copyright (C) 2014 Canonical, Ltd.
4681+ *
4682+ * This program is free software; you can redistribute it and/or modify
4683+ * it under the terms of the GNU General Public License as published by
4684+ * the Free Software Foundation; version 3.
4685+ *
4686+ * This program is distributed in the hope that it will be useful,
4687+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4688+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4689+ * GNU General Public License for more details.
4690+ *
4691+ * You should have received a copy of the GNU General Public License
4692+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4693+ */
4694+
4695+#include "ApplicationInfo.h"
4696+#include "ApplicationDBusAdaptor.h"
4697+#include "ApplicationManager.h"
4698+#include "SurfaceManager.h"
4699+#include "MirSurfaceItem.h"
4700+
4701+quint32 nextId = 0;
4702+
4703+ApplicationDBusAdaptor::ApplicationDBusAdaptor(ApplicationManager* applicationManager)
4704+ : QDBusAbstractAdaptor(applicationManager)
4705+ , m_applicationManager(applicationManager)
4706+{
4707+}
4708+
4709+MirSurfaceItem* topSurface(MirSurfaceItem* surface)
4710+{
4711+ if (!surface)
4712+ return nullptr;
4713+
4714+ MirSurfaceItem* child = NULL;
4715+ Q_FOREACH (MirSurfaceItem *childSurface, surface->childSurfaceList()) {
4716+ child = topSurface(childSurface);
4717+ if (child) {
4718+ break;
4719+ }
4720+ }
4721+ return child ? child : surface;
4722+}
4723+
4724+quint32 ApplicationDBusAdaptor::addChildSurface(const QString &appId, const QString &surfaceImage)
4725+{
4726+ qDebug() << "ApplicationDBusAdaptor::addChildSurface - " << appId;
4727+
4728+ ApplicationInfo* application = m_applicationManager->findApplication(appId);
4729+ if (!application) {
4730+ qDebug() << "ApplicationDBusAdaptor::addChildSurface - No application found for " << appId;
4731+ return 0;
4732+ }
4733+ quint32 surfaceId = ++nextId;
4734+ MirSurfaceItem* surface = new MirSurfaceItem(QString("ChildSurface%1").arg(surfaceId),
4735+ MirSurfaceItem::Normal,
4736+ MirSurfaceItem::Maximized,
4737+ QUrl(surfaceImage));
4738+ m_childItems[surfaceId] = surface;
4739+ surface->setParentSurface(topSurface(application->surface()));
4740+ return surfaceId;
4741+}
4742+
4743+void ApplicationDBusAdaptor::removeChildSurface(int surfaceId)
4744+{
4745+ qDebug() << "ApplicationDBusAdaptor::removeChildSurface - " << surfaceId;
4746+
4747+ if (!m_childItems.contains(surfaceId)) {
4748+ qDebug() << "ApplicationDBusAdaptor::removeChildSurface - No added surface for " << surfaceId;
4749+ return;
4750+ }
4751+ MirSurfaceItem* surface = m_childItems.take(surfaceId);
4752+ Q_EMIT surface->removed();
4753+}
4754
4755=== added file 'tests/mocks/Unity/Application/ApplicationDBusAdaptor.h'
4756--- tests/mocks/Unity/Application/ApplicationDBusAdaptor.h 1970-01-01 00:00:00 +0000
4757+++ tests/mocks/Unity/Application/ApplicationDBusAdaptor.h 2014-07-29 14:07:22 +0000
4758@@ -0,0 +1,43 @@
4759+/*
4760+ * Copyright (C) 2014 Canonical, Ltd.
4761+ *
4762+ * This program is free software; you can redistribute it and/or modify
4763+ * it under the terms of the GNU General Public License as published by
4764+ * the Free Software Foundation; version 3.
4765+ *
4766+ * This program is distributed in the hope that it will be useful,
4767+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4768+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4769+ * GNU General Public License for more details.
4770+ *
4771+ * You should have received a copy of the GNU General Public License
4772+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4773+ */
4774+
4775+#ifndef APPLICATIONDBUSADAPTOR_H
4776+#define APPLICATIONDBUSADAPTOR_H
4777+
4778+#include <QtDBus/QtDBus>
4779+
4780+class ApplicationManager;
4781+class SurfaceManager;
4782+class MirSurfaceItem;
4783+
4784+class ApplicationDBusAdaptor : public QDBusAbstractAdaptor
4785+{
4786+ Q_OBJECT
4787+ Q_CLASSINFO("D-Bus Interface", "com.canonical.Unity8.Mocks.Application")
4788+public:
4789+ ApplicationDBusAdaptor(ApplicationManager* applicationManager);
4790+
4791+public Q_SLOTS:
4792+ quint32 addChildSurface(const QString& appId, const QString& surfaceImage);
4793+ void removeChildSurface(int surfaceId);
4794+
4795+private:
4796+ ApplicationManager* m_applicationManager;
4797+
4798+ QHash<quint32, MirSurfaceItem*> m_childItems;
4799+};
4800+
4801+#endif // APPLICATIONDBUSADAPTOR_H
4802
4803=== modified file 'tests/mocks/Unity/Application/ApplicationInfo.cpp'
4804--- tests/mocks/Unity/Application/ApplicationInfo.cpp 2014-03-20 10:02:52 +0000
4805+++ tests/mocks/Unity/Application/ApplicationInfo.cpp 2014-07-29 14:07:22 +0000
4806@@ -1,5 +1,5 @@
4807 /*
4808- * Copyright (C) 2013 Canonical, Ltd.
4809+ * Copyright (C) 2013-2014 Canonical, Ltd.
4810 *
4811 * This program is free software; you can redistribute it and/or modify
4812 * it under the terms of the GNU General Public License as published by
4813@@ -15,6 +15,8 @@
4814 */
4815
4816 #include "ApplicationInfo.h"
4817+#include "MirSurfaceItem.h"
4818+#include "SurfaceManager.h"
4819
4820 #include <QGuiApplication>
4821 #include <QQuickItem>
4822@@ -24,29 +26,39 @@
4823
4824 ApplicationInfo::ApplicationInfo(const QString &appId, QObject *parent)
4825 : ApplicationInfoInterface(appId, parent)
4826- ,m_appId(appId)
4827- ,m_stage(MainStage)
4828- ,m_state(Starting)
4829- ,m_focused(false)
4830- ,m_fullscreen(false)
4831- ,m_windowItem(0)
4832- ,m_windowComponent(0)
4833- ,m_parentItem(0)
4834+ , m_appId(appId)
4835+ , m_stage(MainStage)
4836+ , m_state(Starting)
4837+ , m_focused(false)
4838+ , m_fullscreen(false)
4839+ , m_windowItem(0)
4840+ , m_windowComponent(0)
4841+ , m_parentItem(0)
4842+ , m_surface(0)
4843 {
4844- QTimer::singleShot(300, this, SLOT(setRunning()));
4845+ connect(this, &ApplicationInfo::stateChanged, this, &ApplicationInfo::onStateChanged);
4846 }
4847
4848 ApplicationInfo::ApplicationInfo(QObject *parent)
4849 : ApplicationInfoInterface(QString(), parent)
4850- ,m_stage(MainStage)
4851- ,m_state(Starting)
4852- ,m_focused(false)
4853- ,m_fullscreen(false)
4854- ,m_windowItem(0)
4855- ,m_windowComponent(0)
4856- ,m_parentItem(0)
4857-{
4858- QTimer::singleShot(300, this, SLOT(setRunning()));
4859+ , m_stage(MainStage)
4860+ , m_state(Starting)
4861+ , m_focused(false)
4862+ , m_fullscreen(false)
4863+ , m_windowItem(0)
4864+ , m_windowComponent(0)
4865+ , m_parentItem(0)
4866+ , m_surface(0)
4867+{
4868+ connect(this, &ApplicationInfo::stateChanged, this, &ApplicationInfo::onStateChanged);
4869+}
4870+
4871+ApplicationInfo::~ApplicationInfo()
4872+{
4873+ if (m_surface) {
4874+ Q_EMIT SurfaceManager::singleton()->surfaceDestroyed(m_surface);
4875+ m_surface->deleteLater();
4876+ }
4877 }
4878
4879 void ApplicationInfo::onWindowComponentStatusChanged(QQmlComponent::Status status)
4880@@ -55,10 +67,46 @@
4881 doCreateWindowItem();
4882 }
4883
4884-void ApplicationInfo::setRunning()
4885-{
4886- m_state = Running;
4887- Q_EMIT stateChanged();
4888+void ApplicationInfo::onStateChanged(State state)
4889+{
4890+ if (state == ApplicationInfo::Running) {
4891+ QTimer::singleShot(1000, this, SLOT(createSurface()));
4892+ } else if (state == ApplicationInfo::Stopped) {
4893+ setSurface(nullptr);
4894+ }
4895+}
4896+
4897+void ApplicationInfo::createSurface()
4898+{
4899+ if (m_surface || state() == ApplicationInfo::Stopped) return;
4900+
4901+ setSurface(new MirSurfaceItem(name(),
4902+ MirSurfaceItem::Normal,
4903+ fullscreen() ? MirSurfaceItem::Fullscreen : MirSurfaceItem::Maximized,
4904+ screenshot()));
4905+}
4906+
4907+void ApplicationInfo::setSurface(MirSurfaceItem* surface)
4908+{
4909+ if (m_surface == surface)
4910+ return;
4911+
4912+ if (m_surface) {
4913+ m_surface->setApplication(nullptr);
4914+ m_surface->setParent(nullptr);
4915+ SurfaceManager::singleton()->unregisterSurface(m_surface);
4916+ }
4917+
4918+ m_surface = surface;
4919+
4920+ if (m_surface) {
4921+ m_surface->setApplication(this);
4922+ m_surface->setParent(this);
4923+ SurfaceManager::singleton()->registerSurface(m_surface);
4924+ }
4925+
4926+ Q_EMIT surfaceChanged(m_surface);
4927+ SurfaceManager::singleton()->registerSurface(m_surface);
4928 }
4929
4930 void ApplicationInfo::createWindowComponent()
4931
4932=== modified file 'tests/mocks/Unity/Application/ApplicationInfo.h'
4933--- tests/mocks/Unity/Application/ApplicationInfo.h 2014-03-20 10:02:52 +0000
4934+++ tests/mocks/Unity/Application/ApplicationInfo.h 2014-07-29 14:07:22 +0000
4935@@ -1,5 +1,5 @@
4936 /*
4937- * Copyright (C) 2013 Canonical, Ltd.
4938+ * Copyright (C) 2013-2014 Canonical, Ltd.
4939 *
4940 * This program is free software; you can redistribute it and/or modify
4941 * it under the terms of the GNU General Public License as published by
4942@@ -21,6 +21,7 @@
4943 #include <QQmlComponent>
4944
4945 class QQuickItem;
4946+class MirSurfaceItem;
4947
4948 // unity-api
4949 #include <unity/shell/application/ApplicationInfoInterface.h>
4950@@ -36,6 +37,7 @@
4951
4952 Q_PROPERTY(bool fullscreen READ fullscreen WRITE setFullscreen NOTIFY fullscreenChanged)
4953 Q_PROPERTY(Stage stage READ stage WRITE setStage NOTIFY stageChanged)
4954+ Q_PROPERTY(MirSurfaceItem* surface READ surface NOTIFY surfaceChanged)
4955
4956 // Only exists in this fake implementation
4957
4958@@ -45,9 +47,10 @@
4959 // QML component used to represent the application window
4960 Q_PROPERTY(QString windowQml READ windowQml WRITE setWindowQml NOTIFY windowQmlChanged)
4961
4962- public:
4963+public:
4964 ApplicationInfo(QObject *parent = NULL);
4965 ApplicationInfo(const QString &appId, QObject *parent = NULL);
4966+ ~ApplicationInfo();
4967
4968 #define IMPLEMENT_PROPERTY(name, Name, type) \
4969 public: \
4970@@ -56,11 +59,11 @@
4971 { \
4972 if (m_##name != value) { \
4973 m_##name = value; \
4974- Q_EMIT name##Changed(); \
4975+ Q_EMIT name##Changed(value); \
4976 } \
4977 } \
4978 Q_SIGNALS: \
4979- void name##Changed(); \
4980+ void name##Changed(const type&); \
4981 private: \
4982 type m_##name;
4983
4984@@ -78,21 +81,31 @@
4985
4986 #undef IMPLEMENT_PROPERTY
4987
4988- public:
4989+public:
4990+ void setSurface(MirSurfaceItem* surface);
4991+ MirSurfaceItem* surface() const { return m_surface; }
4992+
4993+Q_SIGNALS:
4994+ void surfaceChanged(MirSurfaceItem*);
4995+
4996+public:
4997 void showWindow(QQuickItem *parent);
4998 void hideWindow();
4999
5000- private Q_SLOTS:
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches