Merge lp:~dandrader/unity8/lifecycle into lp:unity8

Proposed by Daniel d'Andrada
Status: Merged
Approved by: Michael Zanetti
Approved revision: 1161
Merged at revision: 1201
Proposed branch: lp:~dandrader/unity8/lifecycle
Merge into: lp:unity8
Diff against target: 3182 lines (+1502/-760)
40 files modified
debian/control (+3/-3)
include/paths.h.in (+8/-0)
plugins/Unity/Launcher/CMakeLists.txt (+1/-1)
qml/Stages/AppSurfaceContainer.qml (+0/-96)
qml/Stages/ApplicationWindow.qml (+258/-0)
qml/Stages/PhoneStage.qml (+16/-4)
qml/Stages/SpreadDelegate.qml (+34/-59)
qml/Stages/SurfaceContainer.qml (+5/-11)
qml/Stages/TabletStage.qml (+13/-3)
qml/Stages/TransformedSpreadDelegate.qml (+1/-1)
tests/mocks/Unity/Application/Application.qmltypes (+0/-19)
tests/mocks/Unity/Application/ApplicationImage.cpp (+0/-123)
tests/mocks/Unity/Application/ApplicationImage.h (+0/-79)
tests/mocks/Unity/Application/ApplicationInfo.cpp (+83/-14)
tests/mocks/Unity/Application/ApplicationInfo.h (+52/-46)
tests/mocks/Unity/Application/ApplicationManager.cpp (+91/-106)
tests/mocks/Unity/Application/ApplicationManager.h (+8/-4)
tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp (+2/-4)
tests/mocks/Unity/Application/CMakeLists.txt (+1/-3)
tests/mocks/Unity/Application/InputFilterArea.qml (+0/-21)
tests/mocks/Unity/Application/MirSurfaceItem.cpp (+87/-16)
tests/mocks/Unity/Application/MirSurfaceItem.h (+14/-11)
tests/mocks/Unity/Application/MirSurfaceItem.qml (+57/-0)
tests/mocks/Unity/Application/OSKController.qml (+0/-20)
tests/mocks/Unity/Application/SurfaceManager.cpp (+14/-3)
tests/mocks/Unity/Application/SurfaceManager.h (+1/-2)
tests/mocks/Unity/Application/VirtualKeyboard.cpp (+0/-55)
tests/mocks/Unity/Application/VirtualKeyboard.h (+0/-35)
tests/mocks/Unity/Application/VirtualKeyboard.qml (+40/-0)
tests/mocks/Unity/Application/plugin.cpp (+0/-3)
tests/mocks/Unity/Application/qmldir (+0/-3)
tests/plugins/Unity/Launcher/CMakeLists.txt (+1/-1)
tests/plugins/Unity/Launcher/launchermodeltest.cpp (+0/-1)
tests/qmltests/CMakeLists.txt (+3/-0)
tests/qmltests/Stages/tst_ApplicationWindow.qml (+346/-0)
tests/qmltests/Stages/tst_PhoneStage.qml (+35/-9)
tests/qmltests/Stages/tst_SpreadDelegate.qml (+167/-0)
tests/qmltests/Stages/tst_SurfaceContainer.qml (+154/-0)
tests/qmltests/tst_Shell.qml (+2/-2)
tests/utils/modules/Unity/Test/UnityTestCase.qml (+5/-2)
To merge this branch: bzr merge lp:~dandrader/unity8/lifecycle
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing
Michael Zanetti (community) Approve
Michał Sawicz Approve
Gerry Boland (community) Needs Information
Review via email: mp+230090@code.launchpad.net

Commit message

SpreadDelegate - properly transition between splash screen, surface and screenshot

- A new component, ApplicationWindow, was created to hold the
  splash+surface+screenshot logic originally in the SpreadDelegate. Now the
  responsibility of SpreadDelegate is just to do the vertical swipe to close
  the app.
- qml tests were added for both SpreadDelegate and ApplicationWindow
- Fixed UnityTestCase.tap()

Description of the change

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

https://code.launchpad.net/~dandrader/qtmir/lifecycle/+merge/230092
https://code.launchpad.net/~dandrader/unity-api/lifecycle/+merge/230091

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

Yes.

* 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?

Didn't change.

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

Not applicable.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

Fails to build here. Seems you are mixing requirements of unity-shell-application=3 and unity-shell-application=1

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) wrote :

Please run http://people.canonical.com/~msawicz/unity8/strip-u8-tags.py on this branch and any local checkouts you might have.

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

see some inline comments.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

Addressed all review comments (replied inline).

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

> Please run http://people.canonical.com/~msawicz/unity8/strip-u8-tags.py on
> this branch and any local checkouts you might have.

Done.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

The dropshadow doesn't match with the surface's size any more

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

Surface sizes are not correct in tablet mode any more. (In Shell.qml change tablet to true and run make testShell)

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

The surface position in the spread is not correct any more. Surfaces should be aligned on their bottom edge and then scaled so that when reaching the left edge they're all the same size. Currently they are aligned at the top edge and the scaling doesn't work at all

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

replied to your inline replies. Soem very few still open.

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

plugins/Unity/Launcher/CMakeLists.txt still requires unity-shell-application=1 while the other places want unity-shell-application=3.

Besides wondering why that even works (shouldn't =1 fail now that we have 3 installed?) I'm also wondering if this what we want.

Saviq?

review: Needs Fixing
Revision history for this message
Michał Sawicz (saviq) wrote :

> plugins/Unity/Launcher/CMakeLists.txt still requires unity-shell-application=1
> while the other places want unity-shell-application=3.
>
> Besides wondering why that even works (shouldn't =1 fail now that we have 3
> installed?) I'm also wondering if this what we want.

I think CMake might be caching the output of the first ever call to find_package() for a certain package, so it passes the first check and just ignores the next one.

No, they should all be the same.

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

> > plugins/Unity/Launcher/CMakeLists.txt still requires unity-shell-
> application=1
> > while the other places want unity-shell-application=3.
> >
> > Besides wondering why that even works (shouldn't =1 fail now that we have 3
> > installed?) I'm also wondering if this what we want.
>
> I think CMake might be caching the output of the first ever call to
> find_package() for a certain package, so it passes the first check and just
> ignores the next one.
>
> No, they should all be the same.

Sneaky one. Fixed.

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

And our all time favorite test :)

/build/buildd/unity8-8.00+14.10.20140814.1/tests/qmltests/Stages/tst_ApplicationWindow.qml: multiple new lines at end of file

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

> replied to your inline replies. Soem very few still open.

Done.

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

> replied to your inline replies. Soem very few still open.

Done.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

Some minor new inline comments after having another read through the latest revision.

Also, I've had a run with tryApplicationWindow. There are a few odditites:

* check/uncheck the surface checkbox, you'll see the red shine through, which makes me believe we might have an issue with one of the transitions... No matter which state, the app shouldn't become transparent, no?

* Set the application state to Stopped, then check the surface checkbox. It'll break the state machine quite badly...

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

> The surface position in the spread is not correct any more. Surfaces should be
> aligned on their bottom edge and then scaled so that when reaching the left
> edge they're all the same size. Currently they are aligned at the top edge and
> the scaling doesn't work at all

Weird thing is, I cannot reproduce this in tryPhoneStage, but I can in tryShell. What's the difference?

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

> The dropshadow doesn't match with the surface's size any more

Fixed. The problem in the MirSurfaceItem mock: Its contents were not getting resized as they should.

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

> Surface sizes are not correct in tablet mode any more. (In Shell.qml change
> tablet to true and run make testShell)

Same thing. The problem was in the MirSurfaceItem mock: Its contents were not getting resized as they should.

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

> The surface position in the spread is not correct any more. Surfaces should be
> aligned on their bottom edge and then scaled so that when reaching the left
> edge they're all the same size. Currently they are aligned at the top edge and
> the scaling doesn't work at all

Same thing. The problem was in the MirSurfaceItem mock: Its contents were not getting resized as they should.

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

> And our all time favorite test :)
>
> /build/buildd/unity8-8.00+14.10.20140814.1/tests/qmltests/Stages/tst_Applicati
> onWindow.qml: multiple new lines at end of file

Fixed.

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

>* check/uncheck the surface checkbox, you'll see the red shine through, which makes me believe we might have an issue with one of the transitions... No matter which state, the app shouldn't become transparent, no?
>
>* Set the application state to Stopped, then check the surface checkbox. It'll break the state machine quite badly...

You've triggered invalid situations that I didn't bother to cover with nice state transitions. Those are situations don't make sense and won't happen in real life (unless there's a nasty bug in AppMan or elsewhere).

Replied to your diff comments as well.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote :

Functional testing:
Bug 1: launch clock, then kill it with "kill `pgrep -f clock`" - the clock should vanish immediately - instead there's a weird fade out to splash screen, then vanish.

Bug 2: launch clock. Switch to Dash. wait 3 seconds for clock to be lifecycle stopped. Run "kill `pgrep -f clock`" Open spread, see Clock there, select Clock - I don't see clock being resumed, instead it just fades to the Dash.

Are these not the kind of issues this MR is tackling?

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

Seems the OSK in tablet mode is still positioned wrong

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

> Functional testing:
> Bug 1: launch clock, then kill it with "kill `pgrep -f clock`" - the clock
> should vanish immediately - instead there's a weird fade out to splash screen,
> then vanish.

Fixed.

> Bug 2: launch clock. Switch to Dash. wait 3 seconds for clock to be lifecycle
> stopped. Run "kill `pgrep -f clock`" Open spread, see Clock there, select
> Clock - I don't see clock being resumed, instead it just fades to the Dash.

The SIGTERM is only acted upon once the process is resumed.

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

> Seems the OSK in tablet mode is still positioned wrong

Fixed. Thanks for spotting this. I forgot to update it.

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

> Functional testing:
> Bug 1: launch clock, then kill it with "kill `pgrep -f clock`" - the clock
> should vanish immediately - instead there's a weird fade out to splash screen,
> then vanish.
>
> Bug 2: launch clock. Switch to Dash. wait 3 seconds for clock to be lifecycle
> stopped. Run "kill `pgrep -f clock`" Open spread, see Clock there, select
> Clock - I don't see clock being resumed, instead it just fades to the Dash.
>
> Are these not the kind of issues this MR is tackling?

Ok I wasn't doing accurate testing. Here I was sending TERM signals to apps, which as soon as they were resumed, would shut themselves down. Sending KILL to apps worked correctly.
The fade-out to splash is still wrong though, and can be exhibited by opening media player and pressing the Ok button which quits it.

Revision history for this message
Michał Sawicz (saviq) wrote :

Not sure if it'll happen here, but a design request for "resuming" apps:

- display a desaturated B&W screenshot
- if no frame for 2s or so, display a "Resuming..." and a spinner (will get a visual tomorrow)
- cross-fade to app

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> Not sure if it'll happen here, but a design request for "resuming" apps:
>
> - display a desaturated B&W screenshot
> - if no frame for 2s or so, display a "Resuming..." and a spinner (will get a
> visual tomorrow)
> - cross-fade to app

Good to know. But I think it should be done in a separate MP. Thus one is already big enough as has been hanging around for too long.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

FAIL! : qmltestrunner::Shell::test_fullscreen() property fullscreenMode
   Actual (): true
   Expected (): false
   Loc: [/home/mzanetti/Development/reviews/lifecycle/tests/qmltests/tst_Shell.qml(407)]

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

> FAIL! : qmltestrunner::Shell::test_fullscreen() property fullscreenMode
> Actual (): true
> Expected (): false
> Loc: [/home/mzanetti/Development/reviews/lifecycle/tests/qmltests/tst_Shell
> .qml(407)]

Fixed.

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

looks good to me now!

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

yip yip

 * Did CI run pass? If not, please explain why.

no... missing deps, but it building fine in PPA. Related tests seem to pass here.

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) :
review: Needs Fixing
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

>Michał Sawicz (saviq) wrote 1 hour ago:
> unity8-common needs a runtime bump of the unity-application-impl- dependency. And unity8-fake-env needs a bump of the unity-application-impl- provide.

Ok. Done.

Revision history for this message
Michał Sawicz (saviq) :
review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

still looking good to me.

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/control'
2--- debian/control 2014-08-22 09:27:40 +0000
3+++ debian/control 2014-08-25 08:53:17 +0000
4@@ -24,7 +24,7 @@
5 libpulse-dev,
6 libqmenumodel-dev (>= 0.2.8),
7 libqt5xmlpatterns5-dev,
8- libunity-api-dev (>= 7.88),
9+ libunity-api-dev (>= 7.89),
10 libusermetricsoutput1-dev,
11 libxcb1-dev,
12 pkg-config,
13@@ -112,7 +112,7 @@
14 qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.49) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 0.1.49),
15 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
16 ubuntu-thumbnailer-impl-0,
17- unity-application-impl-2,
18+ unity-application-impl-3,
19 unity-notifications-impl-3,
20 unity-plugin-scopes | unity-scopes-impl,
21 unity-scopes-impl-4,
22@@ -159,7 +159,7 @@
23 Depends: ${misc:Depends},
24 ${shlibs:Depends},
25 Provides: unity-application-impl,
26- unity-application-impl-2,
27+ unity-application-impl-3,
28 Description: Fake environment for running Unity 8 shell
29 Provides fake implementations of some QML modules used by Unity 8 shell
30 (e.g Ubuntu.Application) so that you can run it in a sandboxed environment.
31
32=== modified file 'include/paths.h.in'
33--- include/paths.h.in 2014-04-17 08:06:29 +0000
34+++ include/paths.h.in 2014-08-25 08:53:17 +0000
35@@ -80,6 +80,14 @@
36 return paths;
37 }
38
39+inline QString mockPluginsDir() {
40+ if (isRunningInstalled()) {
41+ return QString("@CMAKE_INSTALL_PREFIX@/@SHELL_INSTALL_QML@/mocks");
42+ } else {
43+ return QString("@CMAKE_BINARY_DIR@/tests/mocks");
44+ }
45+}
46+
47 inline QStringList shellDataDirs() {
48 QStringList dirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
49 if (!isRunningInstalled()) {
50
51=== modified file 'plugins/Unity/Launcher/CMakeLists.txt'
52--- plugins/Unity/Launcher/CMakeLists.txt 2014-05-30 09:22:58 +0000
53+++ plugins/Unity/Launcher/CMakeLists.txt 2014-08-25 08:53:17 +0000
54@@ -1,6 +1,6 @@
55 include(FindPkgConfig)
56 pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=3)
57-pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=1)
58+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=3)
59 pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
60
61 add_definitions(-DSM_BUSNAME=systemBus)
62
63=== modified file 'qml/Dash/graphics/phone/screenshots/vkb_portrait.png'
64Binary files qml/Dash/graphics/phone/screenshots/vkb_portrait.png 2014-07-22 10:29:25 +0000 and qml/Dash/graphics/phone/screenshots/vkb_portrait.png 2014-08-25 08:53:17 +0000 differ
65=== removed file 'qml/Stages/AppSurfaceContainer.qml'
66--- qml/Stages/AppSurfaceContainer.qml 2014-08-08 09:40:41 +0000
67+++ qml/Stages/AppSurfaceContainer.qml 1970-01-01 00:00:00 +0000
68@@ -1,96 +0,0 @@
69-/*
70- * Copyright 2014 Canonical Ltd.
71- *
72- * This program is free software; you can redistribute it and/or modify
73- * it under the terms of the GNU Lesser General Public License as published by
74- * the Free Software Foundation; version 3.
75- *
76- * This program is distributed in the hope that it will be useful,
77- * but WITHOUT ANY WARRANTY; without even the implied warranty of
78- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
79- * GNU Lesser General Public License for more details.
80- *
81- * You should have received a copy of the GNU Lesser General Public License
82- * along with this program. If not, see <http://www.gnu.org/licenses/>.
83-*/
84-
85-import QtQuick 2.0
86-import "Animations"
87-
88-SurfaceContainer {
89- id: container
90- property var promptSurfaces
91-
92- property bool appHasCreatedASurface: false
93-
94- onSurfaceChanged: {
95- if (surface) {
96- if (!appHasCreatedASurface) {
97- surface.visible = false; // hide until splash screen removed
98- appHasCreatedASurface = true;
99- }
100- }
101- }
102-
103- function revealSurface() {
104- surface.visible = true;
105- splashLoader.source = "";
106- }
107-
108- Timer { //FIXME - need to delay removing splash screen to allow surface resize to complete
109- id: surfaceRevealDelay
110- interval: 100
111- onTriggered: surfaceContainer.revealSurface()
112- }
113-
114- Loader {
115- z: 3
116- id: splashLoader
117- anchors.fill: parent
118- }
119-
120- Repeater {
121- model: container.promptSurfaces
122-
123- delegate: SurfaceContainer {
124- anchors {
125- fill: container
126- topMargin: container.surface.anchors.topMargin
127- rightMargin: container.surface.anchors.rightMargin
128- bottomMargin: container.surface.anchors.bottomMargin
129- leftMargin: container.surface.anchors.leftMargin
130- }
131-
132- z: 4 + index
133- surface: modelData
134-
135- Component.onCompleted: {
136- animateIn();
137- }
138- }
139- }
140-
141- StateGroup {
142- id: appSurfaceState
143- states: [
144- State {
145- name: "noSurfaceYet"
146- when: !surfaceContainer.appHasCreatedASurface
147- StateChangeScript {
148- script: { splashLoader.setSource("Splash.qml", { "name": model.name, "image": model.icon }); }
149- }
150- },
151- State {
152- name: "hasSurface"
153- when: surfaceContainer.appHasCreatedASurface && (surfaceContainer.surface !== null)
154- StateChangeScript { script: { surfaceRevealDelay.start(); } }
155- },
156- State {
157- name: "surfaceLostButAppStillAlive"
158- when: surfaceContainer.appHasCreatedASurface && (surfaceContainer.surface === null)
159- // TODO - use app snapshot
160- }
161- ]
162- state: "noSurfaceYet"
163- }
164-}
165
166=== added file 'qml/Stages/ApplicationWindow.qml'
167--- qml/Stages/ApplicationWindow.qml 1970-01-01 00:00:00 +0000
168+++ qml/Stages/ApplicationWindow.qml 2014-08-25 08:53:17 +0000
169@@ -0,0 +1,258 @@
170+/*
171+ * Copyright 2014 Canonical Ltd.
172+ *
173+ * This program is free software; you can redistribute it and/or modify
174+ * it under the terms of the GNU Lesser General Public License as published by
175+ * the Free Software Foundation; version 3.
176+ *
177+ * This program is distributed in the hope that it will be useful,
178+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
179+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
180+ * GNU Lesser General Public License for more details.
181+ *
182+ * You should have received a copy of the GNU Lesser General Public License
183+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
184+ */
185+
186+import QtQuick 2.0
187+import Ubuntu.Components 1.1
188+import Unity.Application 0.1
189+
190+Item {
191+ id: root
192+
193+ // to be read from outside
194+ readonly property bool fullscreen: application ? application.fullscreen : false
195+
196+ // to be set from outside
197+ property bool interactive: true
198+ property QtObject application
199+
200+ QtObject {
201+ id: d
202+
203+ // helpers so that we don't have to check for the existence of an application everywhere
204+ // (in order to avoid breaking qml binding due to a javascript exception)
205+ readonly property string name: root.application ? root.application.name : ""
206+ readonly property url icon: root.application ? root.application.icon : ""
207+ readonly property Item surface: root.application ? root.application.surface : null
208+ readonly property int applicationState: root.application ? root.application.state : -1
209+ readonly property var promptSurfaces: root.application ? root.application.promptSurfaces : null
210+
211+ // Whether the Application had a surface before but lost it.
212+ property bool hadSurface: false
213+
214+ property bool needToTakeScreenshot:
215+ d.surface && d.surfaceInitialized && screenshotImage.status === Image.Null
216+ && d.applicationState === ApplicationInfo.Stopped
217+ onNeedToTakeScreenshotChanged: {
218+ if (needToTakeScreenshot) {
219+ screenshotImage.take();
220+ }
221+ }
222+
223+ //FIXME - this is a hack to avoid the first few rendered frames as they
224+ // might show the UI accommodating due to surface resizes on startup.
225+ // Remove this when possible
226+ property bool surfaceInitialized: false
227+ onSurfaceChanged: {
228+ if (surface) {
229+ surfaceInitTimer.start();
230+ } else {
231+ hadSurface = true;
232+ surfaceInitialized = false;
233+ }
234+ }
235+ }
236+
237+ Timer {
238+ id: surfaceInitTimer
239+ interval: 100
240+ onTriggered: { if (d.surface) {d.surfaceInitialized = true;} }
241+ }
242+
243+ Binding {
244+ target: d.surface
245+ when: d.surface
246+ property: "enabled"
247+ value: root.interactive
248+ }
249+ Binding {
250+ target: d.surface
251+ when: d.surface
252+ property: "focus"
253+ value: root.interactive
254+ }
255+ Connections {
256+ target: d.surface
257+ // FIXME: I would rather not need to do this, but currently it doesn't get
258+ // active focus without it and I don't know why.
259+ onFocusChanged: forceSurfaceActiveFocusIfReady();
260+ onParentChanged: forceSurfaceActiveFocusIfReady();
261+ onEnabledChanged: forceSurfaceActiveFocusIfReady();
262+ function forceSurfaceActiveFocusIfReady() {
263+ if (d.surface.focus && d.surface.parent === surfaceContainer && d.surface.enabled) {
264+ d.surface.forceActiveFocus();
265+ }
266+ }
267+ }
268+
269+ SurfaceContainer {
270+ id: surfaceContainer
271+ objectName: "surfaceContainer"
272+ anchors.fill: parent
273+ surface: d.surface
274+ }
275+
276+ Image {
277+ id: screenshotImage
278+ objectName: "screenshotImage"
279+ source: ""
280+ anchors.fill: parent
281+
282+ function take() {
283+ // Format: "image://application/$APP_ID/$CURRENT_TIME_MS"
284+ // eg: "image://application/calculator-app/123456"
285+ var timeMs = new Date().getTime();
286+ source = "image://application/" + root.application.appId + "/" + timeMs;
287+ }
288+
289+ // Save memory by using a half-resolution (thus quarter size) screenshot
290+ sourceSize.width: root.width / 2
291+ sourceSize.height: root.height / 2
292+ }
293+
294+ Loader {
295+ id: splashLoader
296+ visible: active
297+ active: false
298+ anchors.fill: surfaceContainer
299+ sourceComponent: Component {
300+ Splash { name: d.name; image: d.icon }
301+ }
302+ }
303+
304+ Repeater {
305+ model: d.promptSurfaces
306+
307+ delegate: SurfaceContainer {
308+ anchors {
309+ fill: surfaceContainer
310+ }
311+
312+ surface: modelData
313+
314+ Component.onCompleted: {
315+ animateIn();
316+ }
317+ }
318+ }
319+
320+ StateGroup {
321+ objectName: "applicationWindowStateGroup"
322+ states: [
323+ State {
324+ name: "void"
325+ when:
326+ d.hadSurface && (!d.surface || !d.surfaceInitialized)
327+ &&
328+ screenshotImage.status !== Image.Ready
329+ },
330+ State {
331+ name: "splashScreen"
332+ when:
333+ !d.hadSurface && (!d.surface || !d.surfaceInitialized)
334+ &&
335+ screenshotImage.status !== Image.Ready
336+ },
337+ State {
338+ name: "surface"
339+ when:
340+ (d.surface && d.surfaceInitialized)
341+ &&
342+ (d.applicationState !== ApplicationInfo.Stopped
343+ || screenshotImage.status !== Image.Ready)
344+ },
345+ State {
346+ name: "screenshot"
347+ when:
348+ screenshotImage.status === Image.Ready
349+ &&
350+ (d.applicationState === ApplicationInfo.Stopped
351+ || !d.surface || !d.surfaceInitialized)
352+ }
353+ ]
354+
355+ transitions: [
356+ Transition {
357+ from: ""; to: "splashScreen"
358+ PropertyAction { target: splashLoader; property: "active"; value: true }
359+ },
360+ Transition {
361+ from: "splashScreen"; to: "surface"
362+ SequentialAnimation {
363+ PropertyAction { target: surfaceContainer
364+ property: "visible"; value: true }
365+ UbuntuNumberAnimation { target: splashLoader; property: "opacity";
366+ from: 1.0; to: 0.0
367+ duration: UbuntuAnimation.BriskDuration }
368+ PropertyAction { target: splashLoader; property: "active"; value: false }
369+ }
370+ },
371+ Transition {
372+ from: "surface"; to: "splashScreen"
373+ SequentialAnimation {
374+ PropertyAction { target: splashLoader; property: "active"; value: true }
375+ PropertyAction { target: surfaceContainer
376+ property: "visible"; value: true }
377+ UbuntuNumberAnimation { target: splashLoader; property: "opacity";
378+ from: 0.0; to: 1.0
379+ duration: UbuntuAnimation.BriskDuration }
380+ }
381+ },
382+ Transition {
383+ from: "surface"; to: "screenshot"
384+ SequentialAnimation {
385+ PropertyAction { target: screenshotImage
386+ property: "visible"; value: true }
387+ UbuntuNumberAnimation { target: screenshotImage; property: "opacity";
388+ from: 0.0; to: 1.0
389+ duration: UbuntuAnimation.BriskDuration }
390+ PropertyAction { target: surfaceContainer
391+ property: "visible"; value: false }
392+ ScriptAction { script: { if (d.surface) { d.surface.release(); } } }
393+ }
394+ },
395+ Transition {
396+ from: "screenshot"; to: "surface"
397+ SequentialAnimation {
398+ PropertyAction { target: surfaceContainer
399+ property: "visible"; value: true }
400+ UbuntuNumberAnimation { target: screenshotImage; property: "opacity";
401+ from: 1.0; to: 0.0
402+ duration: UbuntuAnimation.BriskDuration }
403+ PropertyAction { target: screenshotImage; property: "visible"; value: false }
404+ PropertyAction { target: screenshotImage; property: "source"; value: "" }
405+ }
406+ },
407+ Transition {
408+ from: "surface"; to: "void"
409+ SequentialAnimation {
410+ PropertyAction { target: surfaceContainer; property: "visible"; value: false }
411+ ScriptAction { script: { if (d.surface) { d.surface.release(); } } }
412+ }
413+ },
414+ Transition {
415+ from: "void"; to: "surface"
416+ SequentialAnimation {
417+ PropertyAction { target: surfaceContainer; property: "opacity"; value: 0.0 }
418+ PropertyAction { target: surfaceContainer; property: "visible"; value: true }
419+ UbuntuNumberAnimation { target: surfaceContainer; property: "opacity";
420+ from: 0.0; to: 1.0
421+ duration: UbuntuAnimation.BriskDuration }
422+ }
423+ }
424+ ]
425+ }
426+
427+}
428
429=== modified file 'qml/Stages/PhoneStage.qml'
430--- qml/Stages/PhoneStage.qml 2014-07-29 15:22:08 +0000
431+++ qml/Stages/PhoneStage.qml 2014-08-25 08:53:17 +0000
432@@ -110,7 +110,8 @@
433 id: spreadView
434 objectName: "spreadView"
435 anchors.fill: parent
436- interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1) && draggedIndex == -1
437+ interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1)
438+ && draggedDelegateCount === 0
439 contentWidth: spreadRow.width - shift
440 contentX: -shift
441
442@@ -148,7 +149,7 @@
443 property int phase: 0
444
445 property int selectedIndex: -1
446- property int draggedIndex: -1
447+ property int draggedDelegateCount: 0
448 property int closingIndex: -1
449
450 property bool focusChanging: false
451@@ -269,7 +270,7 @@
452 swipeToCloseEnabled: spreadView.interactive
453 maximizedAppTopMargin: root.maximizedAppTopMargin
454 dropShadow: spreadView.active ||
455- priv.focusedAppDelegate.x !== 0
456+ (priv.focusedAppDelegate && priv.focusedAppDelegate.x !== 0)
457
458 readonly property bool isDash: model.appId == "unity8-dash"
459
460@@ -290,6 +291,10 @@
461 // Otherwise line up for the spread
462 return spreadView.width + (index - 1) * spreadView.tileDistance;
463 }
464+
465+ application: ApplicationManager.get(index)
466+ closeable: !isDash
467+
468 property real behavioredIndex: index
469 Behavior on behavioredIndex {
470 enabled: spreadView.closingIndex >= 0
471@@ -372,8 +377,15 @@
472 }
473 }
474
475+ onDraggedChanged: {
476+ if (dragged) {
477+ spreadView.draggedDelegateCount++;
478+ } else {
479+ spreadView.draggedDelegateCount--;
480+ }
481+ }
482+
483 onClosed: {
484- spreadView.draggedIndex = -1;
485 spreadView.closingIndex = index;
486 ApplicationManager.stopApplication(ApplicationManager.get(index).appId);
487 }
488
489=== modified file 'qml/Stages/SpreadDelegate.qml'
490--- qml/Stages/SpreadDelegate.qml 2014-08-08 09:40:41 +0000
491+++ qml/Stages/SpreadDelegate.qml 2014-08-25 08:53:17 +0000
492@@ -14,75 +14,43 @@
493 * along with this program. If not, see <http://www.gnu.org/licenses/>.
494 *
495 * Authors: Michael Zanetti <michael.zanetti@canonical.com>
496-*/
497+ * Daniel d'Andrada <daniel.dandrada@canonical.com>
498+ */
499
500 import QtQuick 2.0
501-import Unity.Application 0.1
502 import Ubuntu.Components 1.1
503 import "../Components"
504
505 Item {
506 id: root
507
508+ // to be read from outside
509+ readonly property bool dragged: dragArea.moving
510+ signal clicked()
511+ signal closed()
512+
513 // to be set from outside
514 property bool interactive: true
515 property bool dropShadow: true
516 property real maximizedAppTopMargin
517 property alias swipeToCloseEnabled: dragArea.enabled
518-
519- readonly property bool isFullscreen: surface !== null && surfaceContainer.surface.anchors.topMargin == 0
520-
521- signal clicked()
522- signal closed()
523-
524- AppSurfaceContainer {
525- id: surfaceContainer
526- objectName: "surfaceContainer"
527- anchors.fill: parent
528- surface: model.surface
529- promptSurfaces: model.application.promptSurfaces
530-
531- Binding {
532- target: surfaceContainer.surface
533- property: "anchors.topMargin"
534- value: {
535- return surfaceContainer.surface.state === MirSurfaceItem.Fullscreen ? 0 : maximizedAppTopMargin;
536- }
537- }
538-
539- Binding {
540- target: surface
541- property: "enabled"
542- value: root.interactive
543- }
544- Binding {
545- target: surface
546- property: "focus"
547- value: root.interactive
548- }
549-
550- Connections {
551- target: surface
552- // FIXME: I would rather not need to do this, but currently it doesn't get
553- // active focus without it and I don't know why.
554- onFocusChanged: forceSurfaceActiveFocusIfReady();
555- onParentChanged: forceSurfaceActiveFocusIfReady();
556- onEnabledChanged: forceSurfaceActiveFocusIfReady();
557- function forceSurfaceActiveFocusIfReady() {
558- if (surface.focus && surface.parent === surfaceContainer && surface.enabled) {
559- surface.forceActiveFocus();
560- }
561- }
562- }
563+ property bool closeable
564+ property alias application: appWindow.application
565+
566+ Item {
567+ objectName: "appWindowWithShadow"
568+
569+ y: dragArea.distance
570+ width: parent.width
571+ height: parent.height
572
573 BorderImage {
574- id: dropShadowImage
575 anchors {
576- fill: parent
577+ fill: appWindow
578 leftMargin: -units.gu(2)
579 rightMargin: -units.gu(2)
580 bottomMargin: -units.gu(2)
581- topMargin: -units.gu(2) + (root.isFullscreen ? 0 : maximizedAppTopMargin)
582+ topMargin: -units.gu(2)
583 }
584 source: "graphics/dropshadow.png"
585 border { left: 50; right: 50; top: 50; bottom: 50 }
586@@ -90,21 +58,26 @@
587 Behavior on opacity { UbuntuNumberAnimation {} }
588 }
589
590- transform: Translate {
591- y: dragArea.distance
592+ ApplicationWindow {
593+ id: appWindow
594+ anchors {
595+ fill: parent
596+ topMargin: appWindow.fullscreen ? 0 : maximizedAppTopMargin
597+ }
598+
599+ interactive: root.interactive
600 }
601 }
602
603 DraggingArea {
604 id: dragArea
605+ objectName: "dragArea"
606 anchors.fill: parent
607
608 property bool moving: false
609 property real distance: 0
610
611- onMovingChanged: {
612- spreadView.draggedIndex = moving ? index : -1
613- }
614+ readonly property real minSpeedToClose: units.gu(40)
615
616 onDragValueChanged: {
617 if (!dragging) {
618@@ -123,15 +96,15 @@
619 }
620
621 onDragEnd: {
622- if (model.appId == "unity8-dash") {
623+ if (!root.closeable) {
624 animation.animate("center")
625 return;
626 }
627
628 // velocity and distance values specified by design prototype
629- if ((dragVelocity < -units.gu(40) && distance < -units.gu(8)) || distance < -root.height / 2) {
630+ if ((dragVelocity < -minSpeedToClose && distance < -units.gu(8)) || distance < -root.height / 2) {
631 animation.animate("up")
632- } else if ((dragVelocity > units.gu(40) && distance > units.gu(8)) || distance > root.height / 2) {
633+ } else if ((dragVelocity > minSpeedToClose && distance > units.gu(8)) || distance > root.height / 2) {
634 animation.animate("down")
635 } else {
636 animation.animate("center")
637@@ -140,6 +113,7 @@
638
639 UbuntuNumberAnimation {
640 id: animation
641+ objectName: "closeAnimation"
642 target: dragArea
643 property: "distance"
644 property bool requestClose: false
645@@ -164,9 +138,10 @@
646 onRunningChanged: {
647 if (!running) {
648 dragArea.moving = false;
649- dragArea.distance = 0;
650 if (requestClose) {
651 root.closed();
652+ } else {
653+ dragArea.distance = 0;
654 }
655 }
656 }
657
658=== modified file 'qml/Stages/SurfaceContainer.qml'
659--- qml/Stages/SurfaceContainer.qml 2014-08-07 17:15:00 +0000
660+++ qml/Stages/SurfaceContainer.qml 2014-08-25 08:53:17 +0000
661@@ -26,9 +26,12 @@
662 if (surface) {
663 surface.parent = container;
664 surface.z = 1;
665- state = "initial"
666 }
667 }
668+ Binding {
669+ target: surface
670+ property: "anchors.fill"; value: container
671+ }
672
673 Connections {
674 target: surface
675@@ -45,7 +48,7 @@
676 }
677
678 Repeater {
679- model: surface.childSurfaces
680+ model: surface ? surface.childSurfaces : null
681
682 delegate: Loader {
683 z: 2
684@@ -81,8 +84,6 @@
685 var popped = tmp.pop();
686 popped.end();
687 d.animations = tmp;
688- } else {
689- container.state = "initial";
690 }
691 }
692
693@@ -96,11 +97,4 @@
694 id: swipeFromBottom
695 SwipeFromBottomAnimation {}
696 }
697-
698- states: [
699- State {
700- name: "initial"
701- PropertyChanges { target: surface; anchors.fill: container }
702- }
703- ]
704 }
705
706=== modified file 'qml/Stages/TabletStage.qml'
707--- qml/Stages/TabletStage.qml 2014-07-29 13:45:30 +0000
708+++ qml/Stages/TabletStage.qml 2014-08-25 08:53:17 +0000
709@@ -145,7 +145,8 @@
710 Flickable {
711 id: spreadView
712 anchors.fill: parent
713- interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1) && draggedIndex == -1
714+ interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1)
715+ && draggedDelegateCount === 0
716 contentWidth: spreadRow.width - shift
717 contentX: -shift
718
719@@ -189,7 +190,7 @@
720 readonly property real snapPosition: 0.75
721
722 property int selectedIndex: -1
723- property int draggedIndex: -1
724+ property int draggedDelegateCount: 0
725 property int closingIndex: -1
726
727 property bool sideStageDragging: sideStageDragHandle.dragging
728@@ -475,6 +476,8 @@
729 swipeToCloseEnabled: spreadView.interactive
730 maximizedAppTopMargin: root.maximizedAppTopMargin
731 dragOffset: !isDash && model.appId == priv.mainStageAppId && root.inverseProgress > 0 ? root.inverseProgress : 0
732+ application: ApplicationManager.get(index)
733+ closeable: !isDash
734
735 readonly property bool isDash: model.appId == "unity8-dash"
736
737@@ -533,8 +536,15 @@
738 }
739 }
740
741+ onDraggedChanged: {
742+ if (dragged) {
743+ spreadView.draggedDelegateCount++;
744+ } else {
745+ spreadView.draggedDelegateCount--;
746+ }
747+ }
748+
749 onClosed: {
750- spreadView.draggedIndex = -1;
751 spreadView.closingIndex = index;
752 ApplicationManager.stopApplication(ApplicationManager.get(index).appId);
753 }
754
755=== modified file 'qml/Stages/TransformedSpreadDelegate.qml'
756--- qml/Stages/TransformedSpreadDelegate.qml 2014-07-29 13:45:30 +0000
757+++ qml/Stages/TransformedSpreadDelegate.qml 2014-08-25 08:53:17 +0000
758@@ -309,7 +309,7 @@
759 Scale {
760 origin { x: 0; y: (spreadView.height * priv.scale) + maximizedAppTopMargin * 3 }
761 xScale: 1
762- yScale: isFullscreen ? 1 - priv.topMarginProgress * maximizedAppTopMargin / spreadView.height : 1
763+ yScale: fullscreen ? 1 - priv.topMarginProgress * maximizedAppTopMargin / spreadView.height : 1
764 },
765 Translate {
766 x: priv.xTranslate
767
768=== modified file 'tests/mocks/Unity/Application/Application.qmltypes'
769--- tests/mocks/Unity/Application/Application.qmltypes 2014-08-14 01:28:06 +0000
770+++ tests/mocks/Unity/Application/Application.qmltypes 2014-08-25 08:53:17 +0000
771@@ -8,25 +8,6 @@
772
773 Module {
774 Component {
775- name: "ApplicationImage"
776- defaultProperty: "data"
777- prototype: "QQuickItem"
778- exports: ["Unity.Application/ApplicationImage 0.1"]
779- exportMetaObjectRevisions: [0]
780- Enum {
781- name: "FillMode"
782- values: {
783- "Stretch": 0,
784- "PreserveAspectCrop": 1
785- }
786- }
787- Property { name: "source"; type: "ApplicationInfo"; isPointer: true }
788- Property { name: "fillMode"; type: "FillMode" }
789- Property { name: "ready"; type: "bool" }
790- Method { name: "scheduleUpdate" }
791- Method { name: "updateFromCache" }
792- }
793- Component {
794 name: "ApplicationInfo"
795 prototype: "unity::shell::application::ApplicationInfoInterface"
796 exports: ["Unity.Application/ApplicationInfo 0.1"]
797
798=== removed file 'tests/mocks/Unity/Application/ApplicationImage.cpp'
799--- tests/mocks/Unity/Application/ApplicationImage.cpp 2013-06-05 22:03:08 +0000
800+++ tests/mocks/Unity/Application/ApplicationImage.cpp 1970-01-01 00:00:00 +0000
801@@ -1,123 +0,0 @@
802-/*
803- * Copyright (C) 2013 Canonical, Ltd.
804- *
805- * This program is free software; you can redistribute it and/or modify
806- * it under the terms of the GNU General Public License as published by
807- * the Free Software Foundation; version 3.
808- *
809- * This program is distributed in the hope that it will be useful,
810- * but WITHOUT ANY WARRANTY; without even the implied warranty of
811- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
812- * GNU General Public License for more details.
813- *
814- * You should have received a copy of the GNU General Public License
815- * along with this program. If not, see <http://www.gnu.org/licenses/>.
816- */
817-
818-#include "ApplicationImage.h"
819-#include "ApplicationInfo.h"
820-
821-#include <QQmlEngine>
822-#include <QQmlComponent>
823-#include <QQmlContext>
824-
825-ApplicationImage::ApplicationImage(QQuickItem* parent)
826- : QQuickItem(parent),
827- m_source(NULL),
828- m_fillMode(Stretch),
829- m_ready(false),
830- m_imageComponent(0),
831- m_imageItem(0)
832-{
833-}
834-
835-void ApplicationImage::setSource(ApplicationInfo* source)
836-{
837- if (m_source != source) {
838- if (m_source)
839- disconnect(m_source, &ApplicationInfo::imageQmlChanged,
840- this, &ApplicationImage::updateImage);
841-
842- m_source = source;
843-
844- if (m_source) {
845- connect(m_source, &ApplicationInfo::imageQmlChanged,
846- this, &ApplicationImage::updateImage);
847- }
848- updateImage();
849-
850- Q_EMIT sourceChanged();
851- }
852-}
853-
854-void ApplicationImage::setFillMode(FillMode newFillMode)
855-{
856- if (m_fillMode != newFillMode) {
857- m_fillMode = newFillMode;
858- Q_EMIT fillModeChanged();
859- }
860-}
861-
862-void ApplicationImage::setReady(bool value)
863-{
864- if (value != m_ready) {
865- m_ready = value;
866- Q_EMIT readyChanged();
867- }
868-}
869-
870-void ApplicationImage::destroyImage()
871-{
872- delete m_imageItem;
873- m_imageItem = 0;
874- delete m_imageComponent;
875- m_imageComponent = 0;
876- m_qmlUsed.clear();
877- setReady(false);
878-}
879-
880-void ApplicationImage::updateImage()
881-{
882- if (!m_source || m_source->imageQml().isEmpty()) {
883- destroyImage();
884- } else if (m_source->imageQml() != m_qmlUsed) {
885- destroyImage();
886- createImageItem();
887- }
888-}
889-
890-void ApplicationImage::createImageItem()
891-{
892-
893- if (!m_imageComponent)
894- createImageComponent();
895-
896- // only create the windowItem one the component is ready
897- if (!m_imageComponent->isReady()) {
898- connect(m_imageComponent, &QQmlComponent::statusChanged,
899- this, &ApplicationImage::onImageComponentStatusChanged);
900- } else {
901- doCreateImageItem();
902- }
903-
904-}
905-
906-void ApplicationImage::createImageComponent()
907-{
908- QQmlEngine *engine = QQmlEngine::contextForObject(this)->engine();
909- m_imageComponent = new QQmlComponent(engine, this);
910- m_imageComponent->setData(m_source->imageQml().toLatin1(), QUrl());
911-}
912-
913-void ApplicationImage::doCreateImageItem()
914-{
915- m_imageItem = qobject_cast<QQuickItem *>(m_imageComponent->create());
916- m_imageItem->setParentItem(this);
917- setReady(true);
918-}
919-
920-void ApplicationImage::onImageComponentStatusChanged(QQmlComponent::Status status)
921-{
922- if (status == QQmlComponent::Ready && !m_imageItem)
923- doCreateImageItem();
924-}
925
926=== removed file 'tests/mocks/Unity/Application/ApplicationImage.h'
927--- tests/mocks/Unity/Application/ApplicationImage.h 2013-06-05 22:03:08 +0000
928+++ tests/mocks/Unity/Application/ApplicationImage.h 1970-01-01 00:00:00 +0000
929@@ -1,79 +0,0 @@
930-/*
931- * Copyright (C) 2013 Canonical, Ltd.
932- *
933- * This program is free software; you can redistribute it and/or modify
934- * it under the terms of the GNU General Public License as published by
935- * the Free Software Foundation; version 3.
936- *
937- * This program is distributed in the hope that it will be useful,
938- * but WITHOUT ANY WARRANTY; without even the implied warranty of
939- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
940- * GNU General Public License for more details.
941- *
942- * You should have received a copy of the GNU General Public License
943- * along with this program. If not, see <http://www.gnu.org/licenses/>.
944- */
945-
946-#ifndef APPLICATION_IMAGE_H
947-#define APPLICATION_IMAGE_H
948-
949-#include <QQuickItem>
950-
951-class ApplicationInfo;
952-
953-/* Fake implementation of ApplicationImage
954-
955- That fake implementation is not made in QML just because we can only declare
956- enumerations in C++. We can't even make readonly properties to mimic an enum
957- because properties must begin with a lower case letter.
958-*/
959-class ApplicationImage : public QQuickItem {
960- Q_OBJECT
961- Q_ENUMS(FillMode)
962- Q_PROPERTY(ApplicationInfo* source READ source WRITE setSource NOTIFY sourceChanged)
963- Q_PROPERTY(FillMode fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged)
964- Q_PROPERTY(bool ready READ ready WRITE setReady NOTIFY readyChanged)
965-
966-public:
967- explicit ApplicationImage(QQuickItem* parent = 0);
968- virtual ~ApplicationImage() {}
969-
970- enum FillMode { Stretch, PreserveAspectCrop };
971-
972- ApplicationInfo* source() const { return m_source; }
973- void setSource(ApplicationInfo* source);
974-
975- FillMode fillMode() const { return m_fillMode; }
976- void setFillMode(FillMode);
977-
978- void setReady(bool value);
979- bool ready() const { return m_ready; }
980-
981- Q_INVOKABLE void scheduleUpdate() {}
982- Q_INVOKABLE void updateFromCache() {}
983-
984-Q_SIGNALS:
985- void sourceChanged();
986- void fillModeChanged();
987- void readyChanged();
988-
989-private Q_SLOTS:
990- void updateImage();
991- void onImageComponentStatusChanged(QQmlComponent::Status status);
992-
993-private:
994- void createImageItem();
995- void createImageComponent();
996- void doCreateImageItem();
997- void destroyImage();
998-
999- ApplicationInfo* m_source;
1000- FillMode m_fillMode;
1001- bool m_ready;
1002- QQmlComponent *m_imageComponent;
1003- QQuickItem* m_imageItem;
1004- // the QML script used to create the current m_imageItem
1005- QString m_qmlUsed;
1006-};
1007-
1008-#endif // APPLICATION_IMAGE_H
1009
1010=== modified file 'tests/mocks/Unity/Application/ApplicationInfo.cpp'
1011--- tests/mocks/Unity/Application/ApplicationInfo.cpp 2014-08-16 16:38:43 +0000
1012+++ tests/mocks/Unity/Application/ApplicationInfo.cpp 2014-08-25 08:53:17 +0000
1013@@ -18,6 +18,8 @@
1014 #include "MirSurfaceItem.h"
1015 #include "SurfaceManager.h"
1016
1017+#include <paths.h>
1018+
1019 #include <QGuiApplication>
1020 #include <QQuickItem>
1021 #include <QQuickView>
1022@@ -31,10 +33,10 @@
1023 , m_state(Starting)
1024 , m_focused(false)
1025 , m_fullscreen(false)
1026+ , m_surface(0)
1027+ , m_manualSurfaceCreation(false)
1028 , m_parentItem(0)
1029- , m_surface(0)
1030 {
1031- connect(this, &ApplicationInfo::stateChanged, this, &ApplicationInfo::onStateChanged);
1032 }
1033
1034 ApplicationInfo::ApplicationInfo(QObject *parent)
1035@@ -43,10 +45,10 @@
1036 , m_state(Starting)
1037 , m_focused(false)
1038 , m_fullscreen(false)
1039+ , m_surface(0)
1040+ , m_manualSurfaceCreation(false)
1041 , m_parentItem(0)
1042- , m_surface(0)
1043 {
1044- connect(this, &ApplicationInfo::stateChanged, this, &ApplicationInfo::onStateChanged);
1045 }
1046
1047 ApplicationInfo::~ApplicationInfo()
1048@@ -58,23 +60,16 @@
1049 }
1050 }
1051
1052-void ApplicationInfo::onStateChanged(State state)
1053-{
1054- if (state == ApplicationInfo::Running) {
1055- QTimer::singleShot(1000, this, SLOT(createSurface()));
1056- } else if (state == ApplicationInfo::Stopped) {
1057- setSurface(nullptr);
1058- }
1059-}
1060-
1061 void ApplicationInfo::createSurface()
1062 {
1063 if (m_surface || state() == ApplicationInfo::Stopped) return;
1064
1065+ QUrl screenshotUrl = QString("file://%1").arg(m_screenshotFileName);
1066+
1067 setSurface(new MirSurfaceItem(name(),
1068 MirSurfaceItem::Normal,
1069 fullscreen() ? MirSurfaceItem::Fullscreen : MirSurfaceItem::Maximized,
1070- screenshot()));
1071+ screenshotUrl));
1072 }
1073
1074 void ApplicationInfo::setSurface(MirSurfaceItem* surface)
1075@@ -152,3 +147,77 @@
1076 return nullptr;
1077 return p->m_promptSurfaces[index];
1078 }
1079+
1080+void ApplicationInfo::setIconId(const QString &iconId)
1081+{
1082+ setIcon(QString("file://%1/graphics/applicationIcons/%2@18.png")
1083+ .arg(qmlDirectory())
1084+ .arg(iconId));
1085+}
1086+
1087+void ApplicationInfo::setScreenshotId(const QString &screenshotId)
1088+{
1089+ m_screenshotFileName = QString("%1/Dash/graphics/phone/screenshots/%2@12.png")
1090+ .arg(qmlDirectory())
1091+ .arg(screenshotId);
1092+}
1093+
1094+void ApplicationInfo::setName(const QString &value)
1095+{
1096+ if (value != m_name) {
1097+ m_name = value;
1098+ Q_EMIT nameChanged(value);
1099+ }
1100+}
1101+
1102+void ApplicationInfo::setIcon(const QUrl &value)
1103+{
1104+ if (value != m_icon) {
1105+ m_icon = value;
1106+ Q_EMIT iconChanged(value);
1107+ }
1108+}
1109+
1110+void ApplicationInfo::setStage(Stage value)
1111+{
1112+ if (value != m_stage) {
1113+ m_stage = value;
1114+ Q_EMIT stageChanged(value);
1115+ }
1116+}
1117+
1118+void ApplicationInfo::setState(State value)
1119+{
1120+ if (value != m_state) {
1121+ m_state = value;
1122+ Q_EMIT stateChanged(value);
1123+
1124+ if (!m_manualSurfaceCreation && m_state == ApplicationInfo::Running) {
1125+ QTimer::singleShot(1000, this, SLOT(createSurface()));
1126+ }
1127+ }
1128+}
1129+
1130+void ApplicationInfo::setFocused(bool value)
1131+{
1132+ if (value != m_focused) {
1133+ m_focused = value;
1134+ Q_EMIT focusedChanged(value);
1135+ }
1136+}
1137+
1138+void ApplicationInfo::setFullscreen(bool value)
1139+{
1140+ if (value != m_fullscreen) {
1141+ m_fullscreen = value;
1142+ Q_EMIT fullscreenChanged(value);
1143+ }
1144+}
1145+
1146+void ApplicationInfo::setManualSurfaceCreation(bool value)
1147+{
1148+ if (value != m_manualSurfaceCreation) {
1149+ m_manualSurfaceCreation = value;
1150+ Q_EMIT manualSurfaceCreationChanged(value);
1151+ }
1152+}
1153
1154=== modified file 'tests/mocks/Unity/Application/ApplicationInfo.h'
1155--- tests/mocks/Unity/Application/ApplicationInfo.h 2014-08-07 17:15:00 +0000
1156+++ tests/mocks/Unity/Application/ApplicationInfo.h 2014-08-25 08:53:17 +0000
1157@@ -26,63 +26,52 @@
1158 // unity-api
1159 #include <unity/shell/application/ApplicationInfoInterface.h>
1160
1161-// A pretty dumb file. Just a container for properties.
1162-// Implemented in C++ instead of QML just because of the enumerations
1163-// See QTBUG-14861
1164-
1165 using namespace unity::shell::application;
1166
1167 class ApplicationInfo : public ApplicationInfoInterface {
1168 Q_OBJECT
1169
1170 Q_PROPERTY(bool fullscreen READ fullscreen WRITE setFullscreen NOTIFY fullscreenChanged)
1171- Q_PROPERTY(Stage stage READ stage WRITE setStage NOTIFY stageChanged)
1172 Q_PROPERTY(MirSurfaceItem* surface READ surface NOTIFY surfaceChanged)
1173 Q_PROPERTY(QQmlListProperty<MirSurfaceItem> promptSurfaces READ promptSurfaces NOTIFY promptSurfacesChanged DESIGNABLE false)
1174
1175 // Only exists in this fake implementation
1176
1177- // QML component used to represent its image/screenhot
1178- Q_PROPERTY(QString imageQml READ imageQml WRITE setImageQml NOTIFY imageQmlChanged)
1179-
1180- // QML component used to represent the application window
1181- Q_PROPERTY(QString windowQml READ windowQml WRITE setWindowQml NOTIFY windowQmlChanged)
1182+ // whether the test code will explicitly control the creation of the application surface
1183+ Q_PROPERTY(bool manualSurfaceCreation READ manualSurfaceCreation WRITE setManualSurfaceCreation NOTIFY manualSurfaceCreationChanged)
1184
1185 public:
1186 ApplicationInfo(QObject *parent = NULL);
1187 ApplicationInfo(const QString &appId, QObject *parent = NULL);
1188 ~ApplicationInfo();
1189
1190- #define IMPLEMENT_PROPERTY(name, Name, type) \
1191- public: \
1192- type name() const { return m_##name; } \
1193- void set##Name(const type& value) \
1194- { \
1195- if (m_##name != value) { \
1196- m_##name = value; \
1197- Q_EMIT name##Changed(value); \
1198- } \
1199- } \
1200- Q_SIGNALS: \
1201- void name##Changed(const type&); \
1202- private: \
1203- type m_##name;
1204-
1205- IMPLEMENT_PROPERTY(appId, AppId, QString)
1206- IMPLEMENT_PROPERTY(name, Name, QString)
1207- IMPLEMENT_PROPERTY(comment, Comment, QString)
1208- IMPLEMENT_PROPERTY(icon, Icon, QUrl)
1209- IMPLEMENT_PROPERTY(stage, Stage, Stage)
1210- IMPLEMENT_PROPERTY(state, State, State)
1211- IMPLEMENT_PROPERTY(focused, Focused, bool)
1212- IMPLEMENT_PROPERTY(fullscreen, Fullscreen, bool)
1213- IMPLEMENT_PROPERTY(imageQml, ImageQml, QString)
1214- IMPLEMENT_PROPERTY(windowQml, WindowQml, QString)
1215- IMPLEMENT_PROPERTY(screenshot, Screenshot, QUrl)
1216-
1217- #undef IMPLEMENT_PROPERTY
1218-
1219-public:
1220+ void setIconId(const QString &iconId);
1221+ void setScreenshotId(const QString &screenshotId);
1222+
1223+ void setAppId(const QString &value) { m_appId = value; }
1224+ QString appId() const override { return m_appId; }
1225+
1226+ void setName(const QString &value);
1227+ QString name() const override { return m_name; }
1228+
1229+ QString comment() const override { return QString(); }
1230+
1231+ QUrl icon() const override { return m_icon; }
1232+
1233+ void setStage(Stage value);
1234+ Stage stage() const override { return m_stage; }
1235+
1236+ Q_INVOKABLE void setState(State value);
1237+ State state() const override { return m_state; }
1238+
1239+ void setFocused(bool value);
1240+ bool focused() const override { return m_focused; }
1241+
1242+ QString screenshot() const { return m_screenshotFileName; }
1243+
1244+ void setFullscreen(bool value);
1245+ bool fullscreen() const { return m_fullscreen; }
1246+
1247 void setSurface(MirSurfaceItem* surface);
1248 MirSurfaceItem* surface() const { return m_surface; }
1249
1250@@ -92,21 +81,38 @@
1251 QList<MirSurfaceItem*> promptSurfaceList() const;
1252 QQmlListProperty<MirSurfaceItem> promptSurfaces();
1253
1254+ bool manualSurfaceCreation() const { return m_manualSurfaceCreation; }
1255+ void setManualSurfaceCreation(bool value);
1256+
1257 Q_SIGNALS:
1258 void surfaceChanged(MirSurfaceItem*);
1259 void promptSurfacesChanged();
1260-
1261-private Q_SLOTS:
1262- void onStateChanged(State state);
1263-
1264- void createSurface();
1265+ void fullscreenChanged(bool value);
1266+ void manualSurfaceCreationChanged(bool value);
1267+
1268+public Q_SLOTS:
1269+ Q_INVOKABLE void createSurface();
1270
1271 private:
1272 static int promptSurfaceCount(QQmlListProperty<MirSurfaceItem> *prop);
1273 static MirSurfaceItem* promptSurfaceAt(QQmlListProperty<MirSurfaceItem> *prop, int index);
1274+ void setIcon(const QUrl &value);
1275+ void setScreenshot(const QUrl &value);
1276+
1277+ QString m_screenshotFileName;
1278+
1279+ QString m_appId;
1280+ QString m_name;
1281+ QUrl m_icon;
1282+ Stage m_stage;
1283+ State m_state;
1284+ bool m_focused;
1285+ bool m_fullscreen;
1286+ MirSurfaceItem* m_surface;
1287+
1288+ bool m_manualSurfaceCreation;
1289
1290 QQuickItem *m_parentItem;
1291- MirSurfaceItem* m_surface;
1292 QList<MirSurfaceItem*> m_promptSurfaces;
1293 };
1294
1295
1296=== modified file 'tests/mocks/Unity/Application/ApplicationManager.cpp'
1297--- tests/mocks/Unity/Application/ApplicationManager.cpp 2014-08-07 17:15:00 +0000
1298+++ tests/mocks/Unity/Application/ApplicationManager.cpp 2014-08-25 08:53:17 +0000
1299@@ -55,14 +55,32 @@
1300
1301 buildListOfAvailableApplications();
1302
1303+ // polling to find out when the toplevel window has been created as there's
1304+ // no signal telling us that
1305+ connect(&m_windowCreatedTimer, &QTimer::timeout,
1306+ this, &ApplicationManager::onWindowCreatedTimerTimeout);
1307+ m_windowCreatedTimer.setSingleShot(false);
1308+ m_windowCreatedTimer.start(200);
1309+}
1310+
1311+ApplicationManager::~ApplicationManager()
1312+{
1313+}
1314+
1315+void ApplicationManager::onWindowCreatedTimerTimeout()
1316+{
1317+ if (QGuiApplication::topLevelWindows().count() > 0) {
1318+ m_windowCreatedTimer.stop();
1319+ onWindowCreated();
1320+ }
1321+}
1322+
1323+void ApplicationManager::onWindowCreated()
1324+{
1325 startApplication("unity8-dash");
1326 focusApplication("unity8-dash");
1327 }
1328
1329-ApplicationManager::~ApplicationManager()
1330-{
1331-}
1332-
1333 int ApplicationManager::rowCount(const QModelIndex& parent) const {
1334 return !parent.isValid() ? m_runningApplications.size() : 0;
1335 }
1336@@ -87,8 +105,6 @@
1337 return app->state();
1338 case RoleFocused:
1339 return app->focused();
1340- case RoleScreenshot:
1341- return app->screenshot();
1342 case RoleSurface:
1343 return QVariant::fromValue(app->surface());
1344 case RoleFullscreen:
1345@@ -143,6 +159,16 @@
1346 if (!appIndex.isValid()) return;
1347 Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << ApplicationManager::RoleSurface);
1348 });
1349+ connect(application, &ApplicationInfo::focusedChanged, this, [application, this]() {
1350+ QModelIndex appIndex = findIndex(application);
1351+ if (!appIndex.isValid()) return;
1352+ Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << ApplicationManager::RoleFocused);
1353+ });
1354+ connect(application, &ApplicationInfo::stateChanged, this, [application, this]() {
1355+ QModelIndex appIndex = findIndex(application);
1356+ if (!appIndex.isValid()) return;
1357+ Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << ApplicationManager::RoleState);
1358+ });
1359 }
1360
1361 void ApplicationManager::remove(ApplicationInfo *application) {
1362@@ -155,7 +181,7 @@
1363 Q_EMIT countChanged();
1364 if (isEmpty()) Q_EMIT emptyChanged(isEmpty());
1365 }
1366- disconnect(application, &ApplicationInfo::surfaceChanged, this, 0);
1367+ disconnect(application, 0, this, 0);
1368 }
1369
1370 void ApplicationManager::move(int from, int to) {
1371@@ -198,15 +224,7 @@
1372 const QStringList &arguments)
1373 {
1374 Q_UNUSED(arguments)
1375- ApplicationInfo *application = 0;
1376-
1377- for (ApplicationInfo *availableApp : m_availableApplications) {
1378- if (availableApp->appId() == appId) {
1379- application = availableApp;
1380- break;
1381- }
1382- }
1383-
1384+ ApplicationInfo *application = add(appId);
1385 if (!application)
1386 return 0;
1387
1388@@ -214,12 +232,28 @@
1389 && application->stage() == ApplicationInfo::SideStage) {
1390 application->setStage(ApplicationInfo::MainStage);
1391 }
1392- add(application);
1393 application->setState(ApplicationInfo::Running);
1394
1395 return application;
1396 }
1397
1398+ApplicationInfo* ApplicationManager::add(QString appId)
1399+{
1400+ ApplicationInfo *application = 0;
1401+
1402+ for (ApplicationInfo *availableApp : m_availableApplications) {
1403+ if (availableApp->appId() == appId) {
1404+ application = availableApp;
1405+ break;
1406+ }
1407+ }
1408+
1409+ if (application)
1410+ add(application);
1411+
1412+ return application;
1413+}
1414+
1415 bool ApplicationManager::stopApplication(const QString &appId)
1416 {
1417 if (appId == "unity8-dash") {
1418@@ -238,27 +272,6 @@
1419 return true;
1420 }
1421
1422-bool ApplicationManager::updateScreenshot(const QString &appId)
1423-{
1424- int idx = -1;
1425- ApplicationInfo *application = nullptr;
1426- for (int i = 0; i < m_availableApplications.count(); ++i) {
1427- application = m_availableApplications.at(i);
1428- if (application->appId() == appId) {
1429- idx = i;
1430- break;
1431- }
1432- }
1433-
1434- if (idx == -1) {
1435- return false;
1436- }
1437-
1438- QModelIndex appIndex = index(idx);
1439- Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << RoleScreenshot);
1440- return true;
1441-}
1442-
1443 QString ApplicationManager::focusedApplicationId() const {
1444 for (ApplicationInfo *app : m_runningApplications) {
1445 if (app->focused()) {
1446@@ -293,32 +306,18 @@
1447 if (application == nullptr)
1448 return false;
1449
1450- if (application->stage() == ApplicationInfo::MainStage) {
1451- // unfocus currently focused mainstage app
1452- for (ApplicationInfo *app : m_runningApplications) {
1453- if (app->focused() && app->stage() == ApplicationInfo::MainStage) {
1454- app->setFocused(false);
1455- app->setState(ApplicationInfo::Suspended);
1456- }
1457- }
1458-
1459- // focus this app
1460- application->setFocused(true);
1461- application->setState(ApplicationInfo::Running);
1462- } else if (application->stage() == ApplicationInfo::SideStage) {
1463- // unfocus currently focused sidestage app
1464- for (ApplicationInfo *app : m_runningApplications) {
1465- if (app->focused() && app->stage() == ApplicationInfo::SideStage) {
1466- app->setFocused(false);
1467- app->setState(ApplicationInfo::Suspended);
1468- }
1469- }
1470-
1471- // focus this app
1472- application->setFocused(true);
1473- application->setState(ApplicationInfo::Running);
1474+ // unfocus currently focused app
1475+ for (ApplicationInfo *app : m_runningApplications) {
1476+ if (app->focused()) {
1477+ app->setFocused(false);
1478+ app->setState(ApplicationInfo::Suspended);
1479+ }
1480 }
1481
1482+ // focus this app
1483+ application->setFocused(true);
1484+ application->setState(ApplicationInfo::Running);
1485+
1486 // move app to top of stack
1487 move(m_runningApplications.indexOf(application), 0);
1488 Q_EMIT focusedApplicationIdChanged();
1489@@ -341,12 +340,6 @@
1490 Q_EMIT focusedApplicationIdChanged();
1491 }
1492
1493-void ApplicationManager::generateQmlStrings(ApplicationInfo *application)
1494-{
1495- application->setScreenshot(QString("file://%1/Dash/graphics/phone/screenshots/%2@12.png").arg(qmlDirectory())
1496- .arg(application->icon().toString()));
1497-}
1498-
1499 void ApplicationManager::buildListOfAvailableApplications()
1500 {
1501 ApplicationInfo *application;
1502@@ -354,128 +347,120 @@
1503 application = new ApplicationInfo(this);
1504 application->setAppId("unity8-dash");
1505 application->setName("Unity 8 Mock Dash");
1506- application->setIcon(QUrl("unity8-dash"));
1507+ application->setScreenshotId("unity8-dash");
1508 application->setStage(ApplicationInfo::MainStage);
1509- generateQmlStrings(application);
1510 m_availableApplications.append(application);
1511
1512 application = new ApplicationInfo(this);
1513 application->setAppId("dialer-app");
1514 application->setName("Dialer");
1515- application->setIcon(QUrl("dialer"));
1516+ application->setScreenshotId("dialer");
1517+ application->setIconId("dialer-app");
1518 application->setStage(ApplicationInfo::SideStage);
1519- generateQmlStrings(application);
1520 m_availableApplications.append(application);
1521
1522 application = new ApplicationInfo(this);
1523 application->setAppId("camera-app");
1524 application->setName("Camera");
1525- application->setIcon(QUrl("camera"));
1526+ application->setScreenshotId("camera");
1527+ application->setIconId("camera");
1528 application->setFullscreen(true);
1529- generateQmlStrings(application);
1530 m_availableApplications.append(application);
1531
1532 application = new ApplicationInfo(this);
1533 application->setAppId("gallery-app");
1534 application->setName("Gallery");
1535- application->setIcon(QUrl("gallery"));
1536- generateQmlStrings(application);
1537+ application->setScreenshotId("gallery");
1538+ application->setIconId("gallery");
1539+ application->setFullscreen(true);
1540 m_availableApplications.append(application);
1541
1542 application = new ApplicationInfo(this);
1543 application->setAppId("facebook-webapp");
1544 application->setName("Facebook");
1545- application->setIcon(QUrl("facebook"));
1546+ application->setScreenshotId("facebook");
1547+ application->setIconId("facebook");
1548 application->setStage(ApplicationInfo::SideStage);
1549- generateQmlStrings(application);
1550 m_availableApplications.append(application);
1551
1552 application = new ApplicationInfo(this);
1553 application->setAppId("webbrowser-app");
1554 application->setFullscreen(true);
1555 application->setName("Browser");
1556- application->setIcon(QUrl("browser"));
1557- generateQmlStrings(application);
1558+ application->setScreenshotId("browser");
1559+ application->setIconId("browser");
1560 m_availableApplications.append(application);
1561
1562 application = new ApplicationInfo(this);
1563 application->setAppId("twitter-webapp");
1564 application->setName("Twitter");
1565- application->setIcon(QUrl("twitter"));
1566+ application->setScreenshotId("twitter");
1567+ application->setIconId("twitter");
1568 application->setStage(ApplicationInfo::SideStage);
1569- generateQmlStrings(application);
1570+ m_availableApplications.append(application);
1571+
1572+ application = new ApplicationInfo(this);
1573+ application->setAppId("map");
1574+ application->setName("Map");
1575+ application->setIconId("map");
1576+ application->setScreenshotId("map");
1577 m_availableApplications.append(application);
1578
1579 application = new ApplicationInfo(this);
1580 application->setAppId("gmail-webapp");
1581 application->setName("GMail");
1582- application->setIcon(QUrl("gmail"));
1583+ application->setIconId("gmail");
1584 m_availableApplications.append(application);
1585
1586 application = new ApplicationInfo(this);
1587 application->setAppId("ubuntu-weather-app");
1588 application->setName("Weather");
1589- application->setIcon(QUrl("weather"));
1590+ application->setIconId("weather");
1591 application->setStage(ApplicationInfo::SideStage);
1592- generateQmlStrings(application);
1593 m_availableApplications.append(application);
1594
1595 application = new ApplicationInfo(this);
1596 application->setAppId("notes-app");
1597 application->setName("Notepad");
1598- application->setIcon(QUrl("notepad"));
1599+ application->setIconId("notepad");
1600 application->setStage(ApplicationInfo::SideStage);
1601 m_availableApplications.append(application);
1602
1603 application = new ApplicationInfo(this);
1604 application->setAppId("calendar-app");
1605 application->setName("Calendar");
1606- application->setIcon(QUrl("calendar"));
1607+ application->setIconId("calendar");
1608 application->setStage(ApplicationInfo::SideStage);
1609 m_availableApplications.append(application);
1610
1611 application = new ApplicationInfo(this);
1612- application->setAppId("mediaplayer-app");
1613- application->setName("Media Player");
1614- application->setIcon(QUrl("mediaplayer-app"));
1615- application->setFullscreen(true);
1616- m_availableApplications.append(application);
1617-
1618- application = new ApplicationInfo(this);
1619 application->setAppId("evernote");
1620 application->setName("Evernote");
1621- application->setIcon(QUrl("evernote"));
1622- m_availableApplications.append(application);
1623-
1624- application = new ApplicationInfo(this);
1625- application->setAppId("map");
1626- application->setName("Map");
1627- application->setIcon(QUrl("map"));
1628- generateQmlStrings(application);
1629+ application->setIconId("evernote");
1630 m_availableApplications.append(application);
1631
1632 application = new ApplicationInfo(this);
1633 application->setAppId("pinterest");
1634 application->setName("Pinterest");
1635- application->setIcon(QUrl("pinterest"));
1636+ application->setIconId("pinterest");
1637 m_availableApplications.append(application);
1638
1639 application = new ApplicationInfo(this);
1640 application->setAppId("soundcloud");
1641 application->setName("SoundCloud");
1642- application->setIcon(QUrl("soundcloud"));
1643+ application->setIconId("soundcloud");
1644 m_availableApplications.append(application);
1645
1646 application = new ApplicationInfo(this);
1647 application->setAppId("wikipedia");
1648 application->setName("Wikipedia");
1649- application->setIcon(QUrl("wikipedia"));
1650+ application->setIconId("wikipedia");
1651 m_availableApplications.append(application);
1652
1653 application = new ApplicationInfo(this);
1654 application->setAppId("youtube");
1655 application->setName("YouTube");
1656- application->setIcon(QUrl("youtube"));
1657+ application->setIconId("youtube");
1658 m_availableApplications.append(application);
1659 }
1660
1661
1662=== modified file 'tests/mocks/Unity/Application/ApplicationManager.h'
1663--- tests/mocks/Unity/Application/ApplicationManager.h 2014-08-07 17:15:00 +0000
1664+++ tests/mocks/Unity/Application/ApplicationManager.h 2014-08-25 08:53:17 +0000
1665@@ -20,6 +20,7 @@
1666 #include <QObject>
1667 #include <QList>
1668 #include <QStringList>
1669+#include <QTimer>
1670 #include "ApplicationInfo.h"
1671
1672 // unity-api
1673@@ -51,7 +52,7 @@
1674 static ApplicationManager *singleton();
1675
1676 enum MoreRoles {
1677- RoleSurface = RoleScreenshot+1,
1678+ RoleSurface = RoleFocused+1,
1679 RoleFullscreen,
1680 RoleApplication,
1681 };
1682@@ -97,7 +98,6 @@
1683 Q_INVOKABLE ApplicationInfo *startApplication(const QString &appId, const QStringList &arguments = QStringList()) override;
1684 Q_INVOKABLE ApplicationInfo *startApplication(const QString &appId, ExecFlags flags, const QStringList &arguments = QStringList());
1685 Q_INVOKABLE bool stopApplication(const QString &appId) override;
1686- Q_INVOKABLE bool updateScreenshot(const QString &appId) override;
1687
1688 QString focusedApplicationId() const override;
1689 bool suspended() const;
1690@@ -105,6 +105,7 @@
1691
1692 // Only for testing
1693 Q_INVOKABLE QStringList availableApplications();
1694+ Q_INVOKABLE ApplicationInfo* add(QString appId);
1695
1696 QModelIndex findIndex(ApplicationInfo* application);
1697
1698@@ -115,15 +116,18 @@
1699 void focusRequested(const QString &appId);
1700 void emptyChanged(bool empty);
1701
1702+ private Q_SLOTS:
1703+ void onWindowCreatedTimerTimeout();
1704+
1705 private:
1706 void add(ApplicationInfo *application);
1707 void remove(ApplicationInfo* application);
1708- void showApplicationWindow(ApplicationInfo *application);
1709 void buildListOfAvailableApplications();
1710- void generateQmlStrings(ApplicationInfo *application);
1711+ void onWindowCreated();
1712 bool m_suspended;
1713 QList<ApplicationInfo*> m_runningApplications;
1714 QList<ApplicationInfo*> m_availableApplications;
1715+ QTimer m_windowCreatedTimer;
1716
1717 static ApplicationManager *the_application_manager;
1718 };
1719
1720=== modified file 'tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp'
1721--- tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp 2014-07-25 00:10:11 +0000
1722+++ tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp 2014-08-25 08:53:17 +0000
1723@@ -44,11 +44,9 @@
1724 return QImage();
1725 }
1726
1727- QString filePath = QString("%1/Dash/graphics/phone/screenshots/%2@12.png").arg(qmlDirectory()).arg(app->icon().toString());
1728-
1729 QImage image;
1730- if (!image.load(filePath)) {
1731- qWarning() << "failed loading app image" << filePath;
1732+ if (!image.load(app->screenshot())) {
1733+ qWarning() << "failed loading app image" << app->screenshot();
1734 }
1735
1736
1737
1738=== modified file 'tests/mocks/Unity/Application/CMakeLists.txt'
1739--- tests/mocks/Unity/Application/CMakeLists.txt 2014-07-22 13:24:37 +0000
1740+++ tests/mocks/Unity/Application/CMakeLists.txt 2014-08-25 08:53:17 +0000
1741@@ -1,15 +1,13 @@
1742-pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application)
1743+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=3)
1744
1745 set(FakeUnityApplicationQml_SOURCES
1746 plugin.cpp
1747 ApplicationDBusAdaptor.cpp
1748 ApplicationInfo.cpp
1749- ApplicationImage.cpp
1750 ApplicationManager.cpp
1751 ApplicationScreenshotProvider.cpp
1752 MirSurfaceItem.cpp
1753 SurfaceManager.cpp
1754- VirtualKeyboard.cpp
1755 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
1756 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
1757 )
1758
1759=== removed file 'tests/mocks/Unity/Application/InputFilterArea.qml'
1760--- tests/mocks/Unity/Application/InputFilterArea.qml 2013-06-05 22:03:08 +0000
1761+++ tests/mocks/Unity/Application/InputFilterArea.qml 1970-01-01 00:00:00 +0000
1762@@ -1,21 +0,0 @@
1763-/*
1764- * Copyright (C) 2013 Canonical, Ltd.
1765- *
1766- * This program is free software; you can redistribute it and/or modify
1767- * it under the terms of the GNU General Public License as published by
1768- * the Free Software Foundation; version 3.
1769- *
1770- * This program is distributed in the hope that it will be useful,
1771- * but WITHOUT ANY WARRANTY; without even the implied warranty of
1772- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1773- * GNU General Public License for more details.
1774- *
1775- * You should have received a copy of the GNU General Public License
1776- * along with this program. If not, see <http://www.gnu.org/licenses/>.
1777- */
1778-
1779-import QtQuick 2.0;
1780-
1781-Item {
1782- property bool blockInput: false
1783-}
1784
1785=== modified file 'tests/mocks/Unity/Application/MirSurfaceItem.cpp'
1786--- tests/mocks/Unity/Application/MirSurfaceItem.cpp 2014-08-08 09:40:50 +0000
1787+++ tests/mocks/Unity/Application/MirSurfaceItem.cpp 2014-08-25 08:53:17 +0000
1788@@ -17,33 +17,67 @@
1789 #include "MirSurfaceItem.h"
1790 #include "ApplicationInfo.h"
1791
1792-#include <QPainter>
1793+#include <paths.h>
1794+
1795+#include <QGuiApplication>
1796+#include <QQuickView>
1797+#include <QQmlProperty>
1798 #include <QQmlEngine>
1799+#include <QString>
1800+
1801+#include <QDebug>
1802
1803 MirSurfaceItem::MirSurfaceItem(const QString& name,
1804 MirSurfaceItem::Type type,
1805 MirSurfaceItem::State state,
1806 const QUrl& screenshot,
1807+ const QString &qmlFilePath,
1808 QQuickItem *parent)
1809- : QQuickPaintedItem(parent)
1810+ : QQuickItem(parent)
1811 , m_application(nullptr)
1812 , m_name(name)
1813 , m_type(type)
1814 , m_state(state)
1815- , m_img(screenshot.isLocalFile() ? screenshot.toLocalFile() : screenshot.toString())
1816 , m_parentSurface(nullptr)
1817- , m_haveInputMethod(false)
1818+ , m_qmlItem(nullptr)
1819+ , m_screenshotUrl(screenshot)
1820 {
1821 qDebug() << "MirSurfaceItem::MirSurfaceItem() " << this->name();
1822
1823 QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
1824
1825- // The virtual keyboard (input method) has a big transparent area so that
1826- // content behind it show through
1827- setFillColor(Qt::transparent);
1828-
1829 connect(this, &QQuickItem::focusChanged,
1830 this, &MirSurfaceItem::onFocusChanged);
1831+
1832+ // The assumptions I make here really should hold.
1833+ QQuickView *quickView =
1834+ qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
1835+
1836+ QString qmlComponentFilePath;
1837+ if (!qmlFilePath.isEmpty()) {
1838+ qmlComponentFilePath.append(qmlFilePath);
1839+ } else {
1840+ qmlComponentFilePath = QString("%1/Unity/Application/MirSurfaceItem.qml")
1841+ .arg(mockPluginsDir());
1842+ }
1843+
1844+ m_qmlContentComponent = new QQmlComponent(quickView->engine(), qmlComponentFilePath);
1845+
1846+ switch (m_qmlContentComponent->status()) {
1847+ case QQmlComponent::Ready:
1848+ createQmlContentItem();
1849+ break;
1850+ case QQmlComponent::Loading:
1851+ connect(m_qmlContentComponent, &QQmlComponent::statusChanged,
1852+ this, &MirSurfaceItem::onComponentStatusChanged);
1853+ break;
1854+ case QQmlComponent::Error:
1855+ printComponentErrors();
1856+ qFatal("MirSurfaceItem: failed to create content component.");
1857+ break;
1858+ default:
1859+ qFatal("MirSurfaceItem: Unhandled component status");
1860+ }
1861 }
1862
1863 MirSurfaceItem::~MirSurfaceItem()
1864@@ -61,10 +95,11 @@
1865 m_application->removeSurface(this);
1866 }
1867
1868-void MirSurfaceItem::paint(QPainter * painter)
1869+void MirSurfaceItem::printComponentErrors()
1870 {
1871- if (!m_img.isNull()) {
1872- painter->drawImage(contentsBoundingRect(), m_img, QRect(QPoint(0,0), m_img.size()));
1873+ QList<QQmlError> errors = m_qmlContentComponent->errors();
1874+ for (int i = 0; i < errors.count(); ++i) {
1875+ qDebug() << errors[i];
1876 }
1877 }
1878
1879@@ -155,19 +190,24 @@
1880 }
1881
1882
1883-void MirSurfaceItem::touchEvent(QTouchEvent * event)
1884+void MirSurfaceItem::onQmlWantInputMethodChanged()
1885 {
1886- if (event->type() == QEvent::TouchBegin && hasFocus()) {
1887+ QQmlProperty prop(m_qmlItem, "wantInputMethod");
1888+ bool wantInputMethod = prop.read().toBool();
1889+
1890+ if (hasFocus() && wantInputMethod) {
1891 Q_EMIT inputMethodRequested();
1892- m_haveInputMethod = true;
1893 }
1894 }
1895
1896 void MirSurfaceItem::onFocusChanged()
1897 {
1898- if (!hasFocus() && m_haveInputMethod) {
1899+ QQmlProperty prop(m_qmlItem, "wantInputMethod");
1900+ bool wantInputMethod = prop.read().toBool();
1901+
1902+ if (!hasFocus() && wantInputMethod) {
1903 Q_EMIT inputMethodDismissed();
1904- m_haveInputMethod = false;
1905+ prop.write(QVariant::fromValue(false));
1906 }
1907 }
1908
1909@@ -178,3 +218,34 @@
1910 Q_EMIT stateChanged(newState);
1911 }
1912 }
1913+
1914+void MirSurfaceItem::onComponentStatusChanged(QQmlComponent::Status status)
1915+{
1916+ if (status == QQmlComponent::Ready) {
1917+ createQmlContentItem();
1918+ }
1919+}
1920+
1921+void MirSurfaceItem::createQmlContentItem()
1922+{
1923+ qDebug() << "MirSurfaceItem::createQmlContentItem()";
1924+
1925+ m_qmlItem = qobject_cast<QQuickItem*>(m_qmlContentComponent->create());
1926+ m_qmlItem->setParentItem(this);
1927+
1928+ setImplicitWidth(m_qmlItem->implicitWidth());
1929+ setImplicitHeight(m_qmlItem->implicitHeight());
1930+
1931+ {
1932+ QQmlProperty screenshotSource(m_qmlItem, "screenshotSource");
1933+ screenshotSource.write(QVariant::fromValue(m_screenshotUrl));
1934+ }
1935+
1936+ {
1937+ QQmlProperty prop(m_qmlItem, "wantInputMethod");
1938+ if (prop.type() == QQmlProperty::Property) {
1939+ bool ok = prop.connectNotifySignal(this, SLOT(onQmlWantInputMethodChanged()));
1940+ if (!ok) qCritical("MirSurfaceItem: failed to connect to wantInputMethod notify signal");
1941+ }
1942+ }
1943+}
1944
1945=== modified file 'tests/mocks/Unity/Application/MirSurfaceItem.h'
1946--- tests/mocks/Unity/Application/MirSurfaceItem.h 2014-08-07 17:15:00 +0000
1947+++ tests/mocks/Unity/Application/MirSurfaceItem.h 2014-08-25 08:53:17 +0000
1948@@ -17,12 +17,13 @@
1949 #ifndef MIRSURFACEITEM_H
1950 #define MIRSURFACEITEM_H
1951
1952-#include <QQuickPaintedItem>
1953-#include <QImage>
1954+#include <QQuickItem>
1955+#include <QQmlComponent>
1956+#include <QUrl>
1957
1958 class ApplicationInfo;
1959
1960-class MirSurfaceItem : public QQuickPaintedItem
1961+class MirSurfaceItem : public QQuickItem
1962 {
1963 Q_OBJECT
1964 Q_ENUMS(Type)
1965@@ -59,6 +60,7 @@
1966 Type type,
1967 State state,
1968 const QUrl& screenshot,
1969+ const QString &qmlFilePath = QString(),
1970 QQuickItem *parent = 0);
1971 ~MirSurfaceItem();
1972
1973@@ -76,9 +78,6 @@
1974 Q_INVOKABLE void setState(State newState);
1975 Q_INVOKABLE void release();
1976
1977- void paint(QPainter * painter) override;
1978- void touchEvent(QTouchEvent * event) override;
1979-
1980 Q_SIGNALS:
1981 void typeChanged(Type);
1982 void stateChanged(State);
1983@@ -93,9 +92,8 @@
1984
1985 private Q_SLOTS:
1986 void onFocusChanged();
1987-
1988-protected:
1989- const QImage &screenshotImage() { return m_img; }
1990+ void onComponentStatusChanged(QQmlComponent::Status status);
1991+ void onQmlWantInputMethodChanged();
1992
1993 private:
1994 void addChildSurface(MirSurfaceItem* surface);
1995@@ -105,15 +103,20 @@
1996 static int childSurfaceCount(QQmlListProperty<MirSurfaceItem> *prop);
1997 static MirSurfaceItem* childSurfaceAt(QQmlListProperty<MirSurfaceItem> *prop, int index);
1998
1999+ void createQmlContentItem();
2000+ void printComponentErrors();
2001+
2002 ApplicationInfo* m_application;
2003 const QString m_name;
2004 const Type m_type;
2005 State m_state;
2006- const QImage m_img;
2007
2008 MirSurfaceItem* m_parentSurface;
2009 QList<MirSurfaceItem*> m_children;
2010- bool m_haveInputMethod;
2011+
2012+ QQmlComponent *m_qmlContentComponent;
2013+ QQuickItem *m_qmlItem;
2014+ QUrl m_screenshotUrl;
2015 };
2016
2017 Q_DECLARE_METATYPE(MirSurfaceItem*)
2018
2019=== added file 'tests/mocks/Unity/Application/MirSurfaceItem.qml'
2020--- tests/mocks/Unity/Application/MirSurfaceItem.qml 1970-01-01 00:00:00 +0000
2021+++ tests/mocks/Unity/Application/MirSurfaceItem.qml 2014-08-25 08:53:17 +0000
2022@@ -0,0 +1,57 @@
2023+/*
2024+ * Copyright 2014 Canonical Ltd.
2025+ *
2026+ * This program is free software; you can redistribute it and/or modify
2027+ * it under the terms of the GNU Lesser General Public License as published by
2028+ * the Free Software Foundation; version 3.
2029+ *
2030+ * This program is distributed in the hope that it will be useful,
2031+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2032+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2033+ * GNU Lesser General Public License for more details.
2034+ *
2035+ * You should have received a copy of the GNU Lesser General Public License
2036+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2037+ */
2038+
2039+import QtQuick 2.0
2040+
2041+Rectangle {
2042+ objectName: "fakeSurfaceQML"
2043+ id: root
2044+ color: "pink"
2045+
2046+ anchors.fill: parent
2047+
2048+ implicitWidth: units.gu(40)
2049+ implicitHeight: units.gu(70)
2050+
2051+ property alias screenshotSource: screenshotImage.source
2052+
2053+ property bool wantInputMethod: false
2054+
2055+ property int touchPressCount: 0
2056+ property int touchReleaseCount: 0
2057+
2058+ Image {
2059+ id: screenshotImage
2060+ anchors.fill: parent
2061+ fillMode: Image.Stretch
2062+ }
2063+
2064+ Text {
2065+ anchors.fill: parent
2066+ text: "SURFACE"
2067+ color: "yellow"
2068+ font.bold: true
2069+ fontSizeMode: Text.Fit
2070+ minimumPixelSize: 10; font.pixelSize: 200
2071+ verticalAlignment: Text.AlignVCenter
2072+ }
2073+
2074+ MultiPointTouchArea {
2075+ anchors.fill: parent
2076+ onPressed: { root.wantInputMethod = true; root.touchPressCount++; }
2077+ onReleased: { root.touchReleaseCount++; }
2078+ }
2079+}
2080
2081=== removed file 'tests/mocks/Unity/Application/OSKController.qml'
2082--- tests/mocks/Unity/Application/OSKController.qml 2013-09-10 15:06:32 +0000
2083+++ tests/mocks/Unity/Application/OSKController.qml 1970-01-01 00:00:00 +0000
2084@@ -1,20 +0,0 @@
2085-/*
2086- * Copyright (C) 2013 Canonical, Ltd.
2087- *
2088- * This program is free software; you can redistribute it and/or modify
2089- * it under the terms of the GNU General Public License as published by
2090- * the Free Software Foundation; version 3.
2091- *
2092- * This program is distributed in the hope that it will be useful,
2093- * but WITHOUT ANY WARRANTY; without even the implied warranty of
2094- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2095- * GNU General Public License for more details.
2096- *
2097- * You should have received a copy of the GNU General Public License
2098- * along with this program. If not, see <http://www.gnu.org/licenses/>.
2099- */
2100-
2101-import QtQuick 2.0;
2102-
2103-Item {
2104-}
2105
2106=== modified file 'tests/mocks/Unity/Application/SurfaceManager.cpp'
2107--- tests/mocks/Unity/Application/SurfaceManager.cpp 2014-07-24 19:22:20 +0000
2108+++ tests/mocks/Unity/Application/SurfaceManager.cpp 2014-08-25 08:53:17 +0000
2109@@ -16,9 +16,8 @@
2110
2111 #include "SurfaceManager.h"
2112
2113+#include <paths.h>
2114 #include "MirSurfaceItem.h"
2115-#include "VirtualKeyboard.h"
2116-
2117
2118 SurfaceManager *SurfaceManager::the_surface_manager = nullptr;
2119
2120@@ -66,7 +65,19 @@
2121 MirSurfaceItem *SurfaceManager::inputMethodSurface()
2122 {
2123 if (!m_virtualKeyboard) {
2124- m_virtualKeyboard = new VirtualKeyboard(MirSurfaceItem::Minimized);
2125+
2126+ QString screenshotPath = QString("file://%1/Dash/graphics/phone/screenshots/vkb_portrait.png")
2127+ .arg(qmlDirectory());
2128+
2129+ QString qmlFilePath = QString("%1/Unity/Application/VirtualKeyboard.qml")
2130+ .arg(mockPluginsDir());
2131+
2132+ m_virtualKeyboard = new MirSurfaceItem(
2133+ "input-method",
2134+ MirSurfaceItem::InputMethod,
2135+ MirSurfaceItem::Minimized,
2136+ screenshotPath,
2137+ qmlFilePath);
2138 Q_EMIT surfaceCreated(m_virtualKeyboard);
2139 }
2140 return m_virtualKeyboard;
2141
2142=== modified file 'tests/mocks/Unity/Application/SurfaceManager.h'
2143--- tests/mocks/Unity/Application/SurfaceManager.h 2014-07-24 19:22:20 +0000
2144+++ tests/mocks/Unity/Application/SurfaceManager.h 2014-08-25 08:53:17 +0000
2145@@ -20,7 +20,6 @@
2146 #include <QObject>
2147
2148 class MirSurfaceItem;
2149-class VirtualKeyboard;
2150
2151 class SurfaceManager : public QObject
2152 {
2153@@ -48,7 +47,7 @@
2154
2155 private:
2156 static SurfaceManager *the_surface_manager;
2157- VirtualKeyboard *m_virtualKeyboard;
2158+ MirSurfaceItem *m_virtualKeyboard;
2159 };
2160
2161 #endif // SURFACEMANAGER_H
2162
2163=== removed file 'tests/mocks/Unity/Application/VirtualKeyboard.cpp'
2164--- tests/mocks/Unity/Application/VirtualKeyboard.cpp 2014-07-22 10:29:25 +0000
2165+++ tests/mocks/Unity/Application/VirtualKeyboard.cpp 1970-01-01 00:00:00 +0000
2166@@ -1,55 +0,0 @@
2167-/*
2168- * Copyright (C) 2014 Canonical, Ltd.
2169- *
2170- * This program is free software; you can redistribute it and/or modify
2171- * it under the terms of the GNU General Public License as published by
2172- * the Free Software Foundation; version 3.
2173- *
2174- * This program is distributed in the hope that it will be useful,
2175- * but WITHOUT ANY WARRANTY; without even the implied warranty of
2176- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2177- * GNU General Public License for more details.
2178- *
2179- * You should have received a copy of the GNU General Public License
2180- * along with this program. If not, see <http://www.gnu.org/licenses/>.
2181- */
2182-
2183-#include "VirtualKeyboard.h"
2184-
2185-#include <paths.h>
2186-
2187-VirtualKeyboard::VirtualKeyboard(State state, QQuickItem *parent)
2188- : MirSurfaceItem("input-method",
2189- MirSurfaceItem::InputMethod,
2190- state,
2191- QString("file://%1/Dash/graphics/phone/screenshots/vkb_portrait.png").arg(qmlDirectory()),
2192- parent)
2193-{
2194-}
2195-
2196-void VirtualKeyboard::touchEvent(QTouchEvent *event)
2197-{
2198- if (event->type() == QEvent::TouchBegin && !hasTouchInsideKeyboard(event)) {
2199- event->ignore();
2200- }
2201-}
2202-
2203-bool VirtualKeyboard::hasTouchInsideKeyboard(QTouchEvent *event)
2204-{
2205- const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
2206- for (int i = 0; i < touchPoints.count(); ++i) {
2207- QPoint pos = touchPoints.at(i).pos().toPoint();
2208-
2209- // Map to image coords
2210- int imageX = (int)( ( ((qreal)pos.x()) / width() ) * ((qreal)screenshotImage().size().width()) );
2211- int imageY = (int)( ( ((qreal)pos.y()) / height() ) * ((qreal)screenshotImage().size().height()) );
2212-
2213- QRgb pixelHit = screenshotImage().pixel(imageX, imageY);
2214-
2215- // The keyboard depicted in the image is in its opaque part.
2216- if (qAlpha(pixelHit) != 0) {
2217- return true;
2218- }
2219- }
2220- return false;
2221-}
2222
2223=== removed file 'tests/mocks/Unity/Application/VirtualKeyboard.h'
2224--- tests/mocks/Unity/Application/VirtualKeyboard.h 2014-07-22 10:29:25 +0000
2225+++ tests/mocks/Unity/Application/VirtualKeyboard.h 1970-01-01 00:00:00 +0000
2226@@ -1,35 +0,0 @@
2227-/*
2228- * Copyright (C) 2014 Canonical, Ltd.
2229- *
2230- * This program is free software; you can redistribute it and/or modify
2231- * it under the terms of the GNU General Public License as published by
2232- * the Free Software Foundation; version 3.
2233- *
2234- * This program is distributed in the hope that it will be useful,
2235- * but WITHOUT ANY WARRANTY; without even the implied warranty of
2236- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2237- * GNU General Public License for more details.
2238- *
2239- * You should have received a copy of the GNU General Public License
2240- * along with this program. If not, see <http://www.gnu.org/licenses/>.
2241- */
2242-
2243-#ifndef VIRTUALKEYBOARD_H
2244-#define VIRTUALKEYBOARD_H
2245-
2246-#include "MirSurfaceItem.h"
2247-
2248-class VirtualKeyboard : public MirSurfaceItem
2249-{
2250- Q_OBJECT
2251-public:
2252- VirtualKeyboard(State state,
2253- QQuickItem *parent = 0);
2254-
2255- void touchEvent(QTouchEvent * event) override;
2256-
2257-private:
2258- bool hasTouchInsideKeyboard(QTouchEvent *event);
2259-};
2260-
2261-#endif // VIRTUALKEYBOARD_H
2262
2263=== added file 'tests/mocks/Unity/Application/VirtualKeyboard.qml'
2264--- tests/mocks/Unity/Application/VirtualKeyboard.qml 1970-01-01 00:00:00 +0000
2265+++ tests/mocks/Unity/Application/VirtualKeyboard.qml 2014-08-25 08:53:17 +0000
2266@@ -0,0 +1,40 @@
2267+/*
2268+ * Copyright 2014 Canonical Ltd.
2269+ *
2270+ * This program is free software; you can redistribute it and/or modify
2271+ * it under the terms of the GNU Lesser General Public License as published by
2272+ * the Free Software Foundation; version 3.
2273+ *
2274+ * This program is distributed in the hope that it will be useful,
2275+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2276+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2277+ * GNU Lesser General Public License for more details.
2278+ *
2279+ * You should have received a copy of the GNU Lesser General Public License
2280+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2281+ */
2282+
2283+import QtQuick 2.0
2284+
2285+Item {
2286+ implicitWidth: units.gu(40)
2287+ implicitHeight: units.gu(70)
2288+
2289+ anchors.fill: parent
2290+
2291+ property alias screenshotSource: screenshotImage.source
2292+
2293+ property bool landscape: width > height
2294+
2295+ Image {
2296+ id: screenshotImage
2297+ height: landscape ? parent.height * 0.4 : width * 0.6
2298+ anchors.bottom: parent.bottom
2299+ anchors.left: parent.left
2300+ anchors.right: parent.right
2301+
2302+ MultiPointTouchArea {
2303+ anchors.fill: parent
2304+ }
2305+ }
2306+}
2307
2308=== modified file 'tests/mocks/Unity/Application/plugin.cpp'
2309--- tests/mocks/Unity/Application/plugin.cpp 2014-07-19 11:52:38 +0000
2310+++ tests/mocks/Unity/Application/plugin.cpp 2014-08-25 08:53:17 +0000
2311@@ -16,7 +16,6 @@
2312
2313 #include "plugin.h"
2314 #include "ApplicationInfo.h"
2315-#include "ApplicationImage.h"
2316 #include "ApplicationManager.h"
2317 #include "ApplicationScreenshotProvider.h"
2318 #include "MirSurfaceItem.h"
2319@@ -49,8 +48,6 @@
2320 qmlRegisterType<ApplicationInfo>(uri, 0, 1, "ApplicationInfo");
2321
2322 qmlRegisterUncreatableType<MirSurfaceItem>(uri, 0, 1, "MirSurfaceItem", "MirSurfaceItem can't be instantiated from QML");
2323-
2324- qmlRegisterType<ApplicationImage>(uri, 0, 1, "ApplicationImage");
2325 }
2326
2327 void FakeUnityApplicationQmlPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
2328
2329=== modified file 'tests/mocks/Unity/Application/qmldir'
2330--- tests/mocks/Unity/Application/qmldir 2014-05-02 22:57:21 +0000
2331+++ tests/mocks/Unity/Application/qmldir 2014-08-25 08:53:17 +0000
2332@@ -1,6 +1,3 @@
2333 module Unity.Application
2334 plugin FakeUnityApplicationQml
2335 typeinfo Application.qmltypes
2336-
2337-InputFilterArea 0.1 InputFilterArea.qml
2338-OSKController 0.1 OSKController.qml
2339
2340=== modified file 'tests/plugins/Unity/Launcher/CMakeLists.txt'
2341--- tests/plugins/Unity/Launcher/CMakeLists.txt 2014-05-30 09:22:58 +0000
2342+++ tests/plugins/Unity/Launcher/CMakeLists.txt 2014-08-25 08:53:17 +0000
2343@@ -1,6 +1,6 @@
2344 pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
2345 pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=3)
2346-pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=1)
2347+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=3)
2348
2349 include_directories(
2350 ${CMAKE_CURRENT_BINARY_DIR}
2351
2352=== modified file 'tests/plugins/Unity/Launcher/launchermodeltest.cpp'
2353--- tests/plugins/Unity/Launcher/launchermodeltest.cpp 2014-07-03 12:03:57 +0000
2354+++ tests/plugins/Unity/Launcher/launchermodeltest.cpp 2014-08-25 08:53:17 +0000
2355@@ -93,7 +93,6 @@
2356 m_list.takeAt(index)->deleteLater();
2357 endRemoveRows();
2358 }
2359- bool updateScreenshot(const QString &appId) { Q_UNUSED(appId); return true; }
2360 bool requestFocusApplication(const QString &appId) { Q_UNUSED(appId); return true; }
2361 bool suspended() const { return false; }
2362 void setSuspended(bool) {}
2363
2364=== modified file 'tests/qmltests/CMakeLists.txt'
2365--- tests/qmltests/CMakeLists.txt 2014-08-20 08:39:09 +0000
2366+++ tests/qmltests/CMakeLists.txt 2014-08-25 08:53:17 +0000
2367@@ -76,4 +76,7 @@
2368 # These MenuItemFactory tests need the test/mocks/ to come before plugins/
2369 add_qml_test(Panel/Indicators MenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
2370 add_qml_test(Panel/Indicators MessageMenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
2371+add_qml_test(Stages ApplicationWindow ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
2372 add_qml_test(Stages PhoneStage ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
2373+add_qml_test(Stages SpreadDelegate ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
2374+add_qml_test(Stages SurfaceContainer ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
2375
2376=== added file 'tests/qmltests/Stages/tst_ApplicationWindow.qml'
2377--- tests/qmltests/Stages/tst_ApplicationWindow.qml 1970-01-01 00:00:00 +0000
2378+++ tests/qmltests/Stages/tst_ApplicationWindow.qml 2014-08-25 08:53:17 +0000
2379@@ -0,0 +1,346 @@
2380+/*
2381+ * Copyright 2014 Canonical Ltd.
2382+ *
2383+ * This program is free software; you can redistribute it and/or modify
2384+ * it under the terms of the GNU General Public License as published by
2385+ * the Free Software Foundation; version 3.
2386+ *
2387+ * This program is distributed in the hope that it will be useful,
2388+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2389+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2390+ * GNU General Public License for more details.
2391+ *
2392+ * You should have received a copy of the GNU General Public License
2393+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2394+ */
2395+
2396+import QtQuick 2.0
2397+import QtTest 1.0
2398+import Unity.Test 0.1 as UT
2399+import ".."
2400+import "../../../qml/Stages"
2401+import Ubuntu.Components 0.1
2402+import Ubuntu.Components.ListItems 1.0 as ListItem
2403+import Unity.Application 0.1
2404+
2405+Rectangle {
2406+ color: "red"
2407+ id: root
2408+ width: units.gu(70)
2409+ height: units.gu(70)
2410+
2411+ Component.onCompleted: {
2412+ root.fakeApplication = ApplicationManager.add("gallery-app");
2413+ root.fakeApplication.manualSurfaceCreation = true;
2414+ root.fakeApplication.setState(ApplicationInfo.Starting);
2415+ }
2416+ property QtObject fakeApplication: null
2417+
2418+ Connections {
2419+ target: fakeApplication
2420+ onSurfaceChanged: {
2421+ surfaceCheckbox.checked = fakeApplication.surface !== null;
2422+ }
2423+ }
2424+
2425+ Component {
2426+ id: applicationWindowComponent
2427+ ApplicationWindow {
2428+ anchors.fill: parent
2429+ application: fakeApplication
2430+ }
2431+ }
2432+ Loader {
2433+ id: applicationWindowLoader
2434+ anchors {
2435+ top: parent.top
2436+ bottom: parent.bottom
2437+ left: parent.left
2438+ }
2439+ width: units.gu(40)
2440+ sourceComponent: applicationWindowComponent
2441+ }
2442+
2443+ Rectangle {
2444+ color: "white"
2445+ anchors {
2446+ top: parent.top
2447+ bottom: parent.bottom
2448+ left: applicationWindowLoader.right
2449+ right: parent.right
2450+ }
2451+
2452+ Column {
2453+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
2454+ spacing: units.gu(1)
2455+ Row {
2456+ anchors { left: parent.left; right: parent.right }
2457+ CheckBox {
2458+ id: surfaceCheckbox; checked: false
2459+ onCheckedChanged: {
2460+ if (applicationWindowLoader.status !== Loader.Ready)
2461+ return;
2462+
2463+ if (checked && !fakeApplication.surface) {
2464+ fakeApplication.createSurface();
2465+ } else if (!checked && fakeApplication.surface) {
2466+ fakeApplication.surface.release();
2467+ }
2468+ }
2469+ }
2470+ Label { text: "Surface" }
2471+ }
2472+ ListItem.ItemSelector {
2473+ id: appStateSelector
2474+ anchors { left: parent.left; right: parent.right }
2475+ text: "Application state"
2476+ model: ["Starting",
2477+ "Running",
2478+ "Suspended",
2479+ "Stopped"]
2480+ property int selectedApplicationState: {
2481+ if (model[selectedIndex] === "Starting") {
2482+ return ApplicationInfo.Starting;
2483+ } else if (model[selectedIndex] === "Running") {
2484+ return ApplicationInfo.Running;
2485+ } else if (model[selectedIndex] === "Suspended") {
2486+ return ApplicationInfo.Suspended;
2487+ } else {
2488+ return ApplicationInfo.Stopped;
2489+ }
2490+ }
2491+ onSelectedApplicationStateChanged: {
2492+ // state is a read-only property, thus we have to call the setter function
2493+ fakeApplication.setState(selectedApplicationState);
2494+ }
2495+ }
2496+ }
2497+ }
2498+
2499+ UT.UnityTestCase {
2500+ id: testCase
2501+ name: "ApplicationWindow"
2502+ when: windowShown
2503+
2504+ // just to make them shorter
2505+ property int appStarting: ApplicationInfo.Starting
2506+ property int appRunning: ApplicationInfo.Running
2507+ property int appSuspended: ApplicationInfo.Suspended
2508+ property int appStopped: ApplicationInfo.Stopped
2509+
2510+ function setApplicationState(appState) {
2511+ switch (appState) {
2512+ case appStarting:
2513+ appStateSelector.selectedIndex = 0;
2514+ break;
2515+ case appRunning:
2516+ appStateSelector.selectedIndex = 1;
2517+ break;
2518+ case appSuspended:
2519+ appStateSelector.selectedIndex = 2;
2520+ break;
2521+ case appStopped:
2522+ appStateSelector.selectedIndex = 3;
2523+ break;
2524+ }
2525+ }
2526+
2527+ // holds some of the internal ApplicationWindow objects we probe during the tests
2528+ property var stateGroup: null
2529+
2530+ function findInterestingApplicationWindowChildren() {
2531+ stateGroup = findInvisibleChild(applicationWindowLoader.item, "applicationWindowStateGroup");
2532+ verify(stateGroup);
2533+ }
2534+
2535+ function forgetApplicationWindowChildren() {
2536+ stateGroup = null;
2537+ }
2538+
2539+ // wait until any transition animation has finished
2540+ function waitUntilTransitionsEnd() {
2541+ var transitions = stateGroup.transitions;
2542+ for (var i = 0; i < transitions.length; ++i) {
2543+ var transition = transitions[i];
2544+ tryCompare(transition, "running", false, 2000);
2545+ }
2546+ }
2547+
2548+ function init() {
2549+ findInterestingApplicationWindowChildren();
2550+ }
2551+
2552+ function cleanup() {
2553+ forgetApplicationWindowChildren();
2554+
2555+ // reload our test subject to get it in a fresh state once again
2556+ applicationWindowLoader.active = false;
2557+
2558+ appStateSelector.selectedIndex = 0;
2559+ surfaceCheckbox.checked = false;
2560+
2561+ if (fakeApplication.surface)
2562+ fakeApplication.surface.release();
2563+
2564+ applicationWindowLoader.active = true;
2565+ }
2566+
2567+ function test_showSplashUntilAppFullyInit_data() {
2568+ return [
2569+ {tag: "state=Running then create surface", swapInitOrder: false},
2570+
2571+ {tag: "create surface then state=Running", swapInitOrder: true},
2572+ ]
2573+ }
2574+
2575+ function test_showSplashUntilAppFullyInit() {
2576+ verify(stateGroup.state === "splashScreen");
2577+
2578+ if (data.swapInitOrder) {
2579+ surfaceCheckbox.checked = true;
2580+ } else {
2581+ setApplicationState(appRunning);
2582+ }
2583+
2584+ verify(stateGroup.state === "splashScreen");
2585+
2586+ if (data.swapInitOrder) {
2587+ setApplicationState(appRunning);
2588+ } else {
2589+ surfaceCheckbox.checked = true;
2590+ }
2591+
2592+ tryCompare(stateGroup, "state", "surface");
2593+ }
2594+
2595+ function test_suspendedAppShowsSurface() {
2596+ surfaceCheckbox.checked = true;
2597+ setApplicationState(appRunning);
2598+
2599+ tryCompare(stateGroup, "state", "surface");
2600+
2601+ waitUntilTransitionsEnd();
2602+
2603+ setApplicationState(appSuspended);
2604+
2605+ verify(stateGroup.state === "surface");
2606+ waitUntilTransitionsEnd();
2607+ }
2608+
2609+ function test_killedAppShowsScreenshot() {
2610+ surfaceCheckbox.checked = true;
2611+ setApplicationState(appRunning);
2612+ tryCompare(stateGroup, "state", "surface");
2613+
2614+ setApplicationState(appSuspended);
2615+
2616+ verify(stateGroup.state === "surface");
2617+ verify(fakeApplication.surface !== null);
2618+
2619+ // kill it!
2620+ setApplicationState(appStopped);
2621+
2622+ tryCompare(stateGroup, "state", "screenshot");
2623+ tryCompare(fakeApplication, "surface", null);
2624+ }
2625+
2626+ function test_restartApp() {
2627+ var screenshotImage = findChild(applicationWindowLoader.item, "screenshotImage");
2628+
2629+ surfaceCheckbox.checked = true;
2630+ setApplicationState(appRunning);
2631+ tryCompare(stateGroup, "state", "surface");
2632+ waitUntilTransitionsEnd();
2633+
2634+ setApplicationState(appSuspended);
2635+
2636+ // kill it
2637+ setApplicationState(appStopped);
2638+
2639+ tryCompare(stateGroup, "state", "screenshot");
2640+ waitUntilTransitionsEnd();
2641+ tryCompare(fakeApplication, "surface", null);
2642+
2643+ // and restart it
2644+ setApplicationState(appStarting);
2645+
2646+ waitUntilTransitionsEnd();
2647+ verify(stateGroup.state === "screenshot");
2648+ verify(fakeApplication.surface === null);
2649+
2650+ setApplicationState(appRunning);
2651+
2652+ waitUntilTransitionsEnd();
2653+ verify(stateGroup.state === "screenshot");
2654+
2655+ surfaceCheckbox.checked = true;
2656+
2657+ tryCompare(stateGroup, "state", "surface");
2658+ tryCompare(screenshotImage, "status", Image.Null);
2659+ }
2660+
2661+ function test_appCrashed() {
2662+ surfaceCheckbox.checked = true;
2663+ setApplicationState(appRunning);
2664+ tryCompare(stateGroup, "state", "surface");
2665+ waitUntilTransitionsEnd();
2666+
2667+ // oh, it crashed...
2668+ setApplicationState(appStopped);
2669+
2670+ tryCompare(stateGroup, "state", "screenshot");
2671+ tryCompare(fakeApplication, "surface", null);
2672+ }
2673+
2674+ function test_keepSurfaceWhileInvisible() {
2675+ surfaceCheckbox.checked = true;
2676+ setApplicationState(appRunning);
2677+ tryCompare(stateGroup, "state", "surface");
2678+ waitUntilTransitionsEnd();
2679+ verify(fakeApplication.surface !== null);
2680+
2681+ applicationWindowLoader.item.visible = false;
2682+
2683+ waitUntilTransitionsEnd();
2684+ verify(stateGroup.state === "surface");
2685+ verify(fakeApplication.surface !== null);
2686+
2687+ applicationWindowLoader.item.visible = true;
2688+
2689+ waitUntilTransitionsEnd();
2690+ verify(stateGroup.state === "surface");
2691+ verify(fakeApplication.surface !== null);
2692+ }
2693+
2694+ function test_touchesReachSurfaceWhenItsShown() {
2695+ setApplicationState(appRunning);
2696+ surfaceCheckbox.checked = true;
2697+
2698+ tryCompare(stateGroup, "state", "surface");
2699+
2700+ waitUntilTransitionsEnd();
2701+
2702+ // Because doing stuff in C++ is a PITA we keep the counter in the interal qml impl.
2703+ var fakeSurface = findChild(fakeApplication.surface, "fakeSurfaceQML");
2704+ fakeSurface.touchPressCount = 0;
2705+ fakeSurface.touchReleaseCount = 0;
2706+
2707+ tap(applicationWindowLoader.item,
2708+ applicationWindowLoader.item.width / 2, applicationWindowLoader.item.height / 2);
2709+
2710+ verify(fakeSurface.touchPressCount === 1);
2711+ verify(fakeSurface.touchReleaseCount === 1);
2712+ }
2713+
2714+ function test_showNothingOnSuddenSurfaceLoss() {
2715+ surfaceCheckbox.checked = true;
2716+ setApplicationState(appRunning);
2717+ tryCompare(stateGroup, "state", "surface");
2718+ waitUntilTransitionsEnd();
2719+
2720+ surfaceCheckbox.checked = false;
2721+
2722+ verify(stateGroup.state === "void");
2723+ }
2724+ }
2725+}
2726
2727=== modified file 'tests/qmltests/Stages/tst_PhoneStage.qml'
2728--- tests/qmltests/Stages/tst_PhoneStage.qml 2014-07-25 00:01:00 +0000
2729+++ tests/qmltests/Stages/tst_PhoneStage.qml 2014-08-25 08:53:17 +0000
2730@@ -26,14 +26,11 @@
2731 width: units.gu(70)
2732 height: units.gu(70)
2733
2734- Rectangle {
2735-
2736- }
2737-
2738 PhoneStage {
2739 id: phoneStage
2740 anchors { fill: parent; rightMargin: units.gu(30) }
2741 dragAreaWidth: units.gu(2)
2742+ maximizedAppTopMargin: units.gu(3) + units.dp(2)
2743 }
2744
2745 Binding {
2746@@ -46,6 +43,7 @@
2747 anchors { fill: parent; leftMargin: phoneStage.width }
2748
2749 Column {
2750+ id: buttons
2751 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
2752 spacing: units.gu(1)
2753 Button {
2754@@ -57,9 +55,33 @@
2755 }
2756 Button {
2757 anchors { left: parent.left; right: parent.right }
2758- text: "Remove App"
2759- onClicked: {
2760- ApplicationManager.stopApplication(ApplicationManager.get(0).appId)
2761+ text: "Remove Selected"
2762+ onClicked: {
2763+ ApplicationManager.stopApplication(ApplicationManager.get(appList.selectedAppIndex).appId);
2764+ }
2765+ }
2766+ Button {
2767+ anchors { left: parent.left; right: parent.right }
2768+ text: "Stop Selected"
2769+ onClicked: {
2770+ ApplicationManager.get(appList.selectedAppIndex).setState(ApplicationInfo.Stopped);
2771+ }
2772+ }
2773+ }
2774+ ListView {
2775+ id: appList
2776+ property int selectedAppIndex
2777+ anchors { left: parent.left; right: parent.right; top: buttons.bottom; bottom: parent.bottom }
2778+ boundsBehavior: Flickable.StopAtBounds
2779+ model: ApplicationManager
2780+ delegate: Rectangle {
2781+ anchors { left: parent.left; right: parent.right }
2782+ height: units.gu(2)
2783+ color: appList.selectedAppIndex === model.index ? "red" : "white"
2784+ Text { anchors.fill: parent; text: model.appId }
2785+ MouseArea {
2786+ anchors.fill: parent
2787+ onPressed: { appList.selectedAppIndex = model.index; }
2788 }
2789 }
2790 }
2791@@ -84,7 +106,9 @@
2792 function goToSpread() {
2793 var spreadView = findChild(phoneStage, "spreadView");
2794
2795- var startX = phoneStage.width;
2796+ // Keep it inside the PhoneStage otherwise the controls on the right side will
2797+ // capture the press thus the "- 2" on startX.
2798+ var startX = phoneStage.width - 2;
2799 var startY = phoneStage.height / 2;
2800 var endY = startY;
2801 var endX = units.gu(2);
2802@@ -132,7 +156,9 @@
2803
2804 var spreadView = findChild(phoneStage, "spreadView");
2805
2806- var startX = phoneStage.width;
2807+ // Keep it inside the PhoneStage otherwise the controls on the right side will
2808+ // capture the press thus the "- 2" on startX.
2809+ var startX = phoneStage.width - 2;
2810 var startY = phoneStage.height / 2;
2811 var endY = startY;
2812 var endX = spreadView.width - (spreadView.width * spreadView[data.positionMarker]) - data.offset;
2813
2814=== added file 'tests/qmltests/Stages/tst_SpreadDelegate.qml'
2815--- tests/qmltests/Stages/tst_SpreadDelegate.qml 1970-01-01 00:00:00 +0000
2816+++ tests/qmltests/Stages/tst_SpreadDelegate.qml 2014-08-25 08:53:17 +0000
2817@@ -0,0 +1,167 @@
2818+/*
2819+ * Copyright 2014 Canonical Ltd.
2820+ *
2821+ * This program is free software; you can redistribute it and/or modify
2822+ * it under the terms of the GNU General Public License as published by
2823+ * the Free Software Foundation; version 3.
2824+ *
2825+ * This program is distributed in the hope that it will be useful,
2826+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2827+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2828+ * GNU General Public License for more details.
2829+ *
2830+ * You should have received a copy of the GNU General Public License
2831+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2832+ */
2833+
2834+import QtQuick 2.0
2835+import QtTest 1.0
2836+import Unity.Test 0.1 as UT
2837+import ".."
2838+import "../../../qml/Stages"
2839+import Ubuntu.Components 0.1
2840+import Ubuntu.Components.ListItems 1.0 as ListItem
2841+import Unity.Application 0.1
2842+
2843+Rectangle {
2844+ color: "red"
2845+ id: root
2846+ width: units.gu(70)
2847+ height: units.gu(70)
2848+
2849+ Component.onCompleted: {
2850+ root.fakeApplication = ApplicationManager.startApplication("gallery-app");
2851+ }
2852+ property QtObject fakeApplication: null
2853+
2854+ Component {
2855+ id: spreadDelegateComponent
2856+ SpreadDelegate {
2857+ anchors.fill: parent
2858+ swipeToCloseEnabled: swipeToCloseCheckbox.checked
2859+ closeable: closeableCheckbox.checked
2860+ application: fakeApplication
2861+ }
2862+ }
2863+ Loader {
2864+ id: spreadDelegateLoader
2865+ anchors {
2866+ top: parent.top
2867+ bottom: parent.bottom
2868+ left: parent.left
2869+ }
2870+ width: units.gu(40)
2871+ sourceComponent: spreadDelegateComponent
2872+ }
2873+
2874+ Rectangle {
2875+ color: "white"
2876+ anchors {
2877+ top: parent.top
2878+ bottom: parent.bottom
2879+ left: spreadDelegateLoader.right
2880+ right: parent.right
2881+ }
2882+
2883+ Column {
2884+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
2885+ spacing: units.gu(1)
2886+ Row {
2887+ anchors { left: parent.left; right: parent.right }
2888+ CheckBox { id: swipeToCloseCheckbox; checked: false; }
2889+ Label { text: "swipeToCloseEnabled" }
2890+ }
2891+ Row {
2892+ anchors { left: parent.left; right: parent.right }
2893+ CheckBox { id: closeableCheckbox; checked: false }
2894+ Label { text: "closeable" }
2895+ }
2896+ }
2897+ }
2898+
2899+ UT.UnityTestCase {
2900+ id: testCase
2901+ name: "SpreadDelegate"
2902+ when: windowShown
2903+
2904+ SignalSpy {
2905+ id: spyClosedSignal
2906+ target: spreadDelegateLoader.item
2907+ signalName: "closed"
2908+ }
2909+
2910+ property var dragArea
2911+
2912+ function init() {
2913+ dragArea = findInvisibleChild(spreadDelegateLoader.item, "dragArea");
2914+ dragArea.__dateTime = fakeDateTime;
2915+ }
2916+
2917+ function cleanup() {
2918+ // reload our test subject to get it in a fresh state once again
2919+ spreadDelegateLoader.active = false;
2920+ spreadDelegateLoader.active = true;
2921+
2922+ spyClosedSignal.clear();
2923+ }
2924+
2925+ function test_swipeToClose_data() {
2926+ return [
2927+ {tag: "swipeToClose=true closeable=true -> appWindow moves away",
2928+ swipeToClose: true, closeable: true },
2929+
2930+ {tag: "swipeToClose=true closeable=alse -> appWindow bounces back",
2931+ swipeToClose: true, closeable: false },
2932+
2933+ {tag: "swipeToClose=false -> appWindow stays put",
2934+ swipeToClose: false, closeable: true },
2935+ ]
2936+ }
2937+
2938+ function test_swipeToClose(data) {
2939+ var appWindowWithShadow = findChild(spreadDelegateLoader.item, "appWindowWithShadow");
2940+
2941+ verify(appWindowWithShadow.y === 0);
2942+
2943+ swipeToCloseCheckbox.checked = data.swipeToClose;
2944+ closeableCheckbox.checked = data.closeable;
2945+
2946+ var dragDistance = spreadDelegateLoader.item.height / 2;
2947+ var touchX = spreadDelegateLoader.item.width / 2;
2948+ var fromY = spreadDelegateLoader.item.height / 2;
2949+ var toY = fromY - dragDistance;
2950+ touchFlick(spreadDelegateLoader.item,
2951+ touchX /* fromX */, fromY, touchX /* toX */, toY,
2952+ true /* beginTouch */, false /* endTouch */, dragArea.minSpeedToClose * 1.1 /* speed */);
2953+
2954+
2955+ if (data.swipeToClose) {
2956+ verify(appWindowWithShadow.y < 0);
2957+ verify(Math.abs(Math.abs(appWindowWithShadow.y) - dragDistance) < units.gu(1));
2958+
2959+ touchRelease(spreadDelegateLoader.item, touchX, toY - units.gu(1));
2960+
2961+ waitForCloseAnimationToFinish();
2962+
2963+ if (data.closeable) {
2964+ verify(spyClosedSignal.count === 1);
2965+ } else {
2966+ verify(spyClosedSignal.count === 0);
2967+ tryCompare(appWindowWithShadow, "y", 0);
2968+ }
2969+
2970+ } else {
2971+ verify(appWindowWithShadow.y === 0);
2972+
2973+ touchRelease(spreadDelegateLoader.item, touchX, toY);
2974+ }
2975+ }
2976+
2977+ function waitForCloseAnimationToFinish() {
2978+ var closeAnimation = findInvisibleChild(spreadDelegateLoader.item, "closeAnimation");
2979+ wait(closeAnimation.duration * 1.5);
2980+ tryCompare(closeAnimation, "running", false);
2981+ }
2982+
2983+ }
2984+}
2985
2986=== added file 'tests/qmltests/Stages/tst_SurfaceContainer.qml'
2987--- tests/qmltests/Stages/tst_SurfaceContainer.qml 1970-01-01 00:00:00 +0000
2988+++ tests/qmltests/Stages/tst_SurfaceContainer.qml 2014-08-25 08:53:17 +0000
2989@@ -0,0 +1,154 @@
2990+/*
2991+ * Copyright 2014 Canonical Ltd.
2992+ *
2993+ * This program is free software; you can redistribute it and/or modify
2994+ * it under the terms of the GNU General Public License as published by
2995+ * the Free Software Foundation; version 3.
2996+ *
2997+ * This program is distributed in the hope that it will be useful,
2998+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2999+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3000+ * GNU General Public License for more details.
3001+ *
3002+ * You should have received a copy of the GNU General Public License
3003+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3004+ */
3005+
3006+import QtQuick 2.0
3007+import QtTest 1.0
3008+import Unity.Test 0.1 as UT
3009+import ".."
3010+import "../../../qml/Stages"
3011+import Ubuntu.Components 0.1
3012+import Unity.Application 0.1
3013+
3014+Rectangle {
3015+ color: "black"
3016+ id: root
3017+ width: units.gu(70)
3018+ height: units.gu(70)
3019+
3020+ Component {
3021+ id: fakeSurfaceComponent
3022+ Rectangle {
3023+ color: "green"
3024+
3025+ Rectangle {
3026+ color: "blue"
3027+ anchors.fill: parent
3028+ anchors.margins: units.gu(2)
3029+ Text {
3030+ anchors.fill: parent
3031+ text: "Surface"
3032+ color: "green"
3033+ font.bold: true
3034+ fontSizeMode: Text.Fit
3035+ minimumPixelSize: 10; font.pixelSize: 200
3036+ verticalAlignment: Text.AlignVCenter
3037+ }
3038+ }
3039+
3040+ width: units.gu(1)
3041+ height: units.gu(1)
3042+
3043+ property int type: MirSurfaceItem.Normal
3044+ property int state: MirSurfaceItem.Restored
3045+ property string name: "Fake Surface"
3046+ property Item parentSurface: null
3047+ property list<Item> childSurfaces
3048+ function release() {}
3049+ signal removed();
3050+ }
3051+ }
3052+
3053+ Item {
3054+ id: tempSurfaceHolder
3055+ }
3056+
3057+ Component {
3058+ id: surfaceContainerComponent
3059+ SurfaceContainer {
3060+ anchors.fill: parent
3061+ }
3062+ }
3063+ Loader {
3064+ id: surfaceContainerLoader
3065+ anchors {
3066+ top: parent.top
3067+ topMargin: fullscreenCheckbox.checked ? 0 : units.gu(3) + units.dp(2)
3068+ bottom: parent.bottom
3069+ left: parent.left
3070+ }
3071+ width: units.gu(40)
3072+ sourceComponent: surfaceContainerComponent
3073+ }
3074+
3075+ Rectangle {
3076+ color: "white"
3077+ anchors {
3078+ top: parent.top
3079+ bottom: parent.bottom
3080+ left: surfaceContainerLoader.right
3081+ right: parent.right
3082+ }
3083+
3084+ Column {
3085+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
3086+ spacing: units.gu(1)
3087+ Row {
3088+ anchors { left: parent.left; right: parent.right }
3089+ CheckBox {
3090+ id: surfaceCheckbox; checked: false;
3091+ onCheckedChanged: {
3092+ if (surfaceContainerLoader.status !== Loader.Ready)
3093+ return;
3094+
3095+ if (checked) {
3096+ var fakeSurface = fakeSurfaceComponent.createObject(tempSurfaceHolder);
3097+ surfaceContainerLoader.item.surface = fakeSurface;
3098+ } else {
3099+ var fakeSurface = surfaceContainerLoader.item.surface;
3100+ surfaceContainerLoader.item.surface = null;
3101+ fakeSurface.parent = null;
3102+ fakeSurface.destroy();
3103+ }
3104+ }
3105+ }
3106+ Label { text: "surface" }
3107+ }
3108+ Row {
3109+ anchors { left: parent.left; right: parent.right }
3110+ CheckBox {id: fullscreenCheckbox; checked: true; }
3111+ Label { text: "fullscreen" }
3112+ }
3113+ }
3114+ }
3115+
3116+ UT.UnityTestCase {
3117+ id: testCase
3118+ name: "SurfaceContainer"
3119+ when: windowShown
3120+
3121+ function cleanup() {
3122+ // reload our test subject to get it in a fresh state once again
3123+ surfaceContainerLoader.active = false;
3124+ surfaceCheckbox.checked = false;
3125+ surfaceContainerLoader.active = true;
3126+ }
3127+
3128+ /*
3129+ Add a first surface. Then remove it. Then add a second surface.
3130+ That second surface should be properly sized.
3131+
3132+ Regression test for https://bugs.launchpad.net/ubuntu/+source/qtmir/+bug/1359819
3133+ */
3134+ function test_resetSurfaceGetsProperlySized() {
3135+ surfaceCheckbox.checked = true;
3136+ surfaceCheckbox.checked = false;
3137+ surfaceCheckbox.checked = true;
3138+ var fakeSurface = surfaceContainerLoader.item.surface;
3139+ compare(fakeSurface.width, surfaceContainerLoader.item.width);
3140+ compare(fakeSurface.height, surfaceContainerLoader.item.height);
3141+ }
3142+ }
3143+}
3144
3145=== modified file 'tests/qmltests/tst_Shell.qml'
3146--- tests/qmltests/tst_Shell.qml 2014-08-21 14:46:12 +0000
3147+++ tests/qmltests/tst_Shell.qml 2014-08-25 08:53:17 +0000
3148@@ -416,11 +416,11 @@
3149 compare(panel.fullscreenMode, false);
3150 ApplicationManager.startApplication("camera-app");
3151 tryCompare(panel, "fullscreenMode", true);
3152- ApplicationManager.startApplication("gallery-app");
3153+ ApplicationManager.startApplication("dialer-app");
3154 tryCompare(panel, "fullscreenMode", false);
3155 ApplicationManager.requestFocusApplication("camera-app");
3156 tryCompare(panel, "fullscreenMode", true);
3157- ApplicationManager.requestFocusApplication("gallery-app");
3158+ ApplicationManager.requestFocusApplication("dialer-app");
3159 tryCompare(panel, "fullscreenMode", false);
3160 }
3161
3162
3163=== modified file 'tests/utils/modules/Unity/Test/UnityTestCase.qml'
3164--- tests/utils/modules/Unity/Test/UnityTestCase.qml 2014-06-23 16:40:11 +0000
3165+++ tests/utils/modules/Unity/Test/UnityTestCase.qml 2014-08-25 08:53:17 +0000
3166@@ -312,12 +312,15 @@
3167 }
3168
3169 function tap(item, x, y) {
3170+ var root = fetchRootItem(item)
3171+ var rootPoint = item.mapToItem(root, x, y)
3172+
3173 var event = touchEvent()
3174- event.press(0 /* touchId */, x, y)
3175+ event.press(0 /* touchId */, rootPoint.x, rootPoint.y)
3176 event.commit()
3177
3178 event = touchEvent()
3179- event.release(0 /* touchId */, x, y)
3180+ event.release(0 /* touchId */, rootPoint.x, rootPoint.y)
3181 event.commit()
3182 }
3183

Subscribers

People subscribed via source and target branches