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
=== modified file 'debian/changelog'
--- debian/changelog 2014-07-25 10:47:35 +0000
+++ debian/changelog 2014-07-29 14:07:22 +0000
@@ -1,3 +1,17 @@
1unity8 (8.00) UNRELEASED; urgency=medium
2
3 [ Gerry Boland ]
4 [ Daniel d'Andrada ]
5 * Re-architecture unity8 to use the QtMirCompositor library so that
6 the Qt scenegraph renderer is used as the Mir compositor, and
7 application surfaces are added to the QML scene as items.
8
9 [ Michael Zanetti ]
10 * Port phone right-edge spread code to use QtCompositor
11 * Add right-edge spread animation for tablet
12
13 -- Gerry Boland <gerry.boland@canonical.com> Thu, 10 Jul 2014 10:34:18 +0100
14
1unity8 (7.90+14.10.20140725-0ubuntu1) utopic; urgency=low15unity8 (7.90+14.10.20140725-0ubuntu1) utopic; urgency=low
216
3 [ CI bot ]17 [ CI bot ]
418
=== modified file 'debian/control'
--- debian/control 2014-07-24 20:40:44 +0000
+++ debian/control 2014-07-29 14:07:22 +0000
@@ -24,7 +24,6 @@
24 libqmenumodel-dev (>= 0.2.8),24 libqmenumodel-dev (>= 0.2.8),
25 libqt5xmlpatterns5-dev,25 libqt5xmlpatterns5-dev,
26 libunity-api-dev (>= 7.85),26 libunity-api-dev (>= 7.85),
27 libunity-mir-dev,
28 libusermetricsoutput1-dev,27 libusermetricsoutput1-dev,
29 libxcb1-dev,28 libxcb1-dev,
30 pkg-config,29 pkg-config,
@@ -80,10 +79,10 @@
80Depends: gsettings-desktop-schemas,79Depends: gsettings-desktop-schemas,
81 libcap2-bin,80 libcap2-bin,
82 libglib2.0-bin,81 libglib2.0-bin,
83 libunity-mir1 (>= 0.4),
84 qmenumodel-qml (>= 0.2.8),82 qmenumodel-qml (>= 0.2.8),
85 qml-module-qtquick-xmllistmodel,83 qml-module-qtquick-xmllistmodel,
86 qtdeclarative5-gsettings1.0,84 qtdeclarative5-gsettings1.0,
85 qtdeclarative5-qtmir-plugin (>= 0.4),
87 qtdeclarative5-ubuntu-settings-components (>= 0.3),86 qtdeclarative5-ubuntu-settings-components (>= 0.3),
88 qtdeclarative5-ubuntu-telephony0.1,87 qtdeclarative5-ubuntu-telephony0.1,
89 unity-launcher-impl-3,88 unity-launcher-impl-3,
9089
=== modified file 'plugins/Utils/CMakeLists.txt'
--- plugins/Utils/CMakeLists.txt 2014-06-23 16:20:51 +0000
+++ plugins/Utils/CMakeLists.txt 2014-07-29 14:07:22 +0000
@@ -15,6 +15,7 @@
15 qsortfilterproxymodelqml.cpp15 qsortfilterproxymodelqml.cpp
16 timeformatter.cpp16 timeformatter.cpp
17 unitymenumodelpaths.cpp17 unitymenumodelpaths.cpp
18 windowkeysfilter.cpp
18 easingcurve.cpp19 easingcurve.cpp
19 plugin.cpp20 plugin.cpp
20 )21 )
2122
=== modified file 'plugins/Utils/easingcurve.cpp'
--- plugins/Utils/easingcurve.cpp 2014-06-30 16:02:05 +0000
+++ plugins/Utils/easingcurve.cpp 2014-07-29 14:07:22 +0000
@@ -34,7 +34,11 @@
3434
35void EasingCurve::setType(const QEasingCurve::Type &type)35void EasingCurve::setType(const QEasingCurve::Type &type)
36{36{
37 m_easingCurve.setType(type);37 // FIXME: Working around bug https://bugreports.qt-project.org/browse/QTBUG-38686 here
38 QEasingCurve newCurve;
39 newCurve.setType(type);
40 newCurve.setPeriod(m_easingCurve.period());
41 m_easingCurve = newCurve;
38 Q_EMIT typeChanged();42 Q_EMIT typeChanged();
39}43}
4044
4145
=== modified file 'plugins/Utils/plugin.cpp'
--- plugins/Utils/plugin.cpp 2014-06-23 16:20:51 +0000
+++ plugins/Utils/plugin.cpp 2014-07-29 14:07:22 +0000
@@ -30,6 +30,7 @@
30#include "qsortfilterproxymodelqml.h"30#include "qsortfilterproxymodelqml.h"
31#include "timeformatter.h"31#include "timeformatter.h"
32#include "unitymenumodelpaths.h"32#include "unitymenumodelpaths.h"
33#include "windowkeysfilter.h"
33#include "easingcurve.h"34#include "easingcurve.h"
3435
35void UtilsPlugin::registerTypes(const char *uri)36void UtilsPlugin::registerTypes(const char *uri)
@@ -40,6 +41,7 @@
40 qmlRegisterType<QSortFilterProxyModelQML>(uri, 0, 1, "SortFilterProxyModel");41 qmlRegisterType<QSortFilterProxyModelQML>(uri, 0, 1, "SortFilterProxyModel");
41 qmlRegisterType<UnityMenuModelPaths>(uri, 0, 1, "UnityMenuModelPaths");42 qmlRegisterType<UnityMenuModelPaths>(uri, 0, 1, "UnityMenuModelPaths");
42 qmlRegisterType<TimeFormatter>(uri, 0, 1, "TimeFormatter");43 qmlRegisterType<TimeFormatter>(uri, 0, 1, "TimeFormatter");
44 qmlRegisterType<WindowKeysFilter>(uri, 0, 1, "WindowKeysFilter");
43 qmlRegisterType<GDateTimeFormatter>(uri, 0, 1, "GDateTimeFormatter");45 qmlRegisterType<GDateTimeFormatter>(uri, 0, 1, "GDateTimeFormatter");
44 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");46 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
45}47}
4648
=== added file 'plugins/Utils/windowkeysfilter.cpp'
--- plugins/Utils/windowkeysfilter.cpp 1970-01-01 00:00:00 +0000
+++ plugins/Utils/windowkeysfilter.cpp 2014-07-29 14:07:22 +0000
@@ -0,0 +1,57 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Author: Daniel d'Andrada <daniel.dandrada@canonical.com>
17 */
18
19#include "windowkeysfilter.h"
20
21#include <QQuickWindow>
22
23WindowKeysFilter::WindowKeysFilter(QQuickItem *parent)
24 : QQuickItem(parent)
25{
26 connect(this, &QQuickItem::windowChanged,
27 this, &WindowKeysFilter::setupFilterOnWindow);
28}
29
30bool WindowKeysFilter::eventFilter(QObject *watched, QEvent *event)
31{
32 Q_ASSERT(!m_filteredWindow.isNull());
33 Q_ASSERT(watched == static_cast<QObject*>(m_filteredWindow.data()));
34
35 if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
36 // Let QML see this event and decide if it does not want it
37 event->accept();
38 QCoreApplication::sendEvent(this, event);
39 return event->isAccepted();
40 } else {
41 // Not interested
42 return false;
43 }
44}
45
46void WindowKeysFilter::setupFilterOnWindow(QQuickWindow *window)
47{
48 if (!m_filteredWindow.isNull()) {
49 m_filteredWindow->removeEventFilter(this);
50 m_filteredWindow.clear();
51 }
52
53 if (window) {
54 window->installEventFilter(this);
55 m_filteredWindow = window;
56 }
57}
058
=== added file 'plugins/Utils/windowkeysfilter.h'
--- plugins/Utils/windowkeysfilter.h 1970-01-01 00:00:00 +0000
+++ plugins/Utils/windowkeysfilter.h 2014-07-29 14:07:22 +0000
@@ -0,0 +1,51 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Author: Daniel d'Andrada <daniel.dandrada@canonical.com>
17 */
18
19#ifndef UNITY_WINDOWKEYSFILTER_H
20#define UNITY_WINDOWKEYSFILTER_H
21
22#include <QQuickItem>
23#include <QPointer>
24
25/*
26 Receives all key events that arrive in the QQuickWindow where this item is placed.
27
28 Rejected key events will be allowed to be processed normally by the QQuickWindow whereas
29 accepted ones will be filtered out. Events are accepted by default, so make sure you reject
30 the keys you're not interested in.
31
32 If more than one WindowKeysFilter exist in the same QML scene (and thus in the same QQuickWindow)
33 they will be called in the order of creation, which can be tricky to assess. So the best practice
34 is to have at most one WindowKeysFilter per QML scene.
35 */
36class WindowKeysFilter : public QQuickItem
37{
38 Q_OBJECT
39public:
40 WindowKeysFilter(QQuickItem *parent = 0);
41
42 bool eventFilter(QObject *watched, QEvent *event);
43
44private Q_SLOTS:
45 void setupFilterOnWindow(QQuickWindow *window);
46
47private:
48 QPointer<QQuickWindow> m_filteredWindow;
49};
50
51#endif // UNITY_WINDOWKEYSFILTER_H
052
=== added file 'qml/Components/Dialogs.qml'
--- qml/Components/Dialogs.qml 1970-01-01 00:00:00 +0000
+++ qml/Components/Dialogs.qml 2014-07-29 14:07:22 +0000
@@ -0,0 +1,217 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18
19import Unity.Application 0.1
20import Unity.Session 0.1
21import Ubuntu.Components 0.1
22import Ubuntu.Components.Popups 0.1
23
24Item {
25 id: root
26
27 function onPowerKeyPressed() {
28 if (!powerKeyTimer.running) {
29 powerKeyTimer.start();
30 }
31 }
32
33 function onPowerKeyReleased() {
34 powerKeyTimer.stop();
35 }
36
37 signal powerOffClicked();
38
39 QtObject {
40 id: d // private stuff
41
42 property bool dialogShown: false
43
44 function showPowerDialog() {
45 if (!d.dialogShown) {
46 d.dialogShown = true;
47 PopupUtils.open(powerDialog, root, {"z": root.z});
48 }
49 }
50 }
51
52 Timer {
53 id: powerKeyTimer
54 interval: 2000
55 repeat: false
56 triggeredOnStart: false
57
58 onTriggered: {
59 d.showPowerDialog();
60 }
61 }
62
63 Component {
64 id: logoutDialog
65 Dialog {
66 id: dialogueLogout
67 title: "Logout"
68 text: "Are you sure that you want to logout?"
69 Button {
70 text: "Cancel"
71 onClicked: {
72 PopupUtils.close(dialogueLogout);
73 d.dialogShown = false;
74 }
75 }
76 Button {
77 text: "Yes"
78 onClicked: {
79 DBusUnitySessionService.Logout();
80 PopupUtils.close(dialogueLogout);
81 d.dialogShown = false;
82 }
83 }
84 }
85 }
86
87 Component {
88 id: shutdownDialog
89 Dialog {
90 id: dialogueShutdown
91 title: "Shutdown"
92 text: "Are you sure that you want to shutdown?"
93 Button {
94 text: "Cancel"
95 onClicked: {
96 PopupUtils.close(dialogueShutdown);
97 d.dialogShown = false;
98 }
99 }
100 Button {
101 text: "Yes"
102 onClicked: {
103 dBusUnitySessionServiceConnection.closeAllApps();
104 DBusUnitySessionService.Shutdown();
105 PopupUtils.close(dialogueShutdown);
106 d.dialogShown = false;
107 }
108 }
109 }
110 }
111
112 Component {
113 id: rebootDialog
114 Dialog {
115 id: dialogueReboot
116 title: "Reboot"
117 text: "Are you sure that you want to reboot?"
118 Button {
119 text: "Cancel"
120 onClicked: {
121 PopupUtils.close(dialogueReboot)
122 d.dialogShown = false;
123 }
124 }
125 Button {
126 text: "Yes"
127 onClicked: {
128 dBusUnitySessionServiceConnection.closeAllApps();
129 DBusUnitySessionService.Reboot();
130 PopupUtils.close(dialogueReboot);
131 d.dialogShown = false;
132 }
133 }
134 }
135 }
136
137 Component {
138 id: powerDialog
139 Dialog {
140 id: dialoguePower
141 title: "Power"
142 text: i18n.tr("Are you sure you would like to turn power off?")
143 Button {
144 text: i18n.tr("Power off")
145 onClicked: {
146 dBusUnitySessionServiceConnection.closeAllApps();
147 PopupUtils.close(dialoguePower);
148 d.dialogShown = false;
149 root.powerOffClicked();
150 }
151 }
152 Button {
153 text: i18n.tr("Restart")
154 onClicked: {
155 dBusUnitySessionServiceConnection.closeAllApps();
156 DBusUnitySessionService.Reboot();
157 PopupUtils.close(dialoguePower);
158 d.dialogShown = false;
159 }
160 }
161 Button {
162 text: i18n.tr("Cancel")
163 onClicked: {
164 PopupUtils.close(dialoguePower);
165 d.dialogShown = false;
166 }
167 }
168 }
169 }
170
171
172 Connections {
173 id: dBusUnitySessionServiceConnection
174 objectName: "dBusUnitySessionServiceConnection"
175 target: DBusUnitySessionService
176
177 function closeAllApps() {
178 while (true) {
179 var app = ApplicationManager.get(0);
180 if (app === null) {
181 break;
182 }
183 ApplicationManager.stopApplication(app.appId);
184 }
185 }
186
187 onLogoutRequested: {
188 // Display a dialog to ask the user to confirm.
189 if (!d.dialogShown) {
190 d.dialogShown = true;
191 PopupUtils.open(logoutDialog, root, {"z": root.z});
192 }
193 }
194
195 onShutdownRequested: {
196 // Display a dialog to ask the user to confirm.
197 if (!d.dialogShown) {
198 d.dialogShown = true;
199 PopupUtils.open(shutdownDialog, root, {"z": root.z});
200 }
201 }
202
203 onRebootRequested: {
204 // Display a dialog to ask the user to confirm.
205 if (!d.dialogShown) {
206 d.dialogShown = true;
207 PopupUtils.open(rebootDialog, root, {"z": root.z});
208 }
209 }
210
211 onLogoutReady: {
212 closeAllApps();
213 Qt.quit();
214 }
215 }
216
217}
0218
=== modified file 'qml/Components/DraggingArea.qml'
--- qml/Components/DraggingArea.qml 2013-10-25 14:26:29 +0000
+++ qml/Components/DraggingArea.qml 2014-07-29 14:07:22 +0000
@@ -117,4 +117,8 @@
117 dragging = false117 dragging = false
118 __pressedPosition = Qt.point(mouse.x, mouse.y)118 __pressedPosition = Qt.point(mouse.x, mouse.y)
119 }119 }
120
121 onCanceled: {
122 dragging = false
123 }
120}124}
121125
=== added file 'qml/Components/InputMethod.qml'
--- qml/Components/InputMethod.qml 1970-01-01 00:00:00 +0000
+++ qml/Components/InputMethod.qml 2014-07-29 14:07:22 +0000
@@ -0,0 +1,79 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18import Unity.Application 0.1
19import Ubuntu.Components 0.1
20
21Item {
22 id: root
23
24 property var surface: null
25
26 property int transitionDuration: UbuntuAnimation.FastDuration
27
28 state: {
29 if (surface && surface.state != MirSurfaceItem.Minimized) {
30 return "shown";
31 } else {
32 return "hidden";
33 }
34 }
35
36 states: [
37 State {
38 name: "shown"
39 PropertyChanges {
40 target: root
41 visible: true
42 y: 0
43 }
44 },
45 State {
46 name: "hidden"
47 PropertyChanges {
48 target: root
49 visible: false
50 // half-way down because the vkb occupies only the lower half of the surface
51 // TODO: consider keyboard rotation
52 y: root.parent.height / 2.0
53 }
54 }
55 ]
56
57 transitions: [
58 Transition {
59 from: "*"; to: "*"
60 PropertyAction { property: "visible"; value: true }
61 UbuntuNumberAnimation { property: "y"; duration: transitionDuration }
62 }
63 ]
64
65 onSurfaceChanged: {
66 if (surface) {
67 surface.parent = root;
68 surface.anchors.fill = root;
69 }
70 }
71
72 Component.onDestruction: {
73 if (surface) {
74 surface.parent = null;
75 surface.release();
76 surface = null;
77 }
78 }
79}
080
=== modified file 'qml/Components/PageHeader.qml'
--- qml/Components/PageHeader.qml 2014-07-21 09:49:41 +0000
+++ qml/Components/PageHeader.qml 2014-07-29 14:07:22 +0000
@@ -117,7 +117,7 @@
117 Flickable {117 Flickable {
118 id: headerContainer118 id: headerContainer
119 objectName: "headerContainer"119 objectName: "headerContainer"
120 clip: true120 clip: openSearchAnimation.running
121 anchors { left: parent.left; top: parent.top; right: parent.right }121 anchors { left: parent.left; top: parent.top; right: parent.right }
122 height: units.gu(6.5)122 height: units.gu(6.5)
123 contentHeight: headersColumn.height123 contentHeight: headersColumn.height
@@ -155,6 +155,7 @@
155 anchors { left: parent.left; right: parent.right }155 anchors { left: parent.left; right: parent.right }
156 height: headerContainer.height156 height: headerContainer.height
157 contentHeight: height157 contentHeight: height
158 opacity: headerContainer.clip || headerContainer.showSearch ? 1 : 0 // setting visible false cause column to relayout
158 separatorSource: ""159 separatorSource: ""
159 // Required to keep PageHeadStyle noise down as it expects the Page's properties around.160 // Required to keep PageHeadStyle noise down as it expects the Page's properties around.
160 property var styledItem: searchHeader161 property var styledItem: searchHeader
@@ -229,6 +230,7 @@
229 anchors { left: parent.left; right: parent.right }230 anchors { left: parent.left; right: parent.right }
230 height: headerContainer.height231 height: headerContainer.height
231 contentHeight: height232 contentHeight: height
233 opacity: headerContainer.clip || !headerContainer.showSearch ? 1 : 0 // setting visible false cause column to relayout
232 separatorSource: ""234 separatorSource: ""
233 textColor: root.scopeStyle ? root.scopeStyle.headerForeground : "grey"235 textColor: root.scopeStyle ? root.scopeStyle.headerForeground : "grey"
234 property var styledItem: header236 property var styledItem: header
235237
=== modified file 'qml/Dash/Apps/RunningApplicationTile.qml'
--- qml/Dash/Apps/RunningApplicationTile.qml 2014-03-25 11:34:34 +0000
+++ qml/Dash/Apps/RunningApplicationTile.qml 2014-07-29 14:07:22 +0000
@@ -29,11 +29,8 @@
29 signal requestedActivationMode()29 signal requestedActivationMode()
30 signal requestedTerminationMode()30 signal requestedTerminationMode()
3131
32 // Was: childrenRect.height32 width: thumbnail.width
33 // To avoid "binding loop" warning33 height: thumbnail.height + labelContainer.height
34 height: shapedApplicationImage.height + labelContainer.height
35
36 width: shapedApplicationImage.width <= units.gu(11) ? units.gu(11) : height
3734
38 property bool terminationModeEnabled: false35 property bool terminationModeEnabled: false
3936
@@ -50,42 +47,53 @@
50 }47 }
51 }48 }
5249
53 UbuntuShape {50 Item {
54 id: shapedApplicationImage51 id: thumbnail
55 anchors { top: parent.top; horizontalCenter: parent.horizontalCenter }52
5653 width: shapedApplicationImage.width
57 height: units.gu(17)54 height: shapedApplicationImage.height
58 width: applicationImage.width55
59 radius: "medium"56 UbuntuShape {
6057 id: shapedApplicationImage
61 image: Image {58 x: 0
62 id: applicationImage59 y: 0
63 source: application.screenshot60 height: applicationImage.height
64 // height : width = ss.height : ss.width61 width: applicationImage.width
65 height: shapedApplicationImage.height62 radius: "medium"
66 fillMode: Image.PreserveAspectCrop63
67 width: Math.min(height, height * sourceSize.width / sourceSize.height)64 image: Image {
68 }65 id: applicationImage
6966 source: application.screenshot
70 }67 // height : width = ss.height : ss.width
7168
72 UbuntuShape {69 property bool isLandscape: sourceSize.width > sourceSize.height
73 id: borderPressed70 property real maxDimension: units.gu(17)
7471
75 anchors.fill: shapedApplicationImage72 width: isLandscape ? Math.min(sourceSize.width, maxDimension) : height * (sourceSize.width / sourceSize.height)
76 radius: "medium"73 height: isLandscape ? width * (sourceSize.height / sourceSize.width) : Math.min(sourceSize.height, maxDimension)
77 borderSource: "radius_pressed.sci"74 fillMode: Image.Stretch
78 opacity: root.pressed ? 1.0 : 0.075 }
79 Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutQuint } }76
77 }
78
79 UbuntuShape {
80 id: borderPressed
81
82 anchors.fill: shapedApplicationImage
83 radius: "medium"
84 borderSource: "radius_pressed.sci"
85 opacity: root.pressed ? 1.0 : 0.0
86 Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutQuint } }
87 }
80 }88 }
8189
82 // FIXME: label code duplicated with Tile90 // FIXME: label code duplicated with Tile
83 Item {91 Item {
84 id: labelContainer92 id: labelContainer
85 anchors {93 anchors {
86 left: parent.left94 left: thumbnail.left
87 right: parent.right95 right: thumbnail.right
88 top: shapedApplicationImage.bottom96 top: thumbnail.bottom
89 }97 }
90 height: units.gu(2)98 height: units.gu(2)
9199
@@ -110,9 +118,9 @@
110 CloseIcon {118 CloseIcon {
111 objectName: "closeIcon " + model.name119 objectName: "closeIcon " + model.name
112 anchors {120 anchors {
113 left: shapedApplicationImage.left121 left: thumbnail.left
114 leftMargin: -units.gu(1)122 leftMargin: -units.gu(1)
115 top: parent.top123 top: thumbnail.top
116 topMargin: -units.gu(1)124 topMargin: -units.gu(1)
117 }125 }
118 height: units.gu(6)126 height: units.gu(6)
119127
=== modified file 'qml/Dash/DashContent.qml'
--- qml/Dash/DashContent.qml 2014-07-08 19:35:59 +0000
+++ qml/Dash/DashContent.qml 2014-07-29 14:07:22 +0000
@@ -121,6 +121,10 @@
121 Loader {121 Loader {
122 width: ListView.view.width122 width: ListView.view.width
123 height: ListView.view.height123 height: ListView.view.height
124 opacity: { // hide delegate if offscreen
125 var xPositionRelativetoView = ListView.view.contentX - x
126 return (xPositionRelativetoView > -width && xPositionRelativetoView < width) ? 1 : 0
127 }
124 asynchronous: true128 asynchronous: true
125 // TODO This if will eventually go away since we're killing DashApps.qml129 // TODO This if will eventually go away since we're killing DashApps.qml
126 // once we move app closing to the spread130 // once we move app closing to the spread
127131
=== added file 'qml/Dash/graphics/phone/screenshots/vkb_portrait.png'
128Binary 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 differ132Binary 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
=== modified file 'qml/Launcher/Launcher.qml'
--- qml/Launcher/Launcher.qml 2014-07-09 13:38:07 +0000
+++ qml/Launcher/Launcher.qml 2014-07-29 14:07:22 +0000
@@ -181,7 +181,7 @@
181 bottom: parent.bottom181 bottom: parent.bottom
182 }182 }
183 x: -width183 x: -width
184 opacity: (x == -width && dragArea.status === DirectionalDragArea.WaitingForTouch) ? 0 : 1184 visible: x > -width || dragArea.status === DirectionalDragArea.Undecided
185 model: LauncherModel185 model: LauncherModel
186186
187 property bool animate: true187 property bool animate: true
188188
=== modified file 'qml/Panel/Indicators.qml'
--- qml/Panel/Indicators.qml 2014-07-10 13:36:41 +0000
+++ qml/Panel/Indicators.qml 2014-07-29 14:07:22 +0000
@@ -37,6 +37,7 @@
37 readonly property int lockThreshold: openedHeight / 237 readonly property int lockThreshold: openedHeight / 2
38 property bool fullyOpened: height == openedHeight38 property bool fullyOpened: height == openedHeight
39 property bool partiallyOpened: height > panelHeight && !fullyOpened39 property bool partiallyOpened: height > panelHeight && !fullyOpened
40 property bool fullyClosed: height <= panelHeight
40 property bool contentEnabled: true41 property bool contentEnabled: true
41 property bool initalizeItem: true42 property bool initalizeItem: true
42 readonly property alias content: menuContent43 readonly property alias content: menuContent
@@ -187,6 +188,7 @@
187 bottom: handle.top188 bottom: handle.top
188 }189 }
189 indicatorsModel: visibleIndicators.model190 indicatorsModel: visibleIndicators.model
191 visible: indicators.partiallyOpened || indicators.fullyOpened
190 clip: !indicators.fullyOpened192 clip: !indicators.fullyOpened
191 activeHeader: indicators.state == "hint" || indicators.state == "reveal"193 activeHeader: indicators.state == "hint" || indicators.state == "reveal"
192 enabled: contentEnabled194 enabled: contentEnabled
@@ -219,6 +221,7 @@
219 }221 }
220 height: Math.max(Math.min(handleImage.height, indicators.height - handleImage.height), 0)222 height: Math.max(Math.min(handleImage.height, indicators.height - handleImage.height), 0)
221 clip: height < handleImage.height223 clip: height < handleImage.height
224 visible: menuContent.visible
222225
223 BorderImage {226 BorderImage {
224 id: handleImage227 id: handleImage
225228
=== modified file 'qml/Shell.qml'
--- qml/Shell.qml 2014-07-18 15:13:35 +0000
+++ qml/Shell.qml 2014-07-29 14:07:22 +0000
@@ -19,9 +19,9 @@
19import GSettings 1.019import GSettings 1.0
20import Unity.Application 0.120import Unity.Application 0.1
21import Ubuntu.Components 0.121import Ubuntu.Components 0.1
22import Ubuntu.Components.Popups 0.1
23import Ubuntu.Gestures 0.122import Ubuntu.Gestures 0.1
24import Unity.Launcher 0.123import Unity.Launcher 0.1
24import Utils 0.1
25import LightDM 0.1 as LightDM25import LightDM 0.1 as LightDM
26import Powerd 0.126import Powerd 0.1
27import SessionBroadcast 0.127import SessionBroadcast 0.1
@@ -31,10 +31,11 @@
31import "Panel"31import "Panel"
32import "Components"32import "Components"
33import "Notifications"33import "Notifications"
34import "Stages"
34import Unity.Notifications 1.0 as NotificationBackend35import Unity.Notifications 1.0 as NotificationBackend
35import Unity.Session 0.136import Unity.Session 0.1
3637
37FocusScope {38Item {
38 id: shell39 id: shell
3940
40 // this is only here to select the width / height of the window if not running fullscreen41 // this is only here to select the width / height of the window if not running fullscreen
@@ -47,7 +48,7 @@
47 property url background48 property url background
48 readonly property real panelHeight: panel.panelHeight49 readonly property real panelHeight: panel.panelHeight
4950
50 property bool dashShown: dash.shown51 property bool dashShown: dash.shown && dash.available && underlay.visible
5152
52 property bool sideStageEnabled: shell.width >= units.gu(100)53 property bool sideStageEnabled: shell.width >= units.gu(100)
53 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId54 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
@@ -55,14 +56,10 @@
55 function activateApplication(appId) {56 function activateApplication(appId) {
56 if (ApplicationManager.findApplication(appId)) {57 if (ApplicationManager.findApplication(appId)) {
57 ApplicationManager.requestFocusApplication(appId);58 ApplicationManager.requestFocusApplication(appId);
58 stages.show(true);
59 if (stages.locked && ApplicationManager.focusedApplicationId == appId) {
60 applicationsDisplayLoader.item.select(appId);
61 }
62 } else {59 } else {
63 var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;60 var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
64 ApplicationManager.startApplication(appId, execFlags);61 ApplicationManager.startApplication(appId, execFlags);
65 stages.show(false);62 stages.show();
66 }63 }
67 }64 }
6865
@@ -89,82 +86,74 @@
89 id: volumeControl86 id: volumeControl
90 }87 }
9188
92 Keys.onVolumeUpPressed: volumeControl.volumeUp()89 WindowKeysFilter {
93 Keys.onVolumeDownPressed: volumeControl.volumeDown()90 // Handle but do not filter out volume keys
91 Keys.onVolumeUpPressed: { volumeControl.volumeUp(); event.accepted = false; }
92 Keys.onVolumeDownPressed: { volumeControl.volumeDown(); event.accepted = false; }
93
94 Keys.onPressed: {
95 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
96 dialogs.onPowerKeyPressed();
97 event.accepted = true;
98 } else {
99 event.accepted = false;
100 }
101 }
102
103 Keys.onReleased: {
104 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
105 dialogs.onPowerKeyReleased();
106 event.accepted = true;
107 } else {
108 event.accepted = false;
109 }
110 }
111 }
94112
95 Item {113 Item {
96 id: underlayClipper114 id: underlay
115 objectName: "underlay"
97 anchors.fill: parent116 anchors.fill: parent
98 anchors.rightMargin: stages.overlayWidth117
99 clip: stages.overlayMode && !stages.painting118 // Whether the underlay is fully covered by opaque UI elements.
100119 property bool fullyCovered: (panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth)
101 InputFilterArea {120 || stages.fullyShown || greeterWrapper.fullyShown
102 anchors.fill: parent121 visible: !fullyCovered
103 blockInput: parent.clip122
123 Image {
124 anchors.fill: dash
125 source: shell.width > shell.height ? "Dash/graphics/paper_landscape.png" : "Dash/graphics/paper_portrait.png"
126 fillMode: Image.PreserveAspectCrop
127 horizontalAlignment: Image.AlignRight
128 verticalAlignment: Image.AlignTop
104 }129 }
105130
106 Item {131 Dash {
107 id: underlay132 id: dash
108 objectName: "underlay"133 objectName: "dash"
109 anchors.fill: parent134
110 anchors.rightMargin: -parent.anchors.rightMargin135 available: !LightDM.Greeter.active
111136 hides: [stages, launcher, panel.indicators]
112 // Whether the underlay is fully covered by opaque UI elements.137 shown: disappearingAnimationProgress !== 1.0 && greeterWrapper.showProgress !== 1.0 &&
113 property bool fullyCovered: panel.indicators.fullyOpened && shell.width <= panel.indicators.width138 !(panel.indicators.fullyOpened && !sideStageEnabled)
114139 enabled: disappearingAnimationProgress === 0.0 && greeterWrapper.showProgress === 0.0 && edgeDemo.dashEnabled
115 // Whether the user should see the topmost application surface (if there's one at all).140
116 readonly property bool applicationSurfaceShouldBeSeen: stages.shown && !stages.painting && !stages.overlayMode141 anchors {
117142 fill: parent
118 // NB! Application surfaces are stacked behind the shell one. So they can only be seen by the user143 topMargin: panel.panelHeight
119 // through the translucent parts of the shell surface.144 }
120 visible: !fullyCovered && !applicationSurfaceShouldBeSeen145
121146 contentScale: 1.0 - 0.2 * disappearingAnimationProgress
122 Rectangle {147 opacity: 1.0 - disappearingAnimationProgress
123 anchors.fill: parent148 property real disappearingAnimationProgress: stages.showProgress
124 color: "black"149
125 opacity: dash.disappearingAnimationProgress150 // FIXME: only necessary because stages.showProgress is not animated
126 }151 Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}
127
128 Image {
129 anchors.fill: dash
130 source: shell.width > shell.height ? "Dash/graphics/paper_landscape.png" : "Dash/graphics/paper_portrait.png"
131 fillMode: Image.PreserveAspectCrop
132 horizontalAlignment: Image.AlignRight
133 verticalAlignment: Image.AlignTop
134 }
135
136 Dash {
137 id: dash
138 objectName: "dash"
139
140 available: !LightDM.Greeter.active
141 hides: [stages, launcher, panel.indicators]
142 shown: disappearingAnimationProgress !== 1.0 && greeterWrapper.showProgress !== 1.0
143 enabled: disappearingAnimationProgress === 0.0 && greeterWrapper.showProgress === 0.0 && edgeDemo.dashEnabled
144
145 anchors {
146 fill: parent
147 topMargin: panel.panelHeight
148 }
149
150 contentScale: 1.0 - 0.2 * disappearingAnimationProgress
151 opacity: 1.0 - disappearingAnimationProgress
152 property real disappearingAnimationProgress: {
153 if (stages.overlayMode) {
154 return 0;
155 } else {
156 return stages.showProgress
157 }
158 }
159
160 // FIXME: only necessary because stages.showProgress is not animated
161 Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}
162 }
163 }152 }
164 }153 }
165154
166 EdgeDragArea {155 EdgeDragArea {
167 id: stagesDragHandle156 id: stagesDragArea
168 direction: Direction.Leftwards157 direction: Direction.Leftwards
169158
170 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }159 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
@@ -174,20 +163,20 @@
174163
175 onTouchXChanged: {164 onTouchXChanged: {
176 if (status == DirectionalDragArea.Recognized) {165 if (status == DirectionalDragArea.Recognized) {
177 if (ApplicationManager.count == 0) {166 if (ApplicationManager.empty) {
178 progress = Math.max(stages.width - stagesDragHandle.width + touchX, stages.width * .3)167 progress = Math.max(stages.width - stagesDragArea.width + touchX, stages.width * .3);
179 } else {168 } else {
180 progress = stages.width - stagesDragHandle.width + touchX169 progress = stages.width - stagesDragArea.width + touchX;
181 }170 }
182 }171 }
183 }172 }
184173
185 onDraggingChanged: {174 onDraggingChanged: {
186 if (!dragging) {175 if (!dragging) {
187 if (ApplicationManager.count > 0 && progress < stages.width - units.gu(10)) {176 if (!ApplicationManager.empty && progress < stages.width - units.gu(10)) {
188 stages.show(true)177 stages.show();
189 }178 }
190 stagesDragHandle.progress = stages.width;179 stagesDragArea.progress = Qt.binding(function () { return stages.width; });
191 }180 }
192 }181 }
193 }182 }
@@ -198,253 +187,93 @@
198 width: parent.width187 width: parent.width
199 height: parent.height188 height: parent.height
200189
190 visible: !fullyHidden && !ApplicationManager.empty
191
201 x: {192 x: {
202 if (shown) {193 if (shown) {
203 if (overlayMode || locked || greeter.fakeActiveForApp !== "") {194 if (locked || greeter.fakeActiveForApp !== "") {
204 return 0;195 return 0;
205 }196 }
206 return launcher.progress197 return launcher.progress;
207 } else {198 } else {
208 return stagesDragHandle.progress199 return stagesDragArea.progress
209 }200 }
210 }201 }
211
212 Behavior on x { SmoothedAnimation { velocity: 600; duration: UbuntuAnimation.FastDuration } }202 Behavior on x { SmoothedAnimation { velocity: 600; duration: UbuntuAnimation.FastDuration } }
213203
214 property bool shown: false204 property bool shown: false
205 onShownChanged: {
206 if (shown) {
207 if (ApplicationManager.count > 0) {
208 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
209 }
210 } else {
211 if (ApplicationManager.focusedApplicationId) {
212 ApplicationManager.updateScreenshot(ApplicationManager.focusedApplicationId);
213 ApplicationManager.unfocusCurrentApplication();
214 }
215 }
216 }
215217
216 property real showProgress: overlayMode ? 0 : MathUtils.clamp(1 - x / shell.width, 0, 1)218 // Avoid a silent "divide by zero -> NaN" situation during init as shell.width will be
219 // zero. That breaks the property binding and the function won't be reevaluated once
220 // shell.width is set, with the NaN result staying there for good.
221 property real showProgress: shell.width ? MathUtils.clamp(1 - x / shell.width, 0, 1) : 0
217222
218 property bool fullyShown: x == 0223 property bool fullyShown: x == 0
219 property bool fullyHidden: x == width224 property bool fullyHidden: x == width
220225
221 property bool painting: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.painting : false
222 property bool fullscreen: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.fullscreen : false
223 property bool overlayMode: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.overlayMode : false
224 property int overlayWidth: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.overlayWidth : false
225 property bool locked: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.locked : false226 property bool locked: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.locked : false
226227
227 function show(focusApp) {228 // It might technically not be fullyShown but visually it just looks so.
229 property bool roughlyFullyShown: x >= 0 && x <= units.gu(1)
230
231 function show() {
228 shown = true;232 shown = true;
229 panel.indicators.hide();
230 edgeDemo.stopDemo();
231 greeter.hide();
232 if (!ApplicationManager.focusedApplicationId && ApplicationManager.count > 0 && focusApp) {
233 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
234 }
235 }233 }
236234
237 function hide() {235 function hide() {
238 shown = false;236 shown = false;
239 if (ApplicationManager.focusedApplicationId) {
240 ApplicationManager.unfocusCurrentApplication();
241 }
242 }237 }
243238
244 Connections {239 Connections {
245 target: ApplicationManager240 target: ApplicationManager
246
247 onFocusRequested: {241 onFocusRequested: {
248 if (greeter.fakeActiveForApp !== "" && greeter.fakeActiveForApp !== appId) {242 if (greeter.fakeActiveForApp !== "" && greeter.fakeActiveForApp !== appId) {
249 lockscreen.show();243 lockscreen.show();
250 }244 }
251 greeter.hide();245 greeter.hide();
252 stages.show(true);246 stages.show();
253 }247 }
254248
255 onFocusedApplicationIdChanged: {249 onFocusedApplicationIdChanged: {
256 if (greeter.fakeActiveForApp !== "" && greeter.fakeActiveForApp !== ApplicationManager.focusedApplicationId) {250 if (greeter.fakeActiveForApp !== "" && greeter.fakeActiveForApp !== ApplicationManager.focusedApplicationId) {
257 lockscreen.show();251 lockscreen.show();
258 }252 }
259 if (ApplicationManager.focusedApplicationId.length > 0) {253 panel.indicators.hide();
260 stages.show(false);
261 } else {
262 if (!stages.overlayMode) {
263 stages.hide();
264 }
265 }
266 }254 }
267255
268 onApplicationAdded: {256 onApplicationAdded: {
269 stages.show(false);257 if (greeter.shown) {
258 greeter.hide();
259 }
260 if (!stages.shown) {
261 stages.show();
262 }
270 }263 }
271264
272 onApplicationRemoved: {265 onEmptyChanged: {
273 if (ApplicationManager.focusedApplicationId.length == 0) {266 if (ApplicationManager.empty) {
274 stages.hide();267 stages.hide();
275 }268 }
276 }269 }
277 }270 }
278271
279 property bool dialogShown: false
280
281 Component {
282 id: logoutDialog
283 Dialog {
284 id: dialogueLogout
285 title: "Logout"
286 text: "Are you sure that you want to logout?"
287 Button {
288 text: "Cancel"
289 onClicked: {
290 PopupUtils.close(dialogueLogout);
291 stages.dialogShown = false;
292 }
293 }
294 Button {
295 text: "Yes"
296 onClicked: {
297 DBusUnitySessionService.Logout();
298 PopupUtils.close(dialogueLogout);
299 stages.dialogShown = false;
300 }
301 }
302 }
303 }
304
305 Component {
306 id: shutdownDialog
307 Dialog {
308 id: dialogueShutdown
309 title: "Shutdown"
310 text: "Are you sure that you want to shutdown?"
311 Button {
312 text: "Cancel"
313 onClicked: {
314 PopupUtils.close(dialogueShutdown);
315 stages.dialogShown = false;
316 }
317 }
318 Button {
319 text: "Yes"
320 onClicked: {
321 dBusUnitySessionServiceConnection.closeAllApps();
322 DBusUnitySessionService.Shutdown();
323 PopupUtils.close(dialogueShutdown);
324 stages.dialogShown = false;
325 }
326 }
327 }
328 }
329
330 Component {
331 id: rebootDialog
332 Dialog {
333 id: dialogueReboot
334 title: "Reboot"
335 text: "Are you sure that you want to reboot?"
336 Button {
337 text: "Cancel"
338 onClicked: {
339 PopupUtils.close(dialogueReboot)
340 stages.dialogShown = false;
341 }
342 }
343 Button {
344 text: "Yes"
345 onClicked: {
346 dBusUnitySessionServiceConnection.closeAllApps();
347 DBusUnitySessionService.Reboot();
348 PopupUtils.close(dialogueReboot);
349 stages.dialogShown = false;
350 }
351 }
352 }
353 }
354
355 Component {
356 id: powerDialog
357 Dialog {
358 id: dialoguePower
359 title: "Power"
360 text: i18n.tr("Are you sure you would like to turn power off?")
361 Button {
362 text: i18n.tr("Power off")
363 onClicked: {
364 dBusUnitySessionServiceConnection.closeAllApps();
365 PopupUtils.close(dialoguePower);
366 stages.dialogShown = false;
367 shutdownFadeOutRectangle.enabled = true;
368 shutdownFadeOutRectangle.visible = true;
369 shutdownFadeOut.start();
370 }
371 }
372 Button {
373 text: i18n.tr("Restart")
374 onClicked: {
375 dBusUnitySessionServiceConnection.closeAllApps();
376 DBusUnitySessionService.Reboot();
377 PopupUtils.close(dialoguePower);
378 stages.dialogShown = false;
379 }
380 }
381 Button {
382 text: i18n.tr("Cancel")
383 onClicked: {
384 PopupUtils.close(dialoguePower);
385 stages.dialogShown = false;
386 }
387 }
388 }
389 }
390
391 function showPowerDialog() {
392 if (!stages.dialogShown) {
393 stages.dialogShown = true;
394 PopupUtils.open(powerDialog);
395 }
396 }
397
398 Connections {
399 id: dBusUnitySessionServiceConnection
400 objectName: "dBusUnitySessionServiceConnection"
401 target: DBusUnitySessionService
402
403 function closeAllApps() {
404 while (true) {
405 var app = ApplicationManager.get(0);
406 if (app === null) {
407 break;
408 }
409 ApplicationManager.stopApplication(app.appId);
410 }
411 }
412
413 onLogoutRequested: {
414 // Display a dialog to ask the user to confirm.
415 if (!stages.dialogShown) {
416 stages.dialogShown = true;
417 PopupUtils.open(logoutDialog);
418 }
419 }
420
421 onShutdownRequested: {
422 // Display a dialog to ask the user to confirm.
423 if (!stages.dialogShown) {
424 stages.dialogShown = true;
425 PopupUtils.open(shutdownDialog);
426 }
427 }
428
429 onRebootRequested: {
430 // Display a dialog to ask the user to confirm.
431 if (!stages.dialogShown) {
432 stages.dialogShown = true;
433 PopupUtils.open(rebootDialog);
434 }
435 }
436
437 onLogoutReady: {
438 closeAllApps();
439 Qt.quit();
440 }
441 }
442
443 Loader {272 Loader {
444 id: applicationsDisplayLoader273 id: applicationsDisplayLoader
445 anchors.fill: parent274 anchors.fill: parent
446275
447 source: shell.sideStageEnabled ? "Stages/StageWithSideStage.qml" : "Stages/PhoneStage.qml"276 source: shell.sideStageEnabled ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
448277
449 Binding {278 Binding {
450 target: applicationsDisplayLoader.item279 target: applicationsDisplayLoader.item
@@ -453,27 +282,56 @@
453 }282 }
454 Binding {283 Binding {
455 target: applicationsDisplayLoader.item284 target: applicationsDisplayLoader.item
456 property: "moving"
457 value: !stages.fullyShown
458 }
459 Binding {
460 target: applicationsDisplayLoader.item
461 property: "shown"
462 value: stages.shown
463 }
464 Binding {
465 target: applicationsDisplayLoader.item
466 property: "dragAreaWidth"285 property: "dragAreaWidth"
467 value: shell.edgeSize286 value: shell.edgeSize
468 }287 }
469 Binding {288 Binding {
470 target: applicationsDisplayLoader.item289 target: applicationsDisplayLoader.item
290 property: "maximizedAppTopMargin"
291 // Not just using panel.panelHeight as that changes depending on the focused app.
292 value: panel.indicators.panelHeight
293 }
294 Binding {
295 target: applicationsDisplayLoader.item
296 property: "interactive"
297 value: stages.roughlyFullyShown && !greeter.shown && !lockscreen.shown
298 && panel.indicators.fullyClosed
299 }
300 Binding {
301 target: applicationsDisplayLoader.item
471 property: "spreadEnabled"302 property: "spreadEnabled"
472 value: greeter.fakeActiveForApp === "" // to support emergency dialer hack303 value: greeter.fakeActiveForApp === "" // to support emergency dialer hack
473 }304 }
474 }305 }
475 }306 }
476307
308 InputMethod {
309 id: inputMethod
310 objectName: "inputMethod"
311 anchors { fill: parent; topMargin: panel.panelHeight }
312 z: notifications.useModal || panel.indicators.shown ? overlay.z + 1 : overlay.z - 1
313 }
314
315 Connections {
316 target: SurfaceManager
317 onSurfaceCreated: {
318 if (surface.type == MirSurfaceItem.InputMethod) {
319 inputMethod.surface = surface;
320 }
321 }
322
323 onSurfaceDestroyed: {
324 if (inputMethod.surface == surface) {
325 inputMethod.surface = null;
326 surface.parent = null;
327 }
328 if (!surface.parent) {
329 // there's no one displaying it. delete it right away
330 surface.release();
331 }
332 }
333 }
334
477 Lockscreen {335 Lockscreen {
478 id: lockscreen336 id: lockscreen
479 objectName: "lockscreen"337 objectName: "lockscreen"
@@ -486,7 +344,7 @@
486 showAnimation: StandardAnimation { property: "opacity"; to: 1 }344 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
487 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }345 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
488 y: panel.panelHeight346 y: panel.panelHeight
489 x: required ? 0 : - width347 visible: required
490 width: parent.width348 width: parent.width
491 height: parent.height - panel.panelHeight349 height: parent.height - panel.panelHeight
492 background: shell.background350 background: shell.background
@@ -566,6 +424,7 @@
566 StandardAnimation {}424 StandardAnimation {}
567 }425 }
568426
427 property bool fullyShown: showProgress === 1.0
569 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)428 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
570 onShowProgressChanged: if (LightDM.Greeter.promptless && showProgress === 0) greeter.login()429 onShowProgressChanged: if (LightDM.Greeter.promptless && showProgress === 0) greeter.login()
571430
@@ -632,12 +491,6 @@
632 }491 }
633 }492 }
634493
635 InputFilterArea {
636 anchors.fill: parent
637 blockInput: ApplicationManager.focusedApplicationId.length === 0 || greeter.shown || lockscreen.shown || launcher.shown
638 || panel.indicators.shown
639 }
640
641 Connections {494 Connections {
642 id: powerConnection495 id: powerConnection
643 target: Powerd496 target: Powerd
@@ -680,7 +533,7 @@
680 return;533 return;
681 }534 }
682535
683 if (stages.shown && !stages.overlayMode && !stages.locked) {536 if (!stages.locked) {
684 stages.hide();537 stages.hide();
685 launcher.fadeOut();538 launcher.fadeOut();
686 } else {539 } else {
@@ -695,6 +548,7 @@
695548
696 Item {549 Item {
697 id: overlay550 id: overlay
551 z: 10
698552
699 anchors.fill: parent553 anchors.fill: parent
700554
@@ -709,29 +563,13 @@
709 width: parent.width > units.gu(60) ? units.gu(40) : parent.width563 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
710 panelHeight: units.gu(3)564 panelHeight: units.gu(3)
711 }565 }
712 property string focusedAppId: ApplicationManager.focusedApplicationId566
713 property var focusedApplication: ApplicationManager.findApplication(focusedAppId)567 property bool topmostApplicationIsFullscreen:
714 fullscreenMode: (focusedApplication && stages.fullscreen && !LightDM.Greeter.active) || greeter.fakeActiveForApp !== ""568 ApplicationManager.focusedApplicationId &&
715569 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
716 InputFilterArea {570
717 anchors {571 fullscreenMode: (stages.roughlyFullyShown && topmostApplicationIsFullscreen
718 top: parent.top572 && !LightDM.Greeter.active) || greeter.fakeActiveForApp !== ""
719 left: parent.left
720 right: parent.right
721 }
722 height: (panel.fullscreenMode) ? shell.edgeSize : panel.panelHeight
723 blockInput: true
724 }
725 }
726
727 InputFilterArea {
728 blockInput: launcher.shown
729 anchors {
730 top: parent.top
731 bottom: parent.bottom
732 left: parent.left
733 }
734 width: launcher.width
735 }573 }
736574
737 Launcher {575 Launcher {
@@ -774,11 +612,6 @@
774 MouseArea {612 MouseArea {
775 anchors.fill: parent613 anchors.fill: parent
776 }614 }
777
778 InputFilterArea {
779 anchors.fill: parent
780 blockInput: modalNotificationBackground.visible
781 }
782 }615 }
783616
784 Notifications {617 Notifications {
@@ -804,36 +637,7 @@
804 PropertyChanges { target: notifications; width: units.gu(38) }637 PropertyChanges { target: notifications; width: units.gu(38) }
805 }638 }
806 ]639 ]
807640 }
808 InputFilterArea {
809 anchors { left: parent.left; right: parent.right }
810 height: parent.contentHeight
811 blockInput: height > 0
812 }
813 }
814 }
815
816 focus: true
817 onFocusChanged: if (!focus) forceActiveFocus();
818
819 InputFilterArea {
820 anchors {
821 top: parent.top
822 bottom: parent.bottom
823 left: parent.left
824 }
825 width: shell.edgeSize
826 blockInput: true
827 }
828
829 InputFilterArea {
830 anchors {
831 top: parent.top
832 bottom: parent.bottom
833 right: parent.right
834 }
835 width: shell.edgeSize
836 blockInput: true
837 }641 }
838642
839 Binding {643 Binding {
@@ -842,24 +646,22 @@
842 value: "unity8"646 value: "unity8"
843 }647 }
844648
845 OSKController {649 Dialogs {
846 anchors.topMargin: panel.panelHeight650 id: dialogs
847 anchors.fill: parent // as needs to know the geometry of the shell651 anchors.fill: parent
848 }652 z: overlay.z + 10
849653 onPowerOffClicked: {
850 //FIXME: This should be handled in the input stack, keyboard shouldnt propagate654 shutdownFadeOutRectangle.enabled = true;
851 MouseArea {655 shutdownFadeOutRectangle.visible = true;
852 anchors.bottom: parent.bottom656 shutdownFadeOut.start();
853 anchors.left: parent.left657 }
854 anchors.right: parent.right
855 height: ApplicationManager.keyboardVisible ? ApplicationManager.keyboardHeight : 0
856
857 enabled: ApplicationManager.keyboardVisible
858 }658 }
859659
860 Label {660 Label {
661 id: alphaDisclaimerLabel
861 anchors.centerIn: parent662 anchors.centerIn: parent
862 visible: ApplicationManager.fake ? ApplicationManager.fake : false663 visible: ApplicationManager.fake ? ApplicationManager.fake : false
664 z: dialogs.z + 10
863 text: "EARLY ALPHA\nNOT READY FOR USE"665 text: "EARLY ALPHA\nNOT READY FOR USE"
864 color: "lightgrey"666 color: "lightgrey"
865 opacity: 0.2667 opacity: 0.2
@@ -873,6 +675,7 @@
873675
874 EdgeDemo {676 EdgeDemo {
875 id: edgeDemo677 id: edgeDemo
678 z: alphaDisclaimerLabel.z + 10
876 greeter: greeter679 greeter: greeter
877 launcher: launcher680 launcher: launcher
878 dash: dash681 dash: dash
@@ -885,35 +688,9 @@
885 onShowHome: showHome()688 onShowHome: showHome()
886 }689 }
887690
888 Keys.onPressed: {
889 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
890 if (!powerKeyTimer.running) {
891 powerKeyTimer.start();
892 }
893 event.accepted = true;
894 }
895 }
896
897 Keys.onReleased: {
898 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
899 powerKeyTimer.stop();
900 event.accepted = true;
901 }
902 }
903
904 Timer {
905 id: powerKeyTimer
906 interval: 2000
907 repeat: false
908 triggeredOnStart: false
909
910 onTriggered: {
911 stages.showPowerDialog();
912 }
913 }
914
915 Rectangle {691 Rectangle {
916 id: shutdownFadeOutRectangle692 id: shutdownFadeOutRectangle
693 z: edgeDemo.z + 10
917 enabled: false694 enabled: false
918 visible: false695 visible: false
919 color: "black"696 color: "black"
@@ -930,4 +707,5 @@
930 }707 }
931 }708 }
932 }709 }
710
933}711}
934712
=== added directory 'qml/Stages/Animations'
=== added file 'qml/Stages/Animations/BaseSurfaceAnimation.qml'
--- qml/Stages/Animations/BaseSurfaceAnimation.qml 1970-01-01 00:00:00 +0000
+++ qml/Stages/Animations/BaseSurfaceAnimation.qml 2014-07-29 14:07:22 +0000
@@ -0,0 +1,93 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18
19/* This is the base case for surface animations, used when adding/removing * child surfaces.
20 * The class is meant to be overridden and changes/animations provided for state changes.
21 * NB. It is important to release the surface at the end of the "out" animation.
22 *
23 * Example - Simple fade in/out
24 *
25 * BaseSurfaceAnimation {
26 * outChanges: [ PropertyChanges { target: animation.surface; opacity: 0.0 } ]
27 * outAnimations: [
28 SequentialAnimation {
29 * NumberAnimation { target: animation.surface; property: "opacity"; duration: 300 }
30 * ScriptAction { script: { if (animation.parent.removing) animation.surface.release(); } }
31 * }
32 * ]
33 *
34 * inChanges: [ PropertyChanges { target: animation.surface; opacity: 1.0 } ]
35 * inAnimations: [ NumberAnimation { target: animation.surface; property: "opacity"; duration: 300 } ]
36 * }
37 */
38Item {
39 id: base
40 property Item surface: null
41
42 // changes applied when state changes to "in"
43 property list<QtObject> outChanges
44 // transition animations when changing state to "in"
45 property list<QtObject> outAnimations
46
47 // changes applied when state changes to "out"
48 property list<QtObject> inChanges
49 // transition animations when changing state to "out"
50 property list<QtObject> inAnimations
51
52 function start() {
53 // "prep" state forces outChanges without transition animations.
54 state = "prep"
55 state = "in";
56 }
57 function end() {
58 state = "out";
59 }
60
61 states: [
62 State {
63 name: "baseAnimation"
64 PropertyChanges { target: base.surface; anchors.fill: undefined }
65 },
66
67 State {
68 name: "prep"
69 extend: "baseAnimation"
70 changes: outChanges
71 },
72 State {
73 name: "out"
74 extend: "prep"
75 },
76 State {
77 name: "in"
78 extend: "baseAnimation"
79 changes: inChanges
80 }
81 ]
82
83 transitions: [
84 Transition {
85 to: "out"
86 animations: outAnimations
87 },
88 Transition {
89 to: "in"
90 animations: inAnimations
91 }
92 ]
93}
094
=== added file 'qml/Stages/Animations/DarkenAndFadeInAnimation.qml'
--- qml/Stages/Animations/DarkenAndFadeInAnimation.qml 1970-01-01 00:00:00 +0000
+++ qml/Stages/Animations/DarkenAndFadeInAnimation.qml 2014-07-29 14:07:22 +0000
@@ -0,0 +1,48 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18import Ubuntu.Components 1.1
19
20BaseSurfaceAnimation {
21 id: animation
22
23 property Rectangle darkenItem: Rectangle {
24 parent: animation.surface.parent
25 anchors.fill: parent
26 color: Qt.rgba(0,0,0,0.0)
27 }
28
29 outChanges: [ PropertyChanges { target: animation.surface; opacity: 0.0 } ]
30 outAnimations: [
31 SequentialAnimation {
32 UbuntuNumberAnimation { target: animation.surface; property: "opacity"; duration: UbuntuAnimation.FastDuration }
33 ColorAnimation { target: darkenItem; duration: UbuntuAnimation.FastDuration }
34 ScriptAction { script: { if (animation.parent.removing) animation.surface.release(); } }
35 }
36 ]
37
38 inChanges: [
39 PropertyChanges { target: darkenItem; color: Qt.rgba(0,0,0,0.7) },
40 PropertyChanges { target: animation.surface; opacity: 1.0 }
41 ]
42 inAnimations: [
43 SequentialAnimation {
44 ColorAnimation { target: darkenItem; duration: UbuntuAnimation.FastDuration }
45 UbuntuNumberAnimation { target: animation.surface; property: "opacity"; duration: UbuntuAnimation.FastDuration }
46 }
47 ]
48}
049
=== added file 'qml/Stages/Animations/SwipeFromBottomAnimation.qml'
--- qml/Stages/Animations/SwipeFromBottomAnimation.qml 1970-01-01 00:00:00 +0000
+++ qml/Stages/Animations/SwipeFromBottomAnimation.qml 2014-07-29 14:07:22 +0000
@@ -0,0 +1,48 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18
19BaseSurfaceAnimation {
20 id: animation
21
22 outChanges: [ AnchorChanges { target: animation.surface; anchors.top: animation.parent.bottom } ]
23 outAnimations: [
24 SequentialAnimation {
25 PropertyAction { target: animation.parent; property: "clip"; value: true }
26 AnchorAnimation { easing.type: Easing.InOutQuad; duration: 400 }
27 PropertyAction { target: animation.surface; property: "visible"; value: !animation.parent.removing }
28 PropertyAction { target: animation.parent; property: "clip"; value: false }
29 ScriptAction { script: { if (animation.parent.removing) animation.surface.release(); } }
30 }
31 ]
32
33 inChanges: [
34 AnchorChanges {
35 target: animation.surface;
36 anchors.top: animation.parent.top
37 anchors.right: undefined
38 anchors.bottom: undefined
39 anchors.left: undefined
40 } ]
41 inAnimations: [
42 SequentialAnimation {
43 PropertyAction { target: animation.parent; property: "clip"; value: true }
44 AnchorAnimation { easing.type: Easing.InOutQuad; duration: 400 }
45 PropertyAction { target: animation.parent; property: "clip"; value: false }
46 }
47 ]
48}
049
=== added file 'qml/Stages/Animations/SwipeUpAnimation.qml'
--- qml/Stages/Animations/SwipeUpAnimation.qml 1970-01-01 00:00:00 +0000
+++ qml/Stages/Animations/SwipeUpAnimation.qml 2014-07-29 14:07:22 +0000
@@ -0,0 +1,49 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18
19BaseSurfaceAnimation {
20 id: animation
21
22 outAnimations: [
23 SequentialAnimation {
24 PropertyAction { target: animation.parent; property: "clip"; value: true }
25 PropertyAction { target: animation.surface; property: "visible"; value: true }
26 AnchorAnimation { easing.type: Easing.InOutQuad; duration: 400 }
27 PropertyAction { target: animation.parent; property: "clip"; value: false }
28 ScriptAction { script: { if (animation.parent.removing) animation.surface.release(); } }
29 }
30 ]
31
32 inChanges: [
33 AnchorChanges {
34 target: animation.surface;
35 anchors.top: undefined
36 anchors.right: undefined
37 anchors.bottom: animation.parent.top
38 anchors.left: undefined
39 }
40 ]
41 inAnimations: [
42 SequentialAnimation {
43 PropertyAction { target: animation.parent; property: "clip"; value: true }
44 AnchorAnimation { easing.type: Easing.InOutQuad; duration: 400 }
45 PropertyAction { target: animation.surface; property: "visible"; value: false}
46 PropertyAction { target: animation.parent; property: "clip"; value: false }
47 }
48 ]
49}
050
=== modified file 'qml/Stages/PhoneStage.qml'
--- qml/Stages/PhoneStage.qml 2014-06-24 23:52:12 +0000
+++ qml/Stages/PhoneStage.qml 2014-07-29 14:07:22 +0000
@@ -18,7 +18,6 @@
18import Ubuntu.Components 0.118import Ubuntu.Components 0.1
19import Ubuntu.Gestures 0.119import Ubuntu.Gestures 0.1
20import Unity.Application 0.120import Unity.Application 0.1
21import LightDM 0.1 as LightDM
22import Utils 0.121import Utils 0.1
23import "../Components"22import "../Components"
2423
@@ -26,88 +25,51 @@
26 id: root25 id: root
2726
28 // Controls to be set from outside27 // Controls to be set from outside
29 property bool shown: false
30 property bool moving: false
31 property int dragAreaWidth28 property int dragAreaWidth
29 property real maximizedAppTopMargin
30 property bool interactive
31 property bool spreadEnabled: true // If false, animations and right edge will be disabled
3232
33 // State information propagated to the outside33 // State information propagated to the outside
34 readonly property bool painting: mainScreenshotImage.visible || fadeInScreenshotImage.visible || appSplash.visible || spreadView.visible34 readonly property bool locked: spreadView.phase == 2
35 property bool fullscreen: priv.focusedApplication ? priv.focusedApplication.fullscreen : false
36 property bool locked: spreadView.visible
37 property bool spreadEnabled: true
38
39 // Not used for PhoneStage, only useful for SideStage and similar
40 property bool overlayMode: false
41 property int overlayWidth: 0
4235
43 function select(appId) {36 function select(appId) {
44 spreadView.snapTo(priv.indexOf(appId))37 spreadView.snapTo(priv.indexOf(appId));
45 }38 }
4639
47 onMovingChanged: {40 onWidthChanged: {
48 if (moving) {41 spreadView.selectedIndex = -1;
49 if (ApplicationManager.focusedApplicationId) {42 spreadView.phase = 0;
50 priv.requestNewScreenshot();43 spreadView.contentX = -spreadView.shift;
51 } else {
52 mainScreenshotImage.anchors.leftMargin = 0;
53 mainScreenshotImage.source = ApplicationManager.get(0).screenshot;
54 mainScreenshotImage.visible = true;
55 }
56 } else {
57 mainScreenshotImage.visible = false;
58 }
59 }44 }
6045
61 Connections {46 Connections {
62 target: ApplicationManager47 target: ApplicationManager
6348
64 onFocusRequested: {49 onFocusRequested: {
65 if (spreadView.visible) {50 if (spreadView.phase > 0) {
66 spreadView.snapTo(priv.indexOf(appId));51 spreadView.snapTo(priv.indexOf(appId));
67 } else {52 } else {
68 priv.switchToApp(appId);53 priv.switchToApp(appId);
69 }54 }
70 }55 }
7156
72 onFocusedApplicationIdChanged: {
73 if (ApplicationManager.focusedApplicationId.length > 0) {
74 if (priv.secondApplicationStarting || priv.applicationStarting) {
75 appSplashTimer.restart();
76 } else {
77 var application = priv.focusedApplication;
78 root.fullscreen = application.fullscreen;
79 mainScreenshotImage.source = application.screenshot;
80 }
81 } else {
82 spreadView.selectedIndex = -1;
83 spreadView.phase = 0;
84 spreadView.contentX = -spreadView.shift;
85 }
86 }
87
88 onApplicationAdded: {57 onApplicationAdded: {
89 if (!priv.focusedApplication) {58 if (spreadView.phase == 2) {
90 mainScreenshotImage.source = "";59 spreadView.snapTo(ApplicationManager.count - 1);
91 mainScreenshotImage.visible = false;
92 priv.applicationStarting = true;
93 } else {60 } else {
94 mainScreenshotImage.source = "";61 spreadView.phase = 0;
95 priv.newFocusedAppId = appId;62 spreadView.contentX = -spreadView.shift;
96 priv.secondApplicationStarting = true;63 priv.switchToApp(appId);
97 priv.requestNewScreenshot();
98 }
99
100 if (spreadView.visible) {
101 spreadView.snapTo(0);
102 }64 }
103 }65 }
10466
105 onApplicationRemoved: {67 onApplicationRemoved: {
106 if (ApplicationManager.count == 0) {68 // Unless we're closing the app ourselves in the spread,
107 mainScreenshotImage.source = ""69 // lets make sure the spread doesn't mess up by the changing app list.
108 mainScreenshotImage.visible = false;70 if (spreadView.closingIndex == -1) {
109 } else {71 spreadView.phase = 0;
110 mainScreenshotImage.source = ApplicationManager.get(0).screenshot;72 spreadView.contentX = -spreadView.shift;
111 }73 }
112 }74 }
113 }75 }
@@ -117,46 +79,11 @@
11779
118 property string focusedAppId: ApplicationManager.focusedApplicationId80 property string focusedAppId: ApplicationManager.focusedApplicationId
119 property var focusedApplication: ApplicationManager.findApplication(focusedAppId)81 property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
120 property url focusedScreenshot: focusedApplication ? focusedApplication.screenshot : ""
121
122 property bool waitingForScreenshot: false
123
124 property bool applicationStarting: false
125 property bool secondApplicationStarting: false
126
127 property string newFocusedAppId
128
129 onFocusedScreenshotChanged: {
130 if (root.moving && priv.waitingForScreenshot) {
131 mainScreenshotImage.anchors.leftMargin = 0;
132 mainScreenshotImage.source = priv.focusedScreenshot
133 mainScreenshotImage.visible = true;
134 } else if (priv.secondApplicationStarting && priv.waitingForScreenshot) {
135 applicationSwitchingAnimation.start();
136 if (LightDM.Greeter.active && !LightDM.Greeter.promptless) {
137 // When greeter is up, no fancy animations, since user may
138 // glimpse an app they shouldn't
139 applicationSwitchingAnimation.complete();
140 }
141 }
142 waitingForScreenshot = false;
143 }
144
145 function requestNewScreenshot() {
146 waitingForScreenshot = true;
147 ApplicationManager.updateScreenshot(focusedAppId);
148 }
14982
150 function switchToApp(appId) {83 function switchToApp(appId) {
151 if (priv.focusedAppId) {84 if (priv.focusedAppId) {
152 priv.newFocusedAppId = appId;85 spreadView.focusChanging = true;
153 root.fullscreen = ApplicationManager.findApplication(appId).fullscreen;86 ApplicationManager.focusApplication(appId);
154 applicationSwitchingAnimation.start();
155 if (LightDM.Greeter.active && !LightDM.Greeter.promptless) {
156 // When greeter is up, no fancy animations, since user may
157 // glimpse an app they shouldn't
158 applicationSwitchingAnimation.complete();
159 }
160 } else {87 } else {
161 ApplicationManager.focusApplication(appId);88 ApplicationManager.focusApplication(appId);
162 }89 }
@@ -173,158 +100,6 @@
173100
174 }101 }
175102
176 // FIXME: the signal connections seems to get lost.
177 Connections {
178 target: priv.focusedApplication
179 onScreenshotChanged: priv.focusedScreenshot = priv.focusedApplication.screenshot
180 }
181 Binding {
182 target: root
183 property: "fullscreen"
184 value: priv.focusedApplication ? priv.focusedApplication.fullscreen : false
185 }
186
187 Timer {
188 id: appSplashTimer
189 // FIXME: We really need to show something meaningful in the app surface instead of guessing
190 // when it might be ready
191 interval: 500
192 repeat: false
193 onTriggered: {
194 priv.applicationStarting = false;
195 priv.secondApplicationStarting = false;
196 }
197 }
198
199 SequentialAnimation {
200 id: applicationSwitchingAnimation
201 // setup
202 PropertyAction { target: mainScreenshotImage; property: "anchors.leftMargin"; value: 0 }
203 PropertyAction { target: mainScreenshotImage; property: "source"; value: priv.focusedScreenshot }
204 PropertyAction { targets: [mainScreenshotImage, fadeInScreenshotImage]; property: "visible"; value: true }
205 PropertyAction { target: fadeInScreenshotImage; property: "source"; value: {
206 var newFocusedApp = ApplicationManager.findApplication(priv.newFocusedAppId);
207 return newFocusedApp ? newFocusedApp.screenshot : "" }
208 }
209 PropertyAction { target: fadeInScreenshotImage; property: "opacity"; value: 0 }
210 PropertyAction { target: fadeInScreenshotImage; property: "scale"; value: .8 }
211
212
213 // The actual animation
214 ParallelAnimation {
215 UbuntuNumberAnimation { target: mainScreenshotImage; property: "anchors.leftMargin"; to: root.width; duration: UbuntuAnimation.SlowDuration }
216 UbuntuNumberAnimation { target: fadeInScreenshotImage; properties: "opacity,scale"; to: 1; duration: UbuntuAnimation.SlowDuration }
217 }
218
219 // restore stuff
220 ScriptAction { script: ApplicationManager.focusApplication(priv.newFocusedAppId); }
221 PropertyAction { target: fadeInScreenshotImage; property: "visible"; value: false }
222 PropertyAction { target: mainScreenshotImage; property: "visible"; value: false }
223 }
224
225 // FIXME: Drop this and make the imageprovider show a splashscreen instead
226 Rectangle {
227 id: appSplash2
228 anchors.fill: parent
229 color: "black"
230 visible: priv.secondApplicationStarting
231 }
232 Image {
233 id: fadeInScreenshotImage
234 anchors { left: parent.left; bottom: parent.bottom }
235 width: parent.width
236 scale: .7
237 visible: false
238 }
239
240 Rectangle {
241 id: appSplash
242 anchors.fill: parent
243 color: "black"
244 visible: priv.applicationStarting
245
246 WaitingDots {
247 }
248 }
249 Image {
250 id: mainScreenshotImage
251 anchors { left: parent.left; bottom: parent.bottom }
252 width: parent.width
253 visible: false
254 }
255
256 EdgeDragArea {
257 id: spreadDragArea
258 direction: Direction.Leftwards
259 enabled: ApplicationManager.count > 1 && spreadView.phase != 2 && root.spreadEnabled
260
261 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
262 width: root.dragAreaWidth
263
264 // Sitting at the right edge of the screen, this EdgeDragArea directly controls the spreadView when
265 // attachedToView is true. When the finger movement passes positionMarker3 we detach it from the
266 // spreadView and make the spreadView snap to positionMarker4.
267 property bool attachedToView: true
268
269 property var gesturePoints: new Array()
270
271 onTouchXChanged: {
272 if (!dragging && !priv.waitingForScreenshot) {
273 // Initial touch. Let's update the screenshot and reset the spreadView to the starting position.
274 priv.requestNewScreenshot();
275 spreadView.phase = 0;
276 spreadView.contentX = -spreadView.shift;
277 }
278 if (dragging && attachedToView) {
279 // Gesture recognized. Let's move the spreadView with the finger
280 spreadView.contentX = -touchX - spreadView.shift;
281 }
282 if (attachedToView && spreadView.shiftedContentX >= spreadView.width * spreadView.positionMarker3) {
283 // We passed positionMarker3. Detach from spreadView and snap it.
284 attachedToView = false;
285 spreadView.snap();
286 }
287 gesturePoints.push(touchX);
288 }
289
290 onStatusChanged: {
291 if (status == DirectionalDragArea.Recognized) {
292 attachedToView = true;
293 }
294 }
295
296 onDraggingChanged: {
297 if (dragging) {
298 // Gesture recognized. Start recording this gesture
299 gesturePoints = [];
300 return;
301 }
302
303 // Ok. The user released. Find out if it was a one-way movement.
304 var oneWayFlick = true;
305 var smallestX = spreadDragArea.width;
306 for (var i = 0; i < gesturePoints.length; i++) {
307 if (gesturePoints[i] >= smallestX) {
308 oneWayFlick = false;
309 break;
310 }
311 smallestX = gesturePoints[i];
312 }
313 gesturePoints = [];
314
315 if (oneWayFlick && spreadView.shiftedContentX > units.gu(2) &&
316 spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
317 // If it was a short one-way movement, do the Alt+Tab switch
318 // no matter if we didn't cross positionMarker1 yet.
319 spreadView.snapTo(1);
320 } else if (!dragging && attachedToView) {
321 // otherwise snap to the closest snap position we can find
322 // (might be back to start, to app 1 or to spread)
323 spreadView.snap();
324 }
325 }
326 }
327
328 Rectangle {103 Rectangle {
329 id: coverFlipBackground104 id: coverFlipBackground
330 anchors.fill: parent105 anchors.fill: parent
@@ -332,16 +107,12 @@
332 visible: spreadView.visible107 visible: spreadView.visible
333 }108 }
334109
335 InputFilterArea {
336 anchors.fill: root
337 blockInput: spreadView.visible
338 }
339110
340 Flickable {111 Flickable {
341 id: spreadView112 id: spreadView
342 objectName: "spreadView"113 objectName: "spreadView"
343 anchors.fill: parent114 anchors.fill: parent
344 visible: spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1 || snapAnimation.running115 interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1) && draggedIndex == -1
345 contentWidth: spreadRow.width - shift116 contentWidth: spreadRow.width - shift
346 contentX: -shift117 contentX: -shift
347118
@@ -350,8 +121,8 @@
350 // that, the beginning of the gesture starts with a negative value for contentX121 // that, the beginning of the gesture starts with a negative value for contentX
351 // so the flickable wants to pull it into the view already. "shift" tunes the122 // so the flickable wants to pull it into the view already. "shift" tunes the
352 // distance where to "lock" the content.123 // distance where to "lock" the content.
353 property real shift: width / 2124 readonly property real shift: width / 2
354 property real shiftedContentX: contentX + shift125 readonly property real shiftedContentX: contentX + shift
355126
356 property int tileDistance: width / 4127 property int tileDistance: width / 4
357128
@@ -375,6 +146,10 @@
375 property int phase: 0146 property int phase: 0
376147
377 property int selectedIndex: -1148 property int selectedIndex: -1
149 property int draggedIndex: -1
150 property int closingIndex: -1
151
152 property bool focusChanging: false
378153
379 onShiftedContentXChanged: {154 onShiftedContentXChanged: {
380 switch (phase) {155 switch (phase) {
@@ -398,9 +173,9 @@
398 snapAnimation.targetContentX = -shift;173 snapAnimation.targetContentX = -shift;
399 snapAnimation.start();174 snapAnimation.start();
400 } else if (shiftedContentX < positionMarker2 * width) {175 } else if (shiftedContentX < positionMarker2 * width) {
401 snapTo(1)176 snapTo(1);
402 } else if (shiftedContentX < positionMarker3 * width) {177 } else if (shiftedContentX < positionMarker3 * width) {
403 snapTo(1)178 snapTo(1);
404 } else if (phase < 2){179 } else if (phase < 2){
405 // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation.180 // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation.
406 snapAnimation.targetContentX = width * positionMarker4 + 1 - shift;181 snapAnimation.targetContentX = width * positionMarker4 + 1 - shift;
@@ -408,9 +183,18 @@
408 }183 }
409 }184 }
410 function snapTo(index) {185 function snapTo(index) {
186 if (ApplicationManager.count <= index) {
187 // In case we're trying to snap to some non existing app, lets snap back to the first one
188 index = 0;
189 }
411 spreadView.selectedIndex = index;190 spreadView.selectedIndex = index;
412 root.fullscreen = ApplicationManager.get(index).fullscreen;191 // If we're not in full spread mode yet, always unwind to start pos
413 snapAnimation.targetContentX = -shift;192 // otherwise unwind up to progress 0 of the selected index
193 if (spreadView.phase < 2) {
194 snapAnimation.targetContentX = -shift;
195 } else {
196 snapAnimation.targetContentX = -shift + index * spreadView.tileDistance;
197 }
414 snapAnimation.start();198 snapAnimation.start();
415 }199 }
416200
@@ -429,7 +213,8 @@
429 script: {213 script: {
430 if (spreadView.selectedIndex >= 0) {214 if (spreadView.selectedIndex >= 0) {
431 ApplicationManager.focusApplication(ApplicationManager.get(spreadView.selectedIndex).appId);215 ApplicationManager.focusApplication(ApplicationManager.get(spreadView.selectedIndex).appId);
432 spreadView.selectedIndex = -1216
217 spreadView.selectedIndex = -1;
433 spreadView.phase = 0;218 spreadView.phase = 0;
434 spreadView.contentX = -spreadView.shift;219 spreadView.contentX = -spreadView.shift;
435 }220 }
@@ -443,6 +228,15 @@
443 // tileDistance * app count (with a minimum of 3 apps, in order to also allow moving 1 and 2 apps a bit)228 // tileDistance * app count (with a minimum of 3 apps, in order to also allow moving 1 and 2 apps a bit)
444 // + some constant value (still scales with the screen width) which looks good and somewhat fills the screen229 // + some constant value (still scales with the screen width) which looks good and somewhat fills the screen
445 width: Math.max(3, ApplicationManager.count) * spreadView.tileDistance + (spreadView.width - spreadView.tileDistance) * 1.5230 width: Math.max(3, ApplicationManager.count) * spreadView.tileDistance + (spreadView.width - spreadView.tileDistance) * 1.5
231 Behavior on width {
232 enabled: spreadView.closingIndex >= 0
233 UbuntuNumberAnimation {}
234 }
235 onWidthChanged: {
236 if (spreadView.closingIndex >= 0) {
237 spreadView.contentX = Math.min(spreadView.contentX, width - spreadView.width - spreadView.shift);
238 }
239 }
446240
447 x: spreadView.contentX241 x: spreadView.contentX
448242
@@ -462,18 +256,55 @@
462 height: spreadView.height256 height: spreadView.height
463 selected: spreadView.selectedIndex == index257 selected: spreadView.selectedIndex == index
464 otherSelected: spreadView.selectedIndex >= 0 && !selected258 otherSelected: spreadView.selectedIndex >= 0 && !selected
259 interactive: !spreadView.interactive && spreadView.phase === 0
260 && spreadView.shiftedContentX === 0 && root.interactive && index === 0
261 swipeToCloseEnabled: spreadView.interactive
262 maximizedAppTopMargin: root.maximizedAppTopMargin
263 dropShadow: spreadView.shiftedContentX > 0 || spreadDragArea.status == DirectionalDragArea.Undecided
465264
466 z: index265 z: behavioredIndex
467 x: index == 0 ? 0 : spreadView.width + (index - 1) * spreadView.tileDistance266 x: index == 0 ? 0 : spreadView.width + (index - 1) * spreadView.tileDistance
267 property real behavioredIndex: index
268 Behavior on behavioredIndex {
269 enabled: spreadView.closingIndex >= 0
270 UbuntuNumberAnimation {
271 onRunningChanged: {
272 if (!running) {
273 spreadView.closingIndex = -1;
274 }
275 }
276 }
277 }
278
279 Behavior on x {
280 enabled: spreadView.focusChanging && index == 0 && root.spreadEnabled
281 UbuntuNumberAnimation {
282 duration: UbuntuAnimation.FastDuration
283 onRunningChanged: {
284 if (!running) {
285 spreadView.focusChanging = false;
286 }
287 }
288 }
289 }
468290
469 // Each tile has a different progress value running from 0 to 1.291 // Each tile has a different progress value running from 0 to 1.
470 // A progress value of 0 means the tile is at the right edge. 1 means the tile has reched the left edge.292 // 0: means the tile is at the right edge.
293 // 1: means the tile has finished the main animation towards the left edge.
294 // >1: after the main animation has finished, tiles will continue to move very slowly to the left
471 progress: {295 progress: {
472 var tileProgress = (spreadView.shiftedContentX - index * spreadView.tileDistance) / spreadView.width;296 var tileProgress = (spreadView.shiftedContentX - behavioredIndex * spreadView.tileDistance) / spreadView.width;
473 // Tile 1 needs to move directly from the beginning...297 // Tile 1 needs to move directly from the beginning...
474 if (index == 1 && spreadView.phase < 2) {298 if (behavioredIndex == 1 && spreadView.phase < 2) {
475 tileProgress += spreadView.tileDistance / spreadView.width;299 tileProgress += spreadView.tileDistance / spreadView.width;
476 }300 }
301 // Limiting progress to ~0 and 1.7 to avoid binding calculations when tiles are not
302 // visible.
303 // < 0 : The tile is outside the screen on the right
304 // > 1.7: The tile is *very* close to the left edge and covered by other tiles now.
305 // Using 0.0001 to differentiate when a tile should still be visible (==0)
306 // or we can hide it (< 0)
307 tileProgress = Math.max(-0.0001, Math.min(1.7, tileProgress));
477 return tileProgress;308 return tileProgress;
478 }309 }
479310
@@ -493,7 +324,7 @@
493324
494 EasingCurve {325 EasingCurve {
495 id: snappingCurve326 id: snappingCurve
496 type: EasingCurve.OutQuad327 type: EasingCurve.Linear
497 period: 0.05328 period: 0.05
498 progress: appDelegate.progress - spreadView.positionMarker1329 progress: appDelegate.progress - spreadView.positionMarker1
499 }330 }
@@ -507,7 +338,84 @@
507 }338 }
508 }339 }
509 }340 }
510 }341
342 onClosed: {
343 spreadView.draggedIndex = -1;
344 spreadView.closingIndex = index;
345 ApplicationManager.stopApplication(ApplicationManager.get(index).appId);
346 }
347 }
348 }
349 }
350 }
351
352 EdgeDragArea {
353 id: spreadDragArea
354 direction: Direction.Leftwards
355 enabled: spreadView.phase != 2 && root.spreadEnabled
356
357 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
358 width: root.dragAreaWidth
359
360 // Sitting at the right edge of the screen, this EdgeDragArea directly controls the spreadView when
361 // attachedToView is true. When the finger movement passes positionMarker3 we detach it from the
362 // spreadView and make the spreadView snap to positionMarker4.
363 property bool attachedToView: true
364
365 property var gesturePoints: new Array()
366
367 onTouchXChanged: {
368 if (!dragging) {
369 // Initial touch. Let's reset the spreadView to the starting position.
370 spreadView.phase = 0;
371 spreadView.contentX = -spreadView.shift;
372 }
373 if (dragging && attachedToView) {
374 // Gesture recognized. Let's move the spreadView with the finger
375 spreadView.contentX = -touchX + spreadDragArea.width - spreadView.shift;
376 }
377 if (attachedToView && spreadView.shiftedContentX >= spreadView.width * spreadView.positionMarker3) {
378 // We passed positionMarker3. Detach from spreadView and snap it.
379 attachedToView = false;
380 spreadView.snap();
381 }
382 gesturePoints.push(touchX);
383 }
384
385 onStatusChanged: {
386 if (status == DirectionalDragArea.Recognized) {
387 attachedToView = true;
388 }
389 }
390
391 onDraggingChanged: {
392 if (dragging) {
393 // Gesture recognized. Start recording this gesture
394 gesturePoints = [];
395 return;
396 }
397
398 // Ok. The user released. Find out if it was a one-way movement.
399 var oneWayFlick = true;
400 var smallestX = spreadDragArea.width;
401 for (var i = 0; i < gesturePoints.length; i++) {
402 if (gesturePoints[i] >= smallestX) {
403 oneWayFlick = false;
404 break;
405 }
406 smallestX = gesturePoints[i];
407 }
408 gesturePoints = [];
409
410 if (oneWayFlick && spreadView.shiftedContentX > units.gu(2) &&
411 spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
412 // If it was a short one-way movement, do the Alt+Tab switch
413 // no matter if we didn't cross positionMarker1 yet.
414 spreadView.snapTo(1);
415 } else if (!dragging && attachedToView) {
416 // otherwise snap to the closest snap position we can find
417 // (might be back to start, to app 1 or to spread)
418 spreadView.snap();
511 }419 }
512 }420 }
513 }421 }
514422
=== removed file 'qml/Stages/SidestageHandle.qml'
--- qml/Stages/SidestageHandle.qml 2014-03-04 11:49:28 +0000
+++ qml/Stages/SidestageHandle.qml 1970-01-01 00:00:00 +0000
@@ -1,22 +0,0 @@
1/*
2 * Copyright (C) 2013 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18import Ubuntu.Components 0.1
19
20BorderImage {
21 source: "graphics/sidestage_handle.sci"
22}
230
=== added file 'qml/Stages/Splash.qml'
--- qml/Stages/Splash.qml 1970-01-01 00:00:00 +0000
+++ qml/Stages/Splash.qml 2014-07-29 14:07:22 +0000
@@ -0,0 +1,68 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15*/
16
17import QtQuick 2.0
18import Ubuntu.Components 0.1
19import "../Components"
20
21Rectangle {
22 id: root
23 color: "black"
24
25 property string name: ""
26 property url image: ""
27
28 UbuntuShape {
29 id: iconShape
30 anchors.horizontalCenter: parent.horizontalCenter
31 anchors.verticalCenter: parent.verticalCenter
32 anchors.verticalCenterOffset: -units.gu(4)
33 width: units.gu(8)
34 height: units.gu(7.5)
35
36 radius: "medium"
37 borderSource: "none"
38
39 image: Image {
40 id: iconImage
41 sourceSize.width: iconShape.width
42 sourceSize.height: iconShape.height
43 source: root.image
44 fillMode: Image.PreserveAspectCrop
45 }
46 }
47
48 Label {
49 text: root.name
50 anchors.horizontalCenter: parent.horizontalCenter
51 anchors.top: iconShape.bottom
52 anchors.topMargin: units.gu(2)
53 fontSize: "large"
54 }
55
56 WaitingDots {
57 visible: parent.visible
58 anchors.horizontalCenter: parent.horizontalCenter
59 anchors.bottom: parent.bottom
60 anchors.bottomMargin: units.gu(12)
61 }
62
63 MouseArea {
64 anchors.fill: parent
65 enabled: parent.visible
66 // absorb all mouse events
67 }
68}
069
=== modified file 'qml/Stages/SpreadDelegate.qml'
--- qml/Stages/SpreadDelegate.qml 2014-03-19 17:29:36 +0000
+++ qml/Stages/SpreadDelegate.qml 2014-07-29 14:07:22 +0000
@@ -17,39 +17,203 @@
17*/17*/
1818
19import QtQuick 2.019import QtQuick 2.0
20import Unity.Application 0.1
21import Ubuntu.Components 1.1
22import "../Components"
2023
21Item {24Item {
22 id: root25 id: root
2326
27 // to be set from outside
28 property bool interactive: true
29 property bool dropShadow: true
30 property real maximizedAppTopMargin
31 property alias swipeToCloseEnabled: dragArea.enabled
32
33 readonly property bool isFullscreen: surface !== null && surfaceContainer.surface.anchors.topMargin == 0
34
24 signal clicked()35 signal clicked()
2536 signal closed()
26 property real topMarginProgress37
2738 SurfaceContainer {
28 QtObject {39 id: surfaceContainer
29 id: priv40 anchors.fill: parent
30 property real heightDifference: root.height - appImage.implicitHeight41 surface: model.surface
31 }42 property bool appHasCreatedASurface: false
3243
33 Image {44 onSurfaceChanged: {
34 id: dropShadow45 if (surface) {
35 anchors.fill: appImage46 if (!appHasCreatedASurface) {
36 anchors.margins: -units.gu(2)47 surface.visible = false; // hide until splash screen removed
37 source: "graphics/dropshadow.png"48 appHasCreatedASurface = true;
38 opacity: .449 }
39 }50 }
40 Image {51 }
41 id: appImage52
42 anchors {53 function revealSurface() {
43 left: parent.left;54 surface.visible = true;
44 bottom: parent.bottom;55 splashLoader.source = "";
45 top: parent.top;56 }
46 topMargin: priv.heightDifference * Math.max(0, 1 - root.topMarginProgress)57
47 }58 Binding {
48 source: model.screenshot59 target: surfaceContainer.surface
49 antialiasing: true60 property: "anchors.topMargin"
50 }61 value: {
51 MouseArea {62 return surfaceContainer.surface.state === MirSurfaceItem.Fullscreen ? 0 : maximizedAppTopMargin;
52 anchors.fill: appImage63 }
53 onClicked: root.clicked()64 }
65
66 Binding {
67 target: surface
68 property: "enabled"
69 value: root.interactive
70 }
71 Binding {
72 target: surface
73 property: "focus"
74 value: root.interactive
75 }
76
77 Timer { //FIXME - need to delay removing splash screen to allow surface resize to complete
78 id: surfaceRevealDelay
79 interval: 100
80 onTriggered: surfaceContainer.revealSurface()
81 }
82
83 Connections {
84 target: surface
85 // FIXME: I would rather not need to do this, but currently it doesn't get
86 // active focus without it and I don't know why.
87 onFocusChanged: forceSurfaceActiveFocusIfReady();
88 onParentChanged: forceSurfaceActiveFocusIfReady();
89 onEnabledChanged: forceSurfaceActiveFocusIfReady();
90 function forceSurfaceActiveFocusIfReady() {
91 if (surface.focus && surface.parent === surfaceContainer && surface.enabled) {
92 surface.forceActiveFocus();
93 }
94 }
95 }
96
97 BorderImage {
98 id: dropShadowImage
99 anchors {
100 fill: parent
101 leftMargin: -units.gu(2)
102 rightMargin: -units.gu(2)
103 bottomMargin: -units.gu(2)
104 topMargin: -units.gu(2) + (root.isFullscreen ? 0 : maximizedAppTopMargin)
105 }
106 source: "graphics/dropshadow.png"
107 border { left: 50; right: 50; top: 50; bottom: 50 }
108 opacity: root.dropShadow ? .4 : 0
109 Behavior on opacity { UbuntuNumberAnimation {} }
110 }
111
112 transform: Translate {
113 y: dragArea.distance
114 }
115 }
116
117
118 StateGroup {
119 id: appSurfaceState
120 states: [
121 State {
122 name: "noSurfaceYet"
123 when: !surfaceContainer.appHasCreatedASurface
124 StateChangeScript {
125 script: { splashLoader.setSource("Splash.qml", { "name": model.name, "image": model.icon }); }
126 }
127 },
128 State {
129 name: "hasSurface"
130 when: surfaceContainer.appHasCreatedASurface && (surfaceContainer.surface !== null)
131 StateChangeScript { script: { surfaceRevealDelay.start(); } }
132 },
133 State {
134 name: "surfaceLostButAppStillAlive"
135 when: surfaceContainer.appHasCreatedASurface && (surfaceContainer.surface === null)
136 // TODO - use app snapshot
137 }
138 ]
139 state: "noSurfaceYet"
140 }
141
142 Loader {
143 id: splashLoader
144 anchors.fill: surfaceContainer
145 }
146
147 DraggingArea {
148 id: dragArea
149 anchors.fill: parent
150
151 property bool moving: false
152 property real distance: 0
153
154 onMovingChanged: {
155 spreadView.draggedIndex = moving ? index : -1
156 }
157
158 onDragValueChanged: {
159 if (!dragging) {
160 return;
161 }
162 moving = moving || Math.abs(dragValue) > units.gu(1)
163 if (moving) {
164 distance = dragValue;
165 }
166 }
167
168 onClicked: {
169 if (!moving) {
170 root.clicked();
171 }
172 }
173
174 onDragEnd: {
175 // velocity and distance values specified by design prototype
176 if ((dragVelocity < -units.gu(40) && distance < -units.gu(8)) || distance < -root.height / 2) {
177 animation.animate("up")
178 } else if ((dragVelocity > units.gu(40) && distance > units.gu(8)) || distance > root.height / 2) {
179 animation.animate("down")
180 } else {
181 animation.animate("center")
182 }
183 }
184
185 UbuntuNumberAnimation {
186 id: animation
187 target: dragArea
188 property: "distance"
189 property bool requestClose: false
190
191 function animate(direction) {
192 animation.from = dragArea.distance;
193 switch (direction) {
194 case "up":
195 animation.to = -root.height * 1.5;
196 requestClose = true;
197 break;
198 case "down":
199 animation.to = root.height * 1.5;
200 requestClose = true;
201 break;
202 default:
203 animation.to = 0
204 }
205 animation.start();
206 }
207
208 onRunningChanged: {
209 if (!running) {
210 dragArea.moving = false;
211 dragArea.distance = 0;
212 if (requestClose) {
213 root.closed();
214 }
215 }
216 }
217 }
54 }218 }
55}219}
56220
=== removed file 'qml/Stages/StageWithSideStage.qml'
--- qml/Stages/StageWithSideStage.qml 2014-05-26 14:12:20 +0000
+++ qml/Stages/StageWithSideStage.qml 1970-01-01 00:00:00 +0000
@@ -1,413 +0,0 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18import Ubuntu.Components 0.1
19import "../Components"
20import Unity.Application 0.1
21import Ubuntu.Gestures 0.1
22
23Item {
24 id: root
25 objectName: "stages"
26 anchors.fill: parent
27
28 // Controls to be set from outside
29 property bool shown: false
30 property bool moving: false
31 property int dragAreaWidth
32
33 // State information propagated to the outside
34 readonly property bool painting: mainStageImage.visible || sideStageImage.visible || sideStageSnapAnimation.running
35 property bool fullscreen: priv.focusedApplication ? priv.focusedApplication.fullscreen : false
36 property bool overlayMode: (sideStageImage.shown && priv.mainStageAppId.length == 0) || priv.overlayOverride
37 || (priv.mainStageAppId.length == 0 && sideStageSnapAnimation.running)
38
39 readonly property int overlayWidth: priv.overlayOverride ? 0 : priv.sideStageWidth
40
41 onShownChanged: {
42 if (!shown) {
43 priv.mainStageAppId = "";
44 }
45 }
46
47 onMovingChanged: {
48 if (moving) {
49 if (!priv.mainStageAppId && !priv.sideStageAppId) {
50 // Pulling in from the right, make the last used (topmost) app visible
51 var application = ApplicationManager.get(0);
52 if (application.stage == ApplicationInfoInterface.SideStage) {
53 sideStageImage.application = application;
54 sideStageImage.x = root.width - sideStageImage.width
55 sideStageImage.visible = true;
56 } else {
57 mainStageImage.application = application;
58 mainStageImage.visible = true;
59 }
60 } else {
61 priv.requestNewScreenshot(ApplicationInfoInterface.MainStage)
62 if (priv.focusedApplicationId == priv.sideStageAppId) {
63 priv.requestNewScreenshot(ApplicationInfoInterface.SideStage)
64 }
65 }
66 } else {
67 mainStageImage.visible = false;
68 sideStageImage.visible = false;
69 }
70 }
71
72 QtObject {
73 id: priv
74
75 property int sideStageWidth: units.gu(40)
76
77
78 property string sideStageAppId
79 property string mainStageAppId
80
81
82 property var sideStageApp: ApplicationManager.findApplication(sideStageAppId)
83 property var mainStageApp: ApplicationManager.findApplication(mainStageAppId)
84
85 property string sideStageScreenshot: sideStageApp ? sideStageApp.screenshot : ""
86 property string mainStageScreenshot: mainStageApp ? mainStageApp.screenshot : ""
87
88 property string focusedApplicationId: ApplicationManager.focusedApplicationId
89 property var focusedApplication: ApplicationManager.findApplication(focusedApplicationId)
90 property url focusedScreenshot: focusedApplication ? focusedApplication.screenshot : ""
91
92 property bool waitingForMainScreenshot: false
93 property bool waitingForSideScreenshot: false
94 property bool waitingForScreenshots: waitingForMainScreenshot || waitingForSideScreenshot
95
96 property string startingAppId: ""
97
98 // Keep overlayMode even if there is no focused app (to allow pulling in the sidestage from the right)
99 property bool overlayOverride: false
100
101 onFocusedApplicationChanged: {
102 if (focusedApplication) {
103 if (focusedApplication.stage == ApplicationInfoInterface.MainStage) {
104 mainStageAppId = focusedApplicationId;
105 priv.overlayOverride = false;
106 if (priv.startingAppId == focusedApplicationId && sideStageImage.shown) {
107 // There was already a sidestage app on top. bring it back!
108 ApplicationManager.focusApplication(priv.sideStageAppId)
109 priv.startingAppId = "";
110 }
111 } else if (focusedApplication.stage == ApplicationInfoInterface.SideStage) {
112 sideStageAppId = focusedApplicationId;
113 if (priv.startingAppId == focusedApplicationId && !sideStageImage.shown) {
114 sideStageImage.snapToApp(focusedApplication);
115 priv.startingAppId = "";
116 }
117 }
118 } else if (root.overlayMode){
119 sideStageImage.snapTo(root.width)
120 }
121 }
122
123 onMainStageScreenshotChanged: {
124 waitingForMainScreenshot = false;
125 }
126
127 onSideStageScreenshotChanged: {
128 waitingForSideScreenshot = false;
129 }
130
131 onFocusedScreenshotChanged: {
132 waitingForSideScreenshot = false;
133 }
134
135 onWaitingForScreenshotsChanged: {
136 if (waitingForScreenshots) {
137 return;
138 }
139
140 if (root.moving) {
141 if (mainStageAppId) {
142 mainStageImage.application = mainStageApp
143 mainStageImage.visible = true;
144 }
145 if (sideStageAppId && focusedApplicationId == sideStageAppId) {
146 sideStageImage.application = sideStageApp;
147 sideStageImage.x = root.width - sideStageImage.width
148 sideStageImage.visible = true;
149 }
150 }
151 if (sideStageHandleMouseArea.pressed) {
152 if (sideStageAppId) {
153 sideStageImage.application = sideStageApp;
154 sideStageImage.x = root.width - sideStageImage.width
155 sideStageImage.visible = true;
156 }
157 if (mainStageAppId) {
158 mainStageImage.application = mainStageApp
159 mainStageImage.visible = true;
160 }
161 }
162 }
163
164 function requestNewScreenshot(stage) {
165 if (stage == ApplicationInfoInterface.MainStage && mainStageAppId) {
166 waitingForMainScreenshot = true;
167 ApplicationManager.updateScreenshot(mainStageAppId);
168 } else if (stage == ApplicationInfoInterface.SideStage && sideStageAppId) {
169 waitingForSideScreenshot = true;
170 ApplicationManager.updateScreenshot(sideStageAppId);
171 }
172 }
173
174 }
175 // FIXME: the signal connection seems to get lost with the fake application manager.
176 Connections {
177 target: priv.sideStageApp
178 onScreenshotChanged: priv.sideStageScreenshot = priv.sideStageApp.screenshot
179 }
180 Connections {
181 target: priv.mainStageApp
182 onScreenshotChanged: priv.mainStageScreenshot = priv.mainStageApp.screenshot
183 }
184
185 Connections {
186 target: ApplicationManager
187
188 onApplicationAdded: {
189 priv.startingAppId = appId;
190 splashScreenTimer.start();
191 var application = ApplicationManager.findApplication(appId)
192 if (application.stage == ApplicationInfoInterface.SideStage) {
193 sideStageSplash.visible = true;
194 } else if (application.stage == ApplicationInfoInterface.MainStage) {
195 mainStageSplash.visible = true;
196 }
197 }
198
199 onFocusRequested: {
200 var application = ApplicationManager.findApplication(appId)
201 if (application.stage == ApplicationInfoInterface.SideStage) {
202 if (!root.shown) {
203 priv.mainStageAppId = "";
204 mainStageImage.application = null
205 }
206 if (sideStageImage.shown) {
207 sideStageImage.switchTo(application);
208 if (priv.mainStageAppId) {
209 mainStageImage.application = priv.mainStageApp;
210 mainStageImage.visible = true;
211 }
212 } else {
213 sideStageImage.application = application;
214 sideStageImage.snapToApp(application);
215 }
216 } else if (application.stage == ApplicationInfoInterface.MainStage) {
217 if (root.shown) {
218 if (sideStageImage.shown) {
219 sideStageImage.application = priv.sideStageApp;
220 sideStageImage.visible = true;
221 }
222 priv.mainStageAppId = application.appId;
223 mainStageImage.switchTo(application)
224 ApplicationManager.focusApplication(appId)
225 if (sideStageImage.shown) {
226 // There was already a focused SS app. Bring it back
227 ApplicationManager.focusApplication(priv.sideStageAppId)
228 }
229 } else {
230 if (sideStageImage.shown) {
231 sideStageImage.visible = false;
232 sideStageImage.x = root.width;
233 }
234
235 mainStageImage.application = application;
236 ApplicationManager.focusApplication(appId)
237 }
238 }
239 }
240
241 onApplicationRemoved: {
242 if (priv.mainStageAppId == appId) {
243 priv.mainStageAppId = "";
244 }
245 if (priv.sideStageAppId == appId) {
246 priv.sideStageAppId = "";
247 }
248 if (priv.sideStageAppId.length == 0) {
249 sideStageImage.shown = false;
250 priv.overlayOverride = false;
251 }
252 }
253
254 }
255
256 Timer {
257 id: splashScreenTimer
258 // FIXME: apart from removing this completely in the future and make the app surface paint
259 // meaningful stuff, also check for colin's stuff to land so we can shape 1.4 secs away from here
260 // https://code.launchpad.net/~cjwatson/upstart-app-launch/libclick-manifest/+merge/210520
261 // https://code.launchpad.net/~cjwatson/upstart-app-launch/libclick-pkgdir/+merge/209909
262 interval: 1700
263 repeat: false
264 onTriggered: {
265 mainStageSplash.visible = false;
266 sideStageSplash.visible = false;
267 }
268 }
269
270 SwitchingApplicationImage {
271 id: mainStageImage
272 anchors.bottom: parent.bottom
273 width: parent.width
274 visible: false
275
276 onSwitched: {
277 sideStageImage.visible = false;
278 }
279 }
280
281 Rectangle {
282 id: mainStageSplash
283 anchors.fill: root
284 anchors.rightMargin: root.width - sideStageImage.x
285 color: "black"
286
287 WaitingDots {
288 }
289 }
290
291 SidestageHandle {
292 id: sideStageHandle
293 anchors { top: parent.top; right: sideStageImage.left; bottom: parent.bottom }
294 width: root.dragAreaWidth
295 visible: root.shown && priv.sideStageAppId && sideStageImage.x < root.width
296
297 }
298 MouseArea {
299 id: sideStageHandleMouseArea
300 anchors { top: parent.top; right: parent.right; bottom: parent.bottom; rightMargin: sideStageImage.shown ? sideStageImage.width : 0}
301 width: root.dragAreaWidth
302 visible: priv.sideStageAppId
303
304 property var dragPoints: new Array()
305
306 onPressed: {
307 priv.requestNewScreenshot(ApplicationInfoInterface.SideStage)
308 if (priv.mainStageAppId) {
309 priv.requestNewScreenshot(ApplicationInfoInterface.MainStage)
310 }
311 }
312
313 onMouseXChanged: {
314 dragPoints.push(mouseX)
315
316 var dragPoint = root.width + mouseX;
317 if (sideStageImage.shown) {
318 dragPoint -= sideStageImage.width
319 }
320 sideStageImage.x = Math.max(root.width - sideStageImage.width, dragPoint)
321 }
322
323 onReleased: {
324 var distance = 0;
325 var lastX = dragPoints[0];
326 var oneWayFlick = true;
327 for (var i = 0; i < dragPoints.length; ++i) {
328 if (dragPoints[i] < lastX) {
329 oneWayFlick = false;
330 }
331 distance += dragPoints[i] - lastX;
332 lastX = dragPoints[i];
333 }
334 dragPoints = [];
335
336 if (oneWayFlick || distance > sideStageImage.width / 2) {
337 sideStageImage.snapTo(root.width)
338 } else {
339 sideStageImage.snapToApp(priv.sideStageApp)
340 }
341 }
342 }
343
344 SwitchingApplicationImage {
345 id: sideStageImage
346 width: priv.sideStageWidth
347 height: root.height
348 x: root.width
349 anchors.bottom: parent.bottom
350 visible: true
351 property bool shown: false
352
353 onSwitched: {
354 mainStageImage.visible = false;
355 ApplicationManager.focusApplication(application.appId)
356 }
357
358 function snapTo(targetX) {
359 sideStageSnapAnimation.targetX = targetX
360 sideStageImage.visible = true;
361 if (priv.mainStageAppId) {
362 mainStageImage.application = priv.mainStageApp
363 mainStageImage.visible = true;
364 }
365 sideStageSnapAnimation.start();
366 }
367
368 function snapToApp(application) {
369 sideStageImage.application = application
370 sideStageSnapAnimation.snapToId = application.appId;
371 snapTo(root.width - sideStageImage.width);
372 }
373
374 SequentialAnimation {
375 id: sideStageSnapAnimation
376 property int targetX: root.width
377 property string snapToId
378
379 UbuntuNumberAnimation { target: sideStageImage; property: "x"; to: sideStageSnapAnimation.targetX; duration: UbuntuAnimation.SlowDuration }
380 ScriptAction {
381 script: {
382 if (sideStageSnapAnimation.targetX == root.width) {
383 if (priv.mainStageAppId) {
384 ApplicationManager.focusApplication(priv.mainStageAppId)
385 } else {
386 priv.overlayOverride = true;
387 ApplicationManager.unfocusCurrentApplication();
388 }
389 sideStageImage.shown = false;
390 }
391 if (sideStageSnapAnimation.snapToId) {
392 ApplicationManager.focusApplication(sideStageSnapAnimation.snapToId)
393 sideStageSnapAnimation.snapToId = "";
394 sideStageImage.shown = true;
395 priv.overlayOverride = false;
396 }
397 sideStageImage.visible = false;
398 mainStageImage.visible = false;
399 }
400 }
401 }
402 }
403
404 Rectangle {
405 id: sideStageSplash
406 anchors.fill: parent
407 anchors.leftMargin: sideStageImage.x
408 color: "black"
409
410 WaitingDots {
411 }
412 }
413}
4140
=== added file 'qml/Stages/SurfaceContainer.qml'
--- qml/Stages/SurfaceContainer.qml 1970-01-01 00:00:00 +0000
+++ qml/Stages/SurfaceContainer.qml 2014-07-29 14:07:22 +0000
@@ -0,0 +1,123 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15*/
16
17import QtQuick 2.0
18import "Animations"
19
20Item {
21 id: container
22 property Item surface: null
23 property bool removing: false
24
25 onSurfaceChanged: {
26 if (surface) {
27 surface.parent = container;
28 surface.z = 1;
29 state = "initial"
30 }
31 }
32
33 Connections {
34 target: surface
35 onRemoved: {
36 container.removing = true;
37
38 var childSurfaces = surface.childSurfaces;
39 for (var i=0; i<childSurfaces.length; i++) {
40 childSurfaces[i].removed();
41 }
42
43 //if we don't have children, nothing will tell us to animate out, so do it.
44 if (childSurfaces.length === 0) {
45 animateOut();
46 }
47 // tell our parent to animate out.
48 if (surface.parentSurface) {
49 surface.parentSurface.parent.animateOut();
50 }
51 }
52 }
53
54 Repeater {
55 model: container.surface ? container.surface.childSurfaces : 0
56
57 delegate: Loader {
58 z: 2
59 anchors {
60 fill: container
61 topMargin: container.surface.anchors.topMargin
62 rightMargin: container.surface.anchors.rightMargin
63 bottomMargin: container.surface.anchors.bottomMargin
64 leftMargin: container.surface.anchors.leftMargin
65 }
66
67 // Only way to do recursive qml items.
68 source: Qt.resolvedUrl("SurfaceContainer.qml")
69 onLoaded: {
70 item.surface = modelData;
71 item.animateIn(swipeFromBottom);
72 container.animateIn(swipeUp);
73 }
74 }
75 }
76
77 function animateIn(component) {
78 var animation = component.createObject(container, { "surface": container.surface });
79 animation.start();
80
81 var tmp = d.animations;
82 tmp.push(animation);
83 d.animations = tmp;
84 }
85
86 function animateOut() {
87 if (d.animations.length > 0) {
88 var tmp = d.animations;
89 var popped = tmp.pop();
90 popped.end();
91 d.animations = tmp;
92 } else {
93 container.state = "initial";
94 }
95 }
96
97 QtObject {
98 id: d
99 property var animations: []
100 property var currentAnimation: animations.length > 0 ? animations[animations.length-1] : undefined
101 }
102
103 Component {
104 id: swipeFromBottom
105 SwipeFromBottomAnimation {}
106 }
107 Component {
108 id: swipeUp
109 SwipeUpAnimation {}
110 }
111 Component {
112 id: darkenFade
113 DarkenAndFadeInAnimation {}
114 }
115
116 states: [
117 State {
118 name: "initial"
119 PropertyChanges { target: surface; anchors.fill: container }
120 }
121 // TODO: more animations!
122 ]
123}
0124
=== removed file 'qml/Stages/SwitchingApplicationImage.qml'
--- qml/Stages/SwitchingApplicationImage.qml 2014-03-19 18:09:27 +0000
+++ qml/Stages/SwitchingApplicationImage.qml 1970-01-01 00:00:00 +0000
@@ -1,81 +0,0 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors: Michael Zanetti <michael.zanetti@canonical.com>
17*/
18
19import QtQuick 2.0
20import Ubuntu.Components 0.1
21
22Rectangle {
23 id: root
24 implicitHeight: image.implicitHeight
25 implicitWidth: image.implicitWidth
26 color: "black"
27
28 property var application
29
30 signal switched()
31
32 function switchTo(application) {
33 if (root.application == application) {
34 root.switched();
35 return;
36 }
37
38 priv.newApplication = application
39 root.visible = true;
40 switchToAnimation.start()
41 }
42
43 QtObject {
44 id: priv
45 property var newApplication
46 }
47
48 Image {
49 id: newImage
50 anchors.bottom: parent.bottom
51 width: root.width
52 source: priv.newApplication ? priv.newApplication.screenshot : ""
53 }
54
55 Image {
56 id: image
57 visible: true
58 source: root.application ? root.application.screenshot : ""
59 width: root.width
60 height: sourceSize.height
61 anchors.bottom: parent.bottom
62
63 }
64
65 SequentialAnimation {
66 id: switchToAnimation
67 ParallelAnimation {
68 UbuntuNumberAnimation { target: image; property: "x"; from: 0; to: root.width; duration: UbuntuAnimation.SlowDuration }
69 UbuntuNumberAnimation { target: newImage; property: "scale"; from: 0.7; to: 1; duration: UbuntuAnimation.SlowDuration }
70 }
71 ScriptAction {
72 script: {
73 image.x = 0
74 root.application = priv.newApplication
75 root.visible = false;
76 priv.newApplication = null
77 root.switched();
78 }
79 }
80 }
81}
820
=== added file 'qml/Stages/TabletStage.qml'
--- qml/Stages/TabletStage.qml 1970-01-01 00:00:00 +0000
+++ qml/Stages/TabletStage.qml 2014-07-29 14:07:22 +0000
@@ -0,0 +1,568 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18import Ubuntu.Components 0.1
19import Ubuntu.Gestures 0.1
20import Unity.Application 0.1
21import Utils 0.1
22import "../Components"
23
24Item {
25 id: root
26 objectName: "stages"
27 anchors.fill: parent
28
29 // Controls to be set from outside
30 property bool shown: false
31 property bool moving: false
32 property int dragAreaWidth
33 property real maximizedAppTopMargin
34 property bool interactive
35
36 // State information propagated to the outside
37 readonly property bool locked: spreadView.phase == 2
38
39 QtObject {
40 id: priv
41
42 property string focusedAppId: ApplicationManager.focusedApplicationId
43 property string oldFocusedAppId: ""
44
45 property string mainStageAppId
46 property string sideStageAppId
47
48 // For convenience, keep properties of the first two apps in the model
49 property string appId0
50 property string appId1
51
52 onFocusedAppIdChanged: {
53 if (priv.focusedAppId.length > 0) {
54 var focusedApp = ApplicationManager.findApplication(focusedAppId);
55 if (focusedApp.stage == ApplicationInfoInterface.SideStage) {
56 priv.sideStageAppId = focusedAppId;
57 } else {
58 priv.mainStageAppId = focusedAppId;
59 }
60 }
61
62 appId0 = ApplicationManager.count >= 1 ? ApplicationManager.get(0).appId : "";
63 appId1 = ApplicationManager.count > 1 ? ApplicationManager.get(1).appId : "";
64 }
65
66 function indexOf(appId) {
67 for (var i = 0; i < ApplicationManager.count; i++) {
68 if (ApplicationManager.get(i).appId == appId) {
69 return i;
70 }
71 }
72 return -1;
73 }
74
75 function evaluateOneWayFlick(gesturePoints) {
76 // Need to have at least 3 points to recognize it as a flick
77 if (gesturePoints.length < 3) {
78 return false;
79 }
80 // Need to have a movement of at least 2 grid units to recognize it as a flick
81 if (Math.abs(gesturePoints[gesturePoints.length - 1] - gesturePoints[0]) < units.gu(2)) {
82 return false;
83 }
84
85 var oneWayFlick = true;
86 var smallestX = gesturePoints[0];
87 var leftWards = gesturePoints[1] < gesturePoints[0];
88 for (var i = 1; i < gesturePoints.length; i++) {
89 if ((leftWards && gesturePoints[i] >= smallestX)
90 || (!leftWards && gesturePoints[i] <= smallestX)) {
91 oneWayFlick = false;
92 break;
93 }
94 smallestX = gesturePoints[i];
95 }
96 return oneWayFlick;
97 }
98 }
99
100 Connections {
101 target: ApplicationManager
102 onFocusRequested: {
103 if (spreadView.interactive) {
104 spreadView.snapTo(priv.indexOf(appId));
105 } else {
106 ApplicationManager.focusApplication(appId);
107 }
108 }
109
110 onApplicationRemoved: {
111 if (priv.mainStageAppId == appId) {
112 priv.mainStageAppId = "";
113 }
114 if (priv.sideStageAppId == appId) {
115 priv.sideStageAppId = "";
116 }
117 if (ApplicationManager.count == 0) {
118 spreadView.phase = 0;
119 spreadView.contentX = 0;
120 }
121 }
122 }
123
124 Flickable {
125 id: spreadView
126 anchors.fill: parent
127 contentWidth: spreadRow.width
128 interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1) && draggedIndex == -1
129
130 property int tileDistance: units.gu(20)
131 property int sideStageWidth: units.gu(40)
132 property bool sideStageVisible: priv.sideStageAppId
133
134 // Phase of the animation:
135 // 0: Starting from right edge, a new app (index 1) comes in from the right
136 // 1: The app has reached the first snap position.
137 // 2: The list is dragged further and snaps into the spread view when entering phase 2
138 property int phase
139
140 readonly property int phase0Width: sideStageWidth
141 readonly property int phase1Width: sideStageWidth
142
143 // Those markers mark the various positions in the spread (ratio to screen width from right to left):
144 // 0 - 1: following finger, snap back to the beginning on release
145 readonly property real positionMarker1: 0.2
146 // 1 - 2: curved snapping movement, snap to nextInStack on release
147 readonly property real positionMarker2: sideStageWidth / spreadView.width
148 // 2 - 3: movement follows finger, snaps to phase 2 (full spread) on release
149 readonly property real positionMarker3: 0.6
150 // passing 3, we detach movement from the finger and snap to phase 2 (full spread)
151 readonly property real positionMarker4: 0.8
152
153 readonly property int startSnapPosition: phase0Width * 0.5
154 readonly property int endSnapPosition: phase0Width * 0.75
155 readonly property real snapPosition: 0.75
156
157 property int selectedIndex: -1
158 property int draggedIndex: -1
159 property int closingIndex: -1
160
161 property bool sideStageDragging: sideStageDragHandle.dragging
162 property real sideStageDragProgress: sideStageDragHandle.progress
163
164 onSideStageDragProgressChanged: {
165 if (sideStageDragProgress == 1) {
166 ApplicationManager.focusApplication(priv.mainStageAppId);
167 priv.sideStageAppId = "";
168 }
169 }
170
171 property int nextInStack: {
172 switch (state) {
173 case "main":
174 if (ApplicationManager.count > 1) {
175 return 1;
176 }
177 return -1;
178 case "mainAndOverlay":
179 if (ApplicationManager.count <= 2) {
180 return -1;
181 }
182 if (priv.appId0 == priv.mainStageAppId || priv.appId0 == priv.sideStageAppId) {
183 if (priv.appId1 == priv.mainStageAppId || priv.appId1 == priv.sideStageAppId) {
184 return 2;
185 }
186 return 1;
187 }
188 return 0;
189 case "overlay":
190 return 1;
191 }
192 print("Unhandled nextInStack case! This shouldn't happen any more when the Dash is an app!");
193 return -1;
194 }
195 property int nextZInStack: indexToZIndex(nextInStack)
196
197 states: [
198 State {
199 name: "empty"
200 },
201 State {
202 name: "main"
203 },
204 State { // Side Stage only in overlay mode
205 name: "overlay"
206 },
207 State { // Main Stage and Side Stage in overlay mode
208 name: "mainAndOverlay"
209 },
210 State { // Main Stage and Side Stage in split mode
211 name: "mainAndSplit"
212 }
213 ]
214 state: {
215 if (priv.mainStageAppId && !priv.sideStageAppId) {
216 return "main";
217 }
218 if (!priv.mainStageAppId && priv.sideStageAppId) {
219 return "overlay";
220 }
221 if (priv.mainStageAppId && priv.sideStageAppId) {
222 return "mainAndOverlay";
223 }
224 return "empty";
225 }
226
227 onContentXChanged: {
228 if (spreadView.phase == 0 && spreadView.contentX > spreadView.width * spreadView.positionMarker2) {
229 spreadView.phase = 1;
230 } else if (spreadView.phase == 1 && spreadView.contentX > spreadView.width * spreadView.positionMarker4) {
231 spreadView.phase = 2;
232 } else if (spreadView.phase == 1 && spreadView.contentX < spreadView.width * spreadView.positionMarker2) {
233 spreadView.phase = 0;
234 }
235 }
236
237 function snap() {
238 if (contentX < phase0Width) {
239 snapAnimation.targetContentX = 0;
240 snapAnimation.start();
241 } else if (contentX < phase1Width) {
242 snapTo(1);
243 } else {
244 // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation.
245 snapAnimation.targetContentX = spreadView.width * spreadView.positionMarker4 + 1;
246 snapAnimation.start();
247 }
248 }
249 function snapTo(index) {
250 spreadView.selectedIndex = index;
251 snapAnimation.targetContentX = 0;
252 snapAnimation.start();
253 }
254
255 // We need to shuffle z ordering a bit in order to keep side stage apps above main stage apps.
256 // We don't want to really reorder them in the model because that allows us to keep track
257 // of the last focused order.
258 function indexToZIndex(index) {
259 var app = ApplicationManager.get(index);
260 if (!app) {
261 return index;
262 }
263
264 var isActive = app.appId == priv.mainStageAppId || app.appId == priv.sideStageAppId;
265 if (isActive && app.stage == ApplicationInfoInterface.MainStage) {
266 // if this app is active, and its the MainStage, always put it to index 0
267 return 0;
268 }
269 if (isActive && app.stage == ApplicationInfoInterface.SideStage) {
270 if (!priv.mainStageAppId) {
271 // Only have SS apps running. Put the active one at 0
272 return 0;
273 }
274
275 // Precondition now: There's an active MS app and this is SS app:
276 if (spreadView.nextInStack >= 0 && ApplicationManager.get(spreadView.nextInStack).stage == ApplicationInfoInterface.MainStage) {
277 // If the next app coming from the right is a MS app, we need to elevate this SS ap above it.
278 // Put it to at least level 2, or higher if there's more apps coming in before this one.
279 return Math.max(index, 2);
280 } else {
281 // 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.
282 return 1;
283 }
284 }
285 if (index <= 2 && app.stage == ApplicationInfoInterface.MainStage && priv.sideStageAppId) {
286 // Ok, this is an inactive MS app. If there's an active SS app around, we need to place this one
287 // in between the active MS app and the active SS app, so that it comes in from there when dragging from the right.
288 // If there's now active SS app, just leave it where it is.
289 return priv.indexOf(priv.sideStageAppId) < index ? index - 1 : index;
290 }
291 if (index == spreadView.nextInStack && app.stage == ApplicationInfoInterface.SideStage) {
292 // This is a SS app and the next one to come in from the right:
293 if (priv.sideStageAppId && priv.mainStageAppId) {
294 // If there's both, an active MS and an active SS app, put this one right on top of that
295 return 2;
296 }
297 // Or if there's only one other active app, put it on top of that.
298 // The case that there isn't any other active app is already handled above.
299 return 1;
300 }
301 if (index == 2 && spreadView.nextInStack == 1 && priv.sideStageAppId) {
302 // If its index 2 but not the next one to come in, it means
303 // we've pulled another one down to index 2. Move this one up to 2 instead.
304 return 3;
305 }
306 // don't touch all others... (mostly index > 3 + simple cases where the above doesn't shuffle much)
307 return index;
308 }
309
310 SequentialAnimation {
311 id: snapAnimation
312 property int targetContentX: 0
313
314 UbuntuNumberAnimation {
315 target: spreadView
316 property: "contentX"
317 to: snapAnimation.targetContentX
318 duration: UbuntuAnimation.FastDuration
319 }
320
321 ScriptAction {
322 script: {
323 if (spreadView.selectedIndex >= 0) {
324 var newIndex = spreadView.selectedIndex;
325 spreadView.selectedIndex = -1;
326 ApplicationManager.focusApplication(ApplicationManager.get(newIndex).appId);
327 spreadView.phase = 0;
328 spreadView.contentX = 0;
329 }
330 }
331 }
332 }
333
334 Rectangle {
335 id: spreadRow
336 color: "black"
337 x: spreadView.contentX
338 height: root.height
339 width: spreadView.width + Math.max(spreadView.width, ApplicationManager.count * spreadView.tileDistance)
340
341 Rectangle {
342 id: sideStageBackground
343 color: "black"
344 anchors.fill: parent
345 anchors.leftMargin: spreadView.width - (1 - sideStageDragHandle.progress) * spreadView.sideStageWidth
346 z: spreadView.indexToZIndex(priv.indexOf(priv.sideStageAppId))
347 opacity: spreadView.phase == 0 ? 1 : 0
348 Behavior on opacity { UbuntuNumberAnimation {} }
349 }
350
351 Item {
352 id: sideStageDragHandle
353 anchors { top: parent.top; bottom: parent.bottom; left: parent.left; leftMargin: spreadView.width - spreadView.sideStageWidth - width }
354 width: units.gu(2)
355 z: sideStageBackground.z
356 opacity: spreadView.phase <= 0 && spreadView.sideStageVisible ? 1 : 0
357 property real progress: 0
358 property bool dragging: false
359
360 Behavior on opacity { UbuntuNumberAnimation {} }
361
362 Connections {
363 target: spreadView
364 onSideStageVisibleChanged: {
365 if (spreadView.sideStageVisible) {
366 sideStageDragHandle.progress = 0;
367 }
368 }
369 }
370
371 Image {
372 anchors.centerIn: parent
373 anchors.horizontalCenterOffset: parent.progress * spreadView.sideStageWidth - (width - parent.width) / 2
374 width: sideStageDragHandleMouseArea.pressed ? parent.width * 2 : parent.width
375 height: parent.height
376 source: "graphics/sidestage_handle@20.png"
377 Behavior on width { UbuntuNumberAnimation {} }
378 }
379
380 MouseArea {
381 id: sideStageDragHandleMouseArea
382 anchors.fill: parent
383 enabled: spreadView.contentX == 0
384 property int startX
385 property var gesturePoints: new Array()
386
387 onPressed: {
388 gesturePoints = [];
389 startX = mouseX;
390 sideStageDragHandle.progress = 0;
391 sideStageDragHandle.dragging = true;
392 }
393 onMouseXChanged: {
394 if (priv.mainStageAppId) {
395 sideStageDragHandle.progress = Math.max(0, (-startX + mouseX) / spreadView.sideStageWidth);
396 }
397 gesturePoints.push(mouseX);
398 }
399 onReleased: {
400 if (priv.mainStageAppId) {
401 var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints);
402 sideStageDragSnapAnimation.to = sideStageDragHandle.progress > 0.5 || oneWayFlick ? 1 : 0;
403 sideStageDragSnapAnimation.start();
404 } else {
405 sideStageDragHandle.dragging = false;
406 }
407 }
408 }
409 UbuntuNumberAnimation {
410 id: sideStageDragSnapAnimation
411 target: sideStageDragHandle
412 property: "progress"
413
414 onRunningChanged: {
415 if (!running) {
416 sideStageDragHandle.dragging = false;;
417 }
418 }
419 }
420 }
421
422 Repeater {
423 id: spreadRepeater
424 model: ApplicationManager
425
426 delegate: TransformedTabletSpreadDelegate {
427 id: spreadTile
428 height: spreadView.height
429 width: model.stage == ApplicationInfoInterface.MainStage ? spreadView.width : spreadView.sideStageWidth
430 x: spreadView.width
431 z: spreadView.indexToZIndex(index)
432 active: model.appId == priv.mainStageAppId || model.appId == priv.sideStageAppId
433 zIndex: z
434 selected: spreadView.selectedIndex == index
435 otherSelected: spreadView.selectedIndex >= 0 && !selected
436 isInSideStage: priv.sideStageAppId == model.appId
437 interactive: !spreadView.interactive && spreadView.phase === 0 && root.interactive
438 swipeToCloseEnabled: spreadView.interactive
439 maximizedAppTopMargin: root.maximizedAppTopMargin
440 dropShadow: spreadView.contentX > 0 || spreadDragArea.status == DirectionalDragArea.Undecided
441
442 property real behavioredZIndex: zIndex
443 Behavior on behavioredZIndex {
444 enabled: spreadView.closingIndex >= 0
445 UbuntuNumberAnimation {}
446 }
447
448 // This is required because none of the bindings are triggered in some cases:
449 // When an app is closed, it might happen that ApplicationManager.get(nextInStack)
450 // returns a different app even though the nextInStackIndex and all the related
451 // bindings (index, mainStageApp, sideStageApp, etc) don't change. Let's force a
452 // binding update in that case.
453 Connections {
454 target: ApplicationManager
455 onApplicationRemoved: spreadTile.z = Qt.binding(function() {
456 return spreadView.indexToZIndex(index);
457 })
458 }
459
460 progress: {
461 var tileProgress = (spreadView.contentX - behavioredZIndex * spreadView.tileDistance) / spreadView.width;
462 // Some tiles (nextInStack, active) need to move directly from the beginning, normalize progress to immediately start at 0
463 if ((index == spreadView.nextInStack && spreadView.phase < 2) || (active && spreadView.phase < 1)) {
464 tileProgress += behavioredZIndex * spreadView.tileDistance / spreadView.width;
465 }
466 return tileProgress;
467 }
468
469 animatedProgress: {
470 if (spreadView.phase == 0 && (spreadTile.active || spreadView.nextInStack == index)) {
471 if (progress < spreadView.positionMarker1) {
472 return progress;
473 } else if (progress < spreadView.positionMarker1 + snappingCurve.period) {
474 return spreadView.positionMarker1 + snappingCurve.value * 3;
475 } else {
476 return spreadView.positionMarker2;
477 }
478 }
479 return progress;
480 }
481
482 onClicked: {
483 if (spreadView.phase == 2) {
484 if (ApplicationManager.focusedApplicationId == ApplicationManager.get(index).appId) {
485 spreadView.snapTo(index);
486 } else {
487 ApplicationManager.requestFocusApplication(ApplicationManager.get(index).appId);
488 }
489 }
490 }
491
492 onClosed: {
493 spreadView.draggedIndex = -1;
494 spreadView.closingIndex = index;
495 ApplicationManager.stopApplication(ApplicationManager.get(index).appId);
496 }
497
498 EasingCurve {
499 id: snappingCurve
500 type: EasingCurve.Linear
501 period: (spreadView.positionMarker2 - spreadView.positionMarker1) / 3
502 progress: spreadTile.progress - spreadView.positionMarker1
503 }
504 }
505 }
506 }
507 }
508
509 EdgeDragArea {
510 id: spreadDragArea
511 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
512 width: root.dragAreaWidth
513 direction: Direction.Leftwards
514
515 property bool attachedToView: false
516 property var gesturePoints: new Array()
517
518 onTouchXChanged: {
519 if (!dragging) {
520 spreadView.phase = 0;
521 spreadView.contentX = 0;
522 }
523
524 if (attachedToView) {
525 spreadView.contentX = -touchX + spreadDragArea.width;
526 if (spreadView.contentX > spreadView.phase0Width + spreadView.phase1Width / 2) {
527 attachedToView = false;
528 spreadView.snap();
529 }
530 }
531 gesturePoints.push(touchX);
532 }
533
534 onStatusChanged: {
535 if (status == DirectionalDragArea.Recognized) {
536 attachedToView = true;
537 }
538 }
539
540 onDraggingChanged: {
541 if (dragging) {
542 // Gesture recognized. Start recording this gesture
543 gesturePoints = [];
544 return;
545 }
546
547 // Ok. The user released. Find out if it was a one-way movement.
548 var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints);
549 gesturePoints = [];
550
551 if (oneWayFlick && spreadView.contentX < spreadView.positionMarker1 * spreadView.width) {
552 // If it was a short one-way movement, do the Alt+Tab switch
553 // no matter if we didn't cross positionMarker1 yet.
554 spreadView.snapTo(spreadView.nextInStack);
555 } else if (!dragging && attachedToView) {
556 if (spreadView.contentX < spreadView.width * spreadView.positionMarker1) {
557 spreadView.snap();
558 } else if (spreadView.contentX < spreadView.width * spreadView.positionMarker2) {
559 spreadView.snapTo(spreadView.nextInStack);
560 } else {
561 // otherwise snap to the closest snap position we can find
562 // (might be back to start, to app 1 or to spread)
563 spreadView.snap();
564 }
565 }
566 }
567 }
568}
0569
=== modified file 'qml/Stages/TransformedSpreadDelegate.qml'
--- qml/Stages/TransformedSpreadDelegate.qml 2014-03-19 19:10:30 +0000
+++ qml/Stages/TransformedSpreadDelegate.qml 2014-07-29 14:07:22 +0000
@@ -23,7 +23,9 @@
23SpreadDelegate {23SpreadDelegate {
24 id: root24 id: root
2525
26 // Set this to true when this tile is selected in the spread. The animation will change to bring the tile to front.
26 property bool selected: false27 property bool selected: false
28 // Set this to true when another tile in the spread is selected. The animation will change to fade this tile out.
27 property bool otherSelected: false29 property bool otherSelected: false
2830
29 // The progress animates the tiles. A value > 0 makes it appear from the right edge. At 1 it reaches the end position.31 // The progress animates the tiles. A value > 0 makes it appear from the right edge. At 1 it reaches the end position.
@@ -44,6 +46,9 @@
44 property real startDistance: units.gu(5)46 property real startDistance: units.gu(5)
45 property real endDistance: units.gu(.5)47 property real endDistance: units.gu(.5)
4648
49 // Hiding tiles when their progress is negative or reached the maximum
50 visible: progress >= 0 && progress < 1.7
51
47 onSelectedChanged: {52 onSelectedChanged: {
48 if (selected) {53 if (selected) {
49 priv.snapshot();54 priv.snapshot();
@@ -106,16 +111,6 @@
106 selectedTopMarginProgress = topMarginProgress;111 selectedTopMarginProgress = topMarginProgress;
107 }112 }
108113
109 // This calculates how much negative progress there can be if unwinding the spread completely
110 // the progress for each tile starts at 0 when it crosses the right edge, so the later a tile comes in,
111 // the bigger its negativeProgress can be.
112 property real negativeProgress: {
113 if (index == 1 && spreadView.phase < 2) {
114 return 0;
115 }
116 return -index * spreadView.tileDistance / spreadView.width;
117 }
118
119 function linearAnimation(startProgress, endProgress, startValue, endValue, progress) {114 function linearAnimation(startProgress, endProgress, startValue, endValue, progress) {
120 // progress : progressDiff = value : valueDiff => value = progress * valueDiff / progressDiff115 // progress : progressDiff = value : valueDiff => value = progress * valueDiff / progressDiff
121 return (progress - startProgress) * (endValue - startValue) / (endProgress - startProgress) + startValue;116 return (progress - startProgress) * (endValue - startValue) / (endProgress - startProgress) + startValue;
@@ -127,22 +122,20 @@
127 return helperEasingCurve.value * (endValue - startValue) + startValue;122 return helperEasingCurve.value * (endValue - startValue) + startValue;
128 }123 }
129124
130 property real animatedEndDistance: linearAnimation(0, 2, root.endDistance, 0, root.progress)
131
132 // The following blocks handle the animation of the tile in the spread.125 // The following blocks handle the animation of the tile in the spread.
133 // At the beginning, each tile is attached at the right edge, outside the screen.126 // At the beginning, each tile is attached at the right edge, outside the screen.
134 // The progress for each tile starts at 0 and it reaches its end position at a progress of 1.127 // The progress for each tile starts at 0 and it reaches its end position at a progress of 1.
135 // The first phases are handled special for the first 2 tiles. as we do the alt-tab and snapping in there128 // The first phases are handled special for the first 2 tiles. as we do the alt-tab and snapping
136 // Once we reached phase 3, the animation is the same for all tiles.129 // in there. Once we reached phase 3, the animation is the same for all tiles.
137 // When a tile is selected, the animation state is snapshotted, and the spreadView is unwound (progress animates130 // When a tile is selected, the animation state is snapshotted, and the spreadView is unwound.
138 // back to negativeProgress). All tiles are kept in place and faded out to 0 opacity except131 // All tiles are kept in place and faded out to 0 opacity except
139 // the selected tile, which is animated from the snapshotted position to be fullscreen.132 // the selected tile, which is animated from the snapshotted position to be fullscreen.
140133
141 property real xTranslate: {134 readonly property real xTranslate: {
142 if (otherSelected) {135 if (otherSelected) {
143 if (spreadView.phase < 2 && index == 0) {136 if (spreadView.phase < 2 && index == 0) {
144 return linearAnimation(selectedProgress, negativeProgress,137 return linearAnimation(selectedProgress, 0, selectedXTranslate,
145 selectedXTranslate, selectedXTranslate - spreadView.tileDistance, root.progress);138 selectedXTranslate - spreadView.tileDistance, root.progress);
146 }139 }
147140
148 return selectedXTranslate;141 return selectedXTranslate;
@@ -160,7 +153,7 @@
160 // Apply the same animation as with the rest but add spreadView.width to align it with the others.153 // Apply the same animation as with the rest but add spreadView.width to align it with the others.
161 return -easingCurve.value * spreadView.width + spreadView.width;154 return -easingCurve.value * spreadView.width + spreadView.width;
162 } else if (priv.isSelected) {155 } else if (priv.isSelected) {
163 return linearAnimation(selectedProgress, negativeProgress, selectedXTranslate, 0, root.progress);156 return linearAnimation(selectedProgress, 0, selectedXTranslate, 0, root.progress);
164 }157 }
165158
166 case 1:159 case 1:
@@ -177,23 +170,28 @@
177 if (priv.isSelected) {170 if (priv.isSelected) {
178 // Distance to left edge171 // Distance to left edge
179 var targetTranslate = -spreadView.width - ((index - 1) * root.startDistance);172 var targetTranslate = -spreadView.width - ((index - 1) * root.startDistance);
180 return linearAnimation(selectedProgress, negativeProgress,173 return linearAnimation(selectedProgress, 0,
181 selectedXTranslate, targetTranslate, root.progress);174 selectedXTranslate, targetTranslate, root.progress);
182 }175 }
183176
184 // Fix it at the right edge...177 // Fix it at the right edge...
185 var rightEdgeOffset = -((index - 1) * root.startDistance);178 var rightEdgeOffset = -((index - 1) * root.startDistance);
186 // ...and use our easing to move them to the left. Stop a bit earlier for each tile179 // ...and use our easing to move them to the left. Stop a bit earlier for each tile
180 var animatedEndDistance = linearAnimation(0, 2, root.endDistance, 0, root.progress);
187 return -easingCurve.value * spreadView.width + (index * animatedEndDistance) + rightEdgeOffset;181 return -easingCurve.value * spreadView.width + (index * animatedEndDistance) + rightEdgeOffset;
188182
189 }183 }
190184
191 property real angle: {185 readonly property real angle: {
186 if (spreadView.focusChanging) {
187 return 0;
188 }
189
192 if (priv.otherSelected) {190 if (priv.otherSelected) {
193 return priv.selectedAngle;191 return priv.selectedAngle;
194 }192 }
195 if (priv.isSelected) {193 if (priv.isSelected) {
196 return linearAnimation(selectedProgress, negativeProgress, selectedAngle, 0, root.progress);194 return linearAnimation(selectedProgress, 0, selectedAngle, 0, root.progress);
197 }195 }
198 switch (index) {196 switch (index) {
199 case 0:197 case 0:
@@ -217,12 +215,15 @@
217 return root.startAngle - easingCurve.value * (root.startAngle - root.endAngle);215 return root.startAngle - easingCurve.value * (root.startAngle - root.endAngle);
218 }216 }
219217
220 property real scale: {218 readonly property real scale: {
219 if (spreadView.focusChanging) {
220 return 1;
221 }
221 if (priv.otherSelected) {222 if (priv.otherSelected) {
222 return priv.selectedScale;223 return priv.selectedScale;
223 }224 }
224 if (priv.isSelected) {225 if (priv.isSelected) {
225 return linearAnimation(selectedProgress, negativeProgress, selectedScale, 1, root.progress);226 return linearAnimation(selectedProgress, 0, selectedScale, 1, root.progress);
226 }227 }
227228
228 switch (index) {229 switch (index) {
@@ -247,7 +248,7 @@
247 return root.startScale - easingCurve.value * (root.startScale - root.endScale);248 return root.startScale - easingCurve.value * (root.startScale - root.endScale);
248 }249 }
249250
250 property real opacity: {251 readonly property real opacity: {
251 if (priv.otherSelected) {252 if (priv.otherSelected) {
252 return linearAnimation (selectedProgress, Math.max(0, selectedProgress - .5),253 return linearAnimation (selectedProgress, Math.max(0, selectedProgress - .5),
253 selectedOpacity, 0, root.progress);254 selectedOpacity, 0, root.progress);
@@ -265,9 +266,9 @@
265 return 1;266 return 1;
266 }267 }
267268
268 property real topMarginProgress: {269 readonly property real topMarginProgress: {
269 if (priv.isSelected) {270 if (priv.isSelected) {
270 return linearAnimation(selectedProgress, negativeProgress, selectedTopMarginProgress, 0, root.progress);271 return linearAnimation(selectedProgress, 0, selectedTopMarginProgress, 0, root.progress);
271 }272 }
272273
273 switch (index) {274 switch (index) {
@@ -290,6 +291,7 @@
290291
291 return easingCurve.value;292 return easingCurve.value;
292 }293 }
294
293 }295 }
294296
295 transform: [297 transform: [
@@ -303,18 +305,22 @@
303 xScale: priv.scale305 xScale: priv.scale
304 yScale: xScale306 yScale: xScale
305 },307 },
308 Scale {
309 origin { x: 0; y: (spreadView.height * priv.scale) + maximizedAppTopMargin * 3 }
310 xScale: 1
311 yScale: isFullscreen ? 1 - priv.topMarginProgress * maximizedAppTopMargin / spreadView.height : 1
312 },
306 Translate {313 Translate {
307 x: priv.xTranslate314 x: priv.xTranslate
308 }315 }
309 ]316 ]
310 opacity: priv.opacity317 opacity: priv.opacity
311 topMarginProgress: priv.topMarginProgress
312318
313 EasingCurve {319 EasingCurve {
314 id: easingCurve320 id: easingCurve
315 type: EasingCurve.OutSine321 type: EasingCurve.OutSine
316 period: 1 - spreadView.positionMarker2322 period: 1 - spreadView.positionMarker2
317 progress: root.animatedProgress323 progress: root.progress
318 }324 }
319325
320 // This is used as a calculation helper to figure values for progress other than the current one326 // This is used as a calculation helper to figure values for progress other than the current one
321327
=== added file 'qml/Stages/TransformedTabletSpreadDelegate.qml'
--- qml/Stages/TransformedTabletSpreadDelegate.qml 1970-01-01 00:00:00 +0000
+++ qml/Stages/TransformedTabletSpreadDelegate.qml 2014-07-29 14:07:22 +0000
@@ -0,0 +1,369 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors: Michael Zanetti <michael.zanetti@canonical.com>
17*/
18
19import QtQuick 2.0
20import Utils 0.1
21import Ubuntu.Components 0.1
22import Unity.Application 0.1
23
24SpreadDelegate {
25 id: root
26
27 // Set this to true when this tile is selected in the spread. The animation will change to bring the tile to front.
28 property bool selected: false
29 // Set this to true when another tile in the spread is selected. The animation will change to fade this tile out.
30 property bool otherSelected: false
31 // Set this to true when this tile a currently active on either the MS or the SS.
32 property bool active: false
33
34 property int zIndex
35 property real progress: 0
36 property real animatedProgress: 0
37
38 property real startDistance: units.gu(5)
39 property real endDistance: units.gu(.5)
40
41 property real startScale: 1.1
42 property real endScale: 0.7
43 property real dragStartScale: startScale + .2
44
45 property real startAngle: 15
46 property real endAngle: 5
47
48 property bool isInSideStage: false
49
50 onSelectedChanged: {
51 if (selected) {
52 priv.snapshot();
53 }
54 priv.isSelected = selected;
55 }
56
57 onOtherSelectedChanged: {
58 if (otherSelected) {
59 priv.snapshot();
60 }
61 priv.otherSelected = otherSelected;
62 }
63
64 Connections {
65 target: spreadView
66
67 onPhaseChanged: {
68 if (spreadView.phase == 1) {
69 var phase2Progress = spreadView.positionMarker4 - (root.zIndex * spreadView.tileDistance / spreadView.width);
70 priv.phase2StartTranslate = priv.easingAnimation(0, 1, 0, -spreadView.width + (root.zIndex * root.endDistance), phase2Progress);
71 priv.phase2StartScale = priv.easingAnimation(0, 1, root.startScale, root.endScale, phase2Progress);
72 priv.phase2StartAngle = priv.easingAnimation(0, 1, root.startAngle, root.endAngle, phase2Progress);
73 priv.phase2StartTopMarginProgress = priv.easingAnimation(0, 1, 0, 1, phase2Progress);
74 }
75 }
76 }
77
78 // Required to be an item because we're using states on it.
79 Item {
80 id: priv
81
82 // true if this is the next tile on the stack that comes in when dragging from the right
83 property bool nextInStack: spreadView.nextZInStack == zIndex
84 // true if the next tile in the stack is the nextInStack one. This one will be moved a bit to the left
85 property bool movedActive: spreadView.nextZInStack == zIndex + 1
86 property real animatedEndDistance: linearAnimation(0, 2, root.endDistance, 0, root.progress)
87
88 property real phase2StartTranslate
89 property real phase2StartScale
90 property real phase2StartAngle
91 property real phase2StartTopMarginProgress
92
93 property bool isSelected: false
94 property bool otherSelected: false
95 property real selectedProgress
96 property real selectedXTranslate
97 property real selectedAngle
98 property real selectedScale
99 property real selectedOpacity
100 property real selectedTopMarginProgress
101
102 function snapshot() {
103 selectedProgress = root.progress;
104 selectedXTranslate = xTranslate;
105 selectedAngle = angle;
106 selectedScale = priv.scale;
107 selectedOpacity = priv.opacityTransform;
108 selectedTopMarginProgress = topMarginProgress;
109 }
110
111 // This calculates how much negative progress there can be if unwinding the spread completely
112 // the progress for each tile starts at 0 when it crosses the right edge, so the later a tile comes in,
113 // the bigger its negativeProgress can be.
114 property real negativeProgress: {
115 if (nextInStack && spreadView.phase < 2) {
116 return 0;
117 }
118 return -root.zIndex * spreadView.tileDistance / spreadView.width;
119 }
120
121 function linearAnimation(startProgress, endProgress, startValue, endValue, progress) {
122 // progress : progressDiff = value : valueDiff => value = progress * valueDiff / progressDiff
123 return (progress - startProgress) * (endValue - startValue) / (endProgress - startProgress) + startValue;
124 }
125
126 function easingAnimation(startProgress, endProgress, startValue, endValue, progress) {
127 helperEasingCurve.progress = progress - startProgress;
128 helperEasingCurve.period = endProgress - startProgress;
129 return helperEasingCurve.value * (endValue - startValue) + startValue;
130 }
131
132 property real xTranslate: {
133 var newTranslate = 0;
134
135 if (otherSelected) {
136 return priv.selectedXTranslate;
137 }
138
139 if (isSelected) {
140 if (model.stage == ApplicationInfoInterface.MainStage) {
141 return linearAnimation(selectedProgress, negativeProgress, selectedXTranslate, -spreadView.width, root.progress);
142 } else {
143 return linearAnimation(selectedProgress, negativeProgress, selectedXTranslate, -spreadView.sideStageWidth, root.progress);
144 }
145 }
146
147 // 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
148 // when we're only dragging the side stage in on top of a main stage app
149 var shouldMoveAway = spreadView.nextInStack >= 0 && priv.movedActive && model.stage === ApplicationInfoInterface.MainStage &&
150 ApplicationManager.get(spreadView.nextInStack).stage === ApplicationInfoInterface.MainStage;
151
152 if (active) {
153 newTranslate -= root.width
154 // Only do the hide animation for active apps in the mainstage, and not if we only drag the ss in
155 if (spreadView.phase == 0 && shouldMoveAway) {
156 newTranslate += linearAnimation(0, spreadView.positionMarker2, 0, -units.gu(4), root.animatedProgress);
157 }
158 }
159 if (nextInStack && spreadView.phase == 0) {
160 if (model.stage == ApplicationInfoInterface.MainStage) {
161 if (spreadView.sideStageVisible && root.progress > 0) {
162 // Move it so it appears from behind the side stage immediately
163 newTranslate += -spreadView.sideStageWidth;
164 }
165 }
166
167 if (model.stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) {
168 // This is when we only drag the side stage in, without rotation or snapping
169 newTranslate = linearAnimation(0, spreadView.positionMarker2, 0, -spreadView.sideStageWidth, root.progress);
170 } else {
171 newTranslate += linearAnimation(0, spreadView.positionMarker2, 0, -spreadView.sideStageWidth * spreadView.snapPosition, root.animatedProgress);
172 }
173 }
174
175 if (spreadView.phase == 1) {
176 if (nextInStack) {
177 if (model.stage == ApplicationInfoInterface.MainStage) {
178 var startValue = -spreadView.sideStageWidth * spreadView.snapPosition + (spreadView.sideStageVisible ? -spreadView.sideStageWidth : 0);
179 newTranslate += linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, startValue, priv.phase2StartTranslate, root.animatedProgress);
180 } else {
181 var endValue = -spreadView.width + spreadView.width * root.zIndex / 6;
182 if (!spreadView.sideStageVisible) {
183 newTranslate += linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, -spreadView.sideStageWidth, priv.phase2StartTranslate, root.progress);
184 } else {
185 newTranslate += linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, -spreadView.sideStageWidth * spreadView.snapPosition, priv.phase2StartTranslate, root.animatedProgress);
186 }
187 }
188 } else if (root.active) {
189 var startProgress = spreadView.positionMarker2 - (zIndex * spreadView.positionMarker2 / 2);
190 var endProgress = spreadView.positionMarker4 - (zIndex * spreadView.tileDistance / spreadView.width);
191 var startTranslate = -root.width + (shouldMoveAway ? -units.gu(4) : 0);
192 newTranslate = linearAnimation(startProgress, endProgress, startTranslate, priv.phase2StartTranslate, root.progress);
193 } else {
194 var startProgress = spreadView.positionMarker2 - (zIndex * spreadView.positionMarker2 / 2);
195 var endProgress = spreadView.positionMarker4 - (zIndex * spreadView.tileDistance / spreadView.width);
196 newTranslate = linearAnimation(startProgress, endProgress, 0, priv.phase2StartTranslate, root.progress);
197 }
198 }
199
200 if (spreadView.phase == 2) {
201 newTranslate = -easingCurve.value * (spreadView.width - root.zIndex * animatedEndDistance);
202 }
203
204 return newTranslate;
205 }
206
207 property real scale: {
208 if (otherSelected) {
209 return selectedScale;
210 }
211
212 if (isSelected) {
213 return linearAnimation(selectedProgress, negativeProgress, selectedScale, 1, root.progress);
214 }
215
216 if (spreadView.phase == 0) {
217 if (nextInStack) {
218 if (model.stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) {
219 return 1;
220 } else {
221 var targetScale = root.dragStartScale - ((root.dragStartScale - 1) * spreadView.snapPosition);
222 return linearAnimation(0, spreadView.positionMarker2, root.dragStartScale, targetScale, root.animatedProgress);
223 }
224 } else if (active) {
225 return 1;
226 } else {
227 return linearAnimation(0, spreadView.positionMarker2, root.startScale, root.endScale, root.progress);
228 }
229 }
230
231 if (spreadView.phase == 1) {
232 if (nextInStack) {
233 var startScale = 1;
234 if (model.stage !== ApplicationInfoInterface.SideStage || spreadView.sideStageVisible) {
235 startScale = root.dragStartScale - ((root.dragStartScale - 1) * spreadView.snapPosition);
236 }
237 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, startScale, priv.phase2StartScale, root.animatedProgress);
238 }
239 var startProgress = spreadView.positionMarker2 - (zIndex * spreadView.positionMarker2 / 2);
240 var endProgress = spreadView.positionMarker4 - (zIndex * spreadView.tileDistance / spreadView.width);
241 return linearAnimation(startProgress, endProgress, 1, priv.phase2StartScale, root.animatedProgress);
242 }
243
244 if (spreadView.phase == 2) {
245 return root.startScale - easingCurve.value * (root.startScale - root.endScale);
246 }
247
248 return 1;
249 }
250
251 property real angle: {
252 if (otherSelected) {
253 return selectedAngle;
254 }
255 if (isSelected) {
256 return linearAnimation(selectedProgress, negativeProgress, selectedAngle, 0, root.progress);
257 }
258
259 // The tile should rotate a bit when another one comes on top, but not when only dragging the side stage in
260 var shouldMoveAway = spreadView.nextInStack >= 0 && movedActive &&
261 (ApplicationManager.get(spreadView.nextInStack).stage === ApplicationInfoInterface.MainStage ||
262 model.stage == ApplicationInfoInterface.SideStage);
263
264 if (spreadView.phase == 0) {
265 if (nextInStack) {
266 if (model.stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) {
267 return 0;
268 } else {
269 return linearAnimation(0, spreadView.positionMarker2, root.startAngle, root.startAngle * (1-spreadView.snapPosition), root.animatedProgress);
270 }
271 }
272 if (shouldMoveAway) {
273 return linearAnimation(0, spreadView.positionMarker2, 0, root.startAngle * (1-spreadView.snapPosition), root.animatedProgress);
274 }
275 }
276 if (spreadView.phase == 1) {
277 if (nextInStack) {
278 if (model.stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) {
279 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, 0, priv.phase2StartAngle, root.animatedProgress);
280 } else {
281 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, root.startAngle * (1-spreadView.snapPosition), priv.phase2StartAngle, root.animatedProgress);
282 }
283 }
284 var startProgress = spreadView.positionMarker2 - (zIndex * spreadView.positionMarker2 / 2);
285 var endProgress = spreadView.positionMarker4 - (zIndex * spreadView.tileDistance / spreadView.width);
286 var startAngle = shouldMoveAway ? root.startAngle * (1-spreadView.snapPosition) : 0;
287 return linearAnimation(startProgress, endProgress, startAngle, priv.phase2StartAngle, root.progress);
288 }
289 if (spreadView.phase == 2) {
290 return root.startAngle - easingCurve.value * (root.startAngle - root.endAngle);
291 }
292
293 return 0;
294 }
295
296 property real opacityTransform: {
297 if (otherSelected && spreadView.phase == 2) {
298 return linearAnimation(selectedProgress, negativeProgress, selectedOpacity, 0, root.progress);
299 }
300
301 return 1;
302 }
303
304 property real topMarginProgress: {
305 if (priv.isSelected) {
306 return linearAnimation(selectedProgress, negativeProgress, selectedTopMarginProgress, 0, root.progress);
307 }
308 switch (spreadView.phase) {
309 case 0:
310 return 0;
311 case 1:
312 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
313 0, priv.phase2StartTopMarginProgress, root.progress);
314 }
315 return 1;
316 }
317
318 states: [
319 State {
320 name: "sideStageDragging"; when: spreadView.sideStageDragging && root.isInSideStage
321 PropertyChanges { target: priv; xTranslate: -spreadView.sideStageWidth + spreadView.sideStageWidth * spreadView.sideStageDragProgress }
322 }
323 ]
324 }
325
326 transform: [
327 Rotation {
328 origin { x: 0; y: spreadView.height / 2 }
329 axis { x: 0; y: 1; z: 0 }
330 angle: priv.angle
331 },
332 Scale {
333 origin { x: 0; y: spreadView.height / 2 }
334 xScale: priv.scale
335 yScale: xScale
336 },
337 Scale {
338 origin { x: 0; y: (spreadView.height * priv.scale) + maximizedAppTopMargin * 3 }
339 xScale: 1
340 yScale: isFullscreen ? 1 - priv.topMarginProgress * maximizedAppTopMargin / spreadView.height : 1
341 },
342 Translate {
343 x: priv.xTranslate
344 }
345 ]
346 opacity: priv.opacityTransform
347
348 UbuntuNumberAnimation {
349 id: fadeBackInAnimation
350 target: root
351 property: "opacity"
352 duration: UbuntuAnimation.SlowDuration
353 from: 0
354 to: 1
355 }
356
357 EasingCurve {
358 id: easingCurve
359 type: EasingCurve.OutSine
360 period: 1
361 progress: root.progress
362 }
363
364 EasingCurve {
365 id: helperEasingCurve
366 type: easingCurve.type
367 period: easingCurve.period
368 }
369}
0370
=== modified file 'src/CMakeLists.txt'
--- src/CMakeLists.txt 2014-06-30 12:46:04 +0000
+++ src/CMakeLists.txt 2014-07-29 14:07:22 +0000
@@ -1,8 +1,5 @@
1pkg_check_modules(UNITY-MIR REQUIRED unity-mir)
2
3include_directories(1include_directories(
4 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}2 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
5 ${UNITY-MIR_INCLUDE_DIRS}
6)3)
74
8file(GLOB_RECURSE QML_FILES5file(GLOB_RECURSE QML_FILES
96
=== modified file 'src/main.cpp'
--- src/main.cpp 2014-07-22 12:46:06 +0000
+++ src/main.cpp 2014-07-29 14:07:22 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2012 Canonical, Ltd.2 * Copyright (C) 2012-2014 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gerry Boland <gerry.boland@canonical.com>5 * Gerry Boland <gerry.boland@canonical.com>
@@ -23,12 +23,9 @@
23#include <QtGui/QGuiApplication>23#include <QtGui/QGuiApplication>
24#include <QtQml/QQmlEngine>24#include <QtQml/QQmlEngine>
25#include <QtQml/QQmlContext>25#include <QtQml/QQmlContext>
26#include <qpa/qplatformnativeinterface.h>
27#include <QLibrary>26#include <QLibrary>
28#include <QDebug>27#include <QDebug>
29#include <libintl.h>28#include <libintl.h>
30#include <dlfcn.h>
31#include <csignal>
3229
33// local30// local
34#include <paths.h>31#include <paths.h>
@@ -36,11 +33,13 @@
36#include "ApplicationArguments.h"33#include "ApplicationArguments.h"
37#include "CachingNetworkManagerFactory.h"34#include "CachingNetworkManagerFactory.h"
3835
39#include <unity-mir/qmirserver.h>36int main(int argc, const char *argv[])
40
41int startShell(int argc, const char** argv, void* server)
42{37{
43 const bool isUbuntuMirServer = qgetenv("QT_QPA_PLATFORM") == "ubuntumirserver";38 bool isMirServer = false;
39 if (qgetenv("QT_QPA_PLATFORM") == "ubuntumirclient") {
40 setenv("QT_QPA_PLATFORM", "mirserver", 1 /* overwrite */);
41 isMirServer = true;
42 }
4443
45 QGuiApplication::setApplicationName("unity8");44 QGuiApplication::setApplicationName("unity8");
46 QGuiApplication *application;45 QGuiApplication *application;
@@ -70,21 +69,7 @@
70Load the testability driver");69Load the testability driver");
71 parser.addOption(testabilityOption);70 parser.addOption(testabilityOption);
7271
73 if (isUbuntuMirServer) {72 application = new QGuiApplication(argc, (char**)argv);
74 QLibrary unityMir("unity-mir", 1);
75 unityMir.load();
76
77 typedef QGuiApplication* (*createServerApplication_t)(int&, const char **, void*);
78 createServerApplication_t createQMirServerApplication = (createServerApplication_t) unityMir.resolve("createQMirServerApplication");
79 if (!createQMirServerApplication) {
80 qDebug() << "unable to resolve symbol: createQMirServerApplication";
81 return 4;
82 }
83
84 application = createQMirServerApplication(argc, argv, server);
85 } else {
86 application = new QGuiApplication(argc, (char**)argv);
87 }
8873
89 // Treat args with single dashes the same as arguments with two dashes74 // Treat args with single dashes the same as arguments with two dashes
90 // Ex: -fullscreen == --fullscreen75 // Ex: -fullscreen == --fullscreen
@@ -100,8 +85,8 @@
100 if (parser.isSet(windowGeometryOption) &&85 if (parser.isSet(windowGeometryOption) &&
101 parser.value(windowGeometryOption).split('x').size() == 2)86 parser.value(windowGeometryOption).split('x').size() == 2)
102 {87 {
103 QStringList geom = parser.value(windowGeometryOption).split('x');88 QStringList geom = parser.value(windowGeometryOption).split('x');
104 qmlArgs.setSize(geom.at(0).toInt(), geom.at(1).toInt());89 qmlArgs.setSize(geom.at(0).toInt(), geom.at(1).toInt());
105 }90 }
10691
107 // The testability driver is only loaded by QApplication but not by QGuiApplication.92 // The testability driver is only loaded by QApplication but not by QGuiApplication.
@@ -125,7 +110,8 @@
125110
126 QQuickView* view = new QQuickView();111 QQuickView* view = new QQuickView();
127 view->setResizeMode(QQuickView::SizeRootObjectToView);112 view->setResizeMode(QQuickView::SizeRootObjectToView);
128 view->setTitle("Qml Phone Shell");113 view->setColor("black");
114 view->setTitle("Unity8 Shell");
129 view->engine()->setBaseUrl(QUrl::fromLocalFile(::qmlDirectory()));115 view->engine()->setBaseUrl(QUrl::fromLocalFile(::qmlDirectory()));
130 view->rootContext()->setContextProperty("applicationArguments", &qmlArgs);116 view->rootContext()->setContextProperty("applicationArguments", &qmlArgs);
131 view->rootContext()->setContextProperty("indicatorProfile", indicatorProfile);117 view->rootContext()->setContextProperty("indicatorProfile", indicatorProfile);
@@ -141,18 +127,9 @@
141 application->installNativeEventFilter(mouseTouchAdaptor);127 application->installNativeEventFilter(mouseTouchAdaptor);
142 }128 }
143129
144 QPlatformNativeInterface* nativeInterface = QGuiApplication::platformNativeInterface();
145 /* Shell is declared as a system session so that it always receives all
146 input events.
147 FIXME: use the enum value corresponding to SYSTEM_SESSION_TYPE (= 1)
148 when it becomes available.
149 */
150 nativeInterface->setProperty("ubuntuSessionType", 1);
151 view->setProperty("role", 2); // INDICATOR_ACTOR_ROLE
152
153 QUrl source(::qmlDirectory()+"Shell.qml");130 QUrl source(::qmlDirectory()+"Shell.qml");
154 prependImportPaths(view->engine(), ::overrideImportPaths());131 prependImportPaths(view->engine(), ::overrideImportPaths());
155 if (!isUbuntuMirServer) {132 if (!isMirServer) {
156 prependImportPaths(view->engine(), ::nonMirImportPaths());133 prependImportPaths(view->engine(), ::nonMirImportPaths());
157 }134 }
158 appendImportPaths(view->engine(), ::fallbackImportPaths());135 appendImportPaths(view->engine(), ::fallbackImportPaths());
@@ -161,10 +138,9 @@
161 view->engine()->setNetworkAccessManagerFactory(managerFactory);138 view->engine()->setNetworkAccessManagerFactory(managerFactory);
162139
163 view->setSource(source);140 view->setSource(source);
164 view->setColor("transparent");
165 QObject::connect(view->engine(), SIGNAL(quit()), application, SLOT(quit()));141 QObject::connect(view->engine(), SIGNAL(quit()), application, SLOT(quit()));
166142
167 if (qgetenv("QT_QPA_PLATFORM") == "ubuntu" || isUbuntuMirServer || parser.isSet(fullscreenOption)) {143 if (isMirServer || parser.isSet(fullscreenOption)) {
168 view->showFullScreen();144 view->showFullScreen();
169 } else {145 } else {
170 view->show();146 view->show();
@@ -178,54 +154,3 @@
178154
179 return result;155 return result;
180}156}
181
182int main(int argc, const char *argv[])
183{
184 /* Workaround Qt platform integration plugin not advertising itself
185 as having the following capabilities:
186 - QPlatformIntegration::ThreadedOpenGL
187 - QPlatformIntegration::BufferQueueingOpenGL
188 */
189 setenv("QML_FORCE_THREADED_RENDERER", "1", 1);
190 setenv("QML_FIXED_ANIMATION_STEP", "1", 1);
191
192 // For ubuntumirserver/ubuntumirclient
193 if (qgetenv("QT_QPA_PLATFORM").startsWith("ubuntumir")) {
194 setenv("QT_QPA_PLATFORM", "ubuntumirserver", 1);
195
196 // If we use unity-mir directly, we automatically link to the Mir-server
197 // platform-api bindings, which result in unexpected behaviour when
198 // running the non-Mir scenario.
199 QLibrary unityMir("unity-mir", 1);
200 unityMir.load();
201 if (!unityMir.isLoaded()) {
202 qDebug() << "Library unity-mir not found/loaded";
203 return 1;
204 }
205
206 typedef QMirServer* (*createServer_t)(int, const char **);
207 createServer_t createQMirServer = (createServer_t) unityMir.resolve("createQMirServer");
208 if (!createQMirServer) {
209 qDebug() << "unable to resolve symbol: createQMirServer";
210 return 2;
211 }
212
213 QMirServer* mirServer = createQMirServer(argc, argv);
214
215 typedef int (*runWithClient_t)(QMirServer*, std::function<int(int, const char**, void*)>);
216 runWithClient_t runWithClient = (runWithClient_t) unityMir.resolve("runQMirServerWithClient");
217 if (!runWithClient) {
218 qDebug() << "unable to resolve symbol: runWithClient";
219 return 3;
220 }
221
222 return runWithClient(mirServer, startShell);
223 } else {
224 if (!qgetenv("UNITY_MIR_EMITS_SIGSTOP").isEmpty()) {
225 // Emit SIGSTOP as expected by upstart, under Mir it's unity-mir that will raise it.
226 // see http://upstart.ubuntu.com/cookbook/#expect-stop
227 raise(SIGSTOP);
228 }
229 return startShell(argc, argv, nullptr);
230 }
231}
232157
=== modified file 'tests/mocks/Unity/Application/Application.qmltypes'
--- tests/mocks/Unity/Application/Application.qmltypes 2014-06-26 08:26:58 +0000
+++ tests/mocks/Unity/Application/Application.qmltypes 2014-07-29 14:07:22 +0000
@@ -102,8 +102,6 @@
102 "ForceMainStage": 1102 "ForceMainStage": 1
103 }103 }
104 }104 }
105 Property { name: "keyboardHeight"; type: "int"; isReadonly: true }
106 Property { name: "keyboardVisible"; type: "bool"; isReadonly: true }
107 Property { name: "sideStageWidth"; type: "int"; isReadonly: true }105 Property { name: "sideStageWidth"; type: "int"; isReadonly: true }
108 Property { name: "stageHint"; type: "StageHint"; isReadonly: true }106 Property { name: "stageHint"; type: "StageHint"; isReadonly: true }
109 Property { name: "formFactorHint"; type: "FormFactorHint"; isReadonly: true }107 Property { name: "formFactorHint"; type: "FormFactorHint"; isReadonly: true }
110108
=== added file 'tests/mocks/Unity/Application/ApplicationDBusAdaptor.cpp'
--- tests/mocks/Unity/Application/ApplicationDBusAdaptor.cpp 1970-01-01 00:00:00 +0000
+++ tests/mocks/Unity/Application/ApplicationDBusAdaptor.cpp 2014-07-29 14:07:22 +0000
@@ -0,0 +1,75 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "ApplicationInfo.h"
18#include "ApplicationDBusAdaptor.h"
19#include "ApplicationManager.h"
20#include "SurfaceManager.h"
21#include "MirSurfaceItem.h"
22
23quint32 nextId = 0;
24
25ApplicationDBusAdaptor::ApplicationDBusAdaptor(ApplicationManager* applicationManager)
26 : QDBusAbstractAdaptor(applicationManager)
27 , m_applicationManager(applicationManager)
28{
29}
30
31MirSurfaceItem* topSurface(MirSurfaceItem* surface)
32{
33 if (!surface)
34 return nullptr;
35
36 MirSurfaceItem* child = NULL;
37 Q_FOREACH (MirSurfaceItem *childSurface, surface->childSurfaceList()) {
38 child = topSurface(childSurface);
39 if (child) {
40 break;
41 }
42 }
43 return child ? child : surface;
44}
45
46quint32 ApplicationDBusAdaptor::addChildSurface(const QString &appId, const QString &surfaceImage)
47{
48 qDebug() << "ApplicationDBusAdaptor::addChildSurface - " << appId;
49
50 ApplicationInfo* application = m_applicationManager->findApplication(appId);
51 if (!application) {
52 qDebug() << "ApplicationDBusAdaptor::addChildSurface - No application found for " << appId;
53 return 0;
54 }
55 quint32 surfaceId = ++nextId;
56 MirSurfaceItem* surface = new MirSurfaceItem(QString("ChildSurface%1").arg(surfaceId),
57 MirSurfaceItem::Normal,
58 MirSurfaceItem::Maximized,
59 QUrl(surfaceImage));
60 m_childItems[surfaceId] = surface;
61 surface->setParentSurface(topSurface(application->surface()));
62 return surfaceId;
63}
64
65void ApplicationDBusAdaptor::removeChildSurface(int surfaceId)
66{
67 qDebug() << "ApplicationDBusAdaptor::removeChildSurface - " << surfaceId;
68
69 if (!m_childItems.contains(surfaceId)) {
70 qDebug() << "ApplicationDBusAdaptor::removeChildSurface - No added surface for " << surfaceId;
71 return;
72 }
73 MirSurfaceItem* surface = m_childItems.take(surfaceId);
74 Q_EMIT surface->removed();
75}
076
=== added file 'tests/mocks/Unity/Application/ApplicationDBusAdaptor.h'
--- tests/mocks/Unity/Application/ApplicationDBusAdaptor.h 1970-01-01 00:00:00 +0000
+++ tests/mocks/Unity/Application/ApplicationDBusAdaptor.h 2014-07-29 14:07:22 +0000
@@ -0,0 +1,43 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef APPLICATIONDBUSADAPTOR_H
18#define APPLICATIONDBUSADAPTOR_H
19
20#include <QtDBus/QtDBus>
21
22class ApplicationManager;
23class SurfaceManager;
24class MirSurfaceItem;
25
26class ApplicationDBusAdaptor : public QDBusAbstractAdaptor
27{
28 Q_OBJECT
29 Q_CLASSINFO("D-Bus Interface", "com.canonical.Unity8.Mocks.Application")
30public:
31 ApplicationDBusAdaptor(ApplicationManager* applicationManager);
32
33public Q_SLOTS:
34 quint32 addChildSurface(const QString& appId, const QString& surfaceImage);
35 void removeChildSurface(int surfaceId);
36
37private:
38 ApplicationManager* m_applicationManager;
39
40 QHash<quint32, MirSurfaceItem*> m_childItems;
41};
42
43#endif // APPLICATIONDBUSADAPTOR_H
044
=== modified file 'tests/mocks/Unity/Application/ApplicationInfo.cpp'
--- tests/mocks/Unity/Application/ApplicationInfo.cpp 2014-03-20 10:02:52 +0000
+++ tests/mocks/Unity/Application/ApplicationInfo.cpp 2014-07-29 14:07:22 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013 Canonical, Ltd.2 * Copyright (C) 2013-2014 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by5 * it under the terms of the GNU General Public License as published by
@@ -15,6 +15,8 @@
15 */15 */
1616
17#include "ApplicationInfo.h"17#include "ApplicationInfo.h"
18#include "MirSurfaceItem.h"
19#include "SurfaceManager.h"
1820
19#include <QGuiApplication>21#include <QGuiApplication>
20#include <QQuickItem>22#include <QQuickItem>
@@ -24,29 +26,39 @@
2426
25ApplicationInfo::ApplicationInfo(const QString &appId, QObject *parent)27ApplicationInfo::ApplicationInfo(const QString &appId, QObject *parent)
26 : ApplicationInfoInterface(appId, parent)28 : ApplicationInfoInterface(appId, parent)
27 ,m_appId(appId)29 , m_appId(appId)
28 ,m_stage(MainStage)30 , m_stage(MainStage)
29 ,m_state(Starting)31 , m_state(Starting)
30 ,m_focused(false)32 , m_focused(false)
31 ,m_fullscreen(false)33 , m_fullscreen(false)
32 ,m_windowItem(0)34 , m_windowItem(0)
33 ,m_windowComponent(0)35 , m_windowComponent(0)
34 ,m_parentItem(0)36 , m_parentItem(0)
37 , m_surface(0)
35{38{
36 QTimer::singleShot(300, this, SLOT(setRunning()));39 connect(this, &ApplicationInfo::stateChanged, this, &ApplicationInfo::onStateChanged);
37}40}
3841
39ApplicationInfo::ApplicationInfo(QObject *parent)42ApplicationInfo::ApplicationInfo(QObject *parent)
40 : ApplicationInfoInterface(QString(), parent)43 : ApplicationInfoInterface(QString(), parent)
41 ,m_stage(MainStage)44 , m_stage(MainStage)
42 ,m_state(Starting)45 , m_state(Starting)
43 ,m_focused(false)46 , m_focused(false)
44 ,m_fullscreen(false)47 , m_fullscreen(false)
45 ,m_windowItem(0)48 , m_windowItem(0)
46 ,m_windowComponent(0)49 , m_windowComponent(0)
47 ,m_parentItem(0)50 , m_parentItem(0)
48{51 , m_surface(0)
49 QTimer::singleShot(300, this, SLOT(setRunning()));52{
53 connect(this, &ApplicationInfo::stateChanged, this, &ApplicationInfo::onStateChanged);
54}
55
56ApplicationInfo::~ApplicationInfo()
57{
58 if (m_surface) {
59 Q_EMIT SurfaceManager::singleton()->surfaceDestroyed(m_surface);
60 m_surface->deleteLater();
61 }
50}62}
5163
52void ApplicationInfo::onWindowComponentStatusChanged(QQmlComponent::Status status)64void ApplicationInfo::onWindowComponentStatusChanged(QQmlComponent::Status status)
@@ -55,10 +67,46 @@
55 doCreateWindowItem();67 doCreateWindowItem();
56}68}
5769
58void ApplicationInfo::setRunning()70void ApplicationInfo::onStateChanged(State state)
59{71{
60 m_state = Running;72 if (state == ApplicationInfo::Running) {
61 Q_EMIT stateChanged();73 QTimer::singleShot(1000, this, SLOT(createSurface()));
74 } else if (state == ApplicationInfo::Stopped) {
75 setSurface(nullptr);
76 }
77}
78
79void ApplicationInfo::createSurface()
80{
81 if (m_surface || state() == ApplicationInfo::Stopped) return;
82
83 setSurface(new MirSurfaceItem(name(),
84 MirSurfaceItem::Normal,
85 fullscreen() ? MirSurfaceItem::Fullscreen : MirSurfaceItem::Maximized,
86 screenshot()));
87}
88
89void ApplicationInfo::setSurface(MirSurfaceItem* surface)
90{
91 if (m_surface == surface)
92 return;
93
94 if (m_surface) {
95 m_surface->setApplication(nullptr);
96 m_surface->setParent(nullptr);
97 SurfaceManager::singleton()->unregisterSurface(m_surface);
98 }
99
100 m_surface = surface;
101
102 if (m_surface) {
103 m_surface->setApplication(this);
104 m_surface->setParent(this);
105 SurfaceManager::singleton()->registerSurface(m_surface);
106 }
107
108 Q_EMIT surfaceChanged(m_surface);
109 SurfaceManager::singleton()->registerSurface(m_surface);
62}110}
63111
64void ApplicationInfo::createWindowComponent()112void ApplicationInfo::createWindowComponent()
65113
=== modified file 'tests/mocks/Unity/Application/ApplicationInfo.h'
--- tests/mocks/Unity/Application/ApplicationInfo.h 2014-03-20 10:02:52 +0000
+++ tests/mocks/Unity/Application/ApplicationInfo.h 2014-07-29 14:07:22 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013 Canonical, Ltd.2 * Copyright (C) 2013-2014 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by5 * it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
21#include <QQmlComponent>21#include <QQmlComponent>
2222
23class QQuickItem;23class QQuickItem;
24class MirSurfaceItem;
2425
25// unity-api26// unity-api
26#include <unity/shell/application/ApplicationInfoInterface.h>27#include <unity/shell/application/ApplicationInfoInterface.h>
@@ -36,6 +37,7 @@
3637
37 Q_PROPERTY(bool fullscreen READ fullscreen WRITE setFullscreen NOTIFY fullscreenChanged)38 Q_PROPERTY(bool fullscreen READ fullscreen WRITE setFullscreen NOTIFY fullscreenChanged)
38 Q_PROPERTY(Stage stage READ stage WRITE setStage NOTIFY stageChanged)39 Q_PROPERTY(Stage stage READ stage WRITE setStage NOTIFY stageChanged)
40 Q_PROPERTY(MirSurfaceItem* surface READ surface NOTIFY surfaceChanged)
3941
40 // Only exists in this fake implementation42 // Only exists in this fake implementation
4143
@@ -45,9 +47,10 @@
45 // QML component used to represent the application window47 // QML component used to represent the application window
46 Q_PROPERTY(QString windowQml READ windowQml WRITE setWindowQml NOTIFY windowQmlChanged)48 Q_PROPERTY(QString windowQml READ windowQml WRITE setWindowQml NOTIFY windowQmlChanged)
4749
48 public:50public:
49 ApplicationInfo(QObject *parent = NULL);51 ApplicationInfo(QObject *parent = NULL);
50 ApplicationInfo(const QString &appId, QObject *parent = NULL);52 ApplicationInfo(const QString &appId, QObject *parent = NULL);
53 ~ApplicationInfo();
5154
52 #define IMPLEMENT_PROPERTY(name, Name, type) \55 #define IMPLEMENT_PROPERTY(name, Name, type) \
53 public: \56 public: \
@@ -56,11 +59,11 @@
56 { \59 { \
57 if (m_##name != value) { \60 if (m_##name != value) { \
58 m_##name = value; \61 m_##name = value; \
59 Q_EMIT name##Changed(); \62 Q_EMIT name##Changed(value); \
60 } \63 } \
61 } \64 } \
62 Q_SIGNALS: \65 Q_SIGNALS: \
63 void name##Changed(); \66 void name##Changed(const type&); \
64 private: \67 private: \
65 type m_##name;68 type m_##name;
6669
@@ -78,21 +81,31 @@
7881
79 #undef IMPLEMENT_PROPERTY82 #undef IMPLEMENT_PROPERTY
8083
81 public:84public:
85 void setSurface(MirSurfaceItem* surface);
86 MirSurfaceItem* surface() const { return m_surface; }
87
88Q_SIGNALS:
89 void surfaceChanged(MirSurfaceItem*);
90
91public:
82 void showWindow(QQuickItem *parent);92 void showWindow(QQuickItem *parent);
83 void hideWindow();93 void hideWindow();
8494
85 private Q_SLOTS:
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches