Merge lp:~gerboland/unity8/initialSurfaceGeometry into lp:unity8

Proposed by Gerry Boland
Status: Superseded
Proposed branch: lp:~gerboland/unity8/initialSurfaceGeometry
Merge into: lp:unity8
Diff against target: 3235 lines (+1428/-772)
41 files modified
debian/control (+4/-4)
include/paths.h.in (+8/-0)
plugins/Unity/Launcher/CMakeLists.txt (+1/-1)
qml/Panel/Panel.qml (+4/-8)
qml/Shell.qml (+9/-10)
qml/Stages/AppSurfaceContainer.qml (+0/-96)
qml/Stages/ApplicationWindow.qml (+258/-0)
qml/Stages/PhoneStage.qml (+29/-4)
qml/Stages/SpreadDelegate.qml (+34/-59)
qml/Stages/SurfaceContainer.qml (+1/-1)
qml/Stages/TabletStage.qml (+34/-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 (+84/-16)
tests/mocks/Unity/Application/ApplicationInfo.h (+52/-46)
tests/mocks/Unity/Application/ApplicationManager.cpp (+109/-104)
tests/mocks/Unity/Application/ApplicationManager.h (+14/-6)
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 (+2/-2)
tests/plugins/Unity/Launcher/launchermodeltest.cpp (+2/-1)
tests/qmltests/CMakeLists.txt (+2/-0)
tests/qmltests/Stages/tst_ApplicationWindow.qml (+346/-0)
tests/qmltests/Stages/tst_PhoneStage.qml (+44/-9)
tests/qmltests/Stages/tst_SpreadDelegate.qml (+167/-0)
tests/qmltests/tst_Shell.qml (+2/-2)
tests/utils/modules/Unity/Test/UnityTestCase.qml (+5/-2)
To merge this branch: bzr merge lp:~gerboland/unity8/initialSurfaceGeometry
Reviewer Review Type Date Requested Status
Michał Sawicz Needs Information
Albert Astals Cid (community) Abstain
PS Jenkins bot (community) continuous-integration Needs Fixing
Daniel d'Andrada (community) Approve
Review via email: mp+230490@code.launchpad.net

This proposal has been superseded by a proposal from 2014-08-21.

Commit message

Implement a surfaceSizer exported by qtmir to override the initial surface geometry requested by clients. Also fix small surface size & position bug.

The SurfaceSizer is a Javascript function defined by unity8 which is registered with QtMir as a callback function whenever a client requests a new surface from Mir. It allows unity8 to override the client-requested width & height of the surface. This fixes the startup-resize visual glitching evident right now.

While implementing the surfaceSizer function, I discovered that all non-fullscreen surfaces were 2DP too tall and being occluded by the orange line of the panel. This MR fixes that fact and cleans up what "panelHeight" refers to in Shell.qml - it is the height of the indicator bar plus the orange line.

Description of the change

Implement a surfaceSizer exported by qtmir to override the initial surface geometry requested by clients

 * Are there any related MPs required for this MP to build/function as expected? Please list.
https://code.launchpad.net/~gerboland/unity-api/surfaceSizerCallback/+merge/230270
https://code.launchpad.net/~gerboland/qtmir/initialSurfaceGeometry/+merge/230491
 * Did you perform an exploratory manual test run of your code change and any related functionality?
Y
 * Did you make sure that your branch does not contain spurious tags?
Y
 * If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
N/A
 * If you changed the UI, has there been a design review?
N/A

To post a comment you must log in.
Revision history for this message
Michał Sawicz (saviq) wrote :

unity8-fake-env should provide unity-applications-4 (or 3?).

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

Oh and you need to depend on the new unity-api.

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

Adjjust to newer API

1144. By Gerry Boland

[debian] depend on unity-api 7.89 (supplying unity-application-impl3) and qtmir 0.4.2

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

Updated to newer unity-api and comments addressed

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
Daniel d'Andrada (dandrader) wrote :

Commented in the diff.

review: Needs Information
Revision history for this message
Gerry Boland (gerboland) :
Revision history for this message
Gerry Boland (gerboland) :
Revision history for this message
Daniel d'Andrada (dandrader) :
review: Needs Information
Revision history for this message
Albert Astals Cid (aacid) wrote :

Text conflict in debian/control
1 conflicts encountered.

review: Needs Fixing
Revision history for this message
Gerry Boland (gerboland) :
1145. By Gerry Boland

Merge trunk & fix conflict

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

@Daniel: replied to your question (sorry for delay, forgot to hit "save comment" until now)

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 :

See diff comment.

review: Needs Fixing
1146. By Gerry Boland

Merge trunk

1147. By Gerry Boland

Remove test crash fix

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

Reply inline

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

Code does merge properly now

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

This conflicts with lp:~dandrader/unity8/lifecycle that's already approved and slated for release soon, maybe rebase on that one and we'll land them together?

review: Needs Information
1148. By Gerry Boland

Merge lp:~dandrader/unity8/lifecycle and fix conflicts

1149. By Gerry Boland

Merge trunk

1150. By Gerry Boland

Merge trunk

1151. By Gerry Boland

merge old trunk

1152. By Gerry Boland

Merge current trunk

1153. By Gerry Boland

Undo all changes to Shell and Panel, start afresh

1154. By Gerry Boland

I hate you whitespace cheker

1155. By Gerry Boland

Bump unity-api & qtmir dep

1156. By Gerry Boland

Merge trunk

1157. By Gerry Boland

Delay Dash DBus service creation slightly, to avoid possible shell deadlock

1158. By Gerry Boland

Merge trunk

1159. By Gerry Boland

Merge trunk

1160. By Gerry Boland

Remove Dash block workaround

1161. By Gerry Boland

Merge trunk

Unmerged revisions

1161. By Gerry Boland

Merge trunk

1160. By Gerry Boland

Remove Dash block workaround

1159. By Gerry Boland

Merge trunk

1158. By Gerry Boland

Merge trunk

1157. By Gerry Boland

Delay Dash DBus service creation slightly, to avoid possible shell deadlock

1156. By Gerry Boland

Merge trunk

1155. By Gerry Boland

Bump unity-api & qtmir dep

1154. By Gerry Boland

I hate you whitespace cheker

1153. By Gerry Boland

Undo all changes to Shell and Panel, start afresh

1152. By Gerry Boland

Merge current trunk

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

Subscribers

People subscribed via source and target branches