Merge lp:~dandrader/unity8/surfaceListModel into lp:unity8
- surfaceListModel
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Michał Sawicz |
Approved revision: | 2360 |
Merged at revision: | 2336 |
Proposed branch: | lp:~dandrader/unity8/surfaceListModel |
Merge into: | lp:unity8 |
Prerequisite: | lp:~dandrader/unity8/declarativeKeymap |
Diff against target: |
11952 lines (+4061/-2915) 78 files modified
debian/control (+1/-1) debian/unity8-private.install (+1/-0) plugins/CMakeLists.txt (+1/-0) plugins/WindowManager/CMakeLists.txt (+13/-0) plugins/WindowManager/TopLevelSurfaceList.cpp (+486/-0) plugins/WindowManager/TopLevelSurfaceList.h (+223/-0) plugins/WindowManager/WindowManagerPlugin.cpp (+28/-0) plugins/WindowManager/WindowManagerPlugin.h (+32/-0) plugins/WindowManager/qmldir (+2/-0) qml/Components/KeymapSwitcher.qml (+1/-9) qml/Greeter/Greeter.qml (+5/-4) qml/Launcher/LauncherPanel.qml (+1/-0) qml/Shell.qml (+67/-53) qml/Stages/AbstractStage.qml (+9/-2) qml/Stages/Animations/BaseSessionAnimation.qml (+0/-96) qml/Stages/Animations/SwipeFromBottomAnimation.qml (+0/-53) qml/Stages/ApplicationWindow.qml (+110/-68) qml/Stages/DecoratedWindow.qml (+5/-5) qml/Stages/DesktopSpread.qml (+22/-18) qml/Stages/DesktopSpreadDelegate.qml (+4/-3) qml/Stages/DesktopStage.qml (+162/-101) qml/Stages/PhoneStage.qml (+154/-91) qml/Stages/PromptSurfaceAnimations.qml (+81/-0) qml/Stages/SpreadDelegate.qml (+4/-3) qml/Stages/StagedFullscreenPolicy.qml (+12/-14) qml/Stages/SurfaceContainer.qml (+0/-154) qml/Stages/TabletStage.qml (+312/-249) qml/Stages/TopLevelSurfaceRepeater.qml (+52/-0) qml/Stages/TransformedSpreadDelegate.qml (+4/-4) qml/Stages/TransformedTabletSpreadDelegate.qml (+29/-23) qml/Stages/WindowedFullscreenPolicy.qml (+5/-7) qml/Tutorial/TutorialBottom.qml (+1/-1) tests/mocks/Unity/Application/ApplicationInfo.cpp (+195/-122) tests/mocks/Unity/Application/ApplicationInfo.h (+39/-24) tests/mocks/Unity/Application/ApplicationManager.cpp (+115/-80) tests/mocks/Unity/Application/ApplicationManager.h (+12/-14) tests/mocks/Unity/Application/ApplicationTestInterface.cpp (+0/-117) tests/mocks/Unity/Application/ApplicationTestInterface.h (+0/-45) tests/mocks/Unity/Application/CMakeLists.txt (+3/-4) tests/mocks/Unity/Application/MirSurface.cpp (+175/-10) tests/mocks/Unity/Application/MirSurface.h (+51/-7) tests/mocks/Unity/Application/MirSurfaceItem.cpp (+13/-7) tests/mocks/Unity/Application/MirSurfaceListModel.cpp (+121/-0) tests/mocks/Unity/Application/MirSurfaceListModel.h (+56/-0) tests/mocks/Unity/Application/ObjectListModel.h (+2/-0) tests/mocks/Unity/Application/Session.cpp (+0/-209) tests/mocks/Unity/Application/Session.h (+0/-104) tests/mocks/Unity/Application/SessionManager.cpp (+0/-55) tests/mocks/Unity/Application/SessionManager.h (+0/-44) tests/mocks/Unity/Application/SessionModel.h (+0/-35) tests/mocks/Unity/Application/SurfaceManager.cpp (+16/-9) tests/mocks/Unity/Application/SurfaceManager.h (+5/-2) tests/mocks/Unity/Application/UbuntuKeyboardInfo.cpp (+0/-2) tests/mocks/Unity/Application/UbuntuKeyboardInfo.h (+0/-9) tests/mocks/Unity/Application/VirtualKeyboard.cpp (+2/-3) tests/mocks/Unity/Application/VirtualKeyboard.h (+1/-1) tests/mocks/Unity/Application/plugin.cpp (+41/-44) tests/mocks/Utils/CMakeLists.txt (+4/-0) tests/mocks/Utils/plugin.cpp (+1/-1) tests/plugins/Unity/Launcher/launchermodeltest.cpp (+2/-3) tests/qmltests/CMakeLists.txt (+0/-1) tests/qmltests/Dash/tst_DashShell.qml (+6/-2) tests/qmltests/Greeter/tst_Greeter.qml (+2/-2) tests/qmltests/Panel/tst_ActiveCallHint.qml (+3/-2) tests/qmltests/Panel/tst_Panel.qml (+2/-2) tests/qmltests/Stages/ApplicationCheckBox.qml (+117/-71) tests/qmltests/Stages/RecursingChildSessionControl.qml (+10/-61) tests/qmltests/Stages/tst_ApplicationWindow.qml (+84/-70) tests/qmltests/Stages/tst_DesktopStage.qml (+253/-171) tests/qmltests/Stages/tst_PhoneStage.qml (+272/-82) tests/qmltests/Stages/tst_SpreadDelegate.qml (+3/-2) tests/qmltests/Stages/tst_SurfaceContainer.qml (+0/-184) tests/qmltests/Stages/tst_TabletStage.qml (+255/-92) tests/qmltests/Tutorial/tst_Tutorial.qml (+4/-8) tests/qmltests/tst_OrientedShell.qml (+96/-69) tests/qmltests/tst_Shell.qml (+244/-171) tests/qmltests/tst_ShellWithPin.qml (+9/-13) tests/utils/modules/Unity/Test/UnityTestCase.qml (+20/-2) |
To merge this branch: | bzr merge lp:~dandrader/unity8/surfaceListModel |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michał Sawicz | Approve | ||
Unity8 CI Bot | continuous-integration | Needs Fixing | |
Lukáš Tinkl (community) | Approve | ||
Gerry Boland (community) | Approve | ||
Nick Dedekind (community) | Needs Fixing | ||
Review via email: mp+290314@code.launchpad.net |
Commit message
Surface-based window management
- We no longer deal with the Session concept.
- Each application has a list of top-level surfaces and each surface has a list of prompt surfaces (general support for child surfaces not yet implemented)
- Stages (desktop, phone and tablet) work on a TopLevelSurfaceList model instead of ApplicationManager.
- TopLevelSurfaceList contains all the top-level surfaces from all running (or suspended) applications
Description of the change
* Are there any related MPs required for this MP to build/function as expected? Please list.
https:/
https:/
* Did you perform an exploratory manual test run of your code change and any related functionality?
Ongoing...
For testing the "multiple surfaces per application" feature you can use multiwindow.qml from lp:~dandrader/+junk/animatedDemos
You can also create as many surfaces for an application as you want in qml tests UI controls, like in "make tryShell"
* 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?
N/A
* If you changed the UI, has there been a design review?
N/A
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2311
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Gerry Boland (gerboland) wrote : | # |
UI bug:
1. open dialer app
2. hit power key to display off, then again to display on
3. enter PIN
4. watch the dialer-app surface
I see dialer-app surface resize after you unlock. AS if it switches from fullscreen back to windowed.
Gerry Boland (gerboland) wrote : | # |
Camera app not becoming fullscreen in 2 ways for me (Nexus 4):
1. panel still visible
2. camera view not filling the whole of the app surface.
See http://
Gerry Boland (gerboland) wrote : | # |
On phone, I'm not seeing background apps being suspended at all!
Wakelocks are being managed ok though, afaics.
Gerry Boland (gerboland) wrote : | # |
Gallery unable to make images fullscreen when viewing them.
Also confirmed that apps not being suspended (they are getting the expose events though) - start Dropping letters and then switch away - game will not stop and you will loose
Gerry Boland (gerboland) wrote : | # |
OOM killing not behaving as I'd expect.
1. I managed to crash unity8 with 4 apps open, and killing a non-visible one via command line. Not easily reproduced though
2. Open dialer & clock. Let both load. Focus dialer. Then kill the clock process via adb. Opening spread, the Clock app has been removed from the list. It should remain, and relaunch the app if focused.
3. Open clock. Switch back to Dash. Kill the clock process. Then tap the Clock app in the dash to re-launch it. No splash appears, but clock does appear a moment later.
Nick Dedekind (nick-dedekind) wrote : | # |
A few code comments attached.
Daniel d'Andrada (dandrader) wrote : | # |
On 30/03/2016 13:16, Nick Dedekind wrote:
> Why slots rather than invokables?
Because that's the new way for expressing functions callable from QML it
seems. At least mzanetti told me to do like that.
I do see the benefit of cleaner looking header files at least...
Nick Dedekind (nick-dedekind) wrote : | # |
Getting an animation artifact in tablet stage. Not sure if it's in trunk, but I think you reported something similar and I fixed it.
TestCase:
Open a bunch of apps, swipe in switcher.
Select an app ( not bottom ).
Expected:
App expands to full screen with apps behind not visible.
Actual:
Briefly see the app behind it until selected one becomes opaque.
Nick Dedekind (nick-dedekind) wrote : | # |
Tablet (N7) just rebooted... power button + greeter swipe a few times.
Daniel d'Andrada (dandrader) wrote : | # |
On 30/03/2016 13:16, Nick Dedekind wrote:
> why not just use INT_MAX to decrease your duplicate hit?
Gerry made the same question. Short answer is:
Code does m_maxId+1. If m_maxId is indeed the absolut maximum the
variable type can handle it will overflow and be -INT_MAX (negative).
Nick Dedekind (nick-dedekind) wrote : | # |
Tablet Stage:
Side stage becomes immediately visible when swiping for spread.
Test:
Open app.
Send to side stage.
Close side stage.
Slowly swipe in spread
Expected:
Side stage swipes in with finger movement.
Can reverse direction
Actual:
Side stage becomes immediately visible.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2312
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Daniel d'Andrada (dandrader) wrote : | # |
"""
Played with this over the weekend on my Bq. Issues found:
1. app start animation broken, splash doesn't slide in, it just appears
2. left edge swipe broken, it always bounces back. Can never get to Dash using that method.
3. launcher not launching apps
4. launcher not switching apps
5. app focus has changed slightly, there are times with OSK stays visible, when it should have gone away. Examples:
i. have app with OSK up, open indicators. OSK remains up.
ii. have app with OSK up, right-edge swipe to bring up spread. OSK remains up
6. not moving focus to trust prompt sessions. Example, in Camera app, try share photo:
i. share selector surface not animated in from bottom
ii. selected share app not brought to front.
"""
Fixed all items.
6.i is wrong though: that share selector seems to be inside camera's surface since I don't see any notice of new surface coming up, trusted session or anything of the like in qtmir's log.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2313
https:/
Executed test runs:
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Daniel d'Andrada (dandrader) wrote : | # |
On 30/03/2016 11:07, Gerry Boland wrote:
> Review: Needs Fixing
>
> UI bug:
> 1. open dialer app
> 2. hit power key to display off, then again to display on
> 3. enter PIN
> 4. watch the dialer-app surface
>
> I see dialer-app surface resize after you unlock. AS if it switches from fullscreen back to windowed.
Can't reproduce it. Might have been fixed as well along with the other
issues.
Daniel d'Andrada (dandrader) wrote : | # |
On 30/03/2016 11:13, Gerry Boland wrote:
> Camera app not becoming fullscreen in 2 ways for me (Nexus 4):
> 1. panel still visible
> 2. camera view not filling the whole of the app surface.
> See http://
Fixed.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2314
https:/
Executed test runs:
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Daniel d'Andrada (dandrader) wrote : | # |
On 30/03/2016 11:21, Gerry Boland wrote:
> Review: Needs Fixing
>
> On phone, I'm not seeing background apps being suspended at all!
>
> Wakelocks are being managed ok though, afaics.
Fixed.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2315
https:/
Executed test runs:
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Daniel d'Andrada (dandrader) wrote : | # |
On 30/03/2016 11:30, Gerry Boland wrote:
> Gallery unable to make images fullscreen when viewing them.
Already fixed
> Also confirmed that apps not being suspended (they are getting the expose events though) - start Dropping letters and then switch away - game will not stop and you will loose
Already fixed
Daniel d'Andrada (dandrader) wrote : | # |
On 30/03/2016 12:10, Gerry Boland wrote:
> Review: Needs Fixing
>
> OOM killing not behaving as I'd expect.
> 1. I managed to crash unity8 with 4 apps open, and killing a non-visible one via command line. Not easily reproduced though
Stacktrace please. And try again since a lot has changed since then
> 2. Open dialer & clock. Let both load. Focus dialer. Then kill the clock process via adb. Opening spread, the Clock app has been removed from the list. It should remain, and relaunch the app if focused.
Already fixed.
> 3. Open clock. Switch back to Dash. Kill the clock process. Then tap the Clock app in the dash to re-launch it. No splash appears, but clock does appear a moment later.
>
Fixed.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2316
https:/
Executed test runs:
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Daniel d'Andrada (dandrader) wrote : | # |
On 30/03/2016 13:39, Nick Dedekind wrote:
> Review: Needs Fixing
>
> Tablet Stage:
> Side stage becomes immediately visible when swiping for spread.
>
> Test:
> Open app.
> Send to side stage.
> Close side stage.
> Slowly swipe in spread
>
> Expected:
> Side stage swipes in with finger movement.
> Can reverse direction
>
> Actual:
> Side stage becomes immediately visible.
Fixed.
Daniel d'Andrada (dandrader) wrote : | # |
On 30/03/2016 13:27, Nick Dedekind wrote:
> Review: Needs Fixing
>
> Getting an animation artifact in tablet stage. Not sure if it's in trunk, but I think you reported something similar and I fixed it.
>
> TestCase:
> Open a bunch of apps, swipe in switcher.
> Select an app ( not bottom ).
>
> Expected:
> App expands to full screen with apps behind not visible.
>
> Actual:
> Briefly see the app behind it until selected one becomes opaque.
>
Didn't get the "not bottom" comment.
Anyway, tried with "make tryTabletStage" by clicking on all app
checkboxes, going to spread, and selecting the left most or middle
window. Didn't spot a difference in behaviour between trunk and this branch.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2317
https:/
Executed test runs:
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Daniel d'Andrada (dandrader) wrote : | # |
On 30/03/2016 13:29, Nick Dedekind wrote:
> Tablet (N7) just rebooted... power button + greeter swipe a few times.
Can't reproduce.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2335
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Gerry Boland (gerboland) wrote : | # |
You've done some good work, phone is behaving much better now!
Some issues I've found while testing. Nothing in any specific order:
1. open 2 apps. Open the spread. Then reveal launcher and start a *new* app. I see spread closing, with focus going to the last focused app. The new app does launch, but in the background.
2. I've noticed that when an application starts & is shown on screen, it's Qt.application.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2337
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Daniel d'Andrada (dandrader) wrote : | # |
On 05/04/2016 08:13, Gerry Boland wrote:
> Review: Needs Fixing
>
> You've done some good work, phone is behaving much better now!
>
> Some issues I've found while testing. Nothing in any specific order:
> 1. open 2 apps. Open the spread. Then reveal launcher and start a *new* app. I see spread closing, with focus going to the last focused app. The new app does launch, but in the background.
Fixed
> 2. I've noticed that when an application starts & is shown on screen, it's Qt.application.
Fixed.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2338
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2339
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2340
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2341
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Gerry Boland (gerboland) wrote : | # |
Was this an existing bug on the Tablet?:
Open these apps & orient them in the Spread in this order:
1. Dialer app - side stage
2. Dash
3. Messaging app - main stage
with Dialer on top, Messaging on bottom of the stack.
Open spread, try to tap the Dash surface - but only tap to the right of the Messaging app (i.e. the smaller bit of the Dash surface exposed), to select it. See https:/
Gerry Boland (gerboland) wrote : | # |
Just testing N7 with mouse connected (desktop mode) - the mouse cursor is not visible
Gerry Boland (gerboland) wrote : | # |
Aha, not accurate. It appears mouse does not draw itself until it moves at least once!
Desktop mode testing:
1. Open System Settings. With indicators are open on Time & date page, tap the "Time & Date settings" entry. For me, the indicator stays open, but System Settings does change to the correct page in the background. The indicator should close.
2. In the desktop spread, System Settings has a preview, an icon, but is missing its text. This might not be new though.
I've tested some basic multi-window apps. You have things working nicely.
Daniel d'Andrada (dandrader) wrote : | # |
On 06/04/2016 08:08, Gerry Boland wrote:
> Was this an existing bug on the Tablet?:
> Open these apps & orient them in the Spread in this order:
> 1. Dialer app - side stage
> 2. Dash
> 3. Messaging app - main stage
> with Dialer on top, Messaging on bottom of the stack.
>
> Open spread, try to tap the Dash surface - but only tap to the right of the Messaging app (i.e. the smaller bit of the Dash surface exposed), to select it. See https:/
Nothing happens when I tap on the red "X" location on trunk. So,
pre-existing behavior
Daniel d'Andrada (dandrader) wrote : | # |
On 06/04/2016 08:28, Gerry Boland wrote:
> Desktop mode testing:
> 1. Open System Settings. With indicators are open on Time & date page, tap the "Time & Date settings" entry. For me, the indicator stays open, but System Settings does change to the correct page in the background. The indicator should close.
Fixed.
>
> 2. In the desktop spread, System Settings has a preview, an icon, but is missing its text. This might not be new though.
>
Fixed.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2342
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2343
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2344
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Daniel d'Andrada (dandrader) wrote : | # |
Run unity8 autopilot tests and got only one failure, which is the same result I got with trunk.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2345
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2346
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2346
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2347
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2348
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Daniel d'Andrada (dandrader) wrote : | # |
All tests should be passing now.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2349
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2350
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2351
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2352
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Gerry Boland (gerboland) wrote : | # |
Did last round of AP tests, unity8 tests ok, UITK tests have similar number of fails before & after. Bunch more manual testing not revealed anything. Overall, I think this is good to go! Nice work
Gerry Boland (gerboland) wrote : | # |
OFC need to sort out dependencies
Lukáš Tinkl (lukas-kde) wrote : | # |
readonly property string title: surface && surface.name !== "" ? surface.name + " - " + d.name : d.name
This leads to funny window titles like "Scopes - Scopes"
Daniel d'Andrada (dandrader) wrote : | # |
On 12/04/2016 12:11, Lukáš Tinkl wrote:
> Review: Needs Fixing
>
> readonly property string title: surface && surface.name !== "" ? surface.name + " - " + d.name : d.name
>
> This leads to funny window titles like "Scopes - Scopes"
Fixed.
Lukáš Tinkl (lukas-kde) wrote : | # |
- onFocusMaximize
- ApplicationMana
- }
^^ this piece of code disappeared without replacement from DesktopStage.qml; now it's no longer possible to focus a maximized app in the background by clicking the panel.
Daniel d'Andrada (dandrader) wrote : | # |
On 13/04/2016 05:59, Lukáš Tinkl wrote:
> Review: Needs Fixing
>
> - onFocusMaximize
> - ApplicationMana
> - }
>
>
> ^^ this piece of code disappeared without replacement from DesktopStage.qml; now it's no longer possible to focus a maximized app in the background by clicking the panel.
Fixed.
Lukáš Tinkl (lukas-kde) wrote : | # |
OK, no more issues spotted
Michał Sawicz (saviq) wrote : | # |
- // Emit signal to notify Upstart that Mir is ready to receive client connections
- // see http://
- if (qgetenv(
- raise(SIGSTOP);
- }
That's no good - autopilot tests hang because of this.
- 2359. By Daniel d'Andrada
-
Bring back raise(SIGSTOP) to our ApplicationManager mock
- 2360. By Daniel d'Andrada
-
Fix typo in comment
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2358
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2360
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Michał Sawicz (saviq) : | # |
Preview Diff
1 | === modified file 'debian/control' |
2 | --- debian/control 2016-04-20 17:09:15 +0000 |
3 | +++ debian/control 2016-04-20 17:09:16 +0000 |
4 | @@ -30,7 +30,7 @@ |
5 | libqt5xmlpatterns5-dev, |
6 | libsystemsettings-dev, |
7 | libudev-dev, |
8 | - libunity-api-dev (>= 7.108), |
9 | + libunity-api-dev (>= 7.110), |
10 | libusermetricsoutput1-dev, |
11 | # Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop |
12 | libx11-dev[!armhf], |
13 | |
14 | === modified file 'debian/unity8-private.install' |
15 | --- debian/unity8-private.install 2015-11-18 09:15:06 +0000 |
16 | +++ debian/unity8-private.install 2016-04-20 17:09:16 +0000 |
17 | @@ -14,6 +14,7 @@ |
18 | usr/lib/*/unity8/qml/Unity |
19 | usr/lib/*/unity8/qml/UInput |
20 | usr/lib/*/unity8/qml/Utils |
21 | +usr/lib/*/unity8/qml/WindowManager |
22 | usr/lib/*/unity8/qml/Wizard |
23 | usr/share/accountsservice/interfaces |
24 | usr/share/dbus-1/interfaces |
25 | |
26 | === modified file 'plugins/CMakeLists.txt' |
27 | --- plugins/CMakeLists.txt 2015-11-11 16:03:24 +0000 |
28 | +++ plugins/CMakeLists.txt 2016-04-20 17:09:16 +0000 |
29 | @@ -25,4 +25,5 @@ |
30 | add_subdirectory(UInput) |
31 | add_subdirectory(Unity) |
32 | add_subdirectory(Utils) |
33 | +add_subdirectory(WindowManager) |
34 | add_subdirectory(Wizard) |
35 | |
36 | === added directory 'plugins/WindowManager' |
37 | === added file 'plugins/WindowManager/CMakeLists.txt' |
38 | --- plugins/WindowManager/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
39 | +++ plugins/WindowManager/CMakeLists.txt 2016-04-20 17:09:16 +0000 |
40 | @@ -0,0 +1,13 @@ |
41 | +set(WINDOWMANAGER_SRC |
42 | + TopLevelSurfaceList.cpp |
43 | + WindowManagerPlugin.cpp |
44 | + ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h |
45 | + ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/Mir.h |
46 | + ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceInterface.h |
47 | + ) |
48 | + |
49 | +add_library(windowmanager-qml SHARED ${WINDOWMANAGER_SRC}) |
50 | + |
51 | +qt5_use_modules(windowmanager-qml Qml Quick Gui) |
52 | + |
53 | +add_unity8_plugin(WindowManager 0.1 WindowManager TARGETS windowmanager-qml) |
54 | |
55 | === added file 'plugins/WindowManager/TopLevelSurfaceList.cpp' |
56 | --- plugins/WindowManager/TopLevelSurfaceList.cpp 1970-01-01 00:00:00 +0000 |
57 | +++ plugins/WindowManager/TopLevelSurfaceList.cpp 2016-04-20 17:09:16 +0000 |
58 | @@ -0,0 +1,486 @@ |
59 | +/* |
60 | + * Copyright (C) 2016 Canonical, Ltd. |
61 | + * |
62 | + * This program is free software; you can redistribute it and/or modify |
63 | + * it under the terms of the GNU General Public License as published by |
64 | + * the Free Software Foundation; version 3. |
65 | + * |
66 | + * This program is distributed in the hope that it will be useful, |
67 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
68 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
69 | + * GNU General Public License for more details. |
70 | + * |
71 | + * You should have received a copy of the GNU General Public License |
72 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
73 | + */ |
74 | + |
75 | +#include "TopLevelSurfaceList.h" |
76 | + |
77 | +// unity-api |
78 | +#include <unity/shell/application/ApplicationInfoInterface.h> |
79 | +#include <unity/shell/application/MirSurfaceInterface.h> |
80 | +#include <unity/shell/application/MirSurfaceListInterface.h> |
81 | + |
82 | +#include <QMetaObject> |
83 | + |
84 | +Q_LOGGING_CATEGORY(UNITY_TOPSURFACELIST, "unity.topsurfacelist", QtDebugMsg) |
85 | + |
86 | +#define DEBUG_MSG qCDebug(UNITY_TOPSURFACELIST).nospace().noquote() << __func__ |
87 | + |
88 | +using namespace unity::shell::application; |
89 | + |
90 | +TopLevelSurfaceList::TopLevelSurfaceList(QObject *parent) : |
91 | + QAbstractListModel(parent) |
92 | +{ |
93 | + DEBUG_MSG << "()"; |
94 | +} |
95 | + |
96 | +TopLevelSurfaceList::~TopLevelSurfaceList() |
97 | +{ |
98 | + DEBUG_MSG << "()"; |
99 | +} |
100 | + |
101 | +int TopLevelSurfaceList::rowCount(const QModelIndex &parent) const |
102 | +{ |
103 | + return !parent.isValid() ? m_surfaceList.size() : 0; |
104 | +} |
105 | + |
106 | +QVariant TopLevelSurfaceList::data(const QModelIndex& index, int role) const |
107 | +{ |
108 | + if (index.row() < 0 || index.row() >= m_surfaceList.size()) |
109 | + return QVariant(); |
110 | + |
111 | + if (role == SurfaceRole) { |
112 | + MirSurfaceInterface *surface = m_surfaceList.at(index.row()).surface; |
113 | + return QVariant::fromValue(surface); |
114 | + } else if (role == ApplicationRole) { |
115 | + return QVariant::fromValue(m_surfaceList.at(index.row()).application); |
116 | + } else if (role == IdRole) { |
117 | + return QVariant::fromValue(m_surfaceList.at(index.row()).id); |
118 | + } else { |
119 | + return QVariant(); |
120 | + } |
121 | +} |
122 | + |
123 | +void TopLevelSurfaceList::raise(MirSurfaceInterface *surface) |
124 | +{ |
125 | + if (!surface) |
126 | + return; |
127 | + |
128 | + DEBUG_MSG << "(MirSurface[" << (void*)surface << "])"; |
129 | + |
130 | + int i = indexOf(surface); |
131 | + if (i != -1) { |
132 | + raiseId(m_surfaceList.at(i).id); |
133 | + } |
134 | +} |
135 | + |
136 | +void TopLevelSurfaceList::appendPlaceholder(ApplicationInfoInterface *application) |
137 | +{ |
138 | + DEBUG_MSG << "(" << application->appId() << ")"; |
139 | + |
140 | + appendSurfaceHelper(nullptr, application); |
141 | +} |
142 | + |
143 | +void TopLevelSurfaceList::appendSurface(MirSurfaceInterface *surface, ApplicationInfoInterface *application) |
144 | +{ |
145 | + Q_ASSERT(surface != nullptr); |
146 | + |
147 | + bool filledPlaceholder = false; |
148 | + for (int i = 0; i < m_surfaceList.count() && !filledPlaceholder; ++i) { |
149 | + ModelEntry &entry = m_surfaceList[i]; |
150 | + if (entry.application == application && entry.surface == nullptr) { |
151 | + entry.surface = surface; |
152 | + connectSurface(surface); |
153 | + DEBUG_MSG << " appId=" << application->appId() << " surface=" << surface |
154 | + << ", filling out placeholder. after: " << toString(); |
155 | + Q_EMIT dataChanged(index(i) /* topLeft */, index(i) /* bottomRight */, QVector<int>() << SurfaceRole); |
156 | + filledPlaceholder = true; |
157 | + } |
158 | + } |
159 | + |
160 | + if (!filledPlaceholder) { |
161 | + DEBUG_MSG << " appId=" << application->appId() << " surface=" << surface << ", adding new row"; |
162 | + appendSurfaceHelper(surface, application); |
163 | + } |
164 | +} |
165 | + |
166 | +void TopLevelSurfaceList::appendSurfaceHelper(MirSurfaceInterface *surface, ApplicationInfoInterface *application) |
167 | +{ |
168 | + if (m_modelState == IdleState) { |
169 | + m_modelState = InsertingState; |
170 | + beginInsertRows(QModelIndex(), m_surfaceList.size() /*first*/, m_surfaceList.size() /*last*/); |
171 | + } else { |
172 | + Q_ASSERT(m_modelState == ResettingState); |
173 | + // No point in signaling anything if we're resetting the whole model |
174 | + } |
175 | + |
176 | + int id = generateId(); |
177 | + m_surfaceList.append(ModelEntry(surface, application, id)); |
178 | + if (surface) { |
179 | + connectSurface(surface); |
180 | + } |
181 | + |
182 | + if (m_modelState == InsertingState) { |
183 | + endInsertRows(); |
184 | + Q_EMIT countChanged(); |
185 | + Q_EMIT listChanged(); |
186 | + m_modelState = IdleState; |
187 | + } |
188 | + |
189 | + DEBUG_MSG << " after " << toString(); |
190 | +} |
191 | + |
192 | +void TopLevelSurfaceList::connectSurface(MirSurfaceInterface *surface) |
193 | +{ |
194 | + connect(surface, &MirSurfaceInterface::focusedChanged, this, [this, surface](bool focused){ |
195 | + if (focused) { |
196 | + this->raise(surface); |
197 | + } |
198 | + }); |
199 | + connect(surface, &MirSurfaceInterface::liveChanged, this, [this, surface](bool live){ |
200 | + if (!live) { |
201 | + onSurfaceDied(surface); |
202 | + } |
203 | + }); |
204 | + connect(surface, &QObject::destroyed, this, [this, surface](){ this->onSurfaceDestroyed(surface); }); |
205 | +} |
206 | + |
207 | +void TopLevelSurfaceList::onSurfaceDied(MirSurfaceInterface *surface) |
208 | +{ |
209 | + int i = indexOf(surface); |
210 | + if (i == -1) { |
211 | + return; |
212 | + } |
213 | + |
214 | + auto application = m_surfaceList[i].application; |
215 | + |
216 | + // can't be starting if it already has a surface |
217 | + Q_ASSERT(application->state() != ApplicationInfoInterface::Starting); |
218 | + |
219 | + if (application->state() == ApplicationInfoInterface::Running) { |
220 | + m_surfaceList[i].removeOnceSurfaceDestroyed = true; |
221 | + } else { |
222 | + // assume it got killed by the out-of-memory daemon. |
223 | + // |
224 | + // So leave entry in the model and only remove its surface, so shell can display a screenshot |
225 | + // in its place. |
226 | + m_surfaceList[i].removeOnceSurfaceDestroyed = false; |
227 | + } |
228 | +} |
229 | + |
230 | +void TopLevelSurfaceList::onSurfaceDestroyed(MirSurfaceInterface *surface) |
231 | +{ |
232 | + int i = indexOf(surface); |
233 | + if (i == -1) { |
234 | + return; |
235 | + } |
236 | + |
237 | + if (m_surfaceList[i].removeOnceSurfaceDestroyed) { |
238 | + removeAt(i); |
239 | + } else { |
240 | + m_surfaceList[i].surface = nullptr; |
241 | + Q_EMIT dataChanged(index(i) /* topLeft */, index(i) /* bottomRight */, QVector<int>() << SurfaceRole); |
242 | + DEBUG_MSG << " Removed surface from entry. After: " << toString(); |
243 | + } |
244 | +} |
245 | + |
246 | +void TopLevelSurfaceList::removeAt(int index) |
247 | +{ |
248 | + if (m_modelState == IdleState) { |
249 | + beginRemoveRows(QModelIndex(), index, index); |
250 | + m_modelState = RemovingState; |
251 | + } else { |
252 | + Q_ASSERT(m_modelState == ResettingState); |
253 | + // No point in signaling anything if we're resetting the whole model |
254 | + } |
255 | + |
256 | + m_surfaceList.removeAt(index); |
257 | + |
258 | + if (m_modelState == RemovingState) { |
259 | + endRemoveRows(); |
260 | + Q_EMIT countChanged(); |
261 | + Q_EMIT listChanged(); |
262 | + m_modelState = IdleState; |
263 | + } |
264 | + |
265 | + DEBUG_MSG << " after " << toString(); |
266 | +} |
267 | + |
268 | +int TopLevelSurfaceList::indexOf(MirSurfaceInterface *surface) |
269 | +{ |
270 | + for (int i = 0; i < m_surfaceList.count(); ++i) { |
271 | + if (m_surfaceList.at(i).surface == surface) { |
272 | + return i; |
273 | + } |
274 | + } |
275 | + return -1; |
276 | +} |
277 | + |
278 | +void TopLevelSurfaceList::move(int from, int to) |
279 | +{ |
280 | + if (from == to) return; |
281 | + DEBUG_MSG << " from=" << from << " to=" << to; |
282 | + |
283 | + if (from >= 0 && from < m_surfaceList.size() && to >= 0 && to < m_surfaceList.size()) { |
284 | + QModelIndex parent; |
285 | + /* When moving an item down, the destination index needs to be incremented |
286 | + by one, as explained in the documentation: |
287 | + http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#beginMoveRows */ |
288 | + |
289 | + Q_ASSERT(m_modelState == IdleState); |
290 | + m_modelState = MovingState; |
291 | + |
292 | + beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0)); |
293 | + m_surfaceList.move(from, to); |
294 | + endMoveRows(); |
295 | + Q_EMIT listChanged(); |
296 | + |
297 | + m_modelState = IdleState; |
298 | + |
299 | + DEBUG_MSG << " after " << toString(); |
300 | + } |
301 | +} |
302 | + |
303 | +MirSurfaceInterface *TopLevelSurfaceList::surfaceAt(int index) const |
304 | +{ |
305 | + if (index >=0 && index < m_surfaceList.count()) { |
306 | + return m_surfaceList[index].surface; |
307 | + } else { |
308 | + return nullptr; |
309 | + } |
310 | +} |
311 | + |
312 | +ApplicationInfoInterface *TopLevelSurfaceList::applicationAt(int index) const |
313 | +{ |
314 | + if (index >=0 && index < m_surfaceList.count()) { |
315 | + return m_surfaceList[index].application; |
316 | + } else { |
317 | + return nullptr; |
318 | + } |
319 | +} |
320 | + |
321 | +int TopLevelSurfaceList::idAt(int index) const |
322 | +{ |
323 | + if (index >=0 && index < m_surfaceList.count()) { |
324 | + return m_surfaceList[index].id; |
325 | + } else { |
326 | + return 0; |
327 | + } |
328 | +} |
329 | + |
330 | +int TopLevelSurfaceList::indexForId(int id) const |
331 | +{ |
332 | + for (int i = 0; i < m_surfaceList.count(); ++i) { |
333 | + if (m_surfaceList[i].id == id) { |
334 | + return i; |
335 | + } |
336 | + } |
337 | + return -1; |
338 | +} |
339 | + |
340 | +void TopLevelSurfaceList::doRaiseId(int id) |
341 | +{ |
342 | + int fromIndex = indexForId(id); |
343 | + if (fromIndex != -1) { |
344 | + move(fromIndex, 0 /* toIndex */); |
345 | + } |
346 | +} |
347 | + |
348 | +void TopLevelSurfaceList::raiseId(int id) |
349 | +{ |
350 | + if (m_modelState == IdleState) { |
351 | + DEBUG_MSG << "(id=" << id << ") - do it now."; |
352 | + doRaiseId(id); |
353 | + } else { |
354 | + DEBUG_MSG << "(id=" << id << ") - Model busy (modelState=" << m_modelState << "). Try again in the next event loop."; |
355 | + // The model has just signalled some change. If we have a Repeater responding to this update, it will get nuts |
356 | + // if we perform yet another model change straight away. |
357 | + // |
358 | + // A bad sympton of this problem is a Repeater.itemAt(index) call returning null event though Repeater.count says |
359 | + // the index is definitely within bounds. |
360 | + QMetaObject::invokeMethod(this, "raiseId", Qt::QueuedConnection, Q_ARG(int, id)); |
361 | + } |
362 | +} |
363 | + |
364 | +int TopLevelSurfaceList::generateId() |
365 | +{ |
366 | + int id = m_nextId; |
367 | + m_nextId = nextFreeId(m_nextId + 1); |
368 | + Q_EMIT nextIdChanged(); |
369 | + return id; |
370 | +} |
371 | + |
372 | +int TopLevelSurfaceList::nextFreeId(int candidateId) |
373 | +{ |
374 | + if (candidateId > m_maxId) { |
375 | + return nextFreeId(1); |
376 | + } else { |
377 | + if (indexForId(candidateId) == -1) { |
378 | + // it's indeed free |
379 | + return candidateId; |
380 | + } else { |
381 | + return nextFreeId(candidateId + 1); |
382 | + } |
383 | + } |
384 | +} |
385 | + |
386 | +QString TopLevelSurfaceList::toString() |
387 | +{ |
388 | + QString str; |
389 | + for (int i = 0; i < m_surfaceList.count(); ++i) { |
390 | + auto item = m_surfaceList.at(i); |
391 | + |
392 | + QString itemStr = QString("(index=%1,appId=%2,surface=0x%3,id=%4)") |
393 | + .arg(i) |
394 | + .arg(item.application->appId()) |
395 | + .arg((qintptr)item.surface, 0, 16) |
396 | + .arg(item.id); |
397 | + |
398 | + if (i > 0) { |
399 | + str.append(","); |
400 | + } |
401 | + str.append(itemStr); |
402 | + } |
403 | + return str; |
404 | +} |
405 | + |
406 | +void TopLevelSurfaceList::addApplication(ApplicationInfoInterface *application) |
407 | +{ |
408 | + DEBUG_MSG << "(" << application->appId() << ")"; |
409 | + Q_ASSERT(!m_applications.contains(application)); |
410 | + m_applications.append(application); |
411 | + |
412 | + MirSurfaceListInterface *surfaceList = application->surfaceList(); |
413 | + |
414 | + if (application->state() != ApplicationInfoInterface::Stopped) { |
415 | + if (surfaceList->count() == 0) { |
416 | + appendPlaceholder(application); |
417 | + } else { |
418 | + for (int i = 0; i < surfaceList->count(); ++i) { |
419 | + appendSurface(surfaceList->get(i), application); |
420 | + } |
421 | + } |
422 | + } |
423 | + |
424 | + connect(surfaceList, &QAbstractItemModel::rowsInserted, this, |
425 | + [this, application, surfaceList](const QModelIndex & /*parent*/, int first, int last) |
426 | + { |
427 | + for (int i = last; i >= first; --i) { |
428 | + this->appendSurface(surfaceList->get(i), application); |
429 | + } |
430 | + }); |
431 | +} |
432 | + |
433 | +void TopLevelSurfaceList::removeApplication(ApplicationInfoInterface *application) |
434 | +{ |
435 | + DEBUG_MSG << "(" << application->appId() << ")"; |
436 | + Q_ASSERT(m_applications.contains(application)); |
437 | + |
438 | + MirSurfaceListInterface *surfaceList = application->surfaceList(); |
439 | + |
440 | + disconnect(surfaceList, 0, this, 0); |
441 | + |
442 | + Q_ASSERT(m_modelState == IdleState); |
443 | + m_modelState = RemovingState; |
444 | + |
445 | + int i = 0; |
446 | + while (i < m_surfaceList.count()) { |
447 | + if (m_surfaceList.at(i).application == application) { |
448 | + beginRemoveRows(QModelIndex(), i, i); |
449 | + m_surfaceList.removeAt(i); |
450 | + endRemoveRows(); |
451 | + Q_EMIT countChanged(); |
452 | + Q_EMIT listChanged(); |
453 | + } else { |
454 | + ++i; |
455 | + } |
456 | + } |
457 | + |
458 | + m_modelState = IdleState; |
459 | + |
460 | + DEBUG_MSG << " after " << toString(); |
461 | + |
462 | + m_applications.removeAll(application); |
463 | +} |
464 | + |
465 | +QAbstractListModel *TopLevelSurfaceList::applicationsModel() const |
466 | +{ |
467 | + return m_applicationsModel; |
468 | +} |
469 | + |
470 | +void TopLevelSurfaceList::setApplicationsModel(QAbstractListModel* value) |
471 | +{ |
472 | + if (m_applicationsModel == value) { |
473 | + return; |
474 | + } |
475 | + |
476 | + DEBUG_MSG << "(" << value << ")"; |
477 | + |
478 | + Q_ASSERT(m_modelState == IdleState); |
479 | + m_modelState = ResettingState; |
480 | + |
481 | + beginResetModel(); |
482 | + |
483 | + if (m_applicationsModel) { |
484 | + m_surfaceList.clear(); |
485 | + m_applications.clear(); |
486 | + disconnect(m_applicationsModel, 0, this, 0); |
487 | + } |
488 | + |
489 | + m_applicationsModel = value; |
490 | + |
491 | + if (m_applicationsModel) { |
492 | + findApplicationRole(); |
493 | + |
494 | + connect(m_applicationsModel, &QAbstractItemModel::rowsInserted, |
495 | + this, [this](const QModelIndex &/*parent*/, int first, int last) { |
496 | + for (int i = first; i <= last; ++i) { |
497 | + auto application = getApplicationFromModelAt(i); |
498 | + addApplication(application); |
499 | + } |
500 | + }); |
501 | + |
502 | + connect(m_applicationsModel, &QAbstractItemModel::rowsAboutToBeRemoved, |
503 | + this, [this](const QModelIndex &/*parent*/, int first, int last) { |
504 | + for (int i = first; i <= last; ++i) { |
505 | + auto application = getApplicationFromModelAt(i); |
506 | + removeApplication(application); |
507 | + } |
508 | + }); |
509 | + |
510 | + for (int i = 0; i < m_applicationsModel->rowCount(); ++i) { |
511 | + auto application = getApplicationFromModelAt(i); |
512 | + addApplication(application); |
513 | + } |
514 | + } |
515 | + |
516 | + endResetModel(); |
517 | + m_modelState = IdleState; |
518 | +} |
519 | + |
520 | +ApplicationInfoInterface *TopLevelSurfaceList::getApplicationFromModelAt(int index) |
521 | +{ |
522 | + QModelIndex modelIndex = m_applicationsModel->index(index); |
523 | + |
524 | + QVariant variant = m_applicationsModel->data(modelIndex, m_applicationRole); |
525 | + |
526 | + // variant.value<ApplicationInfoInterface*>() returns null for some reason. |
527 | + return static_cast<ApplicationInfoInterface*>(variant.value<QObject*>()); |
528 | +} |
529 | + |
530 | +void TopLevelSurfaceList::findApplicationRole() |
531 | +{ |
532 | + QHash<int, QByteArray> namesHash = m_applicationsModel->roleNames(); |
533 | + |
534 | + m_applicationRole = -1; |
535 | + for (auto i = namesHash.begin(); i != namesHash.end() && m_applicationRole == -1; ++i) { |
536 | + if (i.value() == "application") { |
537 | + m_applicationRole = i.key(); |
538 | + } |
539 | + } |
540 | + |
541 | + if (m_applicationRole == -1) { |
542 | + qFatal("TopLevelSurfaceList: applicationsModel must have a \"application\" role."); |
543 | + } |
544 | +} |
545 | |
546 | === added file 'plugins/WindowManager/TopLevelSurfaceList.h' |
547 | --- plugins/WindowManager/TopLevelSurfaceList.h 1970-01-01 00:00:00 +0000 |
548 | +++ plugins/WindowManager/TopLevelSurfaceList.h 2016-04-20 17:09:16 +0000 |
549 | @@ -0,0 +1,223 @@ |
550 | +/* |
551 | + * Copyright (C) 2016 Canonical, Ltd. |
552 | + * |
553 | + * This program is free software; you can redistribute it and/or modify |
554 | + * it under the terms of the GNU General Public License as published by |
555 | + * the Free Software Foundation; version 3. |
556 | + * |
557 | + * This program is distributed in the hope that it will be useful, |
558 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
559 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
560 | + * GNU General Public License for more details. |
561 | + * |
562 | + * You should have received a copy of the GNU General Public License |
563 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
564 | + */ |
565 | + |
566 | +#ifndef TOPLEVELSURFACELIST_H |
567 | +#define TOPLEVELSURFACELIST_H |
568 | + |
569 | +#include <QAbstractListModel> |
570 | +#include <QList> |
571 | +#include <QLoggingCategory> |
572 | + |
573 | +Q_DECLARE_LOGGING_CATEGORY(UNITY_TOPSURFACELIST) |
574 | + |
575 | +namespace unity { |
576 | + namespace shell { |
577 | + namespace application { |
578 | + class ApplicationInfoInterface; |
579 | + class MirSurfaceInterface; |
580 | + } |
581 | + } |
582 | +} |
583 | + |
584 | +/** |
585 | + * @brief A model of top-level surfaces |
586 | + * |
587 | + * It's an abstraction of top-level application windows. |
588 | + * |
589 | + * When an entry first appears, it normaly doesn't have a surface yet, meaning that the application is |
590 | + * still starting up. A shell should then display a splash screen or saved screenshot of the application |
591 | + * until its surface comes up. |
592 | + * |
593 | + * As applications can have multiple surfaces and you can also have entries without surfaces at all, |
594 | + * the only way to unambiguously refer to an entry in this model is through its id. |
595 | + */ |
596 | +class TopLevelSurfaceList : public QAbstractListModel |
597 | +{ |
598 | + |
599 | + Q_OBJECT |
600 | + |
601 | + /** |
602 | + * @brief A list model of applications. |
603 | + * |
604 | + * It's expected to have a role called "application" which returns a ApplicationInfoInterface |
605 | + */ |
606 | + Q_PROPERTY(QAbstractListModel* applicationsModel READ applicationsModel |
607 | + WRITE setApplicationsModel |
608 | + NOTIFY applicationsModelChanged) |
609 | + |
610 | + /** |
611 | + * @brief Number of top-level surfaces in this model |
612 | + * |
613 | + * This is the same as rowCount, added in order to keep compatibility with QML ListModels. |
614 | + */ |
615 | + Q_PROPERTY(int count READ rowCount NOTIFY countChanged) |
616 | + |
617 | + /** |
618 | + The id to be used on the next entry created |
619 | + Useful for tests |
620 | + */ |
621 | + Q_PROPERTY(int nextId READ nextId NOTIFY nextIdChanged) |
622 | +public: |
623 | + |
624 | + /** |
625 | + * @brief The Roles supported by the model |
626 | + * |
627 | + * SurfaceRole - A MirSurfaceInterface. It will be null if the application is still starting up |
628 | + * ApplicationRole - An ApplicationInfoInterface |
629 | + * IdRole - A unique identifier for this entry. Useful to unambiguosly track elements as they move around in the list |
630 | + */ |
631 | + enum Roles { |
632 | + SurfaceRole = Qt::UserRole, |
633 | + ApplicationRole = Qt::UserRole + 1, |
634 | + IdRole = Qt::UserRole + 2, |
635 | + }; |
636 | + |
637 | + explicit TopLevelSurfaceList(QObject *parent = nullptr); |
638 | + virtual ~TopLevelSurfaceList(); |
639 | + |
640 | + // QAbstractItemModel methods |
641 | + int rowCount(const QModelIndex &parent = QModelIndex()) const override; |
642 | + QVariant data(const QModelIndex& index, int role) const override; |
643 | + QHash<int, QByteArray> roleNames() const override { |
644 | + QHash<int, QByteArray> roleNames { {SurfaceRole, "surface"}, |
645 | + {ApplicationRole, "application"}, |
646 | + {IdRole, "id"} }; |
647 | + return roleNames; |
648 | + } |
649 | + |
650 | + int nextId() const { return m_nextId; } |
651 | + |
652 | + QAbstractListModel *applicationsModel() const; |
653 | + void setApplicationsModel(QAbstractListModel*); |
654 | + |
655 | +public Q_SLOTS: |
656 | + /** |
657 | + * @brief Returns the surface at the given index |
658 | + * |
659 | + * It will be a nullptr if the application is still starting up and thus hasn't yet created |
660 | + * and drawn into a surface. |
661 | + */ |
662 | + unity::shell::application::MirSurfaceInterface *surfaceAt(int index) const; |
663 | + |
664 | + /** |
665 | + * @brief Returns the application at the given index |
666 | + */ |
667 | + unity::shell::application::ApplicationInfoInterface *applicationAt(int index) const; |
668 | + |
669 | + /** |
670 | + * @brief Returns the unique id of the element at the given index |
671 | + */ |
672 | + int idAt(int index) const; |
673 | + |
674 | + /** |
675 | + * @brief Returns the index where the row with the given id is located |
676 | + * |
677 | + * Returns -1 if there's no row with the given id. |
678 | + */ |
679 | + int indexForId(int id) const; |
680 | + |
681 | + /** |
682 | + * @brief Raises the row with the given id to index 0 |
683 | + */ |
684 | + void raiseId(int id); |
685 | + |
686 | + void doRaiseId(int id); |
687 | + |
688 | +Q_SIGNALS: |
689 | + void countChanged(); |
690 | + |
691 | + /** |
692 | + * @brief Emitted when the list changes |
693 | + * |
694 | + * Emitted when model gains an element, loses an element or when elements exchange positions. |
695 | + */ |
696 | + void listChanged(); |
697 | + |
698 | + void nextIdChanged(); |
699 | + |
700 | + void applicationsModelChanged(); |
701 | + |
702 | +private: |
703 | + void addApplication(unity::shell::application::ApplicationInfoInterface *application); |
704 | + void removeApplication(unity::shell::application::ApplicationInfoInterface *application); |
705 | + |
706 | + int indexOf(unity::shell::application::MirSurfaceInterface *surface); |
707 | + void raise(unity::shell::application::MirSurfaceInterface *surface); |
708 | + void move(int from, int to); |
709 | + void appendSurfaceHelper(unity::shell::application::MirSurfaceInterface *surface, |
710 | + unity::shell::application::ApplicationInfoInterface *application); |
711 | + void connectSurface(unity::shell::application::MirSurfaceInterface *surface); |
712 | + int generateId(); |
713 | + int nextFreeId(int candidateId); |
714 | + QString toString(); |
715 | + void onSurfaceDestroyed(unity::shell::application::MirSurfaceInterface *surface); |
716 | + void onSurfaceDied(unity::shell::application::MirSurfaceInterface *surface); |
717 | + void removeAt(int index); |
718 | + void findApplicationRole(); |
719 | + |
720 | + unity::shell::application::ApplicationInfoInterface *getApplicationFromModelAt(int index); |
721 | + |
722 | + /* |
723 | + Placeholder for a future surface from a starting or running application. |
724 | + Enables shell to give immediate feedback to the user by showing, eg, |
725 | + a splash screen. |
726 | + |
727 | + It's a model row containing a null surface and the given application. |
728 | + */ |
729 | + void appendPlaceholder(unity::shell::application::ApplicationInfoInterface *application); |
730 | + |
731 | + /* |
732 | + Adds a model row with the given surface and application |
733 | + |
734 | + Alternatively, if a placeholder exists for the given application it's |
735 | + filled with the given surface instead. |
736 | + */ |
737 | + void appendSurface(unity::shell::application::MirSurfaceInterface *surface, |
738 | + unity::shell::application::ApplicationInfoInterface *application); |
739 | + |
740 | + struct ModelEntry { |
741 | + ModelEntry(unity::shell::application::MirSurfaceInterface *surface, unity::shell::application::ApplicationInfoInterface *application, int id) |
742 | + : surface(surface), application(application), id(id) {} |
743 | + unity::shell::application::MirSurfaceInterface *surface; |
744 | + unity::shell::application::ApplicationInfoInterface *application; |
745 | + int id; |
746 | + bool removeOnceSurfaceDestroyed{false}; |
747 | + }; |
748 | + |
749 | + QList<ModelEntry> m_surfaceList; |
750 | + int m_nextId{1}; |
751 | + static const int m_maxId{1000000}; |
752 | + |
753 | + // applications that are being monitored |
754 | + QList<unity::shell::application::ApplicationInfoInterface *> m_applications; |
755 | + |
756 | + QAbstractListModel* m_applicationsModel{nullptr}; |
757 | + int m_applicationRole{-1}; |
758 | + |
759 | + enum ModelState { |
760 | + IdleState, |
761 | + InsertingState, |
762 | + RemovingState, |
763 | + MovingState, |
764 | + ResettingState |
765 | + }; |
766 | + ModelState m_modelState{IdleState}; |
767 | +}; |
768 | + |
769 | +Q_DECLARE_METATYPE(TopLevelSurfaceList*) |
770 | +Q_DECLARE_METATYPE(QAbstractListModel*) |
771 | + |
772 | +#endif // TOPLEVELSURFACELIST_H |
773 | |
774 | === added file 'plugins/WindowManager/WindowManagerPlugin.cpp' |
775 | --- plugins/WindowManager/WindowManagerPlugin.cpp 1970-01-01 00:00:00 +0000 |
776 | +++ plugins/WindowManager/WindowManagerPlugin.cpp 2016-04-20 17:09:16 +0000 |
777 | @@ -0,0 +1,28 @@ |
778 | +/* |
779 | + * Copyright (C) 2016 Canonical, Ltd. |
780 | + * |
781 | + * This program is free software; you can redistribute it and/or modify |
782 | + * it under the terms of the GNU General Public License as published by |
783 | + * the Free Software Foundation; version 3. |
784 | + * |
785 | + * This program is distributed in the hope that it will be useful, |
786 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
787 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
788 | + * GNU General Public License for more details. |
789 | + * |
790 | + * You should have received a copy of the GNU General Public License |
791 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
792 | + */ |
793 | + |
794 | +#include "WindowManagerPlugin.h" |
795 | + |
796 | +#include "TopLevelSurfaceList.h" |
797 | + |
798 | +#include <QtQml> |
799 | + |
800 | +void WindowManagerPlugin::registerTypes(const char *uri) |
801 | +{ |
802 | + qmlRegisterType<TopLevelSurfaceList>(uri, 0, 1, "TopLevelSurfaceList"); |
803 | + |
804 | + qRegisterMetaType<QAbstractListModel*>("QAbstractListModel*"); |
805 | +} |
806 | |
807 | === added file 'plugins/WindowManager/WindowManagerPlugin.h' |
808 | --- plugins/WindowManager/WindowManagerPlugin.h 1970-01-01 00:00:00 +0000 |
809 | +++ plugins/WindowManager/WindowManagerPlugin.h 2016-04-20 17:09:16 +0000 |
810 | @@ -0,0 +1,32 @@ |
811 | +/* |
812 | + * Copyright (C) 2016 Canonical, Ltd. |
813 | + * |
814 | + * This program is free software; you can redistribute it and/or modify |
815 | + * it under the terms of the GNU General Public License as published by |
816 | + * the Free Software Foundation; version 3. |
817 | + * |
818 | + * This program is distributed in the hope that it will be useful, |
819 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
820 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
821 | + * GNU General Public License for more details. |
822 | + * |
823 | + * You should have received a copy of the GNU General Public License |
824 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
825 | + */ |
826 | + |
827 | +#ifndef WINDOWMANAGER_PLUGIN_H |
828 | +#define WINDOWMANAGER_PLUGIN_H |
829 | + |
830 | +#include <QtQml/QQmlEngine> |
831 | +#include <QtQml/QQmlExtensionPlugin> |
832 | + |
833 | +class WindowManagerPlugin : public QQmlExtensionPlugin |
834 | +{ |
835 | + Q_OBJECT |
836 | + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") |
837 | + |
838 | +public: |
839 | + void registerTypes(const char *uri) override; |
840 | +}; |
841 | + |
842 | +#endif // WINDOWMANAGER_PLUGIN_H |
843 | |
844 | === added file 'plugins/WindowManager/qmldir' |
845 | --- plugins/WindowManager/qmldir 1970-01-01 00:00:00 +0000 |
846 | +++ plugins/WindowManager/qmldir 2016-04-20 17:09:16 +0000 |
847 | @@ -0,0 +1,2 @@ |
848 | +module WindowManager |
849 | +plugin windowmanager-qml |
850 | |
851 | === modified file 'qml/Components/KeymapSwitcher.qml' |
852 | --- qml/Components/KeymapSwitcher.qml 2016-04-20 17:09:15 +0000 |
853 | +++ qml/Components/KeymapSwitcher.qml 2016-04-20 17:09:16 +0000 |
854 | @@ -57,16 +57,8 @@ |
855 | currentKeymapIndex = prevIndex; |
856 | } |
857 | |
858 | - // Code below will get much simpler with surface-based window management (the upcoming MirFocusController) |
859 | - property var application: ApplicationManager.focusedApplicationId |
860 | - ? ApplicationManager.findApplication(ApplicationManager.focusedApplicationId) |
861 | - : null |
862 | - property var session: application && application.session ? application.session : null |
863 | - property var surface: session ? session.lastSurface : null |
864 | - |
865 | property Binding surfaceKeymapBinding: Binding { |
866 | - target: root.surface |
867 | - when: root.surface != null && root.surface.live |
868 | + target: MirFocusController.focusedSurface |
869 | property: "keymap" |
870 | value: root.currentKeymap |
871 | } |
872 | |
873 | === modified file 'qml/Greeter/Greeter.qml' |
874 | --- qml/Greeter/Greeter.qml 2016-03-30 07:46:17 +0000 |
875 | +++ qml/Greeter/Greeter.qml 2016-04-20 17:09:16 +0000 |
876 | @@ -64,7 +64,7 @@ |
877 | d.selectUser(d.currentIndex, true); |
878 | } |
879 | |
880 | - function notifyAppFocused(appId) { |
881 | + function notifyAppFocusRequested(appId) { |
882 | if (!active) { |
883 | return; |
884 | } |
885 | @@ -81,19 +81,20 @@ |
886 | } |
887 | } |
888 | |
889 | - function notifyAboutToFocusApp(appId) { |
890 | + // Notify that the user has explicitly requested the given app through unity8 GUI. |
891 | + function notifyUserRequestedApp(appId) { |
892 | if (!active) { |
893 | return; |
894 | } |
895 | |
896 | // A hint that we're about to focus an app. This way we can look |
897 | // a little more responsive, rather than waiting for the above |
898 | - // notifyAppFocused call. We also need this in case we have a locked |
899 | + // notifyAppFocusRequested call. We also need this in case we have a locked |
900 | // app, in order to show lockscreen instead of new app. |
901 | d.startUnlock(false /* toTheRight */); |
902 | } |
903 | |
904 | - // This is a just a glorified notifyAboutToFocusApp(), but it does one |
905 | + // This is a just a glorified notifyUserRequestedApp(), but it does one |
906 | // other thing: it hides any cover pages to the RIGHT, because the user |
907 | // just came from a launcher drag starting on the left. |
908 | // It also returns a boolean value, indicating whether there was a visual |
909 | |
910 | === modified file 'qml/Launcher/LauncherPanel.qml' |
911 | --- qml/Launcher/LauncherPanel.qml 2016-03-16 11:20:24 +0000 |
912 | +++ qml/Launcher/LauncherPanel.qml 2016-04-20 17:09:16 +0000 |
913 | @@ -98,6 +98,7 @@ |
914 | AbstractButton { |
915 | id: dashItem |
916 | anchors.fill: parent |
917 | + activeFocusOnPress: false |
918 | onClicked: root.showDashHome() |
919 | } |
920 | Rectangle { |
921 | |
922 | === modified file 'qml/Shell.qml' |
923 | --- qml/Shell.qml 2016-04-20 17:09:15 +0000 |
924 | +++ qml/Shell.qml 2016-04-20 17:09:16 +0000 |
925 | @@ -42,6 +42,7 @@ |
926 | import Unity.DashCommunicator 0.1 |
927 | import Unity.Indicators 0.1 as Indicators |
928 | import Cursor 1.0 |
929 | +import WindowManager 0.1 |
930 | |
931 | |
932 | Item { |
933 | @@ -96,13 +97,45 @@ |
934 | } |
935 | } |
936 | |
937 | + readonly property var mainApp: |
938 | + applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainApp : null |
939 | + onMainAppChanged: { |
940 | + if (mainApp) { |
941 | + _onMainAppChanged(mainApp.appId); |
942 | + } |
943 | + } |
944 | + Connections { |
945 | + target: ApplicationManager |
946 | + onFocusRequested: { |
947 | + if (shell.mainApp && shell.mainApp.appId === appId) { |
948 | + _onMainAppChanged(appId); |
949 | + } |
950 | + } |
951 | + } |
952 | + function _onMainAppChanged(appId) { |
953 | + if (wizard.active && appId != "" && appId != "unity8-dash") { |
954 | + // If this happens on first boot, we may be in edge |
955 | + // tutorial or wizard while receiving a call. But a call |
956 | + // is more important than wizard so just bail out of those. |
957 | + tutorial.finish(); |
958 | + wizard.hide(); |
959 | + } |
960 | + |
961 | + if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) { |
962 | + // If we are in the middle of a call, make dialer lockedApp and show it. |
963 | + // This can happen if user backs out of dialer back to greeter, then |
964 | + // launches dialer again. |
965 | + greeter.lockedApp = appId; |
966 | + } |
967 | + greeter.notifyAppFocusRequested(appId); |
968 | + |
969 | + panel.indicators.hide(); |
970 | + launcher.hide(); |
971 | + } |
972 | + |
973 | // For autopilot consumption |
974 | readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId |
975 | |
976 | - // internal props from here onwards |
977 | - readonly property var mainApp: |
978 | - applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainApp : null |
979 | - |
980 | // Disable everything while greeter is waiting, so that the user can't swipe |
981 | // the greeter or launcher until we know whether the session is locked. |
982 | enabled: greeter && !greeter.waiting |
983 | @@ -139,9 +172,6 @@ |
984 | |
985 | Component.onCompleted: { |
986 | theme.name = "Ubuntu.Components.Themes.SuruDark" |
987 | - if (ApplicationManager.count > 0) { |
988 | - ApplicationManager.focusApplication(ApplicationManager.get(0).appId); |
989 | - } |
990 | finishStartUpTimer.start(); |
991 | } |
992 | |
993 | @@ -198,36 +228,10 @@ |
994 | width: parent.width |
995 | height: parent.height |
996 | |
997 | - Connections { |
998 | - target: ApplicationManager |
999 | - |
1000 | - // This signal is also fired when we try to focus the current app |
1001 | - // again. We rely on this! |
1002 | - onFocusedApplicationIdChanged: { |
1003 | - var appId = ApplicationManager.focusedApplicationId; |
1004 | - |
1005 | - if (wizard.active && appId != "" && appId != "unity8-dash") { |
1006 | - // If this happens on first boot, we may be in edge |
1007 | - // tutorial or wizard while receiving a call. But a call |
1008 | - // is more important than wizard so just bail out of those. |
1009 | - tutorial.finish(); |
1010 | - wizard.hide(); |
1011 | - } |
1012 | - |
1013 | - if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) { |
1014 | - // If we are in the middle of a call, make dialer lockedApp and show it. |
1015 | - // This can happen if user backs out of dialer back to greeter, then |
1016 | - // launches dialer again. |
1017 | - greeter.lockedApp = appId; |
1018 | - } |
1019 | - greeter.notifyAppFocused(appId); |
1020 | - |
1021 | - panel.indicators.hide(); |
1022 | - } |
1023 | - |
1024 | - onApplicationAdded: { |
1025 | - launcher.hide(); |
1026 | - } |
1027 | + TopLevelSurfaceList { |
1028 | + id: topLevelSurfaceList |
1029 | + objectName: "topLevelSurfaceList" |
1030 | + applicationsModel: ApplicationManager |
1031 | } |
1032 | |
1033 | Loader { |
1034 | @@ -258,6 +262,10 @@ |
1035 | return "Stages/DesktopStage.qml"; |
1036 | } |
1037 | } |
1038 | + // TODO: Ensure the current stage is destroyed before the new one gets loaded. |
1039 | + // Currently the new one will get loaded while the old is still hanging |
1040 | + // around for a bit, which might lead to conflicts where both stages |
1041 | + // change the model simultaneously. |
1042 | onQmlComponentChanged: { |
1043 | if (item) item.stageAboutToBeUnloaded(); |
1044 | source = qmlComponent; |
1045 | @@ -272,6 +280,11 @@ |
1046 | |
1047 | Binding { |
1048 | target: applicationsDisplayLoader.item |
1049 | + property: "focus" |
1050 | + value: true |
1051 | + } |
1052 | + Binding { |
1053 | + target: applicationsDisplayLoader.item |
1054 | property: "objectName" |
1055 | value: "stage" |
1056 | } |
1057 | @@ -356,6 +369,16 @@ |
1058 | property: "leftMargin" |
1059 | value: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0 |
1060 | } |
1061 | + Binding { |
1062 | + target: applicationsDisplayLoader.item |
1063 | + property: "applicationManager" |
1064 | + value: ApplicationManager |
1065 | + } |
1066 | + Binding { |
1067 | + target: applicationsDisplayLoader.item |
1068 | + property: "topLevelSurfaceList" |
1069 | + value: topLevelSurfaceList |
1070 | + } |
1071 | } |
1072 | } |
1073 | |
1074 | @@ -370,16 +393,6 @@ |
1075 | z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running ? overlay.z + 1 : overlay.z - 1 |
1076 | } |
1077 | |
1078 | - Connections { |
1079 | - target: SessionManager |
1080 | - onSessionStopping: { |
1081 | - if (!session.parentSession && !session.application) { |
1082 | - // nothing is using it. delete it right away |
1083 | - session.release(); |
1084 | - } |
1085 | - } |
1086 | - } |
1087 | - |
1088 | Loader { |
1089 | id: greeterLoader |
1090 | anchors.fill: parent |
1091 | @@ -471,7 +484,7 @@ |
1092 | } |
1093 | |
1094 | function showHome() { |
1095 | - greeter.notifyAboutToFocusApp("unity8-dash"); |
1096 | + greeter.notifyUserRequestedApp("unity8-dash"); |
1097 | |
1098 | var animate = !lightDM.greeter.active && !stages.shown |
1099 | dash.setCurrentScope(0, animate, false) |
1100 | @@ -520,9 +533,10 @@ |
1101 | greeterShown: greeter.shown |
1102 | } |
1103 | |
1104 | - readonly property bool topmostApplicationIsFullscreen: mainApp && mainApp.fullscreen |
1105 | - |
1106 | - fullscreenMode: (topmostApplicationIsFullscreen && !lightDM.greeter.active && launcher.progress == 0) |
1107 | + readonly property bool focusedSurfaceIsFullscreen: MirFocusController.focusedSurface |
1108 | + ? MirFocusController.focusedSurface.state === Mir.FullscreenState |
1109 | + : false |
1110 | + fullscreenMode: (focusedSurfaceIsFullscreen && !lightDM.greeter.active && launcher.progress == 0) |
1111 | || greeter.hasLockedApp |
1112 | locked: greeter && greeter.active |
1113 | } |
1114 | @@ -555,7 +569,7 @@ |
1115 | } |
1116 | } |
1117 | onLauncherApplicationSelected: { |
1118 | - greeter.notifyAboutToFocusApp(appId); |
1119 | + greeter.notifyUserRequestedApp(appId); |
1120 | shell.activateApplication(appId); |
1121 | } |
1122 | onShownChanged: { |
1123 | @@ -719,7 +733,7 @@ |
1124 | onMouseMoved: { cursor.opacity = 1; } |
1125 | } |
1126 | |
1127 | - // non-visual item |
1128 | + // non-visual object |
1129 | KeymapSwitcher {} |
1130 | |
1131 | Rectangle { |
1132 | |
1133 | === modified file 'qml/Stages/AbstractStage.qml' |
1134 | --- qml/Stages/AbstractStage.qml 2016-04-20 17:09:15 +0000 |
1135 | +++ qml/Stages/AbstractStage.qml 2016-04-20 17:09:16 +0000 |
1136 | @@ -18,12 +18,19 @@ |
1137 | import Ubuntu.Components 1.3 |
1138 | import GSettings 1.0 |
1139 | |
1140 | -Rectangle { |
1141 | +FocusScope { |
1142 | id: root |
1143 | |
1144 | - color: "#060606" |
1145 | + property alias color: backRect.color |
1146 | + Rectangle { |
1147 | + id: backRect |
1148 | + color: "#060606" |
1149 | + anchors.fill: parent |
1150 | + } |
1151 | |
1152 | // Controls to be set from outside |
1153 | + property QtObject applicationManager |
1154 | + property QtObject topLevelSurfaceList |
1155 | property bool altTabPressed |
1156 | property url background |
1157 | property bool beingResized |
1158 | |
1159 | === removed directory 'qml/Stages/Animations' |
1160 | === removed file 'qml/Stages/Animations/BaseSessionAnimation.qml' |
1161 | --- qml/Stages/Animations/BaseSessionAnimation.qml 2015-07-15 15:07:19 +0000 |
1162 | +++ qml/Stages/Animations/BaseSessionAnimation.qml 1970-01-01 00:00:00 +0000 |
1163 | @@ -1,96 +0,0 @@ |
1164 | -/* |
1165 | - * Copyright (C) 2014 Canonical, Ltd. |
1166 | - * |
1167 | - * This program is free software; you can redistribute it and/or modify |
1168 | - * it under the terms of the GNU General Public License as published by |
1169 | - * the Free Software Foundation; version 3. |
1170 | - * |
1171 | - * This program is distributed in the hope that it will be useful, |
1172 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1173 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1174 | - * GNU General Public License for more details. |
1175 | - * |
1176 | - * You should have received a copy of the GNU General Public License |
1177 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1178 | - */ |
1179 | - |
1180 | -import QtQuick 2.4 |
1181 | - |
1182 | -/* This is the base case for surface animations, used when adding/removing * child surfaces. |
1183 | - * The class is meant to be overridden and changes/animations provided for state changes. |
1184 | - * NB. It is important to release the surface at the end of the "out" animation. |
1185 | - * |
1186 | - * Example - Simple fade in/out |
1187 | - * |
1188 | - * BaseSurfaceAnimation { |
1189 | - * outChanges: [ PropertyChanges { target: animation.surface; opacity: 0.0 } ] |
1190 | - * outAnimations: [ |
1191 | - SequentialAnimation { |
1192 | - * NumberAnimation { target: animation.surface; property: "opacity"; duration: 300 } |
1193 | - * ScriptAction { script: { if (animation.parent.removing) animation.surface.release(); } } |
1194 | - * } |
1195 | - * ] |
1196 | - * |
1197 | - * inChanges: [ PropertyChanges { target: animation.surface; opacity: 1.0 } ] |
1198 | - * inAnimations: [ NumberAnimation { target: animation.surface; property: "opacity"; duration: 300 } ] |
1199 | - * } |
1200 | - */ |
1201 | -Item { |
1202 | - id: base |
1203 | - property var container |
1204 | - objectName: "sessionAnimation" |
1205 | - |
1206 | - // changes applied when state changes to "from" |
1207 | - property list<QtObject> fromChanges |
1208 | - // transition animations when changing state to "from" |
1209 | - property list<QtObject> fromAnimations |
1210 | - |
1211 | - // changes applied when state changes to "to" |
1212 | - property list<QtObject> toChanges |
1213 | - // transition animations when changing state to "to" |
1214 | - property list<QtObject> toAnimations |
1215 | - |
1216 | - function start() { |
1217 | - // "prep" state forces toChanges without transition animations. |
1218 | - state = "prep" |
1219 | - state = "to"; |
1220 | - } |
1221 | - function end() { |
1222 | - state = "from"; |
1223 | - } |
1224 | - |
1225 | - signal completed() |
1226 | - |
1227 | - states: [ |
1228 | - State { |
1229 | - name: "baseAnimation" |
1230 | - PropertyChanges { target: container; anchors.fill: undefined } |
1231 | - }, |
1232 | - |
1233 | - State { |
1234 | - name: "prep" |
1235 | - extend: "baseAnimation" |
1236 | - changes: fromChanges |
1237 | - }, |
1238 | - State { |
1239 | - name: "from" |
1240 | - extend: "prep" |
1241 | - }, |
1242 | - State { |
1243 | - name: "in" |
1244 | - extend: "baseAnimation" |
1245 | - changes: toChanges |
1246 | - } |
1247 | - ] |
1248 | - |
1249 | - transitions: [ |
1250 | - Transition { |
1251 | - to: "from" |
1252 | - animations: fromAnimations |
1253 | - }, |
1254 | - Transition { |
1255 | - to: "to" |
1256 | - animations: toAnimations |
1257 | - } |
1258 | - ] |
1259 | -} |
1260 | |
1261 | === removed file 'qml/Stages/Animations/SwipeFromBottomAnimation.qml' |
1262 | --- qml/Stages/Animations/SwipeFromBottomAnimation.qml 2015-07-15 15:07:19 +0000 |
1263 | +++ qml/Stages/Animations/SwipeFromBottomAnimation.qml 1970-01-01 00:00:00 +0000 |
1264 | @@ -1,53 +0,0 @@ |
1265 | -/* |
1266 | - * Copyright (C) 2014 Canonical, Ltd. |
1267 | - * |
1268 | - * This program is free software; you can redistribute it and/or modify |
1269 | - * it under the terms of the GNU General Public License as published by |
1270 | - * the Free Software Foundation; version 3. |
1271 | - * |
1272 | - * This program is distributed in the hope that it will be useful, |
1273 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1274 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1275 | - * GNU General Public License for more details. |
1276 | - * |
1277 | - * You should have received a copy of the GNU General Public License |
1278 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1279 | - */ |
1280 | - |
1281 | -import QtQuick 2.4 |
1282 | -import Ubuntu.Components 1.3 |
1283 | - |
1284 | -BaseSessionAnimation { |
1285 | - id: animation |
1286 | - |
1287 | - fromChanges: [ |
1288 | - AnchorChanges { |
1289 | - target: container; |
1290 | - anchors.top: container.parent.bottom |
1291 | - } |
1292 | - ] |
1293 | - fromAnimations: [ |
1294 | - SequentialAnimation { |
1295 | - // clip so we don't go out of parent's bounds during spread |
1296 | - PropertyAction { target: container.parent; property: "clip"; value: true } |
1297 | - AnchorAnimation { easing: UbuntuAnimation.StandardEasing; duration: UbuntuAnimation.BriskDuration } |
1298 | - PropertyAction { target: container.parent; property: "clip"; value: false } |
1299 | - ScriptAction { script: { animation.completed(); } } |
1300 | - } |
1301 | - ] |
1302 | - |
1303 | - toChanges: [ |
1304 | - AnchorChanges { |
1305 | - target: container; |
1306 | - anchors.top: container.parent.top |
1307 | - } |
1308 | - ] |
1309 | - toAnimations: [ |
1310 | - SequentialAnimation { |
1311 | - // clip so we don't go out of parent's bounds during spread |
1312 | - PropertyAction { target: container.parent; property: "clip"; value: true } |
1313 | - AnchorAnimation { easing: UbuntuAnimation.StandardEasing; duration: UbuntuAnimation.BriskDuration } |
1314 | - PropertyAction { target: container.parent; property: "clip"; value: false } |
1315 | - } |
1316 | - ] |
1317 | -} |
1318 | |
1319 | === modified file 'qml/Stages/ApplicationWindow.qml' |
1320 | --- qml/Stages/ApplicationWindow.qml 2016-04-20 17:09:15 +0000 |
1321 | +++ qml/Stages/ApplicationWindow.qml 2016-04-20 17:09:16 +0000 |
1322 | @@ -20,35 +20,77 @@ |
1323 | |
1324 | FocusScope { |
1325 | id: root |
1326 | - implicitWidth: sessionContainer.implicitWidth |
1327 | - implicitHeight: sessionContainer.implicitHeight |
1328 | + implicitWidth: surfaceContainer.implicitWidth |
1329 | + implicitHeight: surfaceContainer.implicitHeight |
1330 | |
1331 | // to be read from outside |
1332 | - property alias interactive: sessionContainer.interactive |
1333 | + property alias interactive: surfaceContainer.interactive |
1334 | property bool orientationChangesEnabled: d.supportsSurfaceResize ? d.surfaceOldEnoughToBeResized : true |
1335 | - readonly property string title: sessionContainer.surface && sessionContainer.surface.name !== "" ? |
1336 | - sessionContainer.surface.name : d.name |
1337 | + readonly property string title: surface && surface.name !== "" ? surface.name : d.name |
1338 | |
1339 | // overridable from outside |
1340 | - property bool fullscreen: application ? application.fullscreen : false |
1341 | + property bool fullscreen: { |
1342 | + if (surface) { |
1343 | + return surface.state === Mir.FullscreenState; |
1344 | + } else if (application) { |
1345 | + return application.fullscreen; |
1346 | + } else { |
1347 | + return false; |
1348 | + } |
1349 | + } |
1350 | |
1351 | // to be set from outside |
1352 | + property QtObject surface |
1353 | property QtObject application |
1354 | property int surfaceOrientationAngle |
1355 | - property alias resizeSurface: sessionContainer.resizeSurface |
1356 | + property alias resizeSurface: surfaceContainer.resizeSurface |
1357 | property int requestedWidth: -1 |
1358 | property int requestedHeight: -1 |
1359 | |
1360 | - readonly property int minimumWidth: sessionContainer.surface ? sessionContainer.surface.minimumWidth : 0 |
1361 | - readonly property int minimumHeight: sessionContainer.surface ? sessionContainer.surface.minimumHeight : 0 |
1362 | - readonly property int maximumWidth: sessionContainer.surface ? sessionContainer.surface.maximumWidth : 0 |
1363 | - readonly property int maximumHeight: sessionContainer.surface ? sessionContainer.surface.maximumHeight : 0 |
1364 | - readonly property int widthIncrement: sessionContainer.surface ? sessionContainer.surface.widthIncrement : 0 |
1365 | - readonly property int heightIncrement: sessionContainer.surface ? sessionContainer.surface.heightIncrement : 0 |
1366 | + readonly property int minimumWidth: surface ? surface.minimumWidth : 0 |
1367 | + readonly property int minimumHeight: surface ? surface.minimumHeight : 0 |
1368 | + readonly property int maximumWidth: surface ? surface.maximumWidth : 0 |
1369 | + readonly property int maximumHeight: surface ? surface.maximumHeight : 0 |
1370 | + readonly property int widthIncrement: surface ? surface.widthIncrement : 0 |
1371 | + readonly property int heightIncrement: surface ? surface.heightIncrement : 0 |
1372 | + |
1373 | + onSurfaceChanged: { |
1374 | + // The order in which the instructions are executed here matters, to that the correct state |
1375 | + // transitions in stateGroup take place. |
1376 | + // More specifically, the moment surfaceContainer.surface gets updated relative to the |
1377 | + // other instructions. |
1378 | + if (surface) { |
1379 | + surfaceContainer.surface = surface; |
1380 | + d.liveSurface = surface.live; |
1381 | + d.hadSurface = false; |
1382 | + surfaceInitTimer.start(); |
1383 | + } else { |
1384 | + if (d.surfaceInitialized) { |
1385 | + d.hadSurface = true; |
1386 | + } |
1387 | + d.surfaceInitialized = false; |
1388 | + surfaceContainer.surface = null; |
1389 | + } |
1390 | + } |
1391 | |
1392 | QtObject { |
1393 | id: d |
1394 | |
1395 | + property bool liveSurface: false; |
1396 | + property var con: Connections { |
1397 | + target: root.surface |
1398 | + onLiveChanged: d.liveSurface = root.surface.live |
1399 | + } |
1400 | + // using liveSurface instead of root.surface.live because with the latter |
1401 | + // this expression is not reevaluated when root.surface changes |
1402 | + readonly property bool needToTakeScreenshot: root.surface && d.surfaceInitialized && !d.liveSurface |
1403 | + && applicationState !== ApplicationInfoInterface.Running |
1404 | + onNeedToTakeScreenshotChanged: { |
1405 | + if (needToTakeScreenshot && screenshotImage.status === Image.Null) { |
1406 | + screenshotImage.take(); |
1407 | + } |
1408 | + } |
1409 | + |
1410 | // helpers so that we don't have to check for the existence of an application everywhere |
1411 | // (in order to avoid breaking qml binding due to a javascript exception) |
1412 | readonly property string name: root.application ? root.application.name : "" |
1413 | @@ -62,17 +104,7 @@ |
1414 | readonly property color splashColorFooter: root.application ? root.application.splashColorFooter : "#00000000" |
1415 | |
1416 | // Whether the Application had a surface before but lost it. |
1417 | - property bool hadSurface: sessionContainer.surfaceContainer.hadSurface |
1418 | - |
1419 | - readonly property bool needToTakeScreenshot: |
1420 | - ((sessionContainer.surface && d.surfaceInitialized) || d.hadSurface) |
1421 | - && screenshotImage.status === Image.Null |
1422 | - && d.applicationState === ApplicationInfoInterface.Stopped |
1423 | - onNeedToTakeScreenshotChanged: { |
1424 | - if (needToTakeScreenshot) { |
1425 | - screenshotImage.take(); |
1426 | - } |
1427 | - } |
1428 | + property bool hadSurface: false |
1429 | |
1430 | //FIXME - this is a hack to avoid the first few rendered frames as they |
1431 | // might show the UI accommodating due to surface resizes on startup. |
1432 | @@ -99,7 +131,9 @@ |
1433 | Timer { |
1434 | id: surfaceInitTimer |
1435 | interval: 100 |
1436 | - onTriggered: { if (sessionContainer.surface) {d.surfaceInitialized = true;} } |
1437 | + onTriggered: { |
1438 | + if (root.surface && root.surface.live) {d.surfaceInitialized = true;} |
1439 | + } |
1440 | } |
1441 | |
1442 | Timer { |
1443 | @@ -117,7 +151,7 @@ |
1444 | function take() { |
1445 | // Save memory by using a half-resolution (thus quarter size) screenshot. |
1446 | // Do not make this a binding, we can only take the screenshot once! |
1447 | - sessionContainer.grabToImage( |
1448 | + surfaceContainer.grabToImage( |
1449 | function(result) { |
1450 | screenshotImage.source = result.url; |
1451 | }, |
1452 | @@ -144,46 +178,33 @@ |
1453 | } |
1454 | } |
1455 | |
1456 | - SessionContainer { |
1457 | - id: sessionContainer |
1458 | - // A fake application might not even have a session property. |
1459 | - session: application && application.session ? application.session : null |
1460 | - |
1461 | + SurfaceContainer { |
1462 | + id: surfaceContainer |
1463 | requestedWidth: root.requestedWidth |
1464 | requestedHeight: root.requestedHeight |
1465 | - |
1466 | surfaceOrientationAngle: application && application.rotatesWindowContents ? root.surfaceOrientationAngle : 0 |
1467 | - |
1468 | - onSurfaceChanged: { |
1469 | - if (sessionContainer.surface) { |
1470 | - surfaceInitTimer.start(); |
1471 | - } else { |
1472 | - d.surfaceInitialized = false; |
1473 | - } |
1474 | - } |
1475 | - |
1476 | focus: true |
1477 | } |
1478 | |
1479 | - // SessionContainer size drives ApplicationWindow size |
1480 | + // SurfaceContainer size drives ApplicationWindow size |
1481 | Binding { |
1482 | target: root; property: "width" |
1483 | - value: stateGroup.state === "surface" ? sessionContainer.width : root.requestedWidth |
1484 | + value: stateGroup.state === "surface" ? surfaceContainer.width : root.requestedWidth |
1485 | when: root.requestedWidth >= 0 |
1486 | } |
1487 | Binding { |
1488 | target: root; property: "height" |
1489 | - value: stateGroup.state === "surface" ? sessionContainer.height : root.requestedHeight |
1490 | + value: stateGroup.state === "surface" ? surfaceContainer.height : root.requestedHeight |
1491 | when: root.requestedHeight >= 0 |
1492 | } |
1493 | |
1494 | - // ApplicationWindow size drives SessionContainer size |
1495 | + // ApplicationWindow size drives SurfaceContainer size |
1496 | Binding { |
1497 | - target: sessionContainer; property: "width"; value: root.width |
1498 | + target: surfaceContainer; property: "width"; value: root.width |
1499 | when: root.requestedWidth < 0 |
1500 | } |
1501 | Binding { |
1502 | - target: sessionContainer; property: "height"; value: root.height |
1503 | + target: surfaceContainer; property: "height"; value: root.height |
1504 | when: root.requestedHeight < 0 |
1505 | } |
1506 | |
1507 | @@ -194,32 +215,43 @@ |
1508 | State { |
1509 | name: "void" |
1510 | when: |
1511 | - d.hadSurface && (!sessionContainer.surface || !d.surfaceInitialized) |
1512 | + d.hadSurface && (!root.surface || !d.surfaceInitialized) |
1513 | && |
1514 | screenshotImage.status !== Image.Ready |
1515 | }, |
1516 | State { |
1517 | name: "splashScreen" |
1518 | when: |
1519 | - !d.hadSurface && (!sessionContainer.surface || !d.surfaceInitialized) |
1520 | + !d.hadSurface && (!root.surface || !d.surfaceInitialized) |
1521 | && |
1522 | screenshotImage.status !== Image.Ready |
1523 | }, |
1524 | State { |
1525 | name: "surface" |
1526 | when: |
1527 | - (sessionContainer.surface && d.surfaceInitialized) |
1528 | + (root.surface && d.surfaceInitialized) |
1529 | && |
1530 | - (d.applicationState !== ApplicationInfoInterface.Stopped |
1531 | - || screenshotImage.status !== Image.Ready) |
1532 | + (d.liveSurface || |
1533 | + (d.applicationState !== ApplicationInfoInterface.Running |
1534 | + && screenshotImage.status !== Image.Ready)) |
1535 | }, |
1536 | State { |
1537 | name: "screenshot" |
1538 | when: |
1539 | screenshotImage.status === Image.Ready |
1540 | && |
1541 | - (d.applicationState === ApplicationInfoInterface.Stopped |
1542 | - || !sessionContainer.surface || !d.surfaceInitialized) |
1543 | + (d.applicationState !== ApplicationInfoInterface.Running |
1544 | + || !root.surface || !d.surfaceInitialized) |
1545 | + }, |
1546 | + State { |
1547 | + // This is a dead end. From here we expect the surface to be removed from the model |
1548 | + // shortly after we stop referencing to it in our SurfaceContainer. |
1549 | + name: "closed" |
1550 | + when: |
1551 | + // The surface died while the application is running. It must have been closed |
1552 | + // by the shell or the application decided to destroy it by itself |
1553 | + root.surface && d.surfaceInitialized && !d.liveSurface |
1554 | + && d.applicationState === ApplicationInfoInterface.Running |
1555 | } |
1556 | ] |
1557 | |
1558 | @@ -227,17 +259,17 @@ |
1559 | Transition { |
1560 | from: ""; to: "splashScreen" |
1561 | PropertyAction { target: splashLoader; property: "active"; value: true } |
1562 | - PropertyAction { target: sessionContainer.surfaceContainer |
1563 | + PropertyAction { target: surfaceContainer |
1564 | property: "visible"; value: false } |
1565 | }, |
1566 | Transition { |
1567 | from: "splashScreen"; to: "surface" |
1568 | SequentialAnimation { |
1569 | - PropertyAction { target: sessionContainer.surfaceContainer |
1570 | + PropertyAction { target: surfaceContainer |
1571 | property: "opacity"; value: 0.0 } |
1572 | - PropertyAction { target: sessionContainer.surfaceContainer |
1573 | + PropertyAction { target: surfaceContainer |
1574 | property: "visible"; value: true } |
1575 | - UbuntuNumberAnimation { target: sessionContainer.surfaceContainer; property: "opacity"; |
1576 | + UbuntuNumberAnimation { target: surfaceContainer; property: "opacity"; |
1577 | from: 0.0; to: 1.0 |
1578 | duration: UbuntuAnimation.BriskDuration } |
1579 | ScriptAction { script: { |
1580 | @@ -253,12 +285,12 @@ |
1581 | surfaceIsOldTimer.stop(); |
1582 | d.surfaceOldEnoughToBeResized = false; |
1583 | splashLoader.active = true; |
1584 | - sessionContainer.surfaceContainer.visible = true; |
1585 | + surfaceContainer.visible = true; |
1586 | } } |
1587 | UbuntuNumberAnimation { target: splashLoader; property: "opacity"; |
1588 | from: 0.0; to: 1.0 |
1589 | duration: UbuntuAnimation.BriskDuration } |
1590 | - PropertyAction { target: sessionContainer.surfaceContainer |
1591 | + PropertyAction { target: surfaceContainer |
1592 | property: "visible"; value: false } |
1593 | } |
1594 | }, |
1595 | @@ -274,15 +306,16 @@ |
1596 | from: 0.0; to: 1.0 |
1597 | duration: UbuntuAnimation.BriskDuration } |
1598 | ScriptAction { script: { |
1599 | - sessionContainer.surfaceContainer.visible = false; |
1600 | - if (sessionContainer.session) { sessionContainer.session.release(); } |
1601 | + surfaceContainer.visible = false; |
1602 | + surfaceContainer.surface = null; |
1603 | + d.hadSurface = true; |
1604 | } } |
1605 | } |
1606 | }, |
1607 | Transition { |
1608 | from: "screenshot"; to: "surface" |
1609 | SequentialAnimation { |
1610 | - PropertyAction { target: sessionContainer.surfaceContainer |
1611 | + PropertyAction { target: surfaceContainer |
1612 | property: "visible"; value: true } |
1613 | UbuntuNumberAnimation { target: screenshotImage; property: "opacity"; |
1614 | from: 1.0; to: 0.0 |
1615 | @@ -310,22 +343,31 @@ |
1616 | ScriptAction { script: { |
1617 | surfaceIsOldTimer.stop(); |
1618 | d.surfaceOldEnoughToBeResized = false; |
1619 | - sessionContainer.surfaceContainer.visible = false; |
1620 | - if (sessionContainer.session) { sessionContainer.session.release(); } |
1621 | + surfaceContainer.visible = false; |
1622 | } } |
1623 | }, |
1624 | Transition { |
1625 | from: "void"; to: "surface" |
1626 | SequentialAnimation { |
1627 | - PropertyAction { target: sessionContainer.surfaceContainer; property: "opacity"; value: 0.0 } |
1628 | - PropertyAction { target: sessionContainer.surfaceContainer; property: "visible"; value: true } |
1629 | - UbuntuNumberAnimation { target: sessionContainer.surfaceContainer; property: "opacity"; |
1630 | + PropertyAction { target: surfaceContainer; property: "opacity"; value: 0.0 } |
1631 | + PropertyAction { target: surfaceContainer; property: "visible"; value: true } |
1632 | + UbuntuNumberAnimation { target: surfaceContainer; property: "opacity"; |
1633 | from: 0.0; to: 1.0 |
1634 | duration: UbuntuAnimation.BriskDuration } |
1635 | ScriptAction { script: { |
1636 | surfaceIsOldTimer.start(); |
1637 | } } |
1638 | } |
1639 | + }, |
1640 | + Transition { |
1641 | + to: "closed" |
1642 | + SequentialAnimation { |
1643 | + ScriptAction { script: { |
1644 | + surfaceContainer.visible = false; |
1645 | + surfaceContainer.surface = null; |
1646 | + d.hadSurface = true; |
1647 | + } } |
1648 | + } |
1649 | } |
1650 | ] |
1651 | } |
1652 | |
1653 | === modified file 'qml/Stages/DecoratedWindow.qml' |
1654 | --- qml/Stages/DecoratedWindow.qml 2016-02-17 13:17:12 +0000 |
1655 | +++ qml/Stages/DecoratedWindow.qml 2016-04-20 17:09:16 +0000 |
1656 | @@ -26,10 +26,10 @@ |
1657 | width: applicationWindow.width |
1658 | height: (decorationShown ? decoration.height : 0) + applicationWindow.height |
1659 | |
1660 | - property alias window: applicationWindow |
1661 | property alias application: applicationWindow.application |
1662 | + property alias surface: applicationWindow.surface |
1663 | property alias active: decoration.active |
1664 | - property alias title: decoration.title |
1665 | + readonly property alias title: applicationWindow.title |
1666 | property alias fullscreen: applicationWindow.fullscreen |
1667 | |
1668 | readonly property bool decorationShown: !fullscreen |
1669 | @@ -79,11 +79,11 @@ |
1670 | WindowDecoration { |
1671 | id: decoration |
1672 | target: root.parent |
1673 | - objectName: application ? "appWindowDecoration_" + application.appId : "appWindowDecoration_null" |
1674 | + objectName: "appWindowDecoration" |
1675 | anchors { left: parent.left; top: parent.top; right: parent.right } |
1676 | height: units.gu(3) |
1677 | width: root.width |
1678 | - title: window.title |
1679 | + title: applicationWindow.title |
1680 | visible: root.decorationShown |
1681 | |
1682 | onClose: root.close(); |
1683 | @@ -94,7 +94,7 @@ |
1684 | |
1685 | ApplicationWindow { |
1686 | id: applicationWindow |
1687 | - objectName: application ? "appWindow_" + application.appId : "appWindow_null" |
1688 | + objectName: "appWindow" |
1689 | anchors.top: parent.top |
1690 | anchors.topMargin: decoration.height |
1691 | anchors.left: parent.left |
1692 | |
1693 | === modified file 'qml/Stages/DesktopSpread.qml' |
1694 | --- qml/Stages/DesktopSpread.qml 2016-03-10 22:39:57 +0000 |
1695 | +++ qml/Stages/DesktopSpread.qml 2016-04-20 17:09:16 +0000 |
1696 | @@ -1,5 +1,5 @@ |
1697 | /* |
1698 | - * Copyright (C) 2015 Canonical, Ltd. |
1699 | + * Copyright (C) 2015-2016 Canonical, Ltd. |
1700 | * |
1701 | * This program is free software; you can redistribute it and/or modify |
1702 | * it under the terms of the GNU General Public License as published by |
1703 | @@ -20,6 +20,7 @@ |
1704 | import Ubuntu.Gestures 0.1 |
1705 | import Unity.Application 0.1 |
1706 | import "../Components" |
1707 | +import Utils 0.1 |
1708 | |
1709 | FocusScope { |
1710 | id: root |
1711 | @@ -69,12 +70,12 @@ |
1712 | } |
1713 | |
1714 | function selectNext(isAutoRepeat) { |
1715 | - if (isAutoRepeat && spreadRepeater.highlightedIndex >= ApplicationManager.count -1) { |
1716 | + if (isAutoRepeat && spreadRepeater.highlightedIndex >= topLevelSurfaceList.count -1) { |
1717 | return; // AutoRepeat is not allowed to wrap around |
1718 | } |
1719 | |
1720 | - spreadRepeater.highlightedIndex = (spreadRepeater.highlightedIndex + 1) % ApplicationManager.count; |
1721 | - var newContentX = ((spreadFlickable.contentWidth) / (ApplicationManager.count + 1)) * Math.max(0, Math.min(ApplicationManager.count - 5, spreadRepeater.highlightedIndex - 3)); |
1722 | + spreadRepeater.highlightedIndex = (spreadRepeater.highlightedIndex + 1) % topLevelSurfaceList.count; |
1723 | + var newContentX = ((spreadFlickable.contentWidth) / (topLevelSurfaceList.count + 1)) * Math.max(0, Math.min(topLevelSurfaceList.count - 5, spreadRepeater.highlightedIndex - 3)); |
1724 | if (spreadFlickable.contentX < newContentX || spreadRepeater.highlightedIndex == 0) { |
1725 | spreadFlickable.snapTo(newContentX) |
1726 | } |
1727 | @@ -85,10 +86,10 @@ |
1728 | return; // AutoRepeat is not allowed to wrap around |
1729 | } |
1730 | |
1731 | - var newIndex = spreadRepeater.highlightedIndex - 1 >= 0 ? spreadRepeater.highlightedIndex - 1 : ApplicationManager.count - 1; |
1732 | + var newIndex = spreadRepeater.highlightedIndex - 1 >= 0 ? spreadRepeater.highlightedIndex - 1 : topLevelSurfaceList.count - 1; |
1733 | spreadRepeater.highlightedIndex = newIndex; |
1734 | - var newContentX = ((spreadFlickable.contentWidth) / (ApplicationManager.count + 1)) * Math.max(0, Math.min(ApplicationManager.count - 5, spreadRepeater.highlightedIndex - 1)); |
1735 | - if (spreadFlickable.contentX > newContentX || newIndex == ApplicationManager.count -1) { |
1736 | + var newContentX = ((spreadFlickable.contentWidth) / (topLevelSurfaceList.count + 1)) * Math.max(0, Math.min(topLevelSurfaceList.count - 5, spreadRepeater.highlightedIndex - 1)); |
1737 | + if (spreadFlickable.contentX > newContentX || newIndex == topLevelSurfaceList.count -1) { |
1738 | spreadFlickable.snapTo(newContentX) |
1739 | } |
1740 | } |
1741 | @@ -98,8 +99,8 @@ |
1742 | if (spreadContainer.visible) { |
1743 | root.playFocusAnimation(spreadRepeater.highlightedIndex) |
1744 | } |
1745 | - var application = ApplicationManager.get(spreadRepeater.highlightedIndex); |
1746 | - ApplicationManager.requestFocusApplication(application.appId); |
1747 | + var surface = topLevelSurfaceList.surfaceAt(spreadRepeater.highlightedIndex); |
1748 | + surface.requestFocus(); |
1749 | } |
1750 | } |
1751 | |
1752 | @@ -144,7 +145,7 @@ |
1753 | Repeater { |
1754 | id: spreadRepeater |
1755 | objectName: "spreadRepeater" |
1756 | - model: ApplicationManager |
1757 | + model: topLevelSurfaceList |
1758 | |
1759 | property int highlightedIndex: -1 |
1760 | property int closingIndex: -1 |
1761 | @@ -182,7 +183,8 @@ |
1762 | objectName: "clippedSpreadDelegate" |
1763 | anchors.left: parent.left |
1764 | anchors.top: parent.top |
1765 | - application: ApplicationManager.get(index) |
1766 | + application: model.application |
1767 | + surface: model.surface |
1768 | width: spreadMaths.spreadHeight |
1769 | height: spreadMaths.spreadHeight |
1770 | |
1771 | @@ -216,7 +218,7 @@ |
1772 | id: spreadMaths |
1773 | flickable: spreadFlickable |
1774 | itemIndex: index |
1775 | - totalItems: Math.max(6, ApplicationManager.count) |
1776 | + totalItems: Math.max(6, topLevelSurfaceList.count) |
1777 | sceneHeight: root.height |
1778 | itemHeight: spreadDelegate.height |
1779 | } |
1780 | @@ -310,7 +312,7 @@ |
1781 | Layout.preferredWidth: height * 8 / 7.6 |
1782 | image: Image { |
1783 | anchors.fill: parent |
1784 | - source: model.icon |
1785 | + source: model.application.icon |
1786 | Rectangle { |
1787 | anchors.fill: parent |
1788 | color: "black" |
1789 | @@ -324,7 +326,9 @@ |
1790 | Label { |
1791 | Layout.fillWidth: true |
1792 | Layout.preferredHeight: units.gu(6) |
1793 | - text: model.name |
1794 | + property string surfaceName: model.surface ? model.surface.name : "" |
1795 | + property string applicationName: model.application ? model.application.name : "" |
1796 | + text: surfaceName ? surfaceName : applicationName |
1797 | wrapMode: Text.WordWrap |
1798 | elide: Text.ElideRight |
1799 | maximumLineCount: 2 |
1800 | @@ -354,7 +358,7 @@ |
1801 | anchors.margins: -units.gu(2) |
1802 | onClicked: { |
1803 | spreadRepeater.closingIndex = index; |
1804 | - ApplicationManager.stopApplication(model.appId) |
1805 | + model.surface.close(); |
1806 | } |
1807 | } |
1808 | } |
1809 | @@ -421,7 +425,7 @@ |
1810 | objectName: "spreadFlickable" |
1811 | anchors.fill: parent |
1812 | property int minContentWidth: 6 * Math.min(height / 4, width / 5) |
1813 | - contentWidth: Math.max(6, ApplicationManager.count) * Math.min(height / 4, width / 5) |
1814 | + contentWidth: Math.max(6, topLevelSurfaceList.count) * Math.min(height / 4, width / 5) |
1815 | enabled: false |
1816 | |
1817 | function snapTo(contentX) { |
1818 | @@ -523,7 +527,7 @@ |
1819 | Label { |
1820 | id: currentSelectedLabel |
1821 | anchors { bottom: parent.bottom; bottomMargin: root.height * 0.625; horizontalCenter: parent.horizontalCenter } |
1822 | - text: spreadRepeater.highlightedIndex >= 0 ? ApplicationManager.get(spreadRepeater.highlightedIndex).name : "" |
1823 | + text: spreadRepeater.highlightedIndex >= 0 ? topLevelSurfaceList.surfaceAt(spreadRepeater.highlightedIndex).name : "" |
1824 | visible: false |
1825 | fontSize: "large" |
1826 | } |
1827 | @@ -545,7 +549,7 @@ |
1828 | from: "*" |
1829 | to: "altTab" |
1830 | SequentialAnimation { |
1831 | - PropertyAction { target: spreadRepeater; property: "highlightedIndex"; value: Math.min(ApplicationManager.count - 1, 1) } |
1832 | + PropertyAction { target: spreadRepeater; property: "highlightedIndex"; value: Math.min(topLevelSurfaceList.count - 1, 1) } |
1833 | PauseAnimation { duration: spreadContainer.animateIn ? 0 : 140 } |
1834 | PropertyAction { target: workspaceSelector; property: "visible" } |
1835 | PropertyAction { target: spreadContainer; property: "visible" } |
1836 | |
1837 | === modified file 'qml/Stages/DesktopSpreadDelegate.qml' |
1838 | --- qml/Stages/DesktopSpreadDelegate.qml 2016-01-11 12:29:30 +0000 |
1839 | +++ qml/Stages/DesktopSpreadDelegate.qml 2016-04-20 17:09:16 +0000 |
1840 | @@ -1,5 +1,5 @@ |
1841 | /* |
1842 | - * Copyright (C) 2014-2015 Canonical, Ltd. |
1843 | + * Copyright (C) 2014-2016 Canonical, Ltd. |
1844 | * |
1845 | * This program is free software; you can redistribute it and/or modify |
1846 | * it under the terms of the GNU General Public License as published by |
1847 | @@ -25,12 +25,13 @@ |
1848 | |
1849 | property alias window: applicationWindow |
1850 | property alias application: applicationWindow.application |
1851 | + property alias surface: applicationWindow.surface |
1852 | |
1853 | property bool highlightShown: false |
1854 | property real shadowOpacity: 1 |
1855 | |
1856 | - property int windowWidth: application && application.session && application.session.lastSurface ? application.session.lastSurface.size.width : 0 |
1857 | - property int windowHeight: application && application.session && application.session.lastSurface ? application.session.lastSurface.size.height : 0 |
1858 | + property int windowWidth: surface ? surface.size.width : 0 |
1859 | + property int windowHeight: surface ? surface.size.height : 0 |
1860 | |
1861 | state: "normal" |
1862 | states: [ |
1863 | |
1864 | === modified file 'qml/Stages/DesktopStage.qml' |
1865 | --- qml/Stages/DesktopStage.qml 2016-04-20 17:09:15 +0000 |
1866 | +++ qml/Stages/DesktopStage.qml 2016-04-20 17:09:16 +0000 |
1867 | @@ -39,45 +39,18 @@ |
1868 | // Used by TutorialRight |
1869 | property bool spreadShown: spread.state == "altTab" |
1870 | |
1871 | - mainApp: ApplicationManager.focusedApplicationId |
1872 | - ? ApplicationManager.findApplication(ApplicationManager.focusedApplicationId) |
1873 | - : null |
1874 | + mainApp: priv.focusedAppDelegate ? priv.focusedAppDelegate.application : null |
1875 | |
1876 | // application windows never rotate independently |
1877 | mainAppWindowOrientationAngle: shellOrientationAngle |
1878 | |
1879 | orientationChangesEnabled: true |
1880 | |
1881 | - Connections { |
1882 | - target: ApplicationManager |
1883 | - onApplicationAdded: { |
1884 | - if (spread.state == "altTab") { |
1885 | - spread.state = ""; |
1886 | - } |
1887 | - |
1888 | - ApplicationManager.focusApplication(appId); |
1889 | - } |
1890 | - |
1891 | - onApplicationRemoved: { |
1892 | - priv.focusNext(); |
1893 | - } |
1894 | - |
1895 | - onFocusRequested: { |
1896 | - var appIndex = priv.indexOf(appId); |
1897 | - var appDelegate = appRepeater.itemAt(appIndex); |
1898 | - appDelegate.restore(); |
1899 | - |
1900 | - if (spread.state == "altTab") { |
1901 | - spread.cancel(); |
1902 | - } |
1903 | - } |
1904 | - } |
1905 | - |
1906 | GlobalShortcut { |
1907 | id: closeWindowShortcut |
1908 | shortcut: Qt.AltModifier|Qt.Key_F4 |
1909 | - onTriggered: ApplicationManager.stopApplication(priv.focusedAppId) |
1910 | - active: priv.focusedAppId !== "" |
1911 | + onTriggered: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.close(); } } |
1912 | + active: priv.focusedAppDelegate !== null |
1913 | } |
1914 | |
1915 | GlobalShortcut { |
1916 | @@ -121,40 +94,40 @@ |
1917 | active: priv.focusedAppDelegate !== null |
1918 | } |
1919 | |
1920 | + Connections { |
1921 | + target: root.topLevelSurfaceList |
1922 | + onCountChanged: { |
1923 | + if (spread.state == "altTab") { |
1924 | + spread.cancel(); |
1925 | + } |
1926 | + } |
1927 | + } |
1928 | + |
1929 | QtObject { |
1930 | id: priv |
1931 | + objectName: "DesktopStagePrivate" |
1932 | |
1933 | - readonly property string focusedAppId: ApplicationManager.focusedApplicationId |
1934 | - readonly property var focusedAppDelegate: { |
1935 | - var index = indexOf(focusedAppId); |
1936 | - return index >= 0 && index < appRepeater.count ? appRepeater.itemAt(index) : null |
1937 | + property var focusedAppDelegate: null |
1938 | + onFocusedAppDelegateChanged: { |
1939 | + if (spread.state == "altTab") { |
1940 | + spread.state = ""; |
1941 | + } |
1942 | } |
1943 | - onFocusedAppDelegateChanged: updateForegroundMaximizedApp(); |
1944 | |
1945 | - property int foregroundMaximizedAppZ: -1 |
1946 | - property int foregroundMaximizedAppIndex: -1 // for stuff like drop shadow and focusing maximized app by clicking panel |
1947 | + property var foregroundMaximizedAppDelegate: null // for stuff like drop shadow and focusing maximized app by clicking panel |
1948 | |
1949 | function updateForegroundMaximizedApp() { |
1950 | - var tmp = -1; |
1951 | - var tmpAppId = -1; |
1952 | - for (var i = appRepeater.count - 1; i >= 0; i--) { |
1953 | + var found = false; |
1954 | + for (var i = 0; i < appRepeater.count && !found; i++) { |
1955 | var item = appRepeater.itemAt(i); |
1956 | if (item && item.visuallyMaximized) { |
1957 | - tmpAppId = i; |
1958 | - tmp = Math.max(tmp, item.normalZ); |
1959 | - } |
1960 | - } |
1961 | - foregroundMaximizedAppZ = tmp; |
1962 | - foregroundMaximizedAppIndex = tmpAppId; |
1963 | - } |
1964 | - |
1965 | - function indexOf(appId) { |
1966 | - for (var i = 0; i < ApplicationManager.count; i++) { |
1967 | - if (ApplicationManager.get(i).appId == appId) { |
1968 | - return i; |
1969 | - } |
1970 | - } |
1971 | - return -1; |
1972 | + foregroundMaximizedAppDelegate = item; |
1973 | + found = true; |
1974 | + } |
1975 | + } |
1976 | + if (!found) { |
1977 | + foregroundMaximizedAppDelegate = null; |
1978 | + } |
1979 | } |
1980 | |
1981 | function minimizeAllWindows() { |
1982 | @@ -164,16 +137,13 @@ |
1983 | appDelegate.minimize(); |
1984 | } |
1985 | } |
1986 | - |
1987 | - ApplicationManager.unfocusCurrentApplication(); // no app should have focus at this point |
1988 | } |
1989 | |
1990 | function focusNext() { |
1991 | - ApplicationManager.unfocusCurrentApplication(); |
1992 | for (var i = 0; i < appRepeater.count; i++) { |
1993 | var appDelegate = appRepeater.itemAt(i); |
1994 | if (appDelegate && !appDelegate.minimized) { |
1995 | - ApplicationManager.focusApplication(appDelegate.appId); |
1996 | + appDelegate.focus = true; |
1997 | return; |
1998 | } |
1999 | } |
2000 | @@ -182,15 +152,14 @@ |
2001 | |
2002 | Connections { |
2003 | target: PanelState |
2004 | - onClose: { |
2005 | - ApplicationManager.stopApplication(ApplicationManager.focusedApplicationId) |
2006 | + onClose: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.close(); } } |
2007 | + onMinimize: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.minimize(); } } |
2008 | + onMaximize: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.restoreFromMaximized(); } } |
2009 | + onFocusMaximizedApp: { |
2010 | + if (priv.foregroundMaximizedAppDelegate) { |
2011 | + priv.foregroundMaximizedAppDelegate.focus = true; |
2012 | + } |
2013 | } |
2014 | - onMinimize: priv.focusedAppDelegate && priv.focusedAppDelegate.minimize(); |
2015 | - onMaximize: priv.focusedAppDelegate // don't restore minimized apps when double clicking the panel |
2016 | - && priv.focusedAppDelegate.restoreFromMaximized(); |
2017 | - onFocusMaximizedApp: if (priv.foregroundMaximizedAppIndex != -1) { |
2018 | - ApplicationManager.focusApplication(appRepeater.itemAt(priv.foregroundMaximizedAppIndex).appId); |
2019 | - } |
2020 | } |
2021 | |
2022 | Binding { |
2023 | @@ -218,7 +187,7 @@ |
2024 | Binding { |
2025 | target: PanelState |
2026 | property: "dropShadow" |
2027 | - value: priv.focusedAppDelegate && !priv.focusedAppDelegate.maximized && priv.foregroundMaximizedAppIndex !== -1 |
2028 | + value: priv.focusedAppDelegate && !priv.focusedAppDelegate.maximized && priv.foregroundMaximizedAppDelegate !== null |
2029 | } |
2030 | |
2031 | Component.onDestruction: { |
2032 | @@ -227,6 +196,27 @@ |
2033 | PanelState.dropShadow = false; |
2034 | } |
2035 | |
2036 | + Instantiator { |
2037 | + model: root.applicationManager |
2038 | + delegate: Binding { |
2039 | + target: model.application |
2040 | + property: "requestedState" |
2041 | + |
2042 | + // TODO: figure out some lifecycle policy, like suspending minimized apps |
2043 | + // if running on a tablet or something. |
2044 | + // TODO: If the device has a dozen suspended apps because it was running |
2045 | + // in staged mode, when it switches to Windowed mode it will suddenly |
2046 | + // resume all those apps at once. We might want to avoid that. |
2047 | + value: ApplicationInfoInterface.RequestedRunning // Always running for now |
2048 | + } |
2049 | + } |
2050 | + |
2051 | + Binding { |
2052 | + target: MirFocusController |
2053 | + property: "focusedSurface" |
2054 | + value: priv.focusedAppDelegate ? priv.focusedAppDelegate.surface : null |
2055 | + when: !appRepeater.startingUp && root.parent |
2056 | + } |
2057 | |
2058 | FocusScope { |
2059 | id: appContainer |
2060 | @@ -242,20 +232,25 @@ |
2061 | fillMode: Image.PreserveAspectCrop |
2062 | } |
2063 | |
2064 | - Repeater { |
2065 | + TopLevelSurfaceRepeater { |
2066 | id: appRepeater |
2067 | - model: ApplicationManager |
2068 | + model: topLevelSurfaceList |
2069 | objectName: "appRepeater" |
2070 | |
2071 | delegate: FocusScope { |
2072 | id: appDelegate |
2073 | - objectName: "appDelegate_" + appId |
2074 | + objectName: "appDelegate_" + model.id |
2075 | // z might be overriden in some cases by effects, but we need z ordering |
2076 | // to calculate occlusion detection |
2077 | - property int normalZ: ApplicationManager.count - index |
2078 | + property int normalZ: topLevelSurfaceList.count - index |
2079 | + onNormalZChanged: { |
2080 | + if (visuallyMaximized) { |
2081 | + priv.updateForegroundMaximizedApp(); |
2082 | + } |
2083 | + } |
2084 | z: normalZ |
2085 | y: PanelState.panelHeight |
2086 | - focus: appId === priv.focusedAppId |
2087 | + |
2088 | width: decoratedWindow.width |
2089 | height: decoratedWindow.height |
2090 | property int requestedWidth: -1 |
2091 | @@ -280,36 +275,99 @@ |
2092 | readonly property alias minimized: appDelegatePrivate.minimized |
2093 | readonly property alias fullscreen: decoratedWindow.fullscreen |
2094 | |
2095 | - readonly property string appId: model.appId |
2096 | + readonly property var application: model.application |
2097 | property bool animationsEnabled: true |
2098 | property alias title: decoratedWindow.title |
2099 | - readonly property string appName: model.name |
2100 | + readonly property string appName: model.application ? model.application.name : "" |
2101 | property bool visuallyMaximized: false |
2102 | property bool visuallyMinimized: false |
2103 | |
2104 | + readonly property var surface: model.surface |
2105 | + |
2106 | + function claimFocus() { |
2107 | + if (spread.state == "altTab") { |
2108 | + spread.cancel(); |
2109 | + } |
2110 | + appDelegate.restore(); |
2111 | + } |
2112 | + Connections { |
2113 | + target: model.surface |
2114 | + onFocusRequested: claimFocus(); |
2115 | + } |
2116 | + Connections { |
2117 | + target: model.application |
2118 | + onFocusRequested: { |
2119 | + if (!model.surface) { |
2120 | + // when an app has no surfaces, we assume there's only one entry representing it: |
2121 | + // this delegate. |
2122 | + claimFocus(); |
2123 | + } else { |
2124 | + // if the application has surfaces, focus request should be at surface-level. |
2125 | + } |
2126 | + } |
2127 | + } |
2128 | + |
2129 | onFocusChanged: { |
2130 | - if (focus && ApplicationManager.focusedApplicationId !== appId) { |
2131 | - ApplicationManager.focusApplication(appId); |
2132 | + if (appRepeater.startingUp) |
2133 | + return; |
2134 | + |
2135 | + if (focus) { |
2136 | + priv.focusedAppDelegate = appDelegate; |
2137 | + |
2138 | + // If we're orphan (!parent) it means this stage is no longer the current one |
2139 | + // and will be deleted shortly. So we should no longer have a say over the model |
2140 | + if (root.parent) { |
2141 | + topLevelSurfaceList.raiseId(model.id); |
2142 | + } |
2143 | + } else if (!focus && priv.focusedAppDelegate === appDelegate) { |
2144 | + priv.focusedAppDelegate = null; |
2145 | + // FIXME: No idea why the Binding{} doens't update when focusedAppDelegate turns null |
2146 | + MirFocusController.focusedSurface = null; |
2147 | + } |
2148 | + } |
2149 | + Component.onCompleted: { |
2150 | + // NB: We're differentiating if this delegate was created in response to a new entry in the model |
2151 | + // or if the Repeater is just populating itself with delegates to match the model it received. |
2152 | + if (!appRepeater.startingUp) { |
2153 | + // a top level window is always the focused one when it first appears, unfocusing |
2154 | + // any preexisting one |
2155 | + focus = true; |
2156 | + } |
2157 | + } |
2158 | + Component.onDestruction: { |
2159 | + if (!root.parent) { |
2160 | + // This stage is about to be destroyed. Don't mess up with the model at this point |
2161 | + return; |
2162 | + } |
2163 | + |
2164 | + if (visuallyMaximized) { |
2165 | + priv.updateForegroundMaximizedApp(); |
2166 | + } |
2167 | + |
2168 | + if (focus) { |
2169 | + // focus some other window |
2170 | + for (var i = 0; i < appRepeater.count; i++) { |
2171 | + var appDelegate = appRepeater.itemAt(i); |
2172 | + if (appDelegate && !appDelegate.minimized && i != index) { |
2173 | + appDelegate.focus = true; |
2174 | + return; |
2175 | + } |
2176 | + } |
2177 | } |
2178 | } |
2179 | |
2180 | onVisuallyMaximizedChanged: priv.updateForegroundMaximizedApp() |
2181 | |
2182 | - visible: !visuallyMinimized && |
2183 | - !greeter.fullyShown && |
2184 | - (priv.foregroundMaximizedAppZ === -1 || priv.foregroundMaximizedAppZ <= z) || |
2185 | - decoratedWindow.fullscreen || |
2186 | - (spread.state == "altTab" && index === spread.highlightedIndex) |
2187 | + visible: ( |
2188 | + !visuallyMinimized |
2189 | + && !greeter.fullyShown |
2190 | + && (priv.foregroundMaximizedAppDelegate === null || priv.foregroundMaximizedAppDelegate.normalZ <= z) |
2191 | + ) |
2192 | + || decoratedWindow.fullscreen |
2193 | + || (spread.state == "altTab" && index === spread.highlightedIndex) |
2194 | |
2195 | - Binding { |
2196 | - target: ApplicationManager.get(index) |
2197 | - property: "requestedState" |
2198 | - // TODO: figure out some lifecycle policy, like suspending minimized apps |
2199 | - // if running on a tablet or something. |
2200 | - // TODO: If the device has a dozen suspended apps because it was running |
2201 | - // in staged mode, when it switches to Windowed mode it will suddenly |
2202 | - // resume all those apps at once. We might want to avoid that. |
2203 | - value: ApplicationInfoInterface.RequestedRunning // Always running for now |
2204 | + function close() { |
2205 | + model.surface.close(); |
2206 | } |
2207 | |
2208 | function maximize(animated) { |
2209 | @@ -351,7 +409,8 @@ |
2210 | maximizeLeft(); |
2211 | else if (maximizedRight) |
2212 | maximizeRight(); |
2213 | - ApplicationManager.focusApplication(appId); |
2214 | + |
2215 | + focus = true; |
2216 | } |
2217 | |
2218 | function playFocusAnimation() { |
2219 | @@ -369,7 +428,7 @@ |
2220 | |
2221 | states: [ |
2222 | State { |
2223 | - name: "fullscreen"; when: decoratedWindow.fullscreen |
2224 | + name: "fullscreen"; when: decoratedWindow.fullscreen && !appDelegate.minimized |
2225 | PropertyChanges { |
2226 | target: appDelegate; |
2227 | x: 0; |
2228 | @@ -462,6 +521,7 @@ |
2229 | ScriptAction { |
2230 | script: { |
2231 | if (appDelegate.minimized) { |
2232 | + appDelegate.focus = false; |
2233 | priv.focusNext(); |
2234 | } |
2235 | } |
2236 | @@ -486,7 +546,7 @@ |
2237 | id: previewBinding |
2238 | target: appDelegate |
2239 | property: "z" |
2240 | - value: ApplicationManager.count + 1 |
2241 | + value: topLevelSurfaceList.count + 1 |
2242 | when: index == spread.highlightedIndex && spread.ready |
2243 | } |
2244 | |
2245 | @@ -497,12 +557,12 @@ |
2246 | minWidth: units.gu(10) |
2247 | minHeight: units.gu(10) |
2248 | borderThickness: units.gu(2) |
2249 | - windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing |
2250 | + windowId: model.application.appId // FIXME: Change this to point to windowId once we have such a thing |
2251 | screenWidth: appContainer.width |
2252 | screenHeight: appContainer.height |
2253 | leftMargin: root.leftMargin |
2254 | |
2255 | - onPressed: { ApplicationManager.focusApplication(model.appId) } |
2256 | + onPressed: { appDelegate.focus = true; } |
2257 | |
2258 | Component.onCompleted: { |
2259 | loadWindowState(); |
2260 | @@ -529,24 +589,25 @@ |
2261 | objectName: "decoratedWindow" |
2262 | anchors.left: appDelegate.left |
2263 | anchors.top: appDelegate.top |
2264 | - application: ApplicationManager.get(index) |
2265 | - active: ApplicationManager.focusedApplicationId === model.appId |
2266 | + application: model.application |
2267 | + surface: model.surface |
2268 | + active: appDelegate.focus |
2269 | focus: true |
2270 | |
2271 | requestedWidth: appDelegate.requestedWidth |
2272 | requestedHeight: appDelegate.requestedHeight |
2273 | |
2274 | - onClose: ApplicationManager.stopApplication(model.appId) |
2275 | + onClose: { appDelegate.close(); } |
2276 | onMaximize: appDelegate.maximized || appDelegate.maximizedLeft || appDelegate.maximizedRight |
2277 | ? appDelegate.restoreFromMaximized() : appDelegate.maximize() |
2278 | onMinimize: appDelegate.minimize() |
2279 | - onDecorationPressed: { ApplicationManager.focusApplication(model.appId) } |
2280 | + onDecorationPressed: { appDelegate.focus = true; } |
2281 | } |
2282 | |
2283 | WindowedFullscreenPolicy { |
2284 | id: fullscreenPolicy |
2285 | active: true |
2286 | - application: decoratedWindow.application |
2287 | + surface: model.surface |
2288 | } |
2289 | } |
2290 | } |
2291 | |
2292 | === modified file 'qml/Stages/PhoneStage.qml' |
2293 | --- qml/Stages/PhoneStage.qml 2016-04-20 17:09:15 +0000 |
2294 | +++ qml/Stages/PhoneStage.qml 2016-04-20 17:09:16 +0000 |
2295 | @@ -26,7 +26,6 @@ |
2296 | AbstractStage { |
2297 | id: root |
2298 | |
2299 | - property QtObject applicationManager: ApplicationManager |
2300 | property bool focusFirstApp: true // If false, focused app will appear on right edge like other apps |
2301 | property bool altTabEnabled: true |
2302 | property real startScale: 1.1 |
2303 | @@ -107,9 +106,7 @@ |
2304 | } |
2305 | } |
2306 | |
2307 | - mainApp: applicationManager.focusedApplicationId |
2308 | - ? applicationManager.findApplication(applicationManager.focusedApplicationId) |
2309 | - : null |
2310 | + mainApp: priv.focusedAppDelegate ? priv.focusedAppDelegate.application : null |
2311 | |
2312 | orientationChangesEnabled: priv.focusedAppOrientationChangesEnabled |
2313 | && !priv.focusedAppDelegateIsDislocated |
2314 | @@ -158,72 +155,27 @@ |
2315 | onTriggered: { root.beingResized = false; } |
2316 | } |
2317 | |
2318 | - Connections { |
2319 | - target: applicationManager |
2320 | - |
2321 | - onFocusRequested: { |
2322 | - if (spreadView.phase > 0) { |
2323 | - spreadView.snapTo(priv.indexOf(appId)); |
2324 | - } else { |
2325 | - applicationManager.focusApplication(appId); |
2326 | - } |
2327 | - } |
2328 | - |
2329 | - onApplicationAdded: { |
2330 | - if (spreadView.phase == 2) { |
2331 | - spreadView.snapTo(applicationManager.count - 1); |
2332 | - } else { |
2333 | - spreadView.phase = 0; |
2334 | - spreadView.contentX = -spreadView.shift; |
2335 | - applicationManager.focusApplication(appId); |
2336 | - } |
2337 | - } |
2338 | - |
2339 | - onApplicationRemoved: { |
2340 | - // Unless we're closing the app ourselves in the spread, |
2341 | - // lets make sure the spread doesn't mess up by the changing app list. |
2342 | - if (spreadView.closingIndex == -1) { |
2343 | - spreadView.phase = 0; |
2344 | - spreadView.contentX = -spreadView.shift; |
2345 | - focusTopMostApp(); |
2346 | - } |
2347 | - } |
2348 | - |
2349 | - function focusTopMostApp() { |
2350 | - if (applicationManager.count > 0) { |
2351 | - var topmostApp = applicationManager.get(0); |
2352 | - applicationManager.focusApplication(topmostApp.appId); |
2353 | - } |
2354 | - } |
2355 | - } |
2356 | - |
2357 | QtObject { |
2358 | id: priv |
2359 | |
2360 | - property string focusedAppId: root.applicationManager.focusedApplicationId |
2361 | property bool focusedAppOrientationChangesEnabled: false |
2362 | readonly property int firstSpreadIndex: root.focusFirstApp ? 1 : 0 |
2363 | - readonly property var focusedAppDelegate: { |
2364 | - var index = indexOf(focusedAppId); |
2365 | - return index >= 0 && index < spreadRepeater.count ? spreadRepeater.itemAt(index) : null |
2366 | - } |
2367 | + property var focusedAppDelegate |
2368 | + // NB! This may differ from applicationManager.focusedApplicationId if focusedAppDelegate |
2369 | + // contains a screenshot instead of a surface. |
2370 | + property string focusedAppId: focusedAppDelegate ? focusedAppDelegate.application.appId : "" |
2371 | |
2372 | property real oldInverseProgress: 0 |
2373 | property bool animateX: false |
2374 | property int highlightIndex: 0 |
2375 | |
2376 | - onFocusedAppDelegateChanged: { |
2377 | - if (focusedAppDelegate) { |
2378 | - focusedAppDelegate.focus = true; |
2379 | - } |
2380 | - } |
2381 | - |
2382 | - property bool focusedAppDelegateIsDislocated: focusedAppDelegate && |
2383 | + property bool focusedAppDelegateIsDislocated: focusedAppDelegate ? |
2384 | (focusedAppDelegate.x !== 0 || focusedAppDelegate.xBehavior.running) |
2385 | + : false |
2386 | |
2387 | function indexOf(appId) { |
2388 | - for (var i = 0; i < root.applicationManager.count; i++) { |
2389 | - if (root.applicationManager.get(i).appId == appId) { |
2390 | + for (var i = 0; i < spreadRepeater.count; i++) { |
2391 | + if (spreadRepeater.itemAt(i).application.appId == appId) { |
2392 | return i; |
2393 | } |
2394 | } |
2395 | @@ -237,8 +189,8 @@ |
2396 | function reset() { |
2397 | // The app that's about to go to foreground has to be focused, otherwise |
2398 | // it would leave us in an inconsistent state. |
2399 | - if (!root.applicationManager.focusedApplicationId && root.applicationManager.count > 0) { |
2400 | - root.applicationManager.focusApplication(root.applicationManager.get(0).appId); |
2401 | + if (!MirFocusController.focusedSurface && spreadRepeater.count > 0) { |
2402 | + spreadRepeater.itemAt(0).focus = true; |
2403 | } |
2404 | |
2405 | spreadView.selectedIndex = -1; |
2406 | @@ -258,6 +210,36 @@ |
2407 | } |
2408 | } |
2409 | |
2410 | + Instantiator { |
2411 | + model: root.applicationManager |
2412 | + delegate: QtObject { |
2413 | + property var stateBinding: Binding { |
2414 | + readonly property bool isDash: model.application ? model.application.appId == "unity8-dash" : false |
2415 | + target: model.application |
2416 | + property: "requestedState" |
2417 | + value: (isDash && root.keepDashRunning) |
2418 | + || (!root.suspended && model.application && priv.focusedAppId === model.application.appId) |
2419 | + ? ApplicationInfoInterface.RequestedRunning |
2420 | + : ApplicationInfoInterface.RequestedSuspended |
2421 | + } |
2422 | + |
2423 | + property var lifecycleBinding: Binding { |
2424 | + target: model.application |
2425 | + property: "exemptFromLifecycle" |
2426 | + value: model.application |
2427 | + ? (!model.application.isTouchApp || isExemptFromLifecycle(model.application.appId)) |
2428 | + : false |
2429 | + } |
2430 | + } |
2431 | + } |
2432 | + |
2433 | + Binding { |
2434 | + target: MirFocusController |
2435 | + property: "focusedSurface" |
2436 | + value: priv.focusedAppDelegate ? priv.focusedAppDelegate.surface : null |
2437 | + when: root.parent && !spreadRepeater.startingUp |
2438 | + } |
2439 | + |
2440 | Flickable { |
2441 | id: spreadView |
2442 | objectName: "spreadView" |
2443 | @@ -308,13 +290,38 @@ |
2444 | // rely on having Flickable.contentX keeping an out-of-bounds value when it's set programatically |
2445 | // (as opposed to having contentX reaching an out-of-bounds value through dragging, which will trigger |
2446 | // the Flickable.boundsBehavior upon release). |
2447 | - onContentXChanged: { forceItToRemainStillIfBeingResized(); } |
2448 | + onContentXChanged: { |
2449 | + if (!undoContentXReset()) { |
2450 | + forceItToRemainStillIfBeingResized(); |
2451 | + } |
2452 | + } |
2453 | onShiftChanged: { forceItToRemainStillIfBeingResized(); } |
2454 | function forceItToRemainStillIfBeingResized() { |
2455 | if (root.beingResized && contentX != -spreadView.shift) { |
2456 | contentX = -spreadView.shift; |
2457 | } |
2458 | } |
2459 | + function undoContentXReset() { |
2460 | + if (contentWidth <= 0) { |
2461 | + contentWidthOnLastContentXChange = contentWidth; |
2462 | + lastContentX = contentX; |
2463 | + return false; |
2464 | + } |
2465 | + |
2466 | + if (contentWidth !== contentWidthOnLastContentXChange |
2467 | + && lastContentX === -shift && contentX === 0) { |
2468 | + // Flickable is resetting contentX because contentWidth has changed. Undo it. |
2469 | + contentX = -shift; |
2470 | + return true; |
2471 | + } |
2472 | + |
2473 | + contentWidthOnLastContentXChange = contentWidth; |
2474 | + lastContentX = contentX; |
2475 | + return false; |
2476 | + } |
2477 | + property real contentWidthOnLastContentXChange: -1 |
2478 | + property real lastContentX: 0 |
2479 | + // </FIXME-contentX> |
2480 | |
2481 | Behavior on contentX { |
2482 | enabled: root.altTabPressed |
2483 | @@ -377,7 +384,7 @@ |
2484 | snapAnimation.start(); |
2485 | return; |
2486 | } |
2487 | - if (root.applicationManager.count <= index) { |
2488 | + if (topLevelSurfaceList.count <= index) { |
2489 | // In case we're trying to snap to some non existing app, lets snap back to the first one |
2490 | index = 0; |
2491 | } |
2492 | @@ -414,7 +421,8 @@ |
2493 | ScriptAction { |
2494 | script: { |
2495 | if (spreadView.selectedIndex >= 0) { |
2496 | - root.applicationManager.focusApplication(root.applicationManager.get(spreadView.selectedIndex).appId); |
2497 | + var delegate = spreadRepeater.itemAt(spreadView.selectedIndex) |
2498 | + delegate.focus = true; |
2499 | |
2500 | spreadView.selectedIndex = -1; |
2501 | spreadView.phase = 0; |
2502 | @@ -429,7 +437,7 @@ |
2503 | // This width controls how much the spread can be flicked left/right. It's composed of: |
2504 | // tileDistance * app count (with a minimum of 3 apps, in order to also allow moving 1 and 2 apps a bit) |
2505 | // + some constant value (still scales with the screen width) which looks good and somewhat fills the screen |
2506 | - width: Math.max(3, root.applicationManager.count) * spreadView.tileDistance + (spreadView.width - spreadView.tileDistance) * 1.5 |
2507 | + width: Math.max(3, topLevelSurfaceList.count) * spreadView.tileDistance + (spreadView.width - spreadView.tileDistance) * 1.5 |
2508 | height: parent.height |
2509 | Behavior on width { |
2510 | enabled: spreadView.closingIndex >= 0 |
2511 | @@ -449,13 +457,30 @@ |
2512 | } |
2513 | } |
2514 | |
2515 | - Repeater { |
2516 | + TopLevelSurfaceRepeater { |
2517 | id: spreadRepeater |
2518 | objectName: "spreadRepeater" |
2519 | - model: root.applicationManager |
2520 | + model: topLevelSurfaceList |
2521 | + |
2522 | + onItemRemoved: { |
2523 | + // Unless we're closing the app ourselves in the spread, |
2524 | + // lets make sure the spread doesn't mess up by the changing app list. |
2525 | + if (spreadView.closingIndex == -1) { |
2526 | + spreadView.phase = 0; |
2527 | + spreadView.contentX = -spreadView.shift; |
2528 | + focusTopMostApp(); |
2529 | + } |
2530 | + } |
2531 | + function focusTopMostApp() { |
2532 | + if (spreadRepeater.count > 0) { |
2533 | + var topmostDelegate = spreadRepeater.itemAt(0); |
2534 | + topmostDelegate.focus = true; |
2535 | + } |
2536 | + } |
2537 | + |
2538 | delegate: TransformedSpreadDelegate { |
2539 | id: appDelegate |
2540 | - objectName: "appDelegate" + index |
2541 | + objectName: "spreadDelegate_" + model.id |
2542 | startAngle: 45 |
2543 | endAngle: 5 |
2544 | startScale: root.startScale |
2545 | @@ -467,28 +492,61 @@ |
2546 | selected: spreadView.selectedIndex == index |
2547 | otherSelected: spreadView.selectedIndex >= 0 && !selected |
2548 | interactive: !spreadView.interactive && spreadView.phase === 0 |
2549 | - && priv.fullyShowingFocusedApp && root.interactive && isFocused |
2550 | + && priv.fullyShowingFocusedApp && root.interactive && focus |
2551 | swipeToCloseEnabled: spreadView.interactive && root.interactive && !snapAnimation.running |
2552 | maximizedAppTopMargin: root.maximizedAppTopMargin |
2553 | dropShadow: spreadView.active || priv.focusedAppDelegateIsDislocated |
2554 | focusFirstApp: root.focusFirstApp |
2555 | highlightShown: root.altTabPressed && index === priv.highlightIndex |
2556 | |
2557 | - readonly property bool isDash: model.appId == "unity8-dash" |
2558 | - |
2559 | - Binding { |
2560 | - target: appDelegate.application |
2561 | - property: "exemptFromLifecycle" |
2562 | - value: !model.isTouchApp || isExemptFromLifecycle(model.appId) |
2563 | - } |
2564 | - |
2565 | - Binding { |
2566 | - target: appDelegate.application |
2567 | - property: "requestedState" |
2568 | - value: (isDash && root.keepDashRunning) |
2569 | - || (!root.suspended && appDelegate.focus) |
2570 | - ? ApplicationInfoInterface.RequestedRunning |
2571 | - : ApplicationInfoInterface.RequestedSuspended |
2572 | + readonly property bool isDash: model.application.appId == "unity8-dash" |
2573 | + |
2574 | + Component.onCompleted: { |
2575 | + // NB: We're differentiating if this delegate was created in response to a new entry in the model |
2576 | + // or if the Repeater is just populating itself with delegates to match the model it received. |
2577 | + if (!spreadRepeater.startingUp) { |
2578 | + // a top level window is always the focused one when it first appears, unfocusing |
2579 | + // any preexisting one |
2580 | + // |
2581 | + // new items are appended and must be manually brought to front. |
2582 | + // that's how it *must* be in order to get the animation for new |
2583 | + // surfaces working |
2584 | + claimFocus(); |
2585 | + } |
2586 | + } |
2587 | + |
2588 | + onFocusChanged: { |
2589 | + if (focus && !spreadRepeater.startingUp) { |
2590 | + priv.focusedAppDelegate = appDelegate; |
2591 | + // If we're orphan (!parent) it means this stage is no longer the current one |
2592 | + // and will be deleted shortly. So we should no longer have a say over the model |
2593 | + if (root.parent) { |
2594 | + topLevelSurfaceList.raiseId(model.id); |
2595 | + } |
2596 | + } |
2597 | + } |
2598 | + function claimFocus() { |
2599 | + if (spreadView.phase > 0) { |
2600 | + spreadView.snapTo(model.index); |
2601 | + } else { |
2602 | + appDelegate.focus = true; |
2603 | + } |
2604 | + } |
2605 | + Connections { |
2606 | + target: model.surface |
2607 | + onFocusRequested: claimFocus() |
2608 | + } |
2609 | + Connections { |
2610 | + target: model.application |
2611 | + onFocusRequested: { |
2612 | + if (!model.surface) { |
2613 | + // when an app has no surfaces, we assume there's only one entry representing it: |
2614 | + // this delegate. |
2615 | + claimFocus(); |
2616 | + } else { |
2617 | + // if the application has surfaces, focus request should be at surface-level. |
2618 | + } |
2619 | + } |
2620 | } |
2621 | |
2622 | z: isDash && !spreadView.active ? -1 : behavioredIndex |
2623 | @@ -509,7 +567,8 @@ |
2624 | return spreadView.width + spreadIndex * spreadView.tileDistance; |
2625 | } |
2626 | |
2627 | - application: root.applicationManager.get(index) |
2628 | + application: model.application |
2629 | + surface: model.surface |
2630 | closeable: !isDash |
2631 | |
2632 | property real behavioredIndex: index |
2633 | @@ -593,11 +652,7 @@ |
2634 | |
2635 | onClicked: { |
2636 | if (root.altTabEnabled && spreadView.phase == 2) { |
2637 | - if (root.applicationManager.focusedApplicationId == root.applicationManager.get(index).appId) { |
2638 | - spreadView.snapTo(index); |
2639 | - } else { |
2640 | - root.applicationManager.requestFocusApplication(root.applicationManager.get(index).appId); |
2641 | - } |
2642 | + spreadView.snapTo(index); |
2643 | } |
2644 | } |
2645 | |
2646 | @@ -611,7 +666,15 @@ |
2647 | |
2648 | onClosed: { |
2649 | spreadView.closingIndex = index; |
2650 | - root.applicationManager.stopApplication(root.applicationManager.get(index).appId); |
2651 | + if (appDelegate.surface) { |
2652 | + appDelegate.surface.close(); |
2653 | + } else if (appDelegate.application) { |
2654 | + root.applicationManager.stopApplication(appDelegate.application.appId); |
2655 | + } else { |
2656 | + // should never happen |
2657 | + console.warn("Can't close topLevelSurfaceList entry as it has neither" |
2658 | + + " a surface nor an application"); |
2659 | + } |
2660 | } |
2661 | |
2662 | Binding { |
2663 | @@ -629,7 +692,7 @@ |
2664 | |
2665 | StagedFullscreenPolicy { |
2666 | id: fullscreenPolicy |
2667 | - application: appDelegate.application |
2668 | + surface: model.surface |
2669 | } |
2670 | |
2671 | Connections { |
2672 | @@ -637,7 +700,7 @@ |
2673 | onStageAboutToBeUnloaded: fullscreenPolicy.active = false |
2674 | } |
2675 | } |
2676 | - } |
2677 | + } // Repeater { |
2678 | } |
2679 | } |
2680 | |
2681 | |
2682 | === added file 'qml/Stages/PromptSurfaceAnimations.qml' |
2683 | --- qml/Stages/PromptSurfaceAnimations.qml 1970-01-01 00:00:00 +0000 |
2684 | +++ qml/Stages/PromptSurfaceAnimations.qml 2016-04-20 17:09:16 +0000 |
2685 | @@ -0,0 +1,81 @@ |
2686 | +/* |
2687 | + * Copyright 2016 Canonical Ltd. |
2688 | + * |
2689 | + * This program is free software; you can redistribute it and/or modify |
2690 | + * it under the terms of the GNU Lesser General Public License as published by |
2691 | + * the Free Software Foundation; version 3. |
2692 | + * |
2693 | + * This program is distributed in the hope that it will be useful, |
2694 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2695 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2696 | + * GNU Lesser General Public License for more details. |
2697 | + * |
2698 | + * You should have received a copy of the GNU Lesser General Public License |
2699 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2700 | +*/ |
2701 | + |
2702 | +import QtQuick 2.4 |
2703 | +import Ubuntu.Components 1.3 |
2704 | + |
2705 | +StateGroup { |
2706 | + id: root |
2707 | + property var container |
2708 | + property var surfaceItem |
2709 | + |
2710 | + states: [ |
2711 | + State { |
2712 | + name: "blank" |
2713 | + when: !root.surfaceItem.surface |
2714 | + }, |
2715 | + State { |
2716 | + name: "ready" |
2717 | + when: root.surfaceItem.surface && root.surfaceItem.live |
2718 | + }, |
2719 | + State { |
2720 | + name: "zombie" |
2721 | + when: root.surfaceItem.surface && !root.surfaceItem.live |
2722 | + } |
2723 | + ] |
2724 | + transitions: [ |
2725 | + Transition { |
2726 | + from: "*"; to: "zombie" |
2727 | + // Slide downwards until it's out of view, through the bottom of the window |
2728 | + SequentialAnimation { |
2729 | + // clip so we don't go out of parent's bounds during spread |
2730 | + PropertyAction { target: root.container.parent; property: "clip"; value: true } |
2731 | + UbuntuNumberAnimation { target: root.surfaceItem; property: "y"; to: root.container.height |
2732 | + duration: UbuntuAnimation.BriskDuration } |
2733 | + PropertyAction { target: root.surfaceItem; property: "visible"; value: false } |
2734 | + PropertyAction { target: container.parent; property: "clip"; value: false } |
2735 | + ScriptAction { script: { |
2736 | + // Unity.Application can't destroy a zombie MirSurface if it's still being |
2737 | + // referenced by a MirSurfaceItem. |
2738 | + root.surfaceItem.surface = null; |
2739 | + } } |
2740 | + } |
2741 | + }, |
2742 | + Transition { |
2743 | + from: "*"; to: "ready" |
2744 | + // Slide upwards into view, from the bottom of the window |
2745 | + SequentialAnimation { |
2746 | + // clip so we don't go out of parent's bounds during spread |
2747 | + PropertyAction { target: root.container.parent; property: "clip"; value: true } |
2748 | + ScriptAction { script: { |
2749 | + root.surfaceItem.y = root.container.height; |
2750 | + root.surfaceItem.visible = true; |
2751 | + } } |
2752 | + UbuntuNumberAnimation { |
2753 | + target: root.surfaceItem; property: "y"; to: 0 |
2754 | + duration: UbuntuAnimation.BriskDuration |
2755 | + } |
2756 | + PropertyAction { target: container.parent; property: "clip"; value: false } |
2757 | + } |
2758 | + }, |
2759 | + Transition { |
2760 | + from: "*"; to: "blank" |
2761 | + ScriptAction { script: { |
2762 | + root.surfaceItem.visible = false; |
2763 | + } } |
2764 | + } |
2765 | + ] |
2766 | +} |
2767 | |
2768 | === modified file 'qml/Stages/SpreadDelegate.qml' |
2769 | --- qml/Stages/SpreadDelegate.qml 2016-04-20 17:09:15 +0000 |
2770 | +++ qml/Stages/SpreadDelegate.qml 2016-04-20 17:09:16 +0000 |
2771 | @@ -1,5 +1,5 @@ |
2772 | /* |
2773 | - * Copyright 2014-2015 Canonical Ltd. |
2774 | + * Copyright 2014-2016 Canonical Ltd. |
2775 | * |
2776 | * This program is free software; you can redistribute it and/or modify |
2777 | * it under the terms of the GNU Lesser General Public License as published by |
2778 | @@ -45,6 +45,7 @@ |
2779 | property alias swipeToCloseEnabled: dragArea.enabled |
2780 | property bool closeable |
2781 | property alias application: appWindow.application |
2782 | + property alias surface: appWindow.surface |
2783 | property int shellOrientationAngle |
2784 | property int shellOrientation |
2785 | property QtObject orientations |
2786 | @@ -250,7 +251,7 @@ |
2787 | } |
2788 | PropertyChanges { |
2789 | target: appWindow |
2790 | - surfaceOrientationAngle: orientationAngle |
2791 | + surfaceOrientationAngle: appWindowWithShadow.orientationAngle |
2792 | } |
2793 | }, |
2794 | State { |
2795 | @@ -292,7 +293,7 @@ |
2796 | |
2797 | ApplicationWindow { |
2798 | id: appWindow |
2799 | - objectName: application ? "appWindow_" + application.appId : "appWindow_null" |
2800 | + objectName: "appWindow" |
2801 | focus: true |
2802 | anchors { |
2803 | fill: parent |
2804 | |
2805 | === modified file 'qml/Stages/StagedFullscreenPolicy.qml' |
2806 | --- qml/Stages/StagedFullscreenPolicy.qml 2016-03-15 19:38:03 +0000 |
2807 | +++ qml/Stages/StagedFullscreenPolicy.qml 2016-04-20 17:09:16 +0000 |
2808 | @@ -27,31 +27,29 @@ |
2809 | // Chrome not set and state change to fulscreen -> client window stays "fullscreen" |
2810 | QtObject { |
2811 | property bool active: true |
2812 | - property QtObject application: null |
2813 | |
2814 | - readonly property var lastSurface: application && application.session ? |
2815 | - application.session.lastSurface : null |
2816 | - onLastSurfaceChanged: { |
2817 | - if (!active || !lastSurface) return; |
2818 | - if (lastSurface.shellChrome === Mir.LowChrome) { |
2819 | - lastSurface.state = Mir.FullscreenState; |
2820 | + property var surface: null |
2821 | + onSurfaceChanged: { |
2822 | + if (!active || !surface) return; |
2823 | + if (surface.shellChrome === Mir.LowChrome) { |
2824 | + surface.state = Mir.FullscreenState; |
2825 | } |
2826 | } |
2827 | |
2828 | property var _connections: Connections { |
2829 | - target: lastSurface |
2830 | + target: surface |
2831 | onShellChromeChanged: { |
2832 | - if (!active || !lastSurface) return; |
2833 | - if (lastSurface.shellChrome === Mir.LowChrome) { |
2834 | - lastSurface.state = Mir.FullscreenState; |
2835 | + if (!active || !surface) return; |
2836 | + if (surface.shellChrome === Mir.LowChrome) { |
2837 | + surface.state = Mir.FullscreenState; |
2838 | } else { |
2839 | - lastSurface.state = Mir.RestoredState; |
2840 | + surface.state = Mir.RestoredState; |
2841 | } |
2842 | } |
2843 | onStateChanged: { |
2844 | if (!active) return; |
2845 | - if (lastSurface.state === Mir.RestoredState && lastSurface.shellChrome === Mir.LowChrome) { |
2846 | - lastSurface.state = Mir.FullscreenState; |
2847 | + if (surface.state === Mir.RestoredState && surface.shellChrome === Mir.LowChrome) { |
2848 | + surface.state = Mir.FullscreenState; |
2849 | } |
2850 | } |
2851 | } |
2852 | |
2853 | === renamed file 'qml/Stages/SessionContainer.qml' => 'qml/Stages/SurfaceContainer.qml' |
2854 | --- qml/Stages/SessionContainer.qml 2016-02-03 13:46:18 +0000 |
2855 | +++ qml/Stages/SurfaceContainer.qml 2016-04-20 17:09:16 +0000 |
2856 | @@ -1,5 +1,5 @@ |
2857 | /* |
2858 | - * Copyright 2014-2015 Canonical Ltd. |
2859 | + * Copyright 2014-2016 Canonical Ltd. |
2860 | * |
2861 | * This program is free software; you can redistribute it and/or modify |
2862 | * it under the terms of the GNU Lesser General Public License as published by |
2863 | @@ -15,61 +15,124 @@ |
2864 | */ |
2865 | |
2866 | import QtQuick 2.4 |
2867 | -import "Animations" |
2868 | +import Ubuntu.Components 1.3 |
2869 | +import Ubuntu.Gestures 0.1 // For TouchGate |
2870 | +import Utils 0.1 // for InputWatcher |
2871 | +import Unity.Application 0.1 // for MirSurfaceItem |
2872 | |
2873 | FocusScope { |
2874 | id: root |
2875 | - objectName: "sessionContainer" |
2876 | - implicitWidth: _surfaceContainer.implicitWidth |
2877 | - implicitHeight: _surfaceContainer.implicitHeight |
2878 | - property QtObject session |
2879 | - readonly property var childSessions: session ? session.childSessions : null |
2880 | - readonly property alias surface: _surfaceContainer.surface |
2881 | - property alias interactive: _surfaceContainer.interactive |
2882 | - property alias surfaceOrientationAngle: _surfaceContainer.surfaceOrientationAngle |
2883 | - property alias resizeSurface: _surfaceContainer.resizeSurface |
2884 | - |
2885 | + objectName: "surfaceContainer" |
2886 | + |
2887 | + // Must be set from outside |
2888 | + property var surface: null |
2889 | + |
2890 | + // Might be changed from outside |
2891 | property int requestedWidth: -1 |
2892 | property int requestedHeight: -1 |
2893 | - |
2894 | - readonly property alias surfaceContainer: _surfaceContainer |
2895 | - SurfaceContainer { |
2896 | - id: _surfaceContainer |
2897 | - requestedWidth: root.requestedWidth |
2898 | - requestedHeight: root.requestedHeight |
2899 | - surface: session ? session.lastSurface : null |
2900 | - } |
2901 | - |
2902 | - // SurfaceContainer size drives SessionContainer size |
2903 | - Binding { |
2904 | - target: root; property: "width"; value: _surfaceContainer.width |
2905 | + property bool interactive |
2906 | + property int surfaceOrientationAngle: 0 |
2907 | + property bool resizeSurface: true |
2908 | + property bool inPromptSession: false |
2909 | + |
2910 | + onSurfaceChanged: { |
2911 | + // Not a binding because animations might remove the surface from the surfaceItem |
2912 | + // programatically (in order to signal that a zombie surface is free for deletion), |
2913 | + // even though root.surface is still !null. |
2914 | + surfaceItem.surface = surface; |
2915 | + } |
2916 | + |
2917 | + InputWatcher { |
2918 | + target: surfaceItem |
2919 | + onTargetPressedChanged: { |
2920 | + if (targetPressed && root.interactive) { |
2921 | + root.focus = true; |
2922 | + root.forceActiveFocus(); |
2923 | + } |
2924 | + } |
2925 | + } |
2926 | + |
2927 | + MirSurfaceItem { |
2928 | + id: surfaceItem |
2929 | + objectName: "surfaceItem" |
2930 | + |
2931 | + fillMode: MirSurfaceItem.PadOrCrop |
2932 | + consumesInput: true |
2933 | + |
2934 | + surfaceWidth: { |
2935 | + if (root.resizeSurface) { |
2936 | + if (root.requestedWidth >= 0) { |
2937 | + return root.requestedWidth; |
2938 | + } else { |
2939 | + return width; |
2940 | + } |
2941 | + } else { |
2942 | + return -1; |
2943 | + } |
2944 | + } |
2945 | + |
2946 | + surfaceHeight: { |
2947 | + if (root.resizeSurface) { |
2948 | + if (root.requestedHeight >= 0) { |
2949 | + return root.requestedHeight; |
2950 | + } else { |
2951 | + return height; |
2952 | + } |
2953 | + } else { |
2954 | + return -1; |
2955 | + } |
2956 | + } |
2957 | + |
2958 | + enabled: root.interactive |
2959 | + antialiasing: !root.interactive |
2960 | + orientationAngle: root.surfaceOrientationAngle |
2961 | + } |
2962 | + |
2963 | + TouchGate { |
2964 | + targetItem: surfaceItem |
2965 | + anchors.fill: root |
2966 | + enabled: surfaceItem.enabled |
2967 | + } |
2968 | + |
2969 | + // MirSurface size drives SurfaceContainer size |
2970 | + Binding { |
2971 | + target: surfaceItem; property: "width"; value: root.surface ? root.surface.size.width : 0 |
2972 | + when: root.requestedWidth >= 0 && root.surface |
2973 | + } |
2974 | + Binding { |
2975 | + target: surfaceItem; property: "height"; value: root.surface ? root.surface.size.height : 0 |
2976 | + when: root.requestedHeight >= 0 && root.surface |
2977 | + } |
2978 | + Binding { |
2979 | + target: root; property: "width"; value: surfaceItem.width |
2980 | when: root.requestedWidth >= 0 |
2981 | } |
2982 | Binding { |
2983 | - target: root; property: "height"; value: _surfaceContainer.height |
2984 | + target: root; property: "height"; value: surfaceItem.height |
2985 | when: root.requestedHeight >= 0 |
2986 | } |
2987 | |
2988 | - // SessionContainer size drives SurfaceContainer size |
2989 | + // SurfaceContainer size drives MirSurface size |
2990 | Binding { |
2991 | - target: _surfaceContainer; property: "width"; value: root.width |
2992 | + target: surfaceItem; property: "width"; value: root.width |
2993 | when: root.requestedWidth < 0 |
2994 | } |
2995 | Binding { |
2996 | - target: _surfaceContainer; property: "height"; value: root.height |
2997 | + target: surfaceItem; property: "height"; value: root.height |
2998 | when: root.requestedHeight < 0 |
2999 | } |
3000 | |
3001 | Repeater { |
3002 | - id: childSessionsRepeater |
3003 | - model: root.childSessions |
3004 | + id: childSurfacesRepeater |
3005 | + objectName: "childSurfacesRepeater" |
3006 | + model: root.surface ? root.surface.promptSurfaceList : null |
3007 | |
3008 | delegate: Loader { |
3009 | objectName: "childDelegate" + index |
3010 | - anchors.fill: surfaceContainer |
3011 | + anchors.fill: root |
3012 | |
3013 | // Only way to do recursive qml items. |
3014 | - source: Qt.resolvedUrl("SessionContainer.qml") |
3015 | + source: Qt.resolvedUrl("SurfaceContainer.qml") |
3016 | |
3017 | z: index |
3018 | |
3019 | @@ -81,12 +144,12 @@ |
3020 | |
3021 | Binding { |
3022 | target: item; when: item |
3023 | - property: "interactive"; value: index == (childSessionsRepeater.count - 1) && root.interactive |
3024 | + property: "interactive"; value: index == (childSurfacesRepeater.count - 1) && root.interactive |
3025 | } |
3026 | |
3027 | Binding { |
3028 | target: item; when: item |
3029 | - property: "session"; value: modelData |
3030 | + property: "surface"; value: model.surface |
3031 | } |
3032 | |
3033 | Binding { |
3034 | @@ -98,79 +161,47 @@ |
3035 | target: item; when: item |
3036 | property: "height"; value: root.height |
3037 | } |
3038 | - } |
3039 | - } |
3040 | - |
3041 | - states: [ |
3042 | - State { |
3043 | - name: "rootSession" |
3044 | - when: root.session && !root.session.parentSession |
3045 | - }, |
3046 | - |
3047 | - State { |
3048 | - name: "childSession" |
3049 | - when: root.session && root.session.parentSession !== null && root.session.live |
3050 | - && !root.session.lastSurface |
3051 | - }, |
3052 | - |
3053 | - State { |
3054 | - name: "childSessionReady" |
3055 | - when: root.session && root.session.parentSession !== null && root.session.live |
3056 | - && root.session.lastSurface !== null |
3057 | - }, |
3058 | - |
3059 | - State { |
3060 | - name: "childSessionZombie" |
3061 | - when: root.session && root.session.parentSession !== null && !root.session.live |
3062 | - } |
3063 | - ] |
3064 | - |
3065 | - transitions: [ |
3066 | - Transition { |
3067 | - to: "childSessionReady" |
3068 | - ScriptAction { script: { if (!surfaceContainer.hadSurface) { animateIn(swipeFromBottom); } } } |
3069 | - }, |
3070 | - Transition { |
3071 | - to: "childSessionZombie" |
3072 | - ScriptAction { script: { animateOut(); } } |
3073 | - } |
3074 | - ] |
3075 | - |
3076 | - function animateIn(component) { |
3077 | - var animation = component.createObject(root, { "container": root, }); |
3078 | - animation.start(); |
3079 | - |
3080 | - var tmp = d.animations; |
3081 | - tmp.push(animation); |
3082 | - d.animations = tmp; |
3083 | - } |
3084 | - |
3085 | - function animateOut() { |
3086 | - if (d.animations.length > 0) { |
3087 | - var tmp = d.animations; |
3088 | - var popped = tmp.pop(); |
3089 | - popped.completed.connect(function() { root.session.release(); } ); |
3090 | - popped.end(); |
3091 | - d.animations = tmp; |
3092 | - } else { |
3093 | - root.session.release(); |
3094 | - } |
3095 | - } |
3096 | - |
3097 | - Component { |
3098 | - id: swipeFromBottom |
3099 | - SwipeFromBottomAnimation {} |
3100 | + |
3101 | + Binding { |
3102 | + target: item; when: item |
3103 | + property: "inPromptSession"; value: true |
3104 | + } |
3105 | + } |
3106 | + } |
3107 | + |
3108 | + Loader { |
3109 | + id: animationsLoader |
3110 | + objectName: "animationsLoader" |
3111 | + active: root.surface |
3112 | + source: { |
3113 | + if (root.inPromptSession) { |
3114 | + return "PromptSurfaceAnimations.qml"; |
3115 | + } else { |
3116 | + // Let ApplicationWindow do the animations |
3117 | + return ""; |
3118 | + } |
3119 | + } |
3120 | + Binding { |
3121 | + target: animationsLoader.item |
3122 | + when: animationsLoader.item |
3123 | + property: "surfaceItem" |
3124 | + value: surfaceItem |
3125 | + } |
3126 | + Binding { |
3127 | + target: animationsLoader.item |
3128 | + when: animationsLoader.item |
3129 | + property: "container" |
3130 | + value: root |
3131 | + } |
3132 | } |
3133 | |
3134 | QtObject { |
3135 | id: d |
3136 | - property var animations: [] |
3137 | - |
3138 | property var focusedChild: { |
3139 | - if (childSessionsRepeater.count == 0) { |
3140 | - return _surfaceContainer; |
3141 | + if (childSurfacesRepeater.count == 0) { |
3142 | + return surfaceItem; |
3143 | } else { |
3144 | - return childSessionsRepeater.itemAt(childSessionsRepeater.count - 1); |
3145 | + return childSurfacesRepeater.itemAt(childSurfacesRepeater.count - 1); |
3146 | } |
3147 | } |
3148 | onFocusedChildChanged: { |
3149 | |
3150 | === removed file 'qml/Stages/SurfaceContainer.qml' |
3151 | --- qml/Stages/SurfaceContainer.qml 2016-04-20 17:09:15 +0000 |
3152 | +++ qml/Stages/SurfaceContainer.qml 1970-01-01 00:00:00 +0000 |
3153 | @@ -1,154 +0,0 @@ |
3154 | -/* |
3155 | - * Copyright 2014-2015 Canonical Ltd. |
3156 | - * |
3157 | - * This program is free software; you can redistribute it and/or modify |
3158 | - * it under the terms of the GNU Lesser General Public License as published by |
3159 | - * the Free Software Foundation; version 3. |
3160 | - * |
3161 | - * This program is distributed in the hope that it will be useful, |
3162 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3163 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3164 | - * GNU Lesser General Public License for more details. |
3165 | - * |
3166 | - * You should have received a copy of the GNU Lesser General Public License |
3167 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3168 | -*/ |
3169 | - |
3170 | -import QtQuick 2.4 |
3171 | -import Ubuntu.Components 1.3 |
3172 | -import Ubuntu.Gestures 0.1 // For TouchGate |
3173 | -import Utils 0.1 // for InputWatcher |
3174 | -import Unity.Application 0.1 // for MirSurfaceItem |
3175 | - |
3176 | -FocusScope { |
3177 | - id: root |
3178 | - objectName: "surfaceContainer" |
3179 | - |
3180 | - property var surface: null |
3181 | - property bool hadSurface: false |
3182 | - property bool interactive |
3183 | - property int surfaceOrientationAngle: 0 |
3184 | - property string name: surface ? surface.name : "" |
3185 | - property bool resizeSurface: true |
3186 | - |
3187 | - property int requestedWidth: -1 |
3188 | - property int requestedHeight: -1 |
3189 | - |
3190 | - onSurfaceChanged: { |
3191 | - if (surface) { |
3192 | - surfaceItem.surface = surface; |
3193 | - root.hadSurface = false; |
3194 | - } |
3195 | - } |
3196 | - |
3197 | - InputWatcher { |
3198 | - target: surfaceItem |
3199 | - onTargetPressedChanged: { |
3200 | - if (targetPressed && root.interactive) { |
3201 | - root.focus = true; |
3202 | - root.forceActiveFocus(); |
3203 | - } |
3204 | - } |
3205 | - } |
3206 | - |
3207 | - MirSurfaceItem { |
3208 | - id: surfaceItem |
3209 | - objectName: "surfaceItem" |
3210 | - |
3211 | - fillMode: MirSurfaceItem.PadOrCrop |
3212 | - consumesInput: true |
3213 | - |
3214 | - surfaceWidth: { |
3215 | - if (root.resizeSurface) { |
3216 | - if (root.requestedWidth >= 0) { |
3217 | - return root.requestedWidth; |
3218 | - } else { |
3219 | - return width; |
3220 | - } |
3221 | - } else { |
3222 | - return -1; |
3223 | - } |
3224 | - } |
3225 | - |
3226 | - surfaceHeight: { |
3227 | - if (root.resizeSurface) { |
3228 | - if (root.requestedHeight >= 0) { |
3229 | - return root.requestedHeight; |
3230 | - } else { |
3231 | - return height; |
3232 | - } |
3233 | - } else { |
3234 | - return -1; |
3235 | - } |
3236 | - } |
3237 | - |
3238 | - enabled: root.interactive |
3239 | - focus: true |
3240 | - antialiasing: !root.interactive |
3241 | - orientationAngle: root.surfaceOrientationAngle |
3242 | - } |
3243 | - |
3244 | - // MirSurface size drives SurfaceContainer size |
3245 | - Binding { |
3246 | - target: surfaceItem; property: "width"; value: root.surface ? root.surface.size.width : 0 |
3247 | - when: root.requestedWidth >= 0 && root.surface |
3248 | - } |
3249 | - Binding { |
3250 | - target: surfaceItem; property: "height"; value: root.surface ? root.surface.size.height : 0 |
3251 | - when: root.requestedHeight >= 0 && root.surface |
3252 | - } |
3253 | - Binding { |
3254 | - target: root; property: "width"; value: surfaceItem.width |
3255 | - when: root.requestedWidth >= 0 |
3256 | - } |
3257 | - Binding { |
3258 | - target: root; property: "height"; value: surfaceItem.height |
3259 | - when: root.requestedHeight >= 0 |
3260 | - } |
3261 | - |
3262 | - // SurfaceContainer size drives MirSurface size |
3263 | - Binding { |
3264 | - target: surfaceItem; property: "width"; value: root.width |
3265 | - when: root.requestedWidth < 0 |
3266 | - } |
3267 | - Binding { |
3268 | - target: surfaceItem; property: "height"; value: root.height |
3269 | - when: root.requestedHeight < 0 |
3270 | - } |
3271 | - |
3272 | - |
3273 | - TouchGate { |
3274 | - objectName: "touchGate-"+name |
3275 | - targetItem: surfaceItem |
3276 | - anchors.fill: root |
3277 | - enabled: surfaceItem.enabled |
3278 | - } |
3279 | - |
3280 | - states: [ |
3281 | - State { |
3282 | - name: "zombie" |
3283 | - when: surfaceItem.surface && !surfaceItem.live |
3284 | - } |
3285 | - ] |
3286 | - transitions: [ |
3287 | - Transition { |
3288 | - from: ""; to: "zombie" |
3289 | - SequentialAnimation { |
3290 | - UbuntuNumberAnimation { target: surfaceItem; property: "opacity"; to: 0.0 |
3291 | - duration: UbuntuAnimation.BriskDuration } |
3292 | - PropertyAction { target: surfaceItem; property: "visible"; value: false } |
3293 | - ScriptAction { script: { |
3294 | - surfaceItem.surface = null; |
3295 | - root.hadSurface = true; |
3296 | - } } |
3297 | - } |
3298 | - }, |
3299 | - Transition { |
3300 | - from: "zombie"; to: "" |
3301 | - ScriptAction { script: { |
3302 | - surfaceItem.opacity = 1.0; |
3303 | - surfaceItem.visible = true; |
3304 | - } } |
3305 | - } |
3306 | - ] |
3307 | -} |
3308 | |
3309 | === modified file 'qml/Stages/TabletStage.qml' |
3310 | --- qml/Stages/TabletStage.qml 2016-04-20 17:09:15 +0000 |
3311 | +++ qml/Stages/TabletStage.qml 2016-04-20 17:09:16 +0000 |
3312 | @@ -27,19 +27,24 @@ |
3313 | objectName: "stages" |
3314 | anchors.fill: parent |
3315 | |
3316 | + // <tutorial-hacks> The Tutorial looks into our implementation details |
3317 | property alias sideStageVisible: spreadView.sideStageVisible |
3318 | property alias sideStageWidth: spreadView.sideStageWidth |
3319 | + // The stage the currently focused surface is in |
3320 | + property int stageFocusedSurface: priv.focusedAppDelegate ? priv.focusedAppDelegate.stage : ApplicationInfoInterface.MainStage |
3321 | + // </tutorial-hacks> |
3322 | |
3323 | // Functions to be called from outside |
3324 | function updateFocusedAppOrientation() { |
3325 | - var mainStageAppIndex = priv.indexOf(priv.mainStageAppId); |
3326 | - if (mainStageAppIndex >= 0 && mainStageAppIndex < spreadRepeater.count) { |
3327 | - spreadRepeater.itemAt(mainStageAppIndex).matchShellOrientation(); |
3328 | + var mainStageIndex = root.topLevelSurfaceList.indexForId(priv.mainStageItemId); |
3329 | + |
3330 | + if (priv.mainStageItemId && mainStageIndex >= 0 && mainStageIndex < spreadRepeater.count) { |
3331 | + spreadRepeater.itemAt(mainStageIndex).matchShellOrientation(); |
3332 | } |
3333 | |
3334 | for (var i = 0; i < spreadRepeater.count; ++i) { |
3335 | |
3336 | - if (i === mainStageAppIndex) { |
3337 | + if (i === mainStageIndex) { |
3338 | continue; |
3339 | } |
3340 | |
3341 | @@ -60,16 +65,14 @@ |
3342 | } |
3343 | } |
3344 | function updateFocusedAppOrientationAnimated() { |
3345 | - var mainStageAppIndex = priv.indexOf(priv.mainStageAppId); |
3346 | - if (mainStageAppIndex >= 0 && mainStageAppIndex < spreadRepeater.count) { |
3347 | - spreadRepeater.itemAt(mainStageAppIndex).animateToShellOrientation(); |
3348 | + var mainStageIndex = root.topLevelSurfaceList.indexForId(priv.mainStageItemId); |
3349 | + if (priv.mainStageItemId && mainStageIndex >= 0 && mainStageIndex < spreadRepeater.count) { |
3350 | + spreadRepeater.itemAt(mainStageIndex).animateToShellOrientation(); |
3351 | } |
3352 | |
3353 | - if (priv.sideStageAppId) { |
3354 | - var sideStageAppIndex = priv.indexOf(priv.sideStageAppId); |
3355 | - if (sideStageAppIndex >= 0 && sideStageAppIndex < spreadRepeater.count) { |
3356 | - spreadRepeater.itemAt(sideStageAppIndex).matchShellOrientation(); |
3357 | - } |
3358 | + var sideStageIndex = root.topLevelSurfaceList.indexForId(priv.sideStageItemId); |
3359 | + if (sideStageIndex >= 0 && sideStageIndex < spreadRepeater.count) { |
3360 | + spreadRepeater.itemAt(sideStageIndex).matchShellOrientation(); |
3361 | } |
3362 | } |
3363 | |
3364 | @@ -81,11 +84,20 @@ |
3365 | |
3366 | orientationChangesEnabled: priv.mainAppOrientationChangesEnabled |
3367 | |
3368 | + mainApp: { |
3369 | + if (priv.mainStageItemId > 0) { |
3370 | + var index = root.topLevelSurfaceList.indexForId(priv.mainStageItemId); |
3371 | + return root.topLevelSurfaceList.applicationAt(index); |
3372 | + } else { |
3373 | + return null; |
3374 | + } |
3375 | + } |
3376 | + |
3377 | supportedOrientations: { |
3378 | if (mainApp) { |
3379 | var orientations = mainApp.supportedOrientations; |
3380 | orientations |= Qt.LandscapeOrientation | Qt.InvertedLandscapeOrientation; |
3381 | - if (priv.sideStageAppId && !spreadView.surfaceDragging) { |
3382 | + if (priv.sideStageItemId && !spreadView.surfaceDragging) { |
3383 | // If we have a sidestage app, support Portrait orientation |
3384 | // so that it will switch the sidestage app to mainstage on rotate |
3385 | orientations |= Qt.PortraitOrientation|Qt.InvertedPortraitOrientation; |
3386 | @@ -116,7 +128,7 @@ |
3387 | if (inverseProgress == 0 && priv.oldInverseProgress > 0) { |
3388 | // left edge drag released. Minimum distance is given by design. |
3389 | if (priv.oldInverseProgress > units.gu(22)) { |
3390 | - ApplicationManager.requestFocusApplication("unity8-dash"); |
3391 | + root.applicationManager.requestFocusApplication("unity8-dash"); |
3392 | } |
3393 | } |
3394 | priv.oldInverseProgress = inverseProgress; |
3395 | @@ -154,17 +166,51 @@ |
3396 | } |
3397 | } |
3398 | |
3399 | + Connections { |
3400 | + target: root.topLevelSurfaceList |
3401 | + onListChanged: priv.updateMainAndSideStageIndexes() |
3402 | + } |
3403 | + |
3404 | QtObject { |
3405 | id: priv |
3406 | objectName: "stagesPriv" |
3407 | |
3408 | - property string focusedAppId: ApplicationManager.focusedApplicationId |
3409 | - readonly property var focusedAppDelegate: { |
3410 | - var index = indexOf(focusedAppId); |
3411 | - return index >= 0 && index < spreadRepeater.count ? spreadRepeater.itemAt(index) : null |
3412 | + function updateMainAndSideStageIndexes() { |
3413 | + var choseMainStage = false; |
3414 | + var choseSideStage = false; |
3415 | + |
3416 | + if (!root.topLevelSurfaceList) |
3417 | + return; |
3418 | + |
3419 | + for (var i = 0; i < spreadRepeater.count && (!choseMainStage || !choseSideStage); ++i) { |
3420 | + var spreadDelegate = spreadRepeater.itemAt(i); |
3421 | + if (sideStage.shown && spreadDelegate.stage == ApplicationInfoInterface.SideStage |
3422 | + && !choseSideStage) { |
3423 | + priv.sideStageDelegate = spreadDelegate |
3424 | + priv.sideStageItemId = root.topLevelSurfaceList.idAt(i); |
3425 | + priv.sideStageAppId = root.topLevelSurfaceList.applicationAt(i).appId; |
3426 | + choseSideStage = true; |
3427 | + } else if (!choseMainStage && spreadDelegate.stage == ApplicationInfoInterface.MainStage) { |
3428 | + priv.mainStageDelegate = spreadDelegate; |
3429 | + priv.mainStageItemId = root.topLevelSurfaceList.idAt(i); |
3430 | + priv.mainStageAppId = root.topLevelSurfaceList.applicationAt(i).appId; |
3431 | + choseMainStage = true; |
3432 | + } |
3433 | + } |
3434 | + if (!choseMainStage) { |
3435 | + priv.mainStageDelegate = null; |
3436 | + priv.mainStageItemId = 0; |
3437 | + priv.mainStageAppId = ""; |
3438 | + } |
3439 | + if (!choseSideStage) { |
3440 | + priv.sideStageDelegate = null; |
3441 | + priv.sideStageItemId = 0; |
3442 | + priv.sideStageAppId = ""; |
3443 | + } |
3444 | } |
3445 | |
3446 | - property string oldFocusedAppId: "" |
3447 | + property var focusedAppDelegate: null |
3448 | + |
3449 | property bool mainAppOrientationChangesEnabled: false |
3450 | |
3451 | property real landscapeHeight: root.orientations.native_ == Qt.LandscapeOrientation ? |
3452 | @@ -173,36 +219,21 @@ |
3453 | property bool shellIsLandscape: root.shellOrientation === Qt.LandscapeOrientation |
3454 | || root.shellOrientation === Qt.InvertedLandscapeOrientation |
3455 | |
3456 | - property string mainStageAppId |
3457 | - property string sideStageAppId |
3458 | - |
3459 | - // For convenience, keep properties of the first two apps in the model |
3460 | - property string appId0 |
3461 | - property string appId1 |
3462 | + property var mainStageDelegate: null |
3463 | + property var sideStageDelegate: null |
3464 | + |
3465 | + property int mainStageItemId: 0 |
3466 | + property int sideStageItemId: 0 |
3467 | + |
3468 | + property string mainStageAppId: "" |
3469 | + property string sideStageAppId: "" |
3470 | |
3471 | property int oldInverseProgress: 0 |
3472 | |
3473 | property int highlightIndex: 0 |
3474 | |
3475 | - onFocusedAppIdChanged: updateStageApps() |
3476 | - |
3477 | - onFocusedAppDelegateChanged: { |
3478 | - if (focusedAppDelegate) { |
3479 | - focusedAppDelegate.focus = true; |
3480 | - } |
3481 | - } |
3482 | - |
3483 | property bool focusedAppDelegateIsDislocated: focusedAppDelegate && |
3484 | (focusedAppDelegate.dragOffset !== 0 || focusedAppDelegate.xTranslateAnimating) |
3485 | - function indexOf(appId) { |
3486 | - for (var i = 0; i < ApplicationManager.count; i++) { |
3487 | - if (ApplicationManager.get(i).appId == appId) { |
3488 | - return i; |
3489 | - } |
3490 | - } |
3491 | - return -1; |
3492 | - } |
3493 | - |
3494 | function evaluateOneWayFlick(gesturePoints) { |
3495 | // Need to have at least 3 points to recognize it as a flick |
3496 | if (gesturePoints.length < 3) { |
3497 | @@ -231,88 +262,45 @@ |
3498 | spreadView.contentX = highlightIndex * spreadView.contentWidth / (spreadRepeater.count + 2) |
3499 | } |
3500 | |
3501 | - function getTopApp(stage) { |
3502 | - for (var i = 0; i < ApplicationManager.count; i++) { |
3503 | - var app = ApplicationManager.get(i) |
3504 | - if (app.stage === stage) { |
3505 | - return app; |
3506 | - } |
3507 | - } |
3508 | - return null; |
3509 | - } |
3510 | - |
3511 | - function setAppStage(appId, stage, save) { |
3512 | - var app = ApplicationManager.findApplication(appId); |
3513 | - if (app) { |
3514 | - app.stage = stage; |
3515 | - if (save) { |
3516 | - WindowStateStorage.saveStage(appId, stage); |
3517 | - } |
3518 | - } |
3519 | - } |
3520 | - |
3521 | - function updateStageApps() { |
3522 | - var app = priv.getTopApp(ApplicationInfoInterface.MainStage); |
3523 | - priv.mainStageAppId = app ? app.appId : "" |
3524 | - root.mainApp = app; |
3525 | - |
3526 | - if (sideStage.shown) { |
3527 | - app = priv.getTopApp(ApplicationInfoInterface.SideStage); |
3528 | - priv.sideStageAppId = app ? app.appId : "" |
3529 | - } else { |
3530 | - priv.sideStageAppId = ""; |
3531 | - } |
3532 | - |
3533 | - appId0 = ApplicationManager.count >= 1 ? ApplicationManager.get(0).appId : ""; |
3534 | - appId1 = ApplicationManager.count > 1 ? ApplicationManager.get(1).appId : ""; |
3535 | - } |
3536 | - |
3537 | readonly property bool sideStageEnabled: root.shellOrientation == Qt.LandscapeOrientation || |
3538 | root.shellOrientation == Qt.InvertedLandscapeOrientation |
3539 | - Component.onCompleted: updateStageApps(); |
3540 | - } |
3541 | - |
3542 | - Connections { |
3543 | - target: ApplicationManager |
3544 | - onFocusRequested: { |
3545 | - if (spreadView.interactive) { |
3546 | - spreadView.snapTo(priv.indexOf(appId)); |
3547 | - } else { |
3548 | - ApplicationManager.focusApplication(appId); |
3549 | - } |
3550 | - } |
3551 | - |
3552 | - onApplicationAdded: { |
3553 | - if (spreadView.phase == 2) { |
3554 | - spreadView.snapTo(ApplicationManager.count - 1); |
3555 | - } else { |
3556 | - spreadView.phase = 0; |
3557 | - spreadView.contentX = -spreadView.shift; |
3558 | - ApplicationManager.focusApplication(appId); |
3559 | - } |
3560 | - } |
3561 | - |
3562 | - onApplicationRemoved: { |
3563 | - if (priv.mainStageAppId == appId) { |
3564 | - ApplicationManager.focusApplication("unity8-dash") |
3565 | - } |
3566 | - if (priv.sideStageAppId == appId) { |
3567 | - var app = priv.getTopApp(ApplicationInfoInterface.SideStage); |
3568 | - priv.sideStageAppId = app === null ? "" : app.appId; |
3569 | - } |
3570 | - |
3571 | - if (ApplicationManager.count == 0) { |
3572 | - spreadView.phase = 0; |
3573 | - spreadView.contentX = -spreadView.shift; |
3574 | - } else if (spreadView.closingIndex == -1) { |
3575 | - // Unless we're closing the app ourselves in the spread, |
3576 | - // lets make sure the spread doesn't mess up by the changing app list. |
3577 | - spreadView.phase = 0; |
3578 | - spreadView.contentX = -spreadView.shift; |
3579 | - |
3580 | - ApplicationManager.focusApplication(ApplicationManager.get(0).appId); |
3581 | - } |
3582 | - } |
3583 | + } |
3584 | + |
3585 | + Instantiator { |
3586 | + model: root.applicationManager |
3587 | + delegate: QtObject { |
3588 | + property var stateBinding: Binding { |
3589 | + readonly property bool isDash: model.application ? model.application.appId == "unity8-dash" : false |
3590 | + target: model.application |
3591 | + property: "requestedState" |
3592 | + |
3593 | + // NB: the first application clause is just to ensure we never get warnings for trying to access |
3594 | + // members of a null variable. |
3595 | + value: model.application && |
3596 | + ( |
3597 | + (isDash && root.keepDashRunning) |
3598 | + || (!root.suspended && (model.application.appId === priv.mainStageAppId |
3599 | + || model.application.appId === priv.sideStageAppId)) |
3600 | + ) |
3601 | + ? ApplicationInfoInterface.RequestedRunning |
3602 | + : ApplicationInfoInterface.RequestedSuspended |
3603 | + } |
3604 | + |
3605 | + property var lifecycleBinding: Binding { |
3606 | + target: model.application |
3607 | + property: "exemptFromLifecycle" |
3608 | + value: model.application |
3609 | + ? (!model.application.isTouchApp || isExemptFromLifecycle(model.application.appId)) |
3610 | + : false |
3611 | + } |
3612 | + } |
3613 | + } |
3614 | + |
3615 | + Binding { |
3616 | + target: MirFocusController |
3617 | + property: "focusedSurface" |
3618 | + value: priv.focusedAppDelegate ? priv.focusedAppDelegate.surface : null |
3619 | + when: root.parent && !spreadRepeater.startingUp |
3620 | } |
3621 | |
3622 | Flickable { |
3623 | @@ -363,16 +351,45 @@ |
3624 | property int selectedIndex: -1 |
3625 | property int draggedDelegateCount: 0 |
3626 | property int closingIndex: -1 |
3627 | - property var selectedApplication: selectedIndex !== -1 ? ApplicationManager.get(selectedIndex) : null |
3628 | + property var selectedDelegate: selectedIndex !== -1 ? spreadRepeater.itemAt(selectedIndex) : null |
3629 | |
3630 | - // FIXME: Workaround Flickable's not keepping its contentX still when resized |
3631 | - onContentXChanged: { forceItToRemainStillIfBeingResized(); } |
3632 | + // <FIXME-contentX> Workaround Flickable's behavior of bringing contentX back between valid boundaries |
3633 | + // when resized. The proper way to fix this is refactoring PhoneStage so that it doesn't |
3634 | + // rely on having Flickable.contentX keeping an out-of-bounds value when it's set programatically |
3635 | + // (as opposed to having contentX reaching an out-of-bounds value through dragging, which will trigger |
3636 | + // the Flickable.boundsBehavior upon release). |
3637 | + onContentXChanged: { |
3638 | + if (!undoContentXReset()) { |
3639 | + forceItToRemainStillIfBeingResized(); |
3640 | + } |
3641 | + } |
3642 | onShiftChanged: { forceItToRemainStillIfBeingResized(); } |
3643 | function forceItToRemainStillIfBeingResized() { |
3644 | - if (root.beingResized && contentX != -shift) { |
3645 | + if (root.beingResized && contentX != -spreadView.shift) { |
3646 | + contentX = -spreadView.shift; |
3647 | + } |
3648 | + } |
3649 | + function undoContentXReset() { |
3650 | + if (contentWidth <= 0) { |
3651 | + contentWidthOnLastContentXChange = contentWidth; |
3652 | + lastContentX = contentX; |
3653 | + return false; |
3654 | + } |
3655 | + |
3656 | + if (contentWidth != contentWidthOnLastContentXChange |
3657 | + && lastContentX == -shift && contentX == 0) { |
3658 | + // Flickable is resetting contentX because contentWidth has changed. Undo it. |
3659 | contentX = -shift; |
3660 | + return true; |
3661 | } |
3662 | + |
3663 | + contentWidthOnLastContentXChange = contentWidth; |
3664 | + lastContentX = contentX; |
3665 | + return false; |
3666 | } |
3667 | + property real contentWidthOnLastContentXChange: -1 |
3668 | + property real lastContentX: 0 |
3669 | + // </FIXME-contentX> |
3670 | |
3671 | property bool animateX: true |
3672 | property bool beingResized: root.beingResized |
3673 | @@ -385,31 +402,33 @@ |
3674 | } |
3675 | } |
3676 | |
3677 | - property real sideStageDragProgress: sideStage.progress |
3678 | - property bool sideStageVisible: priv.sideStageAppId |
3679 | property real sideStageWidth: units.gu(40) |
3680 | |
3681 | property bool surfaceDragging: triGestureArea.recognisedDrag |
3682 | |
3683 | - // In case the ApplicationManager already holds an app when starting up we're missing animations |
3684 | + readonly property bool sideStageVisible: priv.sideStageItemId != 0 |
3685 | + |
3686 | + // In case applicationManager already holds an app when starting up we're missing animations |
3687 | // Make sure we end up in the same state |
3688 | Component.onCompleted: { |
3689 | spreadView.contentX = -spreadView.shift |
3690 | } |
3691 | |
3692 | property int nextInStack: { |
3693 | + var mainStageIndex = priv.mainStageDelegate ? priv.mainStageDelegate.index : -1; |
3694 | + var sideStageIndex = priv.sideStageDelegate ? priv.sideStageDelegate.index : -1; |
3695 | switch (state) { |
3696 | case "main": |
3697 | - if (ApplicationManager.count > 1) { |
3698 | + if (root.topLevelSurfaceList.count > 1) { |
3699 | return 1; |
3700 | } |
3701 | return -1; |
3702 | case "mainAndOverlay": |
3703 | - if (ApplicationManager.count <= 2) { |
3704 | + if (root.topLevelSurfaceList.count <= 2) { |
3705 | return -1; |
3706 | } |
3707 | - if (priv.appId0 == priv.mainStageAppId || priv.appId0 == priv.sideStageAppId) { |
3708 | - if (priv.appId1 == priv.mainStageAppId || priv.appId1 == priv.sideStageAppId) { |
3709 | + if (mainStageIndex == 0 || sideStageIndex == 0) { |
3710 | + if (mainStageIndex == 1 || sideStageIndex == 1) { |
3711 | return 2; |
3712 | } |
3713 | return 1; |
3714 | @@ -420,7 +439,7 @@ |
3715 | } |
3716 | return -1; |
3717 | } |
3718 | - property int nextZInStack: indexToZIndex(nextInStack) |
3719 | + property int nextZInStack |
3720 | |
3721 | states: [ |
3722 | State { |
3723 | @@ -440,13 +459,13 @@ |
3724 | } |
3725 | ] |
3726 | state: { |
3727 | - if ((priv.mainStageAppId && !priv.sideStageAppId) || !priv.sideStageEnabled) { |
3728 | + if ((priv.mainStageItemId && !priv.sideStageItemId) || !priv.sideStageEnabled) { |
3729 | return "main"; |
3730 | } |
3731 | - if (!priv.mainStageAppId && priv.sideStageAppId) { |
3732 | + if (!priv.mainStageItemId && priv.sideStageItemId) { |
3733 | return "overlay"; |
3734 | } |
3735 | - if (priv.mainStageAppId && priv.sideStageAppId) { |
3736 | + if (priv.mainStageItemId && priv.sideStageItemId) { |
3737 | return "mainAndOverlay"; |
3738 | } |
3739 | return "empty"; |
3740 | @@ -511,38 +530,41 @@ |
3741 | // only shuffle when we've got a main and overlay |
3742 | if (state !== "mainAndOverlay") return index; |
3743 | |
3744 | - var app = ApplicationManager.get(index); |
3745 | + var app = root.topLevelSurfaceList.applicationAt(index); |
3746 | if (!app) { |
3747 | return index; |
3748 | } |
3749 | + var stage = spreadRepeater.itemAt(index) ? spreadRepeater.itemAt(index).stage : app.stage; |
3750 | |
3751 | // don't shuffle indexes greater than "actives or next" |
3752 | if (index > 2) return index; |
3753 | |
3754 | - if (app.appId === priv.mainStageAppId) { |
3755 | + var mainStageIndex = root.topLevelSurfaceList.indexForId(priv.mainStageItemId); |
3756 | + |
3757 | + if (index == mainStageIndex) { |
3758 | // Active main stage always at 0 |
3759 | return 0; |
3760 | } |
3761 | |
3762 | if (spreadView.nextInStack > 0) { |
3763 | - var nextAppInStack = ApplicationManager.get(spreadView.nextInStack); |
3764 | + var stageOfNextInStack = spreadRepeater.itemAt(spreadView.nextInStack).stage; |
3765 | |
3766 | if (index === spreadView.nextInStack) { |
3767 | // this is the next app in stack. |
3768 | |
3769 | - if (app.stage === ApplicationInfoInterface.SideStage) { |
3770 | + if (stage === ApplicationInfoInterface.SideStage) { |
3771 | // if the next app in stack is a sidestage app, it must order on top of other side stage app |
3772 | - return Math.min(2, ApplicationManager.count-1); |
3773 | + return Math.min(2, root.topLevelSurfaceList.count-1); |
3774 | } |
3775 | return 1; |
3776 | } |
3777 | - if (nextAppInStack.stage === ApplicationInfoInterface.SideStage) { |
3778 | + if (stageOfNextInStack === ApplicationInfoInterface.SideStage) { |
3779 | // if the next app in stack is a sidestage app, it must order on top of other side stage app |
3780 | return 1; |
3781 | } |
3782 | - return Math.min(2, ApplicationManager.count-1); |
3783 | + return Math.min(2, root.topLevelSurfaceList.count-1); |
3784 | } |
3785 | - return Math.min(index+1, ApplicationManager.count-1); |
3786 | + return Math.min(index+1, root.topLevelSurfaceList.count-1); |
3787 | } |
3788 | |
3789 | SequentialAnimation { |
3790 | @@ -560,12 +582,13 @@ |
3791 | script: { |
3792 | if (spreadView.selectedIndex >= 0) { |
3793 | var newIndex = spreadView.selectedIndex; |
3794 | - var application = ApplicationManager.get(newIndex); |
3795 | - if (application.stage === ApplicationInfoInterface.SideStage) { |
3796 | + var application = root.topLevelSurfaceList.applicationAt(newIndex); |
3797 | + var spreadDelegate = spreadRepeater.itemAt(newIndex); |
3798 | + if (spreadDelegate.stage === ApplicationInfoInterface.SideStage) { |
3799 | sideStage.showNow(); |
3800 | } |
3801 | spreadView.selectedIndex = -1; |
3802 | - ApplicationManager.focusApplication(application.appId); |
3803 | + spreadDelegate.focus = true; |
3804 | spreadView.phase = 0; |
3805 | spreadView.contentX = -spreadView.shift; |
3806 | } |
3807 | @@ -581,7 +604,7 @@ |
3808 | MouseArea { |
3809 | id: spreadRow |
3810 | x: spreadView.contentX |
3811 | - width: spreadView.width + Math.max(spreadView.width, ApplicationManager.count * spreadView.tileDistance) |
3812 | + width: spreadView.width + Math.max(spreadView.width, root.topLevelSurfaceList.count * spreadView.tileDistance) |
3813 | height: root.height |
3814 | |
3815 | onClicked: { |
3816 | @@ -599,8 +622,8 @@ |
3817 | enabled: priv.sideStageEnabled |
3818 | |
3819 | onDropped: { |
3820 | - priv.setAppStage(drag.source.appId, ApplicationInfoInterface.MainStage, true); |
3821 | - ApplicationManager.focusApplication(drag.source.appId); |
3822 | + drop.source.spreadDelegate.stage = ApplicationInfoInterface.MainStage; |
3823 | + drop.source.spreadDelegate.focus = true; |
3824 | } |
3825 | keys: "SideStage" |
3826 | } |
3827 | @@ -611,12 +634,12 @@ |
3828 | height: priv.landscapeHeight |
3829 | x: spreadView.width - width |
3830 | z: { |
3831 | - if (!priv.mainStageAppId) return 0; |
3832 | - |
3833 | - if (priv.sideStageAppId && spreadView.nextInStack > 0) { |
3834 | - var nextAppInStack = ApplicationManager.get(spreadView.nextInStack); |
3835 | - |
3836 | - if (nextAppInStack.stage === ApplicationInfoInterface.MainStage) { |
3837 | + if (!priv.mainStageItemId) return 0; |
3838 | + |
3839 | + if (priv.sideStageItemId && spreadView.nextInStack > 0) { |
3840 | + var nextDelegateInStack = spreadRepeater.itemAt(spreadView.nextInStack); |
3841 | + |
3842 | + if (nextDelegateInStack.stage === ApplicationInfoInterface.MainStage) { |
3843 | // if the next app in stack is a main stage app, put the sidestage on top of it. |
3844 | return 2; |
3845 | } |
3846 | @@ -631,12 +654,11 @@ |
3847 | Behavior on opacity { UbuntuNumberAnimation {} } |
3848 | |
3849 | onShownChanged: { |
3850 | - if (!shown && ApplicationManager.focusedApplicationId == priv.sideStageAppId) { |
3851 | - ApplicationManager.requestFocusApplication(priv.mainStageAppId); |
3852 | - } |
3853 | - priv.updateStageApps(); |
3854 | - if (shown && priv.sideStageAppId) { |
3855 | - ApplicationManager.requestFocusApplication(priv.sideStageAppId); |
3856 | + if (!shown && priv.sideStageDelegate && priv.focusedAppDelegate === priv.sideStageDelegate |
3857 | + && priv.mainStageDelegate) { |
3858 | + priv.mainStageDelegate.focus = true; |
3859 | + } else if (shown && priv.sideStageDelegate) { |
3860 | + priv.sideStageDelegate.focus = true; |
3861 | } |
3862 | } |
3863 | |
3864 | @@ -655,8 +677,8 @@ |
3865 | } |
3866 | onDropped: { |
3867 | if (drop.keys == "MainStage") { |
3868 | - priv.setAppStage(drop.source.appId, ApplicationInfoInterface.SideStage, true); |
3869 | - ApplicationManager.requestFocusApplication(drop.source.appId); |
3870 | + drop.source.spreadDelegate.stage = ApplicationInfoInterface.SideStage; |
3871 | + drop.source.spreadDelegate.focus = true; |
3872 | } |
3873 | } |
3874 | drag { |
3875 | @@ -669,41 +691,102 @@ |
3876 | } |
3877 | } |
3878 | |
3879 | - Repeater { |
3880 | + TopLevelSurfaceRepeater { |
3881 | id: spreadRepeater |
3882 | objectName: "spreadRepeater" |
3883 | - model: ApplicationManager |
3884 | + model: root.topLevelSurfaceList |
3885 | + |
3886 | + onItemAdded: { |
3887 | + priv.updateMainAndSideStageIndexes(); |
3888 | + if (spreadView.phase == 2) { |
3889 | + spreadView.snapTo(index); |
3890 | + } |
3891 | + } |
3892 | + |
3893 | + onItemRemoved: { |
3894 | + priv.updateMainAndSideStageIndexes(); |
3895 | + // Unless we're closing the app ourselves in the spread, |
3896 | + // lets make sure the spread doesn't mess up by the changing app list. |
3897 | + if (spreadView.closingIndex == -1) { |
3898 | + spreadView.phase = 0; |
3899 | + spreadView.contentX = -spreadView.shift; |
3900 | + focusTopMostApp(); |
3901 | + } |
3902 | + } |
3903 | + function focusTopMostApp() { |
3904 | + if (spreadRepeater.count > 0) { |
3905 | + var topmostDelegate = spreadRepeater.itemAt(0); |
3906 | + topmostDelegate.focus = true; |
3907 | + } |
3908 | + } |
3909 | |
3910 | delegate: TransformedTabletSpreadDelegate { |
3911 | id: spreadTile |
3912 | - objectName: model.appId ? "tabletSpreadDelegate_" + model.appId |
3913 | - : "tabletSpreadDelegate_null"; |
3914 | + objectName: "spreadDelegate_" + model.id |
3915 | + |
3916 | + readonly property int index: model.index |
3917 | width: spreadView.width |
3918 | height: spreadView.height |
3919 | - active: appId == priv.mainStageAppId || appId == priv.sideStageAppId |
3920 | + active: model.id == priv.mainStageItemId || model.id == priv.sideStageItemId |
3921 | zIndex: selected && stage == ApplicationInfoInterface.MainStage ? 0 : spreadView.indexToZIndex(index) |
3922 | + onZIndexChanged: { |
3923 | + if (spreadView.nextInStack == model.index) { |
3924 | + spreadView.nextZInStack = zIndex; |
3925 | + } |
3926 | + } |
3927 | selected: spreadView.selectedIndex == index |
3928 | otherSelected: spreadView.selectedIndex >= 0 && !selected |
3929 | - isInSideStage: priv.sideStageAppId === appId |
3930 | + isInSideStage: priv.sideStageItemId == model.id |
3931 | interactive: !spreadView.interactive && spreadView.phase === 0 && root.interactive |
3932 | swipeToCloseEnabled: spreadView.interactive && !snapAnimation.running |
3933 | maximizedAppTopMargin: root.maximizedAppTopMargin |
3934 | - dragOffset: !isDash && appId == priv.mainStageAppId && root.inverseProgress > 0 && spreadView.phase === 0 ? root.inverseProgress : 0 |
3935 | - application: ApplicationManager.get(index) |
3936 | + dragOffset: !isDash && model.id == priv.mainStageItemId && root.inverseProgress > 0 |
3937 | + && spreadView.phase === 0 ? root.inverseProgress : 0 |
3938 | + application: model.application |
3939 | + surface: model.surface |
3940 | closeable: !isDash |
3941 | highlightShown: root.altTabPressed && priv.highlightIndex == zIndex |
3942 | |
3943 | - readonly property bool wantsMainStage: model.stage == ApplicationInfoInterface.MainStage |
3944 | - |
3945 | - readonly property string appId: model.appId |
3946 | - readonly property bool isDash: model.appId == "unity8-dash" |
3947 | - |
3948 | - stage: model.stage |
3949 | + readonly property bool wantsMainStage: stage == ApplicationInfoInterface.MainStage |
3950 | + |
3951 | + readonly property bool isDash: model.application.appId == "unity8-dash" |
3952 | + |
3953 | + onFocusChanged: { |
3954 | + if (focus && !spreadRepeater.startingUp) { |
3955 | + priv.focusedAppDelegate = spreadTile; |
3956 | + root.topLevelSurfaceList.raiseId(model.id); |
3957 | + } |
3958 | + if (focus && priv.sideStageEnabled && stage === ApplicationInfoInterface.SideStage) { |
3959 | + sideStage.show(); |
3960 | + } |
3961 | + } |
3962 | + Connections { |
3963 | + target: model.surface |
3964 | + onFocusRequested: spreadTile.focus = true; |
3965 | + } |
3966 | + Connections { |
3967 | + target: model.application |
3968 | + onFocusRequested: { |
3969 | + if (!model.surface) { |
3970 | + // when an app has no surfaces, we assume there's only one entry representing it: |
3971 | + // this delegate. |
3972 | + spreadTile.focus = true; |
3973 | + } else { |
3974 | + // if the application has surfaces, focus request should be at surface-level. |
3975 | + } |
3976 | + } |
3977 | + } |
3978 | + |
3979 | fullscreen: { |
3980 | - if (mainApp && stage === ApplicationInfoInterface.SideStage) { |
3981 | - return mainApp.fullscreen; |
3982 | + if (priv.mainStageDelegate && stage === ApplicationInfoInterface.SideStage) { |
3983 | + return priv.mainStageDelegate.fullscreen; |
3984 | + } else if (surface) { |
3985 | + return surface.state === Mir.FullscreenState; |
3986 | + } else if (application) { |
3987 | + return application.fullscreen; |
3988 | + } else { |
3989 | + return false; |
3990 | } |
3991 | - return application ? application.fullscreen : false; |
3992 | } |
3993 | |
3994 | supportedOrientations: { |
3995 | @@ -724,23 +807,6 @@ |
3996 | } |
3997 | } |
3998 | |
3999 | - |
4000 | - Binding { |
4001 | - target: spreadTile.application |
4002 | - property: "exemptFromLifecycle" |
4003 | - value: !model.isTouchApp || isExemptFromLifecycle(model.appId) |
4004 | - } |
4005 | - |
4006 | - Binding { |
4007 | - target: spreadTile.application |
4008 | - property: "requestedState" |
4009 | - value: (isDash && root.keepDashRunning) |
4010 | - || (!root.suspended && (model.appId == priv.mainStageAppId |
4011 | - || model.appId == priv.sideStageAppId)) |
4012 | - ? ApplicationInfoInterface.RequestedRunning |
4013 | - : ApplicationInfoInterface.RequestedSuspended |
4014 | - } |
4015 | - |
4016 | // FIXME: A regular binding doesn't update any more after closing an app. |
4017 | // Using a Binding for now. |
4018 | Binding { |
4019 | @@ -760,34 +826,33 @@ |
4020 | onSideStageEnabledChanged: refreshStage() |
4021 | } |
4022 | |
4023 | + property bool _constructing: true; |
4024 | + onStageChanged: { |
4025 | + if (!_constructing) { |
4026 | + priv.updateMainAndSideStageIndexes(); |
4027 | + } |
4028 | + } |
4029 | + |
4030 | Component.onCompleted: { |
4031 | - refreshStage() |
4032 | - stageChanged.connect(priv.updateStageApps); |
4033 | + // a top level window is always the focused one when it first appears, unfocusing |
4034 | + // any preexisting one |
4035 | + focus = true; |
4036 | + refreshStage(); |
4037 | + _constructing = false; |
4038 | + } |
4039 | + Component.onDestruction: { |
4040 | + WindowStateStorage.saveStage(model.application.appId, stage); |
4041 | } |
4042 | |
4043 | function refreshStage() { |
4044 | - var stage = ApplicationInfoInterface.MainStage; |
4045 | + var newStage = ApplicationInfoInterface.MainStage; |
4046 | if (priv.sideStageEnabled) { |
4047 | if (application && application.supportedOrientations & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) { |
4048 | - stage = WindowStateStorage.getStage(appId); |
4049 | + newStage = WindowStateStorage.getStage(application.appId); |
4050 | } |
4051 | } |
4052 | |
4053 | - if (model.stage !== stage) { |
4054 | - priv.setAppStage(appId, stage, false); |
4055 | - } |
4056 | - } |
4057 | - |
4058 | - // This is required because none of the bindings are triggered in some cases: |
4059 | - // When an app is closed, it might happen that ApplicationManager.get(nextInStack) |
4060 | - // returns a different app even though the nextInStackIndex and all the related |
4061 | - // bindings (index, mainStageApp, sideStageApp, etc) don't change. Let's force a |
4062 | - // binding update in that case. |
4063 | - Connections { |
4064 | - target: ApplicationManager |
4065 | - onApplicationRemoved: spreadTile.z = Qt.binding(function() { |
4066 | - return spreadView.indexToZIndex(index); |
4067 | - }) |
4068 | + stage = newStage; |
4069 | } |
4070 | |
4071 | progress: { |
4072 | @@ -873,13 +938,10 @@ |
4073 | script: { |
4074 | // rotate immediately. |
4075 | spreadTile.matchShellOrientation(); |
4076 | - if (ApplicationManager.focusedApplicationId === spreadTile.appId && |
4077 | + if (priv.focusedAppDelegate === spreadTile && |
4078 | priv.sideStageEnabled && !sideStage.shown) { |
4079 | // Sidestage was focused, so show the side stage. |
4080 | sideStage.show(); |
4081 | - // if we've switched to a main app which doesnt support portrait, hide the side stage. |
4082 | - } else if (mainApp && (mainApp.supportedOrientations & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) == 0) { |
4083 | - sideStage.hideNow(); |
4084 | } |
4085 | } |
4086 | } |
4087 | @@ -890,10 +952,10 @@ |
4088 | SequentialAnimation { |
4089 | ScriptAction { |
4090 | script: { |
4091 | - if (priv.sideStageAppId === spreadTile.appId && |
4092 | + if (priv.sideStageDelegate === spreadTile && |
4093 | mainApp && (mainApp.supportedOrientations & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) == 0) { |
4094 | // The mainstage app did not natively support portrait orientation, so focus the sidestage. |
4095 | - ApplicationManager.requestFocusApplication(spreadTile.appId); |
4096 | + spreadTile.focus = true; |
4097 | } |
4098 | } |
4099 | } |
4100 | @@ -922,28 +984,26 @@ |
4101 | |
4102 | onClosed: { |
4103 | spreadView.closingIndex = index; |
4104 | - ApplicationManager.stopApplication(ApplicationManager.get(index).appId); |
4105 | - } |
4106 | - |
4107 | - onFocusChanged: { |
4108 | - if (focus && ApplicationManager.focusedApplicationId !== appId) { |
4109 | - ApplicationManager.focusApplication(appId); |
4110 | - } |
4111 | - |
4112 | - if (focus && priv.sideStageEnabled && stage === ApplicationInfoInterface.SideStage) { |
4113 | - sideStage.show(); |
4114 | + if (spreadTile.surface) { |
4115 | + spreadTile.surface.close(); |
4116 | + } else if (spreadTile.application) { |
4117 | + root.applicationManager.stopApplication(spreadTile.application.appId); |
4118 | + } else { |
4119 | + // should never happen |
4120 | + console.warn("Can't close topLevelSurfaceList entry as it has neither" |
4121 | + + " a surface nor an application"); |
4122 | } |
4123 | } |
4124 | |
4125 | Binding { |
4126 | target: root |
4127 | - when: model.appId == priv.mainStageAppId |
4128 | + when: model.id == priv.mainStageItemId |
4129 | property: "mainAppWindowOrientationAngle" |
4130 | value: appWindowOrientationAngle |
4131 | } |
4132 | Binding { |
4133 | target: priv |
4134 | - when: model.appId == priv.mainStageAppId |
4135 | + when: model.id == priv.mainStageItemId |
4136 | property: "mainAppOrientationChangesEnabled" |
4137 | value: orientationChangesEnabled |
4138 | } |
4139 | @@ -957,7 +1017,7 @@ |
4140 | |
4141 | StagedFullscreenPolicy { |
4142 | id: fullscreenPolicy |
4143 | - application: spreadTile.application |
4144 | + surface: model.surface |
4145 | } |
4146 | Connections { |
4147 | target: root |
4148 | @@ -973,17 +1033,19 @@ |
4149 | anchors.fill: parent |
4150 | enabled: priv.sideStageEnabled && !spreadView.active |
4151 | property var dragObject: null |
4152 | - property string appId: "" |
4153 | + |
4154 | + property Item spreadDelegate |
4155 | + |
4156 | dragComponent: dragComponent |
4157 | - dragComponentProperties: { "appId": appId } |
4158 | + dragComponentProperties: { "spreadDelegate": spreadDelegate } |
4159 | |
4160 | onPressed: { |
4161 | - function matchDelegate(obj) { return String(obj.objectName).indexOf("tabletSpreadDelegate") >= 0; } |
4162 | + function matchDelegate(obj) { return String(obj.objectName).indexOf("spreadDelegate") >= 0; } |
4163 | |
4164 | var delegateAtCenter = Functions.itemAt(spreadRow, x, y, matchDelegate); |
4165 | if (!delegateAtCenter) return; |
4166 | |
4167 | - appId = delegateAtCenter.appId; |
4168 | + spreadDelegate = delegateAtCenter; |
4169 | } |
4170 | |
4171 | onClicked: { |
4172 | @@ -1003,11 +1065,11 @@ |
4173 | |
4174 | Component { |
4175 | id: dragComponent |
4176 | - SessionContainer { |
4177 | - property string appId: "" |
4178 | - property var application: ApplicationManager.findApplication(appId) |
4179 | - |
4180 | - session: application ? application.session : null |
4181 | + SurfaceContainer { |
4182 | + property Item spreadDelegate |
4183 | + |
4184 | + surface: spreadDelegate ? spreadDelegate.surface : null |
4185 | + |
4186 | interactive: false |
4187 | resizeSurface: false |
4188 | focus: false |
4189 | @@ -1019,10 +1081,11 @@ |
4190 | Drag.hotSpot.y: height/2 |
4191 | // only accept opposite stage. |
4192 | Drag.keys: { |
4193 | - if (!application) return "Disabled"; |
4194 | + if (!surface) return "Disabled"; |
4195 | |
4196 | - if (application.stage === ApplicationInfo.MainStage) { |
4197 | - if (application.supportedOrientations & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) { |
4198 | + if (spreadDelegate.stage === ApplicationInfo.MainStage) { |
4199 | + if (spreadDelegate.application.supportedOrientations |
4200 | + & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) { |
4201 | return "MainStage"; |
4202 | } |
4203 | return "Disabled"; |
4204 | |
4205 | === added file 'qml/Stages/TopLevelSurfaceRepeater.qml' |
4206 | --- qml/Stages/TopLevelSurfaceRepeater.qml 1970-01-01 00:00:00 +0000 |
4207 | +++ qml/Stages/TopLevelSurfaceRepeater.qml 2016-04-20 17:09:16 +0000 |
4208 | @@ -0,0 +1,52 @@ |
4209 | +/* |
4210 | + * Copyright (C) 2016 Canonical, Ltd. |
4211 | + * |
4212 | + * This program is free software; you can redistribute it and/or modify |
4213 | + * it under the terms of the GNU General Public License as published by |
4214 | + * the Free Software Foundation; version 3. |
4215 | + * |
4216 | + * This program is distributed in the hope that it will be useful, |
4217 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4218 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4219 | + * GNU General Public License for more details. |
4220 | + * |
4221 | + * You should have received a copy of the GNU General Public License |
4222 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4223 | + */ |
4224 | + |
4225 | +import QtQuick 2.4 |
4226 | + |
4227 | +Repeater { |
4228 | + id: root |
4229 | + // FIXME: This is a hack around us not knowing whether the Repeater has finished creating its |
4230 | + // delegates on start up. |
4231 | + // This is a problem when the stage gets a TopLevelSurfaceList already populated with several |
4232 | + // rows. |
4233 | + property bool startingUp: true |
4234 | + onStartingUpChanged: { |
4235 | + if (!startingUp) { |
4236 | + // the top-most surface must be the focused one. |
4237 | + var topmostDelegate = itemAt(0); |
4238 | + topmostDelegate.focus = true; |
4239 | + } |
4240 | + } |
4241 | + |
4242 | + onItemAdded: { |
4243 | + if (startingUp) { |
4244 | + checkIfStillStartingUp(); |
4245 | + } |
4246 | + } |
4247 | + |
4248 | + function checkIfStillStartingUp() { |
4249 | + var i = 0; |
4250 | + var missingDelegate = false; |
4251 | + for (i = 0; i < model.count && !missingDelegate; ++i) { |
4252 | + if (!itemAt(i)) { |
4253 | + missingDelegate = true; |
4254 | + } |
4255 | + } |
4256 | + if (!missingDelegate) { |
4257 | + startingUp = false; |
4258 | + } |
4259 | + } |
4260 | +} |
4261 | |
4262 | === modified file 'qml/Stages/TransformedSpreadDelegate.qml' |
4263 | --- qml/Stages/TransformedSpreadDelegate.qml 2015-07-15 15:07:19 +0000 |
4264 | +++ qml/Stages/TransformedSpreadDelegate.qml 2016-04-20 17:09:16 +0000 |
4265 | @@ -1,5 +1,5 @@ |
4266 | /* |
4267 | - * Copyright 2014 Canonical Ltd. |
4268 | + * Copyright 2014,2016 Canonical Ltd. |
4269 | * |
4270 | * This program is free software; you can redistribute it and/or modify |
4271 | * it under the terms of the GNU Lesser General Public License as published by |
4272 | @@ -312,7 +312,7 @@ |
4273 | // non-fullscreen window when they're stacked on top of each other on the |
4274 | // far left of the spread. |
4275 | Translate { |
4276 | - y: !fullscreen && appWindowRotation === 180 |
4277 | + y: !root.fullscreen && appWindowRotation === 180 |
4278 | ? priv.topMarginProgress * maximizedAppTopMargin |
4279 | : 0 |
4280 | }, |
4281 | @@ -326,7 +326,7 @@ |
4282 | switch (appWindowRotation) { |
4283 | case 90: |
4284 | case 270: |
4285 | - if (fullscreen) { |
4286 | + if (root.fullscreen) { |
4287 | return 1; |
4288 | } else { |
4289 | return 1 + priv.topMarginProgress * maximizedAppTopMargin / spreadView.width; |
4290 | @@ -340,7 +340,7 @@ |
4291 | yScale: { |
4292 | switch (appWindowRotation) { |
4293 | case 0: |
4294 | - if (fullscreen) { |
4295 | + if (root.fullscreen) { |
4296 | return 1 - priv.topMarginProgress * maximizedAppTopMargin / spreadView.height; |
4297 | } else { |
4298 | return 1; |
4299 | |
4300 | === modified file 'qml/Stages/TransformedTabletSpreadDelegate.qml' |
4301 | --- qml/Stages/TransformedTabletSpreadDelegate.qml 2016-03-11 20:18:12 +0000 |
4302 | +++ qml/Stages/TransformedTabletSpreadDelegate.qml 2016-04-20 17:09:16 +0000 |
4303 | @@ -31,7 +31,6 @@ |
4304 | // Set this to true when this tile a currently active on either the MS or the SS. |
4305 | property bool active: false |
4306 | |
4307 | - property int stage |
4308 | property int zIndex |
4309 | property real progress: 0 |
4310 | property real animatedProgress: 0 |
4311 | @@ -48,13 +47,15 @@ |
4312 | |
4313 | property bool isInSideStage: false |
4314 | |
4315 | + property int stage: ApplicationInfoInterface.MainStage |
4316 | + |
4317 | property int dragOffset: 0 |
4318 | readonly property alias xTranslateAnimating: xTranslateAnimation.running |
4319 | readonly property bool offScreen: priv.xTranslate >= 0 |
4320 | |
4321 | dropShadow: spreadView.active || |
4322 | (active |
4323 | - && (stage == ApplicationInfoInterface.MainStage || !priv.shellIsLandscape) |
4324 | + && (root.stage == ApplicationInfoInterface.MainStage || !priv.shellIsLandscape) |
4325 | && priv.xTranslate != 0) |
4326 | |
4327 | onSelectedChanged: { |
4328 | @@ -90,7 +91,7 @@ |
4329 | id: priv |
4330 | |
4331 | // true if this is the next tile on the stack that comes in when dragging from the right |
4332 | - property bool nextInStack: spreadView.nextZInStack == zIndex |
4333 | + property bool nextInStack: spreadView.nextInStack == model.index |
4334 | // true if the next tile in the stack is the nextInStack one. This one will be moved a bit to the left |
4335 | property bool movedActive: spreadView.nextZInStack == zIndex + 1 |
4336 | property real animatedEndDistance: linearAnimation(0, 2, root.endDistance, 0, root.progress) |
4337 | @@ -142,7 +143,7 @@ |
4338 | Behavior on xTranslate { |
4339 | enabled: !spreadView.active && |
4340 | !snapAnimation.running && |
4341 | - model.appId !== "unity8-dash" && |
4342 | + root.application.appId !== "unity8-dash" && |
4343 | spreadView.animateX && |
4344 | !spreadView.beingResized && |
4345 | priv.state !== "sideStage" |
4346 | @@ -156,8 +157,9 @@ |
4347 | var newTranslate = 0; |
4348 | |
4349 | // selected app or opposite stage active app. |
4350 | - if (isSelected || (otherSelected && root.active && spreadView.selectedApplication && spreadView.selectedApplication.stage !== stage)) { |
4351 | - if (stage == ApplicationInfoInterface.MainStage) { |
4352 | + if (isSelected || (otherSelected && root.active && spreadView.selectedDelegate |
4353 | + && spreadView.selectedDelegate.stage !== root.stage)) { |
4354 | + if (root.stage == ApplicationInfoInterface.MainStage) { |
4355 | return linearAnimation(selectedProgress, negativeProgress, selectedXTranslate, -spreadView.width, root.progress); |
4356 | } else { |
4357 | return linearAnimation(selectedProgress, negativeProgress, selectedXTranslate, -spreadView.sideStageWidth, root.progress); |
4358 | @@ -168,8 +170,8 @@ |
4359 | |
4360 | // 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 |
4361 | // when we're only dragging the side stage in on top of a main stage app |
4362 | - var shouldMoveAway = spreadView.nextInStack >= 0 && priv.movedActive && stage === ApplicationInfoInterface.MainStage && |
4363 | - ApplicationManager.get(spreadView.nextInStack).stage === ApplicationInfoInterface.MainStage; |
4364 | + var shouldMoveAway = spreadView.nextInStack >= 0 && priv.movedActive && root.stage === ApplicationInfoInterface.MainStage && |
4365 | + topLevelSurfaceList.applicationAt(spreadView.nextInStack).stage === ApplicationInfoInterface.MainStage; |
4366 | |
4367 | if (active) { |
4368 | newTranslate -= root.width |
4369 | @@ -178,7 +180,7 @@ |
4370 | newTranslate += linearAnimation(0, spreadView.positionMarker2, 0, -units.gu(4), root.animatedProgress); |
4371 | } |
4372 | newTranslate += root.dragOffset; |
4373 | - } else if (!spreadView.active && model.appId == "unity8-dash") { |
4374 | + } else if (!spreadView.active && root.application.appId == "unity8-dash") { |
4375 | newTranslate -= root.width; |
4376 | } |
4377 | |
4378 | @@ -190,14 +192,14 @@ |
4379 | |
4380 | if (spreadView.phase == 0) { |
4381 | if (nextInStack) { |
4382 | - if (stage == ApplicationInfoInterface.MainStage) { |
4383 | + if (root.stage == ApplicationInfoInterface.MainStage) { |
4384 | if (spreadView.sideStageVisible && root.progress > 0) { |
4385 | // Move it so it appears from behind the side stage immediately |
4386 | newTranslate += -spreadView.sideStageWidth; |
4387 | } |
4388 | } |
4389 | |
4390 | - if (stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) { |
4391 | + if (root.stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) { |
4392 | // This is when we only drag the side stage in, without rotation or snapping |
4393 | newTranslate = linearAnimation(0, spreadView.positionMarker2, 0, -spreadView.sideStageWidth, root.animatedProgress); |
4394 | } else { |
4395 | @@ -210,7 +212,7 @@ |
4396 | |
4397 | if (spreadView.phase == 1) { |
4398 | if (nextInStack) { |
4399 | - if (stage == ApplicationInfoInterface.MainStage) { |
4400 | + if (root.stage == ApplicationInfoInterface.MainStage) { |
4401 | var startValue = -spreadView.sideStageWidth * spreadView.snapPosition + (spreadView.sideStageVisible ? -spreadView.sideStageWidth : 0); |
4402 | newTranslate += linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, startValue, priv.phase2StartTranslate, root.animatedProgress); |
4403 | } else { |
4404 | @@ -250,7 +252,8 @@ |
4405 | } |
4406 | |
4407 | // selected app or opposite stage active app. |
4408 | - if (isSelected || (otherSelected && root.active && spreadView.selectedApplication && spreadView.selectedApplication.stage !== stage)) { |
4409 | + if (isSelected || (otherSelected && root.active && spreadView.selectedDelegate |
4410 | + && spreadView.selectedDelegate.stage !== root.stage)) { |
4411 | return linearAnimation(selectedProgress, negativeProgress, selectedScale, 1, root.progress); |
4412 | } else if (otherSelected) { |
4413 | return selectedScale; |
4414 | @@ -258,7 +261,7 @@ |
4415 | |
4416 | if (spreadView.phase == 0) { |
4417 | if (nextInStack) { |
4418 | - if (stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) { |
4419 | + if (root.stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) { |
4420 | return 1; |
4421 | } else { |
4422 | var targetScale = root.dragStartScale - ((root.dragStartScale - 1) * spreadView.snapPosition); |
4423 | @@ -272,7 +275,7 @@ |
4424 | if (spreadView.phase == 1) { |
4425 | if (nextInStack) { |
4426 | var startScale = 1; |
4427 | - if (stage !== ApplicationInfoInterface.SideStage || spreadView.sideStageVisible) { |
4428 | + if (root.stage !== ApplicationInfoInterface.SideStage || spreadView.sideStageVisible) { |
4429 | startScale = root.dragStartScale - ((root.dragStartScale - 1) * spreadView.snapPosition); |
4430 | } |
4431 | return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, startScale, priv.phase2StartScale, root.animatedProgress); |
4432 | @@ -295,7 +298,8 @@ |
4433 | } |
4434 | |
4435 | // selected app or opposite stage active app. |
4436 | - if (isSelected || (otherSelected && root.active && spreadView.selectedApplication && spreadView.selectedApplication.stage !== stage)) { |
4437 | + if (isSelected || (otherSelected && root.active && spreadView.selectedDelegate |
4438 | + && spreadView.selectedDelegate.stage !== root.stage)) { |
4439 | return linearAnimation(selectedProgress, negativeProgress, selectedAngle, 0, root.progress); |
4440 | } else if (otherSelected) { |
4441 | return selectedAngle; |
4442 | @@ -304,12 +308,12 @@ |
4443 | // The tile should rotate a bit when another one comes on top, but not when only dragging the side stage in |
4444 | var shouldMoveAway = spreadView.nextInStack == -1 || |
4445 | spreadView.nextInStack >= 0 && priv.movedActive && |
4446 | - (ApplicationManager.get(spreadView.nextInStack).stage === ApplicationInfoInterface.MainStage || |
4447 | - stage == ApplicationInfoInterface.SideStage); |
4448 | + (topLevelSurfaceList.applicationAt(spreadView.nextInStack).stage === ApplicationInfoInterface.MainStage || |
4449 | + root.stage == ApplicationInfoInterface.SideStage); |
4450 | |
4451 | if (spreadView.phase == 0) { |
4452 | if (nextInStack) { |
4453 | - if (stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) { |
4454 | + if (root.stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) { |
4455 | return 0; |
4456 | } else { |
4457 | return linearAnimation(0, spreadView.positionMarker2, root.startAngle, root.startAngle * (1-spreadView.snapPosition), root.animatedProgress); |
4458 | @@ -321,7 +325,7 @@ |
4459 | } |
4460 | if (spreadView.phase == 1) { |
4461 | if (nextInStack) { |
4462 | - if (stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) { |
4463 | + if (root.stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) { |
4464 | return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, 0, priv.phase2StartAngle, root.animatedProgress); |
4465 | } else { |
4466 | return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, root.startAngle * (1-spreadView.snapPosition), priv.phase2StartAngle, root.animatedProgress); |
4467 | @@ -345,7 +349,8 @@ |
4468 | if (root.isSelected) return 1; |
4469 | |
4470 | if (otherSelected) { |
4471 | - if (root.active && spreadView.selectedApplication && spreadView.selectedApplication.stage !== stage) { |
4472 | + if (root.active && spreadView.selectedDelegate |
4473 | + && spreadView.selectedDelegate.stage !== root.stage) { |
4474 | return 1; |
4475 | } |
4476 | return linearAnimation(selectedProgress, negativeProgress, selectedOpacity, 0, root.progress); |
4477 | @@ -355,7 +360,8 @@ |
4478 | |
4479 | property real topMarginProgress: { |
4480 | // selected app or opposite stage active app. |
4481 | - if (isSelected || (otherSelected && root.active && spreadView.selectedApplication && spreadView.selectedApplication.stage !== stage)) { |
4482 | + if (isSelected || (otherSelected && root.active && spreadView.selectedDelegate |
4483 | + && spreadView.selectedDelegate.stage !== root.stage)) { |
4484 | return linearAnimation(selectedProgress, negativeProgress, selectedTopMarginProgress, 0, root.progress); |
4485 | } |
4486 | |
4487 | @@ -379,7 +385,7 @@ |
4488 | when: root.isInSideStage && spreadView.shiftedContentX == 0 && spreadView.phase == 0 |
4489 | PropertyChanges { |
4490 | target: priv; |
4491 | - xTranslate: -spreadView.sideStageWidth + spreadView.sideStageWidth * (1-spreadView.sideStageDragProgress) |
4492 | + xTranslate: -spreadView.sideStageWidth + spreadView.sideStageWidth * (1-sideStage.progress) |
4493 | } |
4494 | } |
4495 | ] |
4496 | |
4497 | === modified file 'qml/Stages/WindowedFullscreenPolicy.qml' |
4498 | --- qml/Stages/WindowedFullscreenPolicy.qml 2016-03-15 19:38:03 +0000 |
4499 | +++ qml/Stages/WindowedFullscreenPolicy.qml 2016-04-20 17:09:16 +0000 |
4500 | @@ -23,19 +23,17 @@ |
4501 | // state of the window is returned to restored. |
4502 | QtObject { |
4503 | property bool active: true |
4504 | - property QtObject application: null |
4505 | + property QtObject surface: null |
4506 | |
4507 | - readonly property var lastSurface: application && application.session ? |
4508 | - application.session.lastSurface : null |
4509 | property bool _firstTimeSurface: true |
4510 | |
4511 | - onLastSurfaceChanged: { |
4512 | - if (!active || !lastSurface) return; |
4513 | + onSurfaceChanged: { |
4514 | + if (!active || !surface) return; |
4515 | if (!_firstTimeSurface) return; |
4516 | _firstTimeSurface = false; |
4517 | |
4518 | - if (lastSurface.state === Mir.FullscreenState && lastSurface.shellChrome === Mir.LowChrome) { |
4519 | - lastSurface.state = Mir.RestoredState; |
4520 | + if (surface.state === Mir.FullscreenState && surface.shellChrome === Mir.LowChrome) { |
4521 | + surface.state = Mir.RestoredState; |
4522 | } |
4523 | } |
4524 | } |
4525 | |
4526 | === modified file 'qml/Tutorial/TutorialBottom.qml' |
4527 | --- qml/Tutorial/TutorialBottom.qml 2016-03-16 15:08:49 +0000 |
4528 | +++ qml/Tutorial/TutorialBottom.qml 2016-04-20 17:09:16 +0000 |
4529 | @@ -47,7 +47,7 @@ |
4530 | readonly property real sideStageWidth: root.usageScenario === "tablet" && stage.sideStageVisible ? |
4531 | stage.sideStageWidth : 0 |
4532 | readonly property bool isMainStageApp: usageScenario !== "tablet" || |
4533 | - application.stage === ApplicationInfoInterface.MainStage |
4534 | + stage.stageFocusedSurface === ApplicationInfoInterface.MainStage |
4535 | readonly property real dragAreaHeight: units.gu(3) // based on PageWithBottomEdge.qml |
4536 | readonly property real targetDistance: height * 0.2 + dragAreaHeight // based on PageWithBottomEdge.qml |
4537 | |
4538 | |
4539 | === modified file 'tests/mocks/Unity/Application/ApplicationInfo.cpp' |
4540 | --- tests/mocks/Unity/Application/ApplicationInfo.cpp 2016-03-16 12:29:44 +0000 |
4541 | +++ tests/mocks/Unity/Application/ApplicationInfo.cpp 2016-04-20 17:09:16 +0000 |
4542 | @@ -16,8 +16,7 @@ |
4543 | |
4544 | #include "ApplicationInfo.h" |
4545 | #include "MirSurface.h" |
4546 | -#include "Session.h" |
4547 | -#include "SessionManager.h" |
4548 | +#include "SurfaceManager.h" |
4549 | |
4550 | #include <paths.h> |
4551 | |
4552 | @@ -25,97 +24,114 @@ |
4553 | #include <QQuickItem> |
4554 | #include <QQuickView> |
4555 | #include <QQmlComponent> |
4556 | -#include <QTimer> |
4557 | + |
4558 | +#define APPLICATION_DEBUG 0 |
4559 | + |
4560 | +#if APPLICATION_DEBUG |
4561 | +#define DEBUG_MSG(params) qDebug().nospace() << "Application["<<appId()<<"]::" << __func__ << " " << params |
4562 | + |
4563 | +QString stateToStr(ApplicationInfo::State state) |
4564 | +{ |
4565 | + switch (state) { |
4566 | + case ApplicationInfo::Starting: |
4567 | + return "starting"; |
4568 | + case ApplicationInfo::Running: |
4569 | + return "running"; |
4570 | + case ApplicationInfo::Suspended: |
4571 | + return "suspended"; |
4572 | + case ApplicationInfo::Stopped: |
4573 | + return "stopped"; |
4574 | + default: |
4575 | + return "???"; |
4576 | + }; |
4577 | +} |
4578 | + |
4579 | +#else |
4580 | +#define DEBUG_MSG(params) ((void)0) |
4581 | +#endif |
4582 | + |
4583 | +#define WARNING_MSG(params) qWarning().nospace() << "Application["<<appId()<<"]::" << __func__ << " " << params |
4584 | |
4585 | ApplicationInfo::ApplicationInfo(const QString &appId, QObject *parent) |
4586 | : ApplicationInfoInterface(appId, parent) |
4587 | , m_appId(appId) |
4588 | - , m_stage(MainStage) |
4589 | - , m_state(Stopped) |
4590 | - , m_focused(false) |
4591 | - , m_fullscreen(false) |
4592 | - , m_session(0) |
4593 | - , m_supportedOrientations(Qt::PortraitOrientation | |
4594 | - Qt::LandscapeOrientation | |
4595 | - Qt::InvertedPortraitOrientation | |
4596 | - Qt::InvertedLandscapeOrientation) |
4597 | - , m_rotatesWindowContents(false) |
4598 | - , m_requestedState(RequestedRunning) |
4599 | - , m_isTouchApp(true) |
4600 | - , m_exemptFromLifecycle(false) |
4601 | - , m_manualSurfaceCreation(false) |
4602 | - , m_shellChrome(Mir::NormalChrome) |
4603 | { |
4604 | + connect(&m_surfaceList, &MirSurfaceListModel::countChanged, |
4605 | + this, &ApplicationInfo::onSurfaceCountChanged, Qt::QueuedConnection); |
4606 | + |
4607 | + m_surfaceCreationTimer.setSingleShot(true); |
4608 | + m_surfaceCreationTimer.setInterval(500); |
4609 | + connect(&m_surfaceCreationTimer, &QTimer::timeout, this, &ApplicationInfo::createSurface); |
4610 | } |
4611 | |
4612 | ApplicationInfo::ApplicationInfo(QObject *parent) |
4613 | - : ApplicationInfoInterface(QString(), parent) |
4614 | - , m_stage(MainStage) |
4615 | - , m_state(Stopped) |
4616 | - , m_focused(false) |
4617 | - , m_fullscreen(false) |
4618 | - , m_session(0) |
4619 | - , m_supportedOrientations(Qt::PortraitOrientation | |
4620 | - Qt::LandscapeOrientation | |
4621 | - Qt::InvertedPortraitOrientation | |
4622 | - Qt::InvertedLandscapeOrientation) |
4623 | - , m_rotatesWindowContents(false) |
4624 | - , m_requestedState(RequestedRunning) |
4625 | - , m_isTouchApp(true) |
4626 | - , m_exemptFromLifecycle(false) |
4627 | - , m_manualSurfaceCreation(false) |
4628 | - , m_shellChrome(Mir::NormalChrome) |
4629 | + : ApplicationInfo(QString(), parent) |
4630 | { |
4631 | } |
4632 | |
4633 | ApplicationInfo::~ApplicationInfo() |
4634 | { |
4635 | - delete m_session; |
4636 | -} |
4637 | - |
4638 | -void ApplicationInfo::createSession() |
4639 | -{ |
4640 | - if (m_session || state() == ApplicationInfo::Stopped) { return; } |
4641 | - |
4642 | - setSession(SessionManager::singleton()->createSession(appId(), m_screenshotFileName)); |
4643 | -} |
4644 | - |
4645 | -void ApplicationInfo::destroySession() |
4646 | -{ |
4647 | - Session *session = this->session(); |
4648 | - setSession(nullptr); |
4649 | - delete session; |
4650 | -} |
4651 | - |
4652 | -void ApplicationInfo::setSession(Session* session) |
4653 | -{ |
4654 | - if (m_session == session) |
4655 | +} |
4656 | + |
4657 | +void ApplicationInfo::createSurface() |
4658 | +{ |
4659 | + if (state() == ApplicationInfo::Stopped) { return; } |
4660 | + |
4661 | + QString surfaceName = name(); |
4662 | + if (m_surfaceList.count() > 0) { |
4663 | + surfaceName.append(QString(" %1").arg(m_surfaceList.count()+1)); |
4664 | + } |
4665 | + |
4666 | + auto surfaceManager = SurfaceManager::instance(); |
4667 | + if (!surfaceManager) { |
4668 | + WARNING_MSG("No SurfaceManager"); |
4669 | return; |
4670 | - |
4671 | - if (m_session) { |
4672 | - disconnect(this, 0, m_session, 0); |
4673 | - m_session->setApplication(nullptr); |
4674 | - m_session->setParent(nullptr); |
4675 | - Q_EMIT m_session->deregister(); |
4676 | - } |
4677 | - |
4678 | - m_session = session; |
4679 | - |
4680 | - if (m_session) { |
4681 | - m_session->setApplication(this); |
4682 | - m_session->setParent(this); |
4683 | - m_session->setFullscreen(m_fullscreen); |
4684 | - SessionManager::singleton()->registerSession(m_session); |
4685 | - connect(m_session, &Session::surfaceAdded, |
4686 | - this, &ApplicationInfo::onSessionSurfaceAdded); |
4687 | - connect(m_session, &Session::fullscreenChanged, this, &ApplicationInfo::fullscreenChanged); |
4688 | - |
4689 | - if (!m_manualSurfaceCreation) { |
4690 | - QTimer::singleShot(500, m_session, &Session::createSurface); |
4691 | - } |
4692 | - } |
4693 | - |
4694 | - Q_EMIT sessionChanged(m_session); |
4695 | + } |
4696 | + |
4697 | + auto surface = surfaceManager->createSurface(surfaceName, |
4698 | + Mir::NormalType, |
4699 | + fullscreen() ? Mir::FullscreenState : Mir::MaximizedState, |
4700 | + m_screenshotFileName); |
4701 | + |
4702 | + surface->setShellChrome(m_shellChrome); |
4703 | + |
4704 | + m_surfaceList.appendSurface(surface); |
4705 | + |
4706 | + ++m_liveSurfaceCount; |
4707 | + connect(surface, &MirSurface::liveChanged, this, [this, surface](){ |
4708 | + if (!surface->live()) { |
4709 | + --m_liveSurfaceCount; |
4710 | + if (m_liveSurfaceCount == 0) { |
4711 | + if (m_closingSurfaces.contains(surface) |
4712 | + || (m_state == Running && m_requestedState == RequestedRunning)) { |
4713 | + Q_EMIT closed(); |
4714 | + } |
4715 | + setState(Stopped); |
4716 | + } else { |
4717 | + if (m_closingSurfaces.contains(surface) && m_requestedState == RequestedSuspended |
4718 | + && m_closingSurfaces.count() == 1) { |
4719 | + setState(Suspended); |
4720 | + } |
4721 | + } |
4722 | + m_closingSurfaces.removeAll(surface); |
4723 | + } |
4724 | + }); |
4725 | + connect(surface, &MirSurface::closeRequested, this, [this, surface](){ |
4726 | + m_closingSurfaces.append(surface); |
4727 | + if (m_state == Suspended) { |
4728 | + // resume to allow application to close its surface |
4729 | + setState(Running); |
4730 | + } |
4731 | + }); |
4732 | + connect(surface, &MirSurface::focusRequested, this, &ApplicationInfo::focusRequested); |
4733 | + |
4734 | + if (m_state == Starting) { |
4735 | + if (m_requestedState == RequestedRunning) { |
4736 | + setState(Running); |
4737 | + } else { |
4738 | + setState(Suspended); |
4739 | + } |
4740 | + } |
4741 | } |
4742 | |
4743 | void ApplicationInfo::setIconId(const QString &iconId) |
4744 | @@ -138,10 +154,6 @@ |
4745 | |
4746 | if (screenshotFileName != m_screenshotFileName) { |
4747 | m_screenshotFileName = screenshotFileName; |
4748 | - |
4749 | - if (m_session) { |
4750 | - m_session->setScreenshot(screenshotFileName); |
4751 | - } |
4752 | } |
4753 | } |
4754 | |
4755 | @@ -172,38 +184,53 @@ |
4756 | void ApplicationInfo::setState(State value) |
4757 | { |
4758 | if (value != m_state) { |
4759 | + DEBUG_MSG(qPrintable(stateToStr(value))); |
4760 | + if (!m_manualSurfaceCreation && value == ApplicationInfo::Starting) { |
4761 | + Q_ASSERT(m_surfaceList.count() == 0); |
4762 | + m_surfaceCreationTimer.start(); |
4763 | + } else if (value == ApplicationInfo::Stopped) { |
4764 | + m_surfaceCreationTimer.stop(); |
4765 | + for (int i = 0; i < m_surfaceList.count(); ++i) { |
4766 | + MirSurface *surface = static_cast<MirSurface*>(m_surfaceList.get(i)); |
4767 | + surface->setLive(false); |
4768 | + } |
4769 | + } |
4770 | + |
4771 | m_state = value; |
4772 | Q_EMIT stateChanged(value); |
4773 | - |
4774 | - if (!m_manualSurfaceCreation && !m_session && m_state == ApplicationInfo::Starting) { |
4775 | - QTimer::singleShot(500, this, &ApplicationInfo::createSession); |
4776 | - } else if (m_state == ApplicationInfo::Stopped) { |
4777 | - Session *session = m_session; |
4778 | - setSession(nullptr); |
4779 | - delete session; |
4780 | + } |
4781 | +} |
4782 | + |
4783 | +void ApplicationInfo::close() |
4784 | +{ |
4785 | + DEBUG_MSG(""); |
4786 | + |
4787 | + if (m_surfaceList.count() > 0) { |
4788 | + for (int i = 0; i < m_surfaceList.count(); ++i) { |
4789 | + MirSurface *surface = static_cast<MirSurface*>(m_surfaceList.get(i)); |
4790 | + surface->close(); |
4791 | } |
4792 | - } |
4793 | -} |
4794 | - |
4795 | -void ApplicationInfo::setFocused(bool value) |
4796 | -{ |
4797 | - if (value != m_focused) { |
4798 | - m_focused = value; |
4799 | - Q_EMIT focusedChanged(value); |
4800 | + } else { |
4801 | + setState(Stopped); |
4802 | + Q_EMIT closed(); |
4803 | } |
4804 | } |
4805 | |
4806 | void ApplicationInfo::setFullscreen(bool value) |
4807 | { |
4808 | m_fullscreen = value; |
4809 | - if (m_session) { |
4810 | - m_session->setFullscreen(value); |
4811 | + if (m_surfaceList.rowCount() > 0) { |
4812 | + m_surfaceList.get(0)->setState(Mir::FullscreenState); |
4813 | } |
4814 | } |
4815 | |
4816 | bool ApplicationInfo::fullscreen() const |
4817 | { |
4818 | - return m_session ? m_session->fullscreen() : m_fullscreen; |
4819 | + if (m_surfaceList.rowCount() > 0) { |
4820 | + return m_surfaceList.get(0)->state() == Mir::FullscreenState; |
4821 | + } else { |
4822 | + return m_fullscreen; |
4823 | + } |
4824 | } |
4825 | |
4826 | void ApplicationInfo::setManualSurfaceCreation(bool value) |
4827 | @@ -241,15 +268,28 @@ |
4828 | |
4829 | void ApplicationInfo::setRequestedState(RequestedState value) |
4830 | { |
4831 | - if (m_requestedState != value) { |
4832 | - m_requestedState = value; |
4833 | - Q_EMIT requestedStateChanged(m_requestedState); |
4834 | - |
4835 | - if (m_requestedState == RequestedRunning && m_state == Suspended) { |
4836 | + if (m_requestedState == value) { |
4837 | + return; |
4838 | + } |
4839 | + DEBUG_MSG((value == RequestedRunning ? "RequestedRunning" : "RequestedSuspended") ); |
4840 | + |
4841 | + m_requestedState = value; |
4842 | + Q_EMIT requestedStateChanged(m_requestedState); |
4843 | + |
4844 | + if (m_requestedState == RequestedRunning) { |
4845 | + |
4846 | + if (m_state == Suspended) { |
4847 | + Q_ASSERT(m_liveSurfaceCount > 0); |
4848 | setState(Running); |
4849 | - } else if (m_requestedState == RequestedSuspended && m_state == Running) { |
4850 | - setState(Suspended); |
4851 | + } else if (m_state == Stopped) { |
4852 | + Q_ASSERT(m_liveSurfaceCount == 0); |
4853 | + // it's restarting |
4854 | + setState(Starting); |
4855 | } |
4856 | + |
4857 | + } else if (m_requestedState == RequestedSuspended && m_state == Running |
4858 | + && m_closingSurfaces.isEmpty()) { |
4859 | + setState(Suspended); |
4860 | } |
4861 | } |
4862 | |
4863 | @@ -263,18 +303,6 @@ |
4864 | m_isTouchApp = isTouchApp; |
4865 | } |
4866 | |
4867 | -void ApplicationInfo::onSessionSurfaceAdded(MirSurface* surface) |
4868 | -{ |
4869 | - if (surface != nullptr && m_state == Starting) { |
4870 | - if (m_requestedState == RequestedRunning) { |
4871 | - setState(Running); |
4872 | - } else { |
4873 | - setState(Suspended); |
4874 | - } |
4875 | - surface->setShellChrome(m_shellChrome); |
4876 | - } |
4877 | -} |
4878 | - |
4879 | bool ApplicationInfo::exemptFromLifecycle() const |
4880 | { |
4881 | return m_exemptFromLifecycle; |
4882 | @@ -305,7 +333,52 @@ |
4883 | void ApplicationInfo::setShellChrome(Mir::ShellChrome shellChrome) |
4884 | { |
4885 | m_shellChrome = shellChrome; |
4886 | - if (m_session && m_session->lastSurface()) { |
4887 | - m_session->lastSurface()->setShellChrome(shellChrome); |
4888 | + if (m_surfaceList.rowCount() > 0) { |
4889 | + static_cast<MirSurface*>(m_surfaceList.get(0))->setShellChrome(shellChrome); |
4890 | + } |
4891 | +} |
4892 | + |
4893 | +bool ApplicationInfo::focused() const |
4894 | +{ |
4895 | + bool someSurfaceHasFocus = false; // to be proven wrong |
4896 | + for (int i = 0; i < m_surfaceList.count() && !someSurfaceHasFocus; ++i) { |
4897 | + someSurfaceHasFocus = m_surfaceList.get(i)->focused(); |
4898 | + } |
4899 | + return someSurfaceHasFocus; |
4900 | +} |
4901 | + |
4902 | +void ApplicationInfo::setFocused(bool value) |
4903 | +{ |
4904 | + if (focused() == value) { |
4905 | + return; |
4906 | + } |
4907 | + |
4908 | + if (value) { |
4909 | + if (m_surfaceList.count() > 0) { |
4910 | + m_surfaceList.get(0)->requestFocus(); |
4911 | + } |
4912 | + } else { |
4913 | + for (int i = 0; i < m_surfaceList.count(); ++i) { |
4914 | + MirSurface *surface = static_cast<MirSurface*>(m_surfaceList.get(i)); |
4915 | + if (surface->focused()) { |
4916 | + surface->setFocused(false); |
4917 | + } |
4918 | + } |
4919 | + } |
4920 | +} |
4921 | + |
4922 | +void ApplicationInfo::onSurfaceCountChanged() |
4923 | +{ |
4924 | + if (m_surfaceList.count() == 0 && m_state == Running) { |
4925 | + setState(Stopped); |
4926 | + } |
4927 | +} |
4928 | + |
4929 | +void ApplicationInfo::requestFocus() |
4930 | +{ |
4931 | + if (m_surfaceList.count() == 0) { |
4932 | + Q_EMIT focusRequested(); |
4933 | + } else { |
4934 | + m_surfaceList.get(0)->requestFocus(); |
4935 | } |
4936 | } |
4937 | |
4938 | === modified file 'tests/mocks/Unity/Application/ApplicationInfo.h' |
4939 | --- tests/mocks/Unity/Application/ApplicationInfo.h 2016-03-15 20:59:07 +0000 |
4940 | +++ tests/mocks/Unity/Application/ApplicationInfo.h 2016-04-20 17:09:16 +0000 |
4941 | @@ -20,25 +20,32 @@ |
4942 | #include <QObject> |
4943 | |
4944 | class MirSurface; |
4945 | -class Session; |
4946 | |
4947 | // unity-api |
4948 | #include <unity/shell/application/ApplicationInfoInterface.h> |
4949 | #include <unity/shell/application/Mir.h> |
4950 | |
4951 | +#include "MirSurfaceListModel.h" |
4952 | + |
4953 | +#include <QList> |
4954 | +#include <QTimer> |
4955 | + |
4956 | using namespace unity::shell::application; |
4957 | |
4958 | class ApplicationInfo : public ApplicationInfoInterface { |
4959 | Q_OBJECT |
4960 | |
4961 | + //// |
4962 | + // FIXME: Remove those |
4963 | Q_PROPERTY(bool fullscreen READ fullscreen WRITE setFullscreen NOTIFY fullscreenChanged) |
4964 | - Q_PROPERTY(Session* session READ session NOTIFY sessionChanged) |
4965 | |
4966 | // Only exists in this fake implementation |
4967 | |
4968 | // whether the test code will explicitly control the creation of the application surface |
4969 | Q_PROPERTY(bool manualSurfaceCreation READ manualSurfaceCreation WRITE setManualSurfaceCreation NOTIFY manualSurfaceCreationChanged) |
4970 | |
4971 | + Q_PROPERTY(QString screenshot READ screenshot CONSTANT) |
4972 | + |
4973 | public: |
4974 | ApplicationInfo(QObject *parent = nullptr); |
4975 | ApplicationInfo(const QString &appId, QObject *parent = nullptr); |
4976 | @@ -66,8 +73,7 @@ |
4977 | Q_INVOKABLE void setState(State value); |
4978 | State state() const override { return m_state; } |
4979 | |
4980 | - void setFocused(bool value); |
4981 | - bool focused() const override { return m_focused; } |
4982 | + bool focused() const override; |
4983 | |
4984 | QString splashTitle() const override { return QString(); } |
4985 | QUrl splashImage() const override { return QUrl(); } |
4986 | @@ -100,21 +106,26 @@ |
4987 | void setInitialSurfaceSize(const QSize &size) override; |
4988 | |
4989 | Q_INVOKABLE void setShellChrome(Mir::ShellChrome shellChrome); |
4990 | -public: |
4991 | - void setSession(Session* session); |
4992 | - Session* session() const { return m_session; } |
4993 | + |
4994 | + MirSurfaceListInterface* surfaceList() override { return &m_surfaceList; } |
4995 | + |
4996 | + void setFocused(bool value); |
4997 | + |
4998 | + ////// |
4999 | + // internal mock stuff |
5000 | + void close(); |
FAILED: Continuous integration, rev:2310 /unity8- jenkins. ubuntu. com/job/ lp-unity8- ci/881/ /unity8- jenkins. ubuntu. com/job/ build-0- fetch/1184 /unity8- jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= vivid+overlay/ 1161 /unity8- jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= xenial/ 1161 /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=amd64, release= vivid+overlay/ 1159/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=amd64, release= xenial/ 1159/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=armhf, release= vivid+overlay/ 1159/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=armhf, release= xenial/ 1159/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=i386, release= vivid+overlay/ 1159/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=i386, release= xenial/ 1159/console
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild: /unity8- jenkins. ubuntu. com/job/ lp-unity8- ci/881/ rebuild
https:/