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